├── .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