Compare commits
3 Commits
bcee1cf160
...
4e76f1990e
Author | SHA1 | Date |
---|---|---|
mat ess | 4e76f1990e | |
mat ess | e21f575207 | |
mat ess | bce8a006b0 |
|
@ -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,22 @@
|
||||||
|
CECI N'EST PAS UN PERMIS
|
||||||
|
|
||||||
|
Copyright mat ess. All rights reserved.
|
||||||
|
|
||||||
|
This software may not be used by anyone, for any reason.
|
||||||
|
|
||||||
|
As far as the law allows, this software comes as is, without any warranty or condition, and no contributor will be liable to anyone for any damages related to this software or this license, under any kind of legal claim.
|
||||||
|
|
||||||
|
===
|
||||||
|
|
||||||
|
this is not a license.
|
||||||
|
|
||||||
|
free software as envisioned by rms and gnu has failed to defend the freedom of users.
|
||||||
|
open source software is a tool for corporate cooption, control, and profit.
|
||||||
|
|
||||||
|
software should be written for the benefit of the masses.
|
||||||
|
|
||||||
|
if you're still reading this: use, change, or share this code however you want. intellectual property is a farce.
|
||||||
|
|
||||||
|
https://asternova.top/LICENSE.txt
|
||||||
|
https://iliana.fyi/treachery
|
||||||
|
https://www.boringcactus.com/2021/09/29/anti-license-manifesto.html
|
|
@ -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"
|
|
||||||
'';
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
//! Language builtins, wired into the implementation.
|
||||||
|
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
use thiserror::Error;
|
use crate::dictionary::Dictionary;
|
||||||
|
use crate::evaluate::Value;
|
||||||
use crate::evaluate::{Dictionary, Value};
|
use crate::syntax::Type;
|
||||||
|
|
||||||
|
/// Builtin functions.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum Builtin {
|
pub enum Builtin {
|
||||||
Print,
|
Print,
|
||||||
|
@ -16,44 +19,59 @@ pub enum Builtin {
|
||||||
LessThan,
|
LessThan,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[track_caller]
|
||||||
pub enum Error {
|
fn unwrap_int_value(op_name: &'static str) -> impl Fn(Value) -> i64 {
|
||||||
#[error("Tried to {0} a non-integer: {1}")]
|
|
||||||
NonInteger(&'static str, Value),
|
|
||||||
}
|
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, Error>;
|
|
||||||
|
|
||||||
fn unwrap_int_value(op_name: &'static str) -> impl Fn(Value) -> Result<i64> {
|
|
||||||
move |value| match value {
|
move |value| match value {
|
||||||
Value::Integer(i) => Ok(i),
|
Value::Integer(i) => i,
|
||||||
_ => Err(Error::NonInteger(op_name, value)),
|
_ => panic!("got non-integer during {op_name}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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| Ok(acc? $op v?))
|
.reduce(|acc, v| acc $op v)
|
||||||
.unwrap()?,
|
.unwrap();
|
||||||
)
|
Value::Integer(i)
|
||||||
};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! int_cmp {
|
macro_rules! int_cmp {
|
||||||
($arguments:expr, $op:tt) => {{
|
($arguments:expr, $op:tt) => {{
|
||||||
let mut arguments = $arguments;
|
let mut arguments = $arguments;
|
||||||
let unwrap = unwrap_int_value("compare");
|
let unwrap = unwrap_int_value("compare");
|
||||||
let y = unwrap(arguments.pop().unwrap())?;
|
let y = unwrap(arguments.pop().unwrap());
|
||||||
let x = unwrap(arguments.pop().unwrap())?;
|
let x = unwrap(arguments.pop().unwrap());
|
||||||
Value::Boolean(x $op y)
|
Value::Boolean(x $op y)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
pub fn dictionary() -> Dictionary<Value> {
|
pub fn dictionary() -> Dictionary<Value> {
|
||||||
[
|
[
|
||||||
("_print", Builtin::Print),
|
("_print", Builtin::Print),
|
||||||
|
@ -69,6 +87,23 @@ 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.
|
||||||
pub fn min_parameters(&self) -> usize {
|
pub fn min_parameters(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Builtin::Print => 0,
|
Builtin::Print => 0,
|
||||||
|
@ -81,6 +116,8 @@ impl Builtin {
|
||||||
| Builtin::LessThan => 2,
|
| Builtin::LessThan => 2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Maximum # of parameters allowed, if any.
|
||||||
pub fn max_parameters(&self) -> Option<usize> {
|
pub fn max_parameters(&self) -> Option<usize> {
|
||||||
match self {
|
match self {
|
||||||
Builtin::Print
|
Builtin::Print
|
||||||
|
@ -93,8 +130,9 @@ impl Builtin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(&self, arguments: Vec<Value>) -> Result<Value> {
|
/// Execute the given builtin on the provided arguments.
|
||||||
let result = match self {
|
pub fn call(&self, arguments: Vec<Value>) -> Value {
|
||||||
|
match self {
|
||||||
Builtin::Print => {
|
Builtin::Print => {
|
||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
let mut arguments = arguments.into_iter().peekable();
|
let mut arguments = arguments.into_iter().peekable();
|
||||||
|
@ -126,7 +164,6 @@ impl Builtin {
|
||||||
}
|
}
|
||||||
Builtin::GreaterThan => int_cmp!(arguments, >),
|
Builtin::GreaterThan => int_cmp!(arguments, >),
|
||||||
Builtin::LessThan => int_cmp!(arguments, <),
|
Builtin::LessThan => int_cmp!(arguments, <),
|
||||||
};
|
}
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
15
src/cli.rs
15
src/cli.rs
|
@ -1,8 +1,12 @@
|
||||||
use std::{ffi::OsString, path::PathBuf};
|
//! Command line interface for the language binary.
|
||||||
|
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use pico_args::Arguments;
|
use pico_args::Arguments;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Usage help text.
|
||||||
pub const USAGE: &str = "
|
pub const USAGE: &str = "
|
||||||
USAGE:
|
USAGE:
|
||||||
mul [-h|--help] (parse <FILE> | evaluate <FILE> | repl)
|
mul [-h|--help] (parse <FILE> | evaluate <FILE> | repl)
|
||||||
|
@ -16,6 +20,7 @@ COMMANDS:
|
||||||
repl Start the Read-Evaluate-Print-Loop
|
repl Start the Read-Evaluate-Print-Loop
|
||||||
";
|
";
|
||||||
|
|
||||||
|
/// Errors that may occur during command line parsing.
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("Problem while parsing arguments, {0}")]
|
#[error("Problem while parsing arguments, {0}")]
|
||||||
|
@ -26,6 +31,7 @@ pub enum Error {
|
||||||
UnrecognizedArguments(Vec<OsString>),
|
UnrecognizedArguments(Vec<OsString>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Possible top level commands.
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
Parse { path: PathBuf },
|
Parse { path: PathBuf },
|
||||||
Run { path: PathBuf },
|
Run { path: PathBuf },
|
||||||
|
@ -33,8 +39,13 @@ pub enum Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl Command {
|
||||||
|
/// Parse the command line from the current environment.
|
||||||
pub fn from_environment() -> Result<Option<Self>, Error> {
|
pub fn from_environment() -> Result<Option<Self>, Error> {
|
||||||
let mut args = Arguments::from_env();
|
Self::from_arguments(Arguments::from_env())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a command from raw arguments.
|
||||||
|
pub fn from_arguments(mut args: Arguments) -> Result<Option<Self>, Error> {
|
||||||
if args.contains(["-h", "--help"]) {
|
if args.contains(["-h", "--help"]) {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,12 +1,14 @@
|
||||||
use std::{collections::HashMap, fmt::Display};
|
//! Tree-walk interpreter for syntax.
|
||||||
|
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::builtins::{self, 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};
|
||||||
|
|
||||||
pub type Dictionary<T> = HashMap<Name, T>;
|
/// Runtime values.
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Builtin(Builtin),
|
Builtin(Builtin),
|
||||||
|
@ -32,6 +34,7 @@ impl Display for Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Errors that may occur during evaluation.
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("The name '{0} was not bound to any value in this context")]
|
#[error("The name '{0} was not bound to any value in this context")]
|
||||||
|
@ -42,28 +45,41 @@ pub enum Error {
|
||||||
ArgumentsMismatch(usize, usize),
|
ArgumentsMismatch(usize, usize),
|
||||||
#[error("No function called main to run")]
|
#[error("No function called main to run")]
|
||||||
MissingMainFunction,
|
MissingMainFunction,
|
||||||
#[error("Problem with builtin call: {0}")]
|
|
||||||
BuiltinError(#[from] builtins::Error),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, Error>;
|
type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
/// AST walking interpreter.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Interpreter {
|
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.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Interpreter {
|
Interpreter {
|
||||||
environment: Builtin::dictionary(),
|
environment: Builtin::dictionary(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a nested interpreter given an existing environment.
|
||||||
fn nested(environment: Dictionary<Value>) -> Self {
|
fn nested(environment: Dictionary<Value>) -> Self {
|
||||||
Interpreter { environment }
|
Interpreter { environment }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Run a program. Expects a main function to exist.
|
||||||
pub fn run(&mut self, program: &Program) -> Result<Value> {
|
pub fn run(&mut self, program: &Program) -> Result<Value> {
|
||||||
for item in program {
|
for item in program {
|
||||||
self.interpret_item(item)?;
|
self.interpret_item(item)?;
|
||||||
|
@ -75,16 +91,17 @@ impl Interpreter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interpret_item(&mut self, item: &Item) -> Result<Value> {
|
fn interpret_item(&mut self, item: &Item) -> Result<Value> {
|
||||||
match item {
|
match item {
|
||||||
Item::Fn {
|
Item::Fn {
|
||||||
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());
|
||||||
|
@ -93,17 +110,18 @@ impl Interpreter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Evaluate a code fragment from an interactive context.
|
||||||
pub fn evaluate_interactive_entry(&mut self, entry: &InteractiveEntry) -> Result<Value> {
|
pub fn evaluate_interactive_entry(&mut self, entry: &InteractiveEntry) -> Result<Value> {
|
||||||
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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn evaluate(&mut self, term: &Expression) -> Result<Value> {
|
fn evaluate(&mut self, term: &Expression) -> Result<Value> {
|
||||||
match term {
|
match term {
|
||||||
Expression::Variable(name) => self
|
Expression::Variable(name) => self
|
||||||
.environment
|
.environment
|
||||||
|
@ -111,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),
|
||||||
|
@ -139,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)
|
||||||
}
|
}
|
||||||
|
@ -158,7 +179,7 @@ impl Interpreter {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|term| self.evaluate(term))
|
.map(|term| self.evaluate(term))
|
||||||
.collect::<Result<Vec<_>>>()?;
|
.collect::<Result<Vec<_>>>()?;
|
||||||
b.call(arguments).map_err(Error::BuiltinError)
|
Ok(b.call(arguments))
|
||||||
}
|
}
|
||||||
_ => Err(Error::NotAFunction),
|
_ => Err(Error::NotAFunction),
|
||||||
}
|
}
|
||||||
|
@ -167,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;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Transform text into syntax.
|
||||||
|
|
||||||
use lalrpop_util::{lalrpop_mod, lexer::Token, ParseError};
|
use lalrpop_util::{lalrpop_mod, lexer::Token, ParseError};
|
||||||
|
|
||||||
use crate::syntax::{InteractiveEntry, Program};
|
use crate::syntax::{InteractiveEntry, Program};
|
||||||
|
@ -6,10 +8,12 @@ lalrpop_mod!(parser, "/parse/parser.rs");
|
||||||
|
|
||||||
type Result<'input, T> = std::result::Result<T, ParseError<usize, Token<'input>, &'static str>>;
|
type Result<'input, T> = std::result::Result<T, ParseError<usize, Token<'input>, &'static str>>;
|
||||||
|
|
||||||
|
/// Parse a series of items.
|
||||||
pub fn parse_program(input: &str) -> Result<Program> {
|
pub fn parse_program(input: &str) -> Result<Program> {
|
||||||
parser::ProgramParser::new().parse(input)
|
parser::ProgramParser::new().parse(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a fragment of code.
|
||||||
pub fn parse_interactive_entry(input: &str) -> Result<InteractiveEntry> {
|
pub fn parse_interactive_entry(input: &str) -> Result<InteractiveEntry> {
|
||||||
parser::InteractiveEntryParser::new().parse(input)
|
parser::InteractiveEntryParser::new().parse(input)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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