Remove objects, add some other stuff so that I can make fizzbuzz run in node

This commit is contained in:
Dane Johnson 2024-11-13 20:40:40 -06:00
parent 8259b57a46
commit 26bff70b1f
5 changed files with 95 additions and 23 deletions

View File

@ -7,8 +7,8 @@ foo <- -> bar() ## function definitions
foo <- -> foo <- ->
bar() bar()
baz() baz()
## functions with block statements # functions with block statements
foo <- x -> y -> x * y + 7 ## Multiple argument functions oo <- x -> y -> x * y + 7 ## Multiple argument functions
if test?() if test?()
do-something() do-something()
elif other-test?() elif other-test?()
@ -23,9 +23,3 @@ else
if second-test?() if second-test?()
do-a-real-bad-thing() do-a-real-bad-thing()
## Nested blocks ## Nested blocks
obj <- {
field <- "hello"
method <- x -> x * 2
}
## Object creation

9
demo/real.dee Normal file
View File

@ -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)

View File

@ -3,6 +3,10 @@ use std::collections::HashSet;
use crate::parser; use crate::parser;
fn munge(s: &str) -> String {
s.replace("?", "_INT_").replace("-", "_DASH_")
}
pub struct LexicalContext<'a> { pub struct LexicalContext<'a> {
parent: Option<&'a LexicalContext<'a>>, parent: Option<&'a LexicalContext<'a>>,
local: HashSet<String>, local: HashSet<String>,
@ -22,7 +26,7 @@ impl <'a> LexicalContext<'a> {
LexicalContext { LexicalContext {
parent: Some(parent), parent: Some(parent),
local: HashSet::new(), local: HashSet::new(),
is_tail: false, is_tail: parent.is_tail,
} }
} }
fn contains(&self, s: &str) -> bool { fn contains(&self, s: &str) -> bool {
@ -34,8 +38,14 @@ impl <'a> LexicalContext<'a> {
} }
pub fn emit_all(w: &mut dyn Write, ast: &Vec<parser::Stmt>, ctx: &mut LexicalContext) -> std::io::Result<()> { pub fn emit_all(w: &mut dyn Write, ast: &Vec<parser::Stmt>, ctx: &mut LexicalContext) -> std::io::Result<()> {
for stmt in ast { let is_tail = ctx.is_tail;
emit(w, stmt, ctx)?; 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(()) Ok(())
} }
@ -43,20 +53,28 @@ pub fn emit_all(w: &mut dyn Write, ast: &Vec<parser::Stmt>, ctx: &mut LexicalCon
pub fn emit(w: &mut dyn Write, stmt: &parser::Stmt, ctx: &mut LexicalContext) -> std::io::Result<()> { pub fn emit(w: &mut dyn Write, stmt: &parser::Stmt, ctx: &mut LexicalContext) -> std::io::Result<()> {
match &stmt { match &stmt {
parser::Stmt::BareExpr(expr) => { parser::Stmt::BareExpr(expr) => {
write!(w, "console.log((")?; if ctx.is_tail {
write!(w, "return ")?;
}
emit_expr(w, expr, ctx)?; emit_expr(w, expr, ctx)?;
writeln!(w, "));")?; writeln!(w, ";")?;
} }
parser::Stmt::Assignment(id, expr) => { parser::Stmt::Assignment(id, expr) => {
if !ctx.contains(id) { if !ctx.contains(id) {
ctx.insert(id.clone()); ctx.insert(id.clone());
write!(w, "let ")?; write!(w, "let ")?;
} }
write!(w, "{} = ", id)?; write!(w, "{} = ", munge(id))?;
emit_expr(w, expr, ctx)?; emit_expr(w, expr, ctx)?;
writeln!(w, ";")?; writeln!(w, ";")?;
if ctx.is_tail {
writeln!(w, "return;")?;
}
} }
parser::Stmt::Funcall(call) => { parser::Stmt::Funcall(call) => {
if ctx.is_tail {
write!(w, "return ")?;
}
emit_expr(w, call, ctx)?; emit_expr(w, call, ctx)?;
writeln!(w, ";")?; writeln!(w, ";")?;
} }
@ -66,7 +84,7 @@ pub fn emit(w: &mut dyn Write, stmt: &parser::Stmt, ctx: &mut LexicalContext) ->
if first_block { if first_block {
write!(w, "if (")?; write!(w, "if (")?;
} else { } else {
write!(w, " else if(")?; write!(w, "else if(")?;
} }
first_block = false; first_block = false;
emit_expr(w, &if_block.guard, ctx)?; 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<()> { pub fn emit_expr(w: &mut dyn Write, expr: &parser::Expr, ctx: &mut LexicalContext) -> std::io::Result<()> {
match &expr { match &expr {
parser::Expr::Id(id) => { parser::Expr::Id(id) => {
write!(w, "{}", id)?; write!(w, "{}", munge(id))?;
} }
parser::Expr::Atom(atom) => { parser::Expr::Atom(atom) => {
write!(w, "{}", atom)?; write!(w, "{}", atom)?;
} }
parser::Expr::Funcall(id, args) => { parser::Expr::Funcall(id, args) => {
write!(w, "{}", id)?; write!(w, "{}", munge(id))?;
if args.is_empty() { if args.is_empty() {
write!(w, "()")?; write!(w, "()")?;
} }
@ -121,6 +139,36 @@ pub fn emit_expr(w: &mut dyn Write, expr: &parser::Expr, ctx: &mut LexicalContex
write!(w, " + ")?; write!(w, " + ")?;
emit_expr(w, e2.as_ref(), ctx)?; 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!(), _ => todo!(),
} }
Ok(()) Ok(())

View File

@ -15,9 +15,11 @@ struct Cli {
#[clap(help="Specify a file to run")] #[clap(help="Specify a file to run")]
file: Option<PathBuf>, file: Option<PathBuf>,
#[clap(short, long, help="Only parse, do not evaluate")] #[clap(short, long, help="Only parse, do not evaluate")]
parse_only: bool, parse: bool,
#[clap(short, long, help="Cross-compile to ECMAScript")] #[clap(short, long, help="Cross-compile to ECMAScript")]
ecmascript: bool, ecmascript: bool,
#[clap(long, help="Only run the pre-processor")]
preprocess: bool,
} }
fn repl(cli: &Cli) { fn repl(cli: &Cli) {
@ -28,7 +30,7 @@ fn repl(cli: &Cli) {
let mut line = String::new(); let mut line = String::new();
io::stdin().read_line(&mut line).unwrap(); io::stdin().read_line(&mut line).unwrap();
let tree = parser::parse_stmt(&line); let tree = parser::parse_stmt(&line);
if cli.parse_only { if cli.parse {
println!("{:#?}", tree); println!("{:#?}", tree);
} else if cli.ecmascript { } else if cli.ecmascript {
emitter::emit(&mut out, &tree, &mut toplevel).ok(); 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 file = File::open(cli.file.as_ref().unwrap()).expect("Could not read file");
let mut prgm = String::new(); let mut prgm = String::new();
file.read_to_string(&mut prgm).unwrap(); file.read_to_string(&mut prgm).unwrap();
if cli.preprocess {
println!("{}", parser::preprocess(&prgm));
return;
}
let tree = parser::parse(&prgm); let tree = parser::parse(&prgm);
if cli.parse_only { if cli.parse {
println!("{:#?}", tree); println!("{:#?}", tree);
} else if cli.ecmascript { } else if cli.ecmascript {
let mut out = io::stdout(); let mut out = io::stdout();
// TODO remove this hack
writeln!(out, "const print = console.log;").ok();
let mut toplevel = emitter::LexicalContext::toplevel(); let mut toplevel = emitter::LexicalContext::toplevel();
emitter::emit_all(&mut out, &tree, &mut toplevel).ok(); emitter::emit_all(&mut out, &tree, &mut toplevel).ok();
} else { } else {

View File

@ -19,6 +19,9 @@ pub enum Expr {
Minus(Box<Expr>, Box<Expr>), Minus(Box<Expr>, Box<Expr>),
Mult(Box<Expr>, Box<Expr>), Mult(Box<Expr>, Box<Expr>),
Div(Box<Expr>, Box<Expr>), Div(Box<Expr>, Box<Expr>),
Mod(Box<Expr>, Box<Expr>),
Equal(Box<Expr>, Box<Expr>),
Lt(Box<Expr>, Box<Expr>),
} }
pub type Block = Vec<Stmt>; pub type Block = Vec<Stmt>;
@ -56,6 +59,9 @@ peg::parser! {
c:conditional() { c } / c:conditional() { c } /
e:expr() stop() { Stmt::BareExpr(e) } e:expr() stop() { Stmt::BareExpr(e) }
rule expr() -> Expr = precedence! { 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:@ { Expr::UnaryMinus(Box::new(e1)) }
-- --
e1:(@) "+" _ e2:@ { Expr::Plus(Box::new(e1), Box::new(e2)) } 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::Mult(Box::new(e1), Box::new(e2)) }
e1:(@) "/" _ e2:@ { Expr::Div(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 } "(" _ e:expr() ")" _ { e }
['"'] s:$((!['"'] [_] / r#"\""#)*) ['"'] { Expr::Atom(Atom::String(s.to_string())) } ['"'] s:$((!['"'] [_] / r#"\""#)*) ['"'] { Expr::Atom(Atom::String(s.to_string())) }
@ -87,7 +94,7 @@ peg::parser! {
rule funcdef() -> Expr rule funcdef() -> Expr
= i:id()? "->" _ b:(block()) { Expr::Funcdef(i, b) } = i:id()? "->" _ b:(block()) { Expr::Funcdef(i, b) }
rule conditional() -> Stmt 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 rule _if() -> GuardedBlock
= "if" _ g:expr() b:block() { = "if" _ g:expr() b:block() {
GuardedBlock { GuardedBlock {
@ -96,7 +103,7 @@ peg::parser! {
} }
} }
rule elif() -> GuardedBlock rule elif() -> GuardedBlock
= "elif" _ g:expr() b:block() { = "elif" _ g:expr() b:block() __* {
GuardedBlock { GuardedBlock {
guard: g, guard: g,
block: b 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 stack = vec![0];
let mut output = String::new(); let mut output = String::new();
for line in input.lines() { for line in input.lines() {
@ -166,6 +173,12 @@ fn preprocess(input: &str) -> String {
output.push_str(line.trim()); output.push_str(line.trim());
output.push('\n'); output.push('\n');
} }
while stack.len() > 1 {
// place any dedents needed to balance the program
output.push_str("<<<");
output.push('\n');
stack.pop();
}
output output
} }