diff --git a/examples/make_counter.puyo b/examples/make_counter.puyo index d865068..cac4e47 100644 --- a/examples/make_counter.puyo +++ b/examples/make_counter.puyo @@ -11,9 +11,9 @@ a := make_counter!; b := make_counter!; put 65; put 10; -println {^a}.a.inc!; -println a.inc!; -println a.inc!; +println {^x := {^a}.a.inc!}; +println {^x := a.inc!}; +println {^x := a.inc!}; put 66; put 10; println (b.inc!).i; diff --git a/examples/mandelbrot.puyo b/examples/mandelbrot.puyo new file mode 100644 index 0000000..e985f5b --- /dev/null +++ b/examples/mandelbrot.puyo @@ -0,0 +1,52 @@ +while := (); +fn while([test body]) ( + if test! [ + body!; + while test body; + ] []; +); + +fn for([start stop body]) ( + i := start; + while [i < stop] [ + body i; + i := i + 1; + ]; +); + +fn map([min0 max0 min1 max1 x]) ( + min1 + (((x - min0) * (max1 - min1)) / (max0 - min0)) +); + +xmin := -1.5; +xmax := 0.47; + +ymin := -1.12; +ymax := 1.12; + +char_width := 110; +char_height := 50; + +scale_x := map {^min0 := 0; ^max0 := char_width; ^min1 := xmin; ^max1 := xmax}; +scale_y := map {^min0 := 0; ^max0 := char_height; ^min1 := ymin; ^max1 := ymax}; + +iter_max := 26; + +# why is Y changing +for 0 char_height fn([y]) ( + for 0 char_width fn ([x]) ( + x0 := scale_x x; + y0 := scale_y y; + x := 0; + y_ := 0; + iter := 0; + while [(x**2 + y_**2 <= 2**2) * (iter < iter_max)] [ + xtemp := x**2 - y_**2 + x0; + y_ := 2*x*y_ + y0; + x := xtemp; + iter := iter + 1; + ]; + put (if (iter = iter_max) 32 (64 + iter)); + ); + put 10; +); diff --git a/src/ast.rs b/src/ast.rs index b1d671b..796b060 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -60,7 +60,7 @@ pub enum Ast { Fn { inputs: Vec, outputs: Vec, - body: Vec, + body: Box, }, /// `{^x; ^y := z}` Map(Vec), @@ -86,18 +86,13 @@ impl fmt::Display for Ast { outputs, body, } => { - let body: Vec<_> = body.iter().map(|stmt| format!("{stmt}")).collect(); - if body.is_empty() { - write!(f, "(fn ([{}] [{}]))", inputs.join(" "), outputs.join(" "),) - } else { - write!( - f, - "(fn ([{}] [{}]) {})", - inputs.join(" "), - outputs.join(" "), - body.join(" ") - ) - } + write!( + f, + "(fn ([{}] [{}]) {})", + inputs.join(" "), + outputs.join(" "), + body + ) } Ast::Map(body) => { let body: Vec<_> = body.iter().map(|stmt| format!("{stmt}")).collect(); diff --git a/src/eval/env.rs b/src/eval/env.rs index cffbe52..9148f16 100644 --- a/src/eval/env.rs +++ b/src/eval/env.rs @@ -15,6 +15,7 @@ impl Env { self.0.get(name).map(|c| c.borrow().clone()) } + /// Add the value to the map or update its value if it's already closed over pub fn set(&mut self, name: Symbol, val: Val) { match self.0.entry(name) { Entry::Occupied(ent) => { @@ -26,6 +27,11 @@ impl Env { } } + /// Unconditionally replace the value (so you don't update closed over stuff) + pub fn local_set(&mut self, name: Symbol, val: Val) { + self.0.insert(name, Rc::new(RefCell::new(val))); + } + pub fn freeze(self) -> Map { self.0 .into_iter() diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 841fdca..dbf6212 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -67,7 +67,7 @@ fn eval(expr: &Ast, local_env: &mut Env, return_env: &mut Env) -> Result { inputs, outputs, env, - body: FunctionBody::Ast(Ast::Block(body.clone())), + body: FunctionBody::Ast(*body.clone()), }))) } @@ -143,16 +143,18 @@ fn stdenv() -> Map { res.insert( "println".to_owned(), Val::prim(&["x"], &[], |local_env, _| { - println!("{}", local_env.get("x").unwrap()); - Ok(()) + let x = local_env.get("x").unwrap(); + println!("{x}"); + Ok(x) }), ); res.insert( "print".to_owned(), Val::prim(&["x"], &[], |local_env, _| { - print!("{}", local_env.get("x").unwrap()); - Ok(()) + let x = local_env.get("x").unwrap(); + print!("{x}"); + Ok(x) }), ); @@ -171,19 +173,18 @@ fn stdenv() -> Map { #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] let n = (n.abs().floor() % 256.) as u8; io::stdout().write_all(&[n]).unwrap(); - Ok(()) + Ok(Val::default()) }), ); res.insert( "if".to_owned(), - Val::prim(&["cond", "then", "else"], &["x"], |loc, ret| { + Val::prim(&["cond", "then", "else"], &["x"], |loc, _| { if loc.get("cond").unwrap() == Val::Number(0.0) { - ret.set("x".to_owned(), loc.get("else").unwrap().run()?); + loc.get("else").unwrap().run() } else { - ret.set("x".to_owned(), loc.get("then").unwrap().run()?); + loc.get("then").unwrap().run() } - Ok(()) }), ); @@ -246,7 +247,7 @@ impl Function { return self.run().map(Some); }; - self.env.set(var, x); + self.env.local_set(var, x); if self.inputs.is_empty() { self.run().map(Some) @@ -258,7 +259,7 @@ impl Function { self.inputs.retain(|name| !map.contains_key(name)); for (k, v) in map.into_iter() { - self.env.set(k, v); + self.env.local_set(k, v); } if self.inputs.is_empty() { @@ -274,13 +275,8 @@ impl Function { let mut return_env = self.env.clone(); match &self.body { - FunctionBody::Prim(f) => { - f(&mut self.env, &mut return_env)?; - } - FunctionBody::Ast(ast) => { - eval(ast, &mut self.env, &mut return_env)?; - } + FunctionBody::Prim(f) => f(&mut self.env, &mut return_env), + FunctionBody::Ast(ast) => eval(ast, &mut self.env, &mut return_env), } - Ok(Val::Map(Box::new(return_env.freeze()))) } } diff --git a/src/eval/val.rs b/src/eval/val.rs index faf5d80..c444f66 100644 --- a/src/eval/val.rs +++ b/src/eval/val.rs @@ -25,13 +25,22 @@ impl fmt::Display for Val { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Number(n) => write!(f, "{n}"), - // TODO: ok Self::Function(fun) => match &fun.body { FunctionBody::Prim(prim) => { - write!(f, "fn ([{:?} {:?}]) ({prim:?})", fun.inputs, fun.outputs) + write!( + f, + "fn ([{}] [{}]) ({prim:?})", + fun.inputs.join(" "), + fun.outputs.join(" ") + ) } FunctionBody::Ast(ast) => { - write!(f, "fn ([{:?} {:?}]) ({ast})", fun.inputs, fun.outputs) + write!( + f, + "fn ([{}] [{}]) {ast}", + fun.inputs.join(" "), + fun.outputs.join(" ") + ) } }, Self::Map(map) => write!(f, "{map:?}"), @@ -156,7 +165,7 @@ pub struct Function { pub(crate) env: Env, } -type PrimFn = fn(local_env: &mut Env, return_env: &mut Env) -> eval::Result<()>; +type PrimFn = fn(local_env: &mut Env, return_env: &mut Env) -> eval::Result; #[derive(Clone, Debug)] pub enum FunctionBody { diff --git a/src/parser.rs b/src/parser.rs index c82f379..73ea992 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,3 +1,4 @@ +// TODO: SPREAD SYNTAX FOR KWARGS I'M STUPID use std::{fmt, process}; use ariadne::{Color, Label, Report, ReportKind, Source}; @@ -11,9 +12,10 @@ use crate::ast::{Ast, BinOp, Ident}; use logos::Logos; +// TODO: comments at eof don't parse #[derive(Logos, Debug, Clone, PartialEq, Eq, Hash)] #[logos(skip r"[ \t\n\r\f]+")] -#[logos(skip r"#[^\n]*?\n")] +#[logos(skip r"#[^\n]*?(\n)")] enum Token<'a> { #[token("!")] Bang, @@ -147,6 +149,7 @@ where .allow_trailing() .collect() .delimited_by(just(Token::OpenParen), just(Token::CloseParen)) + .map(Ast::Block) .labelled("block"); let thunk = expr @@ -155,6 +158,11 @@ where .allow_trailing() .collect() .delimited_by(just(Token::OpenBracket), just(Token::CloseBracket)) + .map(|body| Ast::Fn { + inputs: vec![], + outputs: vec![], + body: Box::new(Ast::Block(body)), + }) .labelled("thunk"); let map = expr @@ -188,25 +196,27 @@ where .delimited_by(just(Token::OpenParen), just(Token::CloseParen)) .labelled("function signature"); + let func_body = choice((block.clone(), map.clone(), thunk.clone())); + let lambda = just(Token::Fn) .ignore_then(signature.clone()) - .then(block.clone()) + .then(func_body.clone()) .map(|((inputs, outputs), body)| Ast::Fn { inputs, outputs, - body, + body: Box::new(body), }); // this is purely syntax sugar let standalone_func = just(Token::Fn) .ignore_then(ident.clone()) .then(signature.clone()) - .then(block.clone()) + .then(func_body.clone()) .map(|((id, (inputs, outputs)), body)| { let lambda = Ast::Fn { inputs, outputs, - body, + body: Box::new(body), }; Ast::Set(id, Box::new(lambda)) }) @@ -219,12 +229,8 @@ where lambda, ident.map(Ast::Var).labelled("variable"), map, - block.map(Ast::Block), - thunk.map(|body| Ast::Fn { - inputs: vec![], - outputs: vec![], - body, - }), + block, + thunk, )) .boxed();