use std::io::Write; use std::collections::HashSet; use crate::parser; 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: false, } } 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) } } pub fn emit_all(w: &mut dyn Write, ast: &Vec, ctx: &mut LexicalContext) -> std::io::Result<()> { for stmt in ast { emit(w, stmt, ctx)?; } Ok(()) } pub fn emit(w: &mut dyn Write, stmt: &parser::Stmt, ctx: &mut LexicalContext) -> std::io::Result<()> { match &stmt { parser::Stmt::ReplPrint(expr) => { write!(w, "console.log((")?; 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, "{} = ", id)?; emit_expr(w, expr, ctx)?; writeln!(w, ";")?; } parser::Stmt::Funcall(call) => { emit_expr(w, call, ctx)?; writeln!(w, ";")?; } 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_expr(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_expr(w, block, &mut block_ctx)?; writeln!(w, "}}")?; } } } 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, "{}", id)?; } parser::Expr::Atom(atom) => { write!(w, "{}", atom)?; } parser::Expr::Funcall(id, args) => { write!(w, "{}", id)?; if args.is_empty() { write!(w, "()")?; } for arg in args { write!(w, "(")?; emit_expr(w, arg, 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_expr(w, body.as_ref(), &mut fun_ctx)?; write!(w, "}}")?; } parser::Expr::Block(stmts) => { let mut peek_iter = stmts.iter().peekable(); while let Some(stmt) = peek_iter.next() { if peek_iter.peek().is_none() { write!(w, "return ")?; } emit(w, stmt, ctx)?; } } parser::Expr::Plus(e1, e2) => { emit_expr(w, e1.as_ref(), ctx)?; write!(w, " + ")?; emit_expr(w, e2.as_ref(), ctx)?; } _ => todo!(), } Ok(()) }