├── .gitmodules
├── util
├── src
│ ├── lib.rs
│ └── index.rs
└── Cargo.toml
├── rust-toolchain.toml
├── misc
├── error.png
├── example.png
└── call_graph.svg
├── .gitignore
├── lib
├── core.tao
├── std
│ ├── btree.tao
│ ├── mut.tao
│ ├── func.tao
│ ├── io.tao
│ ├── error.tao
│ ├── str.tao
│ ├── container.tao
│ ├── type.tao
│ ├── value.tao
│ ├── map.tao
│ ├── list.tao
│ ├── fmt.tao
│ ├── math.tao
│ └── stream.tao
├── core
│ ├── tail.tao
│ ├── bool.tao
│ └── ops.tao
├── std.tao
├── main.tao
└── parse.tao
├── site
├── deploy-netlify.sh
├── index.html
├── Cargo.toml
└── index.scss
├── cir
├── Cargo.toml
├── README.md
└── src
│ └── lib.rs
├── examples
├── input.tao
├── hello.tao
├── calc.tao
├── polymorphic_effects.tao
├── mutate.tao
├── adventure.tao
├── quickcheck.tao
└── brainfuck.tao
├── syntax
├── examples
│ ├── class.tao
│ ├── demo.tao
│ ├── basic.tao
│ ├── todo.tao
│ ├── test.tao
│ └── sample.tao
├── Cargo.toml
└── src
│ ├── src.rs
│ ├── span.rs
│ ├── lib.rs
│ ├── node.rs
│ └── error.rs
├── test_cases
├── eval.tao
├── obl.tao
├── subtyping.tao
├── calc.tao
├── hello.tao
├── pattern.tao
├── opt.tao
├── io.tao
├── basic.tao
├── debug.tao
├── effect.tao
├── tdark_streams.tao
├── test.tao
├── eff.tao
├── error.tao
├── experiments.tao
├── type-astronomy.tao
├── 99.tao
├── cipher.tao
└── effects.tao
├── Cargo.toml
├── vm
├── Cargo.toml
└── src
│ ├── lib.rs
│ └── code.rs
├── middle
├── src
│ ├── opt
│ │ ├── remove_dead_proc.rs
│ │ ├── commute_branches.rs
│ │ ├── flatten_single_field.rs
│ │ ├── simplify_arithmetic.rs
│ │ ├── remove_identity_branches.rs
│ │ ├── inline.rs
│ │ └── remove_unused_bindings.rs
│ ├── proc.rs
│ ├── lib.rs
│ ├── repr.rs
│ ├── error.rs
│ └── context.rs
└── Cargo.toml
├── compiler
├── tests
│ ├── math.tao
│ ├── records.tao
│ ├── lists.tao
│ └── tester.rs
├── Cargo.toml
└── src
│ ├── error.rs
│ ├── main.rs
│ └── lib.rs
└── analysis
├── Cargo.toml
└── src
├── lib.rs
├── def.rs
├── debug.rs
├── effect.rs
├── reify.rs
└── data.rs
/.gitmodules:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/util/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod index;
2 |
--------------------------------------------------------------------------------
/rust-toolchain.toml:
--------------------------------------------------------------------------------
1 | [toolchain]
2 | channel = "nightly"
3 |
--------------------------------------------------------------------------------
/misc/error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zesterer/tao/HEAD/misc/error.png
--------------------------------------------------------------------------------
/misc/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zesterer/tao/HEAD/misc/example.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | **/*.rs.bk
3 | flamegraph.svg
4 |
5 | perf.*
6 |
7 | /.idea
8 |
--------------------------------------------------------------------------------
/lib/core.tao:
--------------------------------------------------------------------------------
1 | import "core/bool.tao"
2 | import "core/ops.tao"
3 | import "core/tail.tao"
4 |
--------------------------------------------------------------------------------
/lib/std/btree.tao:
--------------------------------------------------------------------------------
1 | #! A binary tree.
2 |
3 | data BTree A =
4 | | Branch (BTree A, BTree A)
5 | \ Leaf A
6 |
--------------------------------------------------------------------------------
/site/deploy-netlify.sh:
--------------------------------------------------------------------------------
1 | brew install trunk
2 | rustup target add wasm32-unknown-unknown
3 | trunk build --release
4 |
--------------------------------------------------------------------------------
/util/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "tao_util"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 |
--------------------------------------------------------------------------------
/cir/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "tao_cir"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | tao_util = { path = "../util" }
8 |
--------------------------------------------------------------------------------
/examples/input.tao:
--------------------------------------------------------------------------------
1 | import "../lib/main.tao"
2 |
3 | def main : io ~ () = do {
4 | print("Enter your name")!;
5 | let name = input!;
6 | print("Hello " ++ name)!;
7 | }
8 |
--------------------------------------------------------------------------------
/syntax/examples/class.tao:
--------------------------------------------------------------------------------
1 | class Zero =
2 | zero : Self
3 |
4 | member Nat of Zero =
5 | zero = 0
6 |
7 | def zero A < Zero = A.zero
8 |
9 | $[main] def main: Nat = zero
10 |
--------------------------------------------------------------------------------
/lib/core/tail.tao:
--------------------------------------------------------------------------------
1 | $[lang(go)]
2 | data Go C, R =
3 | | Next C
4 | \ Done R
5 |
6 | ## Guaranteed to tail call
7 | $[util]
8 | fn go C, R : (C -> Go C R) -> C -> R = f, c =>
9 | @go(f, c)
10 |
11 |
--------------------------------------------------------------------------------
/test_cases/eval.tao:
--------------------------------------------------------------------------------
1 | import "../lib/parse.tao"
2 |
3 | def main : IO () = when "(4 + 5) * 3"->parse(expr_parser) is
4 | | Ok expr => print(expr->eval->display)
5 | \ Err err => print("Failed to parse expression")
6 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = [
3 | "compiler",
4 | "syntax",
5 | "analysis",
6 | "middle",
7 | "cir",
8 | "vm",
9 | "util",
10 | "site",
11 | ]
12 |
13 | [profile.dev]
14 | opt-level = 2
15 |
--------------------------------------------------------------------------------
/test_cases/obl.tao:
--------------------------------------------------------------------------------
1 | import "../lib/std.tao"
2 |
3 | class Biz =
4 | => Bar
5 |
6 | member () of Biz =
7 | => Bar = Nat
8 |
9 | class Foo T
10 |
11 | member () of Foo Self.Bar
12 |
13 | def main = print("Hello, world!")
14 |
--------------------------------------------------------------------------------
/vm/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "tao_vm"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | tao_middle = { path = "../middle" }
8 | hashbrown = "0.11"
9 | im = "15.0"
10 |
11 | [dev-dependencies]
12 | tao_syntax = { path = "../syntax" }
13 | tao_analysis = { path = "../analysis", default-features = false }
14 |
--------------------------------------------------------------------------------
/middle/src/opt/remove_dead_proc.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 |
3 | #[derive(Default)]
4 | pub struct RemoveDeadProc;
5 |
6 | impl Pass for RemoveDeadProc {
7 | fn apply(&mut self, ctx: &mut Context) {
8 | let reachable = ctx.reachable_procs();
9 |
10 | ctx.procs.procs.retain(|proc, _| reachable.contains(proc));
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/syntax/examples/demo.tao:
--------------------------------------------------------------------------------
1 | len =
2 | | [] = 0
3 | | [_ .. xs] = 1 + xs:len
4 |
5 | def len =
6 | | [] => 0
7 | \ [_ .. xs] => 1 + xs:len
8 |
9 | def add =
10 | \ x, y => x + y
11 |
12 | def sum =
13 | | [] => 0
14 | \ [x .. xs] => x + xs:sum
15 |
16 | def fold =
17 | | init, _, [] => init
18 | \ init, f, [x .. xs] => f(x, xs:fold(init, f))
19 |
--------------------------------------------------------------------------------
/vm/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod code;
2 | pub mod exec;
3 | pub mod lower;
4 |
5 | pub use crate::{
6 | code::{Instr, Program, Addr},
7 | exec::{exec, Value, Env},
8 | };
9 | use tao_middle::{
10 | mir,
11 | Context as MirContext,
12 | MirNode,
13 | ProcId,
14 | repr,
15 | Ident,
16 | EffectId,
17 | };
18 | use hashbrown::HashMap;
19 |
--------------------------------------------------------------------------------
/lib/std.tao:
--------------------------------------------------------------------------------
1 | import "core.tao"
2 |
3 | import "std/btree.tao"
4 | import "std/container.tao"
5 | import "std/error.tao"
6 | import "std/fmt.tao"
7 | import "std/func.tao"
8 | import "std/io.tao"
9 | import "std/list.tao"
10 | import "std/map.tao"
11 | import "std/math.tao"
12 | import "std/stream.tao"
13 | import "std/type.tao"
14 | import "std/value.tao"
15 | import "std/mut.tao"
16 |
--------------------------------------------------------------------------------
/examples/hello.tao:
--------------------------------------------------------------------------------
1 | mod main = "../lib/main.tao"
2 |
3 | def main : io ~ () = do {
4 | let list : [?] = range(0, 10)
5 | -> map(fn x => do {
6 | print(x->show)!;
7 | x + 2
8 | })
9 | -> map(fn x => do {
10 | print("Hello!")!;
11 | x + 2
12 | })
13 | -> collect!;
14 | print("Hello, world! " + list)!;
15 | }
16 |
--------------------------------------------------------------------------------
/site/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Tao Playground
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/middle/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "tao_middle"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | tao_analysis = { path = "../analysis", default-features = false }
8 | tao_syntax = { path = "../syntax" }
9 | #ariadne = "0.1"
10 | ariadne = { git = "https://github.com/zesterer/ariadne.git" }
11 | #ariadne = { path = "../../ariadne" }
12 | internment = "0.5"
13 | hashbrown = "0.11"
14 |
15 | [dev-dependencies]
16 | rustyline = "9.0"
17 |
--------------------------------------------------------------------------------
/test_cases/subtyping.tao:
--------------------------------------------------------------------------------
1 | import "../lib/main.tao"
2 |
3 | effect print = Str => ()
4 | effect input = () => Str
5 |
6 | fn write : Str -> print ~ () =
7 | s => @{ @suspend(s) }
8 |
9 | def read : input ~ Str = @{ @suspend(()) }
10 |
11 | effect console = print + input
12 |
13 | def bar : io + console ~ () = @{
14 | print("Hello")!;
15 | write("World")!;
16 | write("World")!;
17 | read!;
18 | }
19 |
20 | def main : io ~ () =
21 | print("Hello, world!")
22 |
--------------------------------------------------------------------------------
/examples/calc.tao:
--------------------------------------------------------------------------------
1 | import "../lib/main.tao"
2 | import "../lib/parse.tao"
3 |
4 | def prompt : io ~ () = when input! is
5 | | "q" => ()
6 | \ line => do {
7 | when line->do_parse(expr_parser) is
8 | | Ok expr => print(expr->eval->show)!
9 | \ Err err => print("Failed to parse '" ++ line ++ "': " ++ err->show)!;
10 | prompt!;
11 | }
12 |
13 | def main : io ~ () = do {
14 | print("Enter an expression (q to quit)")!;
15 | prompt!;
16 | }
17 |
--------------------------------------------------------------------------------
/test_cases/calc.tao:
--------------------------------------------------------------------------------
1 | import "../lib/parse.tao"
2 |
3 | def main : IO () = while(do {
4 | print("Enter an expression to evaluate, q to quit");
5 | let text <- input;
6 | if text = "q" then do {
7 | print("Ok, goodbye");
8 | return False
9 | } else do {
10 | when text->parse(expr_parser) is
11 | | Ok expr => print(expr->eval->display)
12 | \ Err err => print("Failed to parse expression '" ++ text ++ "'")
13 | ;
14 | return True
15 | }
16 | })
17 |
--------------------------------------------------------------------------------
/lib/core/bool.tao:
--------------------------------------------------------------------------------
1 | import "ops.tao"
2 |
3 | $[lang(bool)]
4 | data Bool =
5 | | False
6 | \ True
7 |
8 | member Bool of Not =
9 | => Output = Self
10 | => not = fn
11 | | True => False
12 | \ False => True
13 |
14 | member Bool of And Bool =
15 | => Output = Bool
16 | => and_ = fn
17 | | True, True => True
18 | \ _, _ => False
19 |
20 | member Bool of Or Bool =
21 | => Output = Bool
22 | => or_ = fn
23 | | False, False => False
24 | \ _, _ => True
25 |
--------------------------------------------------------------------------------
/lib/main.tao:
--------------------------------------------------------------------------------
1 | import "std.tao"
2 |
3 | $[entry, util]
4 | fn __start : @ -> (@, ()) = uni =>
5 | let main : io ~ () = main in
6 | let ((), uni) = (main, uni) handle
7 | | print with s, uni =>
8 | let (uni, ()) = __print(s, uni) in
9 | ((), uni)
10 | | input with (), uni =>
11 | let (uni, s) = __input(uni) in
12 | (s, uni)
13 | \ rand with max, uni =>
14 | let (uni, x) = __rand(max, uni) in
15 | (x, uni)
16 | in (uni, ())
17 |
--------------------------------------------------------------------------------
/syntax/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "tao_syntax"
3 | version = "0.1.0"
4 | edition = "2018"
5 |
6 | [dependencies]
7 | #chumsky = { path = "../../chumsky", features = ["nightly"] }
8 | chumsky = { git = "https://github.com/zesterer/chumsky.git", default-features = false, features = ["std"] }
9 | #chumsky = "0.8.0"
10 | internment = "0.5"
11 | #ariadne = "0.1"
12 | ariadne = { git = "https://github.com/zesterer/ariadne.git" }
13 | #ariadne = { path = "../../ariadne" }
14 | hashbrown = "0.11"
15 |
16 | [dev-dependencies]
17 | rustyline = "9.0"
18 |
--------------------------------------------------------------------------------
/lib/std/mut.tao:
--------------------------------------------------------------------------------
1 | #! Utilities for handling mutation via effects.
2 |
3 | effect mut A = (A -> A) => A
4 |
5 | $[util]
6 | fn mut A : (A -> A) -> mut A ~ () =
7 | f => do { @suspend(f); }
8 |
9 | $[util]
10 | fn set A : A -> mut A ~ () =
11 | x => mut(fn _ => x)!
12 |
13 | $[util]
14 | def get A : mut A ~ A = @suspend(fn x => x)
15 |
16 | $[util]
17 | fn apply A : mut A ~ () -> A -> A =
18 | # TODO: Why is this hint necessary?
19 | m : mut A ~ (), x => let (_, x) = (m, x)
20 | handle mut A with f, x => let x = x -> f in (x, x)
21 | in x
22 |
--------------------------------------------------------------------------------
/compiler/tests/math.tao:
--------------------------------------------------------------------------------
1 | # >>>> INPUT
2 |
3 | import "../../lib/std.tao"
4 |
5 | $[main]
6 | def main = 5
7 |
8 | # >>>> OUTPUT
9 |
10 | 5i
11 |
12 | # >>>> INPUT
13 |
14 | import "../../lib/std.tao"
15 |
16 | $[main]
17 | def main =
18 | let five = 5 in
19 | let four = 4 in
20 | five + four
21 |
22 | # >>>> OUTPUT
23 |
24 | 9i
25 |
26 | # >>>> INPUT
27 |
28 | import "../../lib/std.tao"
29 |
30 | fn factorial =
31 | | 0 => 1
32 | \ y ~ x + 1 => y * factorial(x)
33 |
34 | $[main]
35 | def main = factorial(10)
36 |
37 | # >>>> OUTPUT
38 |
39 | 3628800i
40 |
--------------------------------------------------------------------------------
/test_cases/hello.tao:
--------------------------------------------------------------------------------
1 | import "../lib/main.tao"
2 |
3 | effect read = () => Str
4 | effect write = Str => ()
5 |
6 | fn write : Str -> write ~ () = s => @{ @suspend(s) }
7 | def read : read ~ Str = @{ @suspend(()) }
8 |
9 | effect console = read + write
10 |
11 | def say_hi : console ~ () = @{
12 | let name = read!;
13 | write("Hello, " ++ name)!;
14 | }
15 |
16 | def greeting = (
17 | (say_hi, "") handle
18 | | read with (), buf => ("bob", buf)
19 | \ write with s, buf => ((), buf ++ s)
20 | ).1
21 |
22 | def main : io ~ () =
23 | print("Hello, world!" ++ greeting)
24 |
--------------------------------------------------------------------------------
/test_cases/pattern.tao:
--------------------------------------------------------------------------------
1 | import "../lib/main.tao"
2 |
3 | data Value U =
4 | | Unknown U
5 | | Nat Nat
6 | \ List [Self]
7 |
8 | type KnownValue = Value Never
9 | type MaybeValue = Value ()
10 |
11 | member Value Never of Show =
12 | => show = fn
13 | | Nat x => x->show
14 | \ List xs => "[" ++ xs->show ++ "]"
15 |
16 | for A member Value A of Show =
17 | => show = fn
18 | | Unknown _ => "unknown"
19 | | Nat x => x->show
20 | \ List xs => "[" ++ xs->show ++ "]"
21 |
22 | def main : io ~ () =
23 | print((List [Nat 4, Nat 3]) -> show)
24 |
--------------------------------------------------------------------------------
/analysis/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "tao_analysis"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | tao_util = { path = "../util" }
8 | tao_syntax = { path = "../syntax" }
9 | #ariadne = "0.1"
10 | ariadne = { git = "https://github.com/zesterer/ariadne.git" }
11 | #ariadne = { path = "../../ariadne" }
12 | hashbrown = "0.11"
13 | internment = "0.5"
14 | ranges = { git = "https://gitlab.com/bit-refined/ranges.git" }
15 | dot = { version = "0.1", optional = true }
16 |
17 | [features]
18 | debug = ["dot"]
19 | default = ["debug"]
20 |
21 | [dev-dependencies]
22 | rustyline = "9.0"
23 |
--------------------------------------------------------------------------------
/test_cases/opt.tao:
--------------------------------------------------------------------------------
1 | import "../lib/std.tao"
2 |
3 | fn factorial_go = n => go(
4 | fn
5 | | (0, a) => Done a
6 | \ (m ~ n + 1, a) => Next (n, m * a)
7 | ,
8 | (n, 1),
9 | )
10 |
11 | fn factorial =
12 | | 0 => 1
13 | \ m ~ n + 1 => m * factorial(n)
14 |
15 | def factorial_fix = fix(fn
16 | | rec, 0 => 1
17 | \ rec, m ~ n + 1 => m * rec(n))
18 |
19 | #def main = factorial_go(10)
20 |
21 | data Three A, B, C =
22 | | One A
23 | | Two B
24 | \ Three C
25 |
26 | def main = fn x =>
27 | when if x then One 5 else Two 3 is
28 | | One x => x
29 | | Two x => x
30 | \ Three x => x
31 |
--------------------------------------------------------------------------------
/compiler/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "tao"
3 | version = "0.1.0"
4 | edition = "2021"
5 | default-run = "tao"
6 |
7 | [dependencies]
8 | tao_syntax = { path = "../syntax" }
9 | tao_analysis = { path = "../analysis", default-features = false }
10 | tao_middle = { path = "../middle" }
11 | tao_cir = { path = "../cir" }
12 | tao_vm = { path = "../vm" }
13 | internment = "0.5"
14 | rand = { version = "0.8", optional = true }
15 |
16 | #ariadne = "0.1"
17 | ariadne = { git = "https://github.com/zesterer/ariadne.git" }
18 | #ariadne = { path = "../../ariadne" }
19 | structopt = "0.3"
20 |
21 | [features]
22 | debug = ["tao_analysis/debug"]
23 | default = ["rand", "debug"]
24 |
--------------------------------------------------------------------------------
/lib/std/func.tao:
--------------------------------------------------------------------------------
1 | #! Utilities for programming with functions.
2 |
3 | ## Create an recursive function inline using the fix combinator.
4 | ##
5 | ## ```
6 | ## let factorial = fix(fn rec => fn
7 | ## | 0 => 1
8 | ## \ m ~ n + 1 => m * rec(n))
9 | ## in
10 | ## factorial(10)
11 | ## ```
12 | $[util]
13 | fn fix A, B, e : ((A -> e ~ B) -> A -> e ~ B) -> A -> e ~ B =
14 | \ f, n => f(f->fix, n)!
15 |
16 | ## A functor with no environment.
17 | class FnZero I =
18 | => O
19 | => call : I -> Self.O
20 |
21 | ## A functor.
22 | class Fn I =
23 | => O
24 | => call : I -> Self -> Self.O
25 |
26 | for I, O member I -> O of Fn I =
27 | => O = O
28 | => call = fn i, f => f(i)
29 |
--------------------------------------------------------------------------------
/test_cases/io.tao:
--------------------------------------------------------------------------------
1 | import "../lib/parse.tao"
2 |
3 | def main : IO () = do {
4 | ["a", "b", "c", "d"]
5 | -> for_each(fn x => do {
6 | print("Element = " ++ x);
7 | });
8 |
9 | while(do {
10 | print("Enter an expression to evaluate, q to quit");
11 | let text <- input;
12 | if text = "q" then do {
13 | print("Ok, goodbye");
14 | return False
15 | } else do {
16 | when text->parse(expr_parser) is
17 | | Ok expr => print(expr->eval->display)
18 | \ Err err => print("Failed to parse expression '" ++ text ++ "'")
19 | ;
20 | return True
21 | }
22 | })
23 | }
24 |
--------------------------------------------------------------------------------
/test_cases/basic.tao:
--------------------------------------------------------------------------------
1 | import "../lib/core.tao"
2 | import "../lib/std/io.tao"
3 |
4 | $[entry, util]
5 | fn __start : @ -> (@, ()) = uni =>
6 | let main : io ~ () = main in
7 | let ((), uni) = (main, uni) handle
8 | | print with s, uni =>
9 | let (uni, ()) = __print(s, uni) in
10 | ((), uni)
11 | | input with (), uni =>
12 | let (uni, s) = __input(uni) in
13 | (s, uni)
14 | \ rand with max, uni =>
15 | let (uni, x) = __rand(max, uni) in
16 | (x, uni)
17 | in (uni, ())
18 |
19 | def bool : rand ~ [Char] = when rand_bool! is
20 | | True => "true"
21 | \ False => "false"
22 |
23 | def main : io ~ () = do {
24 | print("Hello, world!" ++ bool!)!;
25 | }
26 |
--------------------------------------------------------------------------------
/lib/std/io.tao:
--------------------------------------------------------------------------------
1 | type IO A = @ -> (@, A)
2 |
3 | effect pure = ()
4 | effect input = () => [Char]
5 | effect print = [Char] => ()
6 | effect rand = Nat => Nat
7 | effect io = input + print + rand
8 |
9 | # Core monadic IO operations
10 |
11 | $[util]
12 | fn __print : [Char] -> IO () = s, uni =>
13 | let uni = @print(uni, s) in
14 | (uni, ())
15 |
16 | $[util]
17 | fn __input : IO [Char] = uni => @input(uni)
18 |
19 | $[util]
20 | fn __rand : Nat -> IO Nat = n, uni => @rand(uni, n)
21 |
22 | # High-level effect IO operations
23 |
24 | $[util]
25 | def input : input ~ [Char] = @suspend(())
26 |
27 | $[util]
28 | fn print : [Char] -> print ~ () = s => @suspend(s)
29 |
30 | $[util]
31 | fn rand : Nat -> rand ~ Nat = max => @suspend(max)
32 |
33 | $[util]
34 | def rand_bool : rand ~ Bool = rand(2)! = 0
35 |
--------------------------------------------------------------------------------
/examples/polymorphic_effects.tao:
--------------------------------------------------------------------------------
1 | import "../lib/main.tao"
2 |
3 | ## A function that maps elements of a list using a function.
4 | ## The mapping function, `f`, may have a side-effect, `e`.
5 | ## This side-effect is propagated to the returned list
6 | fn emap A, B, e : (A -> e ~ B) -> [A] -> e ~ [B] =
7 | | _, [] => []
8 | \ f, [x .. xs] => [x->f! .. xs->emap(f)!]
9 |
10 | def main : io ~ () = do {
11 | ## Square every number in a list (i.e: no side effect)
12 | let x = [1, 2, 3]->emap(fn x => x * x);
13 | print("Squared list = " + x)!;
14 |
15 | ## Print every number in a list and double it as we do so
16 | let y = [1, 2, 3]
17 | -> emap(fn x => do {
18 | print(x->show)!; ## This is effectful!
19 | x + x
20 | })!;
21 | print("Doubled list = " + y)!;
22 | }
23 |
--------------------------------------------------------------------------------
/cir/README.md:
--------------------------------------------------------------------------------
1 | # Common Intermediate Representation
2 |
3 | Intended as the main intermediate representation upon which most optimisations occur.
4 |
5 | The general idea:
6 |
7 | - CFG composed of basic blocks
8 | - Every block finishes with a branch (specifically, a match expression)
9 | - Every block declares its dependencies and passes them on to branch targets
10 | - Each statement is three-address code
11 |
12 | ## Example lowering
13 |
14 | ```
15 | foo(!a, if -b + c > 0 then bar else baz)
16 | ```
17 |
18 | becomes
19 |
20 | ```
21 | b0:
22 | let v0 = !a in
23 | let v1 = foo(v0) in
24 | let v2 = -b in
25 | let v3 = v2 + c in
26 | match v3 > 0 in
27 | | True => :b0
28 | \ False => :b1
29 | b1:
30 | let v4 = v1(bar) in
31 | v4
32 | b2:
33 | let v4 = v1(baz) in
34 | v4
35 | ```
36 |
--------------------------------------------------------------------------------
/site/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "site"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | tao = { path = "../compiler", default-features = false, features = ["debug"] }
8 | tao_vm = { path = "../vm" }
9 |
10 | yew = { version = "0.20.0", features = ["csr"] }
11 | gloo = "0.8"
12 | js-sys = "0.3"
13 | wasm-bindgen = "0.2"
14 | web-sys = { version = "0.3", features = ["Event","EventTarget","InputEvent", "HtmlSelectElement", "HtmlIFrameElement", "CssStyleDeclaration"] }
15 | include_dir = "0.7"
16 | rand = { version = "0.8", default-features = false, features = ["small_rng"] }
17 | yew-ansi = { git = "https://github.com/zesterer/yew-ansi.git" }
18 | urlencoding = "2.1"
19 |
20 | # `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
21 | # compared to the default allocator's ~10K. It is slower than the default
22 | # allocator, however.
23 | #
24 | # Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now.
25 | wee_alloc = "0.4.5"
26 |
--------------------------------------------------------------------------------
/lib/std/error.tao:
--------------------------------------------------------------------------------
1 | import "value.tao"
2 | import "fmt.tao"
3 |
4 | data Result A, E =
5 | | Ok A
6 | \ Err E
7 |
8 | data Never =|
9 |
10 | # TODO: Reenable when coherence checker is cleverer
11 | # for A member Result A Never of From A =
12 | # => from = fn x => Ok x
13 |
14 | # TODO: Reenable when coherence checker is cleverer
15 | # for A member A of From (Result A Never) =
16 | # => from = fn Ok x => x
17 |
18 | for A < Show, E < Show member Result A E of Show =
19 | => show = fn
20 | | Ok x => x->show
21 | \ Err e => "error: " ++ e->show
22 |
23 | for A < Debug, E < Debug member Result A E of Debug =
24 | => debug = fn
25 | | Ok x => "Ok " ++ x->debug
26 | \ Err e => "Err " ++ e->debug
27 |
28 | for A, B, E, e member Result A E of Map A B e =
29 | => Output = e ~ Result B E
30 | => map = fn
31 | | f, Ok a => Ok a->f!
32 | \ _, Err e => Err e
33 |
34 | $[util]
35 | fn map_err A, E, F : (E -> F) -> Result A E -> Result A F =
36 | | _, Ok x => Ok x
37 | \ f, Err e => Err e->f
38 |
--------------------------------------------------------------------------------
/examples/mutate.tao:
--------------------------------------------------------------------------------
1 | import "../lib/main.tao"
2 |
3 | ## Mutations take a function that maps a value to another of the same type
4 | ## They don't output anything (unlike an effect such as 'get some user input')
5 | effect mutate A = (A -> A) => ()
6 |
7 | ## TODO: Have the compiler auto-generate this bit instead of
8 | ## forcing the effect user to deal with intrinsics.
9 | fn mutate A : (A -> A) -> mutate A ~ () = f => @suspend(f)
10 |
11 | ## Apply a series of mutations to a value
12 | fn apply_mutation A : mutate A ~ () -> A -> A =
13 | m, x => let (_, x) = (m, x)
14 | handle mutate A with f, x => ((), x -> f)
15 | in x
16 |
17 | ## A mutation that pushes an element to the end of a list
18 | fn push A : A -> mutate [A] ~ () =
19 | x => mutate(fn xs => xs ++ [x])!
20 |
21 | ## Example usage
22 |
23 | def numbers = do {
24 | push(1)!;
25 | push(2)!;
26 | push(3)!;
27 | push(4)!;
28 | }
29 |
30 | ## Debug-prints '[1, 2, 3, 4]' to the console
31 | def main : io ~ () = []
32 | -> apply_mutation(numbers)
33 | -> debug
34 | -> print!
35 |
--------------------------------------------------------------------------------
/middle/src/proc.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 |
3 | pub type ProcId = ConProcId;
4 |
5 | pub struct Proc {
6 | pub body: mir::MirNode,
7 | pub is_recursive: bool,
8 | }
9 |
10 | #[derive(Default)]
11 | pub struct Procs {
12 | pub procs: BTreeMap,
13 | }
14 |
15 | impl Procs {
16 | pub fn id_of_con(def: ConProcId) -> ProcId {
17 | def
18 | }
19 |
20 | pub fn get(&self, id: ProcId) -> Option<&Proc> {
21 | self.procs.get(&id)
22 | }
23 |
24 | pub fn get_mut(&mut self, id: ProcId) -> Option<&mut Proc> {
25 | self.procs.get_mut(&id)
26 | }
27 |
28 | pub fn iter_mut(&mut self) -> impl Iterator- {
29 | self.procs.iter_mut().map(|(id, proc)| (*id, proc))
30 | }
31 |
32 | pub fn iter(&self) -> impl Iterator
- {
33 | self.procs.iter().map(|(id, proc)| (*id, proc))
34 | }
35 |
36 | pub fn insert(&mut self, id: ProcId, proc: Proc) {
37 | assert!(self.procs.insert(id, proc).is_none(), "Proc already inserted");
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/std/str.tao:
--------------------------------------------------------------------------------
1 | import "../core/ops.tao"
2 | import "fmt.tao"
3 |
4 | #! Utilities for programming with strings.
5 |
6 | type Str = [Char]
7 |
8 | for B < Show member [Char] of Add B =
9 | => Output = Str
10 | => add = fn s, b => s ++ b->show
11 |
12 | $[util]
13 | fn codepoint : Char -> Nat = c => @codepoint_char(c)
14 |
15 | class FromStr =
16 | => Err
17 | => from_str : Str -> Result Self Self.Err
18 |
19 | $[util]
20 | def parse A < FromStr : Str -> Result A A.Err = A.from_str
21 |
22 | data NatParseError =
23 | | MissingDigits
24 | \ NotADigit
25 |
26 | member NatParseError of Show =
27 | => show = fn
28 | | MissingDigits => "missing digits"
29 | \ NotADigit => "not a digit"
30 |
31 | member Nat of FromStr =
32 | => Err = NatParseError
33 | => from_str = fn
34 | | [] => Err MissingDigits
35 | \ s => s
36 | -> map(fn c => "0123456789"->find_index(c))
37 | -> fold(Ok 0, fn
38 | | Ok a, Just x => Ok a * 10 + x
39 | | Err e, _ => Err e
40 | \ _, _ => Err NotADigit)
41 |
--------------------------------------------------------------------------------
/syntax/examples/basic.tao:
--------------------------------------------------------------------------------
1 | type Str = [Char]
2 |
3 | def foo = fn x: Str => x
4 |
5 | data Maybe A =
6 | | Just A
7 | | None
8 |
9 | type Option A = Maybe A
10 |
11 | #type Fizz = Buzz
12 | #type Buzz = Fizz
13 |
14 | def make_bool A = fn x: A => x
15 | def five = make_bool(5)
16 |
17 | def a = let a: Nat = a in a
18 |
19 | def bar = fn
20 | | Just x: Option ? => x
21 | | Just 5 => 5
22 |
23 | # def factorial: ? -> ? = fn
24 | # | 0 => 1
25 | # | x => x * factorial(x - 1)
26 |
27 | def life = if True
28 | then 40 + 1 + 1
29 | else life
30 |
31 | def three = let two = 2 in
32 | let one = 1 in
33 | two + one
34 |
35 | def seven =
36 | let x = three in
37 | let y = 2 in
38 | x + y + 2
39 |
40 | def test =
41 | let seven = seven in
42 | when False is
43 | | True => 5
44 | | False => seven
45 |
46 | def main2 = when let x = life in life + 2, False is
47 | | 44, False => "Hello, world!"
48 | | _, _ => "something else"
49 |
50 | def main3 =
51 | let x = life + 10 in
52 | let y = life in
53 | x + y
54 |
55 | def main = when False, 40 + 1 is
56 | | False, 42 => 1
57 | | _, _ => 0
58 |
--------------------------------------------------------------------------------
/lib/std/container.tao:
--------------------------------------------------------------------------------
1 | #! Utilities for manipulating and traversing containers.
2 |
3 | import "math.tao"
4 |
5 | class Map A, B, e =
6 | => Output
7 | => map : (A -> e ~ B) -> Self -> Self.Output
8 |
9 | for A, B, e member Maybe A of Map A B e =
10 | => Output = e ~ Maybe B
11 | => map = fn
12 | | _, None => None
13 | \ f, Just a => Just a->f!
14 |
15 | $[util]
16 | def map V < Map A B e, A, B, e : (A -> e ~ B) -> V -> V.Output = V.map
17 |
18 | $[util]
19 | fn sfold S < Stream A e, A, B, e : B -> (B -> A -> e ~ B) -> S -> e ~ B =
20 | b, f, s => when s->S.next is
21 | | None => b
22 | \ Just (x, s) => sfold(f(b, x!)!, f, s)!
23 |
24 | $[util]
25 | fn fold S < ToStream A e, A, B, e : B -> (B -> A -> e ~ B) -> S -> e ~ B =
26 | b, f, s => s->S.to_stream->sfold(b, f)!
27 |
28 | $[util]
29 | def sum S, A, e : S -> e ~ A where
30 | S < ToStream A e,
31 | A < Zero + Add A with { Output = A },
32 | = fold(A.zero, A.add)
33 |
34 | $[util]
35 | def product S, A, e : S -> e ~ A where
36 | S < ToStream A e,
37 | A < One + Mul A with { Output = A },
38 | = fold(A.one, A.mul)
39 |
--------------------------------------------------------------------------------
/middle/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![feature(arbitrary_self_types, cell_update, never_type, let_chains)]
2 |
3 | pub mod error;
4 | pub mod proc;
5 | pub mod mir;
6 | pub mod opt;
7 | pub mod repr;
8 | pub mod lower;
9 | pub mod context;
10 |
11 | pub use crate::{
12 | error::Error,
13 | opt::Pass,
14 | proc::{ProcId, Proc, Procs},
15 | mir::{MirNode, Pat, Binding, Expr, Handler, Literal, Partial, Intrinsic, Local},
16 | repr::{Repr, Reprs, Prim, Data},
17 | context::{Context, OptMode},
18 | lower::Lowerer,
19 | };
20 | pub use tao_analysis::Ident;
21 |
22 | use tao_syntax::{
23 | Node,
24 | Span,
25 | SrcId,
26 | SrcNode,
27 | ast,
28 | };
29 | use tao_analysis::{
30 | hir,
31 | TyNode,
32 | ty,
33 | DefId,
34 | Context as HirContext,
35 | data::DataId,
36 | ConProc,
37 | ConProcId,
38 | ConContext,
39 | ConExpr,
40 | ConBinding,
41 | ConTy,
42 | ConTyId,
43 | ConDataId,
44 | ConEffectId,
45 | };
46 | use hashbrown::{HashMap, HashSet};
47 | use internment::Intern;
48 | use std::collections::{BTreeMap, BTreeSet};
49 |
50 | pub type EffectId = ConEffectId;
51 |
--------------------------------------------------------------------------------
/lib/std/type.tao:
--------------------------------------------------------------------------------
1 | #! Utilities for type-level programming.
2 |
3 | import "str.tao"
4 |
5 | class Any =
6 | => type_name : Str
7 |
8 | for A member A of Any =
9 | => type_name =
10 | let xs : [A] = [] in
11 | @type_name(xs)
12 |
13 | $[util]
14 | fn type_name_of A < Any =
15 | _ : A => A.type_name
16 |
17 | ## Apply one of two functions to a value depending on the type of the value.
18 | ##
19 | ## This can be used to specialise the implementation of code depending on the type of an
20 | ## expression.
21 | $[util]
22 | fn dispatch A, B, C : A -> (B -> C) -> (A -> C) -> C =
23 | x, special, fallback => dispatch_map(
24 | x,
25 | fn x => (x, x -> special),
26 | fn x => (x, x -> fallback),
27 | ).1
28 |
29 | ## Apply one of two functions to a value depending on the type of the value, mapping the value
30 | ## in the process.
31 | ##
32 | ## This can be used to specialise the implementation of code depending on the type of an
33 | ## expression.
34 | $[util]
35 | fn dispatch_map A, B, C : A -> (B -> (B, C)) -> (A -> (A, C)) -> (A, C) =
36 | x, special, fallback => @dispatch(x, special, fallback)
37 |
--------------------------------------------------------------------------------
/test_cases/debug.tao:
--------------------------------------------------------------------------------
1 | import "../lib/std.tao"
2 |
3 | data Natural =
4 | | Zero
5 | \ Succ Natural
6 |
7 | member Natural of Eq =
8 | => eq = fn
9 | | Zero, Zero => True
10 | | Succ x, Succ y => x = y
11 | \ _, _ => False
12 | => ne = fn x, y => !Self.eq(x, y)
13 |
14 | fn show_negative A : A -> Str where
15 | A < Neg,
16 | A.Output < Neg,
17 | A.Output.Output < Neg,
18 | A.Output.Output.Output < Neg,
19 | A.Output.Output.Output.Output < Show,
20 | =
21 | a => (----a)->show
22 |
23 | def s = "The meaning of life is " + -42
24 |
25 | def main = [1, 2, 3, 4]
26 | -> map(fn x => x + 3)
27 | -> map(fn x => x + x)
28 | -> map(fn x => x * x)
29 | -> rev
30 | -> sort
31 | -> sum
32 |
33 | data Out A, F = A
34 | fn map2 F < FnZero A with { O = B }, A, B : [A] -> [Out B F] =
35 | | [] => []
36 | \ [x .. xs] => [Out F.call(x) .. xs->map2]
37 | data Double
38 | for A < Add A with { Output = A } member Double of FnZero A =
39 | => O = A
40 | => call = fn x => x + x
41 | def forced_monomorphisation : [Out Nat Double] = [1, 2, 3, 4]->map2
42 |
43 | # def main : IO () =
44 | # print("hello")
45 |
--------------------------------------------------------------------------------
/syntax/src/src.rs:
--------------------------------------------------------------------------------
1 | use internment::Intern;
2 | use std::{
3 | path::{Path, PathBuf},
4 | fmt,
5 | };
6 |
7 | #[derive(Copy, Clone, PartialEq, Eq, Hash)]
8 | pub struct SrcId(Intern>);
9 |
10 | impl fmt::Display for SrcId {
11 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
12 | if self.0.len() == 0 {
13 | write!(f, "?")
14 | } else {
15 | write!(f, "{}", self.0.clone().join("/"))
16 | }
17 | }
18 | }
19 |
20 | impl fmt::Debug for SrcId {
21 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self) }
22 | }
23 |
24 | impl SrcId {
25 | #[cfg(test)]
26 | pub fn empty() -> Self {
27 | SrcId(Intern::new(Vec::new()))
28 | }
29 |
30 | pub fn repl() -> Self {
31 | SrcId(Intern::new(vec!["repl".to_string()]))
32 | }
33 |
34 | pub fn from_path>(path: P) -> Self {
35 | SrcId(Intern::new(path
36 | .as_ref()
37 | .iter()
38 | .map(|c| c.to_string_lossy().into_owned())
39 | .collect()))
40 | }
41 |
42 | pub fn to_path(&self) -> PathBuf {
43 | self.0.iter().map(|e| e.to_string()).collect()
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/test_cases/effect.tao:
--------------------------------------------------------------------------------
1 | import "../lib/main.tao"
2 |
3 | fn yield A : A -> yield A ~ () =
4 | x => @{ @suspend(x) }
5 | fn do_print : Str -> print ~ () =
6 | s => @{ @suspend(s) }
7 |
8 | effect yield A = A => ()
9 |
10 | effect print = Str => ()
11 |
12 | # Counts the natural numbers, yielding each one to the caller
13 | fn count_range : Nat -> Nat -> yield Nat ~ () =
14 | n, max => @{
15 | if n < max
16 | then @{
17 | yield(n)!;
18 | count_range(n + 1, max)!;
19 | }!
20 | else ()
21 | }
22 |
23 | # Prints the natural numbers to the console
24 | def print_nats : print ~ () = @{
25 | count_range(0, 10)
26 | handle yield Nat with n => do_print("Next nat: " + n->show)!
27 | }
28 |
29 | effect get_name = () => Str
30 |
31 | # This will be implicit later
32 | def get_name : get_name ~ Str = @{ @suspend(()) }
33 |
34 | def hello : get_name ~ Str = @{
35 | let name = get_name! in
36 | "Hello, " ++ name ++ "!"
37 | }
38 |
39 | fn hello_to : Str -> Str =
40 | name => hello
41 | handle get_name with () => name
42 |
43 | def hello_to_you = hello_to("you")
44 |
45 | def main : io ~ () = @{
46 | print_nats
47 | handle print with s => print(s)!
48 | }
49 |
--------------------------------------------------------------------------------
/test_cases/tdark_streams.tao:
--------------------------------------------------------------------------------
1 | import "../lib/main.tao"
2 |
3 | class ShapeTag
4 |
5 | class GathersInto S where
6 | Self.Output < FromStream Self
7 | =
8 | => Output
9 |
10 | class ShapedStream where
11 | Self < Stream,
12 | Self.Shape < ShapeTag,
13 | Self.Item < GathersInto Self.Shape,
14 | =
15 | => Shape
16 | => gather : Self -> Self.Item.Output
17 |
18 | data ListShape
19 |
20 | member ListShape of ShapeTag
21 |
22 | for A member A of GathersInto ListShape =
23 | => Output = [A]
24 |
25 | for A member [A] of ShapedStream =
26 | => Shape = ListShape
27 | => gather = fn xs => xs
28 |
29 | data MaybeShape
30 |
31 | member MaybeShape of ShapeTag
32 |
33 | for A member A of GathersInto MaybeShape =
34 | => Output = Maybe A
35 |
36 | for A member Maybe A of ShapedStream =
37 | => Shape = MaybeShape
38 | => gather = fn xs => xs
39 |
40 | for S, B member Mapped S B of ShapedStream where
41 | S < Stream + ShapedStream,
42 | B < GathersInto S.Shape,
43 | =
44 | => Shape = S.Shape
45 | => gather = fn s => s->collect
46 |
47 | def gather S : S -> S.Item.Output where
48 | S < Stream + ShapedStream,
49 | = S.gather
50 |
51 | def main : io ~ () = [1, 2, 3]
52 | -> smap(fn x => x + 1)
53 | -> smap(fn x => x * x)
54 | -> gather
55 | -> show
56 | -> print!
57 |
--------------------------------------------------------------------------------
/compiler/tests/records.tao:
--------------------------------------------------------------------------------
1 | # >>>> INPUT
2 |
3 | import "../../lib/std.tao"
4 |
5 | $[main]
6 | def main = {}
7 |
8 | # >>>> OUTPUT
9 |
10 | []
11 |
12 | # >>>> INPUT
13 |
14 | import "../../lib/std.tao"
15 |
16 | $[main]
17 | def main = { b: 6, a: True, c: () }
18 |
19 | # >>>> OUTPUT
20 |
21 | [#1 [], 6i, []]
22 |
23 | # >>>> INPUT
24 |
25 | import "../../lib/std.tao"
26 |
27 | $[main]
28 | def main =
29 | let r = { a: True, b: 5 } in
30 | if r.a
31 | then r.b
32 | else 0
33 |
34 | # >>>> OUTPUT
35 |
36 | 5i
37 |
38 | # >>>> INPUT
39 |
40 | import "../../lib/std.tao"
41 |
42 | fn fold2 A, B : A -> (A -> B -> A) -> [B] -> A =
43 | | init, _, [] => init
44 | \ init, f, [x .. tail] => fold2(f(init, x), f, tail)
45 |
46 | # Emulated typeclass
47 | data Add A = {
48 | add: A -> A -> A
49 | }
50 |
51 | # Implementation of a typeclass
52 | def add_nat = Add {
53 | add: fn x: Nat, y => x + y
54 | }
55 |
56 | # Emulated typeclass
57 | data Summable A = {
58 | add: Add A,
59 | zero: A,
60 | }
61 |
62 | # Implementation of a typeclass
63 | def summable_nat = Summable {
64 | add: add_nat,
65 | zero: 0,
66 | }
67 |
68 | # Generic summing function
69 | fn sum2 A : Summable A -> [A] -> A =
70 | summable => fold2(summable.zero, summable.add.add)
71 |
72 | $[main]
73 | def main = [1, 2, 3, 4, 5, 6, 7, 8, 9]:sum2(summable_nat)
74 |
75 | # >>>> OUTPUT
76 |
77 | 45i
78 |
--------------------------------------------------------------------------------
/compiler/src/error.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 | use std::io::Write;
3 |
4 | #[derive(Debug)]
5 | pub enum Error {
6 | CannotImport(SrcNode>),
7 | }
8 |
9 | impl Error {
10 | pub fn write>(self, cache: C, writer: impl Write) {
11 | use ariadne::{Report, ReportKind, Label, Color, Fmt, Span};
12 |
13 | let (msg, spans, notes) = match self {
14 | Error::CannotImport(path) => (
15 | format!("Cannot import {}, no such file", (*path).fg(Color::Red)),
16 | vec![
17 | (path.span(), format!("Does not exist"), Color::Red),
18 | ],
19 | vec![format!("The file {} must exist", (*path).fg(Color::Yellow))],
20 | ),
21 | };
22 |
23 | let mut report = Report::build(ReportKind::Error, spans.first().unwrap().0.src(), spans.first().unwrap().0.start())
24 | .with_code(3)
25 | .with_message(msg);
26 |
27 | for (span, msg, col) in spans {
28 | report = report.with_label(Label::new(span)
29 | .with_message(msg)
30 | .with_color(col));
31 | }
32 |
33 | for note in notes {
34 | report = report.with_note(note);
35 | }
36 |
37 | report
38 | .finish()
39 | .write(cache, writer)
40 | .unwrap();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/test_cases/test.tao:
--------------------------------------------------------------------------------
1 | import "../lib/std.tao"
2 |
3 | data Value =
4 | | String Str
5 | | Integer Int
6 | \ Pair (Value, Value)
7 |
8 | fn give_me_a : Str -> Value =
9 | | "string" => String "Hello"
10 | | "integer" => Integer 42i
11 | | "pair" => Pair (String "left", String "right")
12 | \ _ => Pair (Integer 0i, Integer 0i)
13 |
14 | fn factorial =
15 | | 0 => 1
16 | \ m ~ n + 1 => m * factorial(n)
17 |
18 | def main3 =
19 | let x = 5 in
20 | let f = fn y => x + y in
21 | f(4)
22 |
23 | # fn main = [1, 2, 3, 4, 5, 6]
24 | # -> stream
25 | # -> take(5)
26 | # -> mapped(fn x => x * x)
27 | # -> nth(3)
28 |
29 | class Mappable =
30 | => Item
31 | => Output
32 | => map : (Self.Item -> Self.Item) -> Self -> Self.Output
33 |
34 | for A member [A] of Mappable =
35 | => Item = A
36 | => Output = [A]
37 | => map = fn f, self => self->map(f)
38 |
39 | fn do_map A < Mappable =
40 | \ f, m => A.map(f, m)
41 |
42 | class Foo =
43 | => Bar
44 | => foo : Self.Bar
45 |
46 | member Nat of Foo =
47 | => Bar = Bool
48 | => foo = True
49 |
50 | def foo_nat : Nat.Bar = Nat.foo
51 |
52 | def main = [
53 | ("dog", "e"),
54 | ("cat", "c"),
55 | ("hamster", "a"),
56 | ("zebra", "z"),
57 | ("mouse", "d"),
58 | ("kangaroo", "b"),
59 | ]
60 | -> from_list
61 | -> contains("cat")
62 | -> debug
63 |
--------------------------------------------------------------------------------
/test_cases/eff.tao:
--------------------------------------------------------------------------------
1 | import "../lib/std.tao"
2 |
3 | # Desugar of...
4 |
5 | #fn greet : io ~ Str is (
6 | # let name <- input!;
7 | # print("Hello, " ++ name)!;
8 | # name
9 | #)
10 |
11 | class Effect =
12 | => Waiting
13 | => Ready
14 | => Input
15 | => Output
16 | => Final
17 | => pull : Self.Output -> Self.Waiting -> Go Self.Ready (Self.Output, Self.Final)
18 | => push : Self.Ready -> (Self.Input, Self.Waiting)
19 |
20 | data Greet
21 |
22 | data GreetWaiting =
23 | | Initial
24 | \ SayHello Str
25 |
26 | data GreetReady =
27 | \ GotName (@, Str)
28 |
29 | member Greet of Effect =
30 | => Waiting = GreetWaiting
31 | => Ready = GreetReady
32 | => Input = @
33 | => Output = @
34 | => Final = Str
35 | => pull = fn
36 | | uni, Initial => Next GotName input(uni)
37 | \ uni, SayHello name =>
38 | let (uni, ()) = print("Hello, " ++ name, uni) in
39 | Done (uni, name)
40 | => push = fn
41 | \ GotName (uni, name) => (uni, SayHello name)
42 |
43 | fn eval : Greet.Waiting -> @ -> (@, Str) =
44 | \ waiting, uni => when Greet.pull(uni, waiting) is
45 | | Next ready => when Greet.push(ready) is
46 | \ (uni, waiting) => eval(waiting, uni)
47 | \ Done (uni, name) => (uni, name)
48 |
49 | fn main : @ -> (@, ()) = uni =>
50 | let (uni, _) = eval(Initial, uni) in
51 | (uni, ())
52 |
--------------------------------------------------------------------------------
/lib/std/value.tao:
--------------------------------------------------------------------------------
1 | #! Utilities for working with values.
2 |
3 | $[util]
4 | fn identity A : A -> A =
5 | \ x => x
6 |
7 | # Default
8 |
9 | class Default =
10 | => default : Self
11 |
12 | $[util]
13 | def default A < Default : A = A.default
14 |
15 | member Nat of Default =
16 | => default = Nat.zero
17 |
18 | for A member [A] of Default =
19 | => default = []
20 |
21 | for A < Default, B < Default member (A, B) of Default =
22 | => default = (A.default, B.default)
23 |
24 | # From
25 |
26 | class From A =
27 | => from : A -> Self
28 |
29 | for A member A of From A =
30 | => from = identity
31 |
32 | $[util]
33 | fn from A, B < From A = a => B.from(a)
34 |
35 | class Into A =
36 | => into : Self -> A
37 |
38 | $[util]
39 | fn into A < Into B, B = a => A.into(a)
40 |
41 | for A, B < From A member A of Into B =
42 | => into = fn x => B.from(x)
43 |
44 | member Int of From Nat =
45 | => from = fn x => --x
46 |
47 | # Maybe
48 |
49 | data Maybe A =
50 | | Just A
51 | \ None
52 |
53 | for A member Maybe A of Default =
54 | => default = None
55 |
56 | $[util]
57 | fn flat_map A, B, e : (A -> e ~ Maybe B) -> Maybe A -> e ~ Maybe B =
58 | | _, None => None
59 | \ f, Just a => a->f!
60 |
61 | $[util]
62 | fn filter_opt A, e : (A -> e ~ Bool) -> Maybe A -> e ~ Maybe A =
63 | | _, None => None
64 | \ f, Just a => if a->f!
65 | then Just a
66 | else None
67 |
68 | $[util]
69 | fn unwrap_or A : A -> Maybe A -> A =
70 | | a, None => a
71 | \ _, Just a => a
72 |
73 | $[util]
74 | def todo A : A = todo
75 |
--------------------------------------------------------------------------------
/syntax/src/span.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 | use std::{
3 | ops::Range,
4 | fmt,
5 | };
6 |
7 | #[derive(Copy, Clone, PartialEq, Eq)]
8 | pub struct Span {
9 | src: SrcId,
10 | range: (usize, usize),
11 | }
12 |
13 | impl Span {
14 | #[cfg(test)]
15 | pub fn empty() -> Self {
16 | Self::new(SrcId::empty(), 0..0)
17 | }
18 |
19 | pub fn src(&self) -> SrcId { self.src }
20 |
21 | pub fn range(&self) -> Range { self.start()..self.end() }
22 |
23 | pub fn union(self, other: Self) -> Self {
24 | assert_eq!(self.src, other.src, "attempted to union spans with different sources");
25 | Self {
26 | range: (self.start().min(other.start()), self.end().max(other.end())),
27 | ..self
28 | }
29 | }
30 | }
31 |
32 | impl fmt::Debug for Span {
33 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
34 | write!(f, "{:?}:{:?}", self.src, self.range())
35 | }
36 | }
37 |
38 | impl chumsky::Span for Span {
39 | type Context = SrcId;
40 | type Offset = usize;
41 |
42 | fn new(src: SrcId, range: Range) -> Self {
43 | assert!(range.start <= range.end);
44 | Self { src, range: (range.start, range.end) }
45 | }
46 |
47 | fn context(&self) -> SrcId { self.src }
48 | fn start(&self) -> Self::Offset { self.range.0 }
49 | fn end(&self) -> Self::Offset { self.range.1 }
50 | }
51 |
52 | impl ariadne::Span for Span {
53 | type SourceId = SrcId;
54 |
55 | fn source(&self) -> &SrcId { &self.src }
56 |
57 | fn start(&self) -> usize { self.range.0 }
58 | fn end(&self) -> usize { self.range.1 }
59 | }
60 |
--------------------------------------------------------------------------------
/test_cases/error.tao:
--------------------------------------------------------------------------------
1 | import "../lib/std.tao"
2 |
3 | data InvalidCharacter = Char
4 | member InvalidCharacter of Display =
5 | => display = fn InvalidCharacter c => "invalid character: " ++ [c]
6 |
7 | fn parse_digit : Char -> Result Nat InvalidCharacter =
8 | | '0' => Ok 0
9 | | '1' => Ok 1
10 | | '2' => Ok 2
11 | | '3' => Ok 3
12 | | '4' => Ok 4
13 | | '5' => Ok 5
14 | | '6' => Ok 6
15 | | '7' => Ok 7
16 | | '8' => Ok 8
17 | | '9' => Ok 9
18 | \ c => Err InvalidCharacter c
19 |
20 | data NoCharacters
21 | member NoCharacters of Display =
22 | => display = fn _ => "no characters"
23 |
24 | data CannotBeginWithZero
25 | member CannotBeginWithZero of Display =
26 | => display = fn _ => "nat must not begin with 0"
27 |
28 | fn parse_digits : Nat -> Str -> Result Nat InvalidCharacter =
29 | | n, [] => Ok n
30 | | n, [c .. cs] => when c->parse_digit is
31 | | Err e => Err e
32 | \ Ok x => cs->parse_digits(n * 10 + x)
33 | \ _, [c ..] => Err InvalidCharacter c
34 |
35 | fn parse_nat : Str -> Result Nat (NoCharacters | InvalidCharacter | CannotBeginWithZero) =
36 | | [] => Err ? NoCharacters
37 | | ['0', _ ..] => Err ? CannotBeginWithZero
38 | \ cs => when cs->parse_digits(0) is
39 | | Ok n => Ok n
40 | \ Err e => Err ?e
41 |
42 | def main : IO () = do {
43 | let nat <- input;
44 | print(when nat->parse_nat is
45 | | Ok n => "ok!"
46 | \ Err e => when ?e is
47 | | ? (e : CannotBeginWithZero) => e->display
48 | | ? (e : NoCharacters) => e->display
49 | \ ? (e : InvalidCharacter) => e->display
50 | );
51 | }
52 |
--------------------------------------------------------------------------------
/cir/src/lib.rs:
--------------------------------------------------------------------------------
1 | use tao_util::index::{Index, Id};
2 |
3 | #[derive(Copy, Clone)]
4 | pub struct Reg(usize);
5 |
6 | #[derive(Clone)]
7 | pub enum Repr {
8 | Nat,
9 | Bool,
10 | Universe,
11 | List(Box),
12 | Tuple(Vec),
13 | Sum(Vec),
14 | Box(Id),
15 | Func(Box, Box),
16 | }
17 |
18 | #[derive(Copy, Clone)]
19 | pub enum Intrinsic {
20 | Box,
21 | Unbox,
22 | }
23 |
24 | #[derive(Copy, Clone)]
25 | pub enum Imm {
26 | Nat(u64),
27 | }
28 |
29 | #[derive(Clone)]
30 | pub enum Op {
31 | Imm(Imm),
32 | Call(Id, Vec),
33 | Apply(Reg, Reg),
34 | Tuple(Vec),
35 | AccessField(Reg, usize),
36 | AccessVariant(Reg, usize),
37 | VariantIdx(Reg),
38 | Intrinsic(Intrinsic, Vec),
39 | }
40 |
41 | #[derive(Copy, Clone)]
42 | pub enum Pat {
43 | Wildcard,
44 | Imm(Imm),
45 | }
46 |
47 | #[derive(Copy, Clone)]
48 | pub enum Target {
49 | Return,
50 | Block(Id),
51 | }
52 |
53 | #[derive(Clone)]
54 | pub struct Branch {
55 | pub pred: Reg,
56 | /// (matching pattern, parameters passed to target, branch destination)
57 | pub arms: Vec<(Pat, Vec, Target)>,
58 | }
59 |
60 | #[derive(Clone)]
61 | pub struct Block {
62 | pub args: Vec,
63 | pub ops: Vec<(Repr, Reg, Op)>,
64 | pub branch: Branch,
65 | }
66 |
67 | #[derive(Copy, Clone)]
68 | pub struct BlockId(usize);
69 |
70 | #[derive(Clone)]
71 | pub struct Func {
72 | pub entry: Id,
73 | pub ret: Vec,
74 | pub blocks: Vec,
75 | }
76 |
77 | #[derive(Clone)]
78 | pub struct Program {
79 | datas: Index,
80 | funcs: Index,
81 | entry: Id,
82 | }
83 |
--------------------------------------------------------------------------------
/compiler/src/main.rs:
--------------------------------------------------------------------------------
1 | use tao::{Options, SrcId, compile};
2 | use tao_vm::{exec, Env};
3 | use structopt::StructOpt;
4 | use std::{fs, path::PathBuf};
5 | use rand::prelude::*;
6 |
7 | #[derive(Clone, Debug, StructOpt)]
8 | pub struct Args {
9 | #[structopt(flatten)]
10 | pub options: Options,
11 | /// Specify the file to run
12 | #[structopt(name = "FILE", parse(from_os_str))]
13 | pub file: PathBuf,
14 | }
15 |
16 | #[derive(Default)]
17 | pub struct Stdio;
18 |
19 | impl Env for Stdio {
20 | fn input(&mut self) -> String {
21 | use std::io::{stdin, stdout, Write};
22 |
23 | let mut s = String::new();
24 | print!("> ");
25 | stdout().flush().expect("IO error");
26 | stdin().read_line(&mut s).expect("IO error");
27 | s
28 | }
29 |
30 | fn print(&mut self, s: String) {
31 | println!("{}", s);
32 | }
33 |
34 | fn rand(&mut self, max: i64) -> i64 {
35 | thread_rng().gen_range(0..max)
36 | }
37 | }
38 |
39 | fn main() {
40 | let args = Args::from_args();
41 | let src = fs::read_to_string(&args.file)
42 | .expect("Failed to read file");
43 | let src_id = SrcId::from_path(args.file);
44 | let prog = compile(
45 | src,
46 | src_id,
47 | args.options,
48 | std::io::stdout(),
49 | |src| fs::read_to_string(src.to_path()).ok(),
50 | |parent, rel| {
51 | let mut path = parent.to_path();
52 | path.pop();
53 | path.push(rel);
54 | Some(SrcId::from_path(path.canonicalize().ok()?))
55 | },
56 | );
57 |
58 | if let Some(prog) = prog {
59 | exec(&prog, &mut Stdio::default());
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/lib/std/map.tao:
--------------------------------------------------------------------------------
1 | #! An ordered map.
2 |
3 | import "../core/ops.tao"
4 |
5 | data Map K, V =
6 | | MEmpty
7 | | MLeaf (K, V)
8 | \ MBranch (K, Map K V, Map K V)
9 |
10 | for K, V member Map K V of Default =
11 | => default = MEmpty
12 |
13 | for K < Debug, V < Debug member Map K V of Debug =
14 | => debug = fn m =>
15 | let debug_elements = fix(fn
16 | | _, MEmpty => ""
17 | | _, MLeaf (k, v) => k -> debug ++ ": " ++ v -> debug
18 | \ rec, MBranch (_, a, b) => a -> rec ++ ", " ++ b -> rec)
19 | in
20 | "{ " ++ m -> debug_elements ++ " }"
21 |
22 | $[util]
23 | fn insert K < Ord, V : K -> V -> Map K V -> Map K V =
24 | | k, v, MEmpty => MLeaf (k, v)
25 | | k, v, MLeaf (k_old, v_old) => when K.cmp(k, k_old) is
26 | | Less => MBranch (k_old, MLeaf (k, v), MLeaf (k_old, v_old))
27 | | More => MBranch (k, MLeaf (k_old, v_old), MLeaf (k, v))
28 | \ Equal => MLeaf (k, v)
29 | \ k, v, MBranch (k_ref, a, b) => if K.less(k, k_ref)
30 | then MBranch (k_ref, a->insert(k, v), b)
31 | else MBranch (k_ref, a, b->insert(k, v))
32 |
33 | $[util]
34 | fn index K < Ord, V : K -> Map K V -> Maybe V =
35 | | _, MEmpty => None
36 | | k, MLeaf (k_old, v) => if K.eq(k, k_old)
37 | then Just v
38 | else None
39 | \ k, MBranch (k_ref, a, b) => if K.less(k, k_ref)
40 | then a->index(k)
41 | else b->index(k)
42 |
43 | $[util]
44 | fn contains K < Ord, V : K -> Map K V -> Bool =
45 | k, m => when m->index(k) is
46 | | Just _ => True
47 | \ None => False
48 |
49 | $[util]
50 | def from_list K < Ord, V : [(K, V)] -> Map K V =
51 | fold(default, fn m, (k, v) => m->insert(k, v))
52 |
--------------------------------------------------------------------------------
/compiler/tests/lists.tao:
--------------------------------------------------------------------------------
1 | # >>>> INPUT
2 |
3 | import "../../lib/std.tao"
4 |
5 | $[main]
6 | def main: [()]= []
7 |
8 | # >>>> OUTPUT
9 |
10 | []
11 |
12 | # >>>> INPUT
13 |
14 | import "../../lib/std.tao"
15 |
16 | $[main]
17 | def main = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
18 |
19 | # >>>> OUTPUT
20 |
21 | [1i, 2i, 3i, 4i, 5i, 6i, 7i, 8i, 9i, 10i]
22 |
23 | # >>>> INPUT
24 |
25 | import "../../lib/std.tao"
26 |
27 | fn len2 A =
28 | | []: [A] => 0
29 | \ [_ .. tail] => 1 + tail:len2
30 |
31 | $[main]
32 | def main = [True, False, False, True, True, False, False]:len2
33 |
34 | # >>>> OUTPUT
35 |
36 | 7i
37 |
38 | # >>>> INPUT
39 |
40 | import "../../lib/std.tao"
41 |
42 | fn sum2 =
43 | | []: [Nat] => 0
44 | \ [x .. tail] => x + tail:sum
45 |
46 | $[main]
47 | def main = [1, 2, 3, 4, 5]:sum2
48 |
49 | # >>>> OUTPUT
50 |
51 | 15i
52 |
53 | # >>>> INPUT
54 |
55 | import "../../lib/std.tao"
56 |
57 | fn nth2 A : Nat -> [A] -> Maybe A =
58 | | 0, [x ..] => Just x
59 | | n + 1, [_ .. tail] => tail:nth2(n)
60 | \ _, _ => None
61 |
62 | $[main]
63 | def main =
64 | let xs = [5, 7, 2, 3, 9, 8] in
65 | when xs:nth2(0), xs:nth2(1), xs:nth2(5), xs:nth2(6) is
66 | | Just 5, Just 7, Just 8, None => True
67 | \ _, _, _, _ => False
68 |
69 | # >>>> OUTPUT
70 |
71 | #1 []
72 |
73 | # >>>> INPUT
74 |
75 | import "../../lib/std.tao"
76 |
77 | fn fold2 A, B : A -> (A -> B -> A) -> [B] -> A =
78 | | init, _, [] => init
79 | \ init, f, [x .. tail] => fold2(f(init, x), f, tail)
80 |
81 | $[main]
82 | def main = [1, 2, 3, 4, 5]:fold2(0, fn x, y => x + y)
83 |
84 | # >>>> OUTPUT
85 |
86 | 15i
87 |
88 | # >>>> INPUT
89 |
90 | import "../../lib/std.tao"
91 |
92 | $[main]
93 | def main = [1, 2 .. [3, 4, 5]]
94 |
95 | # >>>> OUTPUT
96 |
97 | [1i, 2i, 3i, 4i, 5i]
98 |
--------------------------------------------------------------------------------
/analysis/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![feature(arbitrary_self_types, option_zip, never_type, let_chains)]
2 |
3 | pub mod class;
4 | pub mod concrete;
5 | pub mod context;
6 | pub mod data;
7 | #[cfg(feature = "debug")]
8 | pub mod debug;
9 | pub mod def;
10 | pub mod effect;
11 | pub mod error;
12 | pub mod exhaustivity;
13 | pub mod infer;
14 | pub mod hir;
15 | pub mod lower;
16 | pub mod reify;
17 | pub mod ty;
18 |
19 | pub use crate::{
20 | class::{ClassId, Class, Classes, ClassAssoc, ClassField, Member, MemberId, MemberItem},
21 | concrete::{ConContext, ConTyId, ConTy, ConNode, ConMeta, ConProc, ConProcId, ConDataId, ConEffectId},
22 | context::Context,
23 | data::{Datas, Data, DataId, Alias, AliasId},
24 | def::{Defs, Def, DefId},
25 | effect::{Effects, EffectDecl, EffectDeclId, EffectAlias, EffectAliasId},
26 | error::Error,
27 | exhaustivity::{exhaustivity, ExamplePat},
28 | hir::{InferExpr, InferBinding, TyExpr, TyBinding, ConBinding, ConExpr, Intrinsic, Meta},
29 | infer::{Infer, Checked, TyVar, TyInfo, InferNode, InferMeta, InferError, EqInfo, ClassVar, ClassInfo, EffectVar, EffectInfo, EffectInstInfo, EffectInstVar, covariant, contravariant, invariant, Variance},
30 | lower::{Scope, ToHir, TypeLowerCfg},
31 | reify::Reify,
32 | ty::{Types, TyId, GenScope, GenScopeId, Prim, Ty, TyNode, TyMeta, ErrorReason, ImpliedMember, TyImpliedMember, InferImpliedMember, ImpliedItems, InferImpliedItems, Effect, EffectId, EffectInst},
33 | };
34 | pub use tao_syntax::ast::Ident;
35 | pub use tao_util::index::{Id, Index};
36 |
37 | use tao_syntax::{
38 | Node,
39 | Span,
40 | SrcNode,
41 | SrcId,
42 | ast,
43 | };
44 | use hashbrown::{HashMap, HashSet};
45 | use internment::Intern;
46 | use std::{
47 | fmt,
48 | marker::PhantomData,
49 | collections::BTreeMap,
50 | };
51 |
--------------------------------------------------------------------------------
/test_cases/experiments.tao:
--------------------------------------------------------------------------------
1 | fn to_str
2 | 0 = "zero"
3 | n = "non-zero"
4 |
5 | fn add x, y = x + y
6 |
7 | fn filter
8 | _, [] = []
9 | f, [x .. xs] = [.. if x->f then [x] else [], xs]
10 |
11 | fn factorial
12 | 0 = 1
13 | m ~ n + 1 = m * n->factorial
14 |
15 | class Sum < Zero + Add
16 | for A < Zero + Add member A of Sum
17 |
18 | class Product < One + Mul
19 | for A < One + Mul member A of Product
20 |
21 | fn sum : a -> a where
22 | a < Sum
23 | is
24 | [] => zero
25 | [x .. xs] => x + xs->sum
26 |
27 | data Maybe a
28 | Just a
29 | None
30 |
31 | data Result a, e
32 | Ok a
33 | Err e
34 |
35 | class Iterator
36 | Item
37 | next : Self -> (Maybe Item, Self)
38 |
39 | member [a] of Iterator
40 | Item = a
41 | next = fn
42 | [] => (None, [])
43 | [x .. xs] => (Just x, xs)
44 |
45 | fn foobar x, y
46 | let xy = x + y
47 | let xyz = xy + 5
48 | xy
49 |
50 | def main = do
51 | let text = input;
52 | if msg = "exit" then do
53 | print("Now exiting...")
54 | print("Goodbye!")
55 | else do
56 | print("Hello, world!")
57 | main;
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | fn add : Int -> Int -> Int is
66 | x, y => x + y
67 |
68 | fn len : [a] -> Nat is
69 | | [] -> 0
70 | \ [_ .. as] -> 1 + as->len
71 |
72 | fn zip : [a] -> [b] -> [(a, b)] is
73 | | [a .. as], [b .. bs] => [(a, b) .. xs->zip]
74 | \ _, _ => []
75 |
76 | class Sum < Zero + Add
77 |
78 | fn sum : [a] -> a where
79 | a < Sum
80 | is
81 | | [] => zero
82 | \ [a .. as] => a + as->sum
83 |
84 | fn sum(xs: [A]) -> A {
85 | match xs {
86 | [] => zero(),
87 | [x .. xs] => x + sum(xs),
88 | }
89 | }
90 |
91 | fn sum : [a < Sum] -> a is
92 | | [] => zero
93 | \ [a .. as] => a + as->sum
94 |
--------------------------------------------------------------------------------
/syntax/examples/todo.tao:
--------------------------------------------------------------------------------
1 | def fibonacci = fn x: Int => if x <= 1
2 | then 1
3 | else fibonacci(x - 1) + fibonacci(x - 2)
4 |
5 | def factorial = fn
6 | | 0 => 1
7 | | x => x * factorial(x - 1)
8 |
9 | def len A = fn
10 | | []: [A] => 0
11 | | [_ .. tail] => 1 + tail:len
12 |
13 | def sum = fn
14 | | []: [Int] => 0
15 | | [x .. tail] => x + tail:sum
16 |
17 | def append A = fn x: A, xs: [A] => xs ++ [x]
18 |
19 | def map A, B : (A -> B) -> [A] -> [B] = fn
20 | | _, [] => []
21 | | f, [x .. tail] => [x:f] ++ tail:map(f)
22 |
23 | data Result A, E =
24 | | Ok A
25 | | Err E
26 |
27 | def none A : Result A () = Err ()
28 |
29 | data InvalidIndex = ()
30 |
31 | data List A =
32 | | Nil
33 | | Item (A, List A)
34 |
35 | def nth A = fn
36 | | 0, Item (x, _): List A => Ok x
37 | | x, Item (_, tail) => tail:nth(x - 1)
38 | | x, Nil => none
39 |
40 | data Add T = {
41 | add: T -> T -> T,
42 | }
43 |
44 | def add_int = Add {
45 | add: fn x: Int, y => x + y,
46 | }
47 |
48 | data Zero T = {
49 | zero: T,
50 | }
51 |
52 | def zero_int = Zero {
53 | zero: 0,
54 | }
55 |
56 | def sum2 A = fn
57 | | zero: Zero A, add: Add A, [] => zero.zero
58 | | zero, add, [x .. tail] => add.add(x, tail:sum2(zero, add))
59 |
60 | $[main]
61 | def main = [1, 2, 3, 4, 5, 6, 7, 8, 9]:sum2(zero_int, add_int)
62 |
63 | #def main = add_int.add(4, 5)
64 |
65 | # def main = fibonacci(35)
66 | # def main = factorial(10)
67 | # def main = [1, 2, 3, 4, 5, 6, 7, 8, 9]:sum
68 | # def main = [1, 2, 3, 4]:map(fn x => x * x)
69 |
70 | def test =
71 | let five = 5 in
72 | let three = 3 in
73 | let f = fn x: Int => five in
74 | f(1)
75 |
76 | def const_fold = (fn x => x)(5)
77 |
78 | def inlining =
79 | let double = fn x => x * 2 in
80 | let square = fn x => x * x in
81 | 2:double:square
82 |
83 | def captures =
84 | let x = 5 in
85 | let add_five = fn y => x + y in
86 | add_five(5)
87 |
--------------------------------------------------------------------------------
/syntax/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![feature(option_zip, trait_alias)]
2 |
3 | pub mod error;
4 | pub mod token;
5 | pub mod span;
6 | pub mod src;
7 | pub mod ast;
8 | pub mod node;
9 | pub mod parse;
10 |
11 | pub use crate::{
12 | error::{Error, ErrorKind, Pattern},
13 | span::Span,
14 | node::{Node, SrcNode},
15 | src::SrcId,
16 | token::{Token, Op, Delimiter},
17 | };
18 |
19 | use std::fmt;
20 | use chumsky::prelude::*;
21 |
22 | fn parse(parser: impl parse::Parser, code: &str, src: SrcId) -> (Option, Vec) {
23 | let mut errors = Vec::new();
24 |
25 | let len = code.chars().count();
26 | let eoi = Span::new(src, len..len);
27 |
28 | let (tokens, mut lex_errors) = token::lexer()
29 | .parse_recovery(chumsky::Stream::from_iter(
30 | eoi,
31 | code
32 | .chars()
33 | .enumerate()
34 | .map(|(i, c)| (c, Span::new(src, i..i + 1))),
35 | ));
36 | errors.append(&mut lex_errors);
37 |
38 | let tokens = if let Some(tokens) = tokens {
39 | tokens
40 | } else {
41 | return (None, errors);
42 | };
43 |
44 | let (output, mut parse_errors) = parser.parse_recovery(chumsky::Stream::from_iter(eoi, tokens.into_iter()));
45 | errors.append(&mut parse_errors);
46 |
47 | (output, errors)
48 | }
49 |
50 | pub fn parse_expr(code: &str, src: SrcId) -> (Option>, Vec) {
51 | parse(
52 | parse::expr_parser()
53 | .then_ignore(end())
54 | .map_with_span(SrcNode::new),
55 | code,
56 | src,
57 | )
58 | }
59 |
60 | pub fn parse_module(code: &str, src: SrcId) -> (Option>, Vec) {
61 | parse(
62 | parse::module_parser()
63 | .map_with_span(SrcNode::new),
64 | code,
65 | src,
66 | )
67 | }
68 |
--------------------------------------------------------------------------------
/middle/src/opt/commute_branches.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 |
3 | /// Commutes nested branches (i.e: inline the outer match into the arms of the inner match).
4 | ///
5 | /// Commuting nested branches makes it much easier for constant folding to by duplicating the context to remove
6 | /// conditionality of the input. For example:
7 | ///
8 | /// ```ignore
9 | /// when (when xs is
10 | /// | [x] => bar(x)
11 | /// \ _ => False) is
12 | /// | True => False
13 | /// \ False => True
14 | /// ```
15 | ///
16 | /// becomes
17 | ///
18 | /// ```ignore
19 | /// when xs is
20 | /// | [x] => when bar(x) is
21 | /// | True => False
22 | /// \ False => True
23 | /// \ _ => when False is
24 | /// | True => False
25 | /// \ False => True
26 | /// ```
27 | #[derive(Default)]
28 | pub struct CommuteBranches;
29 |
30 | impl Pass for CommuteBranches {
31 | fn apply(&mut self, ctx: &mut Context) {
32 | fn visit(
33 | expr: &mut MirNode,
34 | ) {
35 | expr.for_children_mut(|expr| visit(expr));
36 |
37 | let (expr, expr_meta) = expr.as_mut();
38 | if let Expr::Match(pred, arms) = expr
39 | && let Expr::Match(inner_pred, inner_arms) = &mut (**pred).clone()
40 | {
41 | *pred = inner_pred.clone();
42 | *arms = std::mem::take(inner_arms)
43 | .into_iter()
44 | .map(|(binding, inner_arm)| {
45 | let meta = expr_meta.clone();
46 | (binding, MirNode::new(Expr::Match(inner_arm, arms.clone()), meta))
47 | })
48 | .collect();
49 | }
50 | }
51 |
52 | let proc_bodies = ctx.procs
53 | .iter()
54 | .map(|(id, proc)| (id, proc.body.clone()))
55 | .collect::>();
56 |
57 | for (_, proc) in ctx.procs.iter_mut() {
58 | visit(&mut proc.body);
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/lib/std/list.tao:
--------------------------------------------------------------------------------
1 | #! Utilities for programming with lists.
2 |
3 | import "../core/ops.tao"
4 | import "value.tao"
5 | import "container.tao"
6 |
7 | for A, B, e member [A] of Map (A) B e =
8 | => Output = e ~ [B]
9 | => map = fn
10 | | _, [] => []
11 | \ f, [x .. xs] => [x->f! .. xs->Self.map(f)!]
12 |
13 | $[util]
14 | fn filter A : (A -> Bool) -> [A] -> [A] =
15 | | _, [] => []
16 | \ f, [x .. xs] => (if x->f then [x] else []) ++ xs->filter(f)
17 |
18 | $[util]
19 | fn find_first A : (A -> Bool) -> [A] -> Maybe A = f, xs => when xs->filter(f) is
20 | | [] => None
21 | \ [x ..] => Just x
22 |
23 | $[util]
24 | fn len A : [A] -> Nat = xs => @len_list(xs)
25 |
26 | $[util]
27 | fn repeat A : Nat -> [A] -> [A] =
28 | | 0, _ => []
29 | \ n + 1, x => x ++ x->repeat(n)
30 |
31 | $[util]
32 | fn swap A : Nat -> (A -> A) -> [A] -> [A] = n, f, xs => when @skip_list(xs, n) is
33 | | [] => xs
34 | \ [x .. tail] => @trim_list(xs, n) ++ [x->f .. tail]
35 |
36 | $[util]
37 | fn nth A : Nat -> [A] -> Maybe A = n, xs => when @skip_list(xs, n) is
38 | | [x ..] => Just x
39 | \ [] => None
40 |
41 | $[util]
42 | fn sort A < Ord : [A] -> [A] =
43 | | [] => []
44 | \ [mid .. xs] =>
45 | xs->filter(fn x => x < mid)->sort
46 | ++
47 | [mid]
48 | ++
49 | xs->filter(fn x => x >= mid)->sort
50 |
51 | $[util]
52 | fn rev A : [A] -> [A] =
53 | | [] => []
54 | \ [x .. xs] => xs->rev ++ [x]
55 |
56 | # def sum A : [A] -> A where
57 | # A < Zero + Add A with { Output = A },
58 | # =
59 | # fold(A.zero, fn x, y => x + y)
60 |
61 | # def product A : [A] -> A where
62 | # A < One + Mul A with { Output = A }
63 | # =
64 | # fold(A.one, fn x, y => A.mul(x, y))
65 |
66 | $[util]
67 | fn find_index_start A < Eq : Nat -> A -> [A] -> Maybe Nat =
68 | | idx, c, [head .. tail] => if c = head
69 | then Just idx
70 | else tail->find_index_start(idx + 1, c)
71 | \ idx, c, [] => None
72 |
73 | $[util]
74 | def find_index A < Eq : A -> [A] -> Maybe Nat = find_index_start(0)
75 |
--------------------------------------------------------------------------------
/site/index.scss:
--------------------------------------------------------------------------------
1 | body {
2 | background: #444;
3 | color: white;
4 | font-family: "monospace";
5 | }
6 |
7 | a:link { color: #abd; }
8 | a:visited { color: #bbd; }
9 |
10 | .panel {
11 | border: 0;
12 | text-align: left;
13 | font-size: 16px;
14 | margin: 0.5em;
15 | }
16 |
17 | .panel * {
18 | border: 0;
19 | padding: 0.3em 1em;
20 | }
21 |
22 | .panel select {
23 | background-color: #b0b2c0;
24 | }
25 | .panel select:hover {
26 | background-color:#9092a0
27 | }
28 |
29 | .panel .run {
30 | background-color: #b0c0b2;
31 | }
32 | .panel .run:hover {
33 | background-color: #90a092;
34 | }
35 |
36 | .footer {
37 | text-align: center;
38 | font-size: 12px;
39 | }
40 |
41 | .splitter {
42 | display: grid;
43 | grid-template-columns: 3fr 2fr;
44 | grid-gap: 30px;
45 | position: relative;
46 | width: 100%;
47 | height: 100%;
48 | }
49 |
50 | .left {
51 | grid-column: 1;
52 | display: block;
53 | }
54 |
55 | .right {
56 | grid-column: 2;
57 | scroll: auto;
58 | }
59 |
60 | #editor {
61 | width: 100%;
62 | height: 92.5vh;
63 |
64 | margin: 4px;
65 | padding: 8px;
66 | background: #222;
67 | color: white;
68 | border: 0;
69 | font-family: monospace;
70 | resize: none;
71 | overflow: auto;
72 | spellcheck: false;
73 | autofocus: true;
74 | }
75 |
76 | .output {
77 | height: 92.5vh;
78 | width: 50vw;
79 | max-width: 50vw;
80 |
81 | margin: 4px;
82 | padding: 8px;
83 | background-color: #222;
84 | color: white;
85 | white-space: pre;
86 | font-family: monospace;
87 | overflow: auto;
88 | }
89 |
90 | .call_graph {
91 | height: 92.5vh;
92 | width: 50vw;
93 | max-width: 50vw;
94 |
95 | margin: 4px;
96 | padding: 8px;
97 | background-color: #222;
98 | color: white;
99 | white-space: pre;
100 | font-family: monospace;
101 | overflow: auto;
102 | display: none;
103 | }
104 |
--------------------------------------------------------------------------------
/test_cases/type-astronomy.tao:
--------------------------------------------------------------------------------
1 | import "../lib/main.tao"
2 |
3 | # Natural numbers
4 |
5 | data Zero
6 | data Succ A
7 |
8 | type One = Succ Zero
9 | type Two = Succ One
10 | type Three = Succ Two
11 | type Four = Succ Three
12 | type Five = Succ Four
13 | type Six = Succ Five
14 | type Seven = Succ Six
15 | type Eight = Succ Seven
16 | type Nine = Succ Eight
17 | type Ten = Succ Nine
18 |
19 | # Utility that allows us to print the result of operations
20 | # Note that this isn't really doing any runtime calculations, other than recursively summing
21 | # the number by walking back through the successor chain
22 |
23 | class Number =
24 | => as_nat : Nat
25 |
26 | member Zero of Number =
27 | => as_nat = 0
28 |
29 | for A < Number member Succ A of Number =
30 | => as_nat = A.as_nat + 1
31 |
32 | # Addition
33 |
34 | class TAdd B =
35 | => Output
36 |
37 | for B member Zero of TAdd B =
38 | => Output = B
39 |
40 | for A < TAdd B, B member Succ A of TAdd B =
41 | => Output = Succ A.Output
42 |
43 | # Multiplication
44 |
45 | class TMul B =
46 | => Output
47 |
48 | for B member Zero of TMul B =
49 | => Output = Zero
50 |
51 | for A, B member Succ A of TMul B where
52 | A < TMul B,
53 | A..Output < TAdd B,
54 | =
55 | => Output = A..Output.Output
56 |
57 | fn f A < TMul Zero : A -> () =
58 | _ => ()
59 |
60 | def x = f(Zero)
61 |
62 | # Truthiness
63 |
64 | data TTrue
65 | data TFalse
66 |
67 | class Truthy =
68 | => as_bool : Bool
69 |
70 | member TTrue of Truthy =
71 | => as_bool = True
72 |
73 | member TFalse of Truthy =
74 | => as_bool = False
75 |
76 | class If A, B =
77 | => Output
78 |
79 | for A, B member TTrue of If (A) B =
80 | => Output = A
81 |
82 | for A, B member TFalse of If (A) B =
83 | => Output = B
84 |
85 | # Demo
86 | def main : io ~ () =
87 | print("Answer = " + .Output
90 | Ten..Output
91 | >.Output
92 | TFalse..Output
94 | Nine..Output
95 | >.Output
96 | >.Output>.as_nat)!
97 |
--------------------------------------------------------------------------------
/util/src/index.rs:
--------------------------------------------------------------------------------
1 | use std::{
2 | cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd},
3 | fmt, hash,
4 | marker::PhantomData,
5 | };
6 |
7 | pub struct Id(usize, PhantomData);
8 |
9 | impl Copy for Id {}
10 | impl Clone for Id { fn clone(&self) -> Self { *self } }
11 | impl Eq for Id {}
12 | impl PartialEq for Id {
13 | fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
14 | }
15 | impl Ord for Id {
16 | fn cmp(&self, other: &Self) -> Ordering { self.0.cmp(&(other.0)) }
17 | }
18 | impl PartialOrd for Id {
19 | fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) }
20 | }
21 | impl fmt::Debug for Id {
22 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
23 | write!(f, "Id<{}>({})", std::any::type_name::(), self.0)
24 | }
25 | }
26 | impl hash::Hash for Id {
27 | fn hash(&self, h: &mut H) { self.0.hash(h); }
28 | }
29 |
30 | #[derive(Clone)]
31 | pub struct Index {
32 | items: Vec,
33 | }
34 |
35 | impl Default for Index {
36 | fn default() -> Self {
37 | Self { items: Vec::new() }
38 | }
39 | }
40 |
41 | impl Index {
42 | pub fn keys(&self) -> impl Iterator
- > + '_ {
43 | (0..self.items.len()).map(|idx| Id(idx, PhantomData))
44 | }
45 |
46 | pub fn values(&self) -> impl Iterator
- + '_ {
47 | self.items.iter()
48 | }
49 |
50 | pub fn iter(&self) -> impl Iterator
- , &T)> + '_ {
51 | self.items
52 | .iter()
53 | .enumerate()
54 | .map(|(idx, item)| (Id(idx, PhantomData), item))
55 | }
56 |
57 | pub fn add(&mut self, item: T) -> Id {
58 | let id = Id(self.items.len(), PhantomData);
59 | self.items.push(item);
60 | id
61 | }
62 | }
63 |
64 | impl std::ops::Index> for Index {
65 | type Output = T;
66 | fn index(&self, id: Id) -> &Self::Output { &self.items[id.0] }
67 | }
68 |
69 | impl std::ops::IndexMut> for Index {
70 | fn index_mut(&mut self, id: Id) -> &mut Self::Output { &mut self.items[id.0] }
71 | }
72 |
--------------------------------------------------------------------------------
/lib/std/fmt.tao:
--------------------------------------------------------------------------------
1 | #! Utilities for formatting and displaying data.
2 |
3 | import "type.tao"
4 | import "str.tao"
5 |
6 | # Debug
7 |
8 | class Debug =
9 | => debug : Self -> Str
10 |
11 | $[util]
12 | def debug A < Debug : A -> Str = A.debug
13 |
14 | member Bool of Debug =
15 | => debug = fn
16 | | True => "True"
17 | \ False => "False"
18 |
19 | for A < Debug member [A] of Debug =
20 | => debug = fn xs => dispatch(
21 | xs,
22 | # Specialise on [Char]
23 | fn s : Str => "\"" ++ s ++ "\"",
24 | # Fallback for [A]
25 | fn xs => "[" ++ xs -> fix(fn
26 | | _, [] => ""
27 | | _, [x] => x -> debug
28 | \ rec, [x .. xs] => x -> debug ++ ", " ++ xs -> rec) ++ "]",
29 | )
30 |
31 | member Char of Debug =
32 | => debug = fn c => "'" ++ [c] ++ "'"
33 |
34 | member Nat of Debug =
35 | => debug = fn x => @display_int(--x)
36 |
37 | member () of Debug =
38 | => debug = fn () => "()"
39 |
40 | for A < Debug, B < Debug member (A, B) of Debug =
41 | => debug = fn (a, b) => "(" ++ A.debug(a) ++ ", " ++ B.debug(b) ++ ")"
42 |
43 | for A < Debug member Maybe A of Debug =
44 | => debug = fn
45 | | None => "None"
46 | \ Just a => "Some " + a->debug
47 |
48 | # TODO: Reenable this when conflict with Str is resolved!
49 | #for A < Debug member [A] of Debug =
50 | # => debug = fn xs => "[" ++ xs->fold("", fn s, x => s ++ ", " ++ x->A.debug) ++ "]"
51 |
52 | # Show
53 |
54 | class Show =
55 | => show : Self -> Str
56 |
57 | $[util]
58 | def show A < Show : A -> Str = A.show
59 |
60 | for A < Show member [A] of Show =
61 | => show = fn xs => dispatch(
62 | xs,
63 | # Specialise on [Char]
64 | fn s => s,
65 | # Fallback for [A]
66 | fn xs => xs -> fix(fn
67 | | _, [] => ""
68 | | _, [x] => x -> show
69 | \ rec, [x .. xs] => x -> show ++ ", " ++ xs -> rec),
70 | )
71 |
72 | member Nat of Show =
73 | => show = fn x => @display_int(--x)
74 |
75 | member Int of Show =
76 | => show = fn x => @display_int(x)
77 |
78 | member Bool of Show =
79 | => show = fn
80 | | True => "true"
81 | \ False => "false"
82 |
83 | member Char of Show =
84 | => show = fn c => [c]
85 |
--------------------------------------------------------------------------------
/middle/src/repr.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 | // use hashbrown::hash_map::Entry;
3 | use std::collections::{BTreeMap, btree_map::Entry};
4 |
5 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
6 | pub enum Prim {
7 | Int,
8 | Real,
9 | Char,
10 | Bool,
11 | Universe,
12 | }
13 |
14 | #[derive(Clone, Debug, PartialEq, Eq, Hash)]
15 | pub enum Repr {
16 | Prim(Prim),
17 | List(Box),
18 | Tuple(Vec),
19 | Sum(Vec),
20 | Data(ConDataId),
21 | Func(Box, Box),
22 | Effect(Vec, Box),
23 | }
24 |
25 | pub struct Data {
26 | pub is_recursive: bool,
27 | pub repr: Repr,
28 | }
29 |
30 | #[derive(Default)]
31 | pub struct Reprs {
32 | pub datas: BTreeMap>,
33 | pub r#bool: Option,
34 | }
35 |
36 | impl Reprs {
37 | pub fn get(&self, data: ConDataId) -> &Data {
38 | self.datas
39 | .get(&data)
40 | .unwrap()
41 | .as_ref()
42 | .expect("Data declared but not defined")
43 | }
44 |
45 | pub fn iter_mut(&mut self) -> impl Iterator
- {
46 | self.datas
47 | .values_mut()
48 | .filter_map(|r| r.as_mut())
49 | }
50 |
51 | pub fn declare(&mut self, data: ConDataId) -> bool {
52 | match self.datas.entry(data) {
53 | Entry::Occupied(data) => false,
54 | Entry::Vacant(data) => {
55 | data.insert(None);
56 | true
57 | }
58 | }
59 | }
60 |
61 | pub fn define(&mut self, id: ConDataId, data: Data) {
62 | assert!(self.datas.insert(id, Some(data)).unwrap().is_none());
63 | }
64 |
65 | pub fn has_inhabitants(&self, repr: &Repr) -> bool {
66 | match repr {
67 | Repr::Prim(_) => true,
68 | Repr::List(_) => true, // Empty list
69 | Repr::Tuple(xs) => xs
70 | .iter()
71 | .all(|x| self.has_inhabitants(x)),
72 | Repr::Sum(variants) => variants
73 | .iter()
74 | .any(|v| self.has_inhabitants(v)),
75 | Repr::Data(data) => self.has_inhabitants(&self.get(*data).repr),
76 | Repr::Func(_, _) => true,
77 | Repr::Effect(_, _) => true, // Effect objects always have inhabitants
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/middle/src/error.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 | use std::io::Write;
3 |
4 | #[derive(Debug)]
5 | pub enum Error {
6 | NoEntryPoint(Span),
7 | MultipleEntryPoints(Span, Span),
8 | GenericEntryPoint(SrcNode, Span, Span),
9 | }
10 |
11 | impl Error {
12 | pub fn write>(self, ctx: &Context, cache: C, writer: impl Write) {
13 | use ariadne::{Report, ReportKind, Label, Color, Fmt, Span};
14 |
15 | let (msg, spans, notes) = match self {
16 | Error::NoEntryPoint(root_span) => (
17 | format!("No main definition"),
18 | vec![(root_span, format!("Does not contain a definition marked as the main entry point"), Color::Red)],
19 | vec![format!("Mark a definition as the main entry point with {}", "$[main]".fg(Color::Blue))],
20 | ),
21 | Error::MultipleEntryPoints(a, b) => (
22 | format!("Multiple entry points"),
23 | vec![
24 | (a, format!("First entry point is here"), Color::Red),
25 | (b, format!("Second entry point is here"), Color::Red),
26 | ],
27 | vec![format!("A program may only have a single entry point")],
28 | ),
29 | Error::GenericEntryPoint(name, gen, entry) => (
30 | format!("Entry point {} cannot be generic", (*name).fg(Color::Red)),
31 | vec![
32 | (gen, format!("Generics are not allowed here"), Color::Red),
33 | (entry, format!("Declared as an entry point because of this attribute"), Color::Yellow),
34 | ],
35 | vec![format!("A program cannot be generic over types")],
36 | ),
37 | };
38 |
39 | let mut report = Report::build(ReportKind::Error, spans.first().unwrap().0.src(), spans.first().unwrap().0.start())
40 | .with_code(3)
41 | .with_message(msg);
42 |
43 | for (span, msg, col) in spans {
44 | report = report.with_label(Label::new(span)
45 | .with_message(msg)
46 | .with_color(col));
47 | }
48 |
49 | for note in notes {
50 | report = report.with_note(note);
51 | }
52 |
53 | report
54 | .finish()
55 | .write(cache, writer)
56 | .unwrap();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/analysis/src/def.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 |
3 | pub struct Def {
4 | pub name: SrcNode,
5 | pub attr: Vec>,
6 | pub gen_scope: GenScopeId,
7 | pub ty_hint: Option,
8 | pub body: Option,
9 | }
10 |
11 | #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
12 | pub struct DefId(pub usize, Ident);
13 |
14 | impl fmt::Debug for DefId {
15 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
16 | write!(f, "{}", self.1)
17 | }
18 | }
19 |
20 | #[derive(Default)]
21 | pub struct Lang {
22 | // pub io_unit: Option,
23 | }
24 |
25 | #[derive(Default)]
26 | pub struct Defs {
27 | lut: HashMap,
28 | defs: Vec,
29 | pub lang: Lang,
30 | }
31 |
32 | impl Defs {
33 | pub fn iter(&self) -> impl Iterator
- {
34 | self.defs
35 | .iter()
36 | .enumerate()
37 | .map(|(i, d)| (DefId(i, *d.name), d))
38 | }
39 |
40 | pub fn get(&self, def: DefId) -> &Def {
41 | &self.defs[def.0]
42 | }
43 |
44 | pub fn lookup(&self, name: Ident) -> Option {
45 | self.lut.get(&name).map(|(_, id)| *id)
46 | }
47 |
48 | pub fn declare(&mut self, def: Def) -> Result {
49 | let id = DefId(self.defs.len(), *def.name);
50 | let name = *def.name;
51 | let span = def.name.span();
52 | if let Err(old) = self.lut.try_insert(name, (span, id)) {
53 | Err(Error::DuplicateDefName(name, old.entry.get().0, span))
54 | } else {
55 | if let Some(lang) = def.attr
56 | .iter()
57 | .find(|a| &**a.name == "lang")
58 | .and_then(|a| a.args.as_ref())
59 | {
60 | // if lang.iter().find(|a| &**a.name == "io_unit").is_some() {
61 | // self.lang.io_unit = Some(id);
62 | // }
63 | }
64 |
65 | self.defs.push(def);
66 | Ok(id)
67 | }
68 | }
69 |
70 | pub fn check_lang_items(&self) -> Vec {
71 | let mut errors = Vec::new();
72 |
73 | // if self.lang.io_unit.is_none() { errors.push(Error::MissingLangItem("io_unit")); }
74 |
75 | errors
76 | }
77 |
78 | pub fn define_body(&mut self, id: DefId, expr: TyExpr) {
79 | self.defs[id.0].body = Some(expr);
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/examples/adventure.tao:
--------------------------------------------------------------------------------
1 | import "../lib/main.tao"
2 |
3 | data Location =
4 | | Beach
5 | | Forest
6 | | Village
7 | \ Mountain
8 |
9 | fn describe_loc =
10 | | Beach => "You are standing on a gravel beach"
11 | | Forest => "You are in a dark, spooky forest"
12 | | Village => "You are standing in the centre of sleepy village"
13 | \ Mountain => "You are on the side of a desolate mountain"
14 |
15 | fn directions =
16 | | Beach => [("uphill", Forest)]
17 | | Forest => [("downhill", Beach), ("east", Village)]
18 | | Village => [("west", Forest), ("climb", Mountain)]
19 | \ Mountain => [("descend", Village)]
20 |
21 | data State = {
22 | loc: Location,
23 | }
24 |
25 | def default_state = State {
26 | loc: Beach,
27 | }
28 |
29 | fn describe : State -> io ~ () = state => do {
30 | print(state.loc->describe_loc)!;
31 | print("You can go...")!;
32 | state.loc
33 | -> directions
34 | -> map(fn dir => print("- " ++ dir.0)!)!;
35 | }
36 |
37 | data Event =
38 | | Grab
39 | \ MoveTo Location
40 |
41 | fn apply_event : Event -> State -> io ~ State =
42 | | Grab, state => do {
43 | print("There was nothing to grab!")!;
44 | state
45 | }
46 | \ MoveTo loc, state => do {
47 | print("You moved.")!;
48 | print(loc->describe_loc)!;
49 | state with { loc }
50 | }
51 |
52 | fn game_loop : State -> io ~ () = state => do {
53 | let cmd = input!;
54 | let res = when cmd is
55 | | "quit" => Done
56 | | "help" => do {
57 | print("Commands: 'help', 'quit', 'look', 'grab'")!;
58 | Next None
59 | }
60 | | "look" => do {
61 | state->describe!;
62 | Next None
63 | }
64 | | "grab" => Next Just Grab
65 | \ other => when state.loc
66 | -> directions
67 | -> find_first(fn (name, _) => name = cmd)
68 | is
69 | | Just (_, loc) => Next Just MoveTo loc
70 | \ None => do {
71 | print("Unknown command '" ++ cmd ++ "'")!;
72 | Next None
73 | };
74 | when res is
75 | | Next Just event => game_loop(state->apply_event(event)!)!
76 | | Next None => game_loop(state)!
77 | \ Done => ()
78 | }
79 |
80 | def main : io ~ () = do {
81 | print("Welcome to the adventure game!")!;
82 | print("Type 'help' to get started.")!;
83 | game_loop(default_state)!;
84 | print("Goodbye!")!;
85 | }
86 |
--------------------------------------------------------------------------------
/middle/src/opt/flatten_single_field.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 |
3 | /// Turns tuples with single fields into their inner value.
4 | #[derive(Default)]
5 | pub struct FlattenSingleField;
6 |
7 | #[derive(Default)]
8 | struct TupleMask { used_field: Option }
9 |
10 | impl TupleMask {
11 | fn combine_with(self, binding: &Binding) -> Result {
12 | if binding.name.is_some() {
13 | // If the whole tuple is bound, we can't produce a mask
14 | Err(())
15 | } else {
16 | match &binding.pat {
17 | Pat::Tuple(fields) => Ok(Self { used_field: fields
18 | .iter()
19 | .enumerate()
20 | .try_fold(self.used_field, |used_field, (idx, field)| if field.is_dead() {
21 | Ok(used_field)
22 | } else if used_field.map_or(true, |used_field| used_field == idx) {
23 | Ok(Some(idx))
24 | } else {
25 | Err(())
26 | })? }),
27 | Pat::Wildcard => Ok(self),
28 | _ => Err(()),
29 | }
30 | }
31 | }
32 | }
33 |
34 | impl Pass for FlattenSingleField {
35 | fn apply(&mut self, ctx: &mut Context) {
36 | fn visit(
37 | expr: &mut MirNode,
38 | ) {
39 | expr.for_children_mut(|expr| visit(expr));
40 |
41 | if let Expr::Match(pred, arms) = &mut **expr
42 | && let Expr::Tuple(preds) = &**pred
43 | && let Ok(TupleMask { used_field: Some(used_field) }) = arms
44 | .iter()
45 | .try_fold(TupleMask::default(), |mask, (binding, _)| mask.combine_with(binding))
46 | && preds.iter().enumerate().all(|(idx, p)| idx == used_field || !p.may_have_effect())
47 | {
48 | *pred = preds[used_field].clone();
49 | arms.iter_mut().for_each(|(binding, _)| *binding = match &binding.pat {
50 | Pat::Tuple(fields) => fields[used_field].clone(),
51 | Pat::Wildcard => MirNode::new(Binding::wildcard(None), pred.meta().clone()),
52 | pat => panic!("Pattern was supposed to fit mask but got {pat:?}"),
53 | });
54 | }
55 | }
56 |
57 | let proc_bodies = ctx.procs
58 | .iter()
59 | .map(|(id, proc)| (id, proc.body.clone()))
60 | .collect::>();
61 |
62 | for (_, proc) in ctx.procs.iter_mut() {
63 | visit(&mut proc.body);
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/syntax/src/node.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 | use std::{
3 | ops::{Deref, DerefMut},
4 | cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering},
5 | fmt,
6 | };
7 |
8 | #[derive(Clone)]
9 | pub struct Node {
10 | inner: Box, // TODO: Replace with smallbox or similar optimisation?
11 | meta: M,
12 | }
13 |
14 | impl Node {
15 | /// Create a new node with the given inner value and metadata.
16 | pub fn new(inner: T, meta: M) -> Self {
17 | Node { inner: Box::new(inner), meta }
18 | }
19 |
20 | /// Get a reference to the inner value.
21 | pub fn inner(&self) -> &T {
22 | &self.inner
23 | }
24 |
25 | /// Get a mutable to the inner value.
26 | pub fn inner_mut(&mut self) -> &mut T {
27 | &mut self.inner
28 | }
29 |
30 | // Take the node's inner value.
31 | pub fn into_inner(self) -> T { *self.inner }
32 |
33 | /// Map the node's inner value.
34 | pub fn map U>(self, f: F) -> Node {
35 | Node {
36 | inner: Box::new(f(*self.inner)),
37 | meta: self.meta,
38 | }
39 | }
40 |
41 | /// Get a reference to the metadata.
42 | pub fn meta(&self) -> &M { &self.meta }
43 |
44 | /// Get a mutable reference to the metadata.
45 | pub fn meta_mut(&mut self) -> &mut M { &mut self.meta }
46 |
47 | pub fn as_mut(&mut self) -> (&mut T, &mut M) { (&mut self.inner, &mut self.meta) }
48 | }
49 |
50 | impl Deref for Node {
51 | type Target = T;
52 | fn deref(&self) -> &Self::Target { self.inner() }
53 | }
54 |
55 | impl DerefMut for Node {
56 | fn deref_mut(&mut self) -> &mut Self::Target { self.inner_mut() }
57 | }
58 |
59 | impl PartialEq for Node {
60 | fn eq(&self, other: &Self) -> bool {
61 | // Only compare inner
62 | self.inner == other.inner
63 | }
64 | }
65 |
66 | impl Eq for Node {}
67 |
68 | impl Ord for Node {
69 | fn cmp(&self, other: &Self) -> Ordering {
70 | self.inner.cmp(&other.inner)
71 | }
72 | }
73 |
74 | impl PartialOrd for Node {
75 | fn partial_cmp(&self, other: &Self) -> Option {
76 | self.inner.partial_cmp(&other.inner)
77 | }
78 | }
79 |
80 | impl fmt::Debug for Node {
81 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
82 | write!(f, "{:#?} @ {:?}", self.inner, self.meta)
83 | }
84 | }
85 |
86 | pub type SrcNode = Node;
87 |
88 | impl SrcNode {
89 | pub fn span(&self) -> Span { self.meta }
90 | }
91 |
--------------------------------------------------------------------------------
/analysis/src/debug.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 |
3 | impl Context {
4 | pub fn generate_call_graph(&self) -> CallGraph<'_> {
5 | CallGraph { ctx: self }
6 | }
7 | }
8 |
9 | pub struct CallGraph<'a> {
10 | ctx: &'a Context,
11 | }
12 |
13 | impl<'a> CallGraph<'a> {
14 | pub fn render_to(&self, mut w: impl std::io::Write) -> std::io::Result<()> {
15 | dot::render(self, &mut w)
16 | }
17 | }
18 |
19 | type Node = (DefId, Ident);
20 | type Edge = (Node, Node);
21 |
22 | impl<'hir, 'a> dot::Labeller<'a, Node, Edge> for CallGraph<'hir> {
23 | fn graph_id(&'a self) -> dot::Id<'a> {
24 | dot::Id::new("Program").unwrap()
25 | }
26 |
27 | fn node_id(&'a self, (id, _): &Node) -> dot::Id<'a> {
28 | dot::Id::new(format!("def_{}", id.0)).unwrap()
29 | }
30 | fn node_label<'b>(&'b self, (_, name): &Node) -> dot::LabelText<'b> {
31 | dot::LabelText::LabelStr(name.to_string().into())
32 | }
33 | fn edge_label<'b>(&'b self, _: &Edge) -> dot::LabelText<'b> {
34 | dot::LabelText::LabelStr("".into())
35 | }
36 | }
37 |
38 | impl<'hir, 'a> dot::GraphWalk<'a, Node, Edge> for CallGraph<'hir> {
39 | fn nodes(&'a self) -> dot::Nodes<'a, Node> {
40 | self.ctx.defs
41 | .iter()
42 | .filter_map(|(id, def)| if def.attr.iter().find(|a| &**a.name == "util").is_none() {
43 | Some((id, *def.name))
44 | } else {
45 | None
46 | })
47 | .collect()
48 | }
49 | fn edges(&'a self) -> dot::Edges<'a, Edge> {
50 | fn fetch_edge(ctx: &Context, parent: Node, expr: &TyExpr, edges: &mut HashSet) {
51 | if let hir::Expr::Global(global) = &**expr {
52 | let def = ctx.defs.get(global.0);
53 | if def.attr.iter().find(|a| &**a.name == "util").is_none() {
54 | edges.insert((
55 | parent,
56 | (global.0, *def.name),
57 | ));
58 | }
59 | }
60 |
61 | expr.for_children(|e| fetch_edge(ctx, parent, e, edges));
62 | }
63 |
64 | let mut edges = HashSet::new();
65 | for (id, def) in self.ctx.defs.iter() {
66 | if def.attr.iter().find(|a| &**a.name == "util").is_none() {
67 | let parent = (id, *def.name);
68 | fetch_edge(self.ctx, parent, def.body.as_ref().unwrap(), &mut edges);
69 | }
70 | }
71 | edges.into_iter().collect()
72 | }
73 | fn source(&self, e: &Edge) -> Node { e.0 }
74 | fn target(&self, e: &Edge) -> Node { e.1 }
75 | }
76 |
--------------------------------------------------------------------------------
/middle/src/opt/simplify_arithmetic.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 |
3 | /// Simplify arithmetic expressions where possible.
4 | #[derive(Default)]
5 | pub struct SimplifyArithmetic;
6 |
7 | impl Pass for SimplifyArithmetic {
8 | fn apply(&mut self, ctx: &mut Context) {
9 | fn visit(
10 | expr: &mut Expr,
11 | ) {
12 | // Visit children first to maximally simplify the inner expression
13 | expr.for_children_mut(|expr| visit(expr));
14 |
15 | // Simplify `- - x` to `x`
16 | if let Expr::Intrinsic(Intrinsic::NegInt, args) = expr
17 | && let [arg] = args.as_slice()
18 | && let Expr::Intrinsic(Intrinsic::NegInt, args) = &**arg
19 | && let [arg] = args.as_slice()
20 | {
21 | *expr = (**arg).clone();
22 | }
23 |
24 | // Simplify `a + - b` to `a - b`
25 | if let Expr::Intrinsic(intr @ Intrinsic::AddInt, args) = expr
26 | && let [a, b] = args.as_mut_slice()
27 | && let Expr::Intrinsic(Intrinsic::NegInt, b2) = &**b
28 | && let [b2] = b2.as_slice()
29 | {
30 | *intr = Intrinsic::SubInt;
31 | *b = b2.clone();
32 | }
33 |
34 | // Simplify `a * 1` and `1 * a` to `a`
35 | if let Expr::Intrinsic(Intrinsic::MulInt, args) = expr
36 | && let [a, b] = args.as_slice()
37 | && let (x, Expr::Literal(Literal::Int(1)))
38 | | (Expr::Literal(Literal::Int(1)), x) = (&**a, &**b)
39 | {
40 | *expr = x.clone();
41 | }
42 |
43 | // Simplify `a * 0` and `0 * a` to `a`
44 | if let Expr::Intrinsic(Intrinsic::MulInt, args) = expr
45 | && let [a, b] = args.as_slice()
46 | && let (x, Expr::Literal(Literal::Int(0)))
47 | | (Expr::Literal(Literal::Int(0)), x) = (&**a, &**b)
48 | {
49 | *expr = Expr::Literal(Literal::Int(0));
50 | }
51 |
52 | // Simplify `a + 0` and `0 + a` to `a`
53 | if let Expr::Intrinsic(Intrinsic::AddInt, args) = expr
54 | && let [a, b] = args.as_slice()
55 | && let (x, Expr::Literal(Literal::Int(0)))
56 | | (Expr::Literal(Literal::Int(0)), x) = (&**a, &**b)
57 | {
58 | *expr = x.clone();
59 | }
60 | }
61 |
62 | let proc_bodies = ctx.procs
63 | .iter()
64 | .map(|(id, proc)| (id, proc.body.clone()))
65 | .collect::>();
66 |
67 | for (_, proc) in ctx.procs.iter_mut() {
68 | visit(&mut proc.body);
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/lib/std/math.tao:
--------------------------------------------------------------------------------
1 | #! Arithmetic operations.
2 |
3 | import "../core/ops.tao"
4 |
5 | class Zero =
6 | => zero : Self
7 |
8 | $[util]
9 | def zero A < Zero : A = A.zero
10 |
11 | member Nat of Zero =
12 | => zero = 0
13 |
14 | class One =
15 | => one : Self
16 |
17 | $[util]
18 | def one A < One : A = A.one
19 |
20 | member Nat of One =
21 | => one = 1
22 |
23 | class Sum where
24 | Self < Zero + Add Self with { Output = Self }
25 |
26 | for A member A of Sum where
27 | A < Zero + Add A with { Output = A }
28 |
29 | class Product where
30 | Self < One + Mul Self with { Output = Self }
31 |
32 | for A member A of Product where
33 | A < One + Mul A with { Output = A }
34 |
35 | class Num where
36 | Self < Sum + Product,
37 |
38 | for A member A of Num where
39 | A < Sum + Product
40 |
41 | # A generic vector type
42 |
43 | class VecData A =
44 | => Data
45 | => splat : A -> Self.Data
46 | => map : Self.Data -> (A -> A) -> Self.Data
47 | => map2 : Self.Data -> Self.Data -> (A -> A -> A) -> Self.Data
48 |
49 | data Vec A, N < VecData A = N.Data
50 |
51 | for A, N member Vec A N of Add (Vec A N) where
52 | N < VecData A,
53 | A < Add A with { Output = A },
54 | =
55 | => Output = Self
56 | => add = fn Vec xs, Vec ys => Vec N.map2(xs, ys, fn x, y => x + y)
57 |
58 | #for A, B, N member Vec A N of Add B where
59 | # N < VecData A,
60 | # A < Add B with { Output = A },
61 | #=
62 | # => Output = Self
63 | # => add = fn Vec xs, y => Vec N.map(xs, fn x => x + y)
64 |
65 | for A, N member Vec A N of Mul (Vec A N) where
66 | N < VecData A,
67 | A < Mul A with { Output = A },
68 | =
69 | => Output = Self
70 | => mul = fn Vec xs, Vec ys => Vec N.map2(xs, ys, fn x, y => x * y)
71 |
72 | data N2
73 | for A member N2 of VecData A =
74 | => Data = { x : A, y : A }
75 | => splat = fn e => { x : e, y : e }
76 | => map = fn xs, f => { x : f(xs.x), y : f(xs.y) }
77 | => map2 = fn xs, ys, f => { x : f(xs.x, ys.x), y : f(xs.y, ys.y) }
78 |
79 | data N3
80 | for A member N3 of VecData A =
81 | => Data = { x : A, y : A, z : A }
82 | => splat = fn e => { x : e, y : e, z : e }
83 | => map = fn xs, f => { x : f(xs.x), y : f(xs.y), z : f(xs.z) }
84 | => map2 = fn xs, ys, f => { x : f(xs.x, ys.x), y : f(xs.y, ys.y), z : f(xs.z, ys.z) }
85 |
86 | $[util]
87 | fn splat A, N < VecData A : A -> Vec A N =
88 | e => Vec N.splat(e)
89 |
90 | for A < Zero, N < VecData A member Vec A N of Zero =
91 | => zero = splat(A.zero)
92 |
93 | for A < One, N < VecData A member Vec A N of One =
94 | => one = splat(A.one)
95 |
96 | $[util]
97 | fn mul_add A < Num : A -> A -> A -> A =
98 | x, y, z => x * y + z
99 |
100 | #def mul_add_demo : Vec Nat N3 = mul_add(zero, one, one)
101 |
--------------------------------------------------------------------------------
/test_cases/99.tao:
--------------------------------------------------------------------------------
1 | import "../lib/std.tao"
2 |
3 | ## 1. Find the last element of a list
4 | fn last A : [A] -> Maybe A =
5 | | [] => None
6 | | [x] => Just x
7 | \ [_ .. xs] => xs->last
8 |
9 | ## 2. Find the last but one element of a list
10 | fn last_but_one A : [A] -> Maybe A =
11 | | [] => None
12 | | [_] => None
13 | | [x, _] => Just x
14 | \ [.. xs] => xs->last_but_one
15 |
16 | ## 3. Find the k'th element of a list
17 | fn kth A : Nat -> [A] -> Maybe A =
18 | | 0, [x ..] => Just x
19 | | n + 1, [.. xs] => xs->kth(n)
20 | \ _, _ => None
21 |
22 | ## 4. Find the number of elements of a list
23 | fn length A : [A] -> Nat =
24 | | [] => 0
25 | \ [.. xs] => 1 + xs->length
26 |
27 | ## 5. Reverse a list
28 | fn reverse A : [A] -> [A] =
29 | | [] => []
30 | \ [x .. xs] => xs->reverse ++ [x]
31 |
32 | ## 6. Find out whether a list is a palindrome
33 | fn is_palindrome A < Eq : [A] -> Bool =
34 | xs => xs = xs->reverse
35 |
36 | ## 7. Flatten a nested list structure
37 | fn flatten A : [[A]] -> [A] =
38 | | [] => []
39 | \ [x .. xs] => x ++ xs->flatten
40 |
41 | ## 8. Eliminate consecutive duplicates of list elements
42 | fn dedup A < Eq : [A] -> [A] =
43 | | [x .. tail ~ [y .. xs]] => (if x->eq(y) then [] else [x]) ++ tail->dedup
44 | \ xs => xs
45 |
46 | ## 9. Pack consecutive duplicates of list elements into sublists
47 | fn pack A < Eq : [A] -> [[A]] =
48 | | [x .. xs] =>
49 | let monch = fix(fn monch => fn
50 | | p, xs ~ [x .. tail] => if x->eq(p)
51 | then let (ps, tail) = monch(p, tail) in ([x] ++ ps, tail)
52 | else ([], xs)
53 | \ _, [] => ([], [])
54 | ) in
55 | let (ps, xs) = monch(x, xs) in [[x] ++ ps .. xs->pack]
56 | \ [] => []
57 |
58 | ## 10. Run-length encoding of a list
59 | fn rle A < Eq : [A] -> [(Nat, A)] =
60 | | [x .. xs] =>
61 | let monch = fix(fn monch => fn
62 | | p, xs ~ [x .. tail] => if x->eq(p)
63 | then let (tail, l) = monch(p, tail) in (tail, 1 + l)
64 | else (xs, 0)
65 | \ _, [] => ([], 0)
66 | ) in
67 | let (xs, l) = monch(x, xs) in [(1 + l, x) .. xs->rle]
68 | \ [] => []
69 |
70 | data Element A =
71 | | Multiple (Nat, A)
72 | \ Single A
73 |
74 | ## 11. Modified run-length encoding of a list
75 | fn rle_modified A < Eq : [A] -> [Element A] =
76 | | [x .. xs] =>
77 | let monch = fix(fn monch => fn
78 | | p, xs ~ [x .. tail] => if x->eq(p)
79 | then let (l, tail) = monch(p, tail) in (1 + l, tail)
80 | else (0, xs)
81 | \ _, [] => (0, [])
82 | ) in
83 | let (l, xs) = monch(x, xs) in
84 | let elem = when l is
85 | | 0 => Single x
86 | \ l => Multiple (1 + l, x)
87 | in
88 | [elem .. xs->rle_modified]
89 | \ [] => []
90 |
91 | ## 11. Decode a modified run-length encoded list
92 | fn rle_decode A : [Element A] -> [A] =
93 | | [] => []
94 | \ [x .. xs] => when x is
95 | | Multiple (n, x) => [x]->repeat(n) ++ xs->rle_decode
96 | \ Single x => [x .. xs->rle_decode]
97 |
98 | def main = print("Hello, world!")
99 |
--------------------------------------------------------------------------------
/compiler/tests/tester.rs:
--------------------------------------------------------------------------------
1 | macro_rules! test {
2 | ($x:ident) => {
3 | #[test]
4 | fn $x() {
5 | test_configs(stringify!($x))
6 | }
7 | };
8 | }
9 |
10 | test!(math);
11 | test!(lists);
12 | test!(records);
13 |
14 | use tao::{Options, OptMode, SrcId, run};
15 | use std::fs;
16 |
17 | fn test_configs(name: &str) {
18 | fn test_config(name: &str, options: Options) {
19 | let path = format!("tests/{}.tao", name);
20 | let src = fs::read_to_string(&path).unwrap();
21 | let src_id = SrcId::from_path(&path);
22 |
23 | let mut input = String::new();
24 | let mut expected = String::new();
25 |
26 | #[derive(Debug)]
27 | enum State {
28 | Start,
29 | Input,
30 | Output,
31 | }
32 |
33 | let mut state = State::Start;
34 | for line in src.lines().chain(std::iter::once("# >>>> END")) {
35 | match &state {
36 | State::Start | State::Output => if line.trim() == "# >>>> INPUT" || line.trim() == "# >>>> END" {
37 | if let State::Output = &state {
38 | let mut output = Vec::new();
39 | run(
40 | input.clone(),
41 | src_id,
42 | options.clone(),
43 | &mut output,
44 | |src| fs::read_to_string(src.to_path()).ok(),
45 | |parent, rel| {
46 | let mut path = parent.to_path();
47 | path.pop();
48 | path.push(rel);
49 | let path = path.canonicalize().ok()?;
50 | Some(SrcId::from_path(path))
51 | },
52 | );
53 | let output = String::from_utf8(output).unwrap();
54 | if output.trim() != expected.trim() {
55 | panic!("\n\n \
56 | ========[ EXPECTED OUTPUT ]========\n\n\
57 | {}\n \
58 | ========[ FOUND OUTPUT ]========\n\n\
59 | {}\n", expected, output);
60 | }
61 |
62 | input.clear();
63 | expected.clear();
64 | }
65 | state = State::Input;
66 | } else {
67 | expected += line;
68 | expected += "\n";
69 | },
70 | State::Input => if line.trim() == "# >>>> OUTPUT" {
71 | state = State::Output;
72 | } else {
73 | input += line;
74 | input += "\n";
75 | },
76 | }
77 | }
78 | }
79 |
80 | let mut options = Options {
81 | debug: Vec::new(),
82 | opt: OptMode::None,
83 | };
84 | options.opt = OptMode::None;
85 | test_config(name, options.clone());
86 | options.opt = OptMode::Fast;
87 | test_config(name, options.clone());
88 | }
89 |
--------------------------------------------------------------------------------
/examples/quickcheck.tao:
--------------------------------------------------------------------------------
1 | import "../lib/main.tao"
2 |
3 | # Allows generating arbitrary values of a type
4 | class Arbitrary =
5 | => gen : rand ~ Self
6 |
7 | def arbitrary A < Arbitrary : rand ~ A = A.gen!
8 |
9 | member Nat of Arbitrary =
10 | => gen = rand(1000)!
11 |
12 | member Bool of Arbitrary =
13 | => gen = rand_bool!
14 |
15 | for A < Arbitrary, B < Arbitrary member (A, B) of Arbitrary =
16 | => gen = (arbitrary!, arbitrary!)
17 |
18 | for A < Arbitrary member [A] of Arbitrary =
19 | => gen = range(0, rand(100)!)
20 | -> map(fn _ => arbitrary!)
21 | -> collect!
22 |
23 | # Allows values of a type to be minimised
24 | class Minimise =
25 | => minimise : Self -> rand ~ Maybe Self
26 |
27 | def minimise A < Minimise : A -> rand ~ Maybe A = A.minimise
28 |
29 | member Nat of Minimise =
30 | => minimise = fn
31 | | 0 => None
32 | \ n => Just rand(n)!
33 |
34 | member Bool of Minimise =
35 | => minimise = fn x => Just !x
36 |
37 | for A < Minimise, B < Minimise member (A, B) of Minimise =
38 | => minimise = fn (a, b) => when a->minimise!, b->minimise! is
39 | | None, None => None
40 | \ a_min, b_min => Just (
41 | if rand_bool! then a_min->unwrap_or(a) else a,
42 | if rand_bool! then b_min->unwrap_or(b) else b,
43 | )
44 |
45 | for A < Minimise member [A] of Minimise =
46 | => minimise = fn
47 | | [] => None
48 | \ [x .. xs] => Just (when rand(1 + xs->len)! is
49 | | 0 => []
50 | | 1 => when x->minimise! is
51 | | None => []
52 | \ Just x => [x]
53 | \ _ => [x]) ++ when xs->minimise! is
54 | | None => []
55 | \ Just xs => xs
56 |
57 | fn quickcheck A < Arbitrary + Minimise, B : (A -> B) -> (A -> B -> Bool) -> rand ~ Maybe A =
58 | # Generate 100 arbitrary values
59 | f, check => range(0, 100)
60 | -> map(fn _ => arbitrary!)
61 | -> collect_list!
62 | # Find the first value that fails to pass the check
63 | -> find_first(fn x => !x->f->check(x))
64 | # Try 100 times to minimise the value
65 | -> flat_map(fn x => Just range(0, 100)
66 | -> collect_list
67 | -> fold(x, fn x, _ => x
68 | -> minimise!
69 | # Only take minimised values that fail to pass the check
70 | -> filter_opt(fn x => !x->f->check(x))
71 | -> unwrap_or(x))!)!
72 |
73 | # This reverse function is broken and fails to reverse lists > 3 items long!
74 | fn reverse A : [A] -> [A] =
75 | | [] => []
76 | | [a] => [a]
77 | | [a, b] => [b, a]
78 | | [a, b, c] => [c, b, a]
79 | \ [a, b .. xs] => xs->reverse ++ [a, b]
80 |
81 | def main : io ~ () =
82 | # Here we try to generate a simple test-case for which the reverse function fails
83 | let xs = quickcheck(
84 | # Show quickcheck how to transform an arbitrary list
85 | # You can change the type hint to `[Nat]`: everything still works!
86 | fn xs : [Bool] => xs->reverse,
87 | # Check the validity of the output (i.e: that reversing the reversed list yields the original list)
88 | fn old, new => new->reverse = old,
89 | )! in
90 | print("Failed case: " + xs->debug)!
91 |
--------------------------------------------------------------------------------
/middle/src/opt/remove_identity_branches.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 |
3 | /// Eliminate branches that emit their predicate exactly in all arms.
4 | #[derive(Default)]
5 | pub struct RemoveIdentityBranches;
6 |
7 | fn is_identity_literal(binding: &Binding, litr: &Literal) -> bool {
8 | match (&binding.pat, litr) {
9 | // Since `Never` can never exist, `litr` might as well be an identity coercion of `binding`
10 | (_, Literal::Never) => true,
11 | (Pat::Literal(x), y) => x == y,
12 | (Pat::Tuple(xs), Literal::Tuple(ys)) if xs.len() == ys.len() => xs.iter()
13 | .zip(ys.iter())
14 | .all(|(x, y)| is_identity_literal(x, y)),
15 | (Pat::ListExact(xs), Literal::List(ys)) if xs.len() == ys.len() => xs.iter()
16 | .zip(ys.iter())
17 | .all(|(x, y)| is_identity_literal(x, y)),
18 | (Pat::Variant(x_variant, x), Literal::Sum(y_variant, y)) => x_variant == y_variant && is_identity_literal(x, y),
19 | (Pat::Data(x_data, x), Literal::Data(y_data, y)) => x_data == y_data && is_identity_literal(x, y),
20 | _ => false,
21 | }
22 | }
23 |
24 | fn is_identity(binding: &Binding, expr: &Expr) -> bool {
25 | match (&binding.pat, expr) {
26 | (_, Expr::Local(local)) if binding.name == Some(*local) => true,
27 | // TODO: Remember `binding.name` so that it can be used by the inner call to catch `x ~ y => x`?
28 | (Pat::Single(binding), y) => is_identity(binding, y),
29 | (Pat::Add(x, n), y) => if let Expr::Intrinsic(Intrinsic::AddInt, args) = y
30 | && let [lhs, rhs] = args.as_slice()
31 | && let (other, Expr::Literal(m)) | (Expr::Literal(m), other) = (&**lhs, &**rhs)
32 | && is_identity(x, other) && Literal::Int(*n as i64) == *m
33 | {
34 | true
35 | } else {
36 | false
37 | },
38 | (Pat::Tuple(xs), Expr::Tuple(ys)) if xs.len() == ys.len() => xs.iter()
39 | .zip(ys.iter())
40 | .all(|(x, y)| is_identity(x, y)),
41 | (Pat::ListExact(xs), Expr::List(ys)) if xs.len() == ys.len() => xs.iter()
42 | .zip(ys.iter())
43 | .all(|(x, y)| is_identity(x, y)),
44 | (Pat::Variant(x_variant, x), Expr::Variant(y_variant, y)) => x_variant == y_variant && is_identity(x, y),
45 | (Pat::Data(x_data, x), Expr::Data(y_data, y)) => x_data == y_data && is_identity(x, y),
46 | (_, Expr::Literal(y)) => is_identity_literal(binding, y),
47 | _ => false,
48 | }
49 | }
50 |
51 | impl Pass for RemoveIdentityBranches {
52 | fn apply(&mut self, ctx: &mut Context) {
53 | fn visit(
54 | expr: &mut MirNode,
55 | ) {
56 | expr.for_children_mut(|expr| visit(expr));
57 |
58 | if let Expr::Match(pred, arms) = &mut **expr
59 | && arms
60 | .iter()
61 | .all(|(binding, arm)| is_identity(binding, arm))
62 | {
63 | **expr = (**pred).clone();
64 | }
65 | }
66 |
67 | let proc_bodies = ctx.procs
68 | .iter()
69 | .map(|(id, proc)| (id, proc.body.clone()))
70 | .collect::>();
71 |
72 | for (_, proc) in ctx.procs.iter_mut() {
73 | visit(&mut proc.body);
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/examples/brainfuck.tao:
--------------------------------------------------------------------------------
1 | import "../lib/main.tao"
2 |
3 | data Instr =
4 | | Left
5 | | Right
6 | | Incr
7 | | Decr
8 | | Read
9 | | Write
10 | \ Loop [Instr]
11 |
12 | fn parse_bf =
13 | | ['<' .. s] => let (xs, s) = s->parse_bf in ([Left .. xs], s)
14 | | ['>' .. s] => let (xs, s) = s->parse_bf in ([Right .. xs], s)
15 | | ['+' .. s] => let (xs, s) = s->parse_bf in ([Incr .. xs], s)
16 | | ['-' .. s] => let (xs, s) = s->parse_bf in ([Decr .. xs], s)
17 | | [',' .. s] => let (xs, s) = s->parse_bf in ([Read .. xs], s)
18 | | ['.' .. s] => let (xs, s) = s->parse_bf in ([Write .. xs], s)
19 | | [']' .. s] => ([], s)
20 | | ['[' .. s] =>
21 | let (xs, s) = s->parse_bf in
22 | let (tail, s) = s->parse_bf in
23 | ([Loop xs] ++ tail, s)
24 | | [_ .. s] => s->parse_bf
25 | \ [] => ([], "")
26 |
27 | data State = {
28 | ptr: Nat,
29 | tape: [Nat],
30 | stdin: Str,
31 | stdout: Str,
32 | }
33 |
34 | member State of Default =
35 | => default = State {
36 | ptr: 0,
37 | tape: [0]->repeat(1000),
38 | stdin: "",
39 | stdout: "",
40 | }
41 |
42 | fn exec : State -> Instr -> io ~ State =
43 | | state, Left => state with {
44 | ptr: when state.ptr is
45 | | 0 => 9999
46 | \ l + 1 => l
47 | }
48 | | state, Right => state with {
49 | ptr: when state.ptr is
50 | | 9999 => 0
51 | \ l => l + 1
52 | }
53 | | state, Incr => state with {
54 | tape: state.tape->swap(state.ptr, fn
55 | | 255 => 0
56 | \ n => n + 1)
57 | }
58 | | state, Decr => state with {
59 | tape: state.tape->swap(state.ptr, fn
60 | | 0 => 255
61 | \ n + 1 => n)
62 | }
63 | | state, Read => do {
64 | print("Read not supported yet")!;
65 | state
66 | }
67 | | state, Write => when state.tape->nth(state.ptr) is
68 | # Newline
69 | | Just 10 => do {
70 | print(state.stdout)!;
71 | state with { stdout: "" }
72 | }
73 | | Just n + 32 => when " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"->nth(n) is
74 | | Just c => state with { stdout: state.stdout ++ [c] }
75 | \ None => state
76 | \ _ => state
77 | \ state, Loop xs => when state.tape->nth(state.ptr) is
78 | | Just 0 => state
79 | | Just _ => do {
80 | let state = exec_all(state, xs)!;
81 | exec(state, Loop xs)!
82 | }
83 | \ None => state
84 |
85 | fn exec_all : State -> [Instr] -> io ~ State =
86 | | state, [] => state
87 | \ state, [x .. xs] => do {
88 | let state = exec(state, x)!;
89 | exec_all(state, xs)!
90 | }
91 |
92 | def main : io ~ () = do {
93 | let (xs, _) = "
94 | [sierpinski.b -- display Sierpinski triangle
95 | (c) 2016 Daniel B. Cristofani
96 | http://brainfuck.org/]
97 | ++++++++[>+>++++<<-]>++>>+<[-[>>+<<-]+>>]>+[
98 | -<<<[
99 | ->[+[-]+>++>>>-<<]<[<]>>++++++[<<+++++>>-]+<<++.[-]<<
100 | ]>.>+[>>]>+
101 | ]
102 | [Shows an ASCII representation of the Sierpinski triangle
103 | (iteration 5).]
104 | "->parse_bf;
105 | exec_all(default, xs)!;
106 | }
107 |
--------------------------------------------------------------------------------
/lib/std/stream.tao:
--------------------------------------------------------------------------------
1 | #! Streams and stream combinators.
2 | #!
3 | #! In other languages, streams are often called 'iterators'.
4 |
5 | import "value.tao"
6 |
7 | class Stream A, e =
8 | => next : Self -> Maybe (e ~ A, Self)
9 |
10 | for A, e member Range A of Stream A e where
11 | A < Ord + Add A with { Output = A } + One
12 | =
13 | => next = fn Range (from, to) => if from >= to
14 | then None
15 | else
16 | let one : A = one in # TODO: This line is silly, figure out why we can't infer this
17 | Just (from, Range (from + one, to))
18 |
19 | data Stream S = S
20 |
21 | data Mapped S, A, B, f = (A -> f ~ B, S)
22 |
23 | for S < Stream A f, A, B, f member Mapped S A B f of Stream B f =
24 | => next = fn Mapped (f, s) =>
25 | s->S.next->map(fn (x, s) => (effect { x!->f! }, Mapped (f, s)))
26 | # when s->S.next is
27 | # | None => None
28 | # \ Just (x, s) => Just (effect { x!->f! }, Mapped (f, s))
29 |
30 | for S, A, B, f member Stream S of Map A B f where
31 | S < Stream A f
32 | =
33 | => Output = Stream (Mapped S A B f)
34 | => map = fn f, Stream s => Stream Mapped (f, s)
35 |
36 | class ToStream A, e where
37 | Self.Stream < Stream A e
38 | =
39 | => Stream
40 | => to_stream : Self -> Self.Stream
41 |
42 | for A, e member [e ~ A] of ToStream A e =
43 | => Stream = [e ~ A]
44 | => to_stream = fn xs => xs
45 |
46 | for S < Stream A e, A, e member Stream S of ToStream A e =
47 | => Stream = S
48 | => to_stream = fn Stream s => s
49 |
50 | $[util]
51 | fn to_stream S < ToStream A e, A, e : S -> Stream S.Stream =
52 | \ s => Stream s->S.to_stream
53 |
54 | for A, e member Maybe e ~ A of Stream A e =
55 | => next = fn
56 | | None => None
57 | \ Just x => Just (x, None)
58 |
59 | for A, e member [e ~ A] of Stream A e =
60 | => next = fn
61 | | [] => None
62 | \ [x .. xs] => Just (x, xs)
63 |
64 | class FromStream A =
65 | => from_stream : [A] -> Self
66 |
67 | for A member [A] of FromStream A =
68 | => from_stream = fn xs => xs
69 |
70 | for A member Maybe A of FromStream A =
71 | => from_stream = fn
72 | | [] => None
73 | \ [x ..] => Just x
74 |
75 | member () of FromStream () =
76 | => from_stream = fn _ => ()
77 |
78 | $[util]
79 | fn collect S < Stream A e, A, B < FromStream A, e : Stream S -> e ~ B =
80 | Stream s => let xs = s->fix(fn rec, s => when s->S.next is
81 | | None => []
82 | \ Just (x, s) => [x! .. rec(s)!])! in xs->B.from_stream
83 |
84 | # $[util]
85 | # fn collect S < ToStream A e, A, B < FromStream A, e : S -> e ~ B =
86 | # s => let xs = s->S.to_stream->fix(fn rec, s => when s->.next is
87 | # | None => []
88 | # \ Just (x, s) => [x! .. rec(s)!])! in xs->B.from_stream
89 |
90 | $[util]
91 | fn collect_sum S < Stream A e, A, B < Zero + Add A with { Output = B }, e : Stream S -> e ~ B =
92 | Stream s => s->fix(fn rec, s => when s->S.next is
93 | | None => B.zero
94 | \ Just (x, s) => let x = x! in rec(s)! + x)!
95 |
96 | $[util]
97 | fn collect_list S < Stream A e, A, e : Stream S -> e ~ [A] =
98 | Stream s => s->fix(fn rec, s => when s->S.next is
99 | | None => []
100 | \ Just (x, s) => [x! .. rec(s)!])!
101 |
102 | data Range A = (A, A)
103 |
104 | $[util]
105 | fn range A : A -> A -> Stream (Range A) = from, to => Stream Range (from, to)
106 |
--------------------------------------------------------------------------------
/middle/src/context.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 |
3 | use std::{fmt, str::FromStr};
4 |
5 | pub struct Context {
6 | pub reprs: Reprs,
7 | pub procs: Procs,
8 | pub entry: Option,
9 | }
10 |
11 | impl Context {
12 | pub fn from_concrete(hir: &HirContext, con: &ConContext) -> Self {
13 | let mut this = Self {
14 | reprs: Reprs::default(),
15 | procs: Procs::default(),
16 | entry: None,
17 | };
18 |
19 | // Find special compiler types
20 | // TODO: Quite hacky
21 | this.reprs.r#bool = con.r#bool;
22 |
23 | this.entry = Some(Lowerer {
24 | ctx: &mut this,
25 | hir,
26 | con,
27 | lower_stack: Vec::new(),
28 | }.lower_proc(con.entry_proc()));
29 |
30 | this
31 | }
32 |
33 | pub fn optimize(&mut self, opt_mode: OptMode) {
34 | if matches!(opt_mode, OptMode::None) {
35 | return;
36 | }
37 |
38 | let debug_before = false;
39 | let debug = false;
40 |
41 | if debug_before || debug {
42 | println!("\nMIR before optimisation:\n\n");
43 | for (id, proc) in self.procs.iter() {
44 | println!("PROCEDURE {:?}\n\n{}\n", id, proc.body.print());
45 | }
46 | println!("\n======\n");
47 | }
48 |
49 | for _ in 0..4 {
50 | opt::Inline::create(opt_mode).run(self, debug);
51 | opt::CommuteBranches::create(opt_mode).run(self, debug);
52 | opt::FlattenSingleField::create(opt_mode).run(self, debug);
53 | opt::ConstFold::create(opt_mode).run(self, debug);
54 | opt::RemoveUnusedBindings::create(opt_mode).run(self, debug);
55 | opt::RemoveDeadProc::create(opt_mode).run(self, debug);
56 | opt::SimplifyArithmetic::create(opt_mode).run(self, debug);
57 | opt::RemoveIdentityBranches::create(opt_mode).run(self, debug);
58 | }
59 | }
60 |
61 | fn reachable_procs_from(&self, proc: ProcId, globals: &mut BTreeSet) {
62 | globals.insert(proc);
63 |
64 | let required = self.procs.get(proc).unwrap().body.required_globals();
65 | for req in required {
66 | if globals.insert(req) {
67 | self.reachable_procs_from(req, globals);
68 | }
69 | }
70 | }
71 |
72 | pub fn reachable_procs(&self) -> BTreeSet {
73 | let mut globals = BTreeSet::new();
74 |
75 | if let Some(entry) = self.entry {
76 | self.reachable_procs_from(entry, &mut globals);
77 | }
78 |
79 | globals
80 | }
81 | }
82 |
83 | #[derive(Copy, Clone, Debug)]
84 | pub enum OptMode {
85 | None,
86 | Size,
87 | Fast,
88 | }
89 |
90 | impl Default for OptMode {
91 | fn default() -> Self {
92 | Self::None
93 | }
94 | }
95 |
96 | impl FromStr for OptMode {
97 | type Err = &'static str;
98 |
99 | fn from_str(s: &str) -> Result {
100 | match s {
101 | "none" => Ok(OptMode::None),
102 | "fast" => Ok(OptMode::Fast),
103 | "size" => Ok(OptMode::Size),
104 | _ => Err("Optimisation mode does not exist"),
105 | }
106 | }
107 | }
108 |
109 | impl fmt::Display for OptMode {
110 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
111 | match self {
112 | OptMode::None => write!(f, "none"),
113 | OptMode::Fast => write!(f, "fast"),
114 | OptMode::Size => write!(f, "size"),
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/test_cases/cipher.tao:
--------------------------------------------------------------------------------
1 | import "../lib/main.tao"
2 |
3 | data OutOfRange = Char
4 |
5 | member OutOfRange of Show =
6 | => show = fn OutOfRange c => "character '" ++ [c] ++ "' is out of range!"
7 |
8 | data Character =
9 | | A
10 | | B
11 | | C
12 | | D
13 | | E
14 | | F
15 | | G
16 | | H
17 | | I
18 | | J
19 | | K
20 | | L
21 | | M
22 | | N
23 | | O
24 | | P
25 | | Q
26 | | R
27 | | S
28 | | T
29 | | U
30 | | V
31 | | W
32 | | X
33 | | Y
34 | \ Z
35 |
36 | member Character of Show =
37 | => show = fn
38 | | A => "a"
39 | | B => "b"
40 | | C => "c"
41 | | D => "d"
42 | | E => "e"
43 | | F => "f"
44 | | G => "g"
45 | | H => "h"
46 | | I => "i"
47 | | J => "j"
48 | | K => "k"
49 | | L => "l"
50 | | M => "m"
51 | | N => "n"
52 | | O => "o"
53 | | P => "p"
54 | | Q => "q"
55 | | R => "r"
56 | | S => "s"
57 | | T => "t"
58 | | U => "u"
59 | | V => "v"
60 | | W => "w"
61 | | X => "x"
62 | | Y => "y"
63 | \ Z => "z"
64 |
65 | fn parse_char =
66 | | 'a' => Ok A
67 | | 'b' => Ok B
68 | | 'c' => Ok C
69 | | 'd' => Ok D
70 | | 'e' => Ok E
71 | | 'f' => Ok F
72 | | 'g' => Ok G
73 | | 'h' => Ok H
74 | | 'i' => Ok I
75 | | 'j' => Ok J
76 | | 'k' => Ok K
77 | | 'l' => Ok L
78 | | 'm' => Ok M
79 | | 'n' => Ok N
80 | | 'o' => Ok O
81 | | 'p' => Ok P
82 | | 'q' => Ok Q
83 | | 'r' => Ok R
84 | | 's' => Ok S
85 | | 't' => Ok T
86 | | 'u' => Ok U
87 | | 'v' => Ok V
88 | | 'w' => Ok W
89 | | 'x' => Ok X
90 | | 'y' => Ok Y
91 | | 'z' => Ok Z
92 | \ c => Err OutOfRange c
93 |
94 | fn parse_str =
95 | | [] => Ok []
96 | \ [c .. cs] => when c -> parse_char is
97 | | Ok c => when cs -> parse_str is
98 | | Ok cs => Ok [c .. cs]
99 | \ Err e => Err e
100 | \ Err e => Err e
101 |
102 | fn shift_char =
103 | | 0, c => c
104 | \ n + 1, c => when c is
105 | | A => B -> shift_char(n)
106 | | B => C -> shift_char(n)
107 | | C => D -> shift_char(n)
108 | | D => E -> shift_char(n)
109 | | E => F -> shift_char(n)
110 | | F => G -> shift_char(n)
111 | | G => H -> shift_char(n)
112 | | H => I -> shift_char(n)
113 | | I => J -> shift_char(n)
114 | | J => K -> shift_char(n)
115 | | K => L -> shift_char(n)
116 | | L => M -> shift_char(n)
117 | | M => N -> shift_char(n)
118 | | N => O -> shift_char(n)
119 | | O => P -> shift_char(n)
120 | | P => Q -> shift_char(n)
121 | | Q => R -> shift_char(n)
122 | | R => S -> shift_char(n)
123 | | S => T -> shift_char(n)
124 | | T => U -> shift_char(n)
125 | | U => V -> shift_char(n)
126 | | V => W -> shift_char(n)
127 | | W => X -> shift_char(n)
128 | | X => Y -> shift_char(n)
129 | | Y => Z -> shift_char(n)
130 | \ Z => A -> shift_char(n)
131 |
132 | fn shift_str =
133 | n => map(shift_char(n))
134 |
135 | def main : io ~ () = @{
136 | let text = "hello";
137 | print("Input = " ++ text)!;
138 | when text -> parse_str is
139 | | Ok s => @{
140 | print("Parsed = " + s)!;
141 | let n = 1;
142 | let shifted = s -> shift_str(n);
143 | print("Shifted(" + n + ") = " + shifted)!;
144 | }!
145 | \ Err e => print("Error: " + e)!
146 | }
147 |
--------------------------------------------------------------------------------
/analysis/src/effect.rs:
--------------------------------------------------------------------------------
1 | use super::*;
2 |
3 | pub struct EffectDecl {
4 | pub name: SrcNode,
5 | pub attr: Vec>,
6 | pub gen_scope: GenScopeId,
7 | pub send: Option,
8 | pub recv: Option,
9 | }
10 |
11 | #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
12 | pub struct EffectDeclId(usize, Ident);
13 |
14 | impl fmt::Debug for EffectDeclId {
15 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
16 | write!(f, "{}", self.1)
17 | }
18 | }
19 |
20 | pub struct EffectAlias {
21 | pub name: SrcNode,
22 | pub attr: Vec>,
23 | pub gen_scope: GenScopeId,
24 | pub effects: Option, Vec, Vec