Start working on typechecking
parent
e21f575207
commit
4e76f1990e
|
@ -392,9 +392,21 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.8.1"
|
version = "1.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
|
checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -403,9 +415,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.7.1"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
|
checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
|
@ -482,18 +494,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.43"
|
version = "1.0.44"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42"
|
checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.43"
|
version = "1.0.44"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f"
|
checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
|
@ -7,8 +7,11 @@ edition = "2021"
|
||||||
lalrpop = "0.20.0"
|
lalrpop = "0.20.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.72"
|
anyhow = "1"
|
||||||
lalrpop-util = { version = "0.20.0", features = ["lexer"] }
|
lalrpop-util = { version = "0.20.0", features = ["lexer"] }
|
||||||
pico-args = { version = "0.5.0", features = ["eq-separator", "short-space-opt", "combined-flags"] }
|
pico-args = { version = "0.5.0", features = ["eq-separator", "short-space-opt", "combined-flags"] }
|
||||||
regex = "1"
|
regex = "1"
|
||||||
thiserror = "1.0.43"
|
thiserror = "1"
|
||||||
|
|
||||||
|
# [dev-dependencies]
|
||||||
|
# goldentests = "1"
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
fn main() {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
use goldentests::{TestConfig, TestResult};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn run_golden_tests() -> TestResult<()> {
|
||||||
|
let config = TestConfig::new("target/debug/mul", "tests/golden", "// ")?;
|
||||||
|
config.run_tests()
|
||||||
|
}
|
|
@ -3,10 +3,11 @@
|
||||||
## stages
|
## stages
|
||||||
- [x] parser
|
- [x] parser
|
||||||
- [ ] lossless syntax trees (rowan + ungrammar)
|
- [ ] lossless syntax trees (rowan + ungrammar)
|
||||||
|
- [ ] formatter (pretty-printer)
|
||||||
- [ ] typechecker
|
- [ ] typechecker
|
||||||
- [x] interpreter
|
- [x] interpreter
|
||||||
|
- [ ] virtual machine
|
||||||
- [ ] code generator
|
- [ ] code generator
|
||||||
- [ ] formatter (pretty-printer)
|
|
||||||
|
|
||||||
## features
|
## features
|
||||||
- [ ] primitives
|
- [ ] primitives
|
||||||
|
|
74
flake.lock
74
flake.lock
|
@ -10,11 +10,11 @@
|
||||||
"rust-overlay": "rust-overlay"
|
"rust-overlay": "rust-overlay"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1684468982,
|
"lastModified": 1688772518,
|
||||||
"narHash": "sha256-EoC1N5sFdmjuAP3UOkyQujSOT6EdcXTnRw8hPjJkEgc=",
|
"narHash": "sha256-ol7gZxwvgLnxNSZwFTDJJ49xVY5teaSvF7lzlo3YQfM=",
|
||||||
"owner": "ipetkov",
|
"owner": "ipetkov",
|
||||||
"repo": "crane",
|
"repo": "crane",
|
||||||
"rev": "99de890b6ef4b4aab031582125b6056b792a4a30",
|
"rev": "8b08e96c9af8c6e3a2b69af5a7fa168750fcf88e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -60,11 +60,11 @@
|
||||||
"nixpkgs-lib": "nixpkgs-lib"
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1683560683,
|
"lastModified": 1688466019,
|
||||||
"narHash": "sha256-XAygPMN5Xnk/W2c1aW0jyEa6lfMDZWlQgiNtmHXytPc=",
|
"narHash": "sha256-VeM2akYrBYMsb4W/MmBo1zmaMfgbL4cH3Pu8PGyIwJ0=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "006c75898cf814ef9497252b022e91c946ba8e17",
|
"rev": "8e8d955c22df93dbe24f19ea04f47a74adbdc5ec",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -78,11 +78,11 @@
|
||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1681202837,
|
"lastModified": 1687709756,
|
||||||
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
|
"narHash": "sha256-Y5wKlQSkgEK2weWdOu4J3riRd+kV/VCgHsqLNTTWQ/0=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
|
"rev": "dbabf0ca0c0c4bce6ea5eaf65af5cb694d2082c7",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -92,12 +92,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-utils_2": {
|
"flake-utils_2": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_2"
|
||||||
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1667395993,
|
"lastModified": 1685518550,
|
||||||
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -129,11 +132,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1684570954,
|
"lastModified": 1690640159,
|
||||||
"narHash": "sha256-FX5y4Sm87RWwfu9PI71XFvuRpZLowh00FQpIJ1WfXqE=",
|
"narHash": "sha256-5DZUYnkeMOsVb/eqPYb9zns5YsnQXRJRC8Xx/nPMcno=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "3005f20ce0aaa58169cdee57c8aa12e5f1b6e1b3",
|
"rev": "e6ab46982debeab9831236869539a507f670a129",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -146,11 +149,11 @@
|
||||||
"nixpkgs-lib": {
|
"nixpkgs-lib": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"dir": "lib",
|
"dir": "lib",
|
||||||
"lastModified": 1682879489,
|
"lastModified": 1688049487,
|
||||||
"narHash": "sha256-sASwo8gBt7JDnOOstnps90K1wxmVfyhsTPPNTGBPjjg=",
|
"narHash": "sha256-100g4iaKC9MalDjUW9iN6Jl/OocTDtXdeAj7pEGIRh4=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "da45bf6ec7bbcc5d1e14d3795c025199f28e0de0",
|
"rev": "4bc72cae107788bf3f24f30db2e2f685c9298dc9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -163,16 +166,16 @@
|
||||||
},
|
},
|
||||||
"nixpkgs-stable": {
|
"nixpkgs-stable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1678872516,
|
"lastModified": 1685801374,
|
||||||
"narHash": "sha256-/E1YwtMtFAu2KUQKV/1+KFuReYPANM2Rzehk84VxVoc=",
|
"narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "9b8e5abb18324c7fe9f07cb100c3cd4a29cda8b8",
|
"rev": "c37ca420157f4abc31e26f436c1145f8951ff373",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-22.11",
|
"ref": "nixos-23.05",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
@ -188,11 +191,11 @@
|
||||||
"nixpkgs-stable": "nixpkgs-stable"
|
"nixpkgs-stable": "nixpkgs-stable"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1684195081,
|
"lastModified": 1690743255,
|
||||||
"narHash": "sha256-IKnQUSBhQTChFERxW2AzuauVpY1HRgeVzAjNMAA4B6I=",
|
"narHash": "sha256-dsJzQsyJGWCym1+LMyj2rbYmvjYmzeOrk7ypPrSFOPo=",
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "pre-commit-hooks.nix",
|
"repo": "pre-commit-hooks.nix",
|
||||||
"rev": "96eabec58248ed8f4b0ad59e7ce9398018684fdc",
|
"rev": "fcbf4705d98398d084e6cb1c826a0b90a91d22d7",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -221,11 +224,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1683080331,
|
"lastModified": 1688351637,
|
||||||
"narHash": "sha256-nGDvJ1DAxZIwdn6ww8IFwzoHb2rqBP4wv/65Wt5vflk=",
|
"narHash": "sha256-CLTufJ29VxNOIZ8UTg0lepsn3X03AmopmaLTTeHDCL4=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "d59c3fa0cba8336e115b376c2d9e91053aa59e56",
|
"rev": "f9b92316727af9e6c7fee4a761242f7f46880329",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -248,6 +251,21 @@
|
||||||
"repo": "default",
|
"repo": "default",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"systems_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
pre-commit.settings.hooks = {
|
pre-commit.settings.hooks = {
|
||||||
rustfmt.enable = true;
|
rustfmt.enable = true;
|
||||||
clippy.enable = true;
|
clippy.enable = true;
|
||||||
cargo-check.enable = true;
|
# cargo-check.enable = true;
|
||||||
};
|
};
|
||||||
checks = { inherit package; };
|
checks = { inherit package; };
|
||||||
packages.default = package;
|
packages.default = package;
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
buildInputs = builtins.attrValues {
|
buildInputs = builtins.attrValues {
|
||||||
inherit (pkgs)
|
inherit (pkgs)
|
||||||
rust-analyzer
|
rust-analyzer
|
||||||
# rustfmt
|
rustfmt
|
||||||
clippy
|
clippy
|
||||||
# profiling
|
# profiling
|
||||||
cargo-flamegraph
|
cargo-flamegraph
|
||||||
|
@ -65,11 +65,6 @@
|
||||||
cargo-edit
|
cargo-edit
|
||||||
cargo-license
|
cargo-license
|
||||||
;
|
;
|
||||||
rustfmt = pkgs.rustfmt.overrideAttrs (old: {
|
|
||||||
preFixup = pkgs.lib.optionalString pkgs.stdenv.isDarwin ''
|
|
||||||
install_name_tool -add_rpath "${pkgs.rustc}/lib" "$out/bin/rustfmt"
|
|
||||||
'';
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use crate::evaluate::{Dictionary, Value};
|
use crate::dictionary::Dictionary;
|
||||||
|
use crate::evaluate::Value;
|
||||||
|
use crate::syntax::Type;
|
||||||
|
|
||||||
/// Builtin functions.
|
/// Builtin functions.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
@ -17,6 +19,7 @@ pub enum Builtin {
|
||||||
LessThan,
|
LessThan,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn unwrap_int_value(op_name: &'static str) -> impl Fn(Value) -> i64 {
|
fn unwrap_int_value(op_name: &'static str) -> impl Fn(Value) -> i64 {
|
||||||
move |value| match value {
|
move |value| match value {
|
||||||
Value::Integer(i) => i,
|
Value::Integer(i) => i,
|
||||||
|
@ -25,15 +28,14 @@ fn unwrap_int_value(op_name: &'static str) -> impl Fn(Value) -> i64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! int_op {
|
macro_rules! int_op {
|
||||||
($arguments:expr, $op:tt, $op_name:expr) => {
|
($arguments:expr, $op:tt, $op_name:expr) => {{
|
||||||
Value::Integer(
|
let i = $arguments
|
||||||
$arguments
|
.into_iter()
|
||||||
.into_iter()
|
.map(unwrap_int_value($op_name))
|
||||||
.map(unwrap_int_value($op_name))
|
.reduce(|acc, v| acc $op v)
|
||||||
.reduce(|acc, v| acc $op v)
|
.unwrap();
|
||||||
.unwrap(),
|
Value::Integer(i)
|
||||||
)
|
}};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! int_cmp {
|
macro_rules! int_cmp {
|
||||||
|
@ -46,6 +48,28 @@ macro_rules! int_cmp {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn int_type() -> Type {
|
||||||
|
Type::Constructor("Integer".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bool_type() -> Type {
|
||||||
|
Type::Constructor("Boolean".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int_op_type() -> Type {
|
||||||
|
Type::Function {
|
||||||
|
parameters: vec![int_type(), int_type()],
|
||||||
|
result: Box::new(int_type()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int_cmp_type() -> Type {
|
||||||
|
Type::Function {
|
||||||
|
parameters: vec![int_type(), int_type()],
|
||||||
|
result: Box::new(bool_type()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Builtin {
|
impl Builtin {
|
||||||
/// A mapping from runtime names to builtin sentinels.
|
/// A mapping from runtime names to builtin sentinels.
|
||||||
pub fn dictionary() -> Dictionary<Value> {
|
pub fn dictionary() -> Dictionary<Value> {
|
||||||
|
@ -63,6 +87,22 @@ impl Builtin {
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A mapping from runtime names to builtin types.
|
||||||
|
pub fn type_dictionary() -> Dictionary<Type> {
|
||||||
|
[
|
||||||
|
("_print", Type::Print),
|
||||||
|
("_add", int_op_type()),
|
||||||
|
("_sub", int_op_type()),
|
||||||
|
("_mul", int_op_type()),
|
||||||
|
("_div", int_op_type()),
|
||||||
|
("_equals", Type::Equals),
|
||||||
|
("_greaterthan", int_cmp_type()),
|
||||||
|
("_lessthan", int_cmp_type()),
|
||||||
|
]
|
||||||
|
.map(|(name, ty)| (name.to_string(), ty))
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
/// Minimum # of required arguments for a builtin.
|
/// Minimum # of required arguments for a builtin.
|
||||||
pub fn min_parameters(&self) -> usize {
|
pub fn min_parameters(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::syntax::Name;
|
||||||
|
|
||||||
|
/// Mapping of names to some T.
|
||||||
|
pub type Dictionary<T> = HashMap<Name, T>;
|
|
@ -1,16 +1,13 @@
|
||||||
//! Tree-walk interpreter for syntax.
|
//! Tree-walk interpreter for syntax.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::builtins::Builtin;
|
use crate::builtins::Builtin;
|
||||||
|
use crate::dictionary::Dictionary;
|
||||||
use crate::syntax::{Block, Expression, InteractiveEntry, Item, Name, Program};
|
use crate::syntax::{Block, Expression, InteractiveEntry, Item, Name, Program};
|
||||||
|
|
||||||
/// Mapping of names to some T.
|
|
||||||
pub type Dictionary<T> = HashMap<Name, T>;
|
|
||||||
|
|
||||||
/// Runtime values.
|
/// Runtime values.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
|
@ -58,6 +55,17 @@ pub struct Interpreter {
|
||||||
environment: Dictionary<Value>,
|
environment: Dictionary<Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! bind {
|
||||||
|
($interpreter:expr, $name:expr, $term:expr, to: $nested:expr) => {{
|
||||||
|
let value = $interpreter.evaluate($term)?;
|
||||||
|
$nested.environment.insert($name, value.clone());
|
||||||
|
Ok(value)
|
||||||
|
}};
|
||||||
|
($interpreter:expr, $name:expr, $term:expr) => {
|
||||||
|
bind!($interpreter, $name, $term, to: $interpreter)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
impl Interpreter {
|
impl Interpreter {
|
||||||
/// Create a fresh interpreter populated with builtins.
|
/// Create a fresh interpreter populated with builtins.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
@ -89,10 +97,11 @@ impl Interpreter {
|
||||||
name,
|
name,
|
||||||
parameters,
|
parameters,
|
||||||
body,
|
body,
|
||||||
|
..
|
||||||
} => {
|
} => {
|
||||||
let closure = Value::Closure {
|
let closure = Value::Closure {
|
||||||
environment: self.environment.clone(),
|
environment: self.environment.clone(),
|
||||||
parameters: parameters.clone(),
|
parameters: parameters.iter().map(|p| p.name.clone()).collect(),
|
||||||
result: Expression::Block(Box::new(body.clone())),
|
result: Expression::Block(Box::new(body.clone())),
|
||||||
};
|
};
|
||||||
self.environment.insert(name.clone(), closure.clone());
|
self.environment.insert(name.clone(), closure.clone());
|
||||||
|
@ -106,7 +115,7 @@ impl Interpreter {
|
||||||
match entry {
|
match entry {
|
||||||
InteractiveEntry::Item(item) => self.interpret_item(item),
|
InteractiveEntry::Item(item) => self.interpret_item(item),
|
||||||
InteractiveEntry::Binding(binding) => {
|
InteractiveEntry::Binding(binding) => {
|
||||||
self.bind(binding.name.clone(), &binding.expression)
|
bind!(self, binding.name.clone(), &binding.expression)
|
||||||
}
|
}
|
||||||
InteractiveEntry::Expression(term) => self.evaluate(term),
|
InteractiveEntry::Expression(term) => self.evaluate(term),
|
||||||
}
|
}
|
||||||
|
@ -120,15 +129,18 @@ impl Interpreter {
|
||||||
.ok_or(Error::UnboundName(name.clone()))
|
.ok_or(Error::UnboundName(name.clone()))
|
||||||
.cloned(),
|
.cloned(),
|
||||||
Expression::Block(b) => self.evaluate_block(b.as_ref()),
|
Expression::Block(b) => self.evaluate_block(b.as_ref()),
|
||||||
Expression::Lambda { parameters, result } => Ok(Value::Closure {
|
Expression::Lambda {
|
||||||
|
parameters, result, ..
|
||||||
|
} => Ok(Value::Closure {
|
||||||
environment: self.environment.clone(),
|
environment: self.environment.clone(),
|
||||||
parameters: parameters.clone(),
|
parameters: parameters.iter().map(|p| p.name.clone()).collect(),
|
||||||
result: *result.clone(),
|
result: *result.clone(),
|
||||||
}),
|
}),
|
||||||
Expression::Call { callee, arguments } => {
|
Expression::Call { callee, arguments } => {
|
||||||
let callee = self.evaluate(callee.as_ref())?;
|
let callee = self.evaluate(callee.as_ref())?;
|
||||||
self.apply_call(&callee, arguments)
|
self.apply_call(&callee, arguments)
|
||||||
}
|
}
|
||||||
|
Expression::Annotation { expression, .. } => self.evaluate(expression.as_ref()),
|
||||||
Expression::Boolean(b) => Ok(Value::Boolean(*b)),
|
Expression::Boolean(b) => Ok(Value::Boolean(*b)),
|
||||||
Expression::Integer(i) => Ok(Value::Integer(*i)),
|
Expression::Integer(i) => Ok(Value::Integer(*i)),
|
||||||
Expression::Unit => Ok(Value::Unit),
|
Expression::Unit => Ok(Value::Unit),
|
||||||
|
@ -148,7 +160,7 @@ impl Interpreter {
|
||||||
let mut nested = Interpreter::nested(environment.clone());
|
let mut nested = Interpreter::nested(environment.clone());
|
||||||
for (name, argument) in parameters.iter().zip(arguments) {
|
for (name, argument) in parameters.iter().zip(arguments) {
|
||||||
// we don't want arguments to refer to each other, so use the parent interpreter
|
// we don't want arguments to refer to each other, so use the parent interpreter
|
||||||
self.bind_nested(name.clone(), argument, &mut nested)?;
|
bind!(self, name.clone(), argument, to: nested)?;
|
||||||
}
|
}
|
||||||
nested.evaluate(result)
|
nested.evaluate(result)
|
||||||
}
|
}
|
||||||
|
@ -176,27 +188,10 @@ impl Interpreter {
|
||||||
fn evaluate_block(&mut self, block: &Block) -> Result<Value> {
|
fn evaluate_block(&mut self, block: &Block) -> Result<Value> {
|
||||||
let mut nested = self.clone();
|
let mut nested = self.clone();
|
||||||
for binding in &block.bindings {
|
for binding in &block.bindings {
|
||||||
nested.bind(binding.name.clone(), &binding.expression)?;
|
bind!(nested, binding.name.clone(), &binding.expression)?;
|
||||||
}
|
}
|
||||||
nested.evaluate(&block.result)
|
nested.evaluate(&block.result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bind(&mut self, name: Name, term: &Expression) -> Result<Value> {
|
|
||||||
let value = self.evaluate(term)?;
|
|
||||||
self.environment.insert(name, value.clone());
|
|
||||||
Ok(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bind_nested(
|
|
||||||
&mut self,
|
|
||||||
name: Name,
|
|
||||||
term: &Expression,
|
|
||||||
nested: &mut Interpreter,
|
|
||||||
) -> Result<Value> {
|
|
||||||
let value = self.evaluate(term)?;
|
|
||||||
nested.environment.insert(name, value.clone());
|
|
||||||
Ok(value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Interpreter {
|
impl Default for Interpreter {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
pub mod builtins;
|
pub mod builtins;
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
|
pub mod dictionary;
|
||||||
pub mod evaluate;
|
pub mod evaluate;
|
||||||
pub mod parse;
|
pub mod parse;
|
||||||
pub mod syntax;
|
pub mod syntax;
|
||||||
|
pub mod typecheck;
|
||||||
|
|
|
@ -5,7 +5,9 @@ use crate::syntax::{
|
||||||
Item,
|
Item,
|
||||||
InteractiveEntry,
|
InteractiveEntry,
|
||||||
Name,
|
Name,
|
||||||
|
Parameter,
|
||||||
Program,
|
Program,
|
||||||
|
Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
grammar;
|
grammar;
|
||||||
|
@ -18,20 +20,26 @@ pub InteractiveEntry: InteractiveEntry = {
|
||||||
<Expression> ";"? => InteractiveEntry::Expression(<>),
|
<Expression> ";"? => InteractiveEntry::Expression(<>),
|
||||||
}
|
}
|
||||||
|
|
||||||
Item: Item = {
|
Type: Type = {
|
||||||
"fn" <name:Name> "(" <parameters:Comma<Name>> ")" <body:Block> ";"? => Item::Fn { <> },
|
Name => Type::Constructor(<>),
|
||||||
|
"fn" "(" <parameters:Comma<Type>> ")" "->" <result:Boxed<Type>> => Type::Function { <> },
|
||||||
|
"(" <Type> ")",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item: Item = {
|
||||||
|
"fn" <name:Name> "(" <parameters:Comma<Parameter>> ")" <return_type:("->" <Type>)?> <body:Block> ";"? => Item::Fn { <> },
|
||||||
|
}
|
||||||
|
|
||||||
|
Parameter: Parameter =
|
||||||
|
<name:Name> <annotation:(":" <Type>)?> => Parameter { <> };
|
||||||
|
|
||||||
Binding: Binding =
|
Binding: Binding =
|
||||||
"let" <name:Name> "=" <expression:Expression> => Binding { <> };
|
"let" <name:Name> <annotation:(":" <Type>)?> "=" <expression:Expression> => Binding { <> };
|
||||||
|
|
||||||
Expression: Expression = {
|
Expression: Expression = {
|
||||||
Simple,
|
"|" <parameters:Comma<Parameter>> "|" <return_type:("->" <Type>)?> <result:Boxed<Expression>> => Expression::Lambda { <> },
|
||||||
Boxed<Block> => Expression::Block(<>),
|
Boxed<Block> => Expression::Block(<>),
|
||||||
};
|
<expression:Boxed<Atom>> ":" <annotation:Type> => Expression::Annotation { <> },
|
||||||
|
|
||||||
Simple: Expression = {
|
|
||||||
"|" <parameters:Comma<Name>> "|" <result:Boxed<Expression>> => Expression::Lambda { <> },
|
|
||||||
Atom,
|
Atom,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -40,8 +48,8 @@ Atom: Expression = {
|
||||||
<callee:Boxed<Atom>> "(" <arguments:Comma<Expression>> ")" => Expression::Call { <> },
|
<callee:Boxed<Atom>> "(" <arguments:Comma<Expression>> ")" => Expression::Call { <> },
|
||||||
Integer => Expression::Integer(<>),
|
Integer => Expression::Integer(<>),
|
||||||
Boolean => Expression::Boolean(<>),
|
Boolean => Expression::Boolean(<>),
|
||||||
"(" <Expression> ")",
|
|
||||||
"(" ")" => Expression::Unit,
|
"(" ")" => Expression::Unit,
|
||||||
|
"(" <Expression> ")",
|
||||||
};
|
};
|
||||||
|
|
||||||
BlockBinding = <Binding> ";";
|
BlockBinding = <Binding> ";";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Syntax elements.
|
//! Syntax elements.
|
||||||
|
|
||||||
/// Identifiers.
|
/// An identifier.
|
||||||
pub type Name = String;
|
pub type Name = String;
|
||||||
|
|
||||||
/// The rough equivalent of a module.
|
/// The rough equivalent of a module.
|
||||||
|
@ -14,20 +14,49 @@ pub enum InteractiveEntry {
|
||||||
Expression(Expression),
|
Expression(Expression),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Surface syntax types.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Type {
|
||||||
|
/// A type variable.
|
||||||
|
Variable(Name),
|
||||||
|
/// A function type.
|
||||||
|
Function {
|
||||||
|
/// Parameter types.
|
||||||
|
parameters: Vec<Type>,
|
||||||
|
/// The return type.
|
||||||
|
result: Box<Type>,
|
||||||
|
},
|
||||||
|
/// A type constructor.
|
||||||
|
Constructor(Name),
|
||||||
|
/// Special typing sentinel for the print builtin.
|
||||||
|
Print,
|
||||||
|
/// Special typing sentinel for the equals builtin.
|
||||||
|
Equals,
|
||||||
|
}
|
||||||
|
|
||||||
/// A top level definition.
|
/// A top level definition.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Item {
|
pub enum Item {
|
||||||
Fn {
|
Fn {
|
||||||
name: Name,
|
name: Name,
|
||||||
parameters: Vec<Name>,
|
parameters: Vec<Parameter>,
|
||||||
|
return_type: Option<Type>,
|
||||||
body: Block,
|
body: Block,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Function parameter with an optional type.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Parameter {
|
||||||
|
pub name: Name,
|
||||||
|
pub annotation: Option<Type>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Syntactic mapping from an identifier to an expression.
|
/// Syntactic mapping from an identifier to an expression.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Binding {
|
pub struct Binding {
|
||||||
pub name: Name,
|
pub name: Name,
|
||||||
|
pub annotation: Option<Type>,
|
||||||
pub expression: Expression,
|
pub expression: Expression,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,13 +66,18 @@ pub enum Expression {
|
||||||
Variable(Name),
|
Variable(Name),
|
||||||
Block(Box<Block>),
|
Block(Box<Block>),
|
||||||
Lambda {
|
Lambda {
|
||||||
parameters: Vec<Name>,
|
parameters: Vec<Parameter>,
|
||||||
|
return_type: Option<Type>,
|
||||||
result: Box<Expression>,
|
result: Box<Expression>,
|
||||||
},
|
},
|
||||||
Call {
|
Call {
|
||||||
callee: Box<Expression>,
|
callee: Box<Expression>,
|
||||||
arguments: Vec<Expression>,
|
arguments: Vec<Expression>,
|
||||||
},
|
},
|
||||||
|
Annotation {
|
||||||
|
expression: Box<Expression>,
|
||||||
|
annotation: Type,
|
||||||
|
},
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
Integer(i64),
|
Integer(i64),
|
||||||
Unit,
|
Unit,
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
use crate::builtins::Builtin;
|
||||||
|
use crate::dictionary::Dictionary;
|
||||||
|
use crate::syntax::{Program, Type};
|
||||||
|
|
||||||
|
/// Typechecker state.
|
||||||
|
struct Typechecker {
|
||||||
|
environment: Dictionary<Type>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Typechecker {
|
||||||
|
fn new() -> Self {
|
||||||
|
Typechecker {
|
||||||
|
environment: Builtin::type_dictionary(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_program(&mut self, program: &Program) -> Result<(), ()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue