use std::io::Write; use std::collections::HashSet; use crate::parser; fn munge(s: &str) -> String { s.replace("?", "_INT_").replace("-", "_DASH_") } pub struct LexicalContext<'a> { parent: Option<&'a LexicalContext<'a>>, local: HashSet, is_tail: bool, } impl <'a> LexicalContext<'a> { pub fn toplevel() -> Self { LexicalContext { parent: None, local: HashSet::new(), is_tail: false, } } #[allow(dead_code)] fn new(parent: &'a LexicalContext<'a>) -> Self { LexicalContext { parent: Some(parent), local: HashSet::new(), is_tail: parent.is_tail, } } fn contains(&self, s: &str) -> bool { self.local.contains(s) || self.parent.map_or(false, |c| c.contains(s)) } fn insert(&mut self, s: String) -> bool { self.local.insert(s) } } /// Embeds the pre-written js pub fn emit_injector(w: &mut dyn Write) -> std::io::Result<()>{ let bytes = include_bytes!("./js/deeinject.js"); write!(w, "{}", String::from_utf8_lossy(bytes))?; Ok(()) } pub fn emit_all(w: &mut dyn Write, ast: &[parser::Stmt], ctx: &mut LexicalContext) -> std::io::Result<()> { let is_tail = ctx.is_tail; ctx.is_tail = false; if let Some((last, butlast)) = ast.split_last() { for stmt in butlast { emit(w, stmt, ctx)?; } ctx.is_tail = is_tail; emit(w, last, ctx)?; } Ok(()) } pub fn emit(w: &mut dyn Write, stmt: &parser::Stmt, ctx: &mut LexicalContext) -> std::io::Result<()> { match &stmt { parser::Stmt::BareExpr(expr) => { if ctx.is_tail { write!(w, "return ")?; } emit_expr(w, expr, ctx)?; writeln!(w, ";")?; } parser::Stmt::Assignment(id, expr) => { if !ctx.contains(id) { ctx.insert(id.clone()); write!(w, "let ")?; } write!(w, "{} = ", munge(id))?; emit_expr(w, expr, ctx)?; writeln!(w, ";")?; if ctx.is_tail { writeln!(w, "return;")?; } } parser::Stmt::Conditional(if_blocks, else_block) => { let mut first_block = true; for if_block in if_blocks { if first_block { write!(w, "if (")?; } else { write!(w, "else if(")?; } first_block = false; emit_expr(w, &if_block.guard, ctx)?; writeln!(w, ") {{")?; let mut block_ctx = LexicalContext::new(ctx); emit_all(w, &if_block.block, &mut block_ctx)?; writeln!(w, "}}")?; } if let Some(block) = else_block { writeln!(w, "else {{")?; let mut block_ctx = LexicalContext::new(ctx); emit_all(w, block, &mut block_ctx)?; writeln!(w, "}}")?; } } parser::Stmt::Loop(eloop) => match &eloop { parser::Loop::Over(id, expr, block) => { let is_tail = ctx.is_tail; ctx.is_tail = false; write!(w, "for (let {} of ", id)?; emit_expr(w, expr, ctx)?; writeln!(w, ") {{")?; emit_all(w, block, ctx)?; writeln!(w, "}}")?; ctx.is_tail = is_tail; } _ => todo!() } } Ok(()) } pub fn emit_expr(w: &mut dyn Write, expr: &parser::Expr, ctx: &mut LexicalContext) -> std::io::Result<()> { match &expr { parser::Expr::Id(id) => { write!(w, "{}", munge(id))?; } parser::Expr::Atom(atom) => { write!(w, "{}", atom)?; } parser::Expr::Funcall(id, args) => { write!(w, "{}", munge(id))?; if args.is_empty() { write!(w, "()")?; } else if ctx.contains(id) { // Use deelang calling semantics for arg in args { write!(w, "(")?; emit_expr(w, arg, ctx)?; write!(w, ")")? } } else { // Use JS calling semantics write!(w, "(")?; let (last, butlast) = args.split_last().unwrap(); for arg in butlast { emit_expr(w, arg, ctx)?; write!(w, ",")? } emit_expr(w, last, ctx)?; write!(w, ")")?; } } parser::Expr::Funcdef(arg, body) => { if let Some(arg) = arg { writeln!(w, "function ({}){{", arg)?; } else { writeln!(w, "function (){{")?; } let mut fun_ctx = LexicalContext::new(ctx); fun_ctx.is_tail = true; emit_all(w, body, &mut fun_ctx)?; write!(w, "}}")?; } parser::Expr::Plus(e1, e2) => { emit_expr(w, e1.as_ref(), ctx)?; write!(w, " + ")?; emit_expr(w, e2.as_ref(), ctx)?; } parser::Expr::Minus(e1, e2) => { emit_expr(w, e1.as_ref(), ctx)?; write!(w, " - ")?; emit_expr(w, e2.as_ref(), ctx)?; } parser::Expr::Mult(e1, e2) => { emit_expr(w, e1.as_ref(), ctx)?; write!(w, " * ")?; emit_expr(w, e2.as_ref(), ctx)?; } parser::Expr::Div(e1, e2) => { emit_expr(w, e1.as_ref(), ctx)?; write!(w, " / ")?; emit_expr(w, e2.as_ref(), ctx)?; } parser::Expr::Mod(e1, e2) => { emit_expr(w, e1.as_ref(), ctx)?; write!(w, " % ")?; emit_expr(w, e2.as_ref(), ctx)?; } parser::Expr::Equal(e1, e2) => { emit_expr(w, e1.as_ref(), ctx)?; write!(w, " == ")?; emit_expr(w, e2.as_ref(), ctx)?; } parser::Expr::Relop(op, e1, e2) => { emit_expr(w, e1.as_ref(), ctx)?; write!(w, " {} ", op)?; emit_expr(w, e2.as_ref(), ctx)?; } _ => todo!(), } Ok(()) }