├── .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>)>>, 25 | } 26 | 27 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 28 | pub struct EffectAliasId(usize); 29 | 30 | #[derive(Default)] 31 | pub struct Lang { 32 | // pub not: Option, 33 | } 34 | 35 | #[derive(Default)] 36 | pub struct Effects { 37 | lut: HashMap)>, 38 | effect_decls: Vec, 39 | effect_aliases: Vec, 40 | pub lang: Lang, 41 | } 42 | 43 | impl Effects { 44 | pub fn get_decl(&self, eff: EffectDeclId) -> &EffectDecl { 45 | &self.effect_decls[eff.0] 46 | } 47 | 48 | pub fn get_alias(&self, alias: EffectAliasId) -> &EffectAlias { 49 | &self.effect_aliases[alias.0] 50 | } 51 | 52 | pub fn iter(&self) -> impl Iterator { 53 | self.effect_decls.iter().enumerate().map(|(i, eff)| (EffectDeclId(i, *eff.name), eff)) 54 | } 55 | 56 | // pub fn iter(&self) -> impl Iterator { 57 | // self.effect_names.iter().enumerate().map(|(i, eff)| (EffectDeclId(i), eff)) 58 | // } 59 | 60 | pub fn lookup(&self, name: Ident) -> Option> { 61 | self.lut.get(&name).map(|(_, id)| *id) 62 | } 63 | 64 | pub fn declare(&mut self, eff: EffectDecl) -> Result { 65 | let id = EffectDeclId(self.effect_decls.len(), *eff.name); 66 | let span = eff.name.span(); 67 | if let Err(old) = self.lut.try_insert(*eff.name, (span, Ok(id))) { 68 | Err(Error::DuplicateEffectDecl(*eff.name, old.entry.get().0, span)) 69 | } else { 70 | if let Some(lang) = eff.attr 71 | .iter() 72 | .find(|a| &**a.name == "lang") 73 | .and_then(|a| a.args.as_ref()) 74 | { 75 | // if lang.iter().find(|a| &**a.name == "not").is_some() { 76 | // self.lang.not = Some(id); 77 | // } 78 | } 79 | 80 | self.effect_decls.push(eff); 81 | Ok(id) 82 | } 83 | } 84 | 85 | pub fn declare_alias(&mut self, alias: EffectAlias) -> Result { 86 | let id = EffectAliasId(self.effect_aliases.len()); 87 | let span = alias.name.span(); 88 | if let Err(old) = self.lut.try_insert(*alias.name, (span, Err(id))) { 89 | Err(Error::DuplicateEffectDecl(*alias.name, old.entry.get().0, span)) 90 | } else { 91 | self.effect_aliases.push(alias); 92 | Ok(id) 93 | } 94 | } 95 | 96 | pub fn check_lang_items(&self) -> Vec { 97 | let mut errors = Vec::new(); 98 | 99 | // if self.lang.not.is_none() { errors.push(Error::MissingLangItem("not")); } 100 | 101 | errors 102 | } 103 | 104 | pub fn define_send_recv(&mut self, id: EffectDeclId, send: TyId, recv: TyId) { 105 | self.effect_decls[id.0].send = Some(send); 106 | self.effect_decls[id.0].recv = Some(recv); 107 | } 108 | 109 | pub fn define_alias_effects(&mut self, id: EffectAliasId, effs: Vec<(SrcNode, Vec, Vec>)>) { 110 | self.effect_aliases[id.0].effects = Some(effs); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /test_cases/effects.tao: -------------------------------------------------------------------------------- 1 | # Without effect handling 2 | 3 | fn map A, B : (A -> B) -> [A] -> [B] is 4 | | _, [] => [] 5 | \ f, [a .. as] => [a->f .. as->map(f)] 6 | 7 | # With effect handling 8 | 9 | fn map A, B, e : (A -> e ~ B) -> [A] -> e ~ [B] is 10 | | _, [] => [] 11 | \ f, [a .. as] => [a->f! .. as->map(f)] 12 | 13 | ## General rule: 14 | effect foo A, B, C is A, B => C 15 | ## ...implicitly generates... 16 | fn foo A, B, C : A -> B -> foo(A, B, C) ~ C is 17 | # `~ effect` syntax in intrinsics allows mentioning an effect by name 18 | # Also permitted: `: type` for types 19 | a, b => @suspend(~ foo(A, B, C), a, b) 20 | 21 | # Effects, always take the form [] => 22 | effect print is Str => () 23 | effect input is () => Str 24 | 25 | # Implicitly generated by effects above 26 | fn print : Str -> print ~ () is 27 | \ s => @suspend(~ print, s) 28 | fn input : input ~ Str is 29 | @suspend(~ input, ()) 30 | 31 | # Composed effects 32 | effect io = print + input 33 | 34 | fn for_each A, e : (A -> e ~ ()) -> [A] -> e ~ () is 35 | | _, [] => () 36 | \ f, [a .. as] => a->f!; as->for_each(f) 37 | 38 | effect yield A is A => () 39 | # Implicitly generated by effect above 40 | fn yield A : A -> yield(A) ~ () is 41 | \ x => @suspend(~ yield(A), x) 42 | 43 | # Example usage of effects 44 | # Atomic pattern is (;;) with optional trailing semicolon 45 | fn main : io ~ () is ( 46 | let name = input!; 47 | print("Hello, " ++ name)!; 48 | print("How are you?")!; 49 | handle yield(Nat) in n => print(n->show)! on 50 | [1, 2, 3] 51 | -> for_each(yield); 52 | ) 53 | 54 | # Effect handler for IO 55 | fn handle_io A : print + input ~ A -> @ -> (A, @) is 56 | \ eff, uni => 57 | with uni 58 | handle print in uni, s => (@print(s, uni), ()) 59 | handle input in uni, () => @input(uni) 60 | on 61 | eff 62 | 63 | # Actual program start, just deals in universe mapping 64 | fn _start : @ -> ((), @) is 65 | handle_io(main) 66 | 67 | # Async/await 68 | data Poll A = 69 | | Ready A 70 | \ Pending 71 | 72 | class Future = 73 | => Output 74 | => poll : Self -> Reactor -> (Reactor, Poll(Self.Output)) 75 | 76 | effect async is 77 | => await F < Future is F => F.Output 78 | 79 | fn async_main : async ~ () is ( 80 | print("Waiting for 10 seconds...")!; 81 | timer(10->seconds)!; 82 | print("Hello, world!")!; 83 | ) 84 | 85 | data Reactor = { 86 | uni: @, 87 | ... 88 | } 89 | 90 | fn main : @ -> @ is uni => 91 | with Reactor { uni } 92 | handle async in 93 | => await F < Future is reactor, fut => when F.poll(fut, reactor) is 94 | | (reactor, Ready x) => (reactor, x) 95 | \ (reactor, Pending) => ... 96 | on 97 | async_main 98 | 99 | 100 | fn async_main : async ~ () is 101 | ... 102 | 103 | data Rt = { 104 | waiting: Map(Token, Task) 105 | } 106 | 107 | fn process_event : Event -> @ -> Rt -> (Rt, @) is 108 | ... 109 | 110 | fn process_events : Rt -> @ -> @ is rt, uni => 111 | when @wait_event(uni) is 112 | | (Shutdown, uni) => (rt, uni) 113 | \ (event, uni) => 114 | let (rt, uni) = rt->process_event(event, uni) in 115 | process_events(rt, uni) 116 | 117 | fn main : @ -> @ is uni => 118 | let rt = default in 119 | let fut = async_main in 120 | process_events(rt, uni) 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | fn main : @ -> (@, ()) is 132 | let eff = ... in 133 | fix( 134 | fn rec, uni, eff => when @pull(~print, eff) is 135 | | Next (waiting, f) => 136 | let (uni, r) = uni->f in 137 | rec(uni, @push(~print, waiting, r)) 138 | \ Break () => (uni, ()) 139 | uni, 140 | eff, 141 | ) 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /syntax/examples/test.tao: -------------------------------------------------------------------------------- 1 | # Data syntax 2 | 3 | data Never = # Data without variants has no inhabitants 4 | 5 | data Unit = () 6 | 7 | data List A = 8 | | Item (A, List A) 9 | | Empty 10 | 11 | data Maybe A = 12 | | Just A 13 | | None 14 | 15 | data Result A, E = 16 | | Ok A 17 | | Err E 18 | 19 | data NatWrapper = Nat 20 | 21 | data Person = { 22 | name: Str, 23 | age: Num, 24 | } 25 | 26 | # Type alias syntax 27 | 28 | type Natural = Nat 29 | 30 | type NonEmpty A = (A, List A) 31 | 32 | type Str A = [Char] 33 | 34 | # Definition syntax 35 | 36 | def five = 5 37 | def six = five + 1 38 | 39 | def empty A: [A] = [] 40 | 41 | def bob = Person { 42 | name: "Bob", 43 | age: 42.0, 44 | } 45 | 46 | # Simple typeclass emulation 47 | 48 | data Add A = { 49 | add: A -> A -> A, 50 | } 51 | 52 | def sum A = fn class: Add A, x: A, y: A => class.add(x, y) 53 | 54 | # Function syntax 55 | 56 | def add = fn x: Nat, y: Nat => x + y # TODO: Investigate whether `fn add x, y => x + y` would conflict with inline function syntax 57 | 58 | def factorial = fn 59 | | 0 => 1 60 | | x => x * factorial(x - 1) 61 | 62 | def zip A, B = fn 63 | | Just x: Maybe A, Just y: Maybe B => Just (x, y) 64 | | _, _ => None 65 | 66 | # Type hints can be added with ':' and may include the wildcard type, '?', which gets inferred 67 | # Type parameters come before type hints 68 | def append A : A -> [A] -> [?] = fn 69 | | x, [] => [x] 70 | # 'arg:f' is equivalent to 'f(arg)' and allows method-like chaining of functions 71 | | x, [y .. ys] => [y] ++ ys:append(x) 72 | 73 | def len A : [A] -> Nat = fn 74 | | [] => 0 75 | | [_ .. tail] => 1 + tail:len 76 | 77 | def make_tuple A, B = fn 78 | # Type annotations are permitted in patterns 79 | | a : A, b : B => (a, b) 80 | 81 | def filter A : (A -> Bool) -> [A] -> [A] = fn 82 | | _, [] => [] 83 | | f, [x .. xs] => if x:f 84 | then [x] ++ xs:filter(f) 85 | else xs:filter(f) 86 | 87 | # Typeclasses are specified with `A < Ord` meaning "A is a subset of the class Ord" 88 | # TODO: How to specify multiple typeclasses? 89 | def qsort A < Ord A : [A] -> [A] = fn 90 | | [] => [] 91 | # Binding with '~' is permitted in patterns 92 | | xs ~ [x ..] => 93 | # '< x' is shorthand for 'fn a => a < x' 94 | xs:filter(fn x => x < x):qsort 95 | ++ 96 | xs:filter(fn x => x >= x):qsort 97 | 98 | # Inline function syntax 99 | 100 | def foobar = 101 | # Simple case 102 | let identity = fn x => x in 103 | # Inline functions also support pattern matching 104 | let unwrap_or = fn 105 | | _, Just x => x 106 | | default, None => default 107 | in 108 | let unwrap_or_zero = fn 109 | | Just x => x 110 | | None => 0 111 | in 112 | identity(5) + unwrap_or(5, None) 113 | 114 | # Match syntax 115 | 116 | $[main] 117 | def n = 5 118 | 119 | #$[main] 120 | def main = 121 | let x = 5 in 122 | # Branches, like function pattern matching 123 | let desc = when x is 124 | | 0 => "zero" 125 | | 1 => "one" 126 | | _ => "a big number" 127 | in 128 | let x = when desc is 129 | | "zero" => Just 0 130 | | "one" => Just 1 131 | | _ => None 132 | in 133 | # Multiple assignments are permitted in a let 134 | let 135 | x = 7, 136 | y = 8, 137 | in 138 | x 139 | 140 | # Typeclass constraint syntax 141 | 142 | def less A : A -> A -> Bool = fn 143 | | x, y => x < y 144 | 145 | data Ordering = 146 | | Equal 147 | | Less 148 | | Greater 149 | 150 | data Vec2 A = { 151 | x: A, 152 | y: A, 153 | } 154 | 155 | def add_vec A = fn 156 | | Vec2 { x, y }: Vec2 A, Vec2 { x ~ a, y ~ b }: Vec2 A => Vec2 { 157 | x: x + a, 158 | y: y + b, 159 | } 160 | 161 | def identity A : A -> A = fn x => let (x, ()) = identity(((), x)) in x 162 | 163 | type NatNonEmpty = (Nat, Maybe NatNonEmpty) 164 | 165 | data N = 166 | | Zero 167 | | Zero 168 | | Succ N 169 | 170 | def is_even = fn 171 | | Zero => True 172 | | Succ Succ x => x:is_even 173 | | Succ Zero => False 174 | 175 | 176 | def main = greet({ greeting: "Hello", audience: "World" }) 177 | 178 | def greet = fn { greeting: Str, audience: Str } => 179 | let str = if Str.is_empty(greeting) 180 | then "Hi!" 181 | else "{greeting}, {audience}!" 182 | in 183 | Stdout.!line(str) 184 | -------------------------------------------------------------------------------- /syntax/examples/sample.tao: -------------------------------------------------------------------------------- 1 | # Data syntax 2 | 3 | data Never = # Data without variants has no inhabitants 4 | 5 | data Unit = () 6 | 7 | data List A = 8 | | Item (A, List A) 9 | | Empty 10 | 11 | data Maybe A = 12 | | Just A 13 | | None 14 | 15 | data Result A, E = 16 | | Ok A 17 | | Err E 18 | 19 | data NatWrapper = Nat 20 | 21 | data Person = ( 22 | .name: Str, 23 | .age: Num, 24 | ) 25 | 26 | # Type alias syntax 27 | 28 | type Natural = Nat 29 | 30 | type NonEmpty A = (A, List A) 31 | 32 | # Definition syntax 33 | 34 | def five = 5 35 | def six = five + 1 36 | 37 | def bob = Person ( 38 | # '=' used instead of ':' because the latter is for type hints, which are permitted in patterns during destructuring 39 | .name = "Bob", 40 | .age = 42, 41 | ) 42 | 43 | # Function syntax 44 | 45 | def add = fn x, y => x + y # TODO: Investigate whether `fn add x, y => x + y` would conflict with inline function syntax 46 | 47 | def factorial = fn 48 | | 0 => 1 49 | | x => x * factorial(x - 1) 50 | 51 | def zip = fn 52 | | Just x, Just y => Just (x, y) 53 | | _, _ => None 54 | 55 | # Type hints can be added with ':' and may include the wildcard type, '?', which gets inferred 56 | # Type parameters combe before type hints 57 | def append A : A -> [?] -> [?] = fn 58 | | x, [] => [x] 59 | # 'arg:f' is equivalent to 'f(arg)' and allows method-like chaining of functions 60 | | x, [y .. ys] => [y] ++ ys:append(x) 61 | 62 | def len A : [A] -> Nat = fn 63 | | [] => 0 64 | | [_ .. tail] => 1 + tail:len 65 | 66 | def make_tuple A, B = fn 67 | # Type annotations are permitted in patterns 68 | | a : A, b : B => (a, b) 69 | 70 | def filter A : (A -> Bool) -> [A] -> [A] = fn 71 | | _, [] => [] 72 | | f, [x .. xs] => if x:f 73 | then [x] ++ xs:filter(f) 74 | else xs:filter(f) 75 | 76 | # Typeclasses are specified with `A < Ord` meaning "A is a subset of the class Ord" 77 | # TODO: How to specify multiple typeclasses? 78 | def qsort A < Ord : [A] -> [A] = fn 79 | | [] => [] 80 | # Binding with '~' is permitted in patterns 81 | | xs ~ [x ..] => 82 | # '< x' is shorthand for 'fn a => a < x' 83 | xs:filter(< x):qsort 84 | ++ 85 | xs:filter(>= x):qsort 86 | 87 | # Inline function syntax 88 | 89 | def main = 90 | # Simple case 91 | let identity = fn x => x in 92 | # Inline functions also support pattern matching 93 | let unwrap_or = fn 94 | | _, Just x => x 95 | | default, None => default 96 | in 97 | let unwrap_or_zero = fn 98 | | Just x => x 99 | | None => 0 100 | in 101 | identity(5) 102 | 103 | # Match syntax 104 | 105 | def main = 106 | let x = 5 in 107 | # Branches, like function pattern matching 108 | let desc = when x is 109 | | 0 => "zero" 110 | | 1 => "one" 111 | | _ => "a big number" 112 | in 113 | let x = when desc is 114 | | "zero" => Just 0 115 | | "one" => Just 1 116 | | _ => None 117 | in 118 | # Multiple assignments are permitted in a let 119 | let 120 | x = 7, 121 | y = 8, 122 | in 123 | x 124 | 125 | # Typeclass constraint syntax 126 | 127 | def less A < Ord : A -> A -> Bool = fn 128 | | x, y => x < y 129 | 130 | data Ordering = 131 | | Equal 132 | | Less 133 | | Greater 134 | 135 | # Typeclass membership can sometimes be automatically derived with compiler support 136 | impl element Ordering of Eq = auto 137 | 138 | # Typeclass syntax 139 | 140 | # TODO: Generics on type classes? 141 | class Ord < PartialOrd = 142 | cmp : Self -> Self -> Ordering, 143 | 144 | impl element Nat of Ord = 145 | cmp = fn x, y => ... , 146 | 147 | class Add R = 148 | Output, 149 | add : Self -> R -> Output, 150 | 151 | # As with other constructs, braces are permitted to avoid ambiguity 152 | impl element Nat of Add Nat = { 153 | Output = Nat, 154 | add = fn x, y = x + y, 155 | } 156 | 157 | data Vec2 A = ( 158 | .x : A, 159 | .y : A, 160 | ) 161 | 162 | impl A < Add B, B element Vec2 A of Add B = 163 | Output = Vec2 (A of Add B).Output 164 | add = fn Vec2 ( .x, .y ), p => Vec2 ( .x = x + p, .y = y + p ) 165 | 166 | # Equivalent Rust syntax 167 | impl, B> Add for Vec2 { 168 | type Output = >::Output; 169 | fn add(self, p: B) -> Self::Output { 170 | Vec2 { x: self.x + p, y: self.y + p } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /lib/core/ops.tao: -------------------------------------------------------------------------------- 1 | #! Operator definitions. 2 | 3 | import "bool.tao" 4 | 5 | # Not 6 | 7 | $[lang(not)] 8 | class Not = 9 | => Output 10 | => not : Self -> Self.Output 11 | 12 | for A < Not member [A] of Not = 13 | => Output = [A.Output] 14 | => not = fn 15 | | [] => [] 16 | \ [x .. xs] => [!x .. !xs] 17 | 18 | # Not 19 | 20 | $[lang(neg)] 21 | class Neg where = 22 | => Output 23 | => neg : Self -> Self.Output 24 | 25 | member Nat of Neg = 26 | => Output = Int 27 | => neg = fn x => @neg_nat(x) 28 | 29 | member Int of Neg = 30 | => Output = Int 31 | => neg = fn x => @neg_int(x) 32 | 33 | member Real of Neg = 34 | => Output = Real 35 | => neg = fn x => @neg_real(x) 36 | 37 | for A < Neg member [A] of Neg = 38 | => Output = [A.Output] 39 | => neg = fn 40 | | [] => [] 41 | \ [x .. xs] => [-x .. -xs] 42 | 43 | # Add 44 | 45 | $[lang(add)] 46 | class Add B = 47 | => Output 48 | => add : Self -> B -> Self.Output 49 | 50 | member Nat of Add Nat = 51 | => Output = Nat 52 | => add = fn x, y => @add_nat(x, y) 53 | 54 | member Int of Add Int = 55 | => Output = Int 56 | => add = fn x, y => @add_int(x, y) 57 | 58 | $[lang(sub)] 59 | class Sub B = 60 | => Output 61 | => sub : Self -> B -> Self.Output 62 | 63 | member Nat of Sub Nat = 64 | => Output = Int 65 | # Hacky, hacky, hacky 66 | => sub = fn x, y => --x + -y 67 | 68 | member Int of Sub Int = 69 | => Output = Int 70 | => sub = fn x, y => x + -y 71 | 72 | # Mul 73 | 74 | $[lang(mul)] 75 | class Mul B = 76 | => Output 77 | => mul : Self -> B -> Self.Output 78 | 79 | member Nat of Mul Nat = 80 | => Output = Nat 81 | => mul = fn x, y => @mul_nat(x, y) 82 | 83 | member Int of Mul Int = 84 | => Output = Int 85 | => mul = fn x, y => @mul_int(x, y) 86 | 87 | # Div 88 | 89 | $[lang(div)] 90 | class Div B = 91 | => Output 92 | => div : Self -> B -> Self.Output 93 | 94 | # And 95 | 96 | $[lang(and_)] 97 | class And B = 98 | => Output 99 | => and_ : Self -> B -> Self.Output 100 | 101 | # Or 102 | 103 | $[lang(or_)] 104 | class Or B = 105 | => Output 106 | => or_ : Self -> B -> Self.Output 107 | 108 | # Eq 109 | 110 | $[lang(eq)] 111 | class Eq = 112 | => eq : Self -> Self -> Bool 113 | => ne : Self -> Self -> Bool 114 | 115 | member Nat of Eq = 116 | => eq = fn x, y => @eq_nat(x, y) 117 | => ne = fn x, y => !Self.eq(x, y) 118 | 119 | member Bool of Eq = 120 | => eq = fn 121 | | True, True => True 122 | | False, False => True 123 | \ _, _ => False 124 | => ne = fn x, y => !(x = y) 125 | 126 | member Char of Eq = 127 | => eq = fn x, y => @eq_char(x, y) 128 | => ne = fn x, y => !Self.eq(x, y) 129 | 130 | for A < Eq, B < Eq member (A, B) of Eq = 131 | => eq = fn (a0, b0), (a1, b1) => a0 = a1 and b0 = b1 132 | => ne = fn (a0, b0), (a1, b1) => a0 != a1 or b0 != b1 133 | 134 | for A < Eq member [A] of Eq = 135 | => eq = fn 136 | | [], [] => True 137 | | [x .. xs], [y .. ys] => x = y and xs = ys 138 | \ _, _ => False 139 | => ne = fn x, y => !Self.eq(x, y) 140 | 141 | #fn eq A < Eq : A -> A -> Bool = x, y => A.eq(x, y) 142 | 143 | # Ord 144 | 145 | class Ord < Eq = 146 | => cmp : Self -> Self -> Ordering 147 | 148 | $[util] 149 | fn cmp A < Ord : A -> A -> Ordering = a, b => A.cmp(a, b) 150 | 151 | data Ordering = 152 | | Less 153 | | More 154 | \ Equal 155 | 156 | member Nat of Ord = 157 | => cmp = fn x, y => if @less_nat(x, y) 158 | then Less 159 | else if @less_nat(y, x) 160 | then More 161 | else Equal 162 | 163 | member Char of Ord = 164 | => cmp = fn x, y => cmp(@codepoint_char(x), @codepoint_char(y)) 165 | 166 | for A < Ord member [A] of Ord = 167 | => cmp = fn 168 | | [x .. xs], [y .. ys] => when cmp(x, y) is 169 | | Less => Less 170 | | More => More 171 | \ Equal => cmp(xs, ys) 172 | | [], [_ ..] => Less 173 | | [_ ..], [] => More 174 | \ _, _ => Equal 175 | 176 | $[lang(ord_ext)] 177 | class OrdExt < Ord = 178 | => less : Self -> Self -> Bool 179 | => less_eq : Self -> Self -> Bool 180 | => more : Self -> Self -> Bool 181 | => more_eq : Self -> Self -> Bool 182 | 183 | for A < Ord member A of OrdExt = 184 | => less = fn x, y => when A.cmp(x, y) is 185 | | Less => True 186 | \ _ => False 187 | => less_eq = fn x, y => when A.cmp(x, y) is 188 | | More => False 189 | \ _ => True 190 | => more = fn x, y => when A.cmp(x, y) is 191 | | More => True 192 | \ _ => False 193 | => more_eq = fn x, y => when A.cmp(x, y) is 194 | | Less => False 195 | \ _ => True 196 | 197 | $[lang(join)] 198 | class Join B = 199 | => Output 200 | => join : Self -> B -> Self.Output 201 | 202 | for A member [A] of Join [A] = 203 | => Output = [A] 204 | => join = fn xs, ys => @join_list(xs, ys) 205 | -------------------------------------------------------------------------------- /middle/src/opt/inline.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// Inline procedures where appropriate. 4 | #[derive(Default)] 5 | pub struct Inline { 6 | // (total non-recursive calls, is recursive) 7 | calls: HashMap, 8 | call_stack: Vec, 9 | inline_indirectly_recursive: bool, 10 | inline_size_threshold: usize, 11 | } 12 | 13 | impl Pass for Inline { 14 | fn create(opt_mode: OptMode) -> Self { 15 | Self { 16 | calls: HashMap::default(), 17 | call_stack: Vec::new(), 18 | inline_indirectly_recursive: match opt_mode { 19 | OptMode::None => todo!(), 20 | OptMode::Fast => true, 21 | OptMode::Size => false, 22 | }, 23 | inline_size_threshold: match opt_mode { 24 | OptMode::None => todo!(), 25 | OptMode::Fast => 512, 26 | OptMode::Size => 48, 27 | }, 28 | } 29 | } 30 | 31 | fn apply(&mut self, ctx: &mut Context) { 32 | self.init(ctx); 33 | 34 | let mut proc_bodies = self.calls 35 | .keys() 36 | .map(|proc_id| ( 37 | *proc_id, 38 | ctx.procs.get(*proc_id).unwrap().body.clone(), 39 | self.calls[proc_id], 40 | )) 41 | .collect::>(); 42 | // Perform inlining depth-first (procedures with a high depth are probably hotter) 43 | // Actually stable, procedure IDs are unique 44 | proc_bodies.sort_unstable_by_key(|(id, _, call_info)| (call_info.depth, *id)); 45 | 46 | for (id, mut body, _) in proc_bodies.into_iter().rev() { 47 | let mut call_info = *self.calls.get(&id).unwrap(); 48 | self.do_inlining(id, ctx, &mut body, &mut call_info); 49 | self.calls.insert(id, call_info); 50 | ctx.procs.get_mut(id).unwrap().body = body; 51 | } 52 | } 53 | } 54 | 55 | #[derive(Copy, Clone)] 56 | struct CallInfo { 57 | depth: usize, // Maximum depth of the proc in the call graph 58 | size: usize, // Number of instructions in the proc 59 | indirectly_recursive: bool, 60 | directly_recursive: bool, 61 | } 62 | 63 | impl Inline { 64 | fn analyze_proc(&mut self, ctx: &Context, proc_id: ProcId) -> Option { 65 | if let Some(call_info) = self.calls.get_mut(&proc_id) { 66 | call_info.depth = call_info.depth.max(self.call_stack.len()); 67 | Some(*call_info) // We already calculated this proc on another branch 68 | } else if self.call_stack.contains(&proc_id) { 69 | None // Is recursive since we're already in the proc 70 | } else { 71 | let mut call_info = CallInfo { 72 | depth: self.call_stack.len(), 73 | size: 0, 74 | indirectly_recursive: false, 75 | directly_recursive: false, 76 | }; 77 | 78 | // No information yet, go calculate it 79 | self.call_stack.push(proc_id); 80 | self.analyze_expr(ctx, proc_id, &ctx.procs.get(proc_id).unwrap().body, &mut call_info); 81 | self.call_stack.pop(); 82 | 83 | assert!(self.calls.insert(proc_id, call_info).is_none()); 84 | Some(call_info) 85 | } 86 | } 87 | 88 | fn analyze_expr(&mut self, ctx: &Context, proc_id: ProcId, expr: &Expr, call_info: &mut CallInfo) { 89 | if let Expr::Global(new_proc_id) = expr { 90 | if let Some(new_info) = self.analyze_proc(ctx, *new_proc_id) { 91 | call_info.indirectly_recursive |= new_info.indirectly_recursive; 92 | } else { 93 | call_info.directly_recursive = true; 94 | } 95 | } 96 | 97 | call_info.size += 1; 98 | 99 | expr.for_children(|expr| self.analyze_expr(ctx, proc_id, expr, call_info)); 100 | } 101 | 102 | fn do_inlining(&mut self, proc_id: ProcId, ctx: &Context, expr: &mut Expr, call_info: &mut CallInfo) { 103 | if let Expr::Global(new_proc_id) = expr 104 | && let Some(new_call_info) = self.calls.get(new_proc_id) 105 | && let Some(proc) = ctx.procs.get(*new_proc_id) 106 | // Don't inline directly recursive functions 107 | && !new_call_info.directly_recursive 108 | && (!new_call_info.indirectly_recursive || self.inline_indirectly_recursive) 109 | && let new_size = (call_info.size + new_call_info.size).saturating_sub(1) 110 | // Only inline if the inlinee is smaller than the given threshold 111 | && new_size < self.inline_size_threshold 112 | { 113 | *expr = proc.body.inner().clone(); 114 | expr.refresh_locals(); 115 | call_info.size = new_size; 116 | } 117 | 118 | expr.for_children_mut(|expr| self.do_inlining(proc_id, ctx, expr, call_info)); 119 | } 120 | 121 | fn init(&mut self, ctx: &Context) { 122 | self.calls.clear(); 123 | 124 | if let Some(entry) = ctx.entry { 125 | self.analyze_proc(ctx, entry); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /lib/parse.tao: -------------------------------------------------------------------------------- 1 | import "std.tao" 2 | 3 | # fn fold A, B : ? -> (? -> A -> ?) -> ? -> B = 4 | # | init, _, [] => init 5 | # \ init, f, [x .. tail] => fold(f(init, x), f, tail) 6 | 7 | $[util] 8 | fn reduce A, B = f, (init, xs) : (A, [B]) => fold(init, f, xs) 9 | 10 | $[util] 11 | fn fold_r A, B = 12 | | init, _, [] => init 13 | \ init, f : A -> B -> ?, [x .. xs] => f(x, fold_r(init, f, xs)) 14 | 15 | $[util] 16 | fn reduce_r A, B = f, (xs, init) : ([B], A) => fold_r(init, f, xs) 17 | 18 | $[util] 19 | fn map_res A, B, E : (A -> B) -> Result A E -> Result B E = 20 | | f, Ok ok => Ok ok->f 21 | \ _, Err err => Err err 22 | 23 | data ParseErr I = 24 | | UnexpectedEnd 25 | | ExpectedFound (Maybe I, I) 26 | | ExpectedPat Str 27 | \ ExpectedMore 28 | 29 | for I < Show member ParseErr I of Show = 30 | => show = fn 31 | | UnexpectedEnd => "unexpected end of input" 32 | | ExpectedFound (expected, found) => "expected " ++ (when expected is 33 | | Just expected => "'" ++ expected->show ++ "'" 34 | \ None => "end of input") ++ " found '" ++ found->show ++ "'" 35 | | ExpectedPat pat => "expected " ++ pat 36 | \ ExpectedMore => "not enough elements" 37 | 38 | type Parser I, O = [I] -> Result (O, [I]) (ParseErr I) 39 | 40 | $[util] 41 | fn filter_tok I, O : (I -> Result O (ParseErr I)) -> Parser I O = 42 | | _, [] => Err UnexpectedEnd 43 | \ f, [head .. tail] => head->f->map_res(fn o => (o, tail)) 44 | 45 | $[util] 46 | fn just : Char -> ? = 47 | c => filter_tok(fn head => if head = c 48 | then Ok head 49 | else Err ExpectedFound (Just c, head)) 50 | 51 | $[util] 52 | fn and_then I, O, U : Parser I U -> Parser I O -> Parser I (O, U) = 53 | b, a, xs => when xs->a is 54 | | Err err => Err err 55 | \ Ok (a, xs) => when xs->b is 56 | | Err err => Err err 57 | \ Ok (b, xs) => Ok ((a, b), xs) 58 | 59 | $[util] 60 | fn or_else I, O : Parser I O -> Parser I O -> Parser I O = 61 | a, b, xs => when xs->a is 62 | | ok ~ Ok _ => ok 63 | \ Err _ => when xs->b is 64 | | ok ~ Ok _ => ok 65 | \ err ~ Err _ => err 66 | 67 | $[util] 68 | fn repeated I, O : Parser I O -> Parser I [O] = 69 | a, xs => when xs->a is 70 | | Ok (out, tail) => when tail->repeated(a) is 71 | | Ok (items, tail) => Ok ([out .. items], tail) 72 | \ err ~ Err _ => Ok ([], tail) 73 | \ Err err => Ok ([], xs) 74 | 75 | $[util] 76 | fn repeated_at_least I, O : Nat -> Parser I O -> Parser I [O] = 77 | n, a, xs => when xs->repeated(a) is 78 | | ok ~ Ok (out, tail) => if out->len >= n 79 | then ok 80 | else Err ExpectedMore 81 | \ err ~ Err _ => err 82 | 83 | $[util] 84 | fn map_to I, O, U : (O -> U) -> Parser I O -> Parser I U = 85 | f, a, xs => xs->a->map_res(fn (out, tail) => (out->f, tail)) 86 | 87 | $[util] 88 | fn then_ignore I, O, U : Parser I U -> Parser I O -> Parser I O = 89 | b, a => a 90 | -> and_then(b) 91 | -> map_to(fn (a, _) => a) 92 | 93 | $[util] 94 | fn ignore_then I, O, U : Parser I U -> Parser I O -> Parser I U = 95 | b, a => a 96 | -> and_then(b) 97 | -> map_to(fn (_, b) => b) 98 | 99 | $[util] 100 | def whitespace = just(' ') 101 | -> or_else(just('\t')) 102 | 103 | $[util] 104 | def whitespaces = whitespace->repeated 105 | 106 | $[util] 107 | fn padded O : Parser Char O -> Parser Char O = 108 | a => whitespaces 109 | -> ignore_then(a) 110 | -> then_ignore(whitespaces) 111 | 112 | $[util] 113 | fn recurse I, O : Parser I O -> Parser I O = f, xs => when xs->f is 114 | | Ok (out, tail) => Ok (out, tail) 115 | \ Err e => Err e 116 | 117 | data FindErr = 118 | \ NotFound 119 | 120 | $[util] 121 | fn find_start : Nat -> Char -> Str -> Result Nat FindErr = 122 | | idx, c, [head .. tail] => if c = head 123 | then Ok idx 124 | else tail->find_start(idx + 1, c) 125 | \ idx, c, [] => Err NotFound 126 | 127 | $[util] 128 | def find = find_start(0) 129 | 130 | $[util] 131 | fn eoi I : Parser I () = 132 | | [] => Ok ((), []) 133 | \ [x ..] => Err ExpectedFound (None, x) 134 | 135 | $[util] 136 | fn do_parse I, O : Parser I O -> [I] -> Result O (ParseErr I) = parser, input => 137 | when input->(parser->and_then(eoi)) is 138 | | Ok ((o, _), _) => Ok o 139 | \ Err e => Err e 140 | 141 | fn char_to_num = c => "0123456789"->find(c) 142 | 143 | def digit = filter_tok(fn c => when c->char_to_num is 144 | | Ok n => Ok n 145 | \ Err NotFound => Err ExpectedPat "digit") 146 | 147 | def number = digit 148 | -> repeated_at_least(1) 149 | -> map_to(fn xs => xs->fold(0, fn a, x => a * 10 + x)) 150 | -> padded 151 | 152 | data Unary = 153 | \ Neg 154 | 155 | data Binary = 156 | | Add 157 | | Sub 158 | \ Mul 159 | 160 | data Expr = 161 | | Natural Nat 162 | | Unary (Unary, Expr) 163 | \ Binary (Binary, Expr, Expr) 164 | 165 | def atom = number 166 | -> map_to(fn x => Natural x) 167 | -> or_else(just('(') 168 | -> padded 169 | -> ignore_then(recurse(fn xs => xs->expr_parser)) 170 | -> then_ignore(just(')')) 171 | -> padded) 172 | 173 | fn op = c => just(c)->padded 174 | 175 | def unary = op('-') 176 | -> map_to(fn _ => Neg) 177 | -> repeated 178 | -> and_then(atom) 179 | -> map_to(reduce_r(fn op, a => Unary (op, a))) 180 | 181 | def product_parser = unary 182 | -> and_then(op('*')->map_to(fn _ => Mul) 183 | -> and_then(unary) 184 | -> repeated) 185 | -> map_to(reduce(fn a, (op, b) => Binary (op, a, b))) 186 | 187 | def sum_parser = product_parser 188 | -> and_then(op('+')->map_to(fn _ => Add) 189 | -> or_else(op('-')->map_to(fn _ => Sub)) 190 | -> and_then(product_parser) 191 | -> repeated) 192 | -> map_to(reduce(fn a, (op, b) => Binary (op, a, b))) 193 | 194 | def expr_parser : Parser Char Expr = sum_parser 195 | 196 | fn eval = 197 | | Natural x => --x 198 | | Unary (Neg, a) => -a->eval 199 | | Binary (Add, a, b) => a->eval + b->eval 200 | | Binary (Sub, a, b) => a->eval - b->eval 201 | \ Binary (Mul, a, b) => a->eval * b->eval 202 | -------------------------------------------------------------------------------- /analysis/src/reify.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub trait Reify: Sized { 4 | type Output; 5 | 6 | fn reify(self: InferNode, infer: &mut Checked) -> TyNode; 7 | } 8 | 9 | impl Reify for hir::Binding { 10 | type Output = hir::Binding; 11 | 12 | fn reify(self: InferNode, infer: &mut Checked) -> TyNode { 13 | let (span, ty) = *self.meta(); 14 | 15 | let this = self.into_inner(); 16 | TyNode::new(hir::Binding { 17 | pat: this.pat.map(|pat| match pat { 18 | hir::Pat::Error => hir::Pat::Error, 19 | hir::Pat::Wildcard => hir::Pat::Wildcard, 20 | hir::Pat::Literal(litr) => hir::Pat::Literal(litr), 21 | hir::Pat::Single(inner) => hir::Pat::Single(inner.reify(infer)), 22 | hir::Pat::Add(lhs, rhs) => hir::Pat::Add(lhs.reify(infer), rhs), 23 | hir::Pat::Record(fields, is_tuple) => hir::Pat::Record(fields 24 | .into_iter() 25 | .map(|(name, field)| (name, field.reify(infer))) 26 | .collect(), is_tuple), 27 | hir::Pat::ListExact(items) => hir::Pat::ListExact(items 28 | .into_iter() 29 | .map(|item| item.reify(infer)) 30 | .collect()), 31 | hir::Pat::ListFront(items, tail) => hir::Pat::ListFront(items 32 | .into_iter() 33 | .map(|item| item.reify(infer)) 34 | .collect(), tail.map(|tail| tail.reify(infer))), 35 | hir::Pat::Decons(data, variant, inner) => hir::Pat::Decons(data, variant, inner.reify(infer)), 36 | }), 37 | name: this.name, 38 | }, (span, infer.reify(ty))) 39 | } 40 | } 41 | 42 | impl Reify for hir::Expr { 43 | type Output = hir::Expr; 44 | 45 | fn reify(self: InferNode, infer: &mut Checked) -> TyNode { 46 | let (span, ty) = *self.meta(); 47 | 48 | let expr = match self.into_inner() { 49 | hir::Expr::Error => hir::Expr::Error, 50 | hir::Expr::Literal(litr) => hir::Expr::Literal(litr), 51 | hir::Expr::Local(local) => hir::Expr::Local(local), 52 | hir::Expr::Global((global, gen_tys, gen_effs)) => hir::Expr::Global(( 53 | global, 54 | gen_tys 55 | .into_iter() 56 | .map(|(span, ty)| (span, infer.reify(ty))) 57 | .collect(), 58 | gen_effs 59 | .into_iter() 60 | .map(|eff| infer.reify_effect(eff)) 61 | .collect(), 62 | )), 63 | hir::Expr::List(items, tails) => hir::Expr::List( 64 | items 65 | .into_iter() 66 | .map(|item| item.reify(infer)) 67 | .collect(), 68 | tails 69 | .into_iter() 70 | .map(|tail| tail.reify(infer)) 71 | .collect(), 72 | ), 73 | hir::Expr::Record(fields, is_tuple) => hir::Expr::Record(fields 74 | .into_iter() 75 | .map(|(name, field)| (name, field.reify(infer))) 76 | .collect(), is_tuple), 77 | hir::Expr::Access(record, field_name) => hir::Expr::Access(record.reify(infer), field_name), 78 | hir::Expr::Match(hidden_outer, pred, arms) => { 79 | let pred = pred.reify(infer); 80 | let arms = arms 81 | .into_iter() 82 | .map(|(binding, arm)| (binding.reify(infer), arm.reify(infer))) 83 | .collect::>(); 84 | 85 | if let Err(example) = exhaustivity(infer.ctx(), pred.meta().1, arms.iter().map(|(b, _)| b)) { 86 | infer.ctx_mut().emit(Error::NotExhaustive(span, example, hidden_outer)); 87 | } 88 | 89 | hir::Expr::Match(hidden_outer, pred, arms) 90 | }, 91 | hir::Expr::Func(param, body) => hir::Expr::Func( 92 | TyNode::new(*param, (param.meta().0, infer.reify(param.meta().1))), 93 | body.reify(infer), 94 | ), 95 | hir::Expr::Apply(f, param) => hir::Expr::Apply(f.reify(infer), param.reify(infer)), 96 | hir::Expr::Cons(name, variant, a) => hir::Expr::Cons(name, variant, a.reify(infer)), 97 | hir::Expr::ClassAccess((ty_span, ty), class, field) => { 98 | hir::Expr::ClassAccess((ty_span, infer.reify(ty)), infer.reify_class(class), field) 99 | }, 100 | hir::Expr::Intrinsic(name, args) => hir::Expr::Intrinsic(name, args 101 | .into_iter() 102 | .map(|arg| arg.reify(infer)) 103 | .collect()), 104 | hir::Expr::Update(record, fields) => hir::Expr::Update(record.reify(infer), fields 105 | .into_iter() 106 | .map(|(name, field)| (name, field.reify(infer))) 107 | .collect()), 108 | hir::Expr::Basin(eff, inner) => match infer.reify_effect(eff) { 109 | Some(eff) => hir::Expr::Basin(Some(eff), inner.reify(infer)), 110 | None => inner.reify(infer).into_inner(), 111 | }, 112 | hir::Expr::Suspend(eff, inner) => hir::Expr::Suspend(infer.reify_effect_inst(eff), inner.reify(infer)), 113 | hir::Expr::Handle { expr, handlers } => hir::Expr::Handle { 114 | expr: expr.reify(infer), 115 | handlers: handlers 116 | .into_iter() 117 | .map(|hir::Handler { eff, send, state, recv }| hir::Handler { 118 | eff: infer.reify_effect_inst(eff), 119 | send: TyNode::new(*send, (send.meta().0, infer.reify(send.meta().1))), 120 | state: state.map(|state| TyNode::new(*state, (state.meta().0, infer.reify(state.meta().1)))), 121 | recv: recv.reify(infer), 122 | }) 123 | .collect(), 124 | }, 125 | }; 126 | 127 | TyNode::new(expr, (span, infer.reify(ty))) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /middle/src/opt/remove_unused_bindings.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// Remove bindings that are never used. Also, matches with a single arm that do not bind are flattened and arms that 4 | /// follow irrefutable arms are removed. 5 | #[derive(Default)] 6 | pub struct RemoveUnusedBindings; 7 | 8 | impl Pass for RemoveUnusedBindings { 9 | fn apply(&mut self, ctx: &mut Context) { 10 | fn visit( 11 | mir: &Context, 12 | expr: &mut Expr, 13 | stack: &mut Vec<(Local, u64)>, 14 | proc_stack: &mut Vec, 15 | ) { 16 | match expr { 17 | Expr::Local(local) => if let Some((_, n)) = stack.iter_mut().rev().find(|(name, _)| *name == *local) { 18 | // Increment uses 19 | *n += 1; 20 | } else { 21 | panic!("Could not find local ${} in {:?}", local.0, stack); 22 | }, 23 | Expr::Match(pred, arms) => { 24 | visit(mir, pred, stack, proc_stack); 25 | 26 | // Remove any arms that follow an irrefutable arm 27 | for i in 0..arms.len() { 28 | if !arms[i].0.is_refutable() { 29 | arms.truncate(i + 1); 30 | break; 31 | } 32 | } 33 | 34 | arms 35 | .iter_mut() 36 | .for_each(|(arm, body)| { 37 | let old_stack = stack.len(); 38 | 39 | stack.extend(arm.binding_names().into_iter().map(|name| (name, 0))); 40 | visit(mir, body, stack, proc_stack); 41 | 42 | fn remove_unused(binding: &mut Binding, stack: &mut Vec<(Local, u64)>) { 43 | if let Some(name) = binding.name { 44 | if stack.iter_mut().rev().find(|(n, _)| *n == name).unwrap().1 == 0 { 45 | binding.name = None; 46 | } 47 | } 48 | 49 | match &mut binding.pat { 50 | Pat::Wildcard => {}, 51 | Pat::Literal(_) => {}, 52 | Pat::Single(inner) => remove_unused(inner, stack), 53 | Pat::Add(lhs, _) => remove_unused(lhs, stack), 54 | Pat::Tuple(fields) => fields 55 | .iter_mut() 56 | .for_each(|field| remove_unused(field, stack)), 57 | Pat::ListExact(items) => items 58 | .iter_mut() 59 | .for_each(|item| remove_unused(item, stack)), 60 | Pat::ListFront(items, tail) => { 61 | items 62 | .iter_mut() 63 | .for_each(|item| remove_unused(item, stack)); 64 | tail 65 | .as_mut() 66 | .map(|tail| remove_unused(tail, stack)); 67 | }, 68 | Pat::Variant(_, inner) => remove_unused(inner, stack), 69 | Pat::Data(_, inner) => remove_unused(inner, stack), 70 | } 71 | } 72 | 73 | remove_unused(arm, stack); 74 | 75 | stack.truncate(old_stack); 76 | }); 77 | 78 | // Visit predicate last to avoid visiting it again if the match was removed 79 | visit(mir, pred, stack, proc_stack); 80 | 81 | // Flatten matches with a single arm where the arm does not bind 82 | if arms.len() == 1 && !arms.first().unwrap().0.binds() { 83 | if !pred.may_have_effect() { 84 | *expr = arms.remove(0).1.into_inner(); 85 | } 86 | } else if arms.get(0).map_or(false, |(b, _)| matches!(&b.pat, Pat::Wildcard)) { 87 | if !pred.may_have_effect() { 88 | let (arm, mut body) = arms.remove(0); 89 | if let Some(name) = arm.name { 90 | body.inline_local(name, pred); 91 | } 92 | *expr = body.into_inner(); 93 | } 94 | } 95 | }, 96 | Expr::Func(arg, body) => { 97 | stack.push((**arg, 0)); 98 | visit(mir, body, stack, proc_stack); 99 | stack.pop(); 100 | }, 101 | Expr::Go(next, body, init) => { 102 | visit(mir, init, stack, proc_stack); 103 | stack.push((**next, 0)); 104 | visit(mir, body, stack, proc_stack); 105 | stack.pop(); 106 | }, 107 | Expr::Handle { expr, handlers } => { 108 | visit(mir, expr, stack, proc_stack); 109 | for Handler { eff: _, send, state, recv } in handlers { 110 | let old_len = stack.len(); 111 | stack.push((**send, 0)); 112 | stack.push((**state, 0)); 113 | visit(mir, recv, stack, proc_stack); 114 | stack.truncate(old_len); 115 | } 116 | }, 117 | _ => expr.for_children_mut(|expr| visit(mir, expr, stack, proc_stack)), 118 | } 119 | } 120 | 121 | let proc_bodies = ctx.procs 122 | .iter() 123 | .map(|(id, proc)| (id, proc.body.clone())) 124 | .collect::>(); 125 | 126 | for (id, mut body) in proc_bodies { 127 | let mut proc_stack = vec![id]; 128 | visit(&ctx, &mut body, &mut Vec::new(), &mut proc_stack); 129 | let requires = body.required_locals(None); 130 | debug_assert_eq!(requires.len(), 0, "Procedure requires locals {:?}\n\nOld = {}\n\n\nNew = {}\n", requires, ctx.procs.get_mut(id).unwrap().body.print(), body.print()); 131 | ctx.procs.get_mut(id).unwrap().body = body; 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /compiler/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod error; 2 | 3 | pub use tao_syntax::SrcId; 4 | pub use tao_middle::OptMode; 5 | 6 | use tao_syntax::{parse_module, ast, SrcNode, Error as SyntaxError}; 7 | use tao_analysis::Context as HirContext; 8 | use tao_middle::Context; 9 | use tao_vm::Program; 10 | use ariadne::sources; 11 | use structopt::StructOpt; 12 | use internment::Intern; 13 | use std::{io::Write, collections::HashMap}; 14 | use error::Error; 15 | 16 | #[derive(Clone, Debug, Default, StructOpt)] 17 | pub struct Options { 18 | /// Add a debugging layer to stdout (ast, hir, call_graph, mir, bytecode) 19 | #[structopt(long)] 20 | pub debug: Vec, 21 | /// Specify an optimisation mode (none, fast, size) 22 | #[structopt(short, long, default_value = "none")] 23 | pub opt: OptMode, 24 | } 25 | 26 | pub fn compile Option, G: FnMut(SrcId, &str) -> Option>( 27 | src: String, 28 | src_id: SrcId, 29 | options: Options, 30 | mut writer: impl Write, 31 | mut get_file: F, 32 | mut make_src: G, 33 | ) -> Option { 34 | let (mut ast, mut syntax_errors) = parse_module(&src, src_id); 35 | 36 | // TODO: Write a proper module system you lazy git 37 | fn resolve_imports Option, G: FnMut(SrcId, &str) -> Option>( 38 | parent_src: SrcId, 39 | module: Option<&mut ast::Module>, 40 | imported: &mut HashMap, 41 | import_errors: &mut Vec, 42 | syntax_errors: &mut Vec, 43 | get_file: &mut F, 44 | make_src: &mut G, 45 | ) { 46 | if let Some(module) = module { 47 | let imports = std::mem::take(&mut module.imports); 48 | 49 | // TODO: Remove these in favour of the `ItemKind::Module` 50 | for import in imports { 51 | match make_src(parent_src, import.as_str()).and_then(|src_id| Some((src_id, get_file(src_id)?))) { 52 | Some((src_id, src)) => { 53 | // Check for cycles 54 | if imported.insert(src_id, src.clone()).is_none() { 55 | let (mut ast, mut new_syntax_errors) = parse_module(&src, src_id); 56 | syntax_errors.append(&mut new_syntax_errors); 57 | 58 | resolve_imports(src_id, ast.as_deref_mut(), imported, import_errors, syntax_errors, get_file, make_src); 59 | 60 | if let Some(ast) = ast { 61 | let mut old_items = std::mem::take(&mut module.items); 62 | module.items.append(&mut ast.items.clone()); 63 | module.items.append(&mut old_items); 64 | } 65 | } 66 | }, 67 | None => import_errors.push(Error::CannotImport(import.clone())), 68 | } 69 | } 70 | 71 | let modules = std::mem::take(&mut module.modules); 72 | 73 | for (module_name, module_path) in modules { 74 | match make_src(parent_src, module_path.as_str()).and_then(|src_id| Some((src_id, get_file(src_id)?))) { 75 | Some((src_id, src)) => { 76 | // Check for cycles 77 | if imported.insert(src_id, src.clone()).is_none() { 78 | let (mut ast, mut new_syntax_errors) = parse_module(&src, src_id); 79 | syntax_errors.append(&mut new_syntax_errors); 80 | 81 | resolve_imports(src_id, ast.as_deref_mut(), imported, import_errors, syntax_errors, get_file, make_src); 82 | 83 | if let Some(ast) = ast { 84 | module.items.push(ast::Item { 85 | kind: ast::ItemKind::ModuleDecl(ast::ModuleDecl { 86 | name: module_name, 87 | module: ast.into_inner(), 88 | }), 89 | attrs: Vec::new(), // TODO? 90 | }); 91 | } 92 | } 93 | }, 94 | None => import_errors.push(Error::CannotImport(module_path.clone())), 95 | } 96 | } 97 | } 98 | } 99 | 100 | // Resolve imports 101 | let mut imported = HashMap::new(); 102 | let mut import_errors = Vec::new(); 103 | resolve_imports(src_id, ast.as_deref_mut(), &mut imported, &mut import_errors, &mut syntax_errors, &mut get_file, &mut make_src); 104 | let mut srcs = sources(imported.into_iter().chain(std::iter::once((src_id, src)))); 105 | if !import_errors.is_empty() { 106 | for e in import_errors { 107 | e.write(&mut srcs, &mut writer); 108 | } 109 | return None; 110 | } 111 | 112 | let mut syntax_error = false; 113 | for e in syntax_errors { 114 | syntax_error = true; 115 | e.write(&mut srcs, &mut writer); 116 | } 117 | 118 | if options.debug.contains(&"ast".to_string()) { 119 | writeln!(writer, "{:?}", ast).unwrap(); 120 | } 121 | 122 | if let Some(ast) = ast { 123 | let (ctx, analysis_errors) = HirContext::from_module(&ast); 124 | 125 | if options.debug.contains(&"hir".to_string()) { 126 | for (_, def) in ctx.defs.iter() { 127 | writeln!(writer, "{} = {:?}", *def.name, def.body).unwrap(); 128 | } 129 | } 130 | 131 | #[cfg(feature = "debug")] 132 | if options.debug.contains(&"call_graph".to_string()) { 133 | ctx 134 | .generate_call_graph() 135 | .render_to(&mut writer) 136 | .unwrap(); 137 | } 138 | 139 | if !analysis_errors.is_empty() || syntax_error { 140 | for e in analysis_errors { 141 | e.write(&ctx, &mut srcs, src_id, &mut writer); 142 | } 143 | None 144 | } else { 145 | let (concrete, con_errors) = ctx.concretize(); 146 | 147 | if !con_errors.is_empty() { 148 | for e in con_errors { 149 | e.write(&ctx, &mut srcs, src_id, &mut writer); 150 | } 151 | None 152 | } else { 153 | let mut ctx = Context::from_concrete(&ctx, &concrete); 154 | 155 | ctx.optimize(options.opt); 156 | 157 | if options.debug.contains(&"mir".to_string()) { 158 | for (id, proc) in ctx.procs.iter() { 159 | writeln!(writer, "PROCEDURE {:?}\n\n{}\n", id, proc.body.print()).unwrap(); 160 | } 161 | } 162 | 163 | let prog = Program::from_mir(&ctx); 164 | 165 | if options.debug.contains(&"bytecode".to_string()) { 166 | prog.write(&mut writer); 167 | } 168 | 169 | Some(prog) 170 | } 171 | } 172 | } else { 173 | None 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /syntax/src/error.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::{ 3 | collections::HashSet, 4 | io::Write, 5 | }; 6 | 7 | #[derive(Debug, PartialEq)] 8 | pub enum ErrorKind { 9 | UnexpectedEnd, 10 | Unexpected(Pattern), 11 | Unclosed { start: Pattern, before_span: Span, before: Option }, 12 | NoEndBranch, 13 | } 14 | 15 | #[derive(Debug)] 16 | pub struct Error { 17 | kind: ErrorKind, 18 | span: Span, 19 | while_parsing: Option<(Span, &'static str)>, 20 | // expected: HashSet, 21 | label: Option<&'static str>, 22 | } 23 | 24 | impl Error { 25 | pub fn expected(mut self, pat: Pattern) -> Self { 26 | // self.expected.insert(pat); 27 | self 28 | } 29 | 30 | pub fn while_parsing(mut self, span: Span, structure: &'static str) -> Self { 31 | self.while_parsing = self.while_parsing.or_else(|| Some((span, structure))); 32 | self 33 | } 34 | } 35 | 36 | impl Error { 37 | pub fn new(kind: ErrorKind, span: Span) -> Self { 38 | Self { 39 | kind, 40 | span, 41 | while_parsing: None, 42 | // expected: HashSet::default(), 43 | label: None, 44 | } 45 | } 46 | 47 | pub fn merge(mut self, other: Self) -> Self { 48 | // TODO: Use HashSet 49 | // for expected in other.expected.into_iter() { 50 | // self.expected.insert(expected); 51 | // } 52 | self 53 | } 54 | 55 | pub fn write>(self, cache: C, writer: impl Write) { 56 | use ariadne::{Report, ReportKind, Label, Color, Fmt}; 57 | 58 | let msg = format!( 59 | "{}{}, expected {}", 60 | match &self.kind { 61 | ErrorKind::UnexpectedEnd => "Unexpected end of input".to_string(), 62 | ErrorKind::Unexpected(pat) => format!("Unexpected {}", pat.fg(Color::Red)), 63 | ErrorKind::Unclosed { start, .. } => format!("Unclosed delimiter {}", start.fg(Color::Red)), 64 | ErrorKind::NoEndBranch => format!("No end branch"), 65 | }, 66 | if let Some(label) = self.label { 67 | format!(" while parsing {}", label.fg(Color::Cyan)) 68 | } else { 69 | "".to_string() 70 | }, 71 | "something else".to_string(), 72 | // match self.expected.len() { 73 | // 0 => "something else".to_string(), 74 | // 1 => format!("{}", self.expected.into_iter().next().unwrap().fg(Color::Yellow)), 75 | // _ => format!("one of {}", self.expected.into_iter().map(|x| x.fg(Color::Yellow).to_string()).collect::>().join(", ")), 76 | // }, 77 | ); 78 | 79 | let report = Report::build(ReportKind::Error, self.span.src(), self.span.start()) 80 | .with_code(3) 81 | .with_message(msg) 82 | .with_label(Label::new(self.span) 83 | .with_message(match &self.kind { 84 | ErrorKind::UnexpectedEnd => "End of input".to_string(), 85 | ErrorKind::Unexpected(pat) => format!("Unexpected {}", pat.fg(Color::Red)), 86 | ErrorKind::Unclosed { start, .. } => format!("Delimiter {} is never closed", start.fg(Color::Red)), 87 | ErrorKind::NoEndBranch => format!("Requires a {} branch", "\\ ... => ...".fg(Color::Blue)), 88 | }) 89 | .with_color(Color::Red)); 90 | 91 | let report = if let ErrorKind::Unclosed { before, before_span, .. } = self.kind { 92 | report 93 | .with_label(Label::new(before_span) 94 | .with_message(format!("Must be closed before {}", match before { 95 | Some(before) => format!("this {}", before.fg(Color::Yellow)), 96 | None => "end of input".to_string(), 97 | })) 98 | .with_color(Color::Yellow)) 99 | } else { 100 | report 101 | }; 102 | 103 | let report = if let Some((while_parsing, s)) = self.while_parsing { 104 | report.with_label(Label::new(while_parsing) 105 | .with_message(format!("encountered while parsing this {}", s)) 106 | .with_color(Color::Blue)) 107 | } else { 108 | report 109 | }; 110 | 111 | report 112 | .finish() 113 | .write(cache, writer) 114 | .unwrap(); 115 | } 116 | } 117 | 118 | impl PartialEq for Error { 119 | fn eq(&self, other: &Self) -> bool { 120 | self.kind == other.kind 121 | && self.span == other.span 122 | && self.label == other.label 123 | } 124 | } 125 | 126 | impl> chumsky::Error for Error { 127 | type Span = Span; 128 | type Label = &'static str; 129 | 130 | fn expected_input_found>>( 131 | span: Self::Span, 132 | expected: Iter, 133 | found: Option, 134 | ) -> Self { 135 | Self { 136 | kind: found 137 | .map(Into::into) 138 | .map(ErrorKind::Unexpected) 139 | .unwrap_or(ErrorKind::UnexpectedEnd), 140 | span, 141 | while_parsing: None, 142 | // expected: expected 143 | // .into_iter() 144 | // .map(|x| x.map(Into::into).unwrap_or(Pattern::End)) 145 | // .collect(), 146 | label: None, 147 | } 148 | } 149 | 150 | fn unclosed_delimiter( 151 | span: Self::Span, 152 | start: T, 153 | before_span: Self::Span, 154 | expected: T, 155 | before: Option, 156 | ) -> Self { 157 | Self { 158 | kind: ErrorKind::Unclosed { 159 | start: start.into(), 160 | before_span, 161 | before: before.map(Into::into), 162 | }, 163 | span, 164 | while_parsing: None, 165 | // expected: std::iter::once(expected.into()).collect(), 166 | label: None, 167 | } 168 | } 169 | 170 | fn with_label(mut self, label: Self::Label) -> Self { 171 | self.label.get_or_insert(label); 172 | self 173 | } 174 | 175 | fn merge(self, other: Self) -> Self { 176 | Error::merge(self, other) 177 | } 178 | } 179 | 180 | #[derive(Debug, PartialEq, Eq, Hash)] 181 | pub enum Pattern { 182 | Char(char), 183 | Token(Token), 184 | Literal, 185 | TypeIdent, 186 | TermIdent, 187 | End, 188 | } 189 | 190 | impl From for Pattern { fn from(c: char) -> Self { Self::Char(c) } } 191 | impl From for Pattern { fn from(tok: Token) -> Self { Self::Token(tok) } } 192 | 193 | impl fmt::Display for Pattern { 194 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 195 | match self { 196 | Pattern::Token(token) => write!(f, "{}", token), 197 | Pattern::Char(c) => write!(f, "{:?}", c), 198 | Pattern::Literal => write!(f, "literal"), 199 | Pattern::TypeIdent => write!(f, "type name"), 200 | Pattern::TermIdent => write!(f, "identifier"), 201 | Pattern::End => write!(f, "end of input"), 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /vm/src/code.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::{ 3 | io::Write, 4 | rc::Rc, 5 | }; 6 | use im::Vector; 7 | 8 | #[derive(Clone, Debug)] 9 | pub enum Instr { 10 | Error(&'static str), 11 | Nop, 12 | Break, 13 | 14 | Call(isize), 15 | Ret, 16 | // Make a function using the relative offset and by capturing the last N items on the stack 17 | MakeFunc(isize, usize), 18 | ApplyFunc, 19 | 20 | MakeList(usize), // T * N => [T] 21 | IndexList(usize), // Nth field of list/tuple 22 | SkipListImm(usize), // (N..) fields of list/tuple 23 | SetList(usize), // Set Nth field of list 24 | LenList, 25 | JoinList, 26 | SkipList, 27 | TrimList, 28 | 29 | MakeSum(usize), 30 | IndexSum(usize), 31 | VariantSum, 32 | 33 | Jump(isize), 34 | IfNot, 35 | 36 | Imm(Value), 37 | Pop(usize), 38 | Replace, 39 | Swap, 40 | Dup, // Duplicate value on top of stack 41 | PushLocal, 42 | PopLocal(usize), // Don't push to stack 43 | GetLocal(usize), // Duplicate value in locals position (len - 1 - N) and put on stack 44 | 45 | NotBool, // Bool -> Bool 46 | NegInt, // Int -> Int 47 | NegReal, // Real -> Real 48 | 49 | Display, // ? -> Str 50 | Codepoint, // Char -> Int 51 | 52 | AddInt, // Int -> Int -> Int 53 | SubInt, // Int -> Int -> Int 54 | MulInt, 55 | 56 | EqInt, // Int -> Int -> Bool 57 | EqBool, // Bool -> Bool -> Bool 58 | EqChar, // Char -> Char -> Bool 59 | LessInt, 60 | MoreInt, 61 | LessEqInt, 62 | MoreEqInt, 63 | 64 | AndBool, // Bool -> Bool -> Bool 65 | 66 | Print, 67 | Input, 68 | Rand, 69 | 70 | // Make an effect object using the relative offset and by capturing the last N items on the stack 71 | MakeEffect(isize, usize), 72 | Propagate, 73 | Suspend(EffectId), 74 | Register(EffectId), 75 | Resume(EffectId), 76 | EndHandlers(usize), 77 | } 78 | 79 | impl Instr { 80 | pub fn bool(x: bool) -> Self { 81 | Self::Imm(Value::Sum(x as usize, Rc::new(Value::List(Vector::new())))) 82 | } 83 | } 84 | 85 | #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] 86 | pub struct Addr(pub usize); 87 | 88 | impl Addr { 89 | pub fn incr(self) -> Self { Self(self.0 + 1) } 90 | pub fn jump(self, rel: isize) -> Self { Self((self.0 as isize + rel) as usize) } 91 | pub fn jump_to(self, other: Self) -> isize { other.0 as isize - self.0 as isize } 92 | } 93 | 94 | #[derive(Default, Debug)] 95 | pub struct Program { 96 | pub instrs: Vec, 97 | debug: Vec<(Addr, String)>, 98 | pub entry: Addr, 99 | pub does_io: bool, 100 | } 101 | 102 | impl Program { 103 | pub fn debug(&mut self, msg: impl ToString) { 104 | self.debug.push((self.next_addr(), msg.to_string())); 105 | } 106 | 107 | pub fn last_addr(&self) -> Addr { Addr(self.instrs.len().saturating_sub(1)) } 108 | 109 | pub fn next_addr(&self) -> Addr { Addr(self.instrs.len()) } 110 | 111 | pub fn instr(&self, ip: Addr) -> Instr { 112 | self.instrs 113 | .get(ip.0) 114 | .cloned() 115 | .unwrap_or(Instr::Error("out of bounds instruction")) 116 | } 117 | 118 | pub fn push(&mut self, instr: Instr) -> Addr { 119 | let addr = self.next_addr(); 120 | self.instrs.push(instr); 121 | addr 122 | } 123 | 124 | pub fn fixup(&mut self, addr: Addr, tgt: Addr, make_instr: impl FnOnce(isize) -> Instr) { 125 | self.instrs[addr.0] = make_instr(addr.jump_to(tgt)); 126 | } 127 | 128 | pub fn write(&self, mut writer: impl Write) { 129 | let mut debug = self.debug.iter().peekable(); 130 | for addr in (0..self.instrs.len()).map(Addr) { 131 | while debug.peek().map_or(false, |(a, _)| *a == addr) { 132 | writeln!(writer, " ... | <--------- {}", debug.next().unwrap().1).unwrap(); 133 | } 134 | 135 | let instr = self.instr(addr); 136 | 137 | let stack_diff = match instr { 138 | Instr::Error(_) | Instr::Nop | Instr::Break => 0, 139 | Instr::Imm(_) => 1, 140 | Instr::Pop(n) => -(n as isize), 141 | Instr::Replace => -1, 142 | Instr::Swap => 0, 143 | Instr::Call(_) => 0, 144 | Instr::Ret => 0, 145 | Instr::MakeFunc(_, n) => -(n as isize), 146 | Instr::ApplyFunc => 0, // Turns input stack item into output stack item 147 | Instr::MakeList(n) => -(n as isize) + 1, 148 | Instr::IndexList(_) => 0, 149 | Instr::SkipListImm(_) => 0, 150 | Instr::SetList(_) => -1, 151 | Instr::LenList => 0, 152 | Instr::JoinList => -1, 153 | Instr::SkipList => -1, 154 | Instr::TrimList => -1, 155 | Instr::MakeSum(_) => 0, 156 | Instr::IndexSum(_) => 0, 157 | Instr::VariantSum => 0, 158 | Instr::Dup => 1, 159 | Instr::Jump(_) => 0, 160 | Instr::IfNot => -1, 161 | Instr::PushLocal => -1, 162 | Instr::PopLocal(_) => 0, 163 | Instr::GetLocal(_) => 1, 164 | Instr::NotBool 165 | | Instr::NegInt 166 | | Instr::NegReal 167 | | Instr::Display 168 | | Instr::Codepoint => 0, 169 | Instr::AddInt 170 | | Instr::SubInt 171 | | Instr::MulInt 172 | | Instr::EqInt 173 | | Instr::EqBool 174 | | Instr::EqChar 175 | | Instr::LessInt 176 | | Instr::MoreInt 177 | | Instr::LessEqInt 178 | | Instr::MoreEqInt 179 | | Instr::AndBool => -1, 180 | Instr::Print => -1, 181 | Instr::Input => 0, 182 | Instr::Rand => -1, 183 | Instr::MakeEffect(_, n) => -(n as isize), 184 | Instr::Propagate => 0, 185 | Instr::Suspend(_) => 0, 186 | Instr::Register(_) => -1, 187 | Instr::Resume(_) => 0, 188 | Instr::EndHandlers(_) => -2, 189 | }; 190 | 191 | let instr_display = match instr { 192 | Instr::Error(msg) => format!("error \"{}\"", msg), 193 | Instr::Nop => format!("nop"), 194 | Instr::Break => format!("break"), 195 | Instr::Imm(x) => format!("imm `{}`", x), 196 | Instr::Pop(n) => format!("pop {}", n), 197 | Instr::Replace => format!("replace"), 198 | Instr::Swap => format!("swap"), 199 | Instr::Call(x) => format!("call {:+} (0x{:03X})", x, addr.jump(x).0), 200 | Instr::Ret => format!("ret"), 201 | Instr::MakeFunc(i, n) => format!("func.make {:+} (0x{:03X}) {}", i, addr.jump(i).0, n), 202 | Instr::ApplyFunc => format!("func.apply"), 203 | Instr::MakeList(n) => format!("list.make {}", n), 204 | Instr::IndexList(i) => format!("list.index #{}", i), 205 | Instr::SkipListImm(i) => format!("list.skip_imm #{}", i), 206 | Instr::SetList(idx) => format!("list.set #{}", idx), 207 | Instr::LenList => format!("list.len"), 208 | Instr::JoinList => format!("list.join"), 209 | Instr::SkipList => format!("list.skip"), 210 | Instr::TrimList => format!("list.trim"), 211 | Instr::MakeSum(i) => format!("sum.make #{}", i), 212 | Instr::IndexSum(i) => format!("sum.index #{}", i), 213 | Instr::VariantSum => format!("sum.variant"), 214 | Instr::Dup => format!("dup"), 215 | Instr::Jump(x) => format!("jump {:+} (0x{:03X})", x, addr.jump(x).0), 216 | Instr::IfNot => format!("if_not"), 217 | Instr::PushLocal => format!("local.push"), 218 | Instr::PopLocal(n) => format!("local.pop {}", n), 219 | Instr::GetLocal(x) => format!("local.get +{}", x), 220 | Instr::NotBool => format!("bool.not"), 221 | Instr::NegInt => format!("int.neg"), 222 | Instr::NegReal => format!("real.neg"), 223 | Instr::Display => format!("any.display"), 224 | Instr::Codepoint => format!("char.codepoint"), 225 | Instr::AddInt => format!("int.add"), 226 | Instr::SubInt => format!("int.sub"), 227 | Instr::MulInt => format!("int.mul"), 228 | Instr::EqInt => format!("int.eq"), 229 | Instr::EqBool => format!("bool.eq"), 230 | Instr::EqChar => format!("char.eq"), 231 | Instr::LessInt => format!("int.less"), 232 | Instr::MoreInt => format!("int.more"), 233 | Instr::LessEqInt => format!("int.less_eq"), 234 | Instr::MoreEqInt => format!("int.more_eq"), 235 | Instr::AndBool => format!("bool.and"), 236 | Instr::Print => format!("io.print"), 237 | Instr::Input => format!("io.input"), 238 | Instr::Rand => format!("io.rand"), 239 | Instr::MakeEffect(i, n) => format!("eff.make {:+} (0x{:03X}) {}", i, addr.jump(i).0, n), 240 | Instr::Propagate => format!("eff.propagate"), 241 | Instr::Suspend(eff) => format!("eff.suspend {:?}", eff), 242 | Instr::Register(eff) => format!("eff.register {:?}", eff), 243 | Instr::Resume(eff) => format!("eff.resume {:?}", eff), 244 | Instr::EndHandlers(n) => format!("eff.end_handlers {}", n), 245 | }; 246 | 247 | writeln!(writer, "0x{:03X} | {:>+3} | {}", addr.0, stack_diff, instr_display).unwrap(); 248 | } 249 | 250 | writeln!(writer, "{} instructions in total.", self.instrs.len()).unwrap(); 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /analysis/src/data.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub struct Data { 4 | pub name: SrcNode, 5 | pub attr: Vec>, 6 | pub gen_scope: GenScopeId, 7 | pub variance_ty: Option>, 8 | pub variance_eff: Option>, 9 | pub cons: Vec<(SrcNode, TyId)>, 10 | } 11 | 12 | #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] 13 | pub struct DataId(usize, Ident); 14 | 15 | impl fmt::Debug for DataId { 16 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 17 | write!(f, "{}", self.1) 18 | } 19 | } 20 | 21 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] 22 | pub struct AliasId(usize); 23 | 24 | pub struct Alias { 25 | pub name: SrcNode, 26 | pub attr: Vec>, 27 | pub gen_scope: GenScopeId, 28 | pub ty: TyId, 29 | } 30 | 31 | #[derive(Default)] 32 | pub struct Lang { 33 | pub go: Option, 34 | pub r#bool: Option, 35 | } 36 | 37 | #[derive(Default)] 38 | pub struct Datas { 39 | // TODO: Don't use `Result` 40 | name_lut: HashMap, GenScopeId)>, 41 | cons_lut: HashMap, 42 | alias_lut: HashMap, 43 | datas: Vec<(Span, Option, GenScopeId, Ident)>, 44 | aliases: Vec<(Span, Option, GenScopeId)>, 45 | pub lang: Lang, 46 | } 47 | 48 | impl Datas { 49 | pub fn iter_datas(&self) -> impl Iterator + '_ { 50 | (0..self.datas.len()).map(|i| DataId(i, Ident::new(self.datas[i].3))) 51 | } 52 | 53 | pub fn iter_aliases(&self) -> impl Iterator { 54 | (0..self.aliases.len()).map(|i| AliasId(i)) 55 | } 56 | 57 | pub fn data_gen_scope(&self, data: DataId) -> GenScopeId { 58 | self.datas[data.0].2 59 | } 60 | 61 | pub fn alias_gen_scope(&self, alias: AliasId) -> GenScopeId { 62 | self.aliases[alias.0].2 63 | } 64 | 65 | pub fn name_gen_scope(&self, name: Ident) -> GenScopeId { 66 | self.name_lut[&name].2 67 | } 68 | 69 | pub fn lookup_data(&self, name: Ident) -> Option { 70 | self.name_lut 71 | .get(&name) 72 | .and_then(|data| data.1.as_ref().ok()) 73 | .copied() 74 | } 75 | 76 | pub fn lookup_alias(&self, name: Ident) -> Option { 77 | self.name_lut 78 | .get(&name) 79 | .and_then(|data| data.1.as_ref().err()) 80 | .copied() 81 | } 82 | 83 | pub fn lookup_cons(&self, name: Ident) -> Option { 84 | self.cons_lut.get(&name).map(|(_, id)| *id) 85 | } 86 | 87 | pub fn get_data(&self, data: DataId) -> &Data { 88 | self.datas[data.0] 89 | .1 90 | .as_ref() 91 | .expect("Declared data accessed before being defined") 92 | } 93 | 94 | pub fn get_data_span(&self, data: DataId) -> Span { 95 | self.datas[data.0].0 96 | } 97 | 98 | pub fn get_alias(&self, alias: AliasId) -> Option<&Alias> { 99 | self.aliases[alias.0] 100 | .1 101 | .as_ref() 102 | } 103 | 104 | pub fn get_alias_span(&self, alias: AliasId) -> Span { 105 | self.aliases[alias.0].0 106 | } 107 | 108 | pub fn declare_data(&mut self, name: SrcNode, gen_scope: GenScopeId, attr: &[SrcNode]) -> Result { 109 | let id = DataId(self.datas.len(), *name); 110 | if let Err(old) = self.name_lut.try_insert(*name, (name.span(), Ok(id), gen_scope)) { 111 | Err(Error::DuplicateTypeName(*name, old.entry.get().0, name.span())) 112 | } else { 113 | if let Some(lang) = attr 114 | .iter() 115 | .find(|a| &**a.name == "lang") 116 | .and_then(|a| a.args.as_ref()) 117 | { 118 | if lang.iter().find(|a| &**a.name == "go").is_some() { self.lang.go = Some(id); } 119 | if lang.iter().find(|a| &**a.name == "bool").is_some() { self.lang.r#bool = Some(id); } 120 | } 121 | 122 | self.datas.push((name.span(), None, gen_scope, *name)); 123 | Ok(id) 124 | } 125 | } 126 | 127 | pub fn check_lang_items(&self) -> Vec { 128 | let mut errors = Vec::new(); 129 | 130 | if self.lang.go.is_none() { errors.push(Error::MissingLangItem("go")); } 131 | if self.lang.r#bool.is_none() { errors.push(Error::MissingLangItem("bool")); } 132 | 133 | errors 134 | } 135 | 136 | pub fn declare_alias(&mut self, name: Ident, span: Span, gen_scope: GenScopeId) -> Result { 137 | let id = AliasId(self.aliases.len()); 138 | if let Err(old) = self.name_lut.try_insert(name, (span, Err(id), gen_scope)) { 139 | Err(Error::DuplicateTypeName(name, old.entry.get().0, span)) 140 | } else { 141 | self.aliases.push((span, None, gen_scope)); 142 | Ok(id) 143 | } 144 | } 145 | 146 | pub fn define_data(&mut self, id: DataId, span: Span, data: Data) -> Result<(), Vec> { 147 | let mut errors = Vec::new(); 148 | for (cons, _) in &data.cons { 149 | if let Err(old) = self.cons_lut.try_insert(**cons, (cons.span(), id)) { 150 | errors.push(Error::DuplicateConsName(**cons, old.entry.get().0, cons.span())); 151 | } 152 | } 153 | self.datas[id.0].1 = Some(data); 154 | if errors.len() == 0 { 155 | Ok(()) 156 | } else { 157 | Err(errors) 158 | } 159 | } 160 | 161 | pub fn define_alias(&mut self, id: AliasId, alias: Alias) { 162 | self.aliases[id.0].1 = Some(alias); 163 | } 164 | 165 | // Result 166 | pub fn derive_variance_ty(&mut self, tys: &Types, ty: TyId, stack: &mut Vec, apply_variance: &mut dyn FnMut(Result, Variance)) { 167 | match tys.get(ty) { 168 | Ty::Error(_) | Ty::Prim(_) | Ty::SelfType => {}, 169 | Ty::Gen(idx, _) => apply_variance(Ok(idx), Variance::Out), 170 | Ty::List(item) => self.derive_variance_ty(tys, item, stack, apply_variance), 171 | Ty::Record(fields, _) => fields.values().for_each(|field| self.derive_variance_ty(tys, *field, stack, apply_variance)), 172 | Ty::Func(i, o) => { 173 | self.derive_variance_ty(tys, i, stack, &mut |idx, var| apply_variance(idx, var.flip())); // Contravariance 174 | self.derive_variance_ty(tys, o, stack, apply_variance); 175 | }, 176 | Ty::Data(data_id, gen_tys, _gen_effs) => { 177 | self.derive_variance_for(tys, data_id, stack); 178 | 179 | let ty_variances = self.get_data(data_id).variance_ty.clone() 180 | // If recursive, no variance applied (TODO: is this sound?) 181 | .unwrap_or_else(|| vec![Variance::None; gen_tys.len()]); 182 | for (i, ty) in gen_tys.into_iter().enumerate() { 183 | self.derive_variance_ty(tys, ty, stack, &mut |idx, var| apply_variance(idx, var.project_through(ty_variances[i]))); 184 | } 185 | // TODO: effects 186 | }, 187 | // Everything projected through an associated type is invariant 188 | Ty::Assoc(ty, (_, gen_tys, _gen_effs), _) => { 189 | // TODO: Does the self type need to be invariant? 190 | self.derive_variance_ty(tys, ty, stack, &mut |idx, var| apply_variance(idx, var.combine(Variance::InOut))); 191 | for ty in gen_tys { 192 | self.derive_variance_ty(tys, ty, stack, &mut |idx, var| apply_variance(idx, var.combine(Variance::InOut))); 193 | } 194 | // TODO: effects 195 | }, 196 | Ty::Effect(eff, ty) => { 197 | self.derive_variance_ty(tys, ty, stack, apply_variance); 198 | match tys.get_effect(eff) { 199 | Effect::Error => {}, 200 | Effect::Known(effs) => effs 201 | .iter() 202 | .filter_map(|eff| eff.as_ref().ok()) 203 | .for_each(|eff_inst| match eff_inst { 204 | EffectInst::Gen(idx, _) => apply_variance(Err(*idx), Variance::InOut), 205 | EffectInst::Concrete(_, gen_tys, _gen_effs) => { 206 | for ty in gen_tys { 207 | self.derive_variance_ty(tys, *ty, stack, &mut |idx, var| apply_variance(idx, var.combine(Variance::InOut))); 208 | } 209 | // TODO: effects 210 | }, 211 | }), 212 | } 213 | }, 214 | ty => todo!("{ty:?}"), 215 | } 216 | } 217 | 218 | pub fn derive_variance_for(&mut self, tys: &Types, data_id: DataId, stack: &mut Vec) { 219 | let data = self.get_data(data_id); 220 | if stack.contains(&data_id) { 221 | // Recursive 222 | } else if data.variance_ty.is_none() { 223 | let mut ty_variances = vec![Variance::None; tys.get_gen_scope(data.gen_scope).len()]; 224 | let mut eff_variances = vec![Variance::None; tys.get_gen_scope(data.gen_scope).len_eff()]; 225 | for (_, ty) in data.cons.clone() { 226 | stack.push(data_id); 227 | self.derive_variance_ty(tys, ty, stack, &mut |idx, var| match idx { 228 | Ok(idx) => ty_variances[idx] = ty_variances[idx].combine(var), 229 | Err(idx) => eff_variances[idx] = eff_variances[idx].combine(var), 230 | }); 231 | stack.pop(); 232 | } 233 | self.datas[data_id.0].1.as_mut().unwrap().variance_ty = Some(ty_variances); 234 | self.datas[data_id.0].1.as_mut().unwrap().variance_eff = Some(eff_variances); 235 | } 236 | } 237 | 238 | pub fn derive_variance(&mut self, tys: &Types) { 239 | for data_id in self.iter_datas().collect::>() { 240 | self.derive_variance_for(tys, data_id, &mut Vec::new()); 241 | } 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /misc/call_graph.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | Program 11 | 12 | 13 | 14 | def_21 15 | 16 | char_to_num 17 | 18 | 19 | 20 | def_22 21 | 22 | digit 23 | 24 | 25 | 26 | def_22->def_21 27 | 28 | 29 | 30 | 31 | 32 | def_23 33 | 34 | number 35 | 36 | 37 | 38 | def_23->def_22 39 | 40 | 41 | 42 | 43 | 44 | def_24 45 | 46 | atom 47 | 48 | 49 | 50 | def_24->def_23 51 | 52 | 53 | 54 | 55 | 56 | def_29 57 | 58 | expr_parser 59 | 60 | 61 | 62 | def_24->def_29 63 | 64 | 65 | 66 | 67 | 68 | def_25 69 | 70 | op 71 | 72 | 73 | 74 | def_26 75 | 76 | unary 77 | 78 | 79 | 80 | def_26->def_24 81 | 82 | 83 | 84 | 85 | 86 | def_26->def_25 87 | 88 | 89 | 90 | 91 | 92 | def_27 93 | 94 | product_parser 95 | 96 | 97 | 98 | def_27->def_25 99 | 100 | 101 | 102 | 103 | 104 | def_27->def_26 105 | 106 | 107 | 108 | 109 | 110 | def_28 111 | 112 | sum_parser 113 | 114 | 115 | 116 | def_28->def_25 117 | 118 | 119 | 120 | 121 | 122 | def_28->def_27 123 | 124 | 125 | 126 | 127 | 128 | def_29->def_28 129 | 130 | 131 | 132 | 133 | 134 | def_30 135 | 136 | eval 137 | 138 | 139 | 140 | def_30->def_30 141 | 142 | 143 | 144 | 145 | 146 | def_85 147 | 148 | prompt 149 | 150 | 151 | 152 | def_85->def_29 153 | 154 | 155 | 156 | 157 | 158 | def_85->def_30 159 | 160 | 161 | 162 | 163 | 164 | def_85->def_85 165 | 166 | 167 | 168 | 169 | 170 | def_86 171 | 172 | main 173 | 174 | 175 | 176 | def_86->def_85 177 | 178 | 179 | 180 | 181 | 182 | --------------------------------------------------------------------------------