Remove objects, add some other stuff so that I can make fizzbuzz run in node
This commit is contained in:
parent
8259b57a46
commit
26bff70b1f
@ -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
9
demo/real.dee
Normal 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)
|
@ -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,29 +38,43 @@ 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;
|
||||||
|
ctx.is_tail = false;
|
||||||
|
if let Some((last, butlast)) = ast.split_last() {
|
||||||
|
for stmt in butlast {
|
||||||
emit(w, stmt, ctx)?;
|
emit(w, stmt, ctx)?;
|
||||||
}
|
}
|
||||||
|
ctx.is_tail = is_tail;
|
||||||
|
emit(w, last, ctx)?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
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, ";")?;
|
||||||
}
|
}
|
||||||
@ -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(())
|
||||||
|
14
src/main.rs
14
src/main.rs
@ -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 {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user