2024-11-12 07:19:43 -06:00
|
|
|
use std::io::Write;
|
|
|
|
use std::collections::HashSet;
|
|
|
|
|
|
|
|
use crate::parser;
|
|
|
|
|
2024-11-13 20:40:40 -06:00
|
|
|
fn munge(s: &str) -> String {
|
|
|
|
s.replace("?", "_INT_").replace("-", "_DASH_")
|
|
|
|
}
|
|
|
|
|
2024-11-12 07:19:43 -06:00
|
|
|
pub struct LexicalContext<'a> {
|
|
|
|
parent: Option<&'a LexicalContext<'a>>,
|
2024-11-13 09:50:44 -06:00
|
|
|
local: HashSet<String>,
|
|
|
|
is_tail: bool,
|
2024-11-12 07:19:43 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
impl <'a> LexicalContext<'a> {
|
|
|
|
pub fn toplevel() -> Self {
|
|
|
|
LexicalContext {
|
|
|
|
parent: None,
|
2024-11-13 09:50:44 -06:00
|
|
|
local: HashSet::new(),
|
|
|
|
is_tail: false,
|
2024-11-12 07:19:43 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
|
|
fn new(parent: &'a LexicalContext<'a>) -> Self {
|
|
|
|
LexicalContext {
|
|
|
|
parent: Some(parent),
|
|
|
|
local: HashSet::new(),
|
2024-11-13 20:40:40 -06:00
|
|
|
is_tail: parent.is_tail,
|
2024-11-12 07:19:43 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-14 13:09:54 -06:00
|
|
|
/// 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(())
|
|
|
|
}
|
|
|
|
|
2024-11-16 13:50:20 -06:00
|
|
|
pub fn emit_all(w: &mut dyn Write, ast: &[parser::Stmt], ctx: &mut LexicalContext) -> std::io::Result<()> {
|
2024-11-13 20:40:40 -06:00
|
|
|
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)?;
|
2024-11-12 07:19:43 -06:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn emit(w: &mut dyn Write, stmt: &parser::Stmt, ctx: &mut LexicalContext) -> std::io::Result<()> {
|
|
|
|
match &stmt {
|
2024-11-13 11:13:09 -06:00
|
|
|
parser::Stmt::BareExpr(expr) => {
|
2024-11-13 20:40:40 -06:00
|
|
|
if ctx.is_tail {
|
|
|
|
write!(w, "return ")?;
|
|
|
|
}
|
2024-11-12 07:19:43 -06:00
|
|
|
emit_expr(w, expr, ctx)?;
|
2024-11-13 20:40:40 -06:00
|
|
|
writeln!(w, ";")?;
|
2024-11-12 07:19:43 -06:00
|
|
|
}
|
|
|
|
parser::Stmt::Assignment(id, expr) => {
|
|
|
|
if !ctx.contains(id) {
|
|
|
|
ctx.insert(id.clone());
|
|
|
|
write!(w, "let ")?;
|
|
|
|
}
|
2024-11-13 20:40:40 -06:00
|
|
|
write!(w, "{} = ", munge(id))?;
|
2024-11-12 07:19:43 -06:00
|
|
|
emit_expr(w, expr, ctx)?;
|
|
|
|
writeln!(w, ";")?;
|
2024-11-13 20:40:40 -06:00
|
|
|
if ctx.is_tail {
|
|
|
|
writeln!(w, "return;")?;
|
|
|
|
}
|
2024-11-12 07:19:43 -06:00
|
|
|
}
|
2024-11-13 09:50:44 -06:00
|
|
|
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 {
|
2024-11-13 20:40:40 -06:00
|
|
|
write!(w, "else if(")?;
|
2024-11-13 09:50:44 -06:00
|
|
|
}
|
|
|
|
first_block = false;
|
|
|
|
emit_expr(w, &if_block.guard, ctx)?;
|
|
|
|
writeln!(w, ") {{")?;
|
|
|
|
let mut block_ctx = LexicalContext::new(ctx);
|
2024-11-13 11:13:09 -06:00
|
|
|
emit_all(w, &if_block.block, &mut block_ctx)?;
|
2024-11-13 09:50:44 -06:00
|
|
|
writeln!(w, "}}")?;
|
|
|
|
}
|
|
|
|
if let Some(block) = else_block {
|
|
|
|
writeln!(w, "else {{")?;
|
|
|
|
let mut block_ctx = LexicalContext::new(ctx);
|
2024-11-13 11:13:09 -06:00
|
|
|
emit_all(w, block, &mut block_ctx)?;
|
2024-11-13 09:50:44 -06:00
|
|
|
writeln!(w, "}}")?;
|
|
|
|
}
|
|
|
|
}
|
2024-11-18 10:41:51 -06:00
|
|
|
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!()
|
|
|
|
}
|
2024-11-12 07:19:43 -06:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn emit_expr(w: &mut dyn Write, expr: &parser::Expr, ctx: &mut LexicalContext) -> std::io::Result<()> {
|
|
|
|
match &expr {
|
|
|
|
parser::Expr::Id(id) => {
|
2024-11-13 20:40:40 -06:00
|
|
|
write!(w, "{}", munge(id))?;
|
2024-11-12 07:19:43 -06:00
|
|
|
}
|
|
|
|
parser::Expr::Atom(atom) => {
|
|
|
|
write!(w, "{}", atom)?;
|
|
|
|
}
|
2024-11-13 09:50:44 -06:00
|
|
|
parser::Expr::Funcall(id, args) => {
|
2024-11-14 15:32:46 -06:00
|
|
|
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, ",")?
|
2024-11-14 13:09:54 -06:00
|
|
|
}
|
|
|
|
emit_expr(w, last, ctx)?;
|
2024-11-13 09:50:44 -06:00
|
|
|
write!(w, ")")?;
|
|
|
|
}
|
2024-11-14 15:32:46 -06:00
|
|
|
|
2024-11-13 09:50:44 -06:00
|
|
|
}
|
|
|
|
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;
|
2024-11-16 13:50:20 -06:00
|
|
|
emit_all(w, body, &mut fun_ctx)?;
|
2024-11-13 09:50:44 -06:00
|
|
|
write!(w, "}}")?;
|
|
|
|
}
|
|
|
|
parser::Expr::Plus(e1, e2) => {
|
|
|
|
emit_expr(w, e1.as_ref(), ctx)?;
|
|
|
|
write!(w, " + ")?;
|
|
|
|
emit_expr(w, e2.as_ref(), ctx)?;
|
|
|
|
}
|
2024-11-13 20:40:40 -06:00
|
|
|
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)?;
|
|
|
|
}
|
2024-11-14 15:32:46 -06:00
|
|
|
parser::Expr::Relop(op, e1, e2) => {
|
2024-11-13 20:40:40 -06:00
|
|
|
emit_expr(w, e1.as_ref(), ctx)?;
|
2024-11-14 15:32:46 -06:00
|
|
|
write!(w, " {} ", op)?;
|
2024-11-13 20:40:40 -06:00
|
|
|
emit_expr(w, e2.as_ref(), ctx)?;
|
|
|
|
}
|
2024-11-12 07:19:43 -06:00
|
|
|
_ => todo!(),
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|