updates and lexing
This commit is contained in:
parent
fb3f384c84
commit
c66a193a6e
13 changed files with 746 additions and 447 deletions
201
Cargo.lock
generated
201
Cargo.lock
generated
|
|
@ -59,7 +59,7 @@ version = "1.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -70,7 +70,16 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
|||
dependencies = [
|
||||
"anstyle",
|
||||
"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]]
|
||||
|
|
@ -83,8 +92,10 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|||
name = "ayin-lang"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chumsky",
|
||||
"env_logger",
|
||||
"log",
|
||||
"lyn",
|
||||
"macroquad",
|
||||
"thiserror",
|
||||
]
|
||||
|
|
@ -107,12 +118,36 @@ version = "1.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
|
|
@ -172,6 +207,12 @@ dependencies = [
|
|||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.5"
|
||||
|
|
@ -270,6 +311,12 @@ version = "0.4.29"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "lyn"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f08d9299a146aa1eb3c5451e6d9045708f232a43fd8d4436f69cf00fcb2a019c"
|
||||
|
||||
[[package]]
|
||||
name = "macroquad"
|
||||
version = "0.4.14"
|
||||
|
|
@ -351,6 +398,15 @@ dependencies = [
|
|||
"malloc_buf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.32.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.2"
|
||||
|
|
@ -394,6 +450,16 @@ dependencies = [
|
|||
"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]]
|
||||
name = "quad-rand"
|
||||
version = "0.2.3"
|
||||
|
|
@ -417,8 +483,19 @@ checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
|
|||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
"regex-automata 0.4.13",
|
||||
"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]]
|
||||
|
|
@ -429,15 +506,31 @@ checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
|
|||
dependencies = [
|
||||
"aho-corasick",
|
||||
"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]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
|
|
@ -458,12 +551,31 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "syn"
|
||||
version = "2.0.111"
|
||||
|
|
@ -507,6 +619,12 @@ version = "1.0.22"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
|
|
@ -541,6 +659,15 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
|
|
@ -549,3 +676,67 @@ checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
|||
dependencies = [
|
||||
"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"
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ thiserror = "1"
|
|||
env_logger = "0.11.8"
|
||||
log = "0.4.27"
|
||||
macroquad = "0.4.14"
|
||||
lyn = "0.1.0"
|
||||
chumsky = "0.11.2"
|
||||
|
||||
[workspace.lints.clippy]
|
||||
all = { level = "warn", priority = -1 }
|
||||
|
|
|
|||
4
Makefile
4
Makefile
|
|
@ -2,6 +2,10 @@
|
|||
watch:
|
||||
cargo watch --clear -x 'clippy' -x 'test --all'
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
cargo build
|
||||
|
||||
.PHONY: run
|
||||
run:
|
||||
cargo run
|
||||
|
|
|
|||
|
|
@ -13,21 +13,21 @@ Ayin
|
|||
- first class functions
|
||||
|
||||
```
|
||||
fn init() {
|
||||
let init = fn() {
|
||||
return {
|
||||
player: { position: { x: 10, y: 20 }, },
|
||||
}
|
||||
}
|
||||
|
||||
fn update(state, events) {
|
||||
let update = fn(state, events) {
|
||||
return state';
|
||||
}
|
||||
|
||||
fn draw(frame, state) {
|
||||
let draw = fn(frame, state) {
|
||||
frame.clear(0,0,0);
|
||||
}
|
||||
|
||||
fn migrate(state) {
|
||||
let migrate = fn(state) {
|
||||
return { player: { pos: state.player.position } },
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ pub fn var(str: &str) -> Expr {
|
|||
Expr::Var(name(str))
|
||||
}
|
||||
|
||||
pub fn record(rec: Vec<(&str, Value)>) -> Value {
|
||||
pub fn record(rec: Vec<(&str, Expr)>) -> Value {
|
||||
Value::Record(Record({
|
||||
rec.into_iter()
|
||||
.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))
|
||||
}
|
||||
|
||||
pub fn assign(name: &str, expr: Expr) -> Statement {
|
||||
Statement::Assign {
|
||||
name: name.into(),
|
||||
expr,
|
||||
}
|
||||
Statement::Assign(define_expr(name, expr))
|
||||
}
|
||||
|
||||
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 {
|
||||
Definition::Expr {
|
||||
Definition {
|
||||
name: name.into(),
|
||||
expr,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,15 +7,6 @@ use std::collections::BTreeMap;
|
|||
pub enum Expr {
|
||||
Value(Value),
|
||||
Var(Name),
|
||||
Access {
|
||||
expr: Box<Expr>,
|
||||
field: Label,
|
||||
},
|
||||
MethodCall {
|
||||
expr: Box<Expr>,
|
||||
method: MethodName,
|
||||
args: Vec<Expr>,
|
||||
},
|
||||
Func(Box<Fn>),
|
||||
FunCall {
|
||||
func: Box<Expr>,
|
||||
|
|
@ -27,13 +18,31 @@ pub enum Expr {
|
|||
then: Box<Expr>,
|
||||
r#else: Box<Expr>,
|
||||
},
|
||||
Access {
|
||||
expr: Box<Expr>,
|
||||
field: Label,
|
||||
},
|
||||
/*
|
||||
Loop {
|
||||
body: Box<Expr>,
|
||||
},
|
||||
Continue,
|
||||
Break,
|
||||
Return(Box<Expr>),
|
||||
*/
|
||||
}
|
||||
|
||||
/// A statement.
|
||||
#[derive(PartialEq, PartialOrd, Debug, Clone)]
|
||||
pub enum Statement {
|
||||
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.
|
||||
|
|
@ -44,8 +53,8 @@ pub enum Value {
|
|||
String(String),
|
||||
Boolean(bool),
|
||||
Record(Record),
|
||||
Closure { func: Box<Fn>, env: EnvName },
|
||||
Deferred { expr: Box<Expr>, env: EnvName },
|
||||
Vector(Vector),
|
||||
Closure { env: EnvName, expr: Box<Expr> },
|
||||
}
|
||||
|
||||
/// An anonymous function.
|
||||
|
|
@ -59,28 +68,12 @@ pub struct Fn {
|
|||
#[derive(PartialEq, PartialOrd, Debug, Clone)]
|
||||
pub struct Arg {
|
||||
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.
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone)]
|
||||
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.
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone)]
|
||||
pub struct Label(pub String);
|
||||
|
|
@ -90,50 +83,25 @@ pub struct Label(pub String);
|
|||
pub struct EnvName(pub String);
|
||||
|
||||
#[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.
|
||||
#[derive(PartialEq, PartialOrd, Debug)]
|
||||
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 //
|
||||
// ----- //
|
||||
|
||||
impl Definition {
|
||||
pub fn name(&self) -> &Name {
|
||||
match self {
|
||||
Definition::Function { name, .. } => name,
|
||||
Definition::Expr { name, .. } => name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Name {
|
||||
fn from(value: &str) -> Name {
|
||||
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 {
|
||||
fn from(value: &str) -> Label {
|
||||
Label(value.into())
|
||||
|
|
@ -146,26 +114,23 @@ impl From<&str> for Expr {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Vec<(&str, Value)>> for Value {
|
||||
fn from(rec: Vec<(&str, Value)>) -> Value {
|
||||
impl From<Vec<(&str, Expr)>> for Value {
|
||||
fn from(rec: Vec<(&str, Expr)>) -> Value {
|
||||
Value::Record(Record({
|
||||
rec.into_iter()
|
||||
.map(|(lbl, val)| (Label(lbl.into()), val))
|
||||
.map(|(lbl, expr)| (Label(lbl.into()), expr))
|
||||
.collect()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<(&str, Value)>> for Expr {
|
||||
fn from(rec: Vec<(&str, Value)>) -> Expr {
|
||||
impl From<Vec<(&str, Expr)>> for Expr {
|
||||
fn from(rec: Vec<(&str, Expr)>) -> Expr {
|
||||
Expr::Value(rec.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
pub fn get_type(&self) -> &Type {
|
||||
todo!()
|
||||
}
|
||||
pub fn field(self, label: &str) -> Expr {
|
||||
Expr::Access {
|
||||
expr: Box::new(self),
|
||||
|
|
@ -217,7 +182,7 @@ impl From<Vec<Statement>> for Expr {
|
|||
}
|
||||
|
||||
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) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(Error::LabelNotFound(label.clone())),
|
||||
|
|
|
|||
292
src/interpret/interpret.rs
Normal file
292
src/interpret/interpret.rs
Normal 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())));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,313 +1,6 @@
|
|||
//! Interpreter for Z.
|
||||
//! Interpreter for Ayin.
|
||||
|
||||
pub mod interpret;
|
||||
pub mod types;
|
||||
|
||||
use types::*;
|
||||
|
||||
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())));
|
||||
}
|
||||
}
|
||||
pub use interpret::*;
|
||||
|
|
|
|||
|
|
@ -9,14 +9,8 @@ pub enum Error {
|
|||
DuplicateEnvNames(ast::EnvName),
|
||||
#[error("Duplicate names: {0:?}")]
|
||||
DuplicateNames(ast::Name),
|
||||
#[error("Duplicate method names: {0:?}")]
|
||||
DuplicateMethodNames(ast::MethodName),
|
||||
#[error("Name not found: {0:?}")]
|
||||
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:?}")]
|
||||
FieldNotFound(ast::Label),
|
||||
#[error("Env not found: {0:?}")]
|
||||
|
|
@ -24,7 +18,7 @@ pub enum Error {
|
|||
#[error("Last statement is not an expression")]
|
||||
LastStatementNotAnExpr,
|
||||
#[error("Not a function {0:?}")]
|
||||
NotAFunction(ast::Value),
|
||||
NotAFunction(ast::Expr),
|
||||
#[error("Not a record {0:?}")]
|
||||
NotARecord(ast::Value),
|
||||
#[error("Not a boolean {0:?}")]
|
||||
|
|
@ -59,7 +53,6 @@ pub struct State {
|
|||
pub name: String,
|
||||
namer: Namer,
|
||||
pub envs: Envs,
|
||||
pub method_env: MethodEnv,
|
||||
}
|
||||
|
||||
impl State {
|
||||
|
|
@ -68,7 +61,6 @@ impl State {
|
|||
name,
|
||||
namer: Namer::new(),
|
||||
envs: Envs(BTreeMap::new()),
|
||||
method_env: MethodEnv::new(),
|
||||
}
|
||||
}
|
||||
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)]
|
||||
pub struct Envs(pub BTreeMap<ast::EnvName, Env>);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
pub mod parser;
|
||||
pub mod runtime;
|
||||
pub use runtime::*;
|
||||
|
||||
|
|
|
|||
205
src/runtime/parser.rs
Normal file
205
src/runtime/parser.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
use super::types::*;
|
||||
use crate::ast;
|
||||
use macroquad::prelude::*;
|
||||
|
||||
pub async fn setup() -> State {
|
||||
|
|
@ -6,6 +7,7 @@ pub async fn setup() -> State {
|
|||
|
||||
State {
|
||||
assets: Assets { font },
|
||||
code: ast::Program(vec![]),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use macroquad::prelude::*;
|
|||
|
||||
pub struct State {
|
||||
pub assets: Assets,
|
||||
code: ast::Program,
|
||||
pub code: ast::Program,
|
||||
}
|
||||
|
||||
pub struct Assets {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue