├── .gitignore ├── Cargo.toml ├── docs ├── web-3e88601d133d91f8_bg.wasm ├── index.html └── web-3e88601d133d91f8.js ├── cli ├── Cargo.toml └── src │ └── main.rs ├── web ├── Cargo.toml ├── index.html └── src │ └── main.rs ├── .github └── workflows │ └── rust.yml ├── ordo ├── Cargo.toml ├── src │ ├── lib.rs │ ├── tests │ │ ├── emit_tests.rs │ │ ├── variant_tests.rs │ │ ├── parse_at.rs │ │ ├── ifs.rs │ │ ├── typed_exprs.rs │ │ ├── unwraps.rs │ │ ├── infer_tests.rs │ │ └── parse_only.rs │ ├── env.rs │ ├── core.rs │ ├── lexer.rs │ ├── emit.rs │ ├── expr.rs │ ├── eval.rs │ └── parser.rs ├── benches │ ├── infer_benchmark.rs │ └── eval_benchmark.rs └── Cargo.lock ├── README.md └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | web/dist 3 | web/target 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = ["cli", "ordo"] 4 | exclude = ["web"] 5 | -------------------------------------------------------------------------------- /docs/web-3e88601d133d91f8_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FrankBro/ordo/HEAD/docs/web-3e88601d133d91f8_bg.wasm -------------------------------------------------------------------------------- /cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cli" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | ordo = { path = "../ordo" } 10 | -------------------------------------------------------------------------------- /web/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "web" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | log = "0.4.20" 8 | ordo = { path = "../ordo" } 9 | wasm-logger = "0.2.0" 10 | yew = { git = "https://github.com/yewstack/yew/", features = ["csr"] } 11 | 12 | [dependencies.web-sys] 13 | version = "0.3" 14 | features = [ 15 | "HtmlElement", 16 | "HtmlInputElement", 17 | ] 18 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ordo 6 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /ordo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ordo" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | itertools = "0.12.0" 10 | logos = "0.13.0" 11 | wasmi = "0.31.1" 12 | wat = "1.0.83" 13 | 14 | [dev-dependencies] 15 | criterion = "0.5.1" 16 | 17 | [[bench]] 18 | name = "infer_benchmark" 19 | harness = false 20 | 21 | [[bench]] 22 | name = "eval_benchmark" 23 | harness = false 24 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ordo 5 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /ordo/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod core; 2 | pub mod emit; 3 | pub mod env; 4 | pub mod eval; 5 | pub mod expr; 6 | pub mod infer; 7 | pub mod lexer; 8 | pub mod parser; 9 | 10 | #[cfg(test)] 11 | #[path = "tests/ifs.rs"] 12 | mod ifs; 13 | 14 | #[cfg(test)] 15 | #[path = "tests/unwraps.rs"] 16 | mod unwraps; 17 | 18 | #[cfg(test)] 19 | #[path = "tests/parse_only.rs"] 20 | mod parse_only; 21 | 22 | #[cfg(test)] 23 | #[path = "tests/parse_at.rs"] 24 | mod parse_at; 25 | 26 | #[cfg(test)] 27 | #[path = "tests/infer_tests.rs"] 28 | mod infer_tests; 29 | 30 | #[cfg(test)] 31 | #[path = "tests/typed_exprs.rs"] 32 | mod typed_exprs; 33 | 34 | #[cfg(test)] 35 | #[path = "tests/emit_tests.rs"] 36 | mod emit_tests; 37 | 38 | #[cfg(test)] 39 | #[path = "tests/variant_tests.rs"] 40 | mod variant_tests; 41 | -------------------------------------------------------------------------------- /ordo/src/tests/emit_tests.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | emit::{self, LOAD_NAME}, 3 | infer, 4 | parser::Parser, 5 | }; 6 | use wasmi::*; 7 | 8 | #[track_caller] 9 | fn pass(source: &str, expected: T) { 10 | let mut env = infer::Env::default(); 11 | let expr = Parser::expr(source).unwrap(); 12 | let typed_expr = env.infer(expr.clone()).unwrap(); 13 | let wat = emit::emit(typed_expr).unwrap(); 14 | let wasm = wat::parse_str(wat).unwrap(); 15 | let engine = Engine::default(); 16 | let module = Module::new(&engine, &mut &wasm[..]).unwrap(); 17 | let mut store = Store::new(&engine, ()); 18 | let linker: Linker<()> = Linker::new(&engine); 19 | let instance = linker 20 | .instantiate(&mut store, &module) 21 | .unwrap() 22 | .start(&mut store) 23 | .unwrap(); 24 | let load = instance.get_typed_func::<(), T>(&store, LOAD_NAME).unwrap(); 25 | let actual = load.call(&mut store, ()).unwrap(); 26 | assert_eq!(expected, actual); 27 | } 28 | 29 | #[test] 30 | fn test() { 31 | pass("let add(a, b) = a + b in add(1, 2)", 3_u64); 32 | pass("let a = 1 in a", 1_u64); 33 | pass("4 * 5 / 2 - 1 + 4", 13_u64); 34 | pass("2 == 2", 1_i32); 35 | pass("3 > 4", 0_i32); 36 | } 37 | -------------------------------------------------------------------------------- /ordo/src/tests/variant_tests.rs: -------------------------------------------------------------------------------- 1 | use crate::{core::make_env, infer::Error, parser::Parser}; 2 | 3 | #[track_caller] 4 | fn pass(expr_str: &str, expected: &str) { 5 | let (forall, ty) = Parser::ty(expected).unwrap(); 6 | let mut env = make_env(); 7 | let expected = env.replace_ty_constants_with_vars(forall, ty); 8 | let expr = Parser::expr(expr_str).unwrap(); 9 | let typed_expr = env.infer(expr).unwrap(); 10 | let actual = typed_expr.context.ty.ty; 11 | let expected = env.ty_to_string(&expected).unwrap(); 12 | let actual = env.ty_to_string(&actual).unwrap(); 13 | assert_eq!(expected, actual, "for {}", expr_str); 14 | } 15 | 16 | #[track_caller] 17 | fn fail(expr_str: &str, expected: Error) { 18 | let mut env = make_env(); 19 | let expr = Parser::expr(expr_str).unwrap(); 20 | let actual = env.infer(expr).unwrap_err(); 21 | assert_eq!(expected, actual, "for {}", expr_str); 22 | } 23 | 24 | #[test] 25 | fn variant_tests() { 26 | pass( 27 | "let f(v) = match v { :a a -> 0, :b b -> 1 } in f(:a 1)", 28 | "int", 29 | ); 30 | pass( 31 | "let f(v) = match v { :a a -> 0, :b b -> 1, otherwise -> 2 } in f(:a 1)", 32 | "int", 33 | ); 34 | fail( 35 | "let f(v) = match v { :a a -> 0, :b b -> 1 } in f(:c 1)", 36 | Error::MissingLabel("c".to_owned()), 37 | ); 38 | pass( 39 | "let f(v) = match v { :a a -> 0, :b b -> 1, otherwise -> 2 } in f(:c 1)", 40 | "int", 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /ordo/src/env.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::{ 4 | core::make_env, 5 | eval, infer, 6 | parser::{self, Parser}, 7 | }; 8 | 9 | #[derive(Debug)] 10 | pub enum Error { 11 | Parser(parser::Error), 12 | Infer(infer::Error), 13 | Eval(eval::Error), 14 | } 15 | 16 | impl From for Error { 17 | fn from(value: parser::Error) -> Self { 18 | Self::Parser(value) 19 | } 20 | } 21 | 22 | impl From for Error { 23 | fn from(value: infer::Error) -> Self { 24 | Self::Infer(value) 25 | } 26 | } 27 | 28 | impl From for Error { 29 | fn from(value: eval::Error) -> Self { 30 | Self::Eval(value) 31 | } 32 | } 33 | 34 | impl fmt::Display for Error { 35 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 36 | match self { 37 | Error::Parser(p) => write!(f, "parser: {}", p), 38 | Error::Infer(i) => write!(f, "infer: {}", i), 39 | Error::Eval(e) => write!(f, "eval: {}", e), 40 | } 41 | } 42 | } 43 | 44 | type Result = std::result::Result; 45 | 46 | pub struct Output { 47 | pub ty: String, 48 | pub val: String, 49 | } 50 | 51 | pub struct Env { 52 | pub infer: infer::Env, 53 | pub eval: eval::Env, 54 | } 55 | 56 | impl Default for Env { 57 | fn default() -> Self { 58 | let infer = make_env(); 59 | let eval = Default::default(); 60 | Self { infer, eval } 61 | } 62 | } 63 | 64 | impl Env { 65 | pub fn process(&mut self, source: &str) -> Result { 66 | let expr = Parser::repl(source)?; 67 | let typed_expr = self.infer.infer(expr.clone())?; 68 | let ty = self.infer.ty_to_string(typed_expr.ty()).unwrap(); 69 | let value = self.eval.eval(typed_expr)?; 70 | let val = value.to_string(); 71 | Ok(Output { ty, val }) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /ordo/src/tests/parse_at.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | expr::{Expr, ExprAt, IntBinOp, PatternAt, Position, PositionContext}, 3 | parser::Parser, 4 | }; 5 | 6 | pub fn int_at(i: i64, context: PositionContext) -> ExprAt { 7 | ExprAt { 8 | context, 9 | expr: Expr::Int(i).into(), 10 | } 11 | } 12 | 13 | pub fn var_at(var: &str, context: PositionContext) -> ExprAt { 14 | ExprAt { 15 | context, 16 | expr: Expr::Var(var.to_owned()).into(), 17 | } 18 | } 19 | 20 | pub fn let_at(var: PatternAt, value: ExprAt, body: ExprAt, context: PositionContext) -> ExprAt { 21 | ExprAt { 22 | context, 23 | expr: Expr::Let(var, value, body).into(), 24 | } 25 | } 26 | 27 | pub fn plus_at(lhs: ExprAt, rhs: ExprAt, context: PositionContext) -> ExprAt { 28 | ExprAt { 29 | context, 30 | expr: Expr::IntBinOp(IntBinOp::Plus, lhs, rhs).into(), 31 | } 32 | } 33 | 34 | fn pass_at(source: &str, expected: ExprAt) { 35 | let actual = Parser::expr(source).unwrap(); 36 | assert_eq!(expected, actual); 37 | } 38 | 39 | fn loc(sl: usize, sc: usize, el: usize, ec: usize) -> PositionContext { 40 | PositionContext { 41 | start: Position { 42 | line: sl, 43 | column: sc, 44 | }, 45 | end: Position { 46 | line: el, 47 | column: ec, 48 | }, 49 | } 50 | } 51 | 52 | #[test] 53 | fn at() { 54 | pass_at("a", var_at("a", loc(0, 0, 0, 1))); 55 | pass_at("1", int_at(1, loc(0, 0, 0, 1))); 56 | pass_at( 57 | "1 + 2", 58 | plus_at( 59 | int_at(1, loc(0, 0, 0, 1)), 60 | int_at(2, loc(0, 4, 0, 5)), 61 | loc(0, 2, 0, 3), 62 | ), 63 | ); 64 | pass_at( 65 | "let a = 1 in\na", 66 | let_at( 67 | var_at("a", loc(0, 4, 0, 5)), 68 | int_at(1, loc(0, 8, 0, 9)), 69 | var_at("a", loc(1, 0, 1, 1)), 70 | loc(0, 0, 0, 3), 71 | ), 72 | ); 73 | pass_at("10", int_at(10, loc(0, 0, 0, 2))); 74 | } 75 | -------------------------------------------------------------------------------- /ordo/src/core.rs: -------------------------------------------------------------------------------- 1 | use crate::{infer::Env, parser::Parser}; 2 | 3 | #[track_caller] 4 | fn assume(env: &mut Env, name: &str, sig: &str) { 5 | let (forall, ty) = Parser::ty(sig).unwrap(); 6 | let ty = env.replace_ty_constants_with_vars(forall, ty); 7 | env.insert_var(name.to_owned(), ty); 8 | } 9 | 10 | pub fn make_env() -> Env { 11 | let mut env = Env::default(); 12 | assume(&mut env, "head", "forall a => list[a] -> a"); 13 | assume(&mut env, "tail", "forall a => list[a] -> list[a]"); 14 | assume(&mut env, "nil", "forall a => list[a]"); 15 | assume(&mut env, "cons", "forall a => (a, list[a]) -> list[a]"); 16 | assume( 17 | &mut env, 18 | "cons_curry", 19 | "forall a => a -> list[a] -> list[a]", 20 | ); 21 | assume( 22 | &mut env, 23 | "map", 24 | "forall a b => (a -> b, list[a]) -> list[b]", 25 | ); 26 | assume( 27 | &mut env, 28 | "map_curry", 29 | "forall a b => (a -> b) -> list[a] -> list[b]", 30 | ); 31 | assume(&mut env, "one", "int"); 32 | assume(&mut env, "zero", "int"); 33 | assume(&mut env, "half", "float"); 34 | assume(&mut env, "succ", "int -> int"); 35 | assume(&mut env, "plus", "(int, int) -> int"); 36 | assume(&mut env, "eq", "forall a => (a, a) -> bool"); 37 | assume(&mut env, "eq_curry", "forall a => a -> a -> bool"); 38 | assume(&mut env, "not", "bool -> bool"); 39 | assume(&mut env, "pair", "forall a b => (a, b) -> pair[a, b]"); 40 | assume(&mut env, "pair_curry", "forall a b => a -> b -> pair[a, b]"); 41 | assume(&mut env, "first", "forall a b => pair[a, b] -> a"); 42 | assume(&mut env, "second", "forall a b => pair[a, b] -> b"); 43 | assume(&mut env, "id", "forall a => a -> a"); 44 | assume(&mut env, "const", "forall a b => a -> b -> a"); 45 | assume(&mut env, "apply", "forall a b => (a -> b, a) -> b"); 46 | assume(&mut env, "apply_curry", "forall a b => (a -> b) -> a -> b"); 47 | assume(&mut env, "choose", "forall a => (a, a) -> a"); 48 | assume(&mut env, "choose_curry", "forall a => a -> a -> a"); 49 | env 50 | } 51 | -------------------------------------------------------------------------------- /ordo/src/tests/ifs.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use crate::{ 4 | eval::{self, Value}, 5 | infer, 6 | parser::Parser, 7 | }; 8 | 9 | #[track_caller] 10 | fn pass(source_expr: &str, source_ty: &str, expected_val: Value) { 11 | let (forall, ty) = Parser::ty(source_ty).unwrap(); 12 | let mut env = infer::Env::default(); 13 | let expected = env.replace_ty_constants_with_vars(forall, ty); 14 | let expr = Parser::expr(source_expr).unwrap(); 15 | let typed_expr = env.infer(expr.clone()).unwrap(); 16 | let actual = typed_expr.context.ty.ty.clone(); 17 | let expected_ty = env.ty_to_string(&expected).unwrap(); 18 | let actual_ty = env.ty_to_string(&actual).unwrap(); 19 | assert_eq!(expected_ty, actual_ty); 20 | let mut env = eval::Env::default(); 21 | let actual_val = env.eval(typed_expr).unwrap(); 22 | assert_eq!(expected_val, actual_val); 23 | } 24 | 25 | #[track_caller] 26 | fn fail_ty(source_expr: &str, expected: infer::Error) { 27 | let mut env = infer::Env::default(); 28 | let expr = Parser::expr(source_expr).unwrap(); 29 | let actual = env.infer(expr).unwrap_err(); 30 | assert_eq!(expected, actual); 31 | } 32 | 33 | #[test] 34 | fn ifs() { 35 | pass("if 1 == 1 then 1 else 0", "int", Value::Int(1)); 36 | pass("if 1 == 0 then 1 else 0", "int", Value::Int(0)); 37 | fail_ty( 38 | "if 1 then 1 else 0", 39 | infer::Error::CannotUnify("bool".to_owned(), "int".to_owned()), 40 | ); 41 | fail_ty( 42 | "if 1 == 0 then true else 1", 43 | infer::Error::CannotUnify("bool".to_owned(), "int".to_owned()), 44 | ); 45 | fail_ty( 46 | "if 1 == 0 then 1 else true", 47 | infer::Error::CannotUnify("int".to_owned(), "bool".to_owned()), 48 | ); 49 | pass( 50 | "if 1 == 1 then :one {} else :zero {}", 51 | "forall ra. (ra\\one\\zero) => [one: {}, zero: {} | ra]", 52 | Value::Variant("one".to_owned(), Value::Record(BTreeMap::new()).into()), 53 | ); 54 | pass( 55 | "if 1 == 1 then {a = 1} else {a = 0}", 56 | "{a: int}", 57 | Value::record(vec![("a", Value::Int(1))]), 58 | ); 59 | fail_ty( 60 | "if 1 == 1 then {a = 1} else {b = 0}", 61 | infer::Error::MissingLabel("b".to_owned()), 62 | ); 63 | } 64 | -------------------------------------------------------------------------------- /ordo/benches/infer_benchmark.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, Criterion}; 2 | use ordo::{infer::Env, parser::Parser}; 3 | 4 | pub fn infer_benchmark(c: &mut Criterion) { 5 | let mut group = c.benchmark_group("infer"); 6 | 7 | let expr = "let a = 1 in a"; 8 | group.bench_function(expr, |b| { 9 | b.iter(|| { 10 | let source = black_box(expr); 11 | let parsed = Parser::expr(source).unwrap(); 12 | let mut env = black_box(Env::default()); 13 | let typed = env.infer(parsed).unwrap(); 14 | black_box(typed) 15 | }) 16 | }); 17 | 18 | let expr = "2 + 3 * 4"; 19 | group.bench_function(expr, |b| { 20 | b.iter(|| { 21 | let source = black_box(expr); 22 | let parsed = Parser::expr(source).unwrap(); 23 | let mut env = black_box(Env::default()); 24 | let typed = env.infer(parsed).unwrap(); 25 | black_box(typed) 26 | }) 27 | }); 28 | 29 | let expr = "let add(a, b) = a + b in add(2, 3)"; 30 | group.bench_function(expr, |b| { 31 | b.iter(|| { 32 | let source = black_box(expr); 33 | let parsed = Parser::expr(source).unwrap(); 34 | let mut env = black_box(Env::default()); 35 | let typed = env.infer(parsed).unwrap(); 36 | black_box(typed) 37 | }) 38 | }); 39 | 40 | let expr = "let add({a, b}) = a + b in let a = 2 in let b = 3 in add({a, b})"; 41 | group.bench_function(expr, |b| { 42 | b.iter(|| { 43 | let source = black_box(expr); 44 | let parsed = Parser::expr(source).unwrap(); 45 | let mut env = black_box(Env::default()); 46 | let typed = env.infer(parsed).unwrap(); 47 | black_box(typed) 48 | }) 49 | }); 50 | 51 | let expr = 52 | "let safe_div(n, d) = if d == 0 then :div_by_zero {} else :ok (n / d) in safe_div(4, 2)"; 53 | group.bench_function(expr, |b| { 54 | b.iter(|| { 55 | let source = black_box(expr); 56 | let parsed = Parser::expr(source).unwrap(); 57 | let mut env = black_box(Env::default()); 58 | let typed = env.infer(parsed).unwrap(); 59 | black_box(typed) 60 | }) 61 | }); 62 | } 63 | 64 | criterion_group!(benches, infer_benchmark); 65 | criterion_main!(benches); 66 | -------------------------------------------------------------------------------- /ordo/benches/eval_benchmark.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, Criterion}; 2 | use ordo::{eval, infer, parser::Parser}; 3 | 4 | pub fn eval_benchmark(c: &mut Criterion) { 5 | let mut group = c.benchmark_group("eval"); 6 | 7 | let expr = "let a = 1 in a"; 8 | group.bench_function(expr, |b| { 9 | b.iter(|| { 10 | let source = black_box(expr); 11 | let parsed = Parser::expr(source).unwrap(); 12 | let mut env = black_box(infer::Env::default()); 13 | let typed = env.infer(parsed).unwrap(); 14 | let mut env = black_box(eval::Env::default()); 15 | let result = env.eval(typed).unwrap(); 16 | black_box(result) 17 | }) 18 | }); 19 | 20 | let expr = "2 + 3 * 4"; 21 | group.bench_function(expr, |b| { 22 | b.iter(|| { 23 | let source = black_box(expr); 24 | let parsed = Parser::expr(source).unwrap(); 25 | let mut env = black_box(infer::Env::default()); 26 | let typed = env.infer(parsed).unwrap(); 27 | black_box(typed) 28 | }) 29 | }); 30 | 31 | let expr = "let add(a, b) = a + b in add(2, 3)"; 32 | group.bench_function(expr, |b| { 33 | b.iter(|| { 34 | let source = black_box(expr); 35 | let parsed = Parser::expr(source).unwrap(); 36 | let mut env = black_box(infer::Env::default()); 37 | let typed = env.infer(parsed).unwrap(); 38 | black_box(typed) 39 | }) 40 | }); 41 | 42 | let expr = "let add({a, b}) = a + b in let a = 2 in let b = 3 in add({a, b})"; 43 | group.bench_function(expr, |b| { 44 | b.iter(|| { 45 | let source = black_box(expr); 46 | let parsed = Parser::expr(source).unwrap(); 47 | let mut env = black_box(infer::Env::default()); 48 | let typed = env.infer(parsed).unwrap(); 49 | black_box(typed) 50 | }) 51 | }); 52 | 53 | let expr = 54 | "let safe_div(n, d) = if d == 0 then :div_by_zero {} else :ok (n / d) in safe_div(4, 2)"; 55 | group.bench_function(expr, |b| { 56 | b.iter(|| { 57 | let source = black_box(expr); 58 | let parsed = Parser::expr(source).unwrap(); 59 | let mut env = black_box(infer::Env::default()); 60 | let typed = env.infer(parsed).unwrap(); 61 | black_box(typed) 62 | }) 63 | }); 64 | } 65 | 66 | criterion_group!(benches, eval_benchmark); 67 | criterion_main!(benches); 68 | -------------------------------------------------------------------------------- /ordo/src/tests/typed_exprs.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | core::make_env, 3 | expr::{Expr, ExprIn, ExprTyped, IntBinOp, Type, TypeContext}, 4 | parser::Parser, 5 | }; 6 | 7 | #[track_caller] 8 | fn pass(expr_str: &str, expected: ExprTyped) { 9 | let mut env = make_env(); 10 | let expr = Parser::expr(expr_str).unwrap(); 11 | let actual = env.infer(expr).unwrap(); 12 | let actual = actual.strip_position(); 13 | assert_eq!(expected, actual, "for {}", expr_str); 14 | } 15 | 16 | fn expr(expr: Expr, ty: Type) -> ExprTyped { 17 | let context = TypeContext { ty }; 18 | let expr = Box::new(expr); 19 | ExprIn { context, expr } 20 | } 21 | 22 | fn int(i: i64) -> ExprTyped { 23 | expr(Expr::Int(i), Type::int()) 24 | } 25 | 26 | fn var(name: &str, ty: Type) -> ExprTyped { 27 | expr(Expr::Var(name.to_owned()), ty) 28 | } 29 | 30 | fn let_(pattern: ExprTyped, value: ExprTyped, body: ExprTyped) -> ExprTyped { 31 | let ty = body.context.ty.clone(); 32 | expr(Expr::Let(pattern, value, body), ty) 33 | } 34 | 35 | fn fun(params: Vec, body: ExprTyped) -> ExprTyped { 36 | let params_ty = params 37 | .iter() 38 | .map(|param| param.context.ty.clone()) 39 | .collect(); 40 | let ty = Type::Arrow(params_ty, body.context.ty.clone().into()); 41 | expr(Expr::Fun(params, body), ty) 42 | } 43 | 44 | fn add(a: ExprTyped, b: ExprTyped) -> ExprTyped { 45 | let op = IntBinOp::Plus; 46 | let ty = op.output_ty(); 47 | expr(Expr::IntBinOp(op, a, b), ty) 48 | } 49 | 50 | fn call(fun: ExprTyped, args: Vec) -> ExprTyped { 51 | let ty = match fun.context.ty.clone() { 52 | Type::Arrow(_, ret) => *ret, 53 | _ => unreachable!(), 54 | }; 55 | expr(Expr::Call(fun, args), ty) 56 | } 57 | 58 | #[test] 59 | fn tests() { 60 | pass( 61 | "let x = 1 in x", 62 | let_(var("x", Type::int()), int(1), var("x", Type::int())), 63 | ); 64 | pass( 65 | "let add(a, b) = a + b in add(1, 2)", 66 | let_( 67 | var( 68 | "add", 69 | Type::Arrow(vec![Type::int(), Type::int()], Type::int().into()), 70 | ), 71 | fun( 72 | vec![var("a", Type::int()), var("b", Type::int())], 73 | add(var("a", Type::int()), var("b", Type::int())), 74 | ), 75 | call( 76 | var( 77 | "add", 78 | Type::Arrow(vec![Type::int(), Type::int()], Type::int().into()), 79 | ), 80 | vec![int(1), int(2)], 81 | ), 82 | ), 83 | ); 84 | } 85 | -------------------------------------------------------------------------------- /cli/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, stdout, BufRead, Write}; 2 | 3 | use ordo::env::Env; 4 | 5 | const ORDO: &str = "ordo>>> "; 6 | const ERROR: &str = "error: "; 7 | 8 | fn print_ordo() { 9 | print!("{}", ORDO); 10 | stdout().flush().unwrap(); 11 | } 12 | 13 | fn main() { 14 | let mut env = Env::default(); 15 | let stdin = io::stdin(); 16 | print_ordo(); 17 | for line in stdin.lock().lines() { 18 | let line = line.unwrap(); 19 | let source = line.trim_end(); 20 | match env.process(source) { 21 | Ok(output) => { 22 | println!("{}", output.ty); 23 | println!("{}", output.val); 24 | } 25 | Err(e) => { 26 | println!("{}{}", ERROR, e); 27 | } 28 | } 29 | print_ordo(); 30 | } 31 | } 32 | 33 | #[test] 34 | fn readme_test() { 35 | let file = std::fs::read_to_string("../README.md").unwrap(); 36 | let lines: Vec<&str> = file.lines().collect(); 37 | let mut i = 0; 38 | 39 | let mut env = Env::default(); 40 | 41 | while i < lines.len() { 42 | if lines[i].starts_with(ORDO) { 43 | let source = lines[i]; 44 | let source = source.strip_prefix(ORDO).unwrap(); 45 | let expected = lines[i + 1]; 46 | let actual = env.process(source); 47 | match actual { 48 | Ok(actual) => { 49 | if expected.starts_with(ERROR) { 50 | panic!( 51 | "input '{}' succeeded but expected an error: {}", 52 | source, expected 53 | ); 54 | } else { 55 | let expected_ty = expected; 56 | let expected_val = lines[i + 2]; 57 | assert_eq!(expected_ty, actual.ty, "for {}", source); 58 | assert_eq!(expected_val, actual.val, "for {}", source); 59 | i += 3; 60 | } 61 | } 62 | Err(actual) => { 63 | if expected.starts_with(ERROR) { 64 | let actual = format!("{}{}", ERROR, actual); 65 | assert_eq!(expected, &actual); 66 | i += 2; 67 | } else { 68 | panic!( 69 | "input '{}' error '{}' but expected a success: {}", 70 | source, actual, expected 71 | ); 72 | } 73 | } 74 | } 75 | } else { 76 | i += 1; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /ordo/src/tests/unwraps.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use crate::{ 4 | eval::{self, Value}, 5 | infer, 6 | parser::Parser, 7 | }; 8 | 9 | struct Env { 10 | infer: infer::Env, 11 | eval: eval::Env, 12 | } 13 | 14 | impl Env { 15 | fn new() -> Self { 16 | let infer = infer::Env::default(); 17 | let eval = eval::Env::default(); 18 | let mut env = Self { infer, eval }; 19 | env.add( 20 | "let safe_div(n, d) = if d == 0 then :div_by_zero {} else :ok (n / d)", 21 | "forall ra. (ra\\div_by_zero\\ok) => (int, int) -> [div_by_zero: {}, ok: int | ra]", 22 | ); 23 | env.add( 24 | "let safe_minus(x, y) = if y < 0 then :would_add {} else :ok (x - y)", 25 | "forall ra. (ra\\ok\\would_add) => (int, int) -> [ok: int, would_add: {} | ra]", 26 | ); 27 | env 28 | } 29 | 30 | fn add(&mut self, source: &str, source_ty: &str) { 31 | let (forall, ty) = Parser::ty(source_ty).unwrap(); 32 | let expected_ty = self.infer.replace_ty_constants_with_vars(forall, ty); 33 | let expr = Parser::repl(source).unwrap(); 34 | let typed_expr = self.infer.infer(expr.clone()).unwrap(); 35 | let actual_ty = typed_expr.context.ty.ty.clone(); 36 | let expected_ty = self.infer.ty_to_string(&expected_ty).unwrap(); 37 | let actual_ty = self.infer.ty_to_string(&actual_ty).unwrap(); 38 | assert_eq!(expected_ty, actual_ty); 39 | let _ = self.eval.eval(typed_expr).unwrap(); 40 | } 41 | } 42 | 43 | #[track_caller] 44 | fn pass(source: &str, source_ty: &str, expected_val: Value) { 45 | let mut env = Env::new(); 46 | let (forall, ty) = Parser::ty(source_ty).unwrap(); 47 | let expected_ty = env.infer.replace_ty_constants_with_vars(forall, ty); 48 | let expr = Parser::expr(source).unwrap(); 49 | let typed_expr = env.infer.infer(expr.clone()).unwrap(); 50 | let actual_ty = typed_expr.context.ty.ty.clone(); 51 | let expected_ty = env.infer.ty_to_string(&expected_ty).unwrap(); 52 | let actual_ty = env.infer.ty_to_string(&actual_ty).unwrap(); 53 | assert_eq!(expected_ty, actual_ty); 54 | let actual_val = env.eval.eval(typed_expr).unwrap(); 55 | assert_eq!(expected_val, actual_val); 56 | } 57 | 58 | #[test] 59 | fn unwrap_ok() { 60 | pass( 61 | "safe_div(2, 2)?", 62 | "forall ra. (ra\\div_by_zero\\ok) => [div_by_zero: {}, ok: int | ra]", 63 | Value::Int(1), 64 | ); 65 | pass( 66 | "safe_div(2, 0)?", 67 | "forall ra. (ra\\div_by_zero\\ok) => [div_by_zero: {}, ok: int | ra]", 68 | Value::Variant( 69 | "div_by_zero".to_owned(), 70 | Value::Record(BTreeMap::new()).into(), 71 | ), 72 | ); 73 | pass( 74 | "let x = safe_div(2, 2)? in x", 75 | "forall ra. (ra\\div_by_zero\\ok) => [div_by_zero: {}, ok: int | ra]", 76 | Value::Int(1), 77 | ); 78 | pass( 79 | "let x = safe_div(2, 0)? in x", 80 | "forall ra. (ra\\div_by_zero\\ok) => [div_by_zero: {}, ok: int | ra]", 81 | Value::Variant("div_by_zero".to_owned(), Value::record(Vec::new()).into()), 82 | ); 83 | pass( 84 | "let x = safe_div(2, 2)? in let y = safe_minus(x, 1)? in y", 85 | "forall ra. (ra\\div_by_zero\\ok\\would_add) => [div_by_zero: {}, ok: int, would_add: {} | ra]", 86 | Value::Int(0), 87 | ); 88 | } 89 | -------------------------------------------------------------------------------- /web/src/main.rs: -------------------------------------------------------------------------------- 1 | use ordo::env::{Env, Error, Output}; 2 | use web_sys::HtmlInputElement; 3 | use yew::prelude::*; 4 | 5 | const ENTER_KEY: u32 = 13; 6 | 7 | enum Message { 8 | ProcessLine(String), 9 | } 10 | 11 | #[derive(Default)] 12 | struct App { 13 | env: Env, 14 | history: Vec<(String, Result)>, 15 | } 16 | 17 | impl Component for App { 18 | type Message = Message; 19 | 20 | type Properties = (); 21 | 22 | fn create(_ctx: &Context) -> Self { 23 | Self::default() 24 | } 25 | 26 | fn update(&mut self, _ctx: &Context, msg: Self::Message) -> bool { 27 | match msg { 28 | Message::ProcessLine(source) => { 29 | let res = self.env.process(&source); 30 | self.history.push((source, res)); 31 | true 32 | } 33 | } 34 | } 35 | 36 | fn view(&self, ctx: &Context) -> Html { 37 | let link = ctx.link().clone(); 38 | let onkeydown = move |e: KeyboardEvent| { 39 | if e.key_code() == ENTER_KEY { 40 | let input: HtmlInputElement = e.target_unchecked_into(); 41 | let line = input.value(); 42 | input.set_value(""); 43 | link.send_message(Message::ProcessLine(line)) 44 | } 45 | }; 46 | html! { 47 |
48 |
49 |
50 | { render_legend("History") } 51 | { self.render_history() } 52 |
53 |
54 | { render_legend("Input") } 55 | 56 |
57 |
58 |
59 | { render_legend("Environment") } 60 | { self.render_env() } 61 |
62 |
63 | } 64 | } 65 | } 66 | 67 | impl App { 68 | fn render_history(&self) -> Html { 69 | let history = self 70 | .history 71 | .iter() 72 | .map(|(line, res)| render_line(line, res)); 73 | html! { 74 |
75 | { for history } 76 |
77 | } 78 | } 79 | 80 | fn render_env(&self) -> Html { 81 | let env = self 82 | .env 83 | .infer 84 | .vars 85 | .iter() 86 | .filter_map(|(var, ty)| { 87 | self.env.eval.vars.get(var).map(|val| { 88 | ( 89 | var, 90 | self.env.infer.ty_to_string(ty).unwrap(), 91 | val.to_string(), 92 | ) 93 | }) 94 | }) 95 | .map(|(var, ty, val)| render_input(var, &ty, &val)); 96 | html! { 97 |
98 | { for env } 99 |
100 | } 101 | } 102 | } 103 | 104 | fn render_input(input: &String, ty: &String, val: &String) -> Html { 105 | html! { 106 |
107 |
{input}
108 |
109 |
{val}
110 |
{ty}
111 |
112 |
113 | } 114 | } 115 | 116 | fn render_legend(title: &str) -> Html { 117 | html! { 118 |

{title}

119 | } 120 | } 121 | 122 | fn render_line(line: &String, res: &Result) -> Html { 123 | match res { 124 | Ok(output) => render_input(line, &output.ty, &output.val), 125 | Err(e) => { 126 | let value = e.to_string(); 127 | html! { 128 |
129 |
{line}
130 |
{value}
131 |
132 | } 133 | } 134 | } 135 | } 136 | 137 | fn main() { 138 | wasm_logger::init(wasm_logger::Config::new(log::Level::Trace)); 139 | yew::Renderer::::new().render(); 140 | } 141 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![example workflow](https://github.com/frankbro/ordo/actions/workflows/rust.yml/badge.svg) 2 | 3 | # Ordo 4 | 5 | Latin : ordo 6 | 7 | English : a methodical series, arrangement, or order; regular line, row, or series 8 | 9 | ## Introduction 10 | 11 | A minimal statically-typed programming language focused around row polymorphism, which allows to infer structural types: records and variants. 12 | 13 | * Polymorphic types: 14 | ```ocaml 15 | ordo>>> let f(x) = x 16 | forall a => a -> a 17 | fun(x) 18 | ``` 19 | 20 | * Records: 21 | ```ocaml 22 | ordo>>> let record = { x = 1, y = 2 } 23 | {x: int, y: int} 24 | {x: 1, y: 2} 25 | ``` 26 | 27 | * Variants: 28 | ```ocaml 29 | ordo>>> let variant = :variant 1 30 | forall ra. (ra\variant) => [variant: int | ra] 31 | :variant 1 32 | ``` 33 | 34 | The extra syntax at the end of the variant means it is an open variant. Rows (record or variants) can be open or closed. By default, record literals are closed but variant literals are open. Rows also infer their restrictions. 35 | 36 | Here, `forall` signifies that generic types will be given next. We might also have a portion between parenthesis, indicating row restrictions. In this case, for a generic type `ra`, which we know is a row because it's followed by the restriction that this row should not have the value `variant`. Next we finally have the actual type of our expression, we know it's a variant because it's surrounded by `< >`, whereas records are surrounded by `{ }`. The type of the expression is a variant that extends the generic row `r` by having a new case that is specified: `variant` of type `int`. 37 | 38 | ## Record 39 | 40 | * Record initialization 41 | ```ocaml 42 | ordo>>> {} 43 | {} 44 | {} 45 | ``` 46 | * Record extension adds fields to the record 47 | ```ocaml 48 | ordo>>> { x = 1 | {} } 49 | {x: int} 50 | {x: 1} 51 | ``` 52 | * Record initialization, being sugar for record extension 53 | ```ocaml 54 | ordo>>> { x = 1 } == { x = 1 | {} } 55 | bool 56 | true 57 | ``` 58 | * The order of insertion of labels is not important 59 | ```ocaml 60 | ordo>>> { x = 1, y = 2 } == { y = 2, x = 1 } 61 | bool 62 | true 63 | ``` 64 | * Record restriction removes fields from a record 65 | ```ocaml 66 | ordo>>> { x = 1 }\x == {} 67 | bool 68 | true 69 | ``` 70 | * Record cannot have the same label twice 71 | ```ocaml 72 | ordo>>> { x = 1, x = 2 } 73 | error: parser: duplicate label: x 74 | ``` 75 | 76 | In fact, we can see this property being infered by the type system and shown: 77 | 78 | ```ocaml 79 | ordo>>> let f(r) = { y = 0 | r } 80 | forall ra. (ra\y) => {ra} -> {y: int | ra} 81 | fun(r) 82 | ordo>>> f({ x = 0 }) 83 | {x: int, y: int} 84 | {x: 0, y: 0} 85 | ``` 86 | 87 | The signature here specifies that the function takes a row that does not contain the field `y`. If you provide a record with the field `y` already it in, the type inference won't let you. 88 | 89 | ```ocaml 90 | ordo>>> f({ y = 1 }) 91 | error: infer: row constraint failed for label: y 92 | ``` 93 | 94 | * Structual pattern matching and even make sure it is valid via type checking: 95 | 96 | ```ocaml 97 | ordo>>> let { x = x } = { x = 1 } 98 | {x: int} 99 | {x: 1} 100 | ordo>>> x 101 | int 102 | 1 103 | ordo>>> let { y = y } = { x = 1 } 104 | error: infer: missing label: y 105 | ``` 106 | 107 | * Record matching is open 108 | 109 | While record literals are closed rows, record matching is open: 110 | 111 | ```ocaml 112 | ordo>>> let record = { x = 0 } 113 | {x: int} 114 | {x: 0} 115 | ordo>>> let f({ x = x }) = x 116 | forall a ra. (ra\x) => {x: a | ra} -> a 117 | fun({x: x}) 118 | ``` 119 | 120 | * Sugar for matching and creation 121 | 122 | Records can either directly assign fields to variables in a shorthand syntax or capture variables via closure and set them automatically to their variable name as label: 123 | 124 | ```ocaml 125 | ordo>>> let f({x,y}) = x + y 126 | forall ra. (ra\x\y) => {x: int, y: int | ra} -> int 127 | fun({x: x, y: y}) 128 | ordo>>> let x = 1 129 | int 130 | 1 131 | ordo>>> let y = 2 132 | int 133 | 2 134 | ordo>>> f({x,y}) 135 | int 136 | 3 137 | ``` 138 | 139 | ## Variant 140 | 141 | The few new expressions introduced for variants: 142 | 143 | * Variant creation 144 | 145 | ```ocaml 146 | ordo>>> let variant = :variant 1 147 | forall ra. (ra\variant) => [variant: int | ra] 148 | :variant 1 149 | ``` 150 | 151 | * Variant elimination 152 | 153 | Variant can be eliminated via pattern matching. While variant literals are open rows, we can deduce if the input if open on closed based on the fact that a default case is provided or not. 154 | 155 | ```ocaml 156 | ordo>>> let default_with(default, value) = match value { :some value -> value, :none x -> default } 157 | forall a b => (a, [none: b, some: a]) -> a 158 | fun(default, value) 159 | ``` 160 | 161 | In this case, we did not provide a default case and therefore, the variant was infered to be closed. However, if we wrote it this way: 162 | 163 | ```ocaml 164 | ordo>>> let is_success(v) = match v { :success a -> true , otherwise -> false } 165 | forall a ra. (ra\success) => [success: a | ra] -> bool 166 | fun(v) 167 | ``` 168 | 169 | This is useful to make sure which variant can be passed in. 170 | 171 | ```ocaml 172 | ordo>>> is_success(:fail 0) 173 | bool 174 | false 175 | ordo>>> default_with(1, :fail 0) 176 | error: infer: missing label: fail 177 | ``` 178 | -------------------------------------------------------------------------------- /ordo/src/lexer.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | 3 | use logos::{Lexer, Logos, Skip}; 4 | 5 | fn int(lex: &mut Lexer) -> Option { 6 | let slice = lex.slice(); 7 | if slice.starts_with('-') { 8 | let int: i64 = slice[1..slice.len()].parse().ok()?; 9 | Some(-int) 10 | } else { 11 | let int: i64 = slice[..slice.len()].parse().ok()?; 12 | Some(int) 13 | } 14 | } 15 | 16 | fn ident(lex: &mut Lexer) -> Option { 17 | let slice = lex.slice(); 18 | let ident: String = slice[..slice.len()].parse().ok()?; 19 | Some(ident) 20 | } 21 | 22 | #[derive(Default)] 23 | pub struct Extras { 24 | pub line_breaks: usize, 25 | pub column_offset: usize, 26 | } 27 | 28 | fn newline(lex: &mut Lexer) -> Skip { 29 | lex.extras.line_breaks += 1; 30 | lex.extras.column_offset = lex.span().end; 31 | Skip 32 | } 33 | 34 | #[derive(Logos, Clone, Debug, PartialEq)] 35 | #[logos(extras = Extras)] 36 | #[logos(skip r"[ \t\f\r]+")] 37 | pub enum Token { 38 | #[token("let")] 39 | Let, 40 | #[token("=")] 41 | Equal, 42 | #[token("in")] 43 | In, 44 | #[token("fun")] 45 | Fun, 46 | #[token("->")] 47 | Arrow, 48 | #[token("=>")] 49 | FatArrow, 50 | #[token("(")] 51 | LParen, 52 | #[token(")")] 53 | RParen, 54 | #[token(",")] 55 | Comma, 56 | #[token("forall")] 57 | ForAll, 58 | #[token("[")] 59 | LBracket, 60 | #[token("]")] 61 | RBracket, 62 | #[token("match")] 63 | Match, 64 | #[token("{")] 65 | LBrace, 66 | #[token("}")] 67 | RBrace, 68 | #[token(".")] 69 | Dot, 70 | #[token("|")] 71 | Pipe, 72 | #[token(":")] 73 | Colon, 74 | #[token("\\")] 75 | Backslash, 76 | #[token("+")] 77 | Plus, 78 | #[token("-")] 79 | Minus, 80 | #[token("*")] 81 | Multiply, 82 | #[token("/")] 83 | Divide, 84 | #[token("!")] 85 | Negate, 86 | #[token("==")] 87 | EqualEqual, 88 | #[regex("-?[0-9]+", int)] 89 | Int(i64), 90 | #[token("true")] 91 | True, 92 | #[token("false")] 93 | False, 94 | #[token("if")] 95 | If, 96 | #[token("then")] 97 | Then, 98 | #[token("elif")] 99 | Elif, 100 | #[token("else")] 101 | Else, 102 | #[token("<")] 103 | LessThan, 104 | #[token("<=")] 105 | LessThanOrEqual, 106 | #[token(">")] 107 | GreaterThan, 108 | #[token(">=")] 109 | GreaterThanOrEqual, 110 | #[token("?")] 111 | QuestionMark, 112 | #[regex("[a-zA-Z_][a-zA-Z0-9_]*", ident)] 113 | Ident(String), 114 | #[regex(r"\n", newline)] 115 | Newline, 116 | } 117 | 118 | impl Token { 119 | pub fn empty_ident() -> Self { 120 | Self::Ident("".to_owned()) 121 | } 122 | } 123 | 124 | impl fmt::Display for Token { 125 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 126 | let s = match self { 127 | Token::Let => "let", 128 | Token::Equal => "=", 129 | Token::In => "in", 130 | Token::Fun => "fun", 131 | Token::Arrow => "->", 132 | Token::LParen => "(", 133 | Token::RParen => ")", 134 | Token::Comma => ",", 135 | Token::ForAll => "forall", 136 | Token::LBracket => "[", 137 | Token::RBracket => "]", 138 | Token::Match => "match", 139 | Token::LBrace => "{", 140 | Token::RBrace => "}", 141 | Token::Dot => ".", 142 | Token::Pipe => "|", 143 | Token::Colon => ":", 144 | Token::Backslash => "\\", 145 | Token::Plus => "+", 146 | Token::Minus => "-", 147 | Token::Multiply => "*", 148 | Token::Divide => "/", 149 | Token::Negate => "!", 150 | Token::EqualEqual => "==", 151 | Token::Int(i) => return write!(f, "{}", i), 152 | Token::True => "true", 153 | Token::False => "false", 154 | Token::Ident(i) => i, 155 | Token::FatArrow => "=>", 156 | Token::If => "if", 157 | Token::Then => "then", 158 | Token::Elif => "elif", 159 | Token::Else => "else", 160 | Token::LessThan => "<", 161 | Token::LessThanOrEqual => "<=", 162 | Token::GreaterThan => ">", 163 | Token::GreaterThanOrEqual => ">=", 164 | Token::QuestionMark => "?", 165 | Token::Newline => "\n", 166 | }; 167 | write!(f, "{}", s) 168 | } 169 | } 170 | 171 | #[cfg(test)] 172 | mod tests { 173 | use logos::Logos; 174 | 175 | use super::Token; 176 | 177 | #[test] 178 | fn tests() { 179 | let cases = vec![ 180 | ("", vec![]), 181 | (" \t\n\n\t\r\n\r", vec![]), 182 | ( 183 | "())in,let_ _1Ma->==", 184 | vec![ 185 | Ok(Token::LParen), 186 | Ok(Token::RParen), 187 | Ok(Token::RParen), 188 | Ok(Token::In), 189 | Ok(Token::Comma), 190 | Ok(Token::Ident("let_".to_owned())), 191 | Ok(Token::Ident("_1Ma".to_owned())), 192 | Ok(Token::Arrow), 193 | Ok(Token::EqualEqual), 194 | ], 195 | ), 196 | ( 197 | "let fun in", 198 | vec![Ok(Token::Let), Ok(Token::Fun), Ok(Token::In)], 199 | ), 200 | (";", vec![Err(())]), 201 | ("1", vec![Ok(Token::Int(1))]), 202 | ("-1", vec![Ok(Token::Int(-1))]), 203 | ("10", vec![Ok(Token::Int(10))]), 204 | ("-10", vec![Ok(Token::Int(-10))]), 205 | ]; 206 | for (input, expected) in cases { 207 | let lex = Token::lexer(input); 208 | let mut actual = Vec::new(); 209 | for token in lex { 210 | actual.push(token); 211 | } 212 | assert_eq!(expected, actual, "{}", input); 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /ordo/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "autocfg" 7 | version = "1.1.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 10 | 11 | [[package]] 12 | name = "beef" 13 | version = "0.5.2" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" 16 | 17 | [[package]] 18 | name = "downcast-rs" 19 | version = "1.2.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" 22 | 23 | [[package]] 24 | name = "either" 25 | version = "1.9.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" 28 | 29 | [[package]] 30 | name = "fnv" 31 | version = "1.0.7" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 34 | 35 | [[package]] 36 | name = "indexmap-nostd" 37 | version = "0.4.0" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" 40 | 41 | [[package]] 42 | name = "itertools" 43 | version = "0.12.0" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" 46 | dependencies = [ 47 | "either", 48 | ] 49 | 50 | [[package]] 51 | name = "leb128" 52 | version = "0.2.5" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" 55 | 56 | [[package]] 57 | name = "libm" 58 | version = "0.2.8" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" 61 | 62 | [[package]] 63 | name = "logos" 64 | version = "0.13.0" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1" 67 | dependencies = [ 68 | "logos-derive", 69 | ] 70 | 71 | [[package]] 72 | name = "logos-codegen" 73 | version = "0.13.0" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68" 76 | dependencies = [ 77 | "beef", 78 | "fnv", 79 | "proc-macro2", 80 | "quote", 81 | "regex-syntax", 82 | "syn", 83 | ] 84 | 85 | [[package]] 86 | name = "logos-derive" 87 | version = "0.13.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e" 90 | dependencies = [ 91 | "logos-codegen", 92 | ] 93 | 94 | [[package]] 95 | name = "memchr" 96 | version = "2.7.1" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 99 | 100 | [[package]] 101 | name = "num-traits" 102 | version = "0.2.17" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" 105 | dependencies = [ 106 | "autocfg", 107 | ] 108 | 109 | [[package]] 110 | name = "ordo" 111 | version = "0.1.0" 112 | dependencies = [ 113 | "itertools", 114 | "logos", 115 | "wasmi", 116 | "wat", 117 | ] 118 | 119 | [[package]] 120 | name = "paste" 121 | version = "1.0.14" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" 124 | 125 | [[package]] 126 | name = "proc-macro2" 127 | version = "1.0.69" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" 130 | dependencies = [ 131 | "unicode-ident", 132 | ] 133 | 134 | [[package]] 135 | name = "quote" 136 | version = "1.0.33" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 139 | dependencies = [ 140 | "proc-macro2", 141 | ] 142 | 143 | [[package]] 144 | name = "regex-syntax" 145 | version = "0.6.29" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 148 | 149 | [[package]] 150 | name = "smallvec" 151 | version = "1.11.2" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" 154 | 155 | [[package]] 156 | name = "spin" 157 | version = "0.9.8" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 160 | 161 | [[package]] 162 | name = "syn" 163 | version = "2.0.38" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" 166 | dependencies = [ 167 | "proc-macro2", 168 | "quote", 169 | "unicode-ident", 170 | ] 171 | 172 | [[package]] 173 | name = "unicode-ident" 174 | version = "1.0.12" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 177 | 178 | [[package]] 179 | name = "unicode-width" 180 | version = "0.1.11" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" 183 | 184 | [[package]] 185 | name = "wasm-encoder" 186 | version = "0.39.0" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "111495d6204760238512f57a9af162f45086504da332af210f2f75dd80b34f1d" 189 | dependencies = [ 190 | "leb128", 191 | ] 192 | 193 | [[package]] 194 | name = "wasmi" 195 | version = "0.31.1" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "acfc1e384a36ca532d070a315925887247f3c7e23567e23e0ac9b1c5d6b8bf76" 198 | dependencies = [ 199 | "smallvec", 200 | "spin", 201 | "wasmi_arena", 202 | "wasmi_core", 203 | "wasmparser-nostd", 204 | ] 205 | 206 | [[package]] 207 | name = "wasmi_arena" 208 | version = "0.4.0" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "401c1f35e413fac1846d4843745589d9ec678977ab35a384db8ae7830525d468" 211 | 212 | [[package]] 213 | name = "wasmi_core" 214 | version = "0.13.0" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" 217 | dependencies = [ 218 | "downcast-rs", 219 | "libm", 220 | "num-traits", 221 | "paste", 222 | ] 223 | 224 | [[package]] 225 | name = "wasmparser-nostd" 226 | version = "0.100.1" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "9157cab83003221bfd385833ab587a039f5d6fa7304854042ba358a3b09e0724" 229 | dependencies = [ 230 | "indexmap-nostd", 231 | ] 232 | 233 | [[package]] 234 | name = "wast" 235 | version = "70.0.0" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "2ee4bc54bbe1c6924160b9f75e374a1d07532e7580eb632c0ee6cdd109bb217e" 238 | dependencies = [ 239 | "leb128", 240 | "memchr", 241 | "unicode-width", 242 | "wasm-encoder", 243 | ] 244 | 245 | [[package]] 246 | name = "wat" 247 | version = "1.0.83" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "9f0dce8cdc288c717cf01e461a1e451a7b8445d53451123536ba576e423a101a" 250 | dependencies = [ 251 | "wast", 252 | ] 253 | -------------------------------------------------------------------------------- /ordo/src/emit.rs: -------------------------------------------------------------------------------- 1 | use crate::expr::{Expr, ExprIn, ExprTypedAt, IntBinOp, Pattern, PatternTypedAt, Type}; 2 | 3 | #[derive(Debug)] 4 | pub enum Error { 5 | NoCode, 6 | } 7 | 8 | type Result = std::result::Result; 9 | 10 | pub const LOAD_NAME: &str = ".load"; 11 | 12 | pub fn emit(expr: ExprTypedAt) -> Result { 13 | let mut module = Module::default(); 14 | module.emit(expr)?; 15 | module.function.update_result(); 16 | Ok(module.to_string()) 17 | } 18 | 19 | #[derive(Clone)] 20 | enum WasmType { 21 | I32, 22 | I64, 23 | // F32, 24 | // F64, 25 | } 26 | 27 | impl From<&Type> for WasmType { 28 | fn from(value: &Type) -> Self { 29 | if value == &Type::int() { 30 | WasmType::I64 31 | } else if value == &Type::bool() { 32 | WasmType::I32 33 | } else { 34 | todo!("{:?}", value) 35 | } 36 | } 37 | } 38 | 39 | impl From for WasmType { 40 | fn from(value: Type) -> Self { 41 | WasmType::from(&value) 42 | } 43 | } 44 | 45 | impl std::fmt::Display for WasmType { 46 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 47 | let t = match self { 48 | WasmType::I32 => "i32", 49 | WasmType::I64 => "i64", 50 | // WasmType::F32 => "f32", 51 | // WasmType::F64 => "f64", 52 | }; 53 | write!(f, "{}", t) 54 | } 55 | } 56 | 57 | enum WasmValue { 58 | // I32(i32), 59 | I64(i64), 60 | // F32(f32), 61 | // F64(f64), 62 | } 63 | 64 | impl WasmValue { 65 | fn ty(&self) -> WasmType { 66 | match self { 67 | // WasmValue::I32(_) => WasmType::I32, 68 | WasmValue::I64(_) => WasmType::I64, 69 | // WasmValue::F32(_) => WasmType::F32, 70 | // WasmValue::F64(_) => WasmType::F64, 71 | } 72 | } 73 | } 74 | 75 | impl From for WasmValue { 76 | fn from(value: i64) -> Self { 77 | Self::I64(value) 78 | } 79 | } 80 | 81 | impl std::fmt::Display for WasmValue { 82 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 83 | match self { 84 | // WasmValue::I32(v) => write!(f, "{}", v), 85 | WasmValue::I64(v) => write!(f, "{}", v), 86 | // WasmValue::F32(v) => write!(f, "{}", v), 87 | // WasmValue::F64(v) => write!(f, "{}", v), 88 | } 89 | } 90 | } 91 | 92 | enum LocalKind { 93 | Param, 94 | Local, 95 | } 96 | 97 | impl std::fmt::Display for LocalKind { 98 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 99 | let name = match self { 100 | LocalKind::Param => "param", 101 | LocalKind::Local => "local", 102 | }; 103 | write!(f, "{}", name) 104 | } 105 | } 106 | 107 | struct WasmLocal { 108 | kind: LocalKind, 109 | name: String, 110 | ty: WasmType, 111 | } 112 | 113 | impl WasmLocal { 114 | fn param(value: PatternTypedAt) -> Self { 115 | let kind = LocalKind::Param; 116 | let ty = value.ty().into(); 117 | match *value.expr { 118 | Pattern::Var(name) => WasmLocal { kind, name, ty }, 119 | _ => todo!(), 120 | } 121 | } 122 | 123 | fn local(name: String, ty: WasmType) -> Self { 124 | let kind = LocalKind::Local; 125 | Self { kind, name, ty } 126 | } 127 | } 128 | 129 | impl std::fmt::Display for WasmLocal { 130 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 131 | write!(f, "({} ${} {})", self.kind, self.name, self.ty) 132 | } 133 | } 134 | 135 | enum WasmCode { 136 | LocalGet(String, WasmType), 137 | Instruction(&'static str, WasmType, WasmType), 138 | Const(WasmValue), 139 | Call(String, WasmType), 140 | LocalSet(String, WasmType), 141 | } 142 | 143 | impl std::fmt::Display for WasmCode { 144 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 145 | match self { 146 | WasmCode::LocalGet(name, _) => write!(f, "local.get ${}", name), 147 | WasmCode::Instruction(instruction, in_ty, _) => write!(f, "{}.{}", in_ty, instruction), 148 | WasmCode::Const(v) => write!(f, "{}.const {}", v.ty(), v), 149 | WasmCode::Call(name, _) => write!(f, "call ${}", name), 150 | WasmCode::LocalSet(name, _) => { 151 | write!(f, "local.set ${}", name) 152 | } 153 | } 154 | } 155 | } 156 | 157 | impl WasmCode { 158 | fn ty(&self) -> WasmType { 159 | match self { 160 | WasmCode::LocalGet(_, ty) => ty.clone(), 161 | WasmCode::Instruction(_, _, out_ty) => out_ty.clone(), 162 | WasmCode::Const(value) => value.ty(), 163 | WasmCode::Call(_, ty) => ty.clone(), 164 | WasmCode::LocalSet(_, ty) => ty.clone(), 165 | } 166 | } 167 | } 168 | 169 | struct WasmFunction { 170 | public: bool, 171 | name: String, 172 | params: Vec, 173 | locals: Vec, 174 | result: Option, 175 | code: Vec, 176 | } 177 | 178 | impl WasmFunction { 179 | fn new(name: String, params: Vec) -> Self { 180 | Self { 181 | public: !name.starts_with('_'), 182 | name, 183 | params, 184 | locals: Vec::new(), 185 | result: None, 186 | code: Vec::new(), 187 | } 188 | } 189 | 190 | fn update_result(&mut self) { 191 | self.result = self.code.last().map(|code| code.ty()); 192 | } 193 | } 194 | 195 | impl std::fmt::Display for WasmFunction { 196 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 197 | write!(f, " (func ${}", self.name)?; 198 | for param in &self.params { 199 | write!(f, " {}", param)?; 200 | } 201 | if let Some(result) = &self.result { 202 | write!(f, " (result {})", result)?; 203 | } 204 | for local in &self.locals { 205 | write!(f, " {}", local)?; 206 | } 207 | for code in &self.code { 208 | write!(f, "\n {}", code)?; 209 | } 210 | write!(f, ")")?; 211 | if self.public { 212 | write!(f, "\n (export \"{}\" (func ${}))", self.name, self.name)?; 213 | } 214 | Ok(()) 215 | } 216 | } 217 | 218 | struct Module { 219 | functions: Vec, 220 | function: WasmFunction, 221 | } 222 | 223 | impl Default for Module { 224 | fn default() -> Self { 225 | let functions = Vec::new(); 226 | let function = WasmFunction::new(LOAD_NAME.to_owned(), Vec::new()); 227 | Self { 228 | functions, 229 | function, 230 | } 231 | } 232 | } 233 | 234 | impl Module { 235 | fn emit(&mut self, expr: ExprTypedAt) -> Result<()> { 236 | let ty: WasmType = expr.ty().into(); 237 | match *expr.expr { 238 | Expr::Let(pattern, value, body) => match (*pattern.expr, *value.expr) { 239 | (Pattern::Var(name), Expr::Fun(params, fun_body)) => { 240 | let params = params.into_iter().map(WasmLocal::param).collect(); 241 | let mut function = WasmFunction::new(name, params); 242 | std::mem::swap(&mut self.function, &mut function); 243 | self.emit(fun_body)?; 244 | std::mem::swap(&mut self.function, &mut function); 245 | function.update_result(); 246 | self.functions.push(function); 247 | self.emit(body) 248 | } 249 | (Pattern::Var(name), value_expr) => { 250 | // TODO: Not ideal to reconstruct this 251 | let value = ExprIn { 252 | context: value.context, 253 | expr: value_expr.into(), 254 | }; 255 | self.emit(value)?; 256 | let ty = self 257 | .function 258 | .code 259 | .last() 260 | .map(|code| code.ty()) 261 | .ok_or(Error::NoCode)?; 262 | self.function 263 | .locals 264 | .push(WasmLocal::local(name.clone(), ty.clone())); 265 | self.function.code.push(WasmCode::LocalSet(name, ty)); 266 | self.emit(body) 267 | } 268 | _ => todo!(), 269 | }, 270 | Expr::Int(i) => { 271 | self.function.code.push(WasmCode::Const(i.into())); 272 | Ok(()) 273 | } 274 | Expr::IntBinOp(op, a, b) => { 275 | let out = op.output_ty().into(); 276 | let i = match op { 277 | IntBinOp::Plus => "add", 278 | IntBinOp::Minus => "sub", 279 | IntBinOp::Multiply => "mul", 280 | IntBinOp::Divide => "div_s", 281 | IntBinOp::LessThan => "lt_s", 282 | IntBinOp::LessThanOrEqual => "le_s", 283 | IntBinOp::GreaterThan => "gt_s", 284 | IntBinOp::GreaterThanOrEqual => "ge_s", 285 | }; 286 | self.emit(a)?; 287 | self.emit(b)?; 288 | self.function 289 | .code 290 | .push(WasmCode::Instruction(i, WasmType::I64, out)); 291 | Ok(()) 292 | } 293 | Expr::Var(name) => { 294 | self.function.code.push(WasmCode::LocalGet(name, ty)); 295 | Ok(()) 296 | } 297 | Expr::Call(fun, args) => match *fun.expr { 298 | Expr::Var(name) => { 299 | for arg in args { 300 | self.emit(arg)?; 301 | } 302 | self.function.code.push(WasmCode::Call(name, ty)); 303 | Ok(()) 304 | } 305 | _ => todo!(), 306 | }, 307 | Expr::EqualEqual(a, b) => { 308 | let in_ty = WasmType::from(&a.context.ty.ty); 309 | self.emit(a)?; 310 | self.emit(b)?; 311 | self.function 312 | .code 313 | .push(WasmCode::Instruction("eq", in_ty, WasmType::I32)); 314 | Ok(()) 315 | } 316 | _ => todo!(), 317 | } 318 | } 319 | } 320 | 321 | impl std::fmt::Display for Module { 322 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 323 | write!(f, "(module")?; 324 | for function in &self.functions { 325 | write!(f, "\n{}", function)?; 326 | } 327 | write!(f, "\n{})", self.function) 328 | } 329 | } 330 | -------------------------------------------------------------------------------- /ordo/src/tests/infer_tests.rs: -------------------------------------------------------------------------------- 1 | use crate::{core::make_env, infer::Error, parser::Parser}; 2 | 3 | #[track_caller] 4 | fn pass(expr_str: &str, expected: &str) { 5 | let (forall, ty) = Parser::ty(expected).unwrap(); 6 | let mut env = make_env(); 7 | let expected = env.replace_ty_constants_with_vars(forall, ty); 8 | let expr = Parser::expr(expr_str).unwrap(); 9 | let typed_expr = env.infer(expr).unwrap(); 10 | let actual = typed_expr.context.ty.ty; 11 | let expected = env.ty_to_string(&expected).unwrap(); 12 | let actual = env.ty_to_string(&actual).unwrap(); 13 | assert_eq!(expected, actual, "for {}", expr_str); 14 | } 15 | 16 | #[track_caller] 17 | fn fail(expr_str: &str, expected: Error) { 18 | let mut env = make_env(); 19 | let expr = Parser::expr(expr_str).unwrap(); 20 | let actual = env.infer(expr).unwrap_err(); 21 | assert_eq!(expected, actual, "for {}", expr_str); 22 | } 23 | 24 | #[test] 25 | fn infer_pattern() { 26 | pass("let x = 1 in x", "int"); 27 | pass("let {x = x} = {x = 1} in x", "int"); 28 | pass("let {x} = {x = 1} in x", "int"); 29 | pass("let {x = {y = y}} = {x = {y = 1}} in y", "int"); 30 | } 31 | 32 | #[test] 33 | fn infer_base() { 34 | pass("id", "forall a => a -> a"); 35 | pass("one", "int"); 36 | fail("x", Error::VariableNotFound("x".to_owned())); 37 | fail("let x = x in x", Error::VariableNotFound("x".to_owned())); 38 | pass("let x = id in x", "forall a => a -> a"); 39 | pass("let x = fun(y) -> y in x", "forall a => a -> a"); 40 | pass("fun(x) -> x", "forall a => a -> a"); 41 | pass("pair", "forall a b => (a, b) -> pair[a, b]"); 42 | pass("pair", "forall z x => (x, z) -> pair[x, z]"); 43 | pass( 44 | "fun(x) -> let y = fun(z) -> z in y", 45 | "forall a b => a -> b -> b", 46 | ); 47 | pass( 48 | "let f = fun(x) -> x in let id = fun(y) -> y in eq(f, id)", 49 | "bool", 50 | ); 51 | pass( 52 | "let f = fun(x) -> x in let id = fun(y) -> y in eq_curry(f)(id)", 53 | "bool", 54 | ); 55 | pass("let f = fun(x) -> x in eq(f, succ)", "bool"); 56 | pass("let f = fun(x) -> x in eq_curry(f)(succ)", "bool"); 57 | pass( 58 | "let f = fun(x) -> x in pair(f(one), f(true))", 59 | "pair[int, bool]", 60 | ); 61 | fail( 62 | "fun(f) -> pair(f(one), f(true))", 63 | Error::CannotUnify("bool".to_owned(), "int".to_owned()), 64 | ); 65 | pass( 66 | "let f = fun(x, y) -> let a = eq(x, y) in eq(x, y) in f", 67 | "forall a => (a, a) -> bool", 68 | ); 69 | pass( 70 | "let f = fun(x, y) -> let a = eq_curry(x)(y) in eq_curry(x)(y) in f", 71 | "forall a => (a, a) -> bool", 72 | ); 73 | pass("id(id)", "forall a => a -> a"); 74 | pass( 75 | "choose(fun(x, y) -> x, fun(x, y) -> y)", 76 | "forall a => (a, a) -> a", 77 | ); 78 | pass( 79 | "choose_curry(fun(x, y) -> x)(fun(x, y) -> y)", 80 | "forall a => (a, a) -> a", 81 | ); 82 | pass( 83 | "let x = id in let y = let z = x(id) in z in y", 84 | "forall a => a -> a", 85 | ); 86 | pass("cons(id, nil)", "forall a => list[a -> a]"); 87 | pass("cons_curry(id)(nil)", "forall a => list[a -> a]"); 88 | pass( 89 | "let lst1 = cons(id, nil) in let lst2 = cons(succ, lst1) in lst2", 90 | "list[int -> int]", 91 | ); 92 | pass( 93 | "cons_curry(id)(cons_curry(succ)(cons_curry(id)(nil)))", 94 | "list[int -> int]", 95 | ); 96 | fail( 97 | "plus(one, true)", 98 | Error::CannotUnify("bool".to_owned(), "int".to_owned()), 99 | ); 100 | fail("plus(one)", Error::UnexpectedNumberOfArguments); 101 | pass("fun(x) -> let y = x in y", "forall a => a -> a"); 102 | pass( 103 | "fun(x) -> let y = let z = x(fun(x) -> x) in z in y", 104 | "forall a b => ((a -> a) -> b) -> b", 105 | ); 106 | pass( 107 | "fun(x) -> fun(y) -> let x = x(y) in x(y)", 108 | "forall a b => (a -> a -> b) -> a -> b", 109 | ); 110 | pass( 111 | "fun(x) -> let y = fun(z) -> x(z) in y", 112 | "forall a b => (a -> b) -> a -> b", 113 | ); 114 | pass( 115 | "fun(x) -> let y = fun(z) -> x in y", 116 | "forall a b => a -> b -> a", 117 | ); 118 | pass( 119 | "fun(x) -> fun(y) -> let x = x(y) in fun(x) -> y(x)", 120 | "forall a b c => ((a -> b) -> c) -> (a -> b) -> a -> b", 121 | ); 122 | fail("fun(x) -> let y = x in y(y)", Error::RecursiveType); 123 | pass( 124 | "fun(x) -> let y = fun(z) -> z in y(y)", 125 | "forall a b => a -> b -> b", 126 | ); 127 | fail("fun(x) -> x(x)", Error::RecursiveType); 128 | fail("one(id)", Error::ExpectedFunction("int".to_owned())); 129 | pass( 130 | "fun(f) -> let x = fun(g, y) -> let _ = g(y) in eq(f, g) in x", 131 | "forall a b => (a -> b) -> (a -> b, a) -> bool", 132 | ); 133 | pass( 134 | "let const = fun(x) -> fun(y) -> x in const", 135 | "forall a b => a -> b -> a", 136 | ); 137 | pass( 138 | "let apply = fun(f, x) -> f(x) in apply", 139 | "forall a b => (a -> b, a) -> b", 140 | ); 141 | pass( 142 | "let apply_curry = fun(f) -> fun(x) -> f(x) in apply_curry", 143 | "forall a b => (a -> b) -> a -> b", 144 | ); 145 | pass("1 == 1", "bool"); 146 | pass("1 > 2", "bool"); 147 | } 148 | 149 | #[test] 150 | fn infer_records() { 151 | pass("{}", "{}"); 152 | fail("{}.x", Error::MissingLabel("x".to_owned())); 153 | pass("{a = one}", "{a : int}"); 154 | pass("{a = one, b = true}", "{a : int, b : bool}"); 155 | pass("{b = true, a = one}", "{b : bool, a : int}"); 156 | pass("{a = one, b = true}.a", "int"); 157 | pass("{a = one, b = true}.b", "bool"); 158 | fail("{a = one, b = true}.c", Error::MissingLabel("c".to_owned())); 159 | pass("{f = fun(x) -> x}", "forall a => {f : a -> a}"); 160 | pass( 161 | "let r = {a = id, b = succ} in choose(r.a, r.b)", 162 | "int -> int", 163 | ); 164 | pass( 165 | "let r = {a = id, b = fun(x) -> x} in choose(r.a, r.b)", 166 | "forall a => a -> a", 167 | ); 168 | fail("choose({a = one}, {})", Error::MissingLabel("a".to_owned())); 169 | pass("{ x = zero | { y = one | {} } }", "{y : int, x : int}"); 170 | pass( 171 | "choose({ x = zero | { y = one | {} } }, {x = one, y = zero})", 172 | "{y : int, x : int}", 173 | ); 174 | fail("{}\\x", Error::MissingLabel("x".to_owned())); 175 | pass("{x = one, y = zero} \\ x", "{y : int}"); 176 | pass("{ x = true | {x = one}\\x}", "{x : bool}"); 177 | pass("let a = {} in {b = one | a}", "{b : int}"); 178 | pass("let a = {x = one} in {x = true | a\\x}.x", "bool"); 179 | fail( 180 | "let a = {x = one} in a.y", 181 | Error::MissingLabel("y".to_owned()), 182 | ); 183 | pass("let a = {x = one} in a \\ x", "{}"); 184 | fail( 185 | "let a = {x = one} in let b = {x = true | a\\x} in b\\x.x", 186 | Error::MissingLabel("x".to_owned()), 187 | ); 188 | pass( 189 | "fun(r) -> {x = one | r}", 190 | "forall ra. (ra\\x) => {ra} -> {x : int | ra}", 191 | ); 192 | pass("fun(r) -> r.x", "forall ra a. (ra\\x) => {x : a | ra} -> a"); 193 | pass( 194 | "let get_x = fun(r) -> r.x in get_x({y = one, x = zero})", 195 | "int", 196 | ); 197 | fail( 198 | "let get_x = fun(r) -> r.x in get_x({y = one, z = true})", 199 | Error::MissingLabel("x".to_owned()), 200 | ); 201 | pass( 202 | "fun(r) -> choose({x = zero | r}, {x = one | {}})", 203 | "{} -> {x : int}", 204 | ); 205 | pass( 206 | "fun(r) -> choose({x = zero | r}, {x = one})", 207 | "{} -> {x : int}", 208 | ); 209 | pass( 210 | "fun(r) -> choose({x = zero | r}, {x = one | r})", 211 | "forall ra. (ra\\x) => {ra} -> {x : int | ra}", 212 | ); 213 | fail( 214 | "fun(r) -> choose({x = zero | r}, {y = one | r})", 215 | Error::RowConstraintFailed("y".to_owned()), 216 | ); 217 | pass("let f = fun(x) -> x.t(one) in f({t = succ})", "int"); 218 | pass("let f = fun(x) -> x.t(one) in f({t = id})", "int"); 219 | fail( 220 | "let f = fun(r) -> let y = r.y in choose(r, {x = one}) in f", 221 | Error::MissingLabel("y".to_owned()), 222 | ); 223 | fail( 224 | "fun(r) -> choose({x = zero | r}, {x = true | r})", 225 | Error::CannotUnify("int".to_owned(), "bool".to_owned()), 226 | ); 227 | pass( 228 | "fun(r, s) -> choose({b = true, c = zero | r}, {b = false, c = one, d = half | s})", 229 | "forall ra. (ra\\b\\c\\d) => ({d : float | ra}, {ra}) -> {b : bool, c : int, d : float | ra}", 230 | ); 231 | pass( 232 | "fun(r) -> {x = r | r}", 233 | "forall ra. (ra\\x) => {ra} -> {x : {ra} | ra}", 234 | ); 235 | pass("let { x = x } = { x = 1 } in { x = x }", "{ x: int}"); 236 | } 237 | 238 | #[test] 239 | fn infer_variant() { 240 | pass(":X one", "forall ra. (ra\\X) => [X : int | ra]"); 241 | pass( 242 | "choose(choose(:x one, :Y true), choose(:X half, :y nil))", 243 | "forall a ra. (ra\\X\\Y\\x\\y) => [X : float, Y : bool, x : int, y : list[a] | ra]", 244 | ); 245 | fail( 246 | "choose(:X one, :X true)", 247 | Error::CannotUnify("int".to_owned(), "bool".to_owned()), 248 | ); 249 | pass( 250 | "choose(:X {x = one, y = false}, :Y {w = half})", 251 | "forall ra. (ra\\X\\Y) => [X : {x : int, y : bool}, Y : {w : float} | ra]", 252 | ); 253 | fail( 254 | concat!( 255 | "let e = choose(choose(:x one, :Y true), choose(:X half, :y nil)) in ", 256 | "match e { :x i -> i , :Y y -> zero}" 257 | ), 258 | Error::MissingLabel("X".to_owned()), 259 | ); 260 | pass( 261 | "fun(x, y) -> match x {:a i -> one , :b i -> zero , :c i -> y}", 262 | "forall a b c => ([a : a, b : b, c : c], int) -> int", 263 | ); 264 | pass( 265 | "fun(a) -> match a {:X i -> i , r -> one}", 266 | "forall ra. (ra\\X) => [X : int | ra] -> int", 267 | ); 268 | pass( 269 | concat!( 270 | "let f = fun(m) -> match m {:y a -> one , :Y b -> zero , :z z -> zero} in ", 271 | "fun(e) -> match e { :x i -> i , :X f -> one , r -> f(r)}" 272 | ), 273 | "forall a b c d => [X : a, Y : b, x : int, y : c, z : d] -> int", 274 | ); 275 | pass( 276 | concat!( 277 | "let e = choose(choose(:x one, :Y true), choose(:X half, :y nil)) in ", 278 | "let f = fun(m) -> match m {:y a -> one , :Y b -> zero , :z z -> zero} in ", 279 | "match e { :x i -> i , :X f -> one , r -> f(r)}" 280 | ), 281 | "int", 282 | ); 283 | pass( 284 | "fun(e) -> match e { :X a -> plus(a.x, one) }", 285 | "forall ra. (ra\\x) => [X : {x : int | ra}] -> int", 286 | ); 287 | } 288 | -------------------------------------------------------------------------------- /ordo/src/tests/parse_only.rs: -------------------------------------------------------------------------------- 1 | use crate::expr::At; 2 | use crate::expr::Expr; 3 | use crate::expr::ExprOnly; 4 | use crate::expr::IntBinOp; 5 | use crate::expr::PatternOnly; 6 | use crate::expr::Position; 7 | use crate::lexer::Token; 8 | use crate::parser::Error; 9 | use crate::parser::Parser; 10 | 11 | pub fn pvar(var: &str) -> PatternOnly { 12 | Expr::Var(var.to_owned()).into() 13 | } 14 | 15 | pub fn precord(labels: Vec<(&str, PatternOnly)>) -> PatternOnly { 16 | let labels = labels 17 | .into_iter() 18 | .map(|(label, pattern)| (label.to_owned(), pattern)) 19 | .collect(); 20 | Expr::RecordExtend(labels, Expr::RecordEmpty.into()).into() 21 | } 22 | 23 | pub fn bool(b: bool) -> ExprOnly { 24 | Expr::Bool(b).into() 25 | } 26 | 27 | pub fn int(i: i64) -> ExprOnly { 28 | Expr::Int(i).into() 29 | } 30 | 31 | pub fn var(var: &str) -> ExprOnly { 32 | Expr::Var(var.to_owned()).into() 33 | } 34 | 35 | pub fn call(fn_expr: ExprOnly, args: Vec) -> ExprOnly { 36 | Expr::Call(fn_expr, args).into() 37 | } 38 | 39 | pub fn fun(args: Vec, body: ExprOnly) -> ExprOnly { 40 | Expr::Fun(args, body).into() 41 | } 42 | 43 | pub fn let_(var: PatternOnly, value: ExprOnly, body: ExprOnly) -> ExprOnly { 44 | Expr::Let(var, value, body).into() 45 | } 46 | 47 | pub fn empty() -> ExprOnly { 48 | Expr::RecordEmpty.into() 49 | } 50 | 51 | pub fn select(r: ExprOnly, label: &str) -> ExprOnly { 52 | Expr::RecordSelect(r, label.to_owned()).into() 53 | } 54 | 55 | pub fn restrict(r: ExprOnly, label: &str) -> ExprOnly { 56 | Expr::RecordRestrict(r, label.to_owned()).into() 57 | } 58 | 59 | pub fn record(labels: Vec<(&str, ExprOnly)>, r: ExprOnly) -> ExprOnly { 60 | let labels = labels 61 | .into_iter() 62 | .map(|(label, expr)| (label.to_owned(), expr)) 63 | .collect(); 64 | Expr::RecordExtend(labels, r).into() 65 | } 66 | 67 | pub fn plus(lhs: ExprOnly, rhs: ExprOnly) -> ExprOnly { 68 | Expr::IntBinOp(IntBinOp::Plus, lhs, rhs).into() 69 | } 70 | 71 | pub fn minus(lhs: ExprOnly, rhs: ExprOnly) -> ExprOnly { 72 | Expr::IntBinOp(IntBinOp::Minus, lhs, rhs).into() 73 | } 74 | 75 | pub fn multiply(lhs: ExprOnly, rhs: ExprOnly) -> ExprOnly { 76 | Expr::IntBinOp(IntBinOp::Multiply, lhs, rhs).into() 77 | } 78 | 79 | pub fn divide(lhs: ExprOnly, rhs: ExprOnly) -> ExprOnly { 80 | Expr::IntBinOp(IntBinOp::Divide, lhs, rhs).into() 81 | } 82 | 83 | pub fn negate(expr: ExprOnly) -> ExprOnly { 84 | Expr::Negate(expr).into() 85 | } 86 | 87 | pub fn equalequal(lhs: ExprOnly, rhs: ExprOnly) -> ExprOnly { 88 | Expr::EqualEqual(lhs, rhs).into() 89 | } 90 | 91 | pub fn gt(lhs: ExprOnly, rhs: ExprOnly) -> ExprOnly { 92 | Expr::IntBinOp(IntBinOp::GreaterThan, lhs, rhs).into() 93 | } 94 | 95 | pub fn match_( 96 | val: ExprOnly, 97 | cases: Vec<(&str, &str, ExprOnly)>, 98 | def: Option<(&str, ExprOnly)>, 99 | ) -> ExprOnly { 100 | let cases = cases 101 | .into_iter() 102 | .map(|(variant, var, body)| (variant.to_owned(), var.to_owned(), body)) 103 | .collect(); 104 | let def = def.map(|(var, body)| (var.to_owned(), body)); 105 | Expr::Case(val, cases, def).into() 106 | } 107 | 108 | pub fn if_( 109 | if_expr: ExprOnly, 110 | if_body: ExprOnly, 111 | elifs: Vec<(ExprOnly, ExprOnly)>, 112 | else_body: ExprOnly, 113 | ) -> ExprOnly { 114 | Expr::If(if_expr, if_body, elifs, else_body).into() 115 | } 116 | 117 | #[track_caller] 118 | fn pass(source: &str, expected: ExprOnly) { 119 | let actual = Parser::expr(source).unwrap(); 120 | let actual = actual.strip_context(); 121 | assert_eq!(expected, actual); 122 | } 123 | 124 | #[track_caller] 125 | fn fail(source: &str, expected: Error) { 126 | let actual = Parser::expr(source).unwrap_err(); 127 | assert_eq!(expected, actual, "for {}", source); 128 | } 129 | 130 | #[track_caller] 131 | fn pass_repl(source: &str, expected: ExprOnly) { 132 | let actual = Parser::repl(source).unwrap(); 133 | let actual = actual.strip_context(); 134 | assert_eq!(expected, actual); 135 | } 136 | 137 | #[test] 138 | fn precedence() { 139 | pass("1 + 2", plus(int(1), int(2))); 140 | pass("1 + 2 * 3", plus(int(1), multiply(int(2), int(3)))); 141 | pass("1 - 2 / 3", minus(int(1), divide(int(2), int(3)))); 142 | pass( 143 | "false == !true", 144 | equalequal(bool(false), negate(bool(true))), 145 | ); 146 | pass("1 + 2 > 2", gt(plus(int(1), int(2)), int(2))); 147 | } 148 | 149 | #[test] 150 | fn ifs() { 151 | pass( 152 | "if a == b then a else b", 153 | if_( 154 | equalequal(var("a"), var("b")), 155 | var("a"), 156 | Vec::new(), 157 | var("b"), 158 | ), 159 | ); 160 | pass( 161 | "if a == b then a elif b == c then b else c", 162 | if_( 163 | equalequal(var("a"), var("b")), 164 | var("a"), 165 | vec![(equalequal(var("b"), var("c")), var("b"))], 166 | var("c"), 167 | ), 168 | ); 169 | } 170 | 171 | #[test] 172 | fn patterns() { 173 | pass( 174 | "let {a} = {a = 1} in a", 175 | let_( 176 | precord(vec![("a", pvar("a"))]), 177 | record(vec![("a", int(1))], empty()), 178 | var("a"), 179 | ), 180 | ); 181 | pass( 182 | "let f(a, b) = a + b in f(1, 2)", 183 | let_( 184 | pvar("f"), 185 | fun(vec![pvar("a"), pvar("b")], plus(var("a"), var("b"))), 186 | call(var("f"), vec![int(1), int(2)]), 187 | ), 188 | ); 189 | pass( 190 | "let {x = {y = y}} = {x = {y = 1}} in y", 191 | let_( 192 | precord(vec![("x", precord(vec![("y", pvar("y"))]))]), 193 | record(vec![("x", record(vec![("y", int(1))], empty()))], empty()), 194 | var("y"), 195 | ), 196 | ); 197 | } 198 | 199 | #[test] 200 | fn exprs() { 201 | fail("", Error::UnexpectedEof); 202 | pass("a", var("a")); 203 | pass("f(x, y)", call(var("f"), vec![var("x"), var("y")])); 204 | pass( 205 | "f(x)(y)", 206 | call(call(var("f"), vec![var("x")]), vec![var("y")]), 207 | ); 208 | pass( 209 | "let f = fun(x, y) -> g(x, y) in f(a, b)", 210 | let_( 211 | pvar("f"), 212 | fun( 213 | vec![pvar("x"), pvar("y")], 214 | call(var("g"), vec![var("x"), var("y")]), 215 | ), 216 | call(var("f"), vec![var("a"), var("b")]), 217 | ), 218 | ); 219 | pass( 220 | "let x = a in 221 | let y = b in 222 | f(x, y)", 223 | let_( 224 | pvar("x"), 225 | var("a"), 226 | let_( 227 | pvar("y"), 228 | var("b"), 229 | call(var("f"), vec![var("x"), var("y")]), 230 | ), 231 | ), 232 | ); 233 | fail("f x", Error::ExpectedEof(Token::Ident("x".to_owned()))); 234 | fail( 235 | "let a = one", 236 | Error::Expected("let expr", vec![Token::In], None), 237 | ); 238 | fail("a, b", Error::ExpectedEof(Token::Comma)); 239 | fail("a = b", Error::ExpectedEof(Token::Equal)); 240 | // TODO: Not an ideal error here 241 | fail("()", Error::InvalidPrefix(Some(Token::RParen))); 242 | pass("fun(x) -> x", fun(vec![pvar("x")], var("x"))); 243 | // records 244 | pass("{}", empty()); 245 | pass("{ }", empty()); 246 | fail( 247 | "{", 248 | Error::Expected("record expr label", vec![Token::empty_ident()], None), 249 | ); 250 | pass("a.x", select(var("a"), "x")); 251 | pass("m \\ a", restrict(var("m"), "a")); 252 | pass("{a = x}", record(vec![("a", var("x"))], empty())); 253 | fail( 254 | "{a = x", 255 | Error::Expected( 256 | "record expr", 257 | vec![Token::Pipe, Token::Comma, Token::RBrace], 258 | None, 259 | ), 260 | ); 261 | pass( 262 | "{a=x, b = y}", 263 | record(vec![("a", var("x")), ("b", var("y"))], empty()), 264 | ); 265 | pass( 266 | "{b = y ,a=x}", 267 | record(vec![("a", var("x")), ("b", var("y"))], empty()), 268 | ); 269 | pass( 270 | "{a=x,h=w,d=y,b=q,g=z,c=t,e=s,f=r}", 271 | record( 272 | vec![ 273 | ("a", var("x")), 274 | ("b", var("q")), 275 | ("c", var("t")), 276 | ("d", var("y")), 277 | ("e", var("s")), 278 | ("f", var("r")), 279 | ("g", var("z")), 280 | ("h", var("w")), 281 | ], 282 | empty(), 283 | ), 284 | ); 285 | pass("{a = x|m}", record(vec![("a", var("x"))], var("m"))); 286 | fail( 287 | "{|m}", 288 | Error::Expected( 289 | "record expr label", 290 | vec![Token::empty_ident()], 291 | Some(Token::Pipe), 292 | ), 293 | ); 294 | pass( 295 | "{ a = x, b = y | m}", 296 | record(vec![("a", var("x")), ("b", var("y"))], var("m")), 297 | ); 298 | pass( 299 | "{ a = x, b = y | m \\ a }", 300 | record( 301 | vec![("a", var("x")), ("b", var("y"))], 302 | restrict(var("m"), "a"), 303 | ), 304 | ); 305 | pass( 306 | "let x = {a = f(x), b = y.b} in { a = fun(z) -> z | x \\ a }", 307 | let_( 308 | pvar("x"), 309 | record( 310 | vec![ 311 | ("a", call(var("f"), vec![var("x")])), 312 | ("b", select(var("y"), "b")), 313 | ], 314 | empty(), 315 | ), 316 | record( 317 | vec![("a", fun(vec![pvar("z")], var("z")))], 318 | restrict(var("x"), "a"), 319 | ), 320 | ), 321 | ); 322 | fail( 323 | "{a = x, a = y}", 324 | Error::DuplicateLabel(At { 325 | start: Position { line: 0, column: 8 }, 326 | end: Position { line: 0, column: 9 }, 327 | value: "a".to_owned(), 328 | }), 329 | ); 330 | pass( 331 | "{x,y}", 332 | record(vec![("x", var("x")), ("y", var("y"))], empty()), 333 | ); 334 | pass( 335 | "f({x,y})", 336 | call( 337 | var("f"), 338 | vec![record(vec![("x", var("x")), ("y", var("y"))], empty())], 339 | ), 340 | ); 341 | pass("-9223372036854775807", int(i64::MIN + 1)); 342 | fail("-9223372036854775808", Error::Lexer); 343 | pass("9223372036854775807", int(i64::MAX)); 344 | fail("9223372036854775808", Error::Lexer); 345 | } 346 | 347 | #[test] 348 | fn repl() { 349 | pass_repl("let a = 0", let_(pvar("a"), int(0), var("a"))); 350 | pass_repl( 351 | "let f(x) = x", 352 | let_(pvar("f"), fun(vec![pvar("x")], var("x")), var("f")), 353 | ); 354 | pass_repl( 355 | "let {x = x} = {x = 1}", 356 | let_( 357 | precord(vec![("x", pvar("x"))]), 358 | record(vec![("x", int(1))], empty()), 359 | record(vec![("x", var("x"))], empty()), 360 | ), 361 | ); 362 | pass_repl( 363 | "let f({ x = x }) = x", 364 | let_( 365 | pvar("f"), 366 | fun(vec![precord(vec![("x", pvar("x"))])], var("x")), 367 | var("f"), 368 | ), 369 | ); 370 | pass_repl( 371 | "let default_with(default, value) = match value { :some value -> value, :none x -> default }", 372 | let_( 373 | pvar("default_with"), 374 | fun(vec![pvar("default"), pvar("value")], 375 | match_(var("value"), vec![ 376 | ("some", "value", var("value")), 377 | ("none", "x", var("default")) 378 | ], None)), 379 | var("default_with"))); 380 | } 381 | -------------------------------------------------------------------------------- /ordo/src/expr.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::{BTreeMap, BTreeSet, HashMap}, 3 | fmt, 4 | }; 5 | 6 | use itertools::Itertools; 7 | 8 | pub const OK_LABEL: &str = "ok"; 9 | 10 | #[derive(Clone, Debug, PartialEq)] 11 | pub enum IntBinOp { 12 | Plus, 13 | Minus, 14 | Multiply, 15 | Divide, 16 | LessThan, 17 | LessThanOrEqual, 18 | GreaterThan, 19 | GreaterThanOrEqual, 20 | } 21 | 22 | impl fmt::Display for IntBinOp { 23 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 24 | let op = match self { 25 | IntBinOp::Plus => "+", 26 | IntBinOp::Minus => "-", 27 | IntBinOp::Multiply => "*", 28 | IntBinOp::Divide => "/", 29 | IntBinOp::LessThan => "<", 30 | IntBinOp::LessThanOrEqual => "<=", 31 | IntBinOp::GreaterThan => ">", 32 | IntBinOp::GreaterThanOrEqual => ">=", 33 | }; 34 | write!(f, "{}", op) 35 | } 36 | } 37 | 38 | impl IntBinOp { 39 | pub fn output_ty(&self) -> Type { 40 | match self { 41 | IntBinOp::Plus | IntBinOp::Minus | IntBinOp::Multiply | IntBinOp::Divide => Type::int(), 42 | IntBinOp::LessThan 43 | | IntBinOp::LessThanOrEqual 44 | | IntBinOp::GreaterThan 45 | | IntBinOp::GreaterThanOrEqual => Type::bool(), 46 | } 47 | } 48 | } 49 | 50 | #[derive(Clone, Debug, Default, PartialEq)] 51 | pub struct Position { 52 | pub line: usize, 53 | pub column: usize, 54 | } 55 | 56 | #[derive(Clone, Debug, Default, PartialEq)] 57 | pub struct Location { 58 | pub start: Position, 59 | pub end: Position, 60 | } 61 | 62 | #[derive(Clone, Debug, PartialEq)] 63 | pub struct At { 64 | pub start: Position, 65 | pub end: Position, 66 | pub value: T, 67 | } 68 | 69 | impl At { 70 | pub fn map(self, f: F) -> At 71 | where 72 | F: FnOnce(T) -> U, 73 | { 74 | let start = self.start; 75 | let end = self.end; 76 | let value = f(self.value); 77 | At { start, end, value } 78 | } 79 | 80 | pub fn span_with(self, next: At, value: V) -> At { 81 | At { 82 | start: self.start, 83 | end: next.end, 84 | value, 85 | } 86 | } 87 | } 88 | 89 | pub type NoContext = (); 90 | 91 | #[derive(Clone, Debug, PartialEq)] 92 | pub struct PositionContext { 93 | pub start: Position, 94 | pub end: Position, 95 | } 96 | 97 | impl From> for PositionContext { 98 | fn from(value: At) -> Self { 99 | Self { 100 | start: value.start, 101 | end: value.end, 102 | } 103 | } 104 | } 105 | 106 | #[derive(Clone, Debug, PartialEq)] 107 | pub struct TypeContext { 108 | pub ty: Type, 109 | } 110 | 111 | impl From for TypeContext { 112 | fn from(ty: Type) -> Self { 113 | Self { ty } 114 | } 115 | } 116 | 117 | #[derive(Clone, Debug, PartialEq)] 118 | pub struct ExprIn { 119 | pub context: Context, 120 | pub expr: Box>, 121 | } 122 | 123 | impl fmt::Display for ExprIn { 124 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 125 | write!(f, "{}", self.expr) 126 | } 127 | } 128 | 129 | impl From> for ExprOnly { 130 | fn from(expr: Expr) -> Self { 131 | ExprIn { 132 | context: (), 133 | expr: expr.into(), 134 | } 135 | } 136 | } 137 | 138 | impl From>> for ExprAt { 139 | fn from(value: At>) -> Self { 140 | ExprIn { 141 | context: PositionContext { 142 | start: value.start, 143 | end: value.end, 144 | }, 145 | expr: value.value.into(), 146 | } 147 | } 148 | } 149 | 150 | impl ExprIn { 151 | pub fn strip_context(self) -> ExprIn { 152 | ExprIn { 153 | context: (), 154 | expr: self.expr.strip_context().into(), 155 | } 156 | } 157 | } 158 | 159 | impl ExprIn { 160 | pub fn strip_position(self) -> ExprIn { 161 | ExprIn { 162 | context: self.context.ty, 163 | expr: self.expr.strip_position().into(), 164 | } 165 | } 166 | 167 | pub fn ty(&self) -> &Type { 168 | &self.context.ty.ty 169 | } 170 | } 171 | 172 | #[derive(Clone, Debug, PartialEq)] 173 | pub struct PositionTypeContext { 174 | pub position: PositionContext, 175 | pub ty: TypeContext, 176 | } 177 | 178 | pub type ExprOnly = ExprIn; 179 | pub type ExprAt = ExprIn; 180 | pub type ExprTyped = ExprIn; 181 | pub type ExprTypedAt = ExprIn; 182 | 183 | pub type Pattern = Expr; 184 | pub type PatternIn = ExprIn; 185 | pub type PatternOnly = ExprOnly; 186 | pub type PatternAt = ExprAt; 187 | pub type PatternTypedAt = ExprTypedAt; 188 | 189 | #[derive(Clone, Debug, PartialEq)] 190 | pub enum Expr { 191 | Bool(bool), 192 | Int(i64), 193 | IntBinOp(IntBinOp, ExprIn, ExprIn), 194 | Negate(ExprIn), 195 | EqualEqual(ExprIn, ExprIn), 196 | Var(String), 197 | Call(ExprIn, Vec>), 198 | Fun(Vec>, ExprIn), 199 | Let(PatternIn, ExprIn, ExprIn), 200 | RecordSelect(ExprIn, String), 201 | RecordExtend(BTreeMap>, ExprIn), 202 | RecordRestrict(ExprIn, String), 203 | RecordEmpty, 204 | Variant(String, ExprIn), 205 | Case( 206 | ExprIn, 207 | Vec<(String, String, ExprIn)>, 208 | Option<(String, ExprIn)>, 209 | ), 210 | If( 211 | ExprIn, 212 | ExprIn, 213 | Vec<(ExprIn, ExprIn)>, 214 | ExprIn, 215 | ), 216 | Unwrap(ExprIn), 217 | } 218 | 219 | impl fmt::Display for Expr { 220 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 221 | match self { 222 | Expr::Bool(b) => write!(f, "{}", b), 223 | Expr::Int(i) => write!(f, "{}", i), 224 | Expr::IntBinOp(op, a, b) => write!(f, "{} {} {}", a, op, b), 225 | Expr::Negate(a) => write!(f, "!{}", a), 226 | Expr::EqualEqual(a, b) => write!(f, "{} == {}", a, b), 227 | Expr::Var(v) => write!(f, "{}", v), 228 | Expr::Call(fun, args) => { 229 | let args = args.iter().map(|arg| arg.to_string()).join(", "); 230 | write!(f, "{}({})", fun, args) 231 | } 232 | Expr::Fun(params, body) => { 233 | let params = params.iter().map(|param| param.to_string()).join(", "); 234 | write!(f, "fun({}) -> {}", params, body) 235 | } 236 | Expr::Let(var, val, body) => { 237 | write!(f, "let {} = {} in {}", var, val, body) 238 | } 239 | Expr::RecordSelect(rec, label) => write!(f, "{}.{}", rec, label), 240 | Expr::RecordExtend(labels, rest) => { 241 | let labels = labels 242 | .iter() 243 | .map(|(label, val)| format!("{}: {}", label, val)) 244 | .join(", "); 245 | match rest.expr.as_ref() { 246 | Expr::RecordEmpty => write!(f, "{{{}}}", labels), 247 | _ => write!(f, "{{{} | {}}}", labels, rest), 248 | } 249 | } 250 | Expr::RecordRestrict(record, label) => write!(f, "{}\\{}", record, label), 251 | Expr::RecordEmpty => write!(f, "{{}}"), 252 | Expr::Variant(label, value) => write!(f, ":{} {}", label, value), 253 | Expr::Case(_, _, _) => todo!(), 254 | Expr::If(_, _, _, _) => todo!(), 255 | Expr::Unwrap(expr) => write!(f, "{}?", expr), 256 | } 257 | } 258 | } 259 | 260 | impl Expr { 261 | fn strip_context(self) -> Expr { 262 | fn fix(e: ExprAt) -> ExprOnly { 263 | e.strip_context() 264 | } 265 | match self { 266 | Expr::Bool(b) => Expr::Bool(b), 267 | Expr::Int(i) => Expr::Int(i), 268 | Expr::IntBinOp(op, a, b) => Expr::IntBinOp(op, fix(a), fix(b)), 269 | Expr::Negate(e) => Expr::Negate(fix(e)), 270 | Expr::EqualEqual(a, b) => Expr::EqualEqual(fix(a), fix(b)), 271 | Expr::Var(s) => Expr::Var(s), 272 | Expr::Call(fun, args) => { 273 | let args = args.into_iter().map(ExprAt::strip_context).collect(); 274 | Expr::Call(fix(fun), args) 275 | } 276 | Expr::Fun(params, body) => { 277 | let params = params.into_iter().map(ExprAt::strip_context).collect(); 278 | Expr::Fun(params, fix(body)) 279 | } 280 | Expr::Let(p, v, b) => Expr::Let(fix(p), fix(v), fix(b)), 281 | Expr::RecordSelect(r, l) => Expr::RecordSelect(fix(r), l), 282 | Expr::RecordExtend(ls, r) => { 283 | let ls = ls 284 | .into_iter() 285 | .map(|(l, e)| (l, e.strip_context())) 286 | .collect(); 287 | Expr::RecordExtend(ls, fix(r)) 288 | } 289 | Expr::RecordRestrict(r, l) => Expr::RecordRestrict(fix(r), l), 290 | Expr::RecordEmpty => Expr::RecordEmpty, 291 | Expr::Variant(l, e) => Expr::Variant(l, fix(e)), 292 | Expr::Case(e, cs, d) => { 293 | let cs = cs 294 | .into_iter() 295 | .map(|(l, v, b)| (l, v, b.strip_context())) 296 | .collect(); 297 | let d = d.map(|(v, b)| (v, fix(b))); 298 | Expr::Case(fix(e), cs, d) 299 | } 300 | Expr::If(i, ib, ies, eb) => { 301 | let ies = ies 302 | .into_iter() 303 | .map(|(ie, ieb)| (ie.strip_context(), ieb.strip_context())) 304 | .collect(); 305 | Expr::If(fix(i), fix(ib), ies, fix(eb)) 306 | } 307 | Expr::Unwrap(e) => Expr::Unwrap(fix(e)), 308 | } 309 | } 310 | } 311 | 312 | impl Expr { 313 | pub fn with(self, position: PositionContext, ty: Type) -> ExprTypedAt { 314 | let ty = TypeContext { ty }; 315 | ExprIn { 316 | context: PositionTypeContext { position, ty }, 317 | expr: self.into(), 318 | } 319 | } 320 | 321 | pub fn strip_position(self) -> Expr { 322 | fn fix(e: ExprTypedAt) -> ExprTyped { 323 | e.strip_position() 324 | } 325 | match self { 326 | Expr::Bool(b) => Expr::Bool(b), 327 | Expr::Int(i) => Expr::Int(i), 328 | Expr::IntBinOp(op, a, b) => Expr::IntBinOp(op, fix(a), fix(b)), 329 | Expr::Negate(e) => Expr::Negate(fix(e)), 330 | Expr::EqualEqual(a, b) => Expr::EqualEqual(fix(a), fix(b)), 331 | Expr::Var(s) => Expr::Var(s), 332 | Expr::Call(fun, args) => { 333 | let args = args.into_iter().map(ExprTypedAt::strip_position).collect(); 334 | Expr::Call(fix(fun), args) 335 | } 336 | Expr::Fun(params, body) => { 337 | let params = params 338 | .into_iter() 339 | .map(ExprTypedAt::strip_position) 340 | .collect(); 341 | Expr::Fun(params, fix(body)) 342 | } 343 | Expr::Let(p, v, b) => Expr::Let(fix(p), fix(v), fix(b)), 344 | Expr::RecordSelect(r, l) => Expr::RecordSelect(fix(r), l), 345 | Expr::RecordExtend(ls, r) => { 346 | let ls = ls 347 | .into_iter() 348 | .map(|(l, e)| (l, e.strip_position())) 349 | .collect(); 350 | Expr::RecordExtend(ls, fix(r)) 351 | } 352 | Expr::RecordRestrict(r, l) => Expr::RecordRestrict(fix(r), l), 353 | Expr::RecordEmpty => Expr::RecordEmpty, 354 | Expr::Variant(l, e) => Expr::Variant(l, fix(e)), 355 | Expr::Case(e, cs, d) => { 356 | let cs = cs 357 | .into_iter() 358 | .map(|(l, v, b)| (l, v, b.strip_position())) 359 | .collect(); 360 | let d = d.map(|(v, b)| (v, fix(b))); 361 | Expr::Case(fix(e), cs, d) 362 | } 363 | Expr::If(i, ib, ies, eb) => { 364 | let ies = ies 365 | .into_iter() 366 | .map(|(ie, ieb)| (ie.strip_position(), ieb.strip_position())) 367 | .collect(); 368 | Expr::If(fix(i), fix(ib), ies, fix(eb)) 369 | } 370 | Expr::Unwrap(e) => Expr::Unwrap(fix(e)), 371 | } 372 | } 373 | } 374 | 375 | pub type Id = usize; 376 | pub type Level = i64; 377 | 378 | #[derive(Clone, Debug, PartialEq)] 379 | pub enum Type { 380 | Const(String), 381 | App(Box, Vec), 382 | Arrow(Vec, Box), 383 | Var(Id), 384 | Record(Box), 385 | Variant(Box), 386 | RowEmpty, 387 | RowExtend(BTreeMap, Box), 388 | } 389 | 390 | impl Type { 391 | pub fn bool() -> Self { 392 | Self::Const("bool".to_owned()) 393 | } 394 | 395 | pub fn int() -> Self { 396 | Self::Const("int".to_owned()) 397 | } 398 | 399 | pub fn replace_const_with_vars(self, env: &HashMap) -> Type { 400 | match self { 401 | Type::Const(name) => match env.get(&name) { 402 | Some(ty) => ty.clone(), 403 | None => Type::Const(name.clone()), 404 | }, 405 | Type::Var(_) => self, 406 | Type::App(ty, args) => { 407 | let ty = ty.replace_const_with_vars(env).into(); 408 | let args = args 409 | .into_iter() 410 | .map(|arg| arg.replace_const_with_vars(env)) 411 | .collect(); 412 | Type::App(ty, args) 413 | } 414 | Type::Arrow(params, ret) => { 415 | let params = params 416 | .into_iter() 417 | .map(|param| param.replace_const_with_vars(env)) 418 | .collect(); 419 | let ret = ret.replace_const_with_vars(env).into(); 420 | Type::Arrow(params, ret) 421 | } 422 | Type::Record(row) => Type::Record(row.replace_const_with_vars(env).into()), 423 | Type::Variant(row) => Type::Variant(row.replace_const_with_vars(env).into()), 424 | Type::RowEmpty => Type::RowEmpty, 425 | Type::RowExtend(labels, rest) => { 426 | let labels = labels 427 | .into_iter() 428 | .map(|(label, ty)| (label, ty.replace_const_with_vars(env))) 429 | .collect(); 430 | let rest = rest.replace_const_with_vars(env); 431 | Type::RowExtend(labels, rest.into()) 432 | } 433 | } 434 | } 435 | } 436 | 437 | pub type Constraints = BTreeSet; 438 | 439 | #[derive(Clone, Debug)] 440 | pub enum TypeVar { 441 | Unbound(Level), 442 | UnboundRow(Level, Constraints), 443 | Link(Type), 444 | Generic, 445 | GenericRow(Constraints), 446 | } 447 | -------------------------------------------------------------------------------- /ordo/src/eval.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use std::collections::BTreeMap; 3 | 4 | use itertools::Itertools; 5 | 6 | use crate::expr::{Expr, ExprTypedAt, IntBinOp, Pattern, PatternTypedAt, OK_LABEL}; 7 | 8 | #[derive(Debug)] 9 | pub enum Error { 10 | NotBool(Value), 11 | NotInt(Value), 12 | NotFunction(Value), 13 | NotRecord(Value), 14 | NotVariant(Value), 15 | VarNotFound(String), 16 | UnexpectedNumberOfArguments, 17 | LabelNotFound(String), 18 | NoCase, 19 | UnwrapNotVariant(Value), 20 | PatternRecordRestNotEmpty(ExprTypedAt), 21 | InvalidPattern(ExprTypedAt), 22 | } 23 | 24 | impl fmt::Display for Error { 25 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 26 | match self { 27 | Error::NotBool(v) => write!(f, "expected bool, got {}", v), 28 | Error::NotInt(v) => write!(f, "expected int, got {}", v), 29 | Error::NotFunction(v) => write!(f, "expected function, got {}", v), 30 | Error::NotRecord(v) => write!(f, "expected record, got {}", v), 31 | Error::NotVariant(v) => write!(f, "expected variant, got {}", v), 32 | Error::VarNotFound(var) => write!(f, "variable not found: {}", var), 33 | Error::UnexpectedNumberOfArguments => write!(f, "unexpected number of arguments"), 34 | Error::LabelNotFound(label) => write!(f, "label not found: {}", label), 35 | Error::NoCase => write!(f, "no case"), 36 | Error::UnwrapNotVariant(val) => write!(f, "unwrap on a non-variant: {}", val), 37 | Error::PatternRecordRestNotEmpty(expr) => { 38 | write!(f, "record pattern extended something: {}", expr) 39 | } 40 | Error::InvalidPattern(expr) => write!(f, "invalid pattern: {}", expr), 41 | } 42 | } 43 | } 44 | type Result = std::result::Result; 45 | 46 | #[derive(Clone, Debug, PartialEq)] 47 | pub struct Function { 48 | env: Env, 49 | params: Vec, 50 | body: ExprTypedAt, 51 | } 52 | 53 | impl fmt::Display for Function { 54 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 55 | let params = self.params.iter().map(|param| param.to_string()).join(", "); 56 | write!(f, "fun({})", params) 57 | } 58 | } 59 | 60 | impl Function { 61 | fn apply(&mut self, args: Vec) -> Result { 62 | if self.params.len() != args.len() { 63 | return Err(Error::UnexpectedNumberOfArguments); 64 | } 65 | for (arg, param) in args.into_iter().zip(self.params.clone().into_iter()) { 66 | self.env.eval_pattern(param, arg)?; 67 | } 68 | self.env.eval_inner(self.body.clone()) 69 | } 70 | } 71 | 72 | #[derive(Clone, Debug, PartialEq)] 73 | pub enum Value { 74 | Bool(bool), 75 | Int(i64), 76 | Function(Function), 77 | Record(BTreeMap), 78 | Variant(String, Box), 79 | } 80 | 81 | impl fmt::Display for Value { 82 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 83 | match self { 84 | Value::Bool(b) => write!(f, "{}", b), 85 | Value::Int(i) => write!(f, "{}", i), 86 | Value::Function(fun) => write!(f, "{}", fun), 87 | Value::Record(labels) => { 88 | let labels = labels 89 | .iter() 90 | .map(|(label, val)| format!("{}: {}", label, val)) 91 | .join(", "); 92 | write!(f, "{{{}}}", labels) 93 | } 94 | Value::Variant(label, value) => { 95 | write!(f, ":{} {}", label, value) 96 | } 97 | } 98 | } 99 | } 100 | 101 | impl Value { 102 | pub fn record(labels: Vec<(&str, Value)>) -> Self { 103 | let labels = labels 104 | .into_iter() 105 | .map(|(label, val)| (label.to_owned(), val)) 106 | .collect(); 107 | Value::Record(labels) 108 | } 109 | 110 | fn as_bool(self) -> Result { 111 | match self { 112 | Value::Bool(b) => Ok(b), 113 | _ => Err(Error::NotBool(self.clone())), 114 | } 115 | } 116 | 117 | fn as_int(self) -> Result { 118 | match self { 119 | Value::Int(i) => Ok(i), 120 | _ => Err(Error::NotInt(self.clone())), 121 | } 122 | } 123 | 124 | fn as_function(self) -> Result { 125 | match self { 126 | Value::Function(fun) => Ok(fun), 127 | _ => Err(Error::NotFunction(self.clone())), 128 | } 129 | } 130 | 131 | fn as_record(self) -> Result> { 132 | match self { 133 | Value::Record(record) => Ok(record), 134 | _ => Err(Error::NotRecord(self.clone())), 135 | } 136 | } 137 | 138 | fn as_variant(self) -> Result<(String, Value)> { 139 | match self { 140 | Value::Variant(label, value) => Ok((label, *value)), 141 | _ => Err(Error::NotVariant(self.clone())), 142 | } 143 | } 144 | } 145 | 146 | enum Wrap { 147 | Value(Value), 148 | Wrap(String, Box), 149 | } 150 | 151 | impl Wrap { 152 | fn value(self) -> Value { 153 | match self { 154 | Wrap::Value(val) => val, 155 | Wrap::Wrap(name, val) => Value::Variant(name, val), 156 | } 157 | } 158 | } 159 | 160 | #[derive(Clone, Debug, Default, PartialEq)] 161 | pub struct Env { 162 | pub vars: BTreeMap, 163 | } 164 | 165 | impl Env { 166 | pub fn eval(&mut self, expr: ExprTypedAt) -> Result { 167 | let wrap = self.eval_inner(expr)?; 168 | Ok(wrap.value()) 169 | } 170 | 171 | fn eval_pattern(&mut self, pattern: PatternTypedAt, value: Value) -> Result<()> { 172 | match *pattern.expr { 173 | Pattern::Var(var) => { 174 | self.vars.insert(var.clone(), value); 175 | } 176 | Pattern::RecordExtend(labels, rest) => { 177 | match rest.expr.as_ref() { 178 | Expr::RecordEmpty => (), 179 | _ => return Err(Error::PatternRecordRestNotEmpty(rest.clone())), 180 | } 181 | let mut labels_value = match value { 182 | Value::Record(labels) => labels, 183 | _ => return Err(Error::NotRecord(value)), 184 | }; 185 | for (label, label_pattern) in labels { 186 | match labels_value.remove(&label) { 187 | None => return Err(Error::LabelNotFound(label.clone())), 188 | Some(label_value) => { 189 | self.eval_pattern(label_pattern, label_value.clone())? 190 | } 191 | } 192 | } 193 | } 194 | _ => return Err(Error::InvalidPattern(pattern.clone())), 195 | } 196 | Ok(()) 197 | } 198 | 199 | fn eval_inner(&mut self, expr: ExprTypedAt) -> Result { 200 | match *expr.expr { 201 | Expr::Bool(b) => Ok(Wrap::Value(Value::Bool(b))), 202 | Expr::Int(i) => Ok(Wrap::Value(Value::Int(i))), 203 | Expr::IntBinOp(op, lhs, rhs) => { 204 | let lhs = match self.eval_inner(lhs)? { 205 | Wrap::Value(value) => value.as_int()?, 206 | Wrap::Wrap(name, val) => return Ok(Wrap::Value(Value::Variant(name, val))), 207 | }; 208 | let rhs = match self.eval_inner(rhs)? { 209 | Wrap::Value(value) => value.as_int()?, 210 | Wrap::Wrap(name, val) => return Ok(Wrap::Value(Value::Variant(name, val))), 211 | }; 212 | let val = match op { 213 | IntBinOp::Plus => Value::Int(lhs + rhs), 214 | IntBinOp::Minus => Value::Int(lhs - rhs), 215 | IntBinOp::Multiply => Value::Int(lhs * rhs), 216 | IntBinOp::Divide => Value::Int(lhs / rhs), 217 | IntBinOp::LessThan => Value::Bool(lhs < rhs), 218 | IntBinOp::LessThanOrEqual => Value::Bool(lhs <= rhs), 219 | IntBinOp::GreaterThan => Value::Bool(lhs > rhs), 220 | IntBinOp::GreaterThanOrEqual => Value::Bool(lhs >= rhs), 221 | }; 222 | Ok(Wrap::Value(val)) 223 | } 224 | Expr::Negate(expr) => { 225 | let b = match self.eval_inner(expr)? { 226 | Wrap::Value(value) => value.as_bool()?, 227 | Wrap::Wrap(name, val) => return Ok(Wrap::Value(Value::Variant(name, val))), 228 | }; 229 | Ok(Wrap::Value(Value::Bool(!b))) 230 | } 231 | Expr::EqualEqual(lhs, rhs) => { 232 | let lhs = match self.eval_inner(lhs)? { 233 | Wrap::Value(value) => value, 234 | Wrap::Wrap(name, val) => return Ok(Wrap::Value(Value::Variant(name, val))), 235 | }; 236 | let rhs = match self.eval_inner(rhs)? { 237 | Wrap::Value(value) => value, 238 | Wrap::Wrap(name, val) => return Ok(Wrap::Value(Value::Variant(name, val))), 239 | }; 240 | Ok(Wrap::Value(Value::Bool(lhs == rhs))) 241 | } 242 | Expr::Var(s) => { 243 | let value = self 244 | .vars 245 | .get(&s) 246 | .ok_or_else(|| Error::VarNotFound(s.clone()))?; 247 | Ok(Wrap::Value(value.clone())) 248 | } 249 | Expr::Call(fun, args) => { 250 | let mut fun = match self.eval_inner(fun)? { 251 | Wrap::Value(value) => value.as_function()?, 252 | Wrap::Wrap(name, val) => return Ok(Wrap::Value(Value::Variant(name, val))), 253 | }; 254 | let mut values = Vec::with_capacity(args.len()); 255 | for arg in args { 256 | let value = match self.eval_inner(arg)? { 257 | Wrap::Value(value) => value, 258 | Wrap::Wrap(name, val) => return Ok(Wrap::Value(Value::Variant(name, val))), 259 | }; 260 | values.push(value); 261 | } 262 | fun.apply(values) 263 | } 264 | Expr::Fun(params, body) => { 265 | let env = self.clone(); 266 | let params = params.clone(); 267 | let body = body.clone(); 268 | let fun = Function { env, params, body }; 269 | Ok(Wrap::Value(Value::Function(fun))) 270 | } 271 | Expr::Let(var, val, body) => { 272 | let val = match self.eval_inner(val)? { 273 | Wrap::Value(value) => value, 274 | Wrap::Wrap(name, val) => return Ok(Wrap::Value(Value::Variant(name, val))), 275 | }; 276 | self.eval_pattern(var, val)?; 277 | self.eval_inner(body) 278 | } 279 | Expr::RecordSelect(record, label) => { 280 | let record = match self.eval_inner(record)? { 281 | Wrap::Value(value) => value, 282 | Wrap::Wrap(name, val) => return Ok(Wrap::Value(Value::Variant(name, val))), 283 | }; 284 | let record = record.as_record()?; 285 | let val = record 286 | .get(&label) 287 | .ok_or_else(|| Error::LabelNotFound(label.clone()))?; 288 | Ok(Wrap::Value(val.clone())) 289 | } 290 | Expr::RecordExtend(labels, record) => { 291 | let record = match self.eval_inner(record)? { 292 | Wrap::Value(value) => value, 293 | Wrap::Wrap(name, val) => return Ok(Wrap::Value(Value::Variant(name, val))), 294 | }; 295 | let record = record.as_record()?; 296 | let mut record = record.clone(); 297 | for (label, expr) in labels { 298 | let value = match self.eval_inner(expr)? { 299 | Wrap::Value(value) => value, 300 | Wrap::Wrap(name, val) => return Ok(Wrap::Value(Value::Variant(name, val))), 301 | }; 302 | record.insert(label.clone(), value); 303 | } 304 | Ok(Wrap::Value(Value::Record(record))) 305 | } 306 | Expr::RecordRestrict(record, label) => { 307 | let record = match self.eval_inner(record)? { 308 | Wrap::Value(value) => value, 309 | Wrap::Wrap(name, val) => return Ok(Wrap::Value(Value::Variant(name, val))), 310 | }; 311 | let record = record.as_record()?; 312 | let mut record = record.clone(); 313 | record.remove(&label); 314 | Ok(Wrap::Value(Value::Record(record))) 315 | } 316 | Expr::RecordEmpty => Ok(Wrap::Value(Value::Record(BTreeMap::new()))), 317 | Expr::Variant(label, expr) => { 318 | let value = match self.eval_inner(expr)? { 319 | Wrap::Value(value) => value, 320 | Wrap::Wrap(name, val) => return Ok(Wrap::Value(Value::Variant(name, val))), 321 | }; 322 | Ok(Wrap::Value(Value::Variant(label.clone(), value.into()))) 323 | } 324 | Expr::Case(value, cases, def) => { 325 | let value = match self.eval_inner(value)? { 326 | Wrap::Value(value) => value, 327 | Wrap::Wrap(name, val) => return Ok(Wrap::Value(Value::Variant(name, val))), 328 | }; 329 | let (label, value) = value.as_variant()?; 330 | for (case_label, case_var, case_body) in cases { 331 | if label == case_label { 332 | let mut env = self.clone(); 333 | env.vars.insert(case_var.clone(), value.clone()); 334 | return env.eval_inner(case_body); 335 | } 336 | } 337 | if let Some((var, body)) = def { 338 | let mut env = self.clone(); 339 | env.vars.insert( 340 | var.clone(), 341 | Value::Variant(label.clone(), value.clone().into()), 342 | ); 343 | return env.eval_inner(body); 344 | } 345 | Err(Error::NoCase) 346 | } 347 | Expr::If(if_expr, if_body, elifs, else_body) => { 348 | let b = match self.eval_inner(if_expr)? { 349 | Wrap::Value(value) => value.as_bool()?, 350 | Wrap::Wrap(name, val) => return Ok(Wrap::Value(Value::Variant(name, val))), 351 | }; 352 | if b { 353 | return self.eval_inner(if_body); 354 | } 355 | for (elif_expr, elif_body) in elifs { 356 | let b = match self.eval_inner(elif_expr)? { 357 | Wrap::Value(value) => value.as_bool()?, 358 | Wrap::Wrap(name, val) => return Ok(Wrap::Value(Value::Variant(name, val))), 359 | }; 360 | if b { 361 | return self.eval_inner(elif_body); 362 | } 363 | } 364 | self.eval_inner(else_body) 365 | } 366 | Expr::Unwrap(expr) => { 367 | let val = match self.eval_inner(expr)? { 368 | Wrap::Value(value) => value, 369 | Wrap::Wrap(name, val) => return Ok(Wrap::Value(Value::Variant(name, val))), 370 | }; 371 | match val { 372 | Value::Variant(name, val) if name == OK_LABEL => Ok(Wrap::Value(*val)), 373 | Value::Variant(name, val) => Ok(Wrap::Wrap(name, val)), 374 | val => Err(Error::UnwrapNotVariant(val)), 375 | } 376 | } 377 | } 378 | } 379 | } 380 | 381 | #[cfg(test)] 382 | mod tests { 383 | use crate::{infer, parser::Parser}; 384 | 385 | use super::{Env, Value}; 386 | 387 | #[test] 388 | fn tests() { 389 | let cases: Vec<(&str, Value)> = vec![ 390 | ("1 + 2", Value::Int(3)), 391 | ("let a = 1 in a + 2", Value::Int(3)), 392 | ("{a = 2}.a", Value::Int(2)), 393 | (":a 3", Value::Variant("a".to_owned(), Value::Int(3).into())), 394 | ("match :a 3 { :b b -> b , :a a -> a }", Value::Int(3)), 395 | ("let a = 1 in let r = {a} in r.a", Value::Int(1)), 396 | ( 397 | "let f({x,y}) = x + y in let x = 1 in let y = 2 in f({x,y})", 398 | Value::Int(3), 399 | ), 400 | ("2 > 1", Value::Bool(true)), 401 | ("2 > 3", Value::Bool(false)), 402 | ]; 403 | for (expr_str, expected) in cases { 404 | let expr = Parser::expr(expr_str).unwrap(); 405 | let expr = infer::Env::default().infer(expr).unwrap(); 406 | let actual = Env::default().eval(expr).unwrap(); 407 | assert_eq!(expected, actual); 408 | } 409 | } 410 | } 411 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "anes" 16 | version = "0.1.6" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" 19 | 20 | [[package]] 21 | name = "anstyle" 22 | version = "1.0.5" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "2faccea4cc4ab4a667ce676a30e8ec13922a692c99bb8f5b11f1502c72e04220" 25 | 26 | [[package]] 27 | name = "autocfg" 28 | version = "1.1.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 31 | 32 | [[package]] 33 | name = "beef" 34 | version = "0.5.2" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" 37 | 38 | [[package]] 39 | name = "bitflags" 40 | version = "2.4.2" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" 43 | 44 | [[package]] 45 | name = "bumpalo" 46 | version = "3.14.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" 49 | 50 | [[package]] 51 | name = "cast" 52 | version = "0.3.0" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 55 | 56 | [[package]] 57 | name = "cfg-if" 58 | version = "1.0.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 61 | 62 | [[package]] 63 | name = "ciborium" 64 | version = "0.2.2" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" 67 | dependencies = [ 68 | "ciborium-io", 69 | "ciborium-ll", 70 | "serde", 71 | ] 72 | 73 | [[package]] 74 | name = "ciborium-io" 75 | version = "0.2.2" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" 78 | 79 | [[package]] 80 | name = "ciborium-ll" 81 | version = "0.2.2" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" 84 | dependencies = [ 85 | "ciborium-io", 86 | "half", 87 | ] 88 | 89 | [[package]] 90 | name = "clap" 91 | version = "4.4.18" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" 94 | dependencies = [ 95 | "clap_builder", 96 | ] 97 | 98 | [[package]] 99 | name = "clap_builder" 100 | version = "4.4.18" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" 103 | dependencies = [ 104 | "anstyle", 105 | "clap_lex", 106 | ] 107 | 108 | [[package]] 109 | name = "clap_lex" 110 | version = "0.6.0" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" 113 | 114 | [[package]] 115 | name = "cli" 116 | version = "0.1.0" 117 | dependencies = [ 118 | "ordo", 119 | ] 120 | 121 | [[package]] 122 | name = "criterion" 123 | version = "0.5.1" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" 126 | dependencies = [ 127 | "anes", 128 | "cast", 129 | "ciborium", 130 | "clap", 131 | "criterion-plot", 132 | "is-terminal", 133 | "itertools 0.10.5", 134 | "num-traits", 135 | "once_cell", 136 | "oorandom", 137 | "plotters", 138 | "rayon", 139 | "regex", 140 | "serde", 141 | "serde_derive", 142 | "serde_json", 143 | "tinytemplate", 144 | "walkdir", 145 | ] 146 | 147 | [[package]] 148 | name = "criterion-plot" 149 | version = "0.5.0" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" 152 | dependencies = [ 153 | "cast", 154 | "itertools 0.10.5", 155 | ] 156 | 157 | [[package]] 158 | name = "crossbeam-deque" 159 | version = "0.8.5" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" 162 | dependencies = [ 163 | "crossbeam-epoch", 164 | "crossbeam-utils", 165 | ] 166 | 167 | [[package]] 168 | name = "crossbeam-epoch" 169 | version = "0.9.18" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 172 | dependencies = [ 173 | "crossbeam-utils", 174 | ] 175 | 176 | [[package]] 177 | name = "crossbeam-utils" 178 | version = "0.8.19" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" 181 | 182 | [[package]] 183 | name = "crunchy" 184 | version = "0.2.2" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 187 | 188 | [[package]] 189 | name = "downcast-rs" 190 | version = "1.2.0" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" 193 | 194 | [[package]] 195 | name = "either" 196 | version = "1.9.0" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" 199 | 200 | [[package]] 201 | name = "errno" 202 | version = "0.3.8" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" 205 | dependencies = [ 206 | "libc", 207 | "windows-sys", 208 | ] 209 | 210 | [[package]] 211 | name = "fnv" 212 | version = "1.0.7" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 215 | 216 | [[package]] 217 | name = "half" 218 | version = "2.3.1" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872" 221 | dependencies = [ 222 | "cfg-if", 223 | "crunchy", 224 | ] 225 | 226 | [[package]] 227 | name = "hermit-abi" 228 | version = "0.3.4" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" 231 | 232 | [[package]] 233 | name = "indexmap-nostd" 234 | version = "0.4.0" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" 237 | 238 | [[package]] 239 | name = "is-terminal" 240 | version = "0.4.10" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" 243 | dependencies = [ 244 | "hermit-abi", 245 | "rustix", 246 | "windows-sys", 247 | ] 248 | 249 | [[package]] 250 | name = "itertools" 251 | version = "0.10.5" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 254 | dependencies = [ 255 | "either", 256 | ] 257 | 258 | [[package]] 259 | name = "itertools" 260 | version = "0.12.0" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" 263 | dependencies = [ 264 | "either", 265 | ] 266 | 267 | [[package]] 268 | name = "itoa" 269 | version = "1.0.10" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" 272 | 273 | [[package]] 274 | name = "js-sys" 275 | version = "0.3.67" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" 278 | dependencies = [ 279 | "wasm-bindgen", 280 | ] 281 | 282 | [[package]] 283 | name = "leb128" 284 | version = "0.2.5" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" 287 | 288 | [[package]] 289 | name = "libc" 290 | version = "0.2.153" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" 293 | 294 | [[package]] 295 | name = "libm" 296 | version = "0.2.8" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" 299 | 300 | [[package]] 301 | name = "linux-raw-sys" 302 | version = "0.4.13" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" 305 | 306 | [[package]] 307 | name = "log" 308 | version = "0.4.20" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 311 | 312 | [[package]] 313 | name = "logos" 314 | version = "0.13.0" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1" 317 | dependencies = [ 318 | "logos-derive", 319 | ] 320 | 321 | [[package]] 322 | name = "logos-codegen" 323 | version = "0.13.0" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68" 326 | dependencies = [ 327 | "beef", 328 | "fnv", 329 | "proc-macro2", 330 | "quote", 331 | "regex-syntax 0.6.29", 332 | "syn", 333 | ] 334 | 335 | [[package]] 336 | name = "logos-derive" 337 | version = "0.13.0" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e" 340 | dependencies = [ 341 | "logos-codegen", 342 | ] 343 | 344 | [[package]] 345 | name = "memchr" 346 | version = "2.7.1" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 349 | 350 | [[package]] 351 | name = "num-traits" 352 | version = "0.2.17" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" 355 | dependencies = [ 356 | "autocfg", 357 | ] 358 | 359 | [[package]] 360 | name = "once_cell" 361 | version = "1.19.0" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 364 | 365 | [[package]] 366 | name = "oorandom" 367 | version = "11.1.3" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" 370 | 371 | [[package]] 372 | name = "ordo" 373 | version = "0.1.0" 374 | dependencies = [ 375 | "criterion", 376 | "itertools 0.12.0", 377 | "logos", 378 | "wasmi", 379 | "wat", 380 | ] 381 | 382 | [[package]] 383 | name = "paste" 384 | version = "1.0.14" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" 387 | 388 | [[package]] 389 | name = "plotters" 390 | version = "0.3.5" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" 393 | dependencies = [ 394 | "num-traits", 395 | "plotters-backend", 396 | "plotters-svg", 397 | "wasm-bindgen", 398 | "web-sys", 399 | ] 400 | 401 | [[package]] 402 | name = "plotters-backend" 403 | version = "0.3.5" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" 406 | 407 | [[package]] 408 | name = "plotters-svg" 409 | version = "0.3.5" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" 412 | dependencies = [ 413 | "plotters-backend", 414 | ] 415 | 416 | [[package]] 417 | name = "proc-macro2" 418 | version = "1.0.76" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" 421 | dependencies = [ 422 | "unicode-ident", 423 | ] 424 | 425 | [[package]] 426 | name = "quote" 427 | version = "1.0.35" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 430 | dependencies = [ 431 | "proc-macro2", 432 | ] 433 | 434 | [[package]] 435 | name = "rayon" 436 | version = "1.8.1" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" 439 | dependencies = [ 440 | "either", 441 | "rayon-core", 442 | ] 443 | 444 | [[package]] 445 | name = "rayon-core" 446 | version = "1.12.1" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 449 | dependencies = [ 450 | "crossbeam-deque", 451 | "crossbeam-utils", 452 | ] 453 | 454 | [[package]] 455 | name = "regex" 456 | version = "1.10.3" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" 459 | dependencies = [ 460 | "aho-corasick", 461 | "memchr", 462 | "regex-automata", 463 | "regex-syntax 0.8.2", 464 | ] 465 | 466 | [[package]] 467 | name = "regex-automata" 468 | version = "0.4.5" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" 471 | dependencies = [ 472 | "aho-corasick", 473 | "memchr", 474 | "regex-syntax 0.8.2", 475 | ] 476 | 477 | [[package]] 478 | name = "regex-syntax" 479 | version = "0.6.29" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 482 | 483 | [[package]] 484 | name = "regex-syntax" 485 | version = "0.8.2" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" 488 | 489 | [[package]] 490 | name = "rustix" 491 | version = "0.38.31" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" 494 | dependencies = [ 495 | "bitflags", 496 | "errno", 497 | "libc", 498 | "linux-raw-sys", 499 | "windows-sys", 500 | ] 501 | 502 | [[package]] 503 | name = "ryu" 504 | version = "1.0.16" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" 507 | 508 | [[package]] 509 | name = "same-file" 510 | version = "1.0.6" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 513 | dependencies = [ 514 | "winapi-util", 515 | ] 516 | 517 | [[package]] 518 | name = "serde" 519 | version = "1.0.196" 520 | source = "registry+https://github.com/rust-lang/crates.io-index" 521 | checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" 522 | dependencies = [ 523 | "serde_derive", 524 | ] 525 | 526 | [[package]] 527 | name = "serde_derive" 528 | version = "1.0.196" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" 531 | dependencies = [ 532 | "proc-macro2", 533 | "quote", 534 | "syn", 535 | ] 536 | 537 | [[package]] 538 | name = "serde_json" 539 | version = "1.0.113" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" 542 | dependencies = [ 543 | "itoa", 544 | "ryu", 545 | "serde", 546 | ] 547 | 548 | [[package]] 549 | name = "smallvec" 550 | version = "1.12.0" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e" 553 | 554 | [[package]] 555 | name = "spin" 556 | version = "0.9.8" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 559 | 560 | [[package]] 561 | name = "syn" 562 | version = "2.0.48" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" 565 | dependencies = [ 566 | "proc-macro2", 567 | "quote", 568 | "unicode-ident", 569 | ] 570 | 571 | [[package]] 572 | name = "tinytemplate" 573 | version = "1.2.1" 574 | source = "registry+https://github.com/rust-lang/crates.io-index" 575 | checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" 576 | dependencies = [ 577 | "serde", 578 | "serde_json", 579 | ] 580 | 581 | [[package]] 582 | name = "unicode-ident" 583 | version = "1.0.12" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 586 | 587 | [[package]] 588 | name = "unicode-width" 589 | version = "0.1.11" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" 592 | 593 | [[package]] 594 | name = "walkdir" 595 | version = "2.4.0" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" 598 | dependencies = [ 599 | "same-file", 600 | "winapi-util", 601 | ] 602 | 603 | [[package]] 604 | name = "wasm-bindgen" 605 | version = "0.2.90" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" 608 | dependencies = [ 609 | "cfg-if", 610 | "wasm-bindgen-macro", 611 | ] 612 | 613 | [[package]] 614 | name = "wasm-bindgen-backend" 615 | version = "0.2.90" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" 618 | dependencies = [ 619 | "bumpalo", 620 | "log", 621 | "once_cell", 622 | "proc-macro2", 623 | "quote", 624 | "syn", 625 | "wasm-bindgen-shared", 626 | ] 627 | 628 | [[package]] 629 | name = "wasm-bindgen-macro" 630 | version = "0.2.90" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" 633 | dependencies = [ 634 | "quote", 635 | "wasm-bindgen-macro-support", 636 | ] 637 | 638 | [[package]] 639 | name = "wasm-bindgen-macro-support" 640 | version = "0.2.90" 641 | source = "registry+https://github.com/rust-lang/crates.io-index" 642 | checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" 643 | dependencies = [ 644 | "proc-macro2", 645 | "quote", 646 | "syn", 647 | "wasm-bindgen-backend", 648 | "wasm-bindgen-shared", 649 | ] 650 | 651 | [[package]] 652 | name = "wasm-bindgen-shared" 653 | version = "0.2.90" 654 | source = "registry+https://github.com/rust-lang/crates.io-index" 655 | checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" 656 | 657 | [[package]] 658 | name = "wasm-encoder" 659 | version = "0.39.0" 660 | source = "registry+https://github.com/rust-lang/crates.io-index" 661 | checksum = "111495d6204760238512f57a9af162f45086504da332af210f2f75dd80b34f1d" 662 | dependencies = [ 663 | "leb128", 664 | ] 665 | 666 | [[package]] 667 | name = "wasmi" 668 | version = "0.31.1" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "acfc1e384a36ca532d070a315925887247f3c7e23567e23e0ac9b1c5d6b8bf76" 671 | dependencies = [ 672 | "smallvec", 673 | "spin", 674 | "wasmi_arena", 675 | "wasmi_core", 676 | "wasmparser-nostd", 677 | ] 678 | 679 | [[package]] 680 | name = "wasmi_arena" 681 | version = "0.4.0" 682 | source = "registry+https://github.com/rust-lang/crates.io-index" 683 | checksum = "401c1f35e413fac1846d4843745589d9ec678977ab35a384db8ae7830525d468" 684 | 685 | [[package]] 686 | name = "wasmi_core" 687 | version = "0.13.0" 688 | source = "registry+https://github.com/rust-lang/crates.io-index" 689 | checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" 690 | dependencies = [ 691 | "downcast-rs", 692 | "libm", 693 | "num-traits", 694 | "paste", 695 | ] 696 | 697 | [[package]] 698 | name = "wasmparser-nostd" 699 | version = "0.100.1" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "9157cab83003221bfd385833ab587a039f5d6fa7304854042ba358a3b09e0724" 702 | dependencies = [ 703 | "indexmap-nostd", 704 | ] 705 | 706 | [[package]] 707 | name = "wast" 708 | version = "70.0.0" 709 | source = "registry+https://github.com/rust-lang/crates.io-index" 710 | checksum = "2ee4bc54bbe1c6924160b9f75e374a1d07532e7580eb632c0ee6cdd109bb217e" 711 | dependencies = [ 712 | "leb128", 713 | "memchr", 714 | "unicode-width", 715 | "wasm-encoder", 716 | ] 717 | 718 | [[package]] 719 | name = "wat" 720 | version = "1.0.83" 721 | source = "registry+https://github.com/rust-lang/crates.io-index" 722 | checksum = "9f0dce8cdc288c717cf01e461a1e451a7b8445d53451123536ba576e423a101a" 723 | dependencies = [ 724 | "wast", 725 | ] 726 | 727 | [[package]] 728 | name = "web-sys" 729 | version = "0.3.67" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" 732 | dependencies = [ 733 | "js-sys", 734 | "wasm-bindgen", 735 | ] 736 | 737 | [[package]] 738 | name = "winapi" 739 | version = "0.3.9" 740 | source = "registry+https://github.com/rust-lang/crates.io-index" 741 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 742 | dependencies = [ 743 | "winapi-i686-pc-windows-gnu", 744 | "winapi-x86_64-pc-windows-gnu", 745 | ] 746 | 747 | [[package]] 748 | name = "winapi-i686-pc-windows-gnu" 749 | version = "0.4.0" 750 | source = "registry+https://github.com/rust-lang/crates.io-index" 751 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 752 | 753 | [[package]] 754 | name = "winapi-util" 755 | version = "0.1.6" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" 758 | dependencies = [ 759 | "winapi", 760 | ] 761 | 762 | [[package]] 763 | name = "winapi-x86_64-pc-windows-gnu" 764 | version = "0.4.0" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 767 | 768 | [[package]] 769 | name = "windows-sys" 770 | version = "0.52.0" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 773 | dependencies = [ 774 | "windows-targets", 775 | ] 776 | 777 | [[package]] 778 | name = "windows-targets" 779 | version = "0.52.0" 780 | source = "registry+https://github.com/rust-lang/crates.io-index" 781 | checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" 782 | dependencies = [ 783 | "windows_aarch64_gnullvm", 784 | "windows_aarch64_msvc", 785 | "windows_i686_gnu", 786 | "windows_i686_msvc", 787 | "windows_x86_64_gnu", 788 | "windows_x86_64_gnullvm", 789 | "windows_x86_64_msvc", 790 | ] 791 | 792 | [[package]] 793 | name = "windows_aarch64_gnullvm" 794 | version = "0.52.0" 795 | source = "registry+https://github.com/rust-lang/crates.io-index" 796 | checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" 797 | 798 | [[package]] 799 | name = "windows_aarch64_msvc" 800 | version = "0.52.0" 801 | source = "registry+https://github.com/rust-lang/crates.io-index" 802 | checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" 803 | 804 | [[package]] 805 | name = "windows_i686_gnu" 806 | version = "0.52.0" 807 | source = "registry+https://github.com/rust-lang/crates.io-index" 808 | checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" 809 | 810 | [[package]] 811 | name = "windows_i686_msvc" 812 | version = "0.52.0" 813 | source = "registry+https://github.com/rust-lang/crates.io-index" 814 | checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" 815 | 816 | [[package]] 817 | name = "windows_x86_64_gnu" 818 | version = "0.52.0" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" 821 | 822 | [[package]] 823 | name = "windows_x86_64_gnullvm" 824 | version = "0.52.0" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" 827 | 828 | [[package]] 829 | name = "windows_x86_64_msvc" 830 | version = "0.52.0" 831 | source = "registry+https://github.com/rust-lang/crates.io-index" 832 | checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" 833 | -------------------------------------------------------------------------------- /docs/web-3e88601d133d91f8.js: -------------------------------------------------------------------------------- 1 | let wasm; 2 | 3 | const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } ); 4 | 5 | if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }; 6 | 7 | let cachedUint8Memory0 = null; 8 | 9 | function getUint8Memory0() { 10 | if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) { 11 | cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); 12 | } 13 | return cachedUint8Memory0; 14 | } 15 | 16 | function getStringFromWasm0(ptr, len) { 17 | ptr = ptr >>> 0; 18 | return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); 19 | } 20 | 21 | const heap = new Array(128).fill(undefined); 22 | 23 | heap.push(undefined, null, true, false); 24 | 25 | let heap_next = heap.length; 26 | 27 | function addHeapObject(obj) { 28 | if (heap_next === heap.length) heap.push(heap.length + 1); 29 | const idx = heap_next; 30 | heap_next = heap[idx]; 31 | 32 | heap[idx] = obj; 33 | return idx; 34 | } 35 | 36 | function getObject(idx) { return heap[idx]; } 37 | 38 | function dropObject(idx) { 39 | if (idx < 132) return; 40 | heap[idx] = heap_next; 41 | heap_next = idx; 42 | } 43 | 44 | function takeObject(idx) { 45 | const ret = getObject(idx); 46 | dropObject(idx); 47 | return ret; 48 | } 49 | 50 | function debugString(val) { 51 | // primitive types 52 | const type = typeof val; 53 | if (type == 'number' || type == 'boolean' || val == null) { 54 | return `${val}`; 55 | } 56 | if (type == 'string') { 57 | return `"${val}"`; 58 | } 59 | if (type == 'symbol') { 60 | const description = val.description; 61 | if (description == null) { 62 | return 'Symbol'; 63 | } else { 64 | return `Symbol(${description})`; 65 | } 66 | } 67 | if (type == 'function') { 68 | const name = val.name; 69 | if (typeof name == 'string' && name.length > 0) { 70 | return `Function(${name})`; 71 | } else { 72 | return 'Function'; 73 | } 74 | } 75 | // objects 76 | if (Array.isArray(val)) { 77 | const length = val.length; 78 | let debug = '['; 79 | if (length > 0) { 80 | debug += debugString(val[0]); 81 | } 82 | for(let i = 1; i < length; i++) { 83 | debug += ', ' + debugString(val[i]); 84 | } 85 | debug += ']'; 86 | return debug; 87 | } 88 | // Test for built-in 89 | const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); 90 | let className; 91 | if (builtInMatches.length > 1) { 92 | className = builtInMatches[1]; 93 | } else { 94 | // Failed to match the standard '[object ClassName]' 95 | return toString.call(val); 96 | } 97 | if (className == 'Object') { 98 | // we're a user defined class or Object 99 | // JSON.stringify avoids problems with cycles, and is generally much 100 | // easier than looping through ownProperties of `val`. 101 | try { 102 | return 'Object(' + JSON.stringify(val) + ')'; 103 | } catch (_) { 104 | return 'Object'; 105 | } 106 | } 107 | // errors 108 | if (val instanceof Error) { 109 | return `${val.name}: ${val.message}\n${val.stack}`; 110 | } 111 | // TODO we could test for more things here, like `Set`s and `Map`s. 112 | return className; 113 | } 114 | 115 | let WASM_VECTOR_LEN = 0; 116 | 117 | const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } ); 118 | 119 | const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' 120 | ? function (arg, view) { 121 | return cachedTextEncoder.encodeInto(arg, view); 122 | } 123 | : function (arg, view) { 124 | const buf = cachedTextEncoder.encode(arg); 125 | view.set(buf); 126 | return { 127 | read: arg.length, 128 | written: buf.length 129 | }; 130 | }); 131 | 132 | function passStringToWasm0(arg, malloc, realloc) { 133 | 134 | if (realloc === undefined) { 135 | const buf = cachedTextEncoder.encode(arg); 136 | const ptr = malloc(buf.length, 1) >>> 0; 137 | getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); 138 | WASM_VECTOR_LEN = buf.length; 139 | return ptr; 140 | } 141 | 142 | let len = arg.length; 143 | let ptr = malloc(len, 1) >>> 0; 144 | 145 | const mem = getUint8Memory0(); 146 | 147 | let offset = 0; 148 | 149 | for (; offset < len; offset++) { 150 | const code = arg.charCodeAt(offset); 151 | if (code > 0x7F) break; 152 | mem[ptr + offset] = code; 153 | } 154 | 155 | if (offset !== len) { 156 | if (offset !== 0) { 157 | arg = arg.slice(offset); 158 | } 159 | ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; 160 | const view = getUint8Memory0().subarray(ptr + offset, ptr + len); 161 | const ret = encodeString(arg, view); 162 | 163 | offset += ret.written; 164 | } 165 | 166 | WASM_VECTOR_LEN = offset; 167 | return ptr; 168 | } 169 | 170 | let cachedInt32Memory0 = null; 171 | 172 | function getInt32Memory0() { 173 | if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) { 174 | cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); 175 | } 176 | return cachedInt32Memory0; 177 | } 178 | 179 | function makeClosure(arg0, arg1, dtor, f) { 180 | const state = { a: arg0, b: arg1, cnt: 1, dtor }; 181 | const real = (...args) => { 182 | // First up with a closure we increment the internal reference 183 | // count. This ensures that the Rust closure environment won't 184 | // be deallocated while we're invoking it. 185 | state.cnt++; 186 | try { 187 | return f(state.a, state.b, ...args); 188 | } finally { 189 | if (--state.cnt === 0) { 190 | wasm.__wbindgen_export_2.get(state.dtor)(state.a, state.b); 191 | state.a = 0; 192 | 193 | } 194 | } 195 | }; 196 | real.original = state; 197 | 198 | return real; 199 | } 200 | 201 | let stack_pointer = 128; 202 | 203 | function addBorrowedObject(obj) { 204 | if (stack_pointer == 1) throw new Error('out of js stack'); 205 | heap[--stack_pointer] = obj; 206 | return stack_pointer; 207 | } 208 | function __wbg_adapter_20(arg0, arg1, arg2) { 209 | try { 210 | wasm._dyn_core__ops__function__Fn___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h49020ebf76b4216d(arg0, arg1, addBorrowedObject(arg2)); 211 | } finally { 212 | heap[stack_pointer++] = undefined; 213 | } 214 | } 215 | 216 | function makeMutClosure(arg0, arg1, dtor, f) { 217 | const state = { a: arg0, b: arg1, cnt: 1, dtor }; 218 | const real = (...args) => { 219 | // First up with a closure we increment the internal reference 220 | // count. This ensures that the Rust closure environment won't 221 | // be deallocated while we're invoking it. 222 | state.cnt++; 223 | const a = state.a; 224 | state.a = 0; 225 | try { 226 | return f(a, state.b, ...args); 227 | } finally { 228 | if (--state.cnt === 0) { 229 | wasm.__wbindgen_export_2.get(state.dtor)(a, state.b); 230 | 231 | } else { 232 | state.a = a; 233 | } 234 | } 235 | }; 236 | real.original = state; 237 | 238 | return real; 239 | } 240 | function __wbg_adapter_23(arg0, arg1, arg2) { 241 | wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hdddf6c683d9b8e62(arg0, arg1, addHeapObject(arg2)); 242 | } 243 | 244 | function isLikeNone(x) { 245 | return x === undefined || x === null; 246 | } 247 | 248 | let cachedUint32Memory0 = null; 249 | 250 | function getUint32Memory0() { 251 | if (cachedUint32Memory0 === null || cachedUint32Memory0.byteLength === 0) { 252 | cachedUint32Memory0 = new Uint32Array(wasm.memory.buffer); 253 | } 254 | return cachedUint32Memory0; 255 | } 256 | 257 | function getArrayJsValueFromWasm0(ptr, len) { 258 | ptr = ptr >>> 0; 259 | const mem = getUint32Memory0(); 260 | const slice = mem.subarray(ptr / 4, ptr / 4 + len); 261 | const result = []; 262 | for (let i = 0; i < slice.length; i++) { 263 | result.push(takeObject(slice[i])); 264 | } 265 | return result; 266 | } 267 | 268 | function handleError(f, args) { 269 | try { 270 | return f.apply(this, args); 271 | } catch (e) { 272 | wasm.__wbindgen_exn_store(addHeapObject(e)); 273 | } 274 | } 275 | 276 | async function __wbg_load(module, imports) { 277 | if (typeof Response === 'function' && module instanceof Response) { 278 | if (typeof WebAssembly.instantiateStreaming === 'function') { 279 | try { 280 | return await WebAssembly.instantiateStreaming(module, imports); 281 | 282 | } catch (e) { 283 | if (module.headers.get('Content-Type') != 'application/wasm') { 284 | console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); 285 | 286 | } else { 287 | throw e; 288 | } 289 | } 290 | } 291 | 292 | const bytes = await module.arrayBuffer(); 293 | return await WebAssembly.instantiate(bytes, imports); 294 | 295 | } else { 296 | const instance = await WebAssembly.instantiate(module, imports); 297 | 298 | if (instance instanceof WebAssembly.Instance) { 299 | return { instance, module }; 300 | 301 | } else { 302 | return instance; 303 | } 304 | } 305 | } 306 | 307 | function __wbg_get_imports() { 308 | const imports = {}; 309 | imports.wbg = {}; 310 | imports.wbg.__wbindgen_string_new = function(arg0, arg1) { 311 | const ret = getStringFromWasm0(arg0, arg1); 312 | return addHeapObject(ret); 313 | }; 314 | imports.wbg.__wbindgen_object_clone_ref = function(arg0) { 315 | const ret = getObject(arg0); 316 | return addHeapObject(ret); 317 | }; 318 | imports.wbg.__wbindgen_jsval_eq = function(arg0, arg1) { 319 | const ret = getObject(arg0) === getObject(arg1); 320 | return ret; 321 | }; 322 | imports.wbg.__wbindgen_cb_drop = function(arg0) { 323 | const obj = takeObject(arg0).original; 324 | if (obj.cnt-- == 1) { 325 | obj.a = 0; 326 | return true; 327 | } 328 | const ret = false; 329 | return ret; 330 | }; 331 | imports.wbg.__wbg_listenerid_6dcf1c62b7b7de58 = function(arg0, arg1) { 332 | const ret = getObject(arg1).__yew_listener_id; 333 | getInt32Memory0()[arg0 / 4 + 1] = isLikeNone(ret) ? 0 : ret; 334 | getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret); 335 | }; 336 | imports.wbg.__wbg_setlistenerid_f2e783343fa0cec1 = function(arg0, arg1) { 337 | getObject(arg0).__yew_listener_id = arg1 >>> 0; 338 | }; 339 | imports.wbg.__wbg_subtreeid_e80a1798fee782f9 = function(arg0, arg1) { 340 | const ret = getObject(arg1).__yew_subtree_id; 341 | getInt32Memory0()[arg0 / 4 + 1] = isLikeNone(ret) ? 0 : ret; 342 | getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret); 343 | }; 344 | imports.wbg.__wbg_setsubtreeid_e1fab6b578c800cf = function(arg0, arg1) { 345 | getObject(arg0).__yew_subtree_id = arg1 >>> 0; 346 | }; 347 | imports.wbg.__wbg_cachekey_b81c1aacc6a0645c = function(arg0, arg1) { 348 | const ret = getObject(arg1).__yew_subtree_cache_key; 349 | getInt32Memory0()[arg0 / 4 + 1] = isLikeNone(ret) ? 0 : ret; 350 | getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret); 351 | }; 352 | imports.wbg.__wbg_setcachekey_75bcd45312087529 = function(arg0, arg1) { 353 | getObject(arg0).__yew_subtree_cache_key = arg1 >>> 0; 354 | }; 355 | imports.wbg.__wbg_error_f851667af71bcfc6 = function(arg0, arg1) { 356 | let deferred0_0; 357 | let deferred0_1; 358 | try { 359 | deferred0_0 = arg0; 360 | deferred0_1 = arg1; 361 | console.error(getStringFromWasm0(arg0, arg1)); 362 | } finally { 363 | wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); 364 | } 365 | }; 366 | imports.wbg.__wbg_new_abda76e883ba8a5f = function() { 367 | const ret = new Error(); 368 | return addHeapObject(ret); 369 | }; 370 | imports.wbg.__wbg_stack_658279fe44541cf6 = function(arg0, arg1) { 371 | const ret = getObject(arg1).stack; 372 | const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 373 | const len1 = WASM_VECTOR_LEN; 374 | getInt32Memory0()[arg0 / 4 + 1] = len1; 375 | getInt32Memory0()[arg0 / 4 + 0] = ptr1; 376 | }; 377 | imports.wbg.__wbindgen_is_undefined = function(arg0) { 378 | const ret = getObject(arg0) === undefined; 379 | return ret; 380 | }; 381 | imports.wbg.__wbindgen_is_function = function(arg0) { 382 | const ret = typeof(getObject(arg0)) === 'function'; 383 | return ret; 384 | }; 385 | imports.wbg.__wbg_queueMicrotask_118eeb525d584d9a = function(arg0) { 386 | queueMicrotask(getObject(arg0)); 387 | }; 388 | imports.wbg.__wbg_queueMicrotask_26a89c14c53809c0 = function(arg0) { 389 | const ret = getObject(arg0).queueMicrotask; 390 | return addHeapObject(ret); 391 | }; 392 | imports.wbg.__wbg_error_a526fb08a0205972 = function(arg0, arg1) { 393 | var v0 = getArrayJsValueFromWasm0(arg0, arg1).slice(); 394 | wasm.__wbindgen_free(arg0, arg1 * 4, 4); 395 | console.error(...v0); 396 | }; 397 | imports.wbg.__wbg_instanceof_Window_99dc9805eaa2614b = function(arg0) { 398 | let result; 399 | try { 400 | result = getObject(arg0) instanceof Window; 401 | } catch (_) { 402 | result = false; 403 | } 404 | const ret = result; 405 | return ret; 406 | }; 407 | imports.wbg.__wbg_document_5257b70811e953c0 = function(arg0) { 408 | const ret = getObject(arg0).document; 409 | return isLikeNone(ret) ? 0 : addHeapObject(ret); 410 | }; 411 | imports.wbg.__wbg_body_3eb73da919b867a1 = function(arg0) { 412 | const ret = getObject(arg0).body; 413 | return isLikeNone(ret) ? 0 : addHeapObject(ret); 414 | }; 415 | imports.wbg.__wbg_createElement_1a136faad4101f43 = function() { return handleError(function (arg0, arg1, arg2) { 416 | const ret = getObject(arg0).createElement(getStringFromWasm0(arg1, arg2)); 417 | return addHeapObject(ret); 418 | }, arguments) }; 419 | imports.wbg.__wbg_createElementNS_d47e0c50fa2904e0 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { 420 | const ret = getObject(arg0).createElementNS(arg1 === 0 ? undefined : getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); 421 | return addHeapObject(ret); 422 | }, arguments) }; 423 | imports.wbg.__wbg_createTextNode_dbdd908f92bae1b1 = function(arg0, arg1, arg2) { 424 | const ret = getObject(arg0).createTextNode(getStringFromWasm0(arg1, arg2)); 425 | return addHeapObject(ret); 426 | }; 427 | imports.wbg.__wbg_instanceof_Element_f614cf57d4316979 = function(arg0) { 428 | let result; 429 | try { 430 | result = getObject(arg0) instanceof Element; 431 | } catch (_) { 432 | result = false; 433 | } 434 | const ret = result; 435 | return ret; 436 | }; 437 | imports.wbg.__wbg_namespaceURI_0819c2800784a176 = function(arg0, arg1) { 438 | const ret = getObject(arg1).namespaceURI; 439 | var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 440 | var len1 = WASM_VECTOR_LEN; 441 | getInt32Memory0()[arg0 / 4 + 1] = len1; 442 | getInt32Memory0()[arg0 / 4 + 0] = ptr1; 443 | }; 444 | imports.wbg.__wbg_setinnerHTML_99deeacfff0ae4cc = function(arg0, arg1, arg2) { 445 | getObject(arg0).innerHTML = getStringFromWasm0(arg1, arg2); 446 | }; 447 | imports.wbg.__wbg_outerHTML_69934f9195df65af = function(arg0, arg1) { 448 | const ret = getObject(arg1).outerHTML; 449 | const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 450 | const len1 = WASM_VECTOR_LEN; 451 | getInt32Memory0()[arg0 / 4 + 1] = len1; 452 | getInt32Memory0()[arg0 / 4 + 0] = ptr1; 453 | }; 454 | imports.wbg.__wbg_removeAttribute_5c264e727b67dbdb = function() { return handleError(function (arg0, arg1, arg2) { 455 | getObject(arg0).removeAttribute(getStringFromWasm0(arg1, arg2)); 456 | }, arguments) }; 457 | imports.wbg.__wbg_setAttribute_0918ea45d5a1c663 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { 458 | getObject(arg0).setAttribute(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4)); 459 | }, arguments) }; 460 | imports.wbg.__wbg_debug_0207b724052e591d = function(arg0, arg1, arg2, arg3) { 461 | console.debug(getObject(arg0), getObject(arg1), getObject(arg2), getObject(arg3)); 462 | }; 463 | imports.wbg.__wbg_error_1f4e3e298a7c97f6 = function(arg0) { 464 | console.error(getObject(arg0)); 465 | }; 466 | imports.wbg.__wbg_error_8cf137381b3af25f = function(arg0, arg1, arg2, arg3) { 467 | console.error(getObject(arg0), getObject(arg1), getObject(arg2), getObject(arg3)); 468 | }; 469 | imports.wbg.__wbg_info_eb81e4fcae9ba8f1 = function(arg0, arg1, arg2, arg3) { 470 | console.info(getObject(arg0), getObject(arg1), getObject(arg2), getObject(arg3)); 471 | }; 472 | imports.wbg.__wbg_log_bd0951a507fbf762 = function(arg0, arg1, arg2, arg3) { 473 | console.log(getObject(arg0), getObject(arg1), getObject(arg2), getObject(arg3)); 474 | }; 475 | imports.wbg.__wbg_warn_ea08466617ec5d3a = function(arg0, arg1, arg2, arg3) { 476 | console.warn(getObject(arg0), getObject(arg1), getObject(arg2), getObject(arg3)); 477 | }; 478 | imports.wbg.__wbg_parentNode_f3957fdd408a62f7 = function(arg0) { 479 | const ret = getObject(arg0).parentNode; 480 | return isLikeNone(ret) ? 0 : addHeapObject(ret); 481 | }; 482 | imports.wbg.__wbg_parentElement_86a7612dde875ba9 = function(arg0) { 483 | const ret = getObject(arg0).parentElement; 484 | return isLikeNone(ret) ? 0 : addHeapObject(ret); 485 | }; 486 | imports.wbg.__wbg_childNodes_75d3da5f3a7bb985 = function(arg0) { 487 | const ret = getObject(arg0).childNodes; 488 | return addHeapObject(ret); 489 | }; 490 | imports.wbg.__wbg_lastChild_8f7b6f3825115eff = function(arg0) { 491 | const ret = getObject(arg0).lastChild; 492 | return isLikeNone(ret) ? 0 : addHeapObject(ret); 493 | }; 494 | imports.wbg.__wbg_nextSibling_13e9454ef5323f1a = function(arg0) { 495 | const ret = getObject(arg0).nextSibling; 496 | return isLikeNone(ret) ? 0 : addHeapObject(ret); 497 | }; 498 | imports.wbg.__wbg_setnodeValue_8656e865e9b11bbb = function(arg0, arg1, arg2) { 499 | getObject(arg0).nodeValue = arg1 === 0 ? undefined : getStringFromWasm0(arg1, arg2); 500 | }; 501 | imports.wbg.__wbg_textContent_efe8338af53ddf62 = function(arg0, arg1) { 502 | const ret = getObject(arg1).textContent; 503 | var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 504 | var len1 = WASM_VECTOR_LEN; 505 | getInt32Memory0()[arg0 / 4 + 1] = len1; 506 | getInt32Memory0()[arg0 / 4 + 0] = ptr1; 507 | }; 508 | imports.wbg.__wbg_cloneNode_80501c66ab115588 = function() { return handleError(function (arg0) { 509 | const ret = getObject(arg0).cloneNode(); 510 | return addHeapObject(ret); 511 | }, arguments) }; 512 | imports.wbg.__wbg_insertBefore_882082ef4c5d7766 = function() { return handleError(function (arg0, arg1, arg2) { 513 | const ret = getObject(arg0).insertBefore(getObject(arg1), getObject(arg2)); 514 | return addHeapObject(ret); 515 | }, arguments) }; 516 | imports.wbg.__wbg_removeChild_14b08321b677677a = function() { return handleError(function (arg0, arg1) { 517 | const ret = getObject(arg0).removeChild(getObject(arg1)); 518 | return addHeapObject(ret); 519 | }, arguments) }; 520 | imports.wbg.__wbg_setchecked_3b12f3d602a63e47 = function(arg0, arg1) { 521 | getObject(arg0).checked = arg1 !== 0; 522 | }; 523 | imports.wbg.__wbg_value_c93cb4b4d352228e = function(arg0, arg1) { 524 | const ret = getObject(arg1).value; 525 | const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 526 | const len1 = WASM_VECTOR_LEN; 527 | getInt32Memory0()[arg0 / 4 + 1] = len1; 528 | getInt32Memory0()[arg0 / 4 + 0] = ptr1; 529 | }; 530 | imports.wbg.__wbg_setvalue_9bd3f93b3864ddbf = function(arg0, arg1, arg2) { 531 | getObject(arg0).value = getStringFromWasm0(arg1, arg2); 532 | }; 533 | imports.wbg.__wbg_addEventListener_1b158e9e95e0ab00 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { 534 | getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), getObject(arg4)); 535 | }, arguments) }; 536 | imports.wbg.__wbg_removeEventListener_177ff96081e6f22d = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) { 537 | getObject(arg0).removeEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), arg4 !== 0); 538 | }, arguments) }; 539 | imports.wbg.__wbg_keyCode_6acbcd0e4e062504 = function(arg0) { 540 | const ret = getObject(arg0).keyCode; 541 | return ret; 542 | }; 543 | imports.wbg.__wbg_value_ab23a75318ea828f = function(arg0, arg1) { 544 | const ret = getObject(arg1).value; 545 | const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 546 | const len1 = WASM_VECTOR_LEN; 547 | getInt32Memory0()[arg0 / 4 + 1] = len1; 548 | getInt32Memory0()[arg0 / 4 + 0] = ptr1; 549 | }; 550 | imports.wbg.__wbg_setvalue_918a8ae77531a942 = function(arg0, arg1, arg2) { 551 | getObject(arg0).value = getStringFromWasm0(arg1, arg2); 552 | }; 553 | imports.wbg.__wbg_target_791826e938c3e308 = function(arg0) { 554 | const ret = getObject(arg0).target; 555 | return isLikeNone(ret) ? 0 : addHeapObject(ret); 556 | }; 557 | imports.wbg.__wbg_bubbles_f0783dc095f8e220 = function(arg0) { 558 | const ret = getObject(arg0).bubbles; 559 | return ret; 560 | }; 561 | imports.wbg.__wbg_cancelBubble_191799b8e0ab3254 = function(arg0) { 562 | const ret = getObject(arg0).cancelBubble; 563 | return ret; 564 | }; 565 | imports.wbg.__wbg_composedPath_d94a39b8c8f6eed1 = function(arg0) { 566 | const ret = getObject(arg0).composedPath(); 567 | return addHeapObject(ret); 568 | }; 569 | imports.wbg.__wbg_instanceof_ShadowRoot_cb6366cb0956ce29 = function(arg0) { 570 | let result; 571 | try { 572 | result = getObject(arg0) instanceof ShadowRoot; 573 | } catch (_) { 574 | result = false; 575 | } 576 | const ret = result; 577 | return ret; 578 | }; 579 | imports.wbg.__wbg_host_99e27ed8897850f2 = function(arg0) { 580 | const ret = getObject(arg0).host; 581 | return addHeapObject(ret); 582 | }; 583 | imports.wbg.__wbg_get_c43534c00f382c8a = function(arg0, arg1) { 584 | const ret = getObject(arg0)[arg1 >>> 0]; 585 | return addHeapObject(ret); 586 | }; 587 | imports.wbg.__wbg_from_a663e01d8dab8e44 = function(arg0) { 588 | const ret = Array.from(getObject(arg0)); 589 | return addHeapObject(ret); 590 | }; 591 | imports.wbg.__wbg_length_d99b680fd68bf71b = function(arg0) { 592 | const ret = getObject(arg0).length; 593 | return ret; 594 | }; 595 | imports.wbg.__wbg_newnoargs_5859b6d41c6fe9f7 = function(arg0, arg1) { 596 | const ret = new Function(getStringFromWasm0(arg0, arg1)); 597 | return addHeapObject(ret); 598 | }; 599 | imports.wbg.__wbg_call_a79f1973a4f07d5e = function() { return handleError(function (arg0, arg1) { 600 | const ret = getObject(arg0).call(getObject(arg1)); 601 | return addHeapObject(ret); 602 | }, arguments) }; 603 | imports.wbg.__wbg_is_a5728dbfb61c82cd = function(arg0, arg1) { 604 | const ret = Object.is(getObject(arg0), getObject(arg1)); 605 | return ret; 606 | }; 607 | imports.wbg.__wbg_new_87d841e70661f6e9 = function() { 608 | const ret = new Object(); 609 | return addHeapObject(ret); 610 | }; 611 | imports.wbg.__wbg_resolve_97ecd55ee839391b = function(arg0) { 612 | const ret = Promise.resolve(getObject(arg0)); 613 | return addHeapObject(ret); 614 | }; 615 | imports.wbg.__wbg_then_7aeb7c5f1536640f = function(arg0, arg1) { 616 | const ret = getObject(arg0).then(getObject(arg1)); 617 | return addHeapObject(ret); 618 | }; 619 | imports.wbg.__wbg_globalThis_e5f801a37ad7d07b = function() { return handleError(function () { 620 | const ret = globalThis.globalThis; 621 | return addHeapObject(ret); 622 | }, arguments) }; 623 | imports.wbg.__wbg_self_086b5302bcafb962 = function() { return handleError(function () { 624 | const ret = self.self; 625 | return addHeapObject(ret); 626 | }, arguments) }; 627 | imports.wbg.__wbg_window_132fa5d7546f1de5 = function() { return handleError(function () { 628 | const ret = window.window; 629 | return addHeapObject(ret); 630 | }, arguments) }; 631 | imports.wbg.__wbg_global_f9a61fce4af6b7c1 = function() { return handleError(function () { 632 | const ret = global.global; 633 | return addHeapObject(ret); 634 | }, arguments) }; 635 | imports.wbg.__wbg_set_37a50e901587b477 = function() { return handleError(function (arg0, arg1, arg2) { 636 | const ret = Reflect.set(getObject(arg0), getObject(arg1), getObject(arg2)); 637 | return ret; 638 | }, arguments) }; 639 | imports.wbg.__wbindgen_debug_string = function(arg0, arg1) { 640 | const ret = debugString(getObject(arg1)); 641 | const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); 642 | const len1 = WASM_VECTOR_LEN; 643 | getInt32Memory0()[arg0 / 4 + 1] = len1; 644 | getInt32Memory0()[arg0 / 4 + 0] = ptr1; 645 | }; 646 | imports.wbg.__wbindgen_object_drop_ref = function(arg0) { 647 | takeObject(arg0); 648 | }; 649 | imports.wbg.__wbindgen_throw = function(arg0, arg1) { 650 | throw new Error(getStringFromWasm0(arg0, arg1)); 651 | }; 652 | imports.wbg.__wbindgen_closure_wrapper1894 = function(arg0, arg1, arg2) { 653 | const ret = makeClosure(arg0, arg1, 153, __wbg_adapter_20); 654 | return addHeapObject(ret); 655 | }; 656 | imports.wbg.__wbindgen_closure_wrapper3290 = function(arg0, arg1, arg2) { 657 | const ret = makeMutClosure(arg0, arg1, 262, __wbg_adapter_23); 658 | return addHeapObject(ret); 659 | }; 660 | 661 | return imports; 662 | } 663 | 664 | function __wbg_init_memory(imports, maybe_memory) { 665 | 666 | } 667 | 668 | function __wbg_finalize_init(instance, module) { 669 | wasm = instance.exports; 670 | __wbg_init.__wbindgen_wasm_module = module; 671 | cachedInt32Memory0 = null; 672 | cachedUint32Memory0 = null; 673 | cachedUint8Memory0 = null; 674 | 675 | wasm.__wbindgen_start(); 676 | return wasm; 677 | } 678 | 679 | function initSync(module) { 680 | if (wasm !== undefined) return wasm; 681 | 682 | const imports = __wbg_get_imports(); 683 | 684 | __wbg_init_memory(imports); 685 | 686 | if (!(module instanceof WebAssembly.Module)) { 687 | module = new WebAssembly.Module(module); 688 | } 689 | 690 | const instance = new WebAssembly.Instance(module, imports); 691 | 692 | return __wbg_finalize_init(instance, module); 693 | } 694 | 695 | async function __wbg_init(input) { 696 | if (wasm !== undefined) return wasm; 697 | 698 | if (typeof input === 'undefined') { 699 | input = new URL('web-3e88601d133d91f8_bg.wasm', import.meta.url); 700 | } 701 | const imports = __wbg_get_imports(); 702 | 703 | if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) { 704 | input = fetch(input); 705 | } 706 | 707 | __wbg_init_memory(imports); 708 | 709 | const { instance, module } = await __wbg_load(await input, imports); 710 | 711 | return __wbg_finalize_init(instance, module); 712 | } 713 | 714 | export { initSync } 715 | export default __wbg_init; 716 | -------------------------------------------------------------------------------- /ordo/src/parser.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::{BTreeMap, BTreeSet}, 3 | fmt, 4 | }; 5 | 6 | use itertools::Itertools; 7 | use logos::{Lexer, Logos}; 8 | 9 | use crate::{ 10 | expr::{At, Constraints, Expr, ExprAt, IntBinOp, Pattern, PatternAt, Position, Type}, 11 | lexer::Token, 12 | }; 13 | 14 | const EOF: &str = ""; 15 | 16 | #[derive(Debug, PartialEq)] 17 | pub enum Error { 18 | Lexer, 19 | Expected(&'static str, Vec, Option), 20 | ExpectedEof(Token), 21 | UnexpectedEof, 22 | InvalidPrefix(Option), 23 | DuplicateLabel(At), 24 | InvalidTypeVarName(At), 25 | NoRowForConstraints(At), 26 | RowConstraintsAlreadyDefined(At), 27 | } 28 | 29 | impl fmt::Display for Error { 30 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 31 | match self { 32 | Error::Lexer => write!(f, "lexer encountered an unexpected token"), 33 | Error::Expected(context, tokens, token) => { 34 | let token= match token { 35 | Some(token) => token.to_string(), 36 | None => EOF.to_owned(), 37 | }; 38 | let tokens = tokens.iter().map(|token| token.to_string()).join(", "); 39 | write!(f, "got {}, expected {} in context {}", token, tokens, context) 40 | } 41 | Error::ExpectedEof(token) => { 42 | write!(f, "got {}, expected {}", token, EOF) 43 | } 44 | Error::UnexpectedEof => write!(f, "got {}, expected something", EOF), 45 | Error::InvalidPrefix(token) => { 46 | let token_str = match token { 47 | Some(token) => token.to_string(), 48 | None => EOF.to_owned(), 49 | }; 50 | write!(f, "invalid prefix: {}", token_str) 51 | } 52 | Error::DuplicateLabel(label) => write!(f, "duplicate label: {}", label.value), 53 | Error::InvalidTypeVarName(name) => write!(f, "invalid type var name, should be either one letter or the letter 'r' and one letter, was: {}", name.value), 54 | Error::NoRowForConstraints(name) => write!(f, "found constraint for a row that isn't defined: {}", name.value), 55 | Error::RowConstraintsAlreadyDefined(name) => write!(f, "constraints for a row with constraints already defined: {}", name.value), 56 | } 57 | } 58 | } 59 | 60 | type Result = std::result::Result; 61 | 62 | pub struct Parser<'a> { 63 | is_repl: bool, 64 | lexer: Lexer<'a, Token>, 65 | token: Option, 66 | } 67 | 68 | impl<'a> Parser<'a> { 69 | fn at(&self, value: T) -> At { 70 | let line = self.lexer.extras.line_breaks; 71 | let offset = self.lexer.extras.column_offset; 72 | let span = self.lexer.span(); 73 | At { 74 | value, 75 | start: Position { 76 | line, 77 | column: span.start - offset, 78 | }, 79 | end: Position { 80 | line, 81 | column: span.end - offset, 82 | }, 83 | } 84 | } 85 | 86 | fn check(&mut self, token: Token) -> bool { 87 | self.token == Some(token) 88 | } 89 | 90 | fn matches(&mut self, token: Token) -> Result>> { 91 | if !self.check(token) { 92 | return Ok(None); 93 | } 94 | let at = self.at(()); 95 | self.advance()?; 96 | Ok(Some(at)) 97 | } 98 | 99 | fn matches_ident(&mut self) -> Result>> { 100 | let token = self.token.take(); 101 | if let Some(Token::Ident(ident)) = token { 102 | let ident = self.at(ident); 103 | self.advance()?; 104 | return Ok(Some(ident)); 105 | } 106 | self.token = token; 107 | Ok(None) 108 | } 109 | 110 | fn matches_int(&mut self) -> Result>> { 111 | let token = self.token.take(); 112 | if let Some(Token::Int(i)) = token { 113 | let i = self.at(i); 114 | self.advance()?; 115 | return Ok(Some(i)); 116 | } 117 | self.token = token; 118 | Ok(None) 119 | } 120 | 121 | fn matches_eof(&self) -> bool { 122 | self.token.is_none() 123 | } 124 | 125 | fn advance(&mut self) -> Result<()> { 126 | self.token = self.lexer.next().transpose().map_err(|()| Error::Lexer)?; 127 | Ok(()) 128 | } 129 | 130 | fn expected(&mut self, tokens: Vec, context: &'static str) -> Result { 131 | Err(Error::Expected(context, tokens, self.token.take())) 132 | } 133 | 134 | fn expect(&mut self, expected: Token, context: &'static str) -> Result> { 135 | if self.token.as_ref() != Some(&expected) { 136 | return self.expected(vec![expected], context); 137 | } 138 | let at = self.at(()); 139 | self.advance()?; 140 | Ok(at) 141 | } 142 | 143 | fn expect_ident(&mut self, context: &'static str) -> Result> { 144 | let token = self.token.take(); 145 | if let Some(Token::Ident(ident)) = token { 146 | let ident = self.at(ident); 147 | self.advance()?; 148 | return Ok(ident); 149 | } 150 | Err(Error::Expected(context, vec![Token::empty_ident()], token)) 151 | } 152 | 153 | pub fn repl(source: &str) -> Result { 154 | Self::expr_source(source, true) 155 | } 156 | 157 | pub fn expr(source: &str) -> Result { 158 | Self::expr_source(source, false) 159 | } 160 | 161 | fn expr_source(source: &str, is_repl: bool) -> Result { 162 | let lexer = Token::lexer(source); 163 | let token = None; 164 | let mut parser = Parser { 165 | lexer, 166 | token, 167 | is_repl, 168 | }; 169 | parser.advance()?; 170 | let expr = parser.expr_inner(0)?; 171 | if let Some(token) = parser.token.take() { 172 | return Err(Error::ExpectedEof(token)); 173 | } 174 | Ok(expr) 175 | } 176 | 177 | fn paren_expr(&mut self) -> Result { 178 | let expr = self.expr_inner(0)?; 179 | self.expect(Token::RParen, "paren expr")?; 180 | Ok(expr) 181 | } 182 | 183 | /// Return the labels and closing brace location 184 | fn pattern_expr_inner(&mut self) -> Result<(BTreeMap, At<()>)> { 185 | let mut labels = BTreeMap::new(); 186 | loop { 187 | let label = self.expect_ident("pattern expr record label")?; 188 | let pattern = if self.matches(Token::Equal)?.is_some() { 189 | self.pattern_expr()? 190 | } else { 191 | label.clone().map(Pattern::Var).into() 192 | }; 193 | if labels.insert(label.clone().value, pattern).is_some() { 194 | return Err(Error::DuplicateLabel(label)); 195 | } 196 | if let Some(r) = self.matches(Token::RBrace)? { 197 | return Ok((labels, r)); 198 | } else if self.matches(Token::Comma)?.is_some() { 199 | continue; 200 | } else { 201 | return self.expected( 202 | vec![Token::empty_ident(), Token::RBrace], 203 | "pattern expr record", 204 | ); 205 | } 206 | } 207 | } 208 | 209 | fn pattern_expr(&mut self) -> Result { 210 | if let Some(var) = self.matches_ident()? { 211 | Ok(var.map(Pattern::Var).into()) 212 | } else if let Some(l) = self.matches(Token::LBrace)? { 213 | if let Some(r) = self.matches(Token::RBrace)? { 214 | return Ok(l.span_with(r, Expr::RecordEmpty).into()); 215 | } 216 | let (labels, r) = self.pattern_expr_inner()?; 217 | Ok(l.span_with( 218 | r.clone(), 219 | Expr::RecordExtend(labels, r.map(|()| Expr::RecordEmpty).into()), 220 | ) 221 | .into()) 222 | } else { 223 | self.expected(vec![Token::empty_ident(), Token::LBrace], "pattern expr") 224 | } 225 | } 226 | 227 | fn pattern_list(&mut self) -> Result> { 228 | let mut patterns = Vec::new(); 229 | loop { 230 | let pattern = self.pattern_expr()?; 231 | patterns.push(pattern); 232 | if self.matches(Token::RParen)?.is_some() { 233 | break; 234 | } else if self.matches(Token::Comma)?.is_some() { 235 | continue; 236 | } else { 237 | return self.expected(vec![Token::RParen, Token::Comma], "pattern list"); 238 | } 239 | } 240 | Ok(patterns) 241 | } 242 | 243 | fn let_expr(&mut self, at: At<()>) -> Result { 244 | let pattern = self.pattern_expr()?; 245 | match *pattern.expr { 246 | Pattern::Var(_) if self.matches(Token::LParen)?.is_some() => { 247 | let params = self.pattern_list()?; 248 | self.expect(Token::Equal, "let expr fun")?; 249 | let fun_body = self.expr_inner(0)?; 250 | let body = if self.is_repl && self.token.is_none() { 251 | pattern.clone() 252 | } else { 253 | self.expect(Token::In, "let expr fun")?; 254 | self.expr_inner(0)? 255 | }; 256 | let fun = ExprAt { 257 | context: at.clone().into(), 258 | expr: Expr::Fun(params, fun_body).into(), 259 | }; 260 | let expr = Expr::Let(pattern, fun, body); 261 | Ok(ExprAt { 262 | context: at.into(), 263 | expr: expr.into(), 264 | }) 265 | } 266 | _ => { 267 | self.expect(Token::Equal, "let expr")?; 268 | let value = self.expr_inner(0)?; 269 | if self.is_repl && self.token.is_none() { 270 | let body = pattern.clone(); 271 | Ok(ExprAt { 272 | context: at.into(), 273 | expr: Expr::Let(pattern, value, body).into(), 274 | }) 275 | } else { 276 | self.expect(Token::In, "let expr")?; 277 | let body = self.expr_inner(0)?; 278 | Ok(ExprAt { 279 | context: at.into(), 280 | expr: Expr::Let(pattern, value, body).into(), 281 | }) 282 | } 283 | } 284 | } 285 | } 286 | 287 | fn fun_expr(&mut self, at: At<()>) -> Result { 288 | self.expect(Token::LParen, "fun expr")?; 289 | let params = self.pattern_list()?; 290 | self.expect(Token::Arrow, "fun expr")?; 291 | let body = self.expr_inner(0)?; 292 | Ok(ExprAt { 293 | context: at.into(), 294 | expr: Expr::Fun(params, body).into(), 295 | }) 296 | } 297 | 298 | /// Return is labels, rest and closing brace location 299 | fn record_expr_inner(&mut self) -> Result<(BTreeMap, ExprAt, At<()>)> { 300 | let mut labels = BTreeMap::new(); 301 | loop { 302 | let label = self.expect_ident("record expr label")?; 303 | let expr = if self.matches(Token::Equal)?.is_some() { 304 | self.expr_inner(0)? 305 | } else { 306 | label.clone().map(Expr::Var).into() 307 | }; 308 | 309 | if labels.insert(label.value.clone(), expr).is_some() { 310 | return Err(Error::DuplicateLabel(label)); 311 | } 312 | 313 | if self.matches(Token::Comma)?.is_some() { 314 | continue; 315 | } else if let Some(r) = self.matches(Token::RBrace)? { 316 | let rest = ExprAt { 317 | context: r.clone().into(), 318 | expr: Expr::RecordEmpty.into(), 319 | }; 320 | return Ok((labels, rest, r)); 321 | } else if self.matches(Token::Pipe)?.is_some() { 322 | let rest = self.expr_inner(0)?; 323 | let r = self.expect(Token::RBrace, "record expr rest")?; 324 | return Ok((labels, rest, r)); 325 | } else { 326 | return self.expected( 327 | vec![Token::Pipe, Token::Comma, Token::RBrace], 328 | "record expr", 329 | ); 330 | } 331 | } 332 | } 333 | 334 | fn record_expr(&mut self, l: At<()>) -> Result { 335 | if let Some(r) = self.matches(Token::RBrace)? { 336 | return Ok(l.span_with(r, Expr::RecordEmpty).into()); 337 | } 338 | let (labels, rest, r) = self.record_expr_inner()?; 339 | Ok(ExprAt { 340 | context: l.span_with(r, ()).into(), 341 | expr: Expr::RecordExtend(labels, rest).into(), 342 | }) 343 | } 344 | 345 | fn variant_expr(&mut self, at: At<()>) -> Result { 346 | let label = self.expect_ident("variant expr")?; 347 | let expr = self.expr_inner(0)?; 348 | let label_only = label.value.clone(); 349 | let context = at.span_with(label, ()).into(); 350 | Ok(ExprAt { 351 | context, 352 | expr: Expr::Variant(label_only, expr).into(), 353 | }) 354 | } 355 | 356 | fn match_expr(&mut self, at: At<()>) -> Result { 357 | let expr = self.expr_inner(0)?; 358 | self.expect(Token::LBrace, "match expr")?; 359 | let mut cases = Vec::new(); 360 | let mut default_case = None; 361 | loop { 362 | if self.matches(Token::Colon)?.is_some() { 363 | let variant = self.expect_ident("match expr case variant")?; 364 | let var = self.expect_ident("match expr case value")?; 365 | self.expect(Token::Arrow, "match expr case")?; 366 | let expr = self.expr_inner(0)?; 367 | cases.push((variant.value, var.value, expr)); 368 | } else if let Some(var) = self.matches_ident()? { 369 | self.expect(Token::Arrow, "match expr default case")?; 370 | let expr = self.expr_inner(0)?; 371 | default_case = Some((var.value, expr)); 372 | self.expect(Token::RBrace, "match expr default case")?; 373 | break; 374 | } else { 375 | return self.expected(vec![Token::Colon, Token::empty_ident()], "match expr case"); 376 | } 377 | if self.matches(Token::Comma)?.is_some() { 378 | continue; 379 | } else if self.matches(Token::RBrace)?.is_some() { 380 | break; 381 | } else { 382 | return self.expected(vec![Token::Comma, Token::RBrace], "match expr"); 383 | } 384 | } 385 | Ok(ExprAt { 386 | context: at.into(), 387 | expr: Expr::Case(expr, cases, default_case).into(), 388 | }) 389 | } 390 | 391 | fn if_expr(&mut self, at: At<()>) -> Result { 392 | let if_expr = self.expr_inner(0)?; 393 | self.expect(Token::Then, "if expr")?; 394 | let if_body = self.expr_inner(0)?; 395 | let mut elifs = Vec::new(); 396 | loop { 397 | if self.matches(Token::Else)?.is_some() { 398 | break; 399 | } else if self.matches(Token::Elif)?.is_some() { 400 | let elif_expr = self.expr_inner(0)?; 401 | self.expect(Token::Then, "if expr elif")?; 402 | let elif_body = self.expr_inner(0)?; 403 | elifs.push((elif_expr, elif_body)); 404 | } else { 405 | return self.expected(vec![Token::Elif, Token::Else], "if expr elif"); 406 | } 407 | } 408 | let else_body = self.expr_inner(0)?; 409 | Ok(ExprAt { 410 | context: at.into(), 411 | expr: Expr::If(if_expr, if_body, elifs, else_body).into(), 412 | }) 413 | } 414 | 415 | fn expr_lhs(&mut self) -> Result { 416 | if let Some(var) = self.matches_ident()? { 417 | Ok(var.map(Expr::Var).into()) 418 | } else if let Some(i) = self.matches_int()? { 419 | Ok(i.map(Expr::Int).into()) 420 | } else if let Some(at) = self.matches(Token::True)? { 421 | Ok(at.map(|()| Expr::Bool(true)).into()) 422 | } else if let Some(at) = self.matches(Token::False)? { 423 | Ok(at.map(|()| Expr::Bool(false)).into()) 424 | } else if self.matches(Token::LParen)?.is_some() { 425 | self.paren_expr() 426 | } else if let Some(at) = self.matches(Token::Let)? { 427 | self.let_expr(at) 428 | } else if let Some(at) = self.matches(Token::Fun)? { 429 | self.fun_expr(at) 430 | } else if let Some(l) = self.matches(Token::LBrace)? { 431 | self.record_expr(l) 432 | } else if let Some(at) = self.matches(Token::Colon)? { 433 | self.variant_expr(at) 434 | } else if let Some(at) = self.matches(Token::Match)? { 435 | self.match_expr(at) 436 | } else if let Some(at) = self.matches(Token::If)? { 437 | self.if_expr(at) 438 | } else { 439 | let r_bp = self.prefix_bp()?; 440 | if let Some(at) = self.matches(Token::Negate)? { 441 | let rhs = self.expr_inner(r_bp)?; 442 | Ok(at.map(|()| Expr::Negate(rhs)).into()) 443 | } else { 444 | Err(Error::InvalidPrefix(self.token.take())) 445 | } 446 | } 447 | } 448 | 449 | fn call_expr(&mut self, lhs: ExprAt) -> Result { 450 | let mut args = vec![self.expr_inner(0)?]; 451 | loop { 452 | if self.matches(Token::RParen)?.is_some() { 453 | break; 454 | } else if self.matches(Token::Comma)?.is_some() { 455 | } else { 456 | return self.expected(vec![Token::Comma, Token::RParen], "call expr"); 457 | } 458 | let arg = self.expr_inner(0)?; 459 | args.push(arg); 460 | } 461 | Ok(ExprAt { 462 | context: lhs.context.clone(), 463 | expr: Expr::Call(lhs, args).into(), 464 | }) 465 | } 466 | 467 | fn record_select_expr(&mut self, lhs: ExprAt) -> Result { 468 | let field = self.expect_ident("record select expr")?; 469 | Ok(field.map(|field| Expr::RecordSelect(lhs, field)).into()) 470 | } 471 | 472 | fn record_restrict_expr(&mut self, lhs: ExprAt) -> Result { 473 | let field = self.expect_ident("record restrict expr")?; 474 | Ok(field.map(|field| Expr::RecordRestrict(lhs, field)).into()) 475 | } 476 | 477 | fn unwrap_expr(&mut self, lhs: ExprAt, at: At<()>) -> Result { 478 | Ok(at.map(|()| Expr::Unwrap(lhs)).into()) 479 | } 480 | 481 | fn expr_postfix(&mut self, lhs: ExprAt) -> Result { 482 | if self.matches(Token::LParen)?.is_some() { 483 | self.call_expr(lhs) 484 | } else if self.matches(Token::Dot)?.is_some() { 485 | self.record_select_expr(lhs) 486 | } else if self.matches(Token::Backslash)?.is_some() { 487 | self.record_restrict_expr(lhs) 488 | } else if let Some(at) = self.matches(Token::QuestionMark)? { 489 | self.unwrap_expr(lhs, at) 490 | } else { 491 | self.expected( 492 | vec![Token::LParen, Token::Dot, Token::Backslash], 493 | "expr postfix", 494 | ) 495 | } 496 | } 497 | 498 | fn binop_expr(&mut self, at: At<()>, op: IntBinOp, lhs: ExprAt, r_bp: u8) -> Result { 499 | let rhs = self.expr_inner(r_bp)?; 500 | Ok(at.map(|()| Expr::IntBinOp(op, lhs, rhs)).into()) 501 | } 502 | 503 | fn expr_infix(&mut self, lhs: ExprAt, r_bp: u8) -> Result { 504 | if let Some(at) = self.matches(Token::Plus)? { 505 | self.binop_expr(at, IntBinOp::Plus, lhs, r_bp) 506 | } else if let Some(at) = self.matches(Token::Minus)? { 507 | self.binop_expr(at, IntBinOp::Minus, lhs, r_bp) 508 | } else if let Some(at) = self.matches(Token::Multiply)? { 509 | self.binop_expr(at, IntBinOp::Multiply, lhs, r_bp) 510 | } else if let Some(at) = self.matches(Token::Divide)? { 511 | self.binop_expr(at, IntBinOp::Divide, lhs, r_bp) 512 | } else if let Some(at) = self.matches(Token::EqualEqual)? { 513 | let rhs = self.expr_inner(r_bp)?; 514 | Ok(at.map(|()| Expr::EqualEqual(lhs, rhs)).into()) 515 | } else if let Some(at) = self.matches(Token::LessThan)? { 516 | self.binop_expr(at, IntBinOp::LessThan, lhs, r_bp) 517 | } else if let Some(at) = self.matches(Token::LessThanOrEqual)? { 518 | self.binop_expr(at, IntBinOp::LessThanOrEqual, lhs, r_bp) 519 | } else if let Some(at) = self.matches(Token::GreaterThan)? { 520 | self.binop_expr(at, IntBinOp::GreaterThan, lhs, r_bp) 521 | } else if let Some(at) = self.matches(Token::GreaterThanOrEqual)? { 522 | self.binop_expr(at, IntBinOp::GreaterThanOrEqual, lhs, r_bp) 523 | } else { 524 | self.expected( 525 | vec![ 526 | Token::Plus, 527 | Token::Minus, 528 | Token::Multiply, 529 | Token::Divide, 530 | Token::EqualEqual, 531 | Token::LessThan, 532 | Token::LessThanOrEqual, 533 | Token::GreaterThan, 534 | Token::GreaterThanOrEqual, 535 | ], 536 | "expr infix", 537 | ) 538 | } 539 | } 540 | 541 | fn expr_inner(&mut self, min_bp: u8) -> Result { 542 | let mut lhs = self.expr_lhs()?; 543 | loop { 544 | if self.matches_eof() { 545 | break; 546 | } 547 | if let Some(l_bp) = self.postfix_bp() { 548 | if l_bp < min_bp { 549 | break; 550 | } 551 | lhs = self.expr_postfix(lhs)?; 552 | continue; 553 | } 554 | if let Some((l_bp, r_bp)) = self.infix_bp() { 555 | if l_bp < min_bp { 556 | break; 557 | } 558 | lhs = self.expr_infix(lhs, r_bp)?; 559 | continue; 560 | } 561 | break; 562 | } 563 | Ok(lhs) 564 | } 565 | 566 | fn prefix_bp(&self) -> Result { 567 | match self.token { 568 | Some(Token::Negate) => Ok(10), 569 | None => Err(Error::UnexpectedEof), 570 | _ => Err(Error::InvalidPrefix(self.token.clone())), 571 | } 572 | } 573 | 574 | // TODO: Not sure how to determine precedence 575 | fn postfix_bp(&self) -> Option { 576 | match self.token { 577 | Some(Token::LParen) => Some(11), 578 | Some(Token::Dot) => Some(13), 579 | Some(Token::Backslash) => Some(12), 580 | Some(Token::QuestionMark) => Some(9), 581 | _ => None, 582 | } 583 | } 584 | 585 | fn infix_bp(&self) -> Option<(u8, u8)> { 586 | match self.token { 587 | Some(Token::EqualEqual) => Some((2, 1)), 588 | Some(Token::Plus) | Some(Token::Minus) => Some((5, 6)), 589 | Some(Token::Multiply) | Some(Token::Divide) => Some((7, 8)), 590 | Some(Token::LessThan) 591 | | Some(Token::LessThanOrEqual) 592 | | Some(Token::GreaterThan) 593 | | Some(Token::GreaterThanOrEqual) => Some((3, 4)), 594 | _ => None, 595 | } 596 | } 597 | } 598 | 599 | #[derive(Default)] 600 | pub struct ForAll { 601 | pub vars: BTreeSet, 602 | pub row_vars: BTreeMap, 603 | } 604 | 605 | // Type 606 | impl<'a> Parser<'a> { 607 | fn forall_ty(&mut self) -> Result { 608 | let mut vars = BTreeSet::new(); 609 | let mut row_vars = BTreeMap::new(); 610 | loop { 611 | if let Some(name) = self.matches_ident()? { 612 | // TODO: Could be better 613 | if name.value.len() == 2 && name.value.starts_with('r') { 614 | row_vars.insert(name.value, Constraints::new()); 615 | } else if name.value.len() == 1 { 616 | vars.insert(name.value); 617 | } else { 618 | return Err(Error::InvalidTypeVarName(name)); 619 | } 620 | } else if self.matches(Token::Dot)?.is_some() { 621 | self.expect(Token::LParen, "forall constraints")?; 622 | 'outer: loop { 623 | let row_name = self.expect_ident("forall constraints name")?; 624 | let row_constraints = row_vars 625 | .get_mut(&row_name.value) 626 | .ok_or_else(|| Error::NoRowForConstraints(row_name.clone()))?; 627 | if !row_constraints.is_empty() { 628 | return Err(Error::RowConstraintsAlreadyDefined(row_name)); 629 | } 630 | 'inner: loop { 631 | self.expect(Token::Backslash, "forall constraints label")?; 632 | let label = self.expect_ident("forall constraints label")?; 633 | row_constraints.insert(label.value); 634 | if self.matches(Token::RParen)?.is_some() { 635 | break 'outer; 636 | } else if self.matches(Token::Comma)?.is_some() { 637 | break 'inner; 638 | } 639 | } 640 | } 641 | self.expect(Token::FatArrow, "forall constraints")?; 642 | return Ok(ForAll { vars, row_vars }); 643 | } else if self.matches(Token::FatArrow)?.is_some() { 644 | return Ok(ForAll { vars, row_vars }); 645 | } else { 646 | return self.expected( 647 | vec![Token::empty_ident(), Token::Dot, Token::FatArrow], 648 | "forall", 649 | ); 650 | } 651 | } 652 | } 653 | 654 | pub fn ty(source: &str) -> Result<(ForAll, Type)> { 655 | let lexer = Token::lexer(source); 656 | let mut parser = Parser { 657 | is_repl: false, 658 | lexer, 659 | token: None, 660 | }; 661 | parser.advance()?; 662 | 663 | let forall = if parser.matches(Token::ForAll)?.is_some() { 664 | parser.forall_ty()? 665 | } else { 666 | ForAll::default() 667 | }; 668 | 669 | let ty = parser.ty_inner()?; 670 | if let Some(token) = parser.token.take() { 671 | return Err(Error::ExpectedEof(token)); 672 | } 673 | Ok((forall, ty)) 674 | } 675 | 676 | fn ty_inner(&mut self) -> Result { 677 | let mut ty = if self.matches(Token::LParen)?.is_some() { 678 | self.paren_ty()? 679 | } else if let Some(name) = self.matches_ident()? { 680 | Type::Const(name.value) 681 | } else if self.matches(Token::LBrace)?.is_some() { 682 | self.record_ty()? 683 | } else if self.matches(Token::LBracket)?.is_some() { 684 | self.variant_ty()? 685 | } else { 686 | return self.expected(vec![Token::LParen, Token::empty_ident()], "ty"); 687 | }; 688 | 689 | if self.matches(Token::LBracket)?.is_some() { 690 | let mut args = Vec::new(); 691 | loop { 692 | let arg = self.ty_inner()?; 693 | args.push(arg); 694 | if self.matches(Token::RBracket)?.is_some() { 695 | break; 696 | } 697 | self.expect(Token::Comma, "ty app")?; 698 | } 699 | ty = Type::App(ty.into(), args); 700 | } 701 | 702 | if self.matches(Token::Arrow)?.is_some() { 703 | let ret = self.ty_inner()?; 704 | ty = Type::Arrow(vec![ty], ret.into()); 705 | } 706 | 707 | Ok(ty) 708 | } 709 | 710 | fn record_ty(&mut self) -> Result { 711 | if self.matches(Token::RBrace)?.is_some() { 712 | return Ok(Type::Record(Type::RowEmpty.into())); 713 | } 714 | let mut labels = BTreeMap::new(); 715 | let mut rest = Type::RowEmpty; 716 | loop { 717 | let label = self.expect_ident("record ty label")?; 718 | if labels.is_empty() && self.matches(Token::RBrace)?.is_some() { 719 | return Ok(Type::Record(Type::Const(label.value).into())); 720 | } 721 | self.expect(Token::Colon, "record ty")?; 722 | let ty = self.ty_inner()?; 723 | if labels.insert(label.value.clone(), ty).is_some() { 724 | return Err(Error::DuplicateLabel(label)); 725 | } 726 | if self.matches(Token::Comma)?.is_some() { 727 | continue; 728 | } else if self.matches(Token::RBrace)?.is_some() { 729 | break; 730 | } else if self.matches(Token::Pipe)?.is_some() { 731 | rest = self.ty_inner()?; 732 | self.expect(Token::RBrace, "record ty rest")?; 733 | break; 734 | } else { 735 | return self.expected(vec![Token::Comma, Token::RBrace, Token::Pipe], "record ty"); 736 | } 737 | } 738 | Ok(Type::Record(Type::RowExtend(labels, rest.into()).into())) 739 | } 740 | 741 | fn variant_ty(&mut self) -> Result { 742 | let mut labels = BTreeMap::new(); 743 | let mut rest = Type::RowEmpty; 744 | loop { 745 | let label = self.expect_ident("variant ty")?; 746 | self.expect(Token::Colon, "variant ty")?; 747 | let ty = self.ty_inner()?; 748 | if labels.insert(label.value.clone(), ty).is_some() { 749 | return Err(Error::DuplicateLabel(label)); 750 | } 751 | if self.matches(Token::Comma)?.is_some() { 752 | continue; 753 | } else if self.matches(Token::RBracket)?.is_some() { 754 | break; 755 | } else if self.matches(Token::Pipe)?.is_some() { 756 | rest = self.ty_inner()?; 757 | self.expect(Token::RBracket, "variant ty")?; 758 | break; 759 | } else { 760 | return self.expected( 761 | vec![Token::Comma, Token::RBracket, Token::Pipe], 762 | "variant ty", 763 | ); 764 | } 765 | } 766 | Ok(Type::Variant(Type::RowExtend(labels, rest.into()).into())) 767 | } 768 | 769 | fn paren_ty(&mut self) -> Result { 770 | let mut args = Vec::new(); 771 | loop { 772 | let arg = self.ty_inner()?; 773 | args.push(arg); 774 | if self.matches(Token::RParen)?.is_some() { 775 | break; 776 | } 777 | self.expect(Token::Comma, "paren ty")?; 778 | } 779 | if args.len() == 1 && !self.check(Token::Arrow) { 780 | return Ok(args.pop().unwrap()); 781 | } 782 | self.expect(Token::Arrow, "paren ty")?; 783 | let ret = self.ty_inner()?; 784 | Ok(Type::Arrow(args, ret.into())) 785 | } 786 | } 787 | --------------------------------------------------------------------------------