updates and lexing

This commit is contained in:
me 2025-12-13 23:46:36 +02:00
parent fb3f384c84
commit c66a193a6e
13 changed files with 746 additions and 447 deletions

201
Cargo.lock generated
View file

@ -59,7 +59,7 @@ version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [ dependencies = [
"windows-sys", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
@ -70,7 +70,16 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"once_cell_polyfill", "once_cell_polyfill",
"windows-sys", "windows-sys 0.61.2",
]
[[package]]
name = "ar_archive_writer"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0c269894b6fe5e9d7ada0cf69b5bf847ff35bc25fc271f08e1d080fce80339a"
dependencies = [
"object",
] ]
[[package]] [[package]]
@ -83,8 +92,10 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
name = "ayin-lang" name = "ayin-lang"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"chumsky",
"env_logger", "env_logger",
"log", "log",
"lyn",
"macroquad", "macroquad",
"thiserror", "thiserror",
] ]
@ -107,12 +118,36 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cc"
version = "1.2.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.4" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chumsky"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acc17a6284abccac6e50db35c1cee87f605474a72939b959a3a67d9371800efd"
dependencies = [
"hashbrown",
"regex-automata 0.3.9",
"serde",
"stacker",
"unicode-ident",
"unicode-segmentation",
]
[[package]] [[package]]
name = "color_quant" name = "color_quant"
version = "1.1.0" version = "1.1.0"
@ -172,6 +207,12 @@ dependencies = [
"simd-adler32", "simd-adler32",
] ]
[[package]]
name = "find-msvc-tools"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.1.5" version = "1.1.5"
@ -270,6 +311,12 @@ version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "lyn"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f08d9299a146aa1eb3c5451e6d9045708f232a43fd8d4436f69cf00fcb2a019c"
[[package]] [[package]]
name = "macroquad" name = "macroquad"
version = "0.4.14" version = "0.4.14"
@ -351,6 +398,15 @@ dependencies = [
"malloc_buf", "malloc_buf",
] ]
[[package]]
name = "object"
version = "0.32.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "once_cell_polyfill" name = "once_cell_polyfill"
version = "1.70.2" version = "1.70.2"
@ -394,6 +450,16 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "psm"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d11f2fedc3b7dafdc2851bc52f277377c5473d378859be234bc7ebb593144d01"
dependencies = [
"ar_archive_writer",
"cc",
]
[[package]] [[package]]
name = "quad-rand" name = "quad-rand"
version = "0.2.3" version = "0.2.3"
@ -417,8 +483,19 @@ checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
"regex-automata", "regex-automata 0.4.13",
"regex-syntax", "regex-syntax 0.8.8",
]
[[package]]
name = "regex-automata"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax 0.7.5",
] ]
[[package]] [[package]]
@ -429,15 +506,31 @@ checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
"regex-syntax", "regex-syntax 0.8.8",
] ]
[[package]]
name = "regex-syntax"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.8.8" version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]] [[package]]
name = "serde_core" name = "serde_core"
version = "1.0.228" version = "1.0.228"
@ -458,12 +551,31 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "simd-adler32" name = "simd-adler32"
version = "0.3.8" version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
[[package]]
name = "stacker"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59"
dependencies = [
"cc",
"cfg-if",
"libc",
"psm",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.111" version = "2.0.111"
@ -507,6 +619,12 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "unicode-segmentation"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.2" version = "0.2.2"
@ -541,6 +659,15 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.61.2" version = "0.61.2"
@ -549,3 +676,67 @@ checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [ dependencies = [
"windows-link", "windows-link",
] ]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

View file

@ -21,6 +21,8 @@ thiserror = "1"
env_logger = "0.11.8" env_logger = "0.11.8"
log = "0.4.27" log = "0.4.27"
macroquad = "0.4.14" macroquad = "0.4.14"
lyn = "0.1.0"
chumsky = "0.11.2"
[workspace.lints.clippy] [workspace.lints.clippy]
all = { level = "warn", priority = -1 } all = { level = "warn", priority = -1 }

View file

@ -2,6 +2,10 @@
watch: watch:
cargo watch --clear -x 'clippy' -x 'test --all' cargo watch --clear -x 'clippy' -x 'test --all'
.PHONY: build
build:
cargo build
.PHONY: run .PHONY: run
run: run:
cargo run cargo run

View file

@ -13,21 +13,21 @@ Ayin
- first class functions - first class functions
``` ```
fn init() { let init = fn() {
return { return {
player: { position: { x: 10, y: 20 }, }, player: { position: { x: 10, y: 20 }, },
} }
} }
fn update(state, events) { let update = fn(state, events) {
return state'; return state';
} }
fn draw(frame, state) { let draw = fn(frame, state) {
frame.clear(0,0,0); frame.clear(0,0,0);
} }
fn migrate(state) { let migrate = fn(state) {
return { player: { pos: state.player.position } }, return { player: { pos: state.player.position } },
} }
``` ```

View file

@ -12,7 +12,7 @@ pub fn var(str: &str) -> Expr {
Expr::Var(name(str)) Expr::Var(name(str))
} }
pub fn record(rec: Vec<(&str, Value)>) -> Value { pub fn record(rec: Vec<(&str, Expr)>) -> Value {
Value::Record(Record({ Value::Record(Record({
rec.into_iter() rec.into_iter()
.map(|(lbl, val)| (Label(lbl.into()), val)) .map(|(lbl, val)| (Label(lbl.into()), val))
@ -20,15 +20,12 @@ pub fn record(rec: Vec<(&str, Value)>) -> Value {
})) }))
} }
pub fn record_expr(rec: Vec<(&str, Value)>) -> Expr { pub fn record_expr(rec: Vec<(&str, Expr)>) -> Expr {
Expr::Value(record(rec)) Expr::Value(record(rec))
} }
pub fn assign(name: &str, expr: Expr) -> Statement { pub fn assign(name: &str, expr: Expr) -> Statement {
Statement::Assign { Statement::Assign(define_expr(name, expr))
name: name.into(),
expr,
}
} }
pub fn stmt_expr(expr: Expr) -> Statement { pub fn stmt_expr(expr: Expr) -> Statement {
@ -36,7 +33,7 @@ pub fn stmt_expr(expr: Expr) -> Statement {
} }
pub fn define_expr(name: &str, expr: Expr) -> Definition { pub fn define_expr(name: &str, expr: Expr) -> Definition {
Definition::Expr { Definition {
name: name.into(), name: name.into(),
expr, expr,
} }

View file

@ -7,15 +7,6 @@ use std::collections::BTreeMap;
pub enum Expr { pub enum Expr {
Value(Value), Value(Value),
Var(Name), Var(Name),
Access {
expr: Box<Expr>,
field: Label,
},
MethodCall {
expr: Box<Expr>,
method: MethodName,
args: Vec<Expr>,
},
Func(Box<Fn>), Func(Box<Fn>),
FunCall { FunCall {
func: Box<Expr>, func: Box<Expr>,
@ -27,13 +18,31 @@ pub enum Expr {
then: Box<Expr>, then: Box<Expr>,
r#else: Box<Expr>, r#else: Box<Expr>,
}, },
Access {
expr: Box<Expr>,
field: Label,
},
/*
Loop {
body: Box<Expr>,
},
Continue,
Break,
Return(Box<Expr>),
*/
} }
/// A statement. /// A statement.
#[derive(PartialEq, PartialOrd, Debug, Clone)] #[derive(PartialEq, PartialOrd, Debug, Clone)]
pub enum Statement { pub enum Statement {
Expr(Expr), Expr(Expr),
Assign { name: Name, expr: Expr }, Assign(Definition),
}
#[derive(PartialEq, PartialOrd, Debug, Clone)]
pub struct Definition {
pub name: Name,
pub expr: Expr,
} }
/// A reduced value. /// A reduced value.
@ -44,8 +53,8 @@ pub enum Value {
String(String), String(String),
Boolean(bool), Boolean(bool),
Record(Record), Record(Record),
Closure { func: Box<Fn>, env: EnvName }, Vector(Vector),
Deferred { expr: Box<Expr>, env: EnvName }, Closure { env: EnvName, expr: Box<Expr> },
} }
/// An anonymous function. /// An anonymous function.
@ -59,28 +68,12 @@ pub struct Fn {
#[derive(PartialEq, PartialOrd, Debug, Clone)] #[derive(PartialEq, PartialOrd, Debug, Clone)]
pub struct Arg { pub struct Arg {
pub name: Name, pub name: Name,
pub r#type: Type,
}
/// A type.
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone)]
pub enum Type {
Named(TypeName),
App { typefun: Name, args: Vec<Type> },
} }
/// A symbol. /// A symbol.
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone)] #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone)]
pub struct Name(pub String); pub struct Name(pub String);
/// A type name.
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone)]
pub struct TypeName(pub String);
/// A method name.
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone)]
pub struct MethodName(pub String);
/// A field. /// A field.
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone)] #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone)]
pub struct Label(pub String); pub struct Label(pub String);
@ -90,50 +83,25 @@ pub struct Label(pub String);
pub struct EnvName(pub String); pub struct EnvName(pub String);
#[derive(PartialEq, PartialOrd, Debug, Clone)] #[derive(PartialEq, PartialOrd, Debug, Clone)]
pub struct Record(pub BTreeMap<Label, Value>); pub struct Record(pub BTreeMap<Label, Expr>);
#[derive(PartialEq, PartialOrd, Debug, Clone)]
pub struct Vector(pub Vec<Expr>);
/// A Program. /// A Program.
#[derive(PartialEq, PartialOrd, Debug)] #[derive(PartialEq, PartialOrd, Debug)]
pub struct Program(pub Vec<Definition>); pub struct Program(pub Vec<Definition>);
/// A definition.
#[derive(PartialEq, PartialOrd, Debug)]
pub enum Definition {
Function { name: Name, func: Fn },
Expr { name: Name, expr: Expr },
}
// ----- // // ----- //
// Impls // // Impls //
// ----- // // ----- //
impl Definition {
pub fn name(&self) -> &Name {
match self {
Definition::Function { name, .. } => name,
Definition::Expr { name, .. } => name,
}
}
}
impl From<&str> for Name { impl From<&str> for Name {
fn from(value: &str) -> Name { fn from(value: &str) -> Name {
Name(value.into()) Name(value.into())
} }
} }
impl From<&str> for TypeName {
fn from(value: &str) -> TypeName {
TypeName(value.into())
}
}
impl From<&str> for MethodName {
fn from(value: &str) -> MethodName {
MethodName(value.into())
}
}
impl From<&str> for Label { impl From<&str> for Label {
fn from(value: &str) -> Label { fn from(value: &str) -> Label {
Label(value.into()) Label(value.into())
@ -146,26 +114,23 @@ impl From<&str> for Expr {
} }
} }
impl From<Vec<(&str, Value)>> for Value { impl From<Vec<(&str, Expr)>> for Value {
fn from(rec: Vec<(&str, Value)>) -> Value { fn from(rec: Vec<(&str, Expr)>) -> Value {
Value::Record(Record({ Value::Record(Record({
rec.into_iter() rec.into_iter()
.map(|(lbl, val)| (Label(lbl.into()), val)) .map(|(lbl, expr)| (Label(lbl.into()), expr))
.collect() .collect()
})) }))
} }
} }
impl From<Vec<(&str, Value)>> for Expr { impl From<Vec<(&str, Expr)>> for Expr {
fn from(rec: Vec<(&str, Value)>) -> Expr { fn from(rec: Vec<(&str, Expr)>) -> Expr {
Expr::Value(rec.into()) Expr::Value(rec.into())
} }
} }
impl Expr { impl Expr {
pub fn get_type(&self) -> &Type {
todo!()
}
pub fn field(self, label: &str) -> Expr { pub fn field(self, label: &str) -> Expr {
Expr::Access { Expr::Access {
expr: Box::new(self), expr: Box::new(self),
@ -217,7 +182,7 @@ impl From<Vec<Statement>> for Expr {
} }
impl Record { impl Record {
pub fn get(&self, label: &Label) -> Result<&Value, Error> { pub fn get(&self, label: &Label) -> Result<&Expr, Error> {
match self.0.get(label) { match self.0.get(label) {
Some(v) => Ok(v), Some(v) => Ok(v),
None => Err(Error::LabelNotFound(label.clone())), None => Err(Error::LabelNotFound(label.clone())),

292
src/interpret/interpret.rs Normal file
View file

@ -0,0 +1,292 @@
//! Interpreter for Ayin.
use super::types::*;
use crate::ast;
pub fn run(
program: ast::Program,
func: ast::Name,
args: Vec<ast::Expr>,
) -> Result<ast::Value, Error> {
let env_name = ast::EnvName("global".to_string());
let mut state = State::new("global".into());
defs_to_env(program.0, &env_name, &mut state.envs)?;
let main = ast::Expr::Value(state.envs.get(&env_name)?.get(&func)?.clone());
let expr = ast::Expr::FunCall {
func: Box::new(main),
args,
};
let env: Env = state.envs.get(&env_name)?.clone();
let result = eval_expr(&env, &mut state, &expr)?;
Ok(result)
}
fn eval_statement(
expr_env: &mut Env,
state: &mut State,
statement: &ast::Statement,
) -> Result<ast::Value, Error> {
match statement {
ast::Statement::Expr(expr) => eval_expr(expr_env, state, expr),
ast::Statement::Assign(ast::Definition { name, expr }) => {
let result = eval_expr(expr_env, state, expr)?;
expr_env.insert(name.clone(), result.clone());
Ok(result)
}
}
}
fn eval_expr(expr_env: &Env, state: &mut State, expr: &ast::Expr) -> Result<ast::Value, Error> {
match expr {
ast::Expr::If {
condition,
then,
r#else,
} => match eval_expr(expr_env, state, condition)? {
ast::Value::Boolean(condition) => {
if condition {
eval_expr(expr_env, state, then)
} else {
eval_expr(expr_env, state, r#else)
}
}
v => Err(Error::NotABoolean(v)),
},
ast::Expr::Func(func) => {
let env_name = state.generate_env(expr_env.clone())?;
Ok(ast::Value::Closure {
expr: Box::new(ast::Expr::Func(func.clone())),
env: env_name,
})
}
ast::Expr::Block(statements) => {
let mut block_env = expr_env.clone();
if let Some(last) = statements.iter().try_fold(None, |_, statement| {
let result = eval_statement(&mut block_env, state, statement)?;
Ok::<Option<ast::Value>, Error>(Some(result))
})? {
Ok(last)
} else {
Err(Error::LastStatementNotAnExpr)
}
}
ast::Expr::Access { expr, field } => match eval_expr(expr_env, state, expr)? {
ast::Value::Record(record) => eval_expr(
expr_env,
state,
&record.get(field).map_err(Error::from)?.clone(),
),
v => Err(Error::NotARecord(v)),
},
ast::Expr::Var(var) => {
let value = expr_env.get(var)?.clone();
eval_expr(expr_env, state, &ast::Expr::Value(value))
}
ast::Expr::FunCall { func, args } => match eval_expr(expr_env, state, func)? {
ast::Value::Closure { expr, env } => match *expr {
ast::Expr::Func(func) => {
let mut closure_env: Env = state.envs.get(&env)?.clone();
if func.args.len() != args.len() {
Err(Error::ArgumentsMismatch)?;
}
for (arg, e) in func.args.into_iter().zip(args.iter()) {
let evalled = eval_expr(&closure_env, state, e)?;
closure_env.0.insert(arg.name, evalled);
}
eval_expr(&closure_env, state, &func.body)
}
e => Err(Error::NotAFunction(e)),
},
e => Err(Error::NotAFunction(ast::Expr::Value(e))),
},
ast::Expr::Value(v) => match v {
ast::Value::Closure { expr, env } => {
let closure_env: Env = state.envs.get(env)?.clone();
eval_expr(&closure_env, state, expr)
}
_ => Ok(v.clone()),
},
}
}
fn defs_to_env(
defs: Vec<ast::Definition>,
env_name: &ast::EnvName,
envs: &mut Envs,
) -> Result<(), Error> {
let mut env = Env::new();
for def in defs {
let (name, closure) = match def {
ast::Definition { expr, name } => (
name,
ast::Value::Closure {
expr: Box::new(expr.clone()),
env: env_name.clone(),
},
),
};
env.insert_nodup(&name, closure)?;
}
envs.0.insert(env_name.clone(), env);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::helpers;
#[test]
fn main_0() {
let program = vec![helpers::define_expr("main", 0.into())].into();
let result = run(program);
assert_eq!(result, Ok(0.into()));
}
#[test]
fn var_lookup() {
let program = vec![
helpers::define_expr("main", "lit".into()),
helpers::define_expr("lit", 0.into()),
]
.into();
let result = run(program);
assert_eq!(result, Ok(0.into()));
}
#[test]
fn var_assign_and_lookup() {
let program = vec![helpers::define_expr(
"main",
vec![
helpers::assign("zero", 0.into()),
helpers::stmt_expr("zero".into()),
]
.into(),
)]
.into();
let result = run(program);
assert_eq!(result, Ok(0.into()));
}
#[test]
fn field_access() {
let program = vec![
helpers::define_expr("main", "record".into()),
helpers::define_expr(
"record",
ast::Expr::from(vec![("my_field", 0.into())]).field("my_field"),
),
]
.into();
let result = run(program);
assert_eq!(result, Ok(0.into()));
}
#[test]
fn fun_call() {
let program = vec![
helpers::define_expr("main", "zero".into()),
helpers::define_expr(
"zero",
helpers::func(ast::Fn {
args: vec![],
body: 0.into(),
})
.call(vec![]),
),
]
.into();
let result = run(program);
assert_eq!(result, Ok(0.into()));
}
#[test]
fn if_then_else() {
let program = vec![helpers::define_expr(
"main",
ast::Expr::If {
condition: Box::new(false.into()),
then: Box::new(0.into()),
r#else: Box::new(ast::Expr::If {
condition: Box::new(true.into()),
then: Box::new(1.into()),
r#else: Box::new(2.into()),
}),
},
)]
.into();
let result = run(program);
assert_eq!(result, Ok(1.into()));
}
#[test]
fn fun_call_args() {
let program = vec![
helpers::define_expr("main", "zero".into()),
helpers::define_expr(
"zero",
helpers::func(ast::Fn {
args: vec![
ast::Arg {
name: "a".into(),
r#type: ast::Type::Named("i64".into()),
},
ast::Arg {
name: "b".into(),
r#type: ast::Type::Named("i64".into()),
},
],
body: "b".into(),
})
.call(vec![1.into(), 0.into()]),
),
]
.into();
let result = run(program);
assert_eq!(result, Ok(0.into()));
}
// Errors
#[test]
fn duplicate_toplevel_defs() {
let program = vec![
helpers::define_expr("main", "record".into()),
helpers::define_expr("main", 0.into()),
]
.into();
let result = run(program);
assert_eq!(result, Err(Error::DuplicateNames("main".into())));
}
#[test]
fn field_access_not_a_record() {
let program = vec![
helpers::define_expr("main", "record".into()),
helpers::define_expr("record", ast::Expr::from(0).field("my_field")),
]
.into();
let result = run(program);
assert_eq!(result, Err(Error::NotARecord(0.into())));
}
#[test]
fn fun_call_not_a_function() {
let program = vec![
helpers::define_expr("main", "zero".into()),
helpers::define_expr("zero", ast::Expr::from(0).call(vec![1.into()])),
]
.into();
let result = run(program);
assert_eq!(result, Err(Error::NotAFunction(0.into())));
}
}

View file

@ -1,313 +1,6 @@
//! Interpreter for Z. //! Interpreter for Ayin.
pub mod interpret;
pub mod types; pub mod types;
use types::*; pub use interpret::*;
use crate::ast;
pub fn run(program: ast::Program) -> Result<ast::Value, Error> {
let env_name = ast::EnvName("global".to_string());
let mut state = State::new("global".into());
defs_to_env(program.0, &env_name, &mut state.envs)?;
let main = ast::Expr::Value(state.envs.get(&env_name)?.get(&"main".into())?.clone());
let env: Env = state.envs.get(&env_name)?.clone();
let result = eval_expr(&env, &mut state, &main)?;
println!("{:?}", result);
Ok(result)
}
fn eval_statement(
expr_env: &mut Env,
state: &mut State,
statement: &ast::Statement,
) -> Result<ast::Value, Error> {
match statement {
ast::Statement::Expr(expr) => eval_expr(expr_env, state, expr),
ast::Statement::Assign { name, expr } => {
let result = eval_expr(expr_env, state, expr)?;
expr_env.insert(name.clone(), result.clone());
Ok(result)
}
}
}
fn eval_expr(expr_env: &Env, state: &mut State, expr: &ast::Expr) -> Result<ast::Value, Error> {
match expr {
ast::Expr::If {
condition,
then,
r#else,
} => match eval_expr(expr_env, state, condition)? {
ast::Value::Boolean(condition) => {
if condition {
eval_expr(expr_env, state, then)
} else {
eval_expr(expr_env, state, r#else)
}
}
v => Err(Error::NotABoolean(v)),
},
ast::Expr::Func(func) => {
let env_name = state.generate_env(expr_env.clone())?;
Ok(ast::Value::Closure {
func: func.clone(),
env: env_name,
})
}
ast::Expr::Block(statements) => {
let mut block_env = expr_env.clone();
if let Some(last) = statements.iter().try_fold(None, |_, statement| {
let result = eval_statement(&mut block_env, state, statement)?;
Ok::<Option<ast::Value>, Error>(Some(result))
})? {
Ok(last)
} else {
Err(Error::LastStatementNotAnExpr)
}
}
ast::Expr::Access { expr, field } => match eval_expr(expr_env, state, expr)? {
ast::Value::Record(record) => eval_expr(
expr_env,
state,
&ast::Expr::Value(record.get(field).map_err(Error::from)?.clone()),
),
v => Err(Error::NotARecord(v)),
},
ast::Expr::Var(var) => {
let value = expr_env.get(var)?.clone();
eval_expr(expr_env, state, &ast::Expr::Value(value))
}
ast::Expr::MethodCall { expr, method, args } => {
let r#type = expr.get_type();
let method = state.method_env.get(r#type, method)?;
match eval_expr(expr_env, state, &ast::helpers::func(method.clone()))? {
ast::Value::Closure { func, env } => {
let mut closure_env: Env = state.envs.get(&env)?.clone();
if func.args.len() != args.len() + 1 {
Err(Error::ArgumentsMismatch)?;
}
let mut args = args.clone();
args.push(*expr.clone());
for (arg, e) in func.args.into_iter().zip(args.iter()) {
let evalled = eval_expr(&closure_env, state, e)?;
closure_env.0.insert(arg.name, evalled);
}
eval_expr(&closure_env, state, &func.body)
}
v => Err(Error::NotAFunction(v)),
}
}
ast::Expr::FunCall { func, args } => match eval_expr(expr_env, state, func)? {
ast::Value::Closure { func, env } => {
let mut closure_env: Env = state.envs.get(&env)?.clone();
if func.args.len() != args.len() {
Err(Error::ArgumentsMismatch)?;
}
for (arg, e) in func.args.into_iter().zip(args.iter()) {
let evalled = eval_expr(&closure_env, state, e)?;
closure_env.0.insert(arg.name, evalled);
}
eval_expr(&closure_env, state, &func.body)
}
v => Err(Error::NotAFunction(v)),
},
ast::Expr::Value(v) => match v {
ast::Value::Deferred { expr, env } => {
let closure_env: Env = state.envs.get(env)?.clone();
eval_expr(&closure_env, state, expr)
}
_ => Ok(v.clone()),
},
}
}
fn defs_to_env(
defs: Vec<ast::Definition>,
env_name: &ast::EnvName,
envs: &mut Envs,
) -> Result<(), Error> {
let mut env = Env::new();
for def in defs {
let (name, closure) = match def {
ast::Definition::Function { func, name } => (
name,
ast::Value::Closure {
func: Box::new(func),
env: env_name.clone(),
},
),
ast::Definition::Expr { expr, name } => (
name,
ast::Value::Deferred {
expr: Box::new(expr.clone()),
env: env_name.clone(),
},
),
};
env.insert_nodup(&name, closure)?;
}
envs.0.insert(env_name.clone(), env);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::helpers;
#[test]
fn main_0() {
let program = vec![helpers::define_expr("main", 0.into())].into();
let result = run(program);
assert_eq!(result, Ok(0.into()));
}
#[test]
fn var_lookup() {
let program = vec![
helpers::define_expr("main", "lit".into()),
helpers::define_expr("lit", 0.into()),
]
.into();
let result = run(program);
assert_eq!(result, Ok(0.into()));
}
#[test]
fn var_assign_and_lookup() {
let program = vec![helpers::define_expr(
"main",
vec![
helpers::assign("zero", 0.into()),
helpers::stmt_expr("zero".into()),
]
.into(),
)]
.into();
let result = run(program);
assert_eq!(result, Ok(0.into()));
}
#[test]
fn field_access() {
let program = vec![
helpers::define_expr("main", "record".into()),
helpers::define_expr(
"record",
ast::Expr::from(vec![("my_field", 0.into())]).field("my_field"),
),
]
.into();
let result = run(program);
assert_eq!(result, Ok(0.into()));
}
#[test]
fn fun_call() {
let program = vec![
helpers::define_expr("main", "zero".into()),
helpers::define_expr(
"zero",
helpers::func(ast::Fn {
args: vec![],
body: 0.into(),
})
.call(vec![]),
),
]
.into();
let result = run(program);
assert_eq!(result, Ok(0.into()));
}
#[test]
fn if_then_else() {
let program = vec![helpers::define_expr(
"main",
ast::Expr::If {
condition: Box::new(false.into()),
then: Box::new(0.into()),
r#else: Box::new(ast::Expr::If {
condition: Box::new(true.into()),
then: Box::new(1.into()),
r#else: Box::new(2.into()),
}),
},
)]
.into();
let result = run(program);
assert_eq!(result, Ok(1.into()));
}
#[test]
fn fun_call_args() {
let program = vec![
helpers::define_expr("main", "zero".into()),
helpers::define_expr(
"zero",
helpers::func(ast::Fn {
args: vec![
ast::Arg {
name: "a".into(),
r#type: ast::Type::Named("i64".into()),
},
ast::Arg {
name: "b".into(),
r#type: ast::Type::Named("i64".into()),
},
],
body: "b".into(),
})
.call(vec![1.into(), 0.into()]),
),
]
.into();
let result = run(program);
assert_eq!(result, Ok(0.into()));
}
// Errors
#[test]
fn duplicate_toplevel_defs() {
let program = vec![
helpers::define_expr("main", "record".into()),
helpers::define_expr("main", 0.into()),
]
.into();
let result = run(program);
assert_eq!(result, Err(Error::DuplicateNames("main".into())));
}
#[test]
fn field_access_not_a_record() {
let program = vec![
helpers::define_expr("main", "record".into()),
helpers::define_expr("record", ast::Expr::from(0).field("my_field")),
]
.into();
let result = run(program);
assert_eq!(result, Err(Error::NotARecord(0.into())));
}
#[test]
fn fun_call_not_a_function() {
let program = vec![
helpers::define_expr("main", "zero".into()),
helpers::define_expr("zero", ast::Expr::from(0).call(vec![1.into()])),
]
.into();
let result = run(program);
assert_eq!(result, Err(Error::NotAFunction(0.into())));
}
}

View file

@ -9,14 +9,8 @@ pub enum Error {
DuplicateEnvNames(ast::EnvName), DuplicateEnvNames(ast::EnvName),
#[error("Duplicate names: {0:?}")] #[error("Duplicate names: {0:?}")]
DuplicateNames(ast::Name), DuplicateNames(ast::Name),
#[error("Duplicate method names: {0:?}")]
DuplicateMethodNames(ast::MethodName),
#[error("Name not found: {0:?}")] #[error("Name not found: {0:?}")]
NameNotFound(ast::Name), NameNotFound(ast::Name),
#[error("Type not found: {0:?}")]
TypeNotFound(ast::Type),
#[error("Method name not found: {0:?}")]
MethodNameNotFound(ast::MethodName),
#[error("Field not found: {0:?}")] #[error("Field not found: {0:?}")]
FieldNotFound(ast::Label), FieldNotFound(ast::Label),
#[error("Env not found: {0:?}")] #[error("Env not found: {0:?}")]
@ -24,7 +18,7 @@ pub enum Error {
#[error("Last statement is not an expression")] #[error("Last statement is not an expression")]
LastStatementNotAnExpr, LastStatementNotAnExpr,
#[error("Not a function {0:?}")] #[error("Not a function {0:?}")]
NotAFunction(ast::Value), NotAFunction(ast::Expr),
#[error("Not a record {0:?}")] #[error("Not a record {0:?}")]
NotARecord(ast::Value), NotARecord(ast::Value),
#[error("Not a boolean {0:?}")] #[error("Not a boolean {0:?}")]
@ -59,7 +53,6 @@ pub struct State {
pub name: String, pub name: String,
namer: Namer, namer: Namer,
pub envs: Envs, pub envs: Envs,
pub method_env: MethodEnv,
} }
impl State { impl State {
@ -68,7 +61,6 @@ impl State {
name, name,
namer: Namer::new(), namer: Namer::new(),
envs: Envs(BTreeMap::new()), envs: Envs(BTreeMap::new()),
method_env: MethodEnv::new(),
} }
} }
pub fn generate_env(&mut self, env: Env) -> Result<ast::EnvName, Error> { pub fn generate_env(&mut self, env: Env) -> Result<ast::EnvName, Error> {
@ -104,51 +96,6 @@ impl Env {
} }
} }
#[derive(PartialEq, Debug, Clone)]
pub struct MethodEnv(pub BTreeMap<ast::Type, BTreeMap<ast::MethodName, ast::Fn>>);
impl MethodEnv {
pub fn new() -> MethodEnv {
MethodEnv(BTreeMap::new())
}
pub fn get(
&self,
r#type: &ast::Type,
method_name: &ast::MethodName,
) -> Result<&ast::Fn, Error> {
self.0
.get(r#type)
.ok_or(Error::TypeNotFound(r#type.clone()))?
.get(method_name)
.ok_or(Error::MethodNameNotFound(method_name.clone()))
}
// todo: check for dups
pub fn insert(
&mut self,
r#type: ast::Type,
method_name: ast::MethodName,
func: ast::Fn,
) -> Result<(), Error> {
self.0
.entry(r#type.clone())
.and_modify(|map| {
map.insert(method_name.clone(), func.clone());
})
.or_insert({
let mut map = BTreeMap::new();
map.insert(method_name.clone(), func);
map
});
Ok(())
}
}
impl Default for MethodEnv {
fn default() -> Self {
Self::new()
}
}
#[derive(PartialEq, Debug, Clone)] #[derive(PartialEq, Debug, Clone)]
pub struct Envs(pub BTreeMap<ast::EnvName, Env>); pub struct Envs(pub BTreeMap<ast::EnvName, Env>);

View file

@ -1,3 +1,4 @@
pub mod parser;
pub mod runtime; pub mod runtime;
pub use runtime::*; pub use runtime::*;

205
src/runtime/parser.rs Normal file
View file

@ -0,0 +1,205 @@
use crate::ast;
use chumsky::text::Char;
use log;
use lyn::Scanner;
struct LocatedToken {
start: usize,
end: usize,
token: Token,
}
#[derive(Debug, Clone, PartialEq)]
enum Token {
Let,
Fn,
Equals,
Semicolon,
OpenParen,
CloseParen,
OpenBracket,
CloseBracket,
OpenCurly,
CloseCurly,
Dot,
Comma,
Name(String),
Number(u32),
String(String),
Identifier(String),
}
fn scan(source: String) -> Vec<LocatedToken> {
let mut scanner = Scanner::new(&source);
let mut tokens = Vec::new();
loop {
let start = scanner.cursor();
if let Some(c) = scanner.pop() {
match *c {
'.' => tokens.push(LocatedToken {
start,
end: scanner.cursor(),
token: Token::Dot,
}),
',' => tokens.push(LocatedToken {
start,
end: scanner.cursor(),
token: Token::Comma,
}),
'{' => tokens.push(LocatedToken {
start,
end: scanner.cursor(),
token: Token::OpenCurly,
}),
'}' => tokens.push(LocatedToken {
start,
end: scanner.cursor(),
token: Token::CloseCurly,
}),
'(' => tokens.push(LocatedToken {
start,
end: scanner.cursor(),
token: Token::OpenParen,
}),
')' => tokens.push(LocatedToken {
start,
end: scanner.cursor(),
token: Token::CloseParen,
}),
'[' => tokens.push(LocatedToken {
start,
end: scanner.cursor(),
token: Token::OpenBracket,
}),
']' => tokens.push(LocatedToken {
start,
end: scanner.cursor(),
token: Token::CloseBracket,
}),
'=' => tokens.push(LocatedToken {
start,
end: scanner.cursor(),
token: Token::Equals,
}),
// comments
'#' => {
let mut str = "".to_string();
while let Some(ch) = scanner.pop() {
if ch.is_newline() {
break;
}
str.push(*ch);
}
}
// strings
'"' => {
let mut str = "".to_string();
while let Some(ch) = scanner.pop() {
if *ch == '"' {
break;
}
str.push(*ch);
}
tokens.push(LocatedToken {
start,
end: scanner.cursor(),
token: Token::String(str),
});
}
_ if c.is_whitespace() => {}
// numbers
_ if c.is_numeric() => {
let mut str = "".to_string();
loop {
if let Some(ch) = scanner.peek()
&& !ch.is_numeric()
{
break;
}
if let Some(ch) = scanner.pop() {
str.push(*ch);
} else {
break;
}
}
let i = str.parse::<u32>().unwrap();
tokens.push(LocatedToken {
start,
end: scanner.cursor(),
token: Token::Number(i),
});
}
// identifiers and keywords
_ if c.is_alphabetic() => {
let mut str = "".to_string();
loop {
if let Some(ch) = scanner.peek()
&& !ch.is_alphanumeric()
{
break;
}
if let Some(ch) = scanner.pop() {
str.push(*ch);
} else {
break;
}
}
tokens.push(LocatedToken {
start,
end: scanner.cursor(),
token: match str.as_str() {
"fn" => Token::Fn,
"let" => Token::Let,
_ => Token::Identifier(str),
},
});
}
// error
_ => {
log::error!("Unexpected character: {c}");
}
}
} else {
break;
}
}
tokens
}
fn parse_expr(tokens: &mut Vec<LocatedToken>) -> Result<ast::Expr, Error> {
todo!()
}
enum Error {
UnexpectedToken(LocatedToken),
UnexpectedEndOfInput,
}
fn parse_definition(tokens: &mut Vec<LocatedToken>) -> Result<ast::Definition, Error> {
if let Some(start) = tokens.pop_if(|tok| tok.token == Token::Let) {
if let Some(LocatedToken {
token: Token::Identifier(identifier),
..
}) = tokens.pop_if(|tok| match tok.token {
Token::Identifier(_) => true,
_ => false,
}) {
if let Some(_eq) = tokens.pop_if(|tok| tok.token == Token::Equals) {
let expr = parse_expr(tokens)?;
Ok(ast::Definition {
name: ast::Name(identifier),
expr,
})
} else {
Err(Error::UnexpectedToken(start))
}
} else {
Err(Error::UnexpectedToken(start))
}
} else {
Err(Error::UnexpectedEndOfInput)
}
}

View file

@ -1,4 +1,5 @@
use super::types::*; use super::types::*;
use crate::ast;
use macroquad::prelude::*; use macroquad::prelude::*;
pub async fn setup() -> State { pub async fn setup() -> State {
@ -6,6 +7,7 @@ pub async fn setup() -> State {
State { State {
assets: Assets { font }, assets: Assets { font },
code: ast::Program(vec![]),
} }
} }

View file

@ -3,7 +3,7 @@ use macroquad::prelude::*;
pub struct State { pub struct State {
pub assets: Assets, pub assets: Assets,
code: ast::Program, pub code: ast::Program,
} }
pub struct Assets { pub struct Assets {