deelang/src/emitter.rs

202 lines
4.9 KiB
Rust
Raw Normal View History

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>>,
2024-11-13 09:50:44 -06:00
local: HashSet<String>,
is_tail: bool,
}
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,
}
}
#[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(())
}
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<()> {
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;")?;
}
}
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 {
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);
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);
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!()
}
}
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)?;
}
2024-11-13 09:50:44 -06:00
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)?;
2024-11-13 09:50:44 -06:00
write!(w, ")")?;
}
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)?;
}
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(())
}