WIP to think about tail calls etc

This commit is contained in:
Dane Johnson 2024-11-13 09:50:44 -06:00
parent 201d7d2594
commit d4d4756002
3 changed files with 77 additions and 11 deletions

View File

@ -5,14 +5,16 @@ use crate::parser;
pub struct LexicalContext<'a> {
parent: Option<&'a LexicalContext<'a>>,
local: HashSet<String>
local: HashSet<String>,
is_tail: bool,
}
impl <'a> LexicalContext<'a> {
pub fn toplevel() -> Self {
LexicalContext {
parent: None,
local: HashSet::new()
local: HashSet::new(),
is_tail: false,
}
}
#[allow(dead_code)]
@ -20,6 +22,7 @@ impl <'a> LexicalContext<'a> {
LexicalContext {
parent: Some(parent),
local: HashSet::new(),
is_tail: false,
}
}
fn contains(&self, s: &str) -> bool {
@ -53,7 +56,32 @@ pub fn emit(w: &mut dyn Write, stmt: &parser::Stmt, ctx: &mut LexicalContext) ->
emit_expr(w, expr, ctx)?;
writeln!(w, ";")?;
}
_ => todo!(),
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(())
}
@ -66,6 +94,42 @@ pub fn emit_expr(w: &mut dyn Write, expr: &parser::Expr, ctx: &mut LexicalContex
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(())

View File

@ -49,9 +49,11 @@ pub fn eval(ast: &parser::Stmt, env: &mut Env) {
match res {
Atom::Bool(true) => {
matched = true;
for stmt in block {
eval(stmt, env);
}
if let parser::Expr::Block(block) = block {
for stmt in block {
eval(stmt, env);
}
}
break;
}
Atom::Bool(false) => continue,

View File

@ -43,7 +43,7 @@ impl fmt::Display for Atom {
#[derive(Debug,PartialEq,Clone)]
pub struct GuardedBlock {
pub guard: Expr,
pub block: Vec<Stmt>,
pub block: Expr,
}
peg::parser! {
@ -93,14 +93,14 @@ peg::parser! {
= "{" _ stop() indent() __* a:assignment()+ dedent() __* "}" _ { Expr::Object(a) }
rule _if() -> GuardedBlock
= "if" _ g:expr() b:indented_block() {
= "if" _ g:expr() b:block() {
GuardedBlock {
guard: g,
block: b,
}
}
rule elif() -> GuardedBlock
= "elif" _ g:expr() b:indented_block() {
= "elif" _ g:expr() b:block() {
GuardedBlock {
guard: g,
block: b
@ -333,11 +333,11 @@ else
vec![
GuardedBlock {
guard: Expr::Id("foo".to_string()),
block: vec![Stmt::Funcall(Expr::Funcall("bar".to_string(), vec![]))]
block: Expr::Block(vec![Stmt::Funcall(Expr::Funcall("bar".to_string(), vec![]))])
},
GuardedBlock {
guard: Expr::Id("baz".to_string()),
block: vec![Stmt::Funcall(Expr::Funcall("foobar".to_string(), vec![]))]
block: Expr::Block(vec![Stmt::Funcall(Expr::Funcall("foobar".to_string(), vec![]))])
},
],
Some(Expr::Block(vec![Stmt::Funcall(Expr::Funcall("quux".to_string(), vec![]))])),