Compare commits
No commits in common. "b2584ca1fc7ffc3686101addb8e5e87130b49f8b" and "67e79b18a8e5b0ed64769c002624f4cd63116459" have entirely different histories.
b2584ca1fc
...
67e79b18a8
8 changed files with 50 additions and 176 deletions
|
|
@ -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;
|
|
||||||
|
|
@ -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;
|
|
||||||
);
|
|
||||||
|
|
@ -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(" "))
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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, _| {
|
||||||
|
|
|
||||||
|
|
@ -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 ()))
|
|
||||||
",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue