Compare commits

..

No commits in common. "b2584ca1fc7ffc3686101addb8e5e87130b49f8b" and "67e79b18a8e5b0ed64769c002624f4cd63116459" have entirely different histories.

8 changed files with 50 additions and 176 deletions

View file

@ -1,24 +0,0 @@
fn make_counter([] [inc]) (
i := 0;
fn ^inc([] [i]) (
i := i + 1;
^i := i;
);
);
a := make_counter!;
b := make_counter!;
put 65; put 10;
println {^x := {^a}.a.inc!};
println {^x := a.inc!};
println {^x := a.inc!};
put 66; put 10;
println (b.inc!).i;
println (b.inc!).i;
println (b.inc!).i;
# TODO: comments at eof don't parse
println 42;

View file

@ -1,52 +0,0 @@
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;
);

View file

@ -60,7 +60,7 @@ pub enum Ast {
Fn { Fn {
inputs: Vec<Symbol>, inputs: Vec<Symbol>,
outputs: Vec<Symbol>, outputs: Vec<Symbol>,
body: Box<Ast>, body: Vec<Ast>,
}, },
/// `{^x; ^y := z}` /// `{^x; ^y := z}`
Map(Vec<Ast>), Map(Vec<Ast>),
@ -86,14 +86,19 @@ impl fmt::Display for Ast {
outputs, outputs,
body, body,
} => { } => {
let body: Vec<_> = body.iter().map(|stmt| format!("{stmt}")).collect();
if body.is_empty() {
write!(f, "(fn ([{}] [{}]))", inputs.join(" "), outputs.join(" "),)
} else {
write!( write!(
f, f,
"(fn ([{}] [{}]) {})", "(fn ([{}] [{}]) {})",
inputs.join(" "), inputs.join(" "),
outputs.join(" "), outputs.join(" "),
body body.join(" ")
) )
} }
}
Ast::Map(body) => { Ast::Map(body) => {
let body: Vec<_> = body.iter().map(|stmt| format!("{stmt}")).collect(); let body: Vec<_> = body.iter().map(|stmt| format!("{stmt}")).collect();
write!(f, "{{{}}}", body.join(" ")) write!(f, "{{{}}}", body.join(" "))

View file

@ -15,7 +15,6 @@ impl Env {
self.0.get(name).map(|c| c.borrow().clone()) 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) { pub fn set(&mut self, name: Symbol, val: Val) {
match self.0.entry(name) { match self.0.entry(name) {
Entry::Occupied(ent) => { Entry::Occupied(ent) => {
@ -27,11 +26,6 @@ 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<Val> { pub fn freeze(self) -> Map<Val> {
self.0 self.0
.into_iter() .into_iter()

View file

@ -67,7 +67,7 @@ fn eval(expr: &Ast, local_env: &mut Env, return_env: &mut Env) -> Result<Val> {
inputs, inputs,
outputs, outputs,
env, env,
body: FunctionBody::Ast(*body.clone()), body: FunctionBody::Ast(Ast::Block(body.clone())),
}))) })))
} }
@ -143,18 +143,16 @@ fn stdenv() -> Map<Val> {
res.insert( res.insert(
"println".to_owned(), "println".to_owned(),
Val::prim(&["x"], &[], |local_env, _| { Val::prim(&["x"], &[], |local_env, _| {
let x = local_env.get("x").unwrap(); println!("{}", local_env.get("x").unwrap());
println!("{x}"); Ok(())
Ok(x)
}), }),
); );
res.insert( res.insert(
"print".to_owned(), "print".to_owned(),
Val::prim(&["x"], &[], |local_env, _| { Val::prim(&["x"], &[], |local_env, _| {
let x = local_env.get("x").unwrap(); print!("{}", local_env.get("x").unwrap());
print!("{x}"); Ok(())
Ok(x)
}), }),
); );
@ -173,18 +171,19 @@ fn stdenv() -> Map<Val> {
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
let n = (n.abs().floor() % 256.) as u8; let n = (n.abs().floor() % 256.) as u8;
io::stdout().write_all(&[n]).unwrap(); io::stdout().write_all(&[n]).unwrap();
Ok(Val::default()) Ok(())
}), }),
); );
res.insert( res.insert(
"if".to_owned(), "if".to_owned(),
Val::prim(&["cond", "then", "else"], &["x"], |loc, _| { Val::prim(&["cond", "then", "else"], &["x"], |loc, ret| {
if loc.get("cond").unwrap() == Val::Number(0.0) { if loc.get("cond").unwrap() == Val::Number(0.0) {
loc.get("else").unwrap().run() ret.set("x".to_owned(), loc.get("else").unwrap().run()?);
} else { } else {
loc.get("then").unwrap().run() ret.set("x".to_owned(), loc.get("then").unwrap().run()?);
} }
Ok(())
}), }),
); );
@ -247,7 +246,7 @@ impl Function {
return self.run().map(Some); return self.run().map(Some);
}; };
self.env.local_set(var, x); self.env.set(var, x);
if self.inputs.is_empty() { if self.inputs.is_empty() {
self.run().map(Some) self.run().map(Some)
@ -259,7 +258,7 @@ impl Function {
self.inputs.retain(|name| !map.contains_key(name)); self.inputs.retain(|name| !map.contains_key(name));
for (k, v) in map.into_iter() { for (k, v) in map.into_iter() {
self.env.local_set(k, v); self.env.set(k, v);
} }
if self.inputs.is_empty() { if self.inputs.is_empty() {
@ -275,8 +274,13 @@ impl Function {
let mut return_env = self.env.clone(); let mut return_env = self.env.clone();
match &self.body { match &self.body {
FunctionBody::Prim(f) => f(&mut self.env, &mut return_env), FunctionBody::Prim(f) => {
FunctionBody::Ast(ast) => eval(ast, &mut self.env, &mut return_env), 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())))
}
} }

View file

@ -25,22 +25,13 @@ impl fmt::Display for Val {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::Number(n) => write!(f, "{n}"), Self::Number(n) => write!(f, "{n}"),
// TODO: ok
Self::Function(fun) => match &fun.body { Self::Function(fun) => match &fun.body {
FunctionBody::Prim(prim) => { FunctionBody::Prim(prim) => {
write!( write!(f, "fn ([{:?} {:?}]) ({prim:?})", fun.inputs, fun.outputs)
f,
"fn ([{}] [{}]) ({prim:?})",
fun.inputs.join(" "),
fun.outputs.join(" ")
)
} }
FunctionBody::Ast(ast) => { FunctionBody::Ast(ast) => {
write!( write!(f, "fn ([{:?} {:?}]) ({ast})", fun.inputs, fun.outputs)
f,
"fn ([{}] [{}]) {ast}",
fun.inputs.join(" "),
fun.outputs.join(" ")
)
} }
}, },
Self::Map(map) => write!(f, "{map:?}"), Self::Map(map) => write!(f, "{map:?}"),
@ -165,7 +156,7 @@ pub struct Function {
pub(crate) env: Env, pub(crate) env: Env,
} }
type PrimFn = fn(local_env: &mut Env, return_env: &mut Env) -> eval::Result<Val>; type PrimFn = fn(local_env: &mut Env, return_env: &mut Env) -> eval::Result<()>;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum FunctionBody { pub enum FunctionBody {

View file

@ -1,4 +1,3 @@
// TODO: SPREAD SYNTAX FOR KWARGS I'M STUPID
use std::{fmt, process}; use std::{fmt, process};
use ariadne::{Color, Label, Report, ReportKind, Source}; use ariadne::{Color, Label, Report, ReportKind, Source};
@ -12,14 +11,10 @@ use crate::ast::{Ast, BinOp, Ident};
use logos::Logos; use logos::Logos;
// TODO: comments at eof don't parse
#[derive(Logos, Debug, Clone, PartialEq, Eq, Hash)] #[derive(Logos, Debug, Clone, PartialEq, Eq, Hash)]
#[logos(skip r"[ \t\n\r\f]+")] #[logos(skip r"[ \t\n\r\f]+")]
#[logos(skip r"#[^\n]*?(\n)")] #[logos(skip r"#[^\n]*?\n")]
enum Token<'a> { enum Token<'a> {
#[token("!")]
Bang,
#[token(":")] #[token(":")]
Colon, Colon,
@ -71,7 +66,6 @@ enum Token<'a> {
impl fmt::Display for Token<'_> { impl fmt::Display for Token<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Token::Bang => write!(f, "!"),
Token::Colon => write!(f, ":"), Token::Colon => write!(f, ":"),
Token::Period => write!(f, "."), Token::Period => write!(f, "."),
Token::Comma => write!(f, ","), Token::Comma => write!(f, ","),
@ -149,7 +143,6 @@ where
.allow_trailing() .allow_trailing()
.collect() .collect()
.delimited_by(just(Token::OpenParen), just(Token::CloseParen)) .delimited_by(just(Token::OpenParen), just(Token::CloseParen))
.map(Ast::Block)
.labelled("block"); .labelled("block");
let thunk = expr let thunk = expr
@ -158,11 +151,6 @@ where
.allow_trailing() .allow_trailing()
.collect() .collect()
.delimited_by(just(Token::OpenBracket), just(Token::CloseBracket)) .delimited_by(just(Token::OpenBracket), just(Token::CloseBracket))
.map(|body| Ast::Fn {
inputs: vec![],
outputs: vec![],
body: Box::new(Ast::Block(body)),
})
.labelled("thunk"); .labelled("thunk");
let map = expr let map = expr
@ -196,27 +184,25 @@ where
.delimited_by(just(Token::OpenParen), just(Token::CloseParen)) .delimited_by(just(Token::OpenParen), just(Token::CloseParen))
.labelled("function signature"); .labelled("function signature");
let func_body = choice((block.clone(), map.clone(), thunk.clone()));
let lambda = just(Token::Fn) let lambda = just(Token::Fn)
.ignore_then(signature.clone()) .ignore_then(signature.clone())
.then(func_body.clone()) .then(block.clone())
.map(|((inputs, outputs), body)| Ast::Fn { .map(|((inputs, outputs), body)| Ast::Fn {
inputs, inputs,
outputs, outputs,
body: Box::new(body), body,
}); });
// this is purely syntax sugar // this is purely syntax sugar
let standalone_func = just(Token::Fn) let standalone_func = just(Token::Fn)
.ignore_then(ident.clone()) .ignore_then(ident.clone())
.then(signature.clone()) .then(signature.clone())
.then(func_body.clone()) .then(block.clone())
.map(|((id, (inputs, outputs)), body)| { .map(|((id, (inputs, outputs)), body)| {
let lambda = Ast::Fn { let lambda = Ast::Fn {
inputs, inputs,
outputs, outputs,
body: Box::new(body), body,
}; };
Ast::Set(id, Box::new(lambda)) Ast::Set(id, Box::new(lambda))
}) })
@ -229,8 +215,12 @@ where
lambda, lambda,
ident.map(Ast::Var).labelled("variable"), ident.map(Ast::Var).labelled("variable"),
map, map,
block, block.map(Ast::Block),
thunk, thunk.map(|body| Ast::Fn {
inputs: vec![],
outputs: vec![],
body,
}),
)) ))
.boxed(); .boxed();
@ -244,20 +234,14 @@ where
let e2 = access.or(e1); let e2 = access.or(e1);
let bang = e2.clone().foldl(just(Token::Bang).repeated(), |thunk, _| { let app = e2
Ast::App(Box::new(thunk), Box::new(Ast::Block(vec![])))
});
let e3 = bang.or(e2);
let app = e3
.clone() .clone()
.foldl(e3.clone().repeated(), |f, x| { .foldl(e2.clone().repeated(), |f, x| {
Ast::App(Box::new(f), Box::new(x)) Ast::App(Box::new(f), Box::new(x))
}) })
.labelled("function application"); .labelled("function application");
let atom = app.or(e3); let atom = app.or(e2);
atom.pratt(( atom.pratt((
infix(none(1), just(Token::BinOp("=")), |l, _, r, _| { infix(none(1), just(Token::BinOp("=")), |l, _, r, _| {

View file

@ -215,31 +215,3 @@ if [wow = cool]() [
", ",
); );
} }
#[test]
fn bang() {
test(
"
a := make_counter!;
b := make_counter!;
println {^a}.a.inc!;
println a.inc!;
println a.inc!;
println b.inc!;
println b.inc!;
println b.inc!;
",
"
(set! a (make_counter ()));
(set! b (make_counter ()));
(println ({^a}.a.inc ()));
(println (a.inc ()));
(println (a.inc ()));
(println (b.inc ()));
(println (b.inc ()));
(println (b.inc ()))
",
);
}