From 26bff70b1ffd6b21bae6b3055f0c821248f3ab07 Mon Sep 17 00:00:00 2001 From: Dane Johnson Date: Wed, 13 Nov 2024 20:40:40 -0600 Subject: [PATCH] Remove objects, add some other stuff so that I can make fizzbuzz run in node --- demo/buildup.dee | 10 ++------ demo/real.dee | 9 +++++++ src/emitter.rs | 66 +++++++++++++++++++++++++++++++++++++++++------- src/main.rs | 14 +++++++--- src/parser.rs | 19 +++++++++++--- 5 files changed, 95 insertions(+), 23 deletions(-) create mode 100644 demo/real.dee diff --git a/demo/buildup.dee b/demo/buildup.dee index f600893..b86ea26 100644 --- a/demo/buildup.dee +++ b/demo/buildup.dee @@ -7,8 +7,8 @@ foo <- -> bar() ## function definitions foo <- -> bar() baz() -## functions with block statements -foo <- x -> y -> x * y + 7 ## Multiple argument functions +# functions with block statements +oo <- x -> y -> x * y + 7 ## Multiple argument functions if test?() do-something() elif other-test?() @@ -23,9 +23,3 @@ else if second-test?() do-a-real-bad-thing() ## Nested blocks - -obj <- { - field <- "hello" - method <- x -> x * 2 -} -## Object creation diff --git a/demo/real.dee b/demo/real.dee new file mode 100644 index 0000000..166c459 --- /dev/null +++ b/demo/real.dee @@ -0,0 +1,9 @@ +## Obligatory +fizzbuzz <- x -> + if x % 15 = 0 print("fizzbuzz") + elif x % 3 = 0 print("fizz") + # elif x % 5 = 0 print("buzz") + else print(x) + if x < 100 fizzbuzz(x + 1) ## TODO add looping construct(s) + +fizzbuzz(1) diff --git a/src/emitter.rs b/src/emitter.rs index 6f50b24..2902cdc 100644 --- a/src/emitter.rs +++ b/src/emitter.rs @@ -3,6 +3,10 @@ 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, @@ -22,7 +26,7 @@ impl <'a> LexicalContext<'a> { LexicalContext { parent: Some(parent), local: HashSet::new(), - is_tail: false, + is_tail: parent.is_tail, } } fn contains(&self, s: &str) -> bool { @@ -34,8 +38,14 @@ impl <'a> LexicalContext<'a> { } pub fn emit_all(w: &mut dyn Write, ast: &Vec, ctx: &mut LexicalContext) -> std::io::Result<()> { - for stmt in ast { - emit(w, stmt, ctx)?; + 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(()) } @@ -43,20 +53,28 @@ pub fn emit_all(w: &mut dyn Write, ast: &Vec, ctx: &mut LexicalCon pub fn emit(w: &mut dyn Write, stmt: &parser::Stmt, ctx: &mut LexicalContext) -> std::io::Result<()> { match &stmt { parser::Stmt::BareExpr(expr) => { - write!(w, "console.log((")?; + if ctx.is_tail { + write!(w, "return ")?; + } emit_expr(w, expr, ctx)?; - writeln!(w, "));")?; + writeln!(w, ";")?; } parser::Stmt::Assignment(id, expr) => { if !ctx.contains(id) { ctx.insert(id.clone()); write!(w, "let ")?; } - write!(w, "{} = ", id)?; + write!(w, "{} = ", munge(id))?; emit_expr(w, expr, ctx)?; writeln!(w, ";")?; + if ctx.is_tail { + writeln!(w, "return;")?; + } } parser::Stmt::Funcall(call) => { + if ctx.is_tail { + write!(w, "return ")?; + } emit_expr(w, call, ctx)?; writeln!(w, ";")?; } @@ -66,7 +84,7 @@ pub fn emit(w: &mut dyn Write, stmt: &parser::Stmt, ctx: &mut LexicalContext) -> if first_block { write!(w, "if (")?; } else { - write!(w, " else if(")?; + write!(w, "else if(")?; } first_block = false; emit_expr(w, &if_block.guard, ctx)?; @@ -89,13 +107,13 @@ pub fn emit(w: &mut dyn Write, stmt: &parser::Stmt, ctx: &mut LexicalContext) -> 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)?; + write!(w, "{}", munge(id))?; } parser::Expr::Atom(atom) => { write!(w, "{}", atom)?; } parser::Expr::Funcall(id, args) => { - write!(w, "{}", id)?; + write!(w, "{}", munge(id))?; if args.is_empty() { write!(w, "()")?; } @@ -121,6 +139,36 @@ pub fn emit_expr(w: &mut dyn Write, expr: &parser::Expr, ctx: &mut LexicalContex 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::Lt(e1, e2) => { + emit_expr(w, e1.as_ref(), ctx)?; + write!(w, " < ")?; + emit_expr(w, e2.as_ref(), ctx)?; + } _ => todo!(), } Ok(()) diff --git a/src/main.rs b/src/main.rs index cc47a4a..1706de6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,9 +15,11 @@ struct Cli { #[clap(help="Specify a file to run")] file: Option, #[clap(short, long, help="Only parse, do not evaluate")] - parse_only: bool, + parse: bool, #[clap(short, long, help="Cross-compile to ECMAScript")] ecmascript: bool, + #[clap(long, help="Only run the pre-processor")] + preprocess: bool, } fn repl(cli: &Cli) { @@ -28,7 +30,7 @@ fn repl(cli: &Cli) { let mut line = String::new(); io::stdin().read_line(&mut line).unwrap(); let tree = parser::parse_stmt(&line); - if cli.parse_only { + if cli.parse { println!("{:#?}", tree); } else if cli.ecmascript { emitter::emit(&mut out, &tree, &mut toplevel).ok(); @@ -42,11 +44,17 @@ fn script(cli: &Cli) { let mut file = File::open(cli.file.as_ref().unwrap()).expect("Could not read file"); let mut prgm = String::new(); file.read_to_string(&mut prgm).unwrap(); + if cli.preprocess { + println!("{}", parser::preprocess(&prgm)); + return; + } let tree = parser::parse(&prgm); - if cli.parse_only { + if cli.parse { println!("{:#?}", tree); } else if cli.ecmascript { let mut out = io::stdout(); + // TODO remove this hack + writeln!(out, "const print = console.log;").ok(); let mut toplevel = emitter::LexicalContext::toplevel(); emitter::emit_all(&mut out, &tree, &mut toplevel).ok(); } else { diff --git a/src/parser.rs b/src/parser.rs index 9b914fa..063bd77 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -19,6 +19,9 @@ pub enum Expr { Minus(Box, Box), Mult(Box, Box), Div(Box, Box), + Mod(Box, Box), + Equal(Box, Box), + Lt(Box, Box), } pub type Block = Vec; @@ -56,6 +59,9 @@ peg::parser! { c:conditional() { c } / e:expr() stop() { Stmt::BareExpr(e) } rule expr() -> Expr = precedence! { + e1:(@) "=" _ e2:@ { Expr::Equal(Box::new(e1), Box::new(e2)) } + e1:(@) "<" _ e2:@ { Expr::Lt(Box::new(e1), Box::new(e2)) } + -- "-" _ e1:@ { Expr::UnaryMinus(Box::new(e1)) } -- e1:(@) "+" _ e2:@ { Expr::Plus(Box::new(e1), Box::new(e2)) } @@ -63,6 +69,7 @@ peg::parser! { -- e1:(@) "*" _ e2:@ { Expr::Mult(Box::new(e1), Box::new(e2)) } e1:(@) "/" _ e2:@ { Expr::Div(Box::new(e1), Box::new(e2)) } + e1:(@) "%" _ e2:@ { Expr::Mod(Box::new(e1), Box::new(e2)) } -- "(" _ e:expr() ")" _ { e } ['"'] s:$((!['"'] [_] / r#"\""#)*) ['"'] { Expr::Atom(Atom::String(s.to_string())) } @@ -87,7 +94,7 @@ peg::parser! { rule funcdef() -> Expr = i:id()? "->" _ b:(block()) { Expr::Funcdef(i, b) } rule conditional() -> Stmt - = i:_if() __* ei:elif()* __* e:_else()? __* { Stmt::Conditional([vec![i], ei].concat(), e) } + = i:_if() __* ei:elif()* e:_else()? __* { Stmt::Conditional([vec![i], ei].concat(), e) } rule _if() -> GuardedBlock = "if" _ g:expr() b:block() { GuardedBlock { @@ -96,7 +103,7 @@ peg::parser! { } } rule elif() -> GuardedBlock - = "elif" _ g:expr() b:block() { + = "elif" _ g:expr() b:block() __* { GuardedBlock { guard: g, block: b @@ -133,7 +140,7 @@ peg::parser! { } } -fn preprocess(input: &str) -> String { +pub fn preprocess(input: &str) -> String { let mut stack = vec![0]; let mut output = String::new(); for line in input.lines() { @@ -166,6 +173,12 @@ fn preprocess(input: &str) -> String { output.push_str(line.trim()); output.push('\n'); } + while stack.len() > 1 { + // place any dedents needed to balance the program + output.push_str("<<<"); + output.push('\n'); + stack.pop(); + } output }