├── test.ceu ├── src ├── test │ ├── ceu │ │ ├── .gitignore │ │ ├── 01-hello.ceu │ │ ├── 41-random.ceu │ │ ├── 06-types.ceu │ │ ├── 14-groups.ceu │ │ ├── 22-cast.ceu │ │ ├── 11-lambdas.ceu │ │ ├── 13-tests.ceu │ │ ├── 05-comments.ceu │ │ ├── 24-where-thus.ceu │ │ ├── 07-collections.ceu │ │ ├── 04-literals.ceu │ │ ├── 42-hello.ceu │ │ ├── 15-defers.ceu │ │ ├── 38-toggle.ceu │ │ ├── 10-prototypes.ceu │ │ ├── 23-ppp.ceu │ │ ├── 25-precedence.ceu │ │ ├── 09-collections.ceu │ │ ├── 26-ifs.ceu │ │ ├── 29-iterators.ceu │ │ ├── 12-actives.ceu │ │ ├── 40-math.ceu │ │ ├── 34-awaits.ceu │ │ ├── 08-user-types.ceu │ │ ├── 20-calls.ceu │ │ ├── 17-assignments.ceu │ │ ├── xx-data.ceu │ │ ├── 21-indexes.ceu │ │ ├── 18-enums.ceu │ │ ├── 35-broadcasts.ceu │ │ ├── 03-templates.ceu │ │ ├── 28-loops.ceu │ │ ├── 30-errors.ceu │ │ ├── 02-tags.ceu │ │ ├── 16-declarations.ceu │ │ ├── 32-resume-yield-all.ceu │ │ ├── 13-blocks.ceu │ │ ├── 36-sugar.ceu │ │ ├── 19-templates.ceu │ │ ├── 37-pars.ceu │ │ ├── 31-coroutines.ceu │ │ ├── 27-match.ceu │ │ ├── 39-basic.ceu │ │ ├── 25-operators.ceu │ │ └── 33-tasks.ceu │ └── kotlin │ │ ├── tst_50 │ │ ├── Lexer_50.kt │ │ └── Parser_50.kt │ │ ├── tst_04 │ │ ├── Lexer_04.kt │ │ └── Parser_04.kt │ │ ├── tst_03 │ │ ├── Lexer_03.kt │ │ └── Parser_03.kt │ │ ├── tst_02 │ │ ├── Lexer_02.kt │ │ └── Parser_02.kt │ │ ├── tst_05 │ │ ├── Lexer_05.kt │ │ └── Parser_05.kt │ │ └── tst_99 │ │ ├── Lexer_99.kt │ │ └── Book_99.kt ├── main │ ├── resources │ │ └── META-INF │ │ │ └── MANIFEST.MF │ └── kotlin │ │ ├── Ups.kt │ │ ├── Prelude.kt │ │ ├── Optim.kt │ │ ├── Cache.kt │ │ ├── Tostr.kt │ │ ├── Aux.kt │ │ ├── Expr.kt │ │ ├── Mem.kt │ │ ├── Main.kt │ │ ├── Lexer.kt │ │ └── Checks.kt └── README.md ├── .idea ├── .gitignore ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── vcs.xml ├── modules.xml ├── misc.xml ├── kotlinc.xml ├── libraries │ ├── junit.xml │ ├── jetbrains_kotlinx_coroutines_core_jvm.xml │ ├── jetbrains_kotlinx_coroutines_core.xml │ └── KotlinJavaRuntime.xml ├── artifacts │ └── dceu_jar.xml └── uiDesigner.xml ├── doc ├── bcast.dia ├── bcast.png ├── manual.lua └── lexical.md ├── Makefile ├── .gitignore ├── ceu.vim ├── dceu.iml ├── HISTORY.md ├── README.md ├── TODO.txt └── hold.txt /test.ceu: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | -------------------------------------------------------------------------------- /src/test/ceu/.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.c 3 | -------------------------------------------------------------------------------- /src/test/ceu/01-hello.ceu: -------------------------------------------------------------------------------- 1 | println([1,2,3]) 2 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /doc/bcast.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsantanna-no/dceu/HEAD/doc/bcast.dia -------------------------------------------------------------------------------- /doc/bcast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsantanna-no/dceu/HEAD/doc/bcast.png -------------------------------------------------------------------------------- /src/test/ceu/41-random.ceu: -------------------------------------------------------------------------------- 1 | random.seed(0) 2 | random.next() --> println ;; --> 3 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: MainKt 3 | 4 | -------------------------------------------------------------------------------- /src/test/ceu/06-types.ceu: -------------------------------------------------------------------------------- 1 | type(10) -> println ;; --> :number 2 | type('x') -> println ;; --> :char 3 | -------------------------------------------------------------------------------- /src/test/ceu/14-groups.ceu: -------------------------------------------------------------------------------- 1 | group { 2 | val x = 10 3 | val y = x * 2 4 | } 5 | println(x, y) ;; --> 10 20 6 | -------------------------------------------------------------------------------- /src/test/ceu/22-cast.ceu: -------------------------------------------------------------------------------- 1 | data :Pos = [x,y] 2 | val p = [10,20] 3 | println(p.(:Pos).x) ;; `p` is cast to `:Pos` 4 | -------------------------------------------------------------------------------- /src/test/ceu/11-lambdas.ceu: -------------------------------------------------------------------------------- 1 | val f = { \x,y => x+y } 2 | println(f(10,20)) ;; --> 30 3 | 4 | println({it}(10)) ;; --> 10 5 | -------------------------------------------------------------------------------- /src/test/ceu/13-tests.ceu: -------------------------------------------------------------------------------- 1 | func add (x,y) { 2 | x + y 3 | } 4 | test { 5 | println(:testing) 6 | assert(add(10,20) == 30) 7 | } 8 | -------------------------------------------------------------------------------- /src/test/ceu/05-comments.ceu: -------------------------------------------------------------------------------- 1 | ;; a comment ;; single-line comment 2 | ;;; ;; multi-line comment 3 | ;; a 4 | ;; comment 5 | ;;; 6 | -------------------------------------------------------------------------------- /src/test/ceu/24-where-thus.ceu: -------------------------------------------------------------------------------- 1 | var x = (2 * y) where { ;; x = 20 2 | var y = 10 3 | } 4 | 5 | (x * x) thus { \v => 6 | println(v) ;; --> 400 7 | } 8 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/test/ceu/07-collections.ceu: -------------------------------------------------------------------------------- 1 | [1, 'a', nil] -> println ;; a tuple with 3 values 2 | #[1, 2, 3] -> println ;; a vector of numbers 3 | @[(:x,10), (:y,20)] -> println ;; a dictionary with 2 mappings 4 | -------------------------------------------------------------------------------- /src/test/ceu/04-literals.ceu: -------------------------------------------------------------------------------- 1 | val n = `:number 10` ;; native 10 is converted to Ceu number 2 | val x = `:ceu $n` ;; `x` is set to Ceu `n` as is 3 | `printf("> %f\n", $n.Number);` ;; outputs `n` as a number 4 | -------------------------------------------------------------------------------- /src/test/ceu/42-hello.ceu: -------------------------------------------------------------------------------- 1 | spawn { 2 | watching <1:s> { 3 | every <100:ms> { 4 | println("Hello World!") 5 | } 6 | } 7 | } 8 | 9 | loop in {1=>10} { 10 | broadcast(:Clock [100]) 11 | } 12 | -------------------------------------------------------------------------------- /src/test/ceu/15-defers.ceu: -------------------------------------------------------------------------------- 1 | do { 2 | println(1) 3 | defer { 4 | println(2) ;; last to execute 5 | } 6 | defer { 7 | println(3) 8 | } 9 | println(4) 10 | } ;; --> 1, 4, 3, 2 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | install: 2 | mkdir -p $(DIR) 3 | cp out/artifacts/dceu_jar/dceu.jar $(DIR)/ceu.jar 4 | cp build/ceu.sh $(DIR)/ceu 5 | cp build/prelude.ceu $(DIR)/prelude.ceu 6 | ls -l $(DIR)/ 7 | $(DIR)/ceu --version 8 | $(DIR)/ceu build/hello-world.ceu 9 | -------------------------------------------------------------------------------- /src/test/ceu/38-toggle.ceu: -------------------------------------------------------------------------------- 1 | spawn { 2 | toggle :T { 3 | every :E { 4 | println(it[0]) ;; --> 1 3 5 | } 6 | } 7 | } 8 | broadcast(:E [1]) 9 | broadcast(:T [false]) 10 | broadcast(:E [2]) 11 | broadcast(:T [true]) 12 | broadcast(:E [3]) 13 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/test/ceu/10-prototypes.ceu: -------------------------------------------------------------------------------- 1 | func (v) { v } ;; a function 2 | coro () { yield() } ;; a coroutine 3 | task () { await(:X) } ;; a task 4 | 5 | func (v1) { ;; a closure 6 | func () { 7 | v1 ;; v1 is an upvalue 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/ceu/23-ppp.ceu: -------------------------------------------------------------------------------- 1 | val stk = #[1,2,3] 2 | println(stk[=]) ;; --> 3 3 | set stk[=] = 30 4 | println(stk) ;; --> #[1, 2, 30] 5 | println(stk[-]) ;; --> 30 6 | println(stk) ;; --> #[1, 2] 7 | set stk[+] = 3 8 | println(stk) ;; --> #[1, 2, 3] 9 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/test/ceu/25-precedence.ceu: -------------------------------------------------------------------------------- 1 | func f (v) { @[x=[v]] } 2 | val x = 10 3 | val y = 10 4 | val z = false 5 | 6 | #f(10).x --> println ;; # ((f(10)) .x) 7 | (x + 10) - 1 --> println ;; ERR: requires parenthesis 8 | - x + y --> println ;; (-x) + y 9 | x or y or z --> println ;; (x or y) or z 10 | -------------------------------------------------------------------------------- /src/test/ceu/09-collections.ceu: -------------------------------------------------------------------------------- 1 | [1,2,3] -> println ;; a tuple 2 | (:Pos [10,10]) -> println ;; a tagged tuple 3 | #[1,2,3] -> println ;; a vector 4 | "abc" -> println ;; a character vector ['a','b','c'] 5 | @[(:x,10), x=10] -> println ;; a dictionary with equivalent key mappings 6 | 7 | -------------------------------------------------------------------------------- /src/test/ceu/26-ifs.ceu: -------------------------------------------------------------------------------- 1 | val x = 10 2 | val y = 100 3 | 4 | val max = if x>y => x => y 5 | println(max) 6 | 7 | ifs { 8 | x < y => x 9 | x > y => y 10 | else => error(:error, "values are equal") 11 | } -> println 12 | 13 | if :f { \v => 14 | println("f() evaluates to " ++ to.string(v)) 15 | } else { 16 | println("f() evaluates to false") 17 | } 18 | 19 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /src/test/ceu/29-iterators.ceu: -------------------------------------------------------------------------------- 1 | loop v in [10,20,30] { ;; implicit to-iter([10,20,30]) 2 | println(v) ;; --> 10,20,30 3 | } 4 | 5 | 6 | func num-iter (N) { 7 | val f = func (t) { 8 | val v = t[2] 9 | set t[2] = v + 1 10 | ((v < N) and v) or nil 11 | } 12 | :Iterator [f, N, 0] 13 | } 14 | loop in num-iter(5) { 15 | println(it) ;; --> 0,1,2,3,4 16 | } 17 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /src/test/ceu/12-actives.ceu: -------------------------------------------------------------------------------- 1 | coro C () { println(:1) } ;; a coro prototype `C` 2 | val c = coroutine(C) ;; is instantiated as `c` 3 | resume c() ;; and resumed explicitly 4 | 5 | val ts = tasks() ;; a task pool `ts` 6 | task T () { println(:2) } ;; a task prototype `T` 7 | val t = spawn T() in ts ;; is instantiated as `t` in pool `ts` 8 | broadcast(:X) ;; broadcast resumes `t` 9 | 10 | -------------------------------------------------------------------------------- /.idea/libraries/junit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### IntelliJ IDEA ### 2 | out/ 3 | !**/src/main/**/out/ 4 | !**/src/test/**/out/ 5 | 6 | ### Eclipse ### 7 | .apt_generated 8 | .classpath 9 | .factorypath 10 | .project 11 | .settings 12 | .springBeans 13 | .sts4-cache 14 | bin/ 15 | !**/src/main/**/bin/ 16 | !**/src/test/**/bin/ 17 | 18 | ### NetBeans ### 19 | /nbproject/private/ 20 | /nbbuild/ 21 | /dist/ 22 | /nbdist/ 23 | /.nb-gradle/ 24 | 25 | ### VS Code ### 26 | .vscode/ 27 | 28 | ### Mac OS ### 29 | .DS_Store -------------------------------------------------------------------------------- /src/test/ceu/40-math.ceu: -------------------------------------------------------------------------------- 1 | math.PI --> println ;; --> 3.14 2 | math.sin(math.PI) --> println ;; --> 0 3 | math.cos(math.PI) --> println ;; --> -1 4 | 5 | math.ceil(10.14) --> println ;; --> 15 6 | math.floor(10.14) --> println ;; --> 10 7 | math.round(10.14) --> println ;; --> 10 8 | 9 | math.min(10,20) --> println ;; --> 10 10 | math.max(10,20) --> println ;; --> 20 11 | math.between(10, 8, 20) --> println ;; --> 10 12 | -------------------------------------------------------------------------------- /src/test/ceu/34-awaits.ceu: -------------------------------------------------------------------------------- 1 | data :key = [code] 2 | 3 | spawn { 4 | await(|true) ;; never awakes 5 | await(:key | it.code==:escape) ;; awakes on :key with code=:escape 6 | await <1:min 30:s> ;; awakes after the specified time 7 | await e { ;; awakes on any event 8 | println(e) ;; and shows it 9 | } 10 | } 11 | 12 | broadcast(nil) 13 | broadcast(:key [:escape]) 14 | broadcast(:Clock [100000]) 15 | broadcast(:X [1,2,3]) 16 | -------------------------------------------------------------------------------- /src/test/ceu/08-user-types.ceu: -------------------------------------------------------------------------------- 1 | do { 2 | val x = [] ;; an empty tuple 3 | tag(:T, x) ;; x is now of user type :T 4 | println(tag(x)) ;; --> :T 5 | } 6 | 7 | println(sup?(:T, :T.A)) ;; --> true 8 | println(sup?(:T.A, :T)) ;; --> false 9 | println(sup?(:T.A, :T.B)) ;; --> false 10 | 11 | do { 12 | val x = [] ;; an empty tuple 13 | tag(:T.A, x) ;; x is now of user type :T.A 14 | println(x is? :tuple) ;; --> true 15 | println(x is? :T) ;; --> true 16 | } 17 | -------------------------------------------------------------------------------- /src/test/ceu/20-calls.ceu: -------------------------------------------------------------------------------- 1 | val vec = [10] 2 | #vec --> println ;; unary operation 3 | 4 | val x = 100 5 | x - 10 --> println ;; binary operation 6 | {{-}}(x,10) --> println ;; operation as call 7 | 8 | do { 9 | func f (a,b) { a - b } 10 | f(10,20) --> println ;; normal call 11 | } 12 | 13 | do { 14 | func g (v) { 2*v } 15 | func f (v1,v2) { v1 + (v2 or 0) } 16 | val t = 10 17 | println(f <-- 10 -> g) ;; equivalent to `f(g(10))` 18 | println(t -> f(10)) ;; equivalent to `f(t,10)` 19 | } 20 | -------------------------------------------------------------------------------- /src/test/ceu/17-assignments.ceu: -------------------------------------------------------------------------------- 1 | var x 2 | set x = 20 ;; OK 3 | println(x) 4 | 5 | val y = [10] 6 | ;;set y = 0 ;; ERR: cannot reassign `y` 7 | set y[0] = 20 ;; OK 8 | println(y) 9 | 10 | data :Pos = [x,y] 11 | val p1 :Pos = [10,20] ;; (assumes :Pos has fields [x,y]) 12 | println(p1.x) ;; --> 10 13 | 14 | val p2 = :Pos [10,20] ;; (assumes :Pos has fields [x,y]) 15 | println(p2.y) ;; --> 20 16 | 17 | task T (v) { 18 | set pub = v ;; OK 19 | await(|false) 20 | } 21 | val t = spawn T(10) 22 | println(t.pub) 23 | -------------------------------------------------------------------------------- /src/test/ceu/xx-data.ceu: -------------------------------------------------------------------------------- 1 | data :Pos = [x,y] ;; a flat template 2 | val pos :Pos = [10,20] ;; pos uses :Pos as template 3 | println(pos.x, pos.y) ;; --> 10, 20 4 | 5 | data :Dim = [w,h] 6 | data :Rect = [pos :Pos, dim :Dim] ;; a nested template 7 | val r1 :Rect = [pos, [100,100]] ;; r uses :Rect as template 8 | println(r1.dim, r1.pos.x) ;; --> [100,100], 10 9 | 10 | val r2 = :Rect [[0,0],[10,10]] ;; combining tag template/constructor 11 | println(r2 is? :Rect, r2.dim.h) ;; --> true, 10 12 | -------------------------------------------------------------------------------- /src/test/ceu/21-indexes.ceu: -------------------------------------------------------------------------------- 1 | val tup = [1,2,3,4] 2 | val vec = #[1,2,3] 3 | val i = 2 4 | 5 | tup[3] -> println ;; tuple access by index 6 | vec[i] -> println ;; vector access by index 7 | 8 | val dict = @[x=10] 9 | 10 | dict[:x] -> println ;; dict access by index 11 | dict.x -> println ;; dict access by field 12 | 13 | data :T = [x] 14 | val t :T = [10] ;; tuple template 15 | t.x -> println 16 | 17 | task S () { 18 | set pub = 10 19 | await() 20 | } 21 | val s = spawn S() 22 | s.pub -> println ;; public field of task 23 | -------------------------------------------------------------------------------- /src/test/ceu/18-enums.ceu: -------------------------------------------------------------------------------- 1 | enum { :x, :y, :z } ;; declares :x, :y, :z in sequence 2 | to.number(:x) -> println ;; --> 100 3 | to.number(:y) -> println ;; --> 101 4 | to.number(:z) -> println ;; --> 102 5 | 6 | println(:z - :x) ;; --> 2 7 | println(:x + 1) ;; --> :y 8 | println(:y < :z) ;; --> true 9 | 10 | enum :Key { 11 | Left, ;; declares :Key-Left (200) 12 | Up, ;; declares :Key-Up (201) 13 | Right, ;; ... 14 | Down, ;; ... 15 | } 16 | :Key-Left -> to.number -> println 17 | :Key-Down -> to.number -> println 18 | -------------------------------------------------------------------------------- /src/test/ceu/35-broadcasts.ceu: -------------------------------------------------------------------------------- 1 | spawn { 2 | every |true { 3 | println(:glb, it) 4 | } 5 | } 6 | 7 | task T () { 8 | task X () { 9 | every |true { 10 | println(:X, it) 11 | } 12 | } 13 | val x = spawn X() 14 | spawn { 15 | every |true { 16 | println(:T, it) 17 | } 18 | } 19 | broadcast(:1) in :task ;; restricted to enclosing task `T` 20 | broadcast(:2) ;; restricted to enclosing task `T` 21 | broadcast(:3) in x ;; restricted to spawned `x` 22 | broadcast(:4) in :global ;; no restrictions 23 | } 24 | 25 | spawn T() 26 | -------------------------------------------------------------------------------- /src/test/kotlin/tst_50/Lexer_50.kt: -------------------------------------------------------------------------------- 1 | package tst_50 2 | 3 | import dceu.* 4 | import org.junit.Test 5 | 6 | fun lexer (str: String): Lexer { 7 | return Lexer(listOf(Pair(Triple("anon",1,1), str.reader()))) 8 | } 9 | 10 | class Lexer_50 { 11 | @Test 12 | fun aa_01_ids() { 13 | val l = lexer("val' var' drop") 14 | val tks = l.lex().iterator() 15 | assert(tks.next().let { it is Tk.Fix && it.str == "val'" }) 16 | assert(tks.next().let { it is Tk.Fix && it.str == "var'" }) 17 | assert(tks.next().let { it is Tk.Fix && it.str == "drop" }) 18 | assert(tks.next() is Tk.Eof) 19 | assert(!tks.hasNext()) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/ceu/03-templates.ceu: -------------------------------------------------------------------------------- 1 | data :Pos = [x,y] ;; a template `:Pos` with fields `x` and `y` 2 | val pos :Pos = [10,20] ;; declares that `pos` satisfies template `:Pos` 3 | println(pos.x, pos.y) ;; --> 10, 20 4 | 5 | data :Event = [ts] { ;; All events carry a timestamp 6 | :Key = [key] ;; :Event.Key [ts,key] is a sub-type of :Event [ts] 7 | :Mouse = [pos :Pos] { ;; :Event.Mouse [ts, pos :Pos] 8 | :Motion = [] ;; :Event.Mouse.Motion [ts, pos :Pos] 9 | :Button = [but] ;; :Event.Mouse.Button [ts, pos :Pos, but] 10 | } 11 | } 12 | 13 | val but = :Event.Mouse.Button [0, [10,20], 1] ;; [ts,[x,y],but] 14 | println(but.ts, but.pos.y, but is? :Event.Mouse) ;; --> 0, 20, true 15 | -------------------------------------------------------------------------------- /src/test/ceu/28-loops.ceu: -------------------------------------------------------------------------------- 1 | do { 2 | var i = 1 3 | loop { ;; infinite loop 4 | println(i) ;; --> 1,2,...,10 5 | while i < 10 ;; immediate termination 6 | set i = i + 1 7 | } 8 | } 9 | 10 | loop in {0 => 3{ { 11 | println(it) ;; --> 0,1,2 12 | } 13 | 14 | loop v in }3 => 0} :step -1 { 15 | println(v) ;; --> 2,1,0 16 | } 17 | 18 | 19 | ;;; 20 | loop j { 21 | println(j) ;; --> 0,1,2,... 22 | } 23 | ;;; 24 | 25 | do { 26 | var i = 0 27 | loop { ;; infinite loop 28 | set i = i + 1 29 | if (i % 2) == 0 { 30 | skip() ;; jump back 31 | } 32 | println(i) ;; --> 1,3,5,... 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/ceu/30-errors.ceu: -------------------------------------------------------------------------------- 1 | val x = catch :Error { 2 | error(:Error) 3 | println("unreachable") 4 | } 5 | println(x) ;; --> :Error 6 | 7 | val y = 8 | catch :X { 9 | catch :Y { 10 | error(:X [10,20]) 11 | } 12 | } 13 | println(y) ;; --> :X [10,20] 14 | 15 | data :Err = [] { 16 | :One = [] 17 | :Two = [] 18 | } 19 | 20 | func f () { 21 | catch :Err.One { ;; catches specific error 22 | defer { 23 | println(1) 24 | } 25 | error(:Err.Two ["err msg"]) ;; throws another error 26 | } 27 | } 28 | catch :Err { ;; catches generic error 29 | defer { 30 | println(2) 31 | } 32 | f() 33 | ;; unreachable 34 | } ;; --> 1, 2 35 | -------------------------------------------------------------------------------- /src/test/ceu/02-tags.ceu: -------------------------------------------------------------------------------- 1 | val pos = @[] ;; a new dictionary 2 | set pos[:x] = 10 3 | set pos.y = 20 ;; equivalent to pos[:y]=20 4 | println(pos.x, pos[:y]) ;; --> 10, 20 5 | 6 | :T ; :T.A 7 | println <- sup?(:T, :T.A.x) ;; --> true (:T is a supertype of :T.A.x) 8 | println <- sup?(:T.A, :T.A.x) ;; --> true 9 | println <- sup?(:T.A.x, :T.A.x) ;; --> true 10 | println <- sup?(:T.A.x, :T) ;; --> false (:T.A.x is *not* a supertype of :T) 11 | println <- sup?(:T.A, :T.B) ;; --> false 12 | 13 | val x = [] ;; an empty tuple 14 | tag(:T.A, x) ;; x is of user type :T.A 15 | println(tag(x)) ;; --> :T.A 16 | println(sup?(:T, tag(x))) ;; --> true 17 | println(sup?(:T.A, tag(x))) ;; --> true 18 | println(sup?(:T.B, tag(x))) ;; --> false 19 | println(x is? :T) ;; --> true (equivalent to sup?(:T,tag(x))) 20 | -------------------------------------------------------------------------------- /src/test/kotlin/tst_04/Lexer_04.kt: -------------------------------------------------------------------------------- 1 | package tst_04 2 | 3 | import dceu.* 4 | import org.junit.Test 5 | 6 | fun lexer (str: String): Lexer { 7 | return Lexer(listOf(Pair(Triple("anon",1,1), str.reader()))) 8 | } 9 | 10 | class Lexer_04 { 11 | @Test 12 | fun aa_01_ids() { 13 | val l = 14 | lexer("task' broadcast in delay tasks pub") 15 | val tks = l.lex().iterator() 16 | assert(tks.next().let { it is Tk.Fix && it.str == "task'" }) 17 | assert(tks.next().let { it is Tk.Fix && it.str == "broadcast" }) 18 | assert(tks.next().let { it is Tk.Fix && it.str == "in" }) 19 | assert(tks.next().let { it is Tk.Fix && it.str == "delay" }) 20 | assert(tks.next().let { it is Tk.Id && it.str == "tasks" }) 21 | assert(tks.next().let { it is Tk.Fix && it.str == "pub" }) 22 | assert(tks.next() is Tk.Eof) 23 | assert(!tks.hasNext()) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/kotlin/tst_03/Lexer_03.kt: -------------------------------------------------------------------------------- 1 | package tst_03 2 | 3 | import dceu.* 4 | import org.junit.Test 5 | 6 | fun lexer (str: String): Lexer { 7 | return Lexer(listOf(Pair(Triple("anon",1,1), str.reader()))) 8 | } 9 | 10 | class Lexer_03 { 11 | @Test 12 | fun aa_01_ids() { 13 | val l = 14 | lexer("coro coro' coroutine spawn yield resume") 15 | val tks = l.lex().iterator() 16 | assert(tks.next().let { it is Tk.Id && it.str == "coro" }) 17 | assert(tks.next().let { it is Tk.Fix && it.str == "coro'" }) 18 | assert(tks.next().let { it is Tk.Id && it.str == "coroutine" }) 19 | assert(tks.next().let { it is Tk.Id && it.str == "spawn" }) 20 | assert(tks.next().let { it is Tk.Fix && it.str == "yield" }) 21 | assert(tks.next().let { it is Tk.Fix && it.str == "resume" }) 22 | assert(tks.next() is Tk.Eof) 23 | assert(!tks.hasNext()) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/test/ceu/16-declarations.ceu: -------------------------------------------------------------------------------- 1 | do { 2 | val i = 10 3 | ;;set i = 20 ;; ERROR: `x´ is immutable 4 | } 5 | ;;println(x) ;; ERROR: `x´ is out of scope 6 | 7 | var y = 10 8 | set y = 20 ;; OK: `y´ is mutable 9 | println(y) ;; --> 20 10 | 11 | data :Pos = [x,y] 12 | val p1 :Pos = [10,20] ;; (assumes :Pos has fields [x,y]) 13 | println(p1.x) ;; --> 10 14 | val p2 = :Pos [10,20] ;; (assumes :Pos has fields [x,y]) 15 | println(p2.y) ;; --> 20 16 | 17 | val [1,a,b] = [1,2,3] 18 | println(a,b) ;; --> 2 3 19 | ;;val [10,z] = [20,20] ;; ERROR: match fails 20 | 21 | func f (v) { 22 | v + 1 23 | } 24 | println(f(10)) ;; --> 11 25 | 26 | func g () { 27 | println(1) ;; --> 1 28 | return(2) 29 | println(3) ;; never executes 30 | } 31 | println(g()) ;; --> 2 32 | 33 | val x 34 | do { 35 | val x ;; `x´ cannot be redeclared 36 | } 37 | -------------------------------------------------------------------------------- /src/test/ceu/32-resume-yield-all.ceu: -------------------------------------------------------------------------------- 1 | coro G (b1) { ;; b1=1 2 | coro L (c1) { ;; c1=4 3 | val c2 = yield(c1+1) ;; y(5), c2=6 4 | val c3 = yield(c2+1) ;; y(7), c3=8 5 | c3+1 ;; 9 6 | } 7 | val l = coroutine(L) 8 | val b2 = yield(b1+1) ;; y(2), b2=3 9 | val b3 = resume-yield-all l(b2+1) ;; b3=9 10 | val b4 = yield(b3+1) ;; y(10) 11 | b4+1 ;; 12 12 | } 13 | 14 | val g = coroutine(G) 15 | val a1 = resume g(1) ;; g(1), a1=2 16 | val a2 = resume g(a1+1) ;; g(3), a2=5 17 | val a3 = resume g(a2+1) ;; g(6), a3=7 18 | val a4 = resume g(a3+1) ;; g(8), a4=10 19 | val a5 = resume g(a4+1) ;; g(11), a5=10 20 | println(a1, a2, a3, a4, a5) ;; --> 2, 5, 7, 10, 12 21 | -------------------------------------------------------------------------------- /src/test/kotlin/tst_02/Lexer_02.kt: -------------------------------------------------------------------------------- 1 | package tst_02 2 | 3 | import dceu.* 4 | import org.junit.BeforeClass 5 | import org.junit.Test 6 | 7 | fun lexer (str: String): Lexer { 8 | return Lexer(listOf(Pair(Triple("anon",1,1), str.reader()))) 9 | } 10 | 11 | class Lexer_02 { 12 | @Test 13 | fun aa_01_ids() { 14 | val l = 15 | lexer("defer catch throw it loop' loop") 16 | val tks = l.lex().iterator() 17 | assert(tks.next().let { it is Tk.Fix && it.str == "defer" }) 18 | assert(tks.next().let { it is Tk.Fix && it.str == "catch" }) 19 | assert(tks.next().let { it is Tk.Id && it.str == "throw" }) 20 | assert(tks.next().let { it is Tk.Id && it.str == "it" }) 21 | assert(tks.next().let { it is Tk.Fix && it.str == "loop'" }) 22 | assert(tks.next().let { it is Tk.Id && it.str == "loop" }) 23 | assert(tks.next() is Tk.Eof) 24 | assert(!tks.hasNext()) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/kotlin/tst_05/Lexer_05.kt: -------------------------------------------------------------------------------- 1 | package tst_05 2 | 3 | import dceu.* 4 | import org.junit.Test 5 | 6 | fun lexer (str: String): Lexer { 7 | return Lexer(listOf(Pair(Triple("anon",1,1), str.reader()))) 8 | } 9 | 10 | class Lexer_05 { 11 | @Test 12 | fun aa_01_ids() { 13 | val l = 14 | lexer("task' broadcast in as tasks track detrack") 15 | val tks = l.lex().iterator() 16 | assert(tks.next().let { it is Tk.Fix && it.str == "task'" }) 17 | assert(tks.next().let { it is Tk.Fix && it.str == "broadcast" }) 18 | assert(tks.next().let { it is Tk.Fix && it.str == "in" }) 19 | assert(tks.next().let { it is Tk.Id && it.str == "as" }) 20 | assert(tks.next().let { it is Tk.Fix && it.str == "tasks" }) 21 | assert(tks.next().let { it is Tk.Id && it.str == "track" }) 22 | assert(tks.next().let { it is Tk.Id && it.str == "detrack" }) 23 | assert(tks.next() is Tk.Eof) 24 | assert(!tks.hasNext()) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/ceu/13-blocks.ceu: -------------------------------------------------------------------------------- 1 | do { ;; block prints :ok and evals to 1 2 | println(:ok) 3 | 1 4 | } 5 | 6 | do { 7 | val a = 1 ;; `a` is only visible in the block 8 | ;;<...> 9 | } 10 | ;;a ;; ERR: `a` is out of scope 11 | 12 | do { 13 | task T () {} 14 | spawn T() ;; spawns task T and attaches it to the block 15 | ;;<...> 16 | } ;; aborts spawned task 17 | 18 | val v = do :X { 19 | println(1) ;; --> 1 20 | do :Y { 21 | escape(:X, 2) 22 | println(3) ;; never executes 23 | } 24 | println(4) ;; never executes 25 | } 26 | println(v) ;; --> 2 27 | 28 | do { 29 | drop(#[1,2,3]) ;; OK 30 | } 31 | println(`:ceu ceu_acc`) 32 | 33 | val x = 10 34 | drop(x) ;; --> 10 (innocuous drop) 35 | 36 | val y = do { 37 | val t = [10] 38 | drop(t) ;; --> [10] (deattaches from `t`, reattaches to `u`) 39 | } 40 | println(y) 41 | -------------------------------------------------------------------------------- /src/test/ceu/36-sugar.ceu: -------------------------------------------------------------------------------- 1 | spawn { 2 | await(:X) 3 | println(":X occurred") 4 | } 5 | broadcast(:X) 6 | 7 | do { 8 | task T () { 9 | set pub = 10 10 | spawn { 11 | println(pub) ;; --> 10 12 | } 13 | } 14 | spawn T() 15 | } 16 | 17 | do { 18 | spawn { 19 | every <1:ms> { 20 | println("1 more second has elapsed") 21 | } 22 | } 23 | broadcast(:Clock [1]) 24 | broadcast(:Clock [1]) 25 | } 26 | 27 | do { 28 | func f (x) { x[0] > 50 } 29 | spawn { 30 | every x :X | f(x) { 31 | println(":X satisfies f(x)") 32 | } 33 | } 34 | broadcast(:Y) 35 | broadcast(:X [100]) 36 | broadcast(:X [10]) 37 | } 38 | 39 | do { 40 | spawn { 41 | watching <1:ms> { 42 | every :X { 43 | println("one more :X occurred before 1 second") 44 | } 45 | } 46 | } 47 | broadcast(:X) 48 | broadcast(:X) 49 | broadcast(:Clock [1]) 50 | broadcast(:X) 51 | } 52 | -------------------------------------------------------------------------------- /src/test/ceu/19-templates.ceu: -------------------------------------------------------------------------------- 1 | data :Pos = [x,y] ;; a flat template 2 | val pos :Pos = [10,20] ;; pos uses :Pos as template 3 | println(pos.x, pos.y) ;; --> 10, 20 4 | 5 | data :Dim = [w,h] 6 | data :Rect = [pos :Pos, dim :Dim] ;; a nested template 7 | val r1 :Rect = [pos, [100,100]] ;; r uses :Rect as template 8 | println(r1.dim, r1.pos.x) ;; --> [100,100], 10 9 | 10 | val r2 = :Rect [[0,0],[10,10]] ;; combining tag template/constructor 11 | println(r2 is? :Rect, r2.dim.h) ;; --> true, 10 12 | 13 | data :Event = [ts] { ;; All events carry a timestamp 14 | :Key = [key] ;; :Event.Key [ts,key] is a sub-type of :Event [ts] 15 | :Mouse = [pos :Pos] { ;; :Event.Mouse [ts, pos :Pos] 16 | :Motion = [] ;; :Event.Mouse.Motion [ts, pos :Pos] 17 | :Button = [but] ;; :Event.Mouse.Button [ts, pos :Pos, but] 18 | } 19 | } 20 | 21 | val but = :Event.Mouse.Button [0, [10,20], 1] 22 | val evt :Event = but 23 | println(evt.ts, but.pos.y) ;; --> 0, 20 24 | 25 | -------------------------------------------------------------------------------- /src/test/ceu/37-pars.ceu: -------------------------------------------------------------------------------- 1 | do { 2 | spawn { 3 | par { 4 | every <100:ms> { 5 | println("1 second has elapsed") 6 | } 7 | } with { 8 | every <200:ms> { 9 | println("1 minute has elapsed") 10 | } 11 | } with { 12 | every <300:ms> { 13 | println("1 hour has elapsed") 14 | } 15 | } 16 | println("never reached") 17 | } 18 | loop in {1 => 10} { 19 | broadcast(:Clock [100]) 20 | } 21 | } 22 | 23 | println("-=-=-") 24 | 25 | do { 26 | spawn { 27 | par-or { 28 | await <1:s> 29 | } with { 30 | await(:X) 31 | println(":X occurred before 1 second") 32 | } 33 | } 34 | broadcast(:Clock [100]) 35 | broadcast(:Clock [100]) 36 | broadcast(:X) 37 | } 38 | 39 | println("-=-=-") 40 | 41 | do { 42 | spawn { 43 | par-and { 44 | await(:X) 45 | } with { 46 | await(:Y) 47 | } 48 | println(":X and :Y have occurred") 49 | } 50 | broadcast(:X) 51 | broadcast(:Y) 52 | } 53 | -------------------------------------------------------------------------------- /.idea/libraries/jetbrains_kotlinx_coroutines_core_jvm.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/test/ceu/31-coroutines.ceu: -------------------------------------------------------------------------------- 1 | do { 2 | coro C (x) { ;; first resume 3 | println(x) ;; --> 10 4 | val w = yield(x + 1) ;; returns 11, second resume, receives 12 5 | println(w) ;; --> 12 6 | w + 1 ;; returns 13 7 | } 8 | val c = coroutine(C) ;; creates `c` from prototype `C` 9 | val y = resume c(10) ;; starts `c`, receives `11` 10 | val z = resume c(y+1) ;; resumes `c`, receives `13` 11 | println(status(c)) ;; --> :terminated 12 | } 13 | 14 | do { 15 | coro C () { 16 | defer { 17 | println("aborted") 18 | } 19 | yield() 20 | } 21 | do { 22 | val c = coroutine(C) 23 | resume c() 24 | } ;; --> aborted 25 | } 26 | 27 | do { 28 | coro C () { } 29 | val c = coroutine(C) 30 | println(C, c) ;; --> coro: 0x... / exe-coro: 0x... 31 | } 32 | 33 | do { 34 | coro C () { 35 | yield() 36 | } 37 | val c = coroutine(C) 38 | println(status(c)) ;; --> :yielded 39 | resume c() 40 | println(status(c)) ;; --> :yielded 41 | resume c() 42 | println(status(c)) ;; --> :terminated 43 | } 44 | 45 | do { 46 | coro C () { 47 | println(:1) 48 | yield() 49 | println(:2) 50 | } 51 | val co = coroutine(C) 52 | resume co() ;; --> 1 53 | resume co() ;; --> 2 54 | } 55 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # Compile from sources 2 | 3 | 1. Clone the source repository: 4 | 5 | ``` 6 | git clone https://github.com/fsantanna/dceu/ 7 | ``` 8 | 9 | 2. Install `gcc`, `make`, and the Java SDK: 10 | 11 | ``` 12 | sudo apt install gcc make default-jdk 13 | ``` 14 | 15 | 3. Open `IntelliJ IDEA` (version `2023.1.2`): 16 | - Open project/directory `dceu/` 17 | - Wait for all imports (takes long...) 18 | - Run self tests: 19 | - On the left pane, click tab `Project`: 20 | - Left click fold `dceu -> src -> main -> kotlin` 21 | - Double click file `Main.kt` 22 | - set `var CEU = 1` 23 | - Left click fold `dceu -> src -> test -> kotlin` 24 | - Right click fold `test_01` 25 | - Left click `Run 'Testes in tst_01'` 26 | - Repeat the last two steps for `2`,`3`,`4`,`5`,`99` (skip `6`) 27 | - It is ok if tests with prefixes `TODO` and `BUG` fail. 28 | - Generate artifacts (maybe can already skip to next step?): 29 | - Click `File -> Project Structure -> Artifacts -> + -> JAR -> From modules with dependencies` 30 | - Click `Module -> dceu` 31 | - Click `OK` 32 | - Verify that `dceu:jar` appears at the top 33 | - Click `OK` 34 | - Rebuild artifacts: 35 | - Click `Build -> Build artifacts -> Build` 36 | 37 | 3. Install `ceu`: 38 | 39 | ``` 40 | cd dceu/ 41 | make DIR=.. 42 | ``` 43 | 44 | 4. Use `ceu`: 45 | 46 | ``` 47 | ../ceu build/hello-world.ceu 48 | ``` 49 | -------------------------------------------------------------------------------- /src/test/ceu/27-match.ceu: -------------------------------------------------------------------------------- 1 | func odd? () { 2 | true 3 | } 4 | func f () { 5 | 13 6 | } 7 | 8 | match f() { 9 | x :X => x ;; capture `x=f()`, return `x` if `x is? :X` 10 | <= 5 => it ;; capture `it=f()`, return `it` if `it <= 5` 11 | 10 => :10 ;; return :ok if `f() == 10` 12 | {{odd?}} => :odd ;; return :ok if `odd?(f())` 13 | else => :no 14 | } -> println 15 | 16 | func g () { 17 | false 18 | } 19 | 20 | match [10,20,30,40] { 21 | | g(it) { \v => v } ;; capture `it=[...]`, check `g(it)` 22 | ;; capture and return `g(it)` as `v` 23 | [10, i, j|j>10] => i+j ;; capture `it=[...]`, check `#it>=3`, 24 | ;; check `it[0]==10` 25 | ;; capture `i=it[1]` 26 | ;; capture `j=it[1]`, check `j>10` 27 | ;; return i+j 28 | } -> println 29 | 30 | do { 31 | val [1,x,y] = [1,2,3] 32 | println(x,y) ;; --> 2 3 33 | } 34 | 35 | ;;val [10,x] = [20,20] ;; ERROR: match fails 36 | 37 | val d = @[x=1, y=2] 38 | loop [k,v] in d { 39 | println(k,v) ;; --> x,1 y,2 40 | } 41 | 42 | catch :X { ;; ok match 43 | catch :Y { ;; no match 44 | error(:X) 45 | } 46 | ;; never reached 47 | } 48 | 49 | data :Pos = [x,y] 50 | spawn { 51 | await(:Pos | it.x==it.y) 52 | println(:2) 53 | } 54 | broadcast(:Pos [10,20]) ;; no match 55 | println(:1) 56 | broadcast(:Pos [10,10]) ;; ok match 57 | 58 | -------------------------------------------------------------------------------- /src/test/ceu/39-basic.ceu: -------------------------------------------------------------------------------- 1 | do { 2 | assert((10<20) and :ok, "bug found") ;; --> :ok 3 | ;;assert(1 == 2) <-- { "1 /= 2" } ;; --> ERROR: "1 /= 2" 4 | } 5 | 6 | do { 7 | val d = @[(:k1,10), (:k2,20)] 8 | val k1 = next(d) 9 | val k2 = next(d, k1) 10 | println(k1, k2) ;; --> :k1 / :k2 11 | } 12 | 13 | do { 14 | task T () { yield() } 15 | val ts = tasks() 16 | spawn T() in ts ;; tsk1 17 | spawn T() in ts ;; tsk2 18 | val t1 = next(ts) 19 | val t2 = next(ts, t1) 20 | println(t1, t2) ;; --> tsk1 / tsk2 21 | } 22 | 23 | do { 24 | val x = println(1, :x) ;; --> 1 :x 25 | print(x) 26 | println(2) ;; --> 12 27 | } 28 | 29 | do { 30 | :T 31 | sup?(:T.A, :T.A.x) --> println ;; --> true 32 | sup?(:T.A.x, :T) --> println ;; --> false 33 | 34 | val x = tag(:X, []) --> println ;; value x=[] is associated with tag :X 35 | tag(x) --> println ;; --> :X 36 | 37 | type(10) --> println ;; --> :number 38 | } 39 | 40 | do { 41 | val x = 10 42 | to.bool(nil) --> println ;; --> false 43 | to.char(65) --> println ;; --> 'A' 44 | to.dict([[:x,1]]) --> println ;; --> @[(:x,1)] 45 | to.number("10") --> println ;; --> 10 46 | to.pointer(#[x]) --> println ;; --> (C pointer to 1st element `x`) 47 | to.string(42) --> println ;; --> "42" 48 | to.tag(":number") --> println ;; --> :number 49 | to.tuple(#[1,2,3]) --> println ;; --> [1,2,3] 50 | to.vector([1,2,3]) --> println ;; --> #[1,2,3] 51 | } 52 | 53 | -------------------------------------------------------------------------------- /src/test/kotlin/tst_50/Parser_50.kt: -------------------------------------------------------------------------------- 1 | package tst_50 2 | 3 | import dceu.* 4 | import org.junit.Test 5 | 6 | class Parser_50 { 7 | @Test 8 | fun aa_01_func_nested() { 9 | val l = lexer( 10 | """ 11 | func' :nested () { nil } 12 | """ 13 | ) 14 | val parser = Parser(l) 15 | val e = parser.expr() 16 | assert(e.to_str() == "(func' :nested () {\nnil;\n})") { e.to_str() } 17 | //assert(trap { parser.expr() } == "anon : (lin 2, col 18) : expected \"(\" : have \":nested\"") 18 | } 19 | @Test 20 | fun aa_02_coro_nested() { 21 | val l = tst_04.lexer( 22 | """ 23 | coro' :nested () { nil } 24 | """ 25 | ) 26 | val parser = Parser(l) 27 | val e = parser.expr() 28 | assert(e.to_str() == "(coro' :nested () {\nnil;\n})") { e.to_str() } 29 | //assert(trap { parser.expr() } == "anon : (lin 2, col 18) : expected \"(\" : have \":nested\"") 30 | } 31 | @Test 32 | fun aa_03_task_err() { 33 | val l = lexer(""" 34 | task' :xxx () {} 35 | """.trimIndent()) 36 | val parser = Parser(l) 37 | //assert(trap { parser.expr() } == "anon : (lin 1, col 1) : invalid task : unexpected \":xxx\"") 38 | assert(trap { parser.expr() } == "anon : (lin 1, col 7) : expected \"(\" : have \":xxx\"") 39 | } 40 | @Test 41 | fun aa_04_task() { 42 | val l = lexer("task' (a,b) :nested { 10 }") 43 | val parser = Parser(l) 44 | val e = parser.expr() 45 | assert(e.to_str() == "(task' (a,b) :nested {\n10;\n})") { e.to_str() } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/kotlin/Ups.kt: -------------------------------------------------------------------------------- 1 | package dceu 2 | 3 | fun Expr.up_first (cnd: (Expr)->Boolean): Expr? { 4 | return when { 5 | cnd(this) -> this 6 | (this.fup() === null) -> null 7 | else -> this.fupx().up_first(cnd) 8 | } 9 | } 10 | 11 | fun Expr.up_all_until (cnd: (Expr)->Boolean): List { 12 | return listOf(this) + when { 13 | cnd(this) -> emptyList() 14 | (this.fup() === null) -> emptyList() 15 | else -> this.fupx().up_all_until(cnd) 16 | } 17 | } 18 | 19 | fun Expr.up_first_without (cnd1: (Expr)->Boolean, cnd2: (Expr)->Boolean): Expr? { 20 | return when { 21 | cnd2(this) -> null 22 | cnd1(this) -> this 23 | (this.fup() === null) -> null 24 | else -> this.fupx().up_first_without(cnd1,cnd2) 25 | } 26 | } 27 | 28 | fun Expr.up_any (cnd: (Expr)->Boolean): Boolean { 29 | return this.up_first(cnd) !== null 30 | } 31 | 32 | fun Expr.up_none (cnd: (Expr)->Boolean): Boolean { 33 | return this.up_first(cnd) === null 34 | } 35 | 36 | fun Expr.up_first_task_outer (): Expr.Proto? { 37 | return this.up_first { 38 | when { 39 | (it !is Expr.Proto) -> false 40 | (it.tk.str != "task'") -> false 41 | !it.fake -> true 42 | (it.up_first { it is Expr.Do }!!.fup() === null) -> true 43 | else -> false 44 | } 45 | } as Expr.Proto? 46 | } 47 | 48 | fun Expr.up_exe (tp: String?=null): Expr.Proto? { 49 | return this.up_first { it is Expr.Proto }.let { 50 | if (it===null || it.tk.str=="func'" || (tp!==null && it.tk.str!=tp)) { 51 | null 52 | } else { 53 | it as Expr.Proto 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/ceu/25-operators.ceu: -------------------------------------------------------------------------------- 1 | do { 2 | val tup = [] 3 | val vec = #[1,2,3] 4 | println(#tup, #vec) ;; --> 0 / 3 5 | } 6 | 7 | println("-=-=-") 8 | 9 | do { 10 | 1 == 1 --> println ;; --> true 11 | 1 /= 1 --> println ;; --> false 12 | 1 == '1' --> println ;; --> false 13 | [1] == [1] --> println ;; --> false 14 | 15 | val t1 = [1] 16 | val t2 = t1 17 | t1 == t2 --> println ;; --> true 18 | } 19 | 20 | println("-=-=-") 21 | 22 | do { 23 | 1 === 1 --> println ;; --> true 24 | 1 =/= 1 --> println ;; --> false 25 | 1 === '1' --> println ;; --> false 26 | #[1] === #[1] --> println ;; --> true 27 | @[(:x,1),(:y,2)] =/= 28 | @[(:y,2),(:x,1)] --> println ;; --> false 29 | } 30 | 31 | println("-=-=-") 32 | 33 | do { 34 | 1 > 2 --> println ;; --> false 35 | 2 >= 1 --> println ;; --> true 36 | 1 <= 1 --> println ;; --> true 37 | 1 < 2 --> println ;; --> true 38 | } 39 | 40 | println("-=-=-") 41 | 42 | do { 43 | 1 + 2 --> println ;; --> 3 44 | 1 - 2 --> println ;; --> -1 45 | 2 * 3 --> println ;; --> 6 46 | 5 / 2 --> println ;; --> 2.5 47 | 5 % 2 --> println ;; --> 1 48 | -20 --> println ;; --> -20 49 | } 50 | 51 | println("-=-=-") 52 | 53 | do { 54 | not not nil --> println ;; --> false 55 | nil or 10 --> println ;; --> 10 56 | 10 and nil --> println ;; --> nil 57 | } 58 | 59 | println("-=-=-") 60 | 61 | do { 62 | 10 is? :number --> println ;; --> true 63 | 10 is? nil --> println ;; --> false 64 | tag(:X,[]) is? :X --> println ;; --> true 65 | } 66 | 67 | println("-=-=-") 68 | 69 | do { 70 | 10 in? [1,10] --> println ;; true 71 | 20 in? #[1,10] --> println ;; false 72 | 10 in? @[(1,10)] --> println ;; false 73 | } 74 | -------------------------------------------------------------------------------- /ceu.vim: -------------------------------------------------------------------------------- 1 | " Vim syntax file 2 | " Language: Ceu 3 | " Maintainer: Francisco Sant'Anna 4 | " Last Change: 2024 October 5 | 6 | if exists("b:current_syntax") 7 | finish 8 | endif 9 | 10 | syn iskeyword 33,39,45,46,63,95,97-122 11 | 12 | syn match Comment ";;.*$" 13 | syn region Comment start=/;;;$/ end=/;;;$/ 14 | 15 | syn region String start=/\v"/ skip=/\v(\\[\\"]){-1}/ end=/\v"/ 16 | syn match String "'.'" 17 | "syntax region String start=/\v'/ skip=/\v(\\[\\"]){-1}/ end=/\v'/ 18 | 19 | syn match Constant '\d\+' 20 | syn keyword Constant false nil true 21 | 22 | syn keyword Function drop dump error pub 23 | syn keyword Function next-dict print println 24 | syn keyword Function sup? tag to-string-number 25 | syn keyword Function to-string-pointer to-string-tag 26 | syn keyword Function to-tag-string tuple type next-tasks 27 | 28 | syn keyword Function not and or 29 | syn keyword Function is is-not 30 | syn keyword Function in in-not 31 | 32 | syn keyword Function assert 33 | syn keyword Function static? dynamic? string? 34 | syn keyword Function tag-or next create-resume copy 35 | syn match Function '\<\(to\|random\|math\)' 36 | syn match Function '\<\(pico\|iup\)' 37 | 38 | syn match Type ':[a-zA-Z0-9'?!\.\-]\+' 39 | 40 | syn match Statement '[\+\-\*\/\%\>\<\=\|\&\~]' 41 | 42 | syn keyword Statement data defer do else group if set 43 | syn keyword Statement val var catch escape resume 44 | syn keyword Statement yield broadcast delay in it 45 | syn keyword Statement spawn toggle tasks await 46 | syn keyword Statement break coro enum every func 47 | syn keyword Statement ifs loop match par par-and 48 | syn keyword Statement par-or resume-yield-all 49 | syn keyword Statement return skip task tasks test thus 50 | syn keyword Statement until watching with where 51 | syn keyword Statement while 52 | syn keyword Statement coroutine status 53 | 54 | syn keyword Todo TODO FIXME XXX 55 | syn region String start='"' end='"' 56 | -------------------------------------------------------------------------------- /src/test/ceu/33-tasks.ceu: -------------------------------------------------------------------------------- 1 | do { 2 | task T (x) { 3 | set pub = x ;; sets 1 or 2 4 | val n = await(:number) ;; awaits a number broadcast 5 | println(pub + n) ;; --> 11 or 12 6 | } 7 | val t1 = spawn T(1) 8 | val t2 = spawn T(2) 9 | println(t1.pub, t2.pub) ;; --> 1, 2 10 | broadcast(10) ;; awakes all tasks passing 10 11 | } 12 | 13 | do { 14 | task T () { 15 | val n = await(:number) 16 | println(n) 17 | } 18 | val ts = tasks() ;; task pool 19 | do { 20 | spawn T() in ts ;; attached to outer pool, 21 | spawn T() in ts ;; not to enclosing block 22 | } 23 | broadcast(10) ;; --> 10, 10 24 | } 25 | 26 | do { 27 | task T (v, vs) { ;; task prototype accepts 2 args 28 | nil 29 | } 30 | val t = spawn T(10, [1,2,3]) ;; starts task passing args 31 | println(t) ;; --> exe-task 0x... 32 | } 33 | 34 | do { 35 | task T () { 36 | yield() 37 | } 38 | val ts = tasks(1) ;; task pool 39 | val t1 = spawn T() in ts ;; success 40 | val t2 = spawn T() in ts ;; failure 41 | println(ts, t1, t2) ;; --> tasks: 0x... / exe-task 0x... / nil 42 | } 43 | 44 | do { 45 | task T () { 46 | await(|true) 47 | } 48 | val t = spawn T() 49 | println(status(t)) ;; --> :yielded 50 | toggle t(false) 51 | broadcast(nil) 52 | println(status(t)) ;; --> :toggled 53 | toggle t(true) 54 | broadcast(nil) 55 | println(status(t)) ;; --> :terminated 56 | } 57 | 58 | do { 59 | task T () { 60 | set pub = 10 61 | await(|true) 62 | println(pub) ;; --> 20 63 | 30 ;; final task value 64 | } 65 | val t = spawn T() 66 | println(t.pub) ;; --> 10 67 | set t.pub = 20 68 | broadcast(nil) 69 | println(t.pub) ;; --> 30 70 | } 71 | -------------------------------------------------------------------------------- /.idea/libraries/jetbrains_kotlinx_coroutines_core.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/libraries/KotlinJavaRuntime.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /dceu.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/main/kotlin/Prelude.kt: -------------------------------------------------------------------------------- 1 | package dceu 2 | 3 | val PLUS = """ 4 | val {{+}} = func' (v1, v2) { 5 | `:number (${D}v1.Number + ${D}v2.Number)` 6 | } 7 | val {{-}} = func' (v1, v2) { 8 | if v2 == nil { 9 | `:number - ${D}v1.Number` 10 | } else { 11 | `:number (${D}v1.Number - ${D}v2.Number)` 12 | } 13 | } 14 | """.replace("\n", " ") 15 | 16 | val MULT = """ 17 | val {{*}} = func' (v1, v2) { 18 | `:number (${D}v1.Number * ${D}v2.Number)` 19 | } 20 | val {{/}} = func' (v1, v2) { 21 | `:number (${D}v1.Number / ${D}v2.Number)` 22 | } 23 | """.replace("\n", " ") 24 | 25 | val COMP = """ 26 | func {{>}} (v1,v2) { 27 | ifs { 28 | (type(v1) == :tag) and (type(v2) == :tag) { `:bool (${D}v1.Tag > ${D}v2.Tag)` } 29 | (type(v1) == :number) and (type(v2) == :number) { `:bool (${D}v1.Number > ${D}v2.Number)` } 30 | else => error(:error) 31 | } 32 | } 33 | func {{<}} (v1,v2) { 34 | not ((v1 == v2) or (v1 > v2)) 35 | } 36 | func {{>=}} (v1,v2) { 37 | (v1 == v2) or (v1 > v2) 38 | } 39 | func {{<=}} (v1,v2) { 40 | (v1 == v2) or (v1 < v2) 41 | } 42 | func {{===}} (v1,v2) { 43 | (v1 == v2) 44 | } 45 | """.replace("\n", " ") 46 | 47 | fun OR (v1:String, v2:String): String { 48 | G.N++ 49 | return "do { val it_${G.N} = $v1 ; if it_${G.N} { it_${G.N} } else { $v2 } }" 50 | } 51 | 52 | fun AND (v1:String, v2:String): String { 53 | G.N++ 54 | return "do { val it_${G.N} = $v1 ; if it_${G.N} { $v2 } else { $v1 } }" 55 | } 56 | 57 | fun AWAIT (v:String="(type(it) /= :exe-task)"): String { 58 | return """ 59 | enclose' :break { 60 | loop' { 61 | val${(CEU>=50).cond{"'"}} it = yield(nil) 62 | val${(CEU>=50).cond{"'"}} xxx = ${AND(v, OR("it","true"))} 63 | if xxx { 64 | escape(:break,xxx) 65 | } else {nil} 66 | } 67 | } 68 | """.replace("\n", " ") 69 | } 70 | 71 | val IS = """ 72 | func is' (v1,v2) { 73 | ifs { 74 | (v1 == v2) { true } 75 | (type(v1) == v2) { true } 76 | (type(v2) == :tag) { sup?(v2, tag(v1)) } 77 | else => false 78 | } 79 | } 80 | """.replace("\n", " ") 81 | 82 | val ASR = """ 83 | func assert (v, msg) { 84 | if v => v => error(msg) 85 | } 86 | """.replace("\n", " ") -------------------------------------------------------------------------------- /.idea/artifacts/dceu_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | $PROJECT_DIR$/out/artifacts/dceu_jar 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /doc/manual.lua: -------------------------------------------------------------------------------- 1 | -- lua manual.lua manual.md > manual-out.md 2 | 3 | local S1 = { 4 | -- [1] = { 5 | -- n = 1 6 | -- sec = '1.', 7 | -- tit = "XXX", 8 | -- [1] = { n=2, sec='1.', tit="YYY", [1]={...}, ... }, 9 | -- [2] = { ... }, 10 | -- } 11 | } 12 | 13 | local S2 = { 14 | -- ["XXX"] = '1.' 15 | -- ["YYY"] = '1.1.' 16 | } 17 | 18 | local S3 = { 19 | -- { '1.', "XXX" }, 20 | -- { '1.1.', "YYY" }, 21 | } 22 | 23 | do 24 | local N = { 25 | 0, 0, 0, 0, 0, 0, 26 | -- [1] = i -- head # is on n=i 27 | -- [2] = j -- head ## is on n=j 28 | } 29 | 30 | local CONTENTS = false -- ignore title 31 | 32 | for l in io.lines(...) do 33 | if l == "" then 34 | CONTENTS = true 35 | end 36 | 37 | local n, tit = string.match(l, '^(#+) (.+)$') 38 | if CONTENTS and n then 39 | n = string.len(n) 40 | assert(N[n], "too many levels") 41 | N[n] = N[n] + 1 42 | for i=n+1, #N do 43 | N[i] = 0 -- reset further levels 44 | end 45 | 46 | local pre = '' -- 2.1.3. 47 | for i=1, n do 48 | pre = pre .. N[i] .. '.' 49 | end 50 | assert(not S2[tit], tit) -- duplicate title 51 | S2[tit] = pre 52 | if n < 3 then 53 | S3[#S3+1] = { pre, tit } 54 | end 55 | 56 | --print(n, pre, tit) 57 | 58 | local T = S1 59 | for i=1, n-1 do 60 | T = T[N[i]] 61 | end 62 | assert(not T[N[n]]) 63 | T[N[n]] = { n=n, sec=N[n]..'.', tit=tit } 64 | end 65 | end 66 | end 67 | 68 | --[[ 69 | function summary (T) 70 | for i=1, #T do 71 | local t = T[i] 72 | print(string.rep(' ',4*(t.n-1)) .. t.sec .. ' ' .. t.tit) 73 | summary(t) 74 | end 75 | end 76 | summary(S1) 77 | ]] 78 | 79 | function tolink (s) 80 | return string.lower(string.gsub(string.gsub(s,"[%p]",""), "%s+", "-")) 81 | end 82 | 83 | do 84 | local CONTENTS = false -- ignore title 85 | local s3 = 1 86 | 87 | for l in io.lines(...) do 88 | if l == "" then 89 | CONTENTS = true 90 | end 91 | 92 | local n1, tit1 = string.match(l, '^( *)%* (.+)$') 93 | local n2, tit2 = string.match(l, '^(#+) (.+)$') 94 | if not CONTENTS and n1 then 95 | --print('>', S3[s3][2], tit1) 96 | assert(S3[s3][2] == tit1) 97 | print(n1..'- '..S3[s3][1]..' '..tit1) 98 | s3 = s3 + 1 99 | elseif CONTENTS and n2 then 100 | print('') 101 | print() 102 | print(n2 .. ' ' .. S2[tit2] .. ' ' .. tit2) 103 | else 104 | print(l) 105 | end 106 | end 107 | 108 | assert(s3 == #S3+1) 109 | end 110 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | v0.5 (oct'24) 2 | ------------- 3 | 4 | - Additions: 5 | - lexical memory management (lex): back - experimental 6 | - statements 7 | - `drop` (lex): dettach value from block 8 | - `escape`: to escape from blocks 9 | - `return`: to return from functions 10 | - standard library 11 | - `random.bool`, `random.int` 12 | - Changes: 13 | - exceptions: tag required as argument 14 | 15 | v0.4 (jul'24) 16 | ------------- 17 | 18 | - Additions 19 | - pattern matching: `match`, `await`, `loop`, `val` 20 | - statements 21 | - `group`: to group commands without a block 22 | - `test`: for tests 23 | - `match`: for pattern matching 24 | - tags: arithmetic and relational operations 25 | - Changes 26 | - lambda syntax: `\{...}` -> `{\...}` 27 | - clock syntax: `:1:ms` -> `<1:ms>` 28 | - `break` `skip`: no nesting restrictions 29 | - standard library modules: `to.*`, `math.*`, `random.*` 30 | - Removals 31 | - `...` variable arguments 32 | - `pass` (`do`) statement 33 | 34 | v0.3 (mar'24) 35 | ------------- 36 | 37 | - Additions 38 | - condition patterns (`ifs`, `catch`, `await`) 39 | - pipe operators `->` `-->` `<--` `<-` 40 | - Changes 41 | - arrow clause `->` to `=>` 42 | - types `x-coro` `x-task` `x-tasks` -> `exe-coro` `exe-task` `tasks` 43 | - statements 44 | - `throw` -> `error` 45 | - `loop if` `loop until` -> `break if` `skip if` `until` `while` 46 | - `awaiting` -> `watching` 47 | - `pass` -> `do` 48 | - Removals 49 | - lexical memory management for dynamic allocation 50 | - `evt` `err` variables 51 | - closure modifiers 52 | - `track` statement 53 | - `if` `ifs` condition declaration 54 | - tags `:tmp` `:check-now` `:fake` 55 | - Full re-implementation 56 | 57 | v0.2 (may'23) 58 | ------------- 59 | 60 | - Additions 61 | - `--verbose` (compile option) 62 | - `...` as main program arguments 63 | - dynamic cast `:X v.x` 64 | - implicit tag declaration `val x = :X ...` 65 | - assignments in conditions: `if`, `ifs`, `until` 66 | - functions 67 | - `in?` / `in-not?` functions 68 | - `to-tag` 69 | - `===`, `=/=` for deep equality 70 | - `math-` `sin` `cos` `floor` 71 | - statements 72 | - `if ... -> ... -> ... 73 | - `loop-while` clause 74 | - `every-while-until` clause 75 | - `thus { ... }` (pipe operator) 76 | - `spawn coro` 77 | - lambda syntax: 78 | - `\x{ x }` 79 | - `f \{ ... }` (call w/o parens) 80 | - `it` as implicit arg (also for `loop`, `func`, `thus`) 81 | - iterators: 82 | - `func` iterator 83 | - tag modifiers `:all`, `:idx`, `:key`, `:val` 84 | - dict iterator defaults to `:key` 85 | - Changes 86 | - `is`, `is-not` -> `is?`, `is-not?` 87 | - `ifs`: case separators are either `-> ...` or `{ ... }` 88 | - `do :unnset` -> `export` 89 | - `print` shows tags 90 | - `func :rec` modifier 91 | - `evt` and `detrack(...)` values cannot be assigned 92 | - better compilation time 93 | - Removals 94 | - ` { :x ... }` block tags 95 | 96 | v0.1 (mar'23) 97 | ------------- 98 | 99 | - first release 100 | -------------------------------------------------------------------------------- /doc/lexical.md: -------------------------------------------------------------------------------- 1 | # Lexical Memory Management 2 | 3 | As described in the [design of Ceu](manual-out.md#1-design), lexical memory 4 | management is one of the key aspects of the language: 5 | 6 | - Even dynamic allocation is attached to lexical blocks. 7 | - Strict escaping rules to preserve structure reasoning. 8 | - Garbage collection restricted to local references only. 9 | 10 | - goal 11 | - basic concepts 12 | - basic rules 13 | 14 | ## Dynamic Values 15 | 16 | Ceu respects the lexical structure of the program even when dealing with 17 | dynamic values, which allocate new memory. 18 | Ceu supports 10 types with [dynamic values](manual-out#dynamic-values), which 19 | are all subject to lexical memory management: 20 | 21 | ``` 22 | tuple | vector | dict ;; collections 23 | func | coro | task ;; prototypes 24 | x-coro | x-task | x-tasks | x-track ;; active values (next section) 25 | ``` 26 | 27 | In the next example, the tuple `[1,2,3]` is a dynamic value that allocates 28 | memory and is subject to lexical memory management. 29 | 30 | ``` 31 | do { 32 | val tup = [1,2,3] 33 | println(tup) 34 | } 35 | ``` 36 | 37 | ## Holding Blocks 38 | 39 | A dynamic value is attached to exactly one [block](manual-out.md#blocks) at any 40 | given time during execution. 41 | The value may move between blocks during execution, but it is still always 42 | attached to a single holding block. 43 | The value is automatically released from memory when the holding block 44 | terminates. 45 | 46 | In the next example, the tuple `[1,2,3]` is attached to the enclosing `do` 47 | block and is automatically released from memory when the block terminates: 48 | 49 | ``` 50 | do { ;; holding block 51 | val tup = [1,2,3] ;; dynamic tuple 52 | println(tup) 53 | } ;; releases the tuple 54 | ``` 55 | 56 | ## Holding States 57 | 58 | ``` 59 | HOLD-FLEETING ;; not assigned, dst assigns 60 | HOLD-MUTABLE ;; set and assignable to narrow 61 | HOLD-IMMUTABLE ;; set but not assignable across unsafe (even if same/narrow) 62 | HOLD-EVENT ;; 63 | ``` 64 | 65 | ## Constructors 66 | 67 | When a dynamic value is first allocated through a constructor, it is attached 68 | by default to the closest enclosing lexical block. 69 | 70 | Each one of the 10 dynamic types have a primitive constructor to create values: 71 | 72 | ``` 73 | [...] ;; tuple 74 | #[...] ;; vector 75 | @[...] ;; dict 76 | 77 | func () { ... } ;; func 78 | coro () { ... } ;; coro 79 | task () { ... } ;; task 80 | 81 | coroutine(...) ;; x-coro 82 | spawn T(...) ;; x-task 83 | tasks(...) ;; x-tasks 84 | track(...) ;; x-track 85 | ``` 86 | 87 | ## Assignments 88 | 89 | Examples: 90 | 91 | ``` 92 | do { 93 | do { 94 | [1,2,3] 95 | } 96 | } 97 | ``` 98 | 99 | When a [dynamic value](#dynamic-values) is first assigned to a variable, it 100 | becomes attached to the [block](#block) in which the variable is declared, and 101 | the value cannot escape that block in further assignments or as return 102 | expressions. 103 | 104 | - TDrop.kt 105 | - extend w design decisions 106 | - doc as primitive, not copy-move 107 | - refer to manual 108 | - move -> drop 109 | - all dyns have exactly 0 or 1 block 110 | - some are fixed 111 | - FLEET, VAR, FIXED 112 | - protos, closures and never-closures 113 | - protos, coros and tasks 114 | - tracks, refs 115 | - pubs 116 | - events 117 | -------------------------------------------------------------------------------- /src/test/kotlin/tst_03/Parser_03.kt: -------------------------------------------------------------------------------- 1 | package tst_03 2 | 3 | import dceu.* 4 | import org.junit.Test 5 | 6 | class Parser_03 { 7 | 8 | // CORO 9 | 10 | @Test 11 | fun aa_01_coro_err() { 12 | val l = lexer("coro' (a,b) :fake { 10 }") 13 | val parser = Parser(l) 14 | assert(trap { parser.expr_prim() } == "anon : (lin 1, col 13) : expected \"{\" : have \":fake\"") 15 | } 16 | @Test 17 | fun aa_02_coro_err() { 18 | val l = lexer("coro' (a,b) :xxx { 10 }") 19 | val parser = Parser(l) 20 | assert(trap { parser.expr_prim() } == "anon : (lin 1, col 13) : expected \"{\" : have \":xxx\"") 21 | } 22 | @Test 23 | fun aa_03_coro() { 24 | val l = lexer("coro' (a,b) { 10 }") 25 | val parser = Parser(l) 26 | val e = parser.expr_prim() 27 | assert(e is Expr.Proto && e.pars.size==2) 28 | assert(e.to_str() == "(coro' (a,b) {\n10;\n})") { e.to_str() } 29 | } 30 | @Test 31 | fun aa_04_coro_nested() { 32 | val l = tst_04.lexer( 33 | """ 34 | coro' :nested () { nil } 35 | """ 36 | ) 37 | val parser = Parser(l) 38 | //val e = parser.expr() 39 | //assert(e.tostr() == "(coro :nested () {\nnil\n})") { e.tostr() } 40 | assert(trap { parser.expr() } == "anon : (lin 2, col 19) : expected \"(\" : have \":nested\"") 41 | } 42 | 43 | // COROUTINE / YIELD / RESUME 44 | 45 | @Test 46 | fun bb_03_resume_err() { 47 | val l = lexer(""" 48 | resume a 49 | """) 50 | val parser = Parser(l) 51 | assert(trap { parser.expr() } == "anon : (lin 2, col 20) : resume error : expected call") 52 | } 53 | @Test 54 | fun bb_04_yield_err() { 55 | val l = lexer(""" 56 | yield 57 | 1 58 | """.trimIndent()) 59 | val parser = Parser(l) 60 | assert(trap { parser.expr() } == "anon : (lin 2, col 1) : expected \"(\" : have \"1\"") 61 | //assert(trap { parser.expr() } == "anon : (lin 1, col 1) : yield error : line break before expression") 62 | } 63 | @Test 64 | fun bb_06_resume() { 65 | val l = lexer(""" 66 | resume (f()) () 67 | """) 68 | val parser = Parser(l) 69 | val e = parser.expr() 70 | assert(e.to_str() == "(resume (f())())") { e.to_str() } 71 | } 72 | @Test 73 | fun bb_07_yield_err() { 74 | val l = lexer(""" 75 | yield(1) 76 | """) 77 | val parser = Parser(l) 78 | val e = parser.expr() 79 | assert(e.to_str() == "yield(1)") 80 | //assert(trap { parser.expr() } == "anon : (lin 3, col 9) : expected \"thus\" : have end of file") 81 | } 82 | 83 | // MULTI ARGS 84 | 85 | @Test 86 | fun cc_01_resume() { 87 | val l = lexer(""" 88 | resume co(1;;;,...;;;) 89 | """) 90 | val parser = Parser(l) 91 | val e = parser.expr() 92 | assert(e.to_str() == "(resume (co)(1))") { e.to_str() } 93 | 94 | } 95 | @Test 96 | fun cc_02_yield() { 97 | val l = lexer(""" 98 | yield(1;;;,...;;;) 99 | """) 100 | val parser = Parser(l) 101 | val e = parser.expr() 102 | assert(e.to_str() == "yield(1)") { e.to_str() } 103 | } 104 | @Test 105 | fun cc_03_yield() { 106 | val l = lexer(""" 107 | yield() 108 | """) 109 | val parser = Parser(l) 110 | //val e = parser.expr() 111 | //assert(e.tostr() == "yield(nil)") { e.tostr() } 112 | assert(trap { parser.expr() } == "anon : (lin 2, col 19) : expected expression : have \")\"") 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Programming Language Ceu (v0.5) 2 | 3 | Ceu is a [synchronous programming language][1] that reconciles *[Structured 4 | Concurrency][2]* with *[Event-Driven Programming][3]* in order to extend 5 | classical structured programming with three main functionalities: 6 | 7 | - Structured Deterministic Concurrency: 8 | - A set of structured primitives to lexically compose concurrent tasks 9 | (e.g., `spawn`, `par-or`, `toggle`). 10 | - A synchronous and deterministic scheduling policy, which provides 11 | predictable behavior and safe abortion of tasks. 12 | - A container primitive to hold dynamic tasks, which automatically releases 13 | them as they terminate. 14 | - Event Signaling Mechanisms: 15 | - An `await` primitive to suspend a task and wait for events. 16 | - A `broadcast` primitive to signal events and awake awaiting tasks. 17 | - Lexical Memory Management *(experimental)*: 18 | - A lexical policy to manage dynamic allocation automatically. 19 | - A set of strict escaping rules to preserve structured reasoning. 20 | - A reference-counter collector for deterministic reclamation. 21 | 22 | Ceu is inspired by [Esterel][4] and [Lua][5]. 23 | 24 | Follows a summary of the main ideas in the design of Ceu: 25 | 26 | - https://github.com/fsantanna/dceu/blob/main/doc/manual-out.md#1-design 27 | 28 | Follows an extended list of functionalities in Ceu: 29 | 30 | - Dynamic typing 31 | - Statements as expressions 32 | - Dynamic collections (tuples, vectors, and dictionaries) 33 | - Stackless coroutines (the basis of tasks) 34 | - Restricted closures (upvalues must be final) 35 | - Deferred statements (for finalization) 36 | - Exception handling (error & catch) 37 | - Hierarchical Tags and Tuple Templates (for data description) 38 | - Seamless integration with C (source-level compatibility) 39 | 40 | Ceu is in **experimental stage**. 41 | Both the compiler and runtime can become very slow. 42 | 43 | # Hello World! 44 | 45 | During 10 seconds, displays `Hello World!` every second: 46 | 47 | ``` 48 | spawn { 49 | watching <10:s> { 50 | every <1:s> { 51 | println("Hello World!") 52 | } 53 | } 54 | } 55 | ``` 56 | 57 | # Manual 58 | 59 | - https://github.com/fsantanna/dceu/blob/main/doc/manual-out.md 60 | 61 | # Install 62 | 63 | 1. Install `gcc` and `java`: 64 | 65 | ``` 66 | sudo apt install gcc default-jre 67 | ``` 68 | 69 | 2. Install `ceu`: 70 | 71 | ``` 72 | wget https://github.com/fsantanna/dceu/releases/download/v0.5.0/install-v0.5.0.sh 73 | sh install-v0.5.0.sh ./ceu/ 74 | ``` 75 | 76 | - We assume that you add `./ceu/` (the full path) to your environment `$PATH`. 77 | 78 | 3. Execute `ceu`: 79 | 80 | ``` 81 | ceu ./ceu/hello-world.ceu 82 | hello 83 | world 84 | ``` 85 | 86 | # pico-ceu 87 | 88 | The best way to try Ceu is through `pico-ceu`, a graphical library based on 89 | [SDL][7]: 90 | 91 | - 92 | 93 | # Resources 94 | 95 | - A toy Problem: Drag, Click, or Cancel 96 | - https://fsantanna.github.io/toy.html 97 | - Run with `pico-ceu` in `ceu/pico/tst/`: 98 | - `ceu --lib=pico click-drag-cancel-x.ceu` 99 | - Comparison with JS generators: 100 | - https://github.com/fsantanna/dceu/blob/main/src/test/kotlin/tst_99/JS_99.kt 101 | - A simple but complete 2D game in Ceu: 102 | - https://github.com/fsantanna/pico-ceu-rocks 103 | - Clone in `ceu/pico/`, `cd` to it, and run with `pico-ceu`: 104 | - `ceu --lib=pico main.ceu` 105 | - Academic publications: 106 | - http://ceu-lang.org/chico/#ceu 107 | - Mailing list (JOIN US!): 108 | - https://groups.google.com/g/ceu-lang 109 | 110 | [1]: https://fsantanna.github.io/sc.html 111 | [2]: https://en.wikipedia.org/wiki/Structured_concurrency 112 | [3]: https://en.wikipedia.org/wiki/Event-driven_programming 113 | [4]: https://en.wikipedia.org/wiki/Esterel 114 | [5]: https://en.wikipedia.org/wiki/Lua_(programming_language) 115 | [6]: https://github.com/fsantanna/pico-ceu 116 | [7]: https://www.libsdl.org/ 117 | -------------------------------------------------------------------------------- /src/main/kotlin/Optim.kt: -------------------------------------------------------------------------------- 1 | package dceu 2 | 3 | fun Expr.do_has_var (prime: Boolean): Boolean { 4 | return this.dn_collect { 5 | when (it) { 6 | is Expr.Proto, is Expr.Do -> null 7 | is Expr.Dcl -> if (prime || it.tk.str=="val" || it.tk.str=="var" || it.idtag.first.str=="it") listOf(Unit) else emptyList() 8 | is Expr.Defer, is Expr.Yield, is Expr.Spawn, is Expr.Delay, is Expr.Tasks -> listOf(Unit) 9 | else -> emptyList() 10 | } 11 | }.isNotEmpty() 12 | } 13 | 14 | fun Expr.has_escape (tag: String): Boolean { 15 | return this.dn_collect { 16 | when (it) { 17 | // TODO: should stop at Protos, but not fake spawn 18 | is Expr.Enclose -> if (it.tag.str == tag) null else emptyList() 19 | is Expr.Escape -> if (it.tag.str == tag) listOf(Unit) else emptyList() 20 | else -> emptyList() 21 | } 22 | }.isNotEmpty() 23 | } 24 | 25 | fun Expr.prune (): Expr { 26 | return when (this) { 27 | is Expr.Proto -> { 28 | val pars = this.pars.map { it.prune() } as List 29 | val blk = this.blk.prune() as Expr.Do 30 | Expr.Proto(this.tk_, this.nst, this.fake, this.tag, pars, blk) 31 | } 32 | 33 | is Expr.Do -> { 34 | val es = this.es.map { it.prune() } 35 | val up = this.fup() 36 | val isup = (up is Expr.Proto || up is Expr.Catch || up is Expr.Defer) 37 | val req = (up===null || isup || es.any { it.do_has_var(up is Expr.Loop) }) 38 | if (req) { 39 | Expr.Do(this.tk_, es) 40 | } else { 41 | Expr.Group(Tk.Fix("group", this.tk.pos.copy()), es) 42 | } 43 | } 44 | 45 | is Expr.Enclose -> { 46 | val es = this.es.map { it.prune() } 47 | if (this.es.any { it.has_escape(this.tag.str) }) { 48 | Expr.Enclose(this.tk_, this.tag, es) 49 | } else { 50 | Expr.Group(this.tk_, es) 51 | } 52 | } 53 | is Expr.Escape -> Expr.Escape(this.tk_, this.tag, this.e?.prune()) 54 | is Expr.Group -> Expr.Group(this.tk_, this.es.map { it.prune() }) 55 | is Expr.Dcl -> Expr.Dcl(this.tk_, lex, idtag, this.src?.prune()) 56 | 57 | is Expr.Set -> Expr.Set(this.tk_, this.dst.prune(), this.src.prune()) 58 | is Expr.If -> Expr.If(this.tk_, this.cnd.prune(), this.t.prune(), this.f.prune()) 59 | is Expr.Loop -> Expr.Loop(this.tk_, this.blk.prune()) 60 | is Expr.Data -> this 61 | is Expr.Drop -> Expr.Drop(this.tk_, this.e.prune()) 62 | is Expr.Catch -> Expr.Catch(this.tk_, this.tag, this.blk.prune() as Expr.Do) 63 | is Expr.Defer -> Expr.Defer(this.tk_, this.blk.prune() as Expr.Do) 64 | is Expr.Yield -> Expr.Yield(this.tk_, this.e.prune()) 65 | is Expr.Resume -> Expr.Resume(this.tk_, this.co.prune(), this.args.map { it.prune() }) 66 | is Expr.Spawn -> Expr.Spawn(this.tk_, this.tsks?.prune(), this.tsk.prune(), this.args.map { it.prune() }) 67 | is Expr.Delay -> this 68 | is Expr.Pub -> Expr.Pub(this.tk_, this.tsk?.prune()) 69 | is Expr.Toggle -> Expr.Toggle(this.tk_, this.tsk.prune(), this.on.prune()) 70 | is Expr.Tasks -> Expr.Tasks(this.tk_, this.max.prune()) 71 | 72 | is Expr.Tuple -> Expr.Tuple(this.tk_, this.args.map { it.prune() }) 73 | is Expr.Vector -> Expr.Vector(this.tk_, this.args.map { it.prune() }) 74 | is Expr.Dict -> { 75 | val args = this.args.map { (k, v) -> 76 | Pair(k.prune(), v.prune()) 77 | } 78 | Expr.Dict(this.tk_, args) 79 | } 80 | 81 | is Expr.Index -> Expr.Index(this.tk_, this.col.prune(), this.idx.prune()) 82 | is Expr.Call -> Expr.Call(this.tk_, this.clo.prune(), this.args.map { it.prune() }) 83 | 84 | is Expr.Nat, is Expr.Acc, is Expr.Nil, is Expr.Tag, 85 | is Expr.Bool, is Expr.Char, is Expr.Num -> this 86 | }.let { 87 | if (it !== this) { 88 | it.n = this.n 89 | G.ns[it.n] = it 90 | if (this !== G.outer) { 91 | G.ups[it.n] = G.ups[this.n]!! 92 | } 93 | } 94 | it 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/test/kotlin/tst_05/Parser_05.kt: -------------------------------------------------------------------------------- 1 | package tst_05 2 | 3 | import dceu.* 4 | import org.junit.Test 5 | 6 | class Parser_05 { 7 | // TASKS 8 | 9 | @Test 10 | fun aa_01_tasks_err() { 11 | val l = lexer(""" 12 | spawn {} in nil 13 | """) 14 | val parser = Parser(l) 15 | assert(trap { parser.exprs() } == "anon : (lin 2, col 19) : expected expression : have \"{\"") 16 | } 17 | @Test 18 | fun aa_02_tasks_err() { 19 | val l = lexer(""" 20 | spawn f() in () 21 | """) 22 | val parser = Parser(l) 23 | //parser.exprs() 24 | assert(trap { parser.exprs() } == "anon : (lin 2, col 27) : expected expression : have \")\"") 25 | //assert(trap { parser.exprs() } == "anon : (lin 2, col 26) : list error : expected expression") 26 | } 27 | @Test 28 | fun aa_03_tasks_err() { 29 | val l = lexer(""" 30 | spawn f in nil 31 | """) 32 | val parser = Parser(l) 33 | //assert(trap { parser.exprs() } == "anon : (lin 3, col 9) : invalid spawn : expected call") 34 | assert(trap { parser.exprs() } == "anon : (lin 2, col 21) : spawn error : expected call") 35 | } 36 | @Test 37 | fun aa_04_tasks() { 38 | val l = lexer(""" 39 | spawn T() in ts 40 | """) 41 | val parser = Parser(l) 42 | val e = parser.exprs() 43 | assert(e.to_str() == "(spawn T() in ts);\n") { e.to_str() } 44 | } 45 | 46 | // DETRACK 47 | /* 48 | @Test 49 | fun bb_01_detrack_err() { 50 | val l = tst_03.lexer(""" 51 | detrack(1) 52 | """) 53 | val parser = Parser(l) 54 | val e = parser.exprs() 55 | assert(e.tostr() == "detrack''(1)\n") { e.tostr() } 56 | //assert(trap { parser.exprs() } == "anon : (lin 3, col 9) : expected \"{\" : have end of file") 57 | } 58 | @Test 59 | fun bb_02_detrack_ok() { 60 | val l = tst_03.lexer( 61 | """ 62 | detrack(1) { it=> nil } 63 | """ 64 | ) 65 | val parser = Parser(l) 66 | val e = parser.exprs() 67 | //assert(e.tostr() == "((detrack(1)) thus { it =>\n" + 68 | // "nil\n" + 69 | // "})\n") { e.tostr() } 70 | assert(e.tostr() == "(detrack(1) { it =>\n" + 71 | "nil\n" + 72 | "})\n") { e.tostr() } 73 | } 74 | @Test 75 | fun bb_03_as() { 76 | val out = test(""" 77 | detrack(1) { 1 } 78 | """) 79 | assert(out == "anon : (lin 2, col 26) : expected identifier : have \"1\"\n") { out } 80 | } 81 | @Test 82 | fun bb_04_as() { 83 | val out = test(""" 84 | detrack(1) { x } 85 | """) 86 | assert(out == "anon : (lin 2, col 28) : expected \"=>\" : have \"}\"\n") { out } 87 | } 88 | @Test 89 | fun bb_05_as() { 90 | val out = test(""" 91 | detrack(1) { x => } 92 | """) 93 | assert(out == "anon : (lin 2, col 31) : expected expression : have \"}\"\n") { out } 94 | } 95 | @Test 96 | fun bb_06_as() { 97 | val out = test(""" 98 | detrack(1) { x => 1 99 | """) 100 | assert(out == "anon : (lin 3, col 9) : expected \"}\" : have end of file\n") { out } 101 | } 102 | @Test 103 | fun bb_07_as() { 104 | val out = test(""" 105 | catch ( x => 1 ) {nil} 106 | """) 107 | //assert(out == "anon : (lin 2, col 30) : expected \"in\" : have \"{\"\n") { out } 108 | assert(out == "") { out } 109 | } 110 | @Test 111 | fun bb_08_as() { 112 | val l = tst_03.lexer(""" 113 | detrack(1) { x => 1 } 114 | """) 115 | val parser = Parser(l) 116 | val e = parser.exprs() 117 | assert(e.tostr() == "(detrack(1) { x =>\n" + 118 | "1\n" + 119 | "})\n") { e.tostr() } 120 | //assert(trap { parser.exprs() } == "anon : (lin 3, col 9) : expected \"{\" : have end of file") 121 | } 122 | 123 | // TRACK 124 | 125 | @Test 126 | fun cc_01_track() { 127 | val l = lexer(""" 128 | track(x) 129 | """) 130 | val parser = Parser(l) 131 | val e = parser.exprs() 132 | assert(e.tostr() == "track(x)\n") { e.tostr() } 133 | } 134 | */ 135 | } 136 | -------------------------------------------------------------------------------- /src/main/kotlin/Cache.kt: -------------------------------------------------------------------------------- 1 | package dceu 2 | 3 | fun NExpr.fnex (): Expr { 4 | return G.ns[this]!! 5 | } 6 | 7 | fun Expr.fup (): Expr? { 8 | return G.ups[this.n]?.fnex() 9 | } 10 | 11 | fun Expr.fupx (): Expr { 12 | return this.fup()!! 13 | } 14 | 15 | fun cache_ns () { 16 | G.outer!!.dn_visit { 17 | G.ns[it.n] = it 18 | } 19 | } 20 | 21 | fun cache_ups () { 22 | G.outer!!.dn_visit { me -> 23 | when (me) { 24 | is Expr.Proto -> { 25 | G.ups[me.blk.n] = me.n 26 | me.pars.forEach { G.ups[it.n] = me.n } 27 | } 28 | 29 | is Expr.Do -> me.es.forEach { G.ups[it.n] = me.n } 30 | is Expr.Group -> me.es.forEach { G.ups[it.n] = me.n } 31 | is Expr.Enclose -> me.es.forEach { G.ups[it.n] = me.n } 32 | is Expr.Escape -> if (me.e !== null) G.ups[me.e.n] = me.n 33 | is Expr.Dcl -> if (me.src !== null) G.ups[me.src.n] = me.n 34 | is Expr.Set -> { 35 | G.ups[me.dst.n] = me.n 36 | G.ups[me.src.n] = me.n 37 | } 38 | 39 | is Expr.If -> { 40 | G.ups[me.cnd.n] = me.n 41 | G.ups[me.t.n] = me.n 42 | G.ups[me.f.n] = me.n 43 | } 44 | 45 | is Expr.Loop -> G.ups[me.blk.n] = me.n 46 | is Expr.Data -> {} 47 | is Expr.Drop -> G.ups[me.e.n] = me.n 48 | 49 | is Expr.Catch -> G.ups[me.blk.n] = me.n 50 | is Expr.Defer -> G.ups[me.blk.n] = me.n 51 | 52 | is Expr.Yield -> G.ups[me.e.n] = me.n 53 | is Expr.Resume -> { 54 | G.ups[me.co.n] = me.n 55 | me.args.forEach { G.ups[it.n] = me.n } 56 | } 57 | 58 | is Expr.Spawn -> { 59 | if (me.tsks !== null) G.ups[me.tsks.n] = me.n 60 | G.ups[me.tsk.n] = me.n 61 | me.args.forEach { G.ups[it.n] = me.n } 62 | } 63 | 64 | is Expr.Delay -> {} 65 | is Expr.Pub -> if (me.tsk !== null) G.ups[me.tsk.n] = me.n 66 | is Expr.Toggle -> { 67 | G.ups[me.tsk.n] = me.n 68 | G.ups[me.on.n] = me.n 69 | } 70 | 71 | is Expr.Tasks -> G.ups[me.max.n] = me.n 72 | 73 | is Expr.Nat -> {} 74 | is Expr.Acc -> {} 75 | is Expr.Nil -> {} 76 | is Expr.Tag -> {} 77 | is Expr.Bool -> {} 78 | is Expr.Char -> {} 79 | is Expr.Num -> {} 80 | is Expr.Tuple -> me.args.forEach { G.ups[it.n] = me.n } 81 | is Expr.Vector -> me.args.forEach { G.ups[it.n] = me.n } 82 | is Expr.Dict -> { 83 | me.args.forEach { 84 | G.ups[it.first.n] = me.n 85 | G.ups[it.second.n] = me.n 86 | } 87 | } 88 | 89 | is Expr.Index -> { 90 | G.ups[me.col.n] = me.n 91 | G.ups[me.idx.n] = me.n 92 | } 93 | 94 | is Expr.Call -> { 95 | G.ups[me.clo.n] = me.n 96 | me.args.forEach { G.ups[it.n] = me.n } 97 | } 98 | } 99 | } 100 | } 101 | 102 | fun cache_tags () { 103 | TAGS.forEach { 104 | G.tags[it] = Tk.Tag(it,G.outer!!.tk.pos.copy()) 105 | } 106 | G.outer!!.dn_visit { 107 | when (it) { 108 | is Expr.Enclose-> G.tags[it.tag.str] = it.tag 109 | is Expr.Escape -> G.tags[it.tag.str] = it.tag 110 | is Expr.Data -> G.tags[it.tk_.str] = it.tk_ 111 | is Expr.Catch -> if (it.tag !== null) { G.tags[it.tag.str] = it.tag } 112 | is Expr.Tag -> G.tags[it.tk_.str] = it.tk_ 113 | else -> {} 114 | } 115 | } 116 | } 117 | 118 | fun cache_nonlocs () { 119 | fun Expr.Proto.to_nonlocs (): List { 120 | return this 121 | .dn_collect { if (it is Expr.Acc) listOf(it) else emptyList() } 122 | .map { acc -> acc.id_to_dcl(acc.tk.str)!!.let { 123 | Pair(it, it.to_blk()) 124 | }} 125 | .filter { (_,blk) -> blk.fup()!=null } 126 | .filter { (_,blk) -> this.fupx().up_first { it==blk } != null } 127 | .map { (dcl,_) -> dcl } 128 | .sortedBy { it.n } 129 | } 130 | G.outer!!.dn_visit { 131 | if (it is Expr.Proto) { 132 | G.nonlocs[it.n] = it.to_nonlocs().map { it.n } 133 | } 134 | } 135 | 136 | } -------------------------------------------------------------------------------- /src/test/kotlin/tst_99/Lexer_99.kt: -------------------------------------------------------------------------------- 1 | package tst_99 2 | 3 | import dceu.* 4 | import org.junit.Test 5 | 6 | fun lexer (str: String): Lexer { 7 | return Lexer(listOf(Pair(Triple("anon",1,1), str.reader()))) 8 | } 9 | 10 | class Lexer_99 { 11 | @Test 12 | fun aa_01_ops() { 13 | val l = 14 | lexer("and or not in? is? in-not? is-not?") 15 | val tks = l.lex().iterator() 16 | assert(tks.next().let { it is Tk.Op && it.str == "and" }) 17 | assert(tks.next().let { it is Tk.Op && it.str == "or" }) 18 | assert(tks.next().let { it is Tk.Op && it.str == "not" }) 19 | assert(tks.next().let { it is Tk.Op && it.str == "in?" }) 20 | assert(tks.next().let { it is Tk.Op && it.str == "is?" }) 21 | assert(tks.next().let { it is Tk.Op && it.str == "in-not?" }) 22 | assert(tks.next().let { it is Tk.Op && it.str == "is-not?" }) 23 | assert(tks.next() is Tk.Eof) 24 | assert(!tks.hasNext()) 25 | } 26 | @Test 27 | fun aa_02_ops() { 28 | val l = lexer("{{not}} not -> --> <-- <- \\") 29 | val tks = l.lex().iterator() 30 | assert(tks.next().let { it is Tk.Id && it.str == "not" }) 31 | assert(tks.next().let { it is Tk.Op && it.str == "not" }) 32 | assert(tks.next().let { it is Tk.Fix && it.str == "->" }) 33 | assert(tks.next().let { it is Tk.Fix && it.str == "-->" }) 34 | assert(tks.next().let { it is Tk.Fix && it.str == "<--" }) 35 | assert(tks.next().let { it is Tk.Fix && it.str == "<-" }) 36 | assert(tks.next().let { it is Tk.Fix && it.str == "\\" }) 37 | assert(tks.next() is Tk.Eof) 38 | assert(!tks.hasNext()) 39 | } 40 | @Test 41 | fun BUG_aa_03_ops_err() { 42 | val l = lexer("{{f.x}}") 43 | val tks = l.lex().iterator() 44 | assert(tks.next().let { it is Tk.Id && it.str == "TODO" }) 45 | assert(!tks.hasNext()) 46 | } 47 | @Test 48 | fun aa_03_cmds() { 49 | val l = 50 | lexer("ifs thus resume-yield-all await while enum watching par every func where par-and par-or until break skip with") 51 | val tks = l.lex().iterator() 52 | assert(tks.next().let { it is Tk.Fix && it.str == "ifs" }) 53 | assert(tks.next().let { it is Tk.Fix && it.str == "thus" }) 54 | assert(tks.next().let { it is Tk.Fix && it.str == "resume-yield-all" }) 55 | assert(tks.next().let { it is Tk.Fix && it.str == "await" }) 56 | assert(tks.next().let { it is Tk.Fix && it.str == "while" }) 57 | assert(tks.next().let { it is Tk.Fix && it.str == "enum" }) 58 | assert(tks.next().let { it is Tk.Fix && it.str == "watching" }) 59 | assert(tks.next().let { it is Tk.Fix && it.str == "par" }) 60 | assert(tks.next().let { it is Tk.Fix && it.str == "every" }) 61 | assert(tks.next().let { it is Tk.Fix && it.str == "func" }) 62 | assert(tks.next().let { it is Tk.Fix && it.str == "where" }) 63 | assert(tks.next().let { it is Tk.Fix && it.str == "par-and" }) 64 | assert(tks.next().let { it is Tk.Fix && it.str == "par-or" }) 65 | assert(tks.next().let { it is Tk.Fix && it.str == "until" }) 66 | assert(tks.next().let { it is Tk.Fix && it.str == "break" }) 67 | assert(tks.next().let { it is Tk.Fix && it.str == "skip" }) 68 | assert(tks.next().let { it is Tk.Fix && it.str == "with" }) 69 | assert(tks.next() is Tk.Eof) 70 | assert(!tks.hasNext()) 71 | } 72 | 73 | @Test 74 | fun bb_01_clk() { 75 | val l = lexer("1:h x :min 30:s (1) :ms") 76 | val tks = l.lex().iterator() 77 | assert(tks.next().let { it is Tk.Num && it.str == "1" }) 78 | assert(tks.next().let { it is Tk.Tag && it.str == ":h" }) 79 | assert(tks.next().let { it is Tk.Id && it.str == "x" }) 80 | assert(tks.next().let { it is Tk.Tag && it.str == ":min" }) 81 | assert(tks.next().let { it is Tk.Num && it.str == "30" }) 82 | assert(tks.next().let { it is Tk.Tag && it.str == ":s" }) 83 | assert(tks.next().let { it is Tk.Fix && it.str == "(" }) 84 | assert(tks.next().let { it is Tk.Num && it.str == "1" }) 85 | assert(tks.next().let { it is Tk.Fix && it.str == ")" }) 86 | assert(tks.next().let { it is Tk.Tag && it.str == ":ms" }) 87 | assert(tks.next() is Tk.Eof) 88 | assert(!tks.hasNext()) 89 | } 90 | @Test 91 | fun bb_02_clk() { 92 | val l = lexer("1s10k") 93 | val tks = l.lex().iterator() 94 | assert(tks.next().let { it is Tk.Num && it.str == "1s10k" }) 95 | //println(tks.next()) 96 | //assert(trap { tks.next() } == "anon : (lin 1, col 1) : invalid time constant") 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/kotlin/Tostr.kt: -------------------------------------------------------------------------------- 1 | package dceu 2 | 3 | fun Pos.pre (): String { 4 | assert(this.lin>=0 && this.col>=0) 5 | return "^[${this.lin},${this.col}]" 6 | } 7 | 8 | fun Tk.dump (): String { 9 | return if (!DUMP) "" else { 10 | "(${this.pos.file} : lin ${this.pos.lin} : col ${this.pos.col})" 11 | } 12 | } 13 | fun Expr.dump (): String { 14 | return if (!DUMP) "" else { 15 | this.tk.dump() + " | " + this.to_str().quote(15) 16 | } 17 | } 18 | 19 | fun Tk.fpre (pre: Boolean): String { 20 | return if (pre) this.pos.pre() else "" 21 | } 22 | 23 | @JvmName("Id_Tag_tostr") 24 | fun Id_Tag.to_str (pre: Boolean = false): String { 25 | return this.first.fpre(pre) + this.first.str + this.second.cond { " " + it.fpre(pre) + it.str } 26 | } 27 | 28 | @JvmName("Clock_tostr") 29 | fun Clock.to_str (pre: Boolean): String { 30 | return "<" + this.map { it.second.to_str(pre) + " " + it.first.str }.joinToString(",") + ">" 31 | } 32 | 33 | fun Patt.to_str (pre: Boolean = false): String { 34 | return when (this) { 35 | is Patt.None -> "(${Pair(this.id,this.tag).to_str(pre)} | ${this.pos.to_str(true)})" 36 | is Patt.One -> "(${Pair(this.id,this.tag).to_str(pre)} | ${this.e.to_str(pre)} and ${this.pos.to_str(true)})" 37 | is Patt.Tup -> TODO() 38 | } 39 | } 40 | 41 | fun Expr.to_str_x (pre: Boolean): String { 42 | return when (this) { 43 | is Expr.Do -> this.es.to_str(pre) 44 | is Expr.Group -> this.es.to_str(pre) 45 | else -> error("impossible case") 46 | }.let { 47 | "{\n" + it + "}" 48 | } 49 | } 50 | 51 | fun Expr.to_str (pre: Boolean = false): String { 52 | return when (this) { 53 | is Expr.Proto -> { 54 | val mod = when { 55 | this.fake -> " :fake" 56 | this.nst -> " :nested" 57 | else -> "" 58 | } 59 | val pars = this.pars.map { it.idtag.to_str(pre) }.joinToString(",") 60 | "(" + this.tk.str + mod + " (" + pars + ") " + this.tag.cond{ it.str+" " } + this.blk.to_str_x(pre) + ")" 61 | } 62 | is Expr.Do -> "do " + this.to_str_x(pre) 63 | is Expr.Group -> "group " + this.to_str_x(pre) 64 | is Expr.Enclose -> "enclose' " + this.tag.str + " {\n" + this.es.to_str(pre) + "}" 65 | is Expr.Escape -> "escape(" + this.tag.str + this.e.cond { ","+it.to_str(pre) } + ")" 66 | is Expr.Dcl -> { 67 | "(" + this.tk_.str + " " + this.idtag.to_str(pre) + this.src.cond { " = ${it.to_str(pre)}" } + ")" 68 | } 69 | is Expr.Set -> "(set " + this.dst.to_str(pre) + " = " + this.src.to_str(pre) + ")" 70 | is Expr.If -> "if " + this.cnd.to_str(pre) + " " + this.t.to_str_x(pre) + " else " + this.f.to_str_x(pre) 71 | is Expr.Loop -> "loop' " + this.blk.to_str_x(pre) 72 | is Expr.Data -> "(data " + this.tk.str + " = [" + this.ids.map { it.to_str() }.joinToString(",") + "])" 73 | is Expr.Drop -> "drop(" + this.e.to_str(pre) + ")" 74 | 75 | is Expr.Catch -> "catch " + this.tag.cond { it.str+" " } + this.blk.to_str_x(pre) 76 | is Expr.Defer -> "defer " + this.blk.to_str_x(pre) 77 | 78 | is Expr.Yield -> "yield(" + this.e.to_str(pre) + ")" 79 | is Expr.Resume -> "(resume (" + this.co.to_str(pre) + ")(" + this.args.map { it.to_str(pre) }.joinToString(",") + "))" 80 | 81 | is Expr.Spawn -> "(spawn " + this.tsk.to_str(pre) + "(" + this.args.map { it.to_str(pre) }.joinToString(",") + ")" + this.tsks.cond { " in ${it.to_str(pre)}" } + ")" 82 | is Expr.Delay -> "delay" 83 | is Expr.Pub -> this.tsk.cond { it.to_str(pre)+"." } + "pub" 84 | is Expr.Toggle -> "(toggle ${this.tsk.to_str(pre)}(${this.on.to_str(pre)}))" 85 | is Expr.Tasks -> "tasks(" + this.max.to_str(pre) + ")" 86 | 87 | is Expr.Nat -> "```" + this.tk_.tag.cond { it+" " } + this.tk.str + "```" 88 | is Expr.Acc -> this.ign.cond { "__" } + this.tk.str 89 | is Expr.Nil -> this.tk.str 90 | is Expr.Tag -> this.tk.str 91 | is Expr.Bool -> this.tk.str 92 | is Expr.Char -> this.tk.str 93 | is Expr.Num -> this.tk.str 94 | is Expr.Tuple -> "[" + this.args.map { it.to_str(pre) }.joinToString(",") + "]" 95 | is Expr.Vector -> "#[" + this.args.map { it.to_str(pre) }.joinToString(",") + "]" 96 | is Expr.Dict -> "@[" + this.args.map { "(${it.first.to_str(pre)},${it.second.to_str(pre)})" }.joinToString(",") + "]" 97 | is Expr.Index -> this.col.to_str(pre) + "[" + this.idx.to_str(pre) + "]" 98 | is Expr.Call -> this.clo.to_str(pre) + "(" + this.args.map { it.to_str(pre) }.joinToString(",") + ")" 99 | // TODO: collapse broadcast' 100 | }.let { 101 | when { 102 | !pre -> it 103 | (it.length>0 && it[0]=='(') -> '(' + this.tk.pos.pre() + it.drop(1) 104 | else -> this.tk.pos.pre() + it 105 | } 106 | } 107 | } 108 | 109 | fun List.to_str (pre: Boolean=false): String { 110 | return this.map { it.to_str(pre) }.joinToString(";\n") + ";\n" 111 | } 112 | -------------------------------------------------------------------------------- /src/main/kotlin/Aux.kt: -------------------------------------------------------------------------------- 1 | package dceu 2 | 3 | fun List>.union (): Map { 4 | return this.fold(emptyMap()) { acc, value -> acc + value } 5 | } 6 | 7 | fun Expr.dn_collect (f: (Expr)->List?): List { 8 | val v = f(this) 9 | if (v === null) { 10 | return emptyList() 11 | } 12 | return v + when (this) { 13 | is Expr.Proto -> this.blk.dn_collect(f) + this.pars.map { it.dn_collect(f) }.flatten() 14 | is Expr.Do -> this.es.map { it.dn_collect(f) }.flatten() 15 | is Expr.Group -> this.es.map { it.dn_collect(f) }.flatten() 16 | is Expr.Enclose -> this.es.map { it.dn_collect(f) }.flatten() 17 | is Expr.Escape -> this.e?.dn_collect(f) ?: emptyList() 18 | is Expr.Dcl -> this.src?.dn_collect(f) ?: emptyList() 19 | is Expr.Set -> this.dst.dn_collect(f) + this.src.dn_collect(f) 20 | is Expr.If -> this.cnd.dn_collect(f) + this.t.dn_collect(f) + this.f.dn_collect(f) 21 | is Expr.Loop -> this.blk.dn_collect(f) 22 | is Expr.Drop -> this.e.dn_collect(f) 23 | 24 | is Expr.Catch -> this.blk.dn_collect(f) 25 | is Expr.Defer -> this.blk.dn_collect(f) 26 | 27 | is Expr.Yield -> this.e.dn_collect(f) 28 | is Expr.Resume -> this.co.dn_collect(f) + this.args.map { it.dn_collect(f) }.flatten() 29 | 30 | is Expr.Spawn -> (this.tsks?.dn_collect(f) ?: emptyList()) + this.tsk.dn_collect(f) + this.args.map { it.dn_collect(f) }.flatten() 31 | is Expr.Delay -> emptyList() 32 | is Expr.Pub -> this.tsk?.dn_collect(f) ?: emptyList() 33 | is Expr.Toggle -> this.tsk.dn_collect(f) + this.on.dn_collect(f) 34 | is Expr.Tasks -> this.max.dn_collect(f) 35 | 36 | is Expr.Tuple -> this.args.map { it.dn_collect(f) }.flatten() 37 | is Expr.Vector -> this.args.map { it.dn_collect(f) }.flatten() 38 | is Expr.Dict -> this.args.map { it.first.dn_collect(f) + it.second.dn_collect(f) }.flatten() 39 | is Expr.Index -> this.col.dn_collect(f) + this.idx.dn_collect(f) 40 | is Expr.Call -> this.clo.dn_collect(f) + this.args.map { it.dn_collect(f) }.flatten() 41 | 42 | is Expr.Acc, is Expr.Data, is Expr.Nat, 43 | is Expr.Nil, is Expr.Tag, is Expr.Bool, 44 | is Expr.Char, is Expr.Num -> emptyList() 45 | } 46 | } 47 | 48 | fun Expr.dn_visit (f: (Expr)->Unit) { 49 | this.dn_collect { f(it) ; emptyList() } 50 | } 51 | 52 | fun trap (f: ()->Unit): String { 53 | try { 54 | f() 55 | error("impossible case") 56 | } catch (e: Throwable) { 57 | return e.message!! 58 | } 59 | } 60 | 61 | fun Pos.is_same_line (oth: Pos): Boolean { 62 | return (this.file==oth.file && this.lin==oth.lin && this.brks==oth.brks) 63 | } 64 | 65 | fun T?.cond2 (f: (v:T)->String, g: (()->String)?): String { 66 | return when (this) { 67 | false, null -> if (g !== null) g() else "" 68 | else -> f(this) 69 | } 70 | } 71 | 72 | fun T?.cond (f: (v:T)->String): String { 73 | return this.cond2(f) {""} 74 | } 75 | fun String.quote (n: Int): String { 76 | return this 77 | .replace('\n',' ') 78 | .replace('"','.') 79 | .replace('\\','.') 80 | .let { 81 | if (it.length<=n) it else it.take(n-3)+"..." 82 | } 83 | 84 | } 85 | 86 | fun err (pos: Pos, str: String): Nothing { 87 | error(pos.file + " : (lin ${pos.lin}, col ${pos.col}) : $str") 88 | } 89 | fun err (tk: Tk, str: String): Nothing { 90 | err(tk.pos, str) 91 | } 92 | fun err_expected (tk: Tk, str: String) { 93 | val have = when { 94 | (tk is Tk.Eof) -> "end of file" 95 | else -> '"' + tk.str + '"' 96 | } 97 | err(tk, "expected $str : have $have") 98 | } 99 | 100 | fun Array.cmds_opts () : Pair,Map> { 101 | val cmds = this.filter { !it.startsWith("--") } 102 | val opts = this 103 | .filter { it.startsWith("--") } 104 | .map { 105 | if (it.contains('=')) { 106 | val (k,v) = Regex("(--.*)=(.*)").find(it)!!.destructured 107 | Pair(k,v) 108 | } else { 109 | Pair(it, null) 110 | } 111 | } 112 | .toMap() 113 | return Pair(cmds,opts) 114 | } 115 | 116 | fun String.idc (): String { 117 | return when { 118 | (this[0] == '{') -> { 119 | val MAP = mapOf( 120 | Pair('+', "plus"), 121 | Pair('-', "minus"), 122 | Pair('*', "asterisk"), 123 | Pair('/', "slash"), 124 | Pair('>', "greater"), 125 | Pair('<', "less"), 126 | Pair('=', "equals"), 127 | Pair('!', "exclaim"), 128 | Pair('|', "bar"), 129 | Pair('&', "ampersand"), 130 | Pair('#', "hash"), 131 | ) 132 | this.drop(2).dropLast(2).toList().map { MAP[it] }.joinToString("_") 133 | } 134 | else -> { 135 | val MAP = mapOf( 136 | Pair(':', ""), 137 | Pair('.', "_"), 138 | Pair('-', "_dash_"), 139 | Pair('\'', "_plic_"), 140 | Pair('?', "_question_"), 141 | Pair('!', "_bang_"), 142 | ) 143 | this.toList().map { MAP[it] ?: it }.joinToString("") 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | - define function for col set / call instead of inline 4 | 5 | - DLEX 6 | - novo refactor de chk_set 7 | - func vs thus 8 | - remove :tmp 9 | 10 | - detrack/pub de task_in continua com problemas, 11 | uma vez que pode ser atribuido a var em bloco a que pertence, 12 | terminar, ser reclamado, e gerar um dangling pointer 13 | - deve pegar escopo do bloco interno da task 14 | - no caso do track, isso nao é suficiente, uma vez que o thus vai 15 | receber um FLEET com ref pendente e gerar erro 16 | 17 | - preciso de um THUS que os args nao sejam chk_set para FLEET 18 | - faz sentido na ideia de que é um pipe que nao vai consumir a entrada 19 | - no entando, se esse pipe chamar uma funcao, vai dar problema 20 | - eu poderia checar se é FLEET, guardar essa info, mover como IMMUT para a local, 21 | deixar o codigo do usuario trabalhar, no fim verificar flag e dar um drop 22 | - o immut nao permitiria a variavel ser movida ou coletada, garantindo que o drop 23 | ao final funcione 24 | - thus entao nunca é chk_set 25 | 26 | - can be dropped: exe clo, task, in? 27 | 28 | - bcast inside func 29 | 30 | O `thus` atribui um valor de fora diretamente ao `ceu_acc`, como uma nova 31 | variável simples, sem nenhuma verificação. Isso permite que o escopo 32 | interno acesse o valor de fora como uma local temporária. 33 | Esse mecanismo é fundamental em 4 cenários: 34 | - or, and: 35 | - não recalcular expressão 36 | - yield: 37 | O valor de fora pode vir de um `resume` em um escopo muito aninhado, 38 | que não sobreviveria a outro acesso que cruze um novo `yield`. Também 39 | pode vir de um broadcast com a mesma característica. Por essa razão, 40 | é importante que o corpo do `thus` não tenha `yield`s. 41 | - detrack: 42 | - task pode morrer entre yields 43 | - pub 44 | - task pode morrer entre yields 45 | 46 | - THUS 47 | - no yields, bcasts, spawns, set outs 48 | - yield, detrack, pub 49 | - remove ylds 50 | - coro/task accept exact 1 arg in decl resume 51 | 52 | - SOBRE valores passados como argumentos para funções e thus: 53 | - sempre inc, dec 54 | - chk_set no inicio somente se FLEETING 55 | - caso contrário, eu sei que é correto, mas poderia falhar no caso de assign paralelo 56 | - SOBRE valores passados como argumentos para broadcast: 57 | - precisam virar HLD_IMMUT para que não sejam dropados, uma 58 | vez que são passados para escopos alienigenas 59 | - são gc_inc/dec para não serem coletados por chks em fcs chamadas 60 | 61 | # BUGS 62 | 63 | - detrack, scope 999 64 | - ll_track16_hold_err 65 | - block defer should be after block clean up 66 | - mudei coro,coros,track para hld_type=0, preciso avaliar e testar 67 | - como vou fazer com os holes? 68 | - o certo seria 69 | - ao terminar o loop 70 | - verificar que nao tem outra instancia na pilha 71 | - fazer o shift left 72 | - checar todos os bcasts e testar se estou morto 73 | - todo_status5 74 | - globals should be dyns like the others 75 | - currently they are not dealloc in block end 76 | - copy: tags and upvals 77 | 78 | # NEXT 79 | 80 | - bcast :global, tasks, enclosing task, enclosing block, specific task 81 | - await, bcast: obligatory parenthesis 82 | - break(e) <-- return expression 83 | - share primitive? 84 | - call: f <- [...], f() <-- [...] 85 | - next => dict-next, iter-next 86 | - itr->next() 87 | - bcast in block 88 | - val [x,y] = ... 89 | - pattern matching em geral 90 | - val _ = ... (no decl, no acc) 91 | - where { x=10 } // no val x=10 92 | - export :all [...] { } 93 | - var x :T = y 94 | - set x = y 95 | - assert(y is :T) 96 | - val x = assert(f() as :number, "xxx") 97 | - as checks but returns e if successful 98 | - :error -> tag(:error,"message") 99 | - [x,y] = tup (destructor) 100 | - kill task 101 | - polymorphism 102 | - poly var f = func ({a:X}, {b:Y}) 103 | - poly func f (a:X, b:Y) 104 | - f({a},{b}) 105 | - set MAX :T = 10 106 | - ~= (like) 107 | - 1 ~= "1" 108 | - #x ~= #x.y 109 | - str ~= ".*" 110 | - await/awaiting/every :type 111 | - all asserts should become throws w/ tags 112 | - numbers 113 | +002 114 | -100 115 | 1_000_010 116 | - test each ceu_bstack case 117 | - if no catch, create test 118 | 119 | # BACK 120 | 121 | - power assert 122 | - pyret check 123 | group { 124 | var x = 10 125 | var y = f(x) 126 | assert x < z ;; default/power message 127 | assert x > y, "error" ;; custom message 128 | } 129 | - native 130 | - `${complex-expr}` (nested parsing in w/ XCEU?) 131 | - args keyword (or ...) 132 | - access to variable args from func args 133 | - 1 >> f >> g -> g(f(1)) 134 | - parser 135 | - reject no assign stmts 136 | - defer 137 | - while 138 | - ? 139 | - reject anything after throw (last stmt) 140 | - reject yield with no direct enclosing task 141 | - ceu2c 142 | - struct <- tuple 143 | - vec <- buf 144 | - call w/o blocks optimize 145 | - ranges 146 | - [1..2] 147 | - traverse 148 | call fat (x=10) { 149 | if x == 1 { 150 | 1 151 | } else { 152 | fat(x-1) 153 | } 154 | } 155 | 156 | # IDEAS 157 | 158 | - uv-ceu 159 | - example with read in parallel 160 | - pipe compose 161 | - code NAV tree 162 | - lessmilk 163 | - 7 guis 164 | - IUP + canvas 165 | - doc pico-sdl/ceu 166 | - incremental GC 167 | - is a coro itself (start from leaves, back to root) 168 | - contracts 169 | - android SDK 170 | - modelo sincrono permite copiar memória em aplicações simétricos 171 | - Toda memória é gerenciada dict array task 172 | - pico-sdl 173 | - image/font cache 174 | - rx-ceu 175 | - Meier "dual" paper 176 | -------------------------------------------------------------------------------- /src/test/kotlin/tst_02/Parser_02.kt: -------------------------------------------------------------------------------- 1 | package tst_02 2 | 3 | import dceu.* 4 | import org.junit.Test 5 | 6 | class Parser_02 { 7 | // DEFER 8 | 9 | @Test 10 | fun aa_01_defer() { 11 | val l = lexer("defer { nil }") 12 | val parser = Parser(l) 13 | val e = parser.exprs() 14 | assert(e.to_str() == "defer {\nnil;\n};\n") { e.to_str() } 15 | } 16 | 17 | // THROW / CATCH 18 | 19 | @Test 20 | fun bb_01_throw_catch() { 21 | val l = lexer("catch :x { error(1) }") 22 | //val l = lexer("catch ( it|1) { error(1) }") 23 | val parser = Parser(l) 24 | val e = parser.exprs() 25 | /* 26 | assert(e.tostr() == "catch' (do {\n" + 27 | "(val it = ```:ceu *(ceu_acc.Dyn->Error.val)```);\n" + 28 | "1;\n" + 29 | "}) {\n" + 30 | "error(1);\n" + 31 | "};\n") { e.tostr() } 32 | */ 33 | assert(e.to_str() == "catch :x {\n" + 34 | "error(1);\n" + 35 | "};\n") { e.to_str() } 36 | } 37 | @Test 38 | fun bb_02_throw_catch() { 39 | val l = lexer("catch (it :T| it[0]) { nil }") 40 | val parser = Parser(l) 41 | /* 42 | val e = parser.exprs() 43 | assert(e.tostr() == "catch' (do {\n" + 44 | "(val it :T = ```:ceu *(ceu_acc.Dyn->Error.val)```);\n" + 45 | "it[0];\n" + 46 | "}) {\n" + 47 | "nil;\n" + 48 | "};\n") { e.tostr() } 49 | */ 50 | } 51 | 52 | // IT / AS 53 | 54 | @Test 55 | fun cc_01_it() { 56 | val l = lexer("it") 57 | val parser = Parser(l) 58 | val e = parser.exprs() 59 | assert(e.to_str() == "it;\n") { e.to_str() } 60 | } 61 | @Test 62 | fun cc_02_as() { 63 | val out = test(""" 64 | catch ( 1 ) 65 | """) 66 | assert(out == "anon : (lin 2, col 21) : expected tag : have \"1\"\n") { out } 67 | //assert(out == "anon : (lin 2, col 21) : expected tag : have \"1\"\n") { out } 68 | //assert(out == "anon : (lin 2, col 21) : expected identifier : have \"1\"\n") { out } 69 | } 70 | @Test 71 | fun cc_03_as() { 72 | val out = test(""" 73 | catch ( x ) 74 | """) 75 | assert(out == "anon : (lin 2, col 21) : expected tag : have \"x\"\n") { out } 76 | //assert(out == "anon : (lin 2, col 21) : expected tag : have \"x\"\n") { out } 77 | //assert(out == "anon : (lin 2, col 23) : expected \"|\" : have \")\"\n") { out } 78 | } 79 | @Test 80 | fun cc_04_as() { 81 | val out = test(""" 82 | catch ( :x | ) 83 | """) 84 | assert(out == "anon : (lin 2, col 24) : expected \")\" : have \"|\"\n") { out } 85 | //assert(out == "anon : (lin 2, col 26) : expected expression : have \")\"\n") { out } 86 | //assert(out == "anon : (lin 3, col 9) : expected \"{\" : have end of file\n") { out } 87 | } 88 | @Test 89 | fun cc_05_as() { 90 | val out = test(""" 91 | catch ( x | 1 92 | """) 93 | assert(out == "anon : (lin 2, col 21) : expected tag : have \"x\"\n") { out } 94 | //assert(out == "anon : (lin 3, col 9) : expected \")\" : have end of file\n") { out } 95 | } 96 | @Test 97 | fun cc_06_as() { 98 | val out = test(""" 99 | catch ( x| 1 ) {nil} 100 | println(:ok) 101 | """) 102 | //assert(out == "anon : (lin 2, col 27) : expected \"in\" : have \"{\"\n") { out } 103 | assert(out == "anon : (lin 2, col 21) : expected tag : have \"x\"\n") { out } 104 | //assert(out == ":ok\n") { out } 105 | } 106 | 107 | // LOOP 108 | 109 | @Test 110 | fun qq_01_loop_err() { 111 | val l = tst_01.lexer("loop' { ;;;do;;; nil }") 112 | val parser = Parser(l) 113 | val e1 = parser.expr() as Expr.Loop 114 | assert(e1.blk.to_str_x(false) == "{\nnil;\n}") { e1.blk.to_str() } 115 | } 116 | @Test 117 | fun qq_02_loop_err() { 118 | val l = tst_01.lexer("loop' until {") 119 | val parser = Parser(l) 120 | assert(trap { parser.expr_1_bin() } == "anon : (lin 1, col 7) : expected \"{\" : have \"until\"") 121 | //assert(trap { parser.expr_1_bin() } == "anon : (lin 1, col 12) : expected expression : have \"{\"") 122 | } 123 | @Test 124 | fun qq_03_loop_err() { 125 | val l = tst_02.lexer("loop' x { }") 126 | val parser = Parser(l) 127 | //assert(trap { parser.expr_prim() } == "anon : (lin 1, col 7) : invalid loop : unexpected x") 128 | assert(trap { parser.expr_prim() } == "anon : (lin 1, col 7) : expected \"{\" : have \"x\"") 129 | } 130 | 131 | // PATTS 132 | 133 | /* 134 | @Test 135 | fun dd_01_patt () { 136 | val l = tst_01.lexer( 137 | """ 138 | () 139 | """ 140 | ) 141 | val parser = Parser(l) 142 | assert(trap { parser.patt() } == "anon : (lin 2, col 14) : expected identifier : have \")\"") 143 | } 144 | @Test 145 | fun dd_02_patt () { 146 | val l = tst_01.lexer( 147 | """ 148 | (x) 149 | """ 150 | ) 151 | val parser = Parser(l) 152 | assert(trap { parser.patt() } == "anon : (lin 2, col 15) : expected \"|\" : have \")\"") 153 | } 154 | @Test 155 | fun dd_03_patt () { 156 | val l = tst_01.lexer( 157 | """ 158 | (x|) 159 | """ 160 | ) 161 | val parser = Parser(l) 162 | assert(trap { parser.patt() } == "anon : (lin 2, col 16) : expected expression : have \")\"") 163 | } 164 | @Test 165 | fun dd_04_patt () { 166 | val l = tst_01.lexer( 167 | """ 168 | (x|true) 169 | """ 170 | ) 171 | val parser = Parser(l) 172 | val p = parser.patt() 173 | assert(Patts_Any_tostr(p) == "(x | true)") { Patts_Any_tostr(p) } 174 | } 175 | @Test 176 | fun dd_05_patt () { 177 | val l = tst_01.lexer( 178 | """ 179 | (x:X|nil) 180 | """ 181 | ) 182 | val parser = Parser(l) 183 | val p = parser.patt() 184 | assert(Patts_Any_tostr(p) == "(x :X | nil)") { Patts_Any_tostr(p) } 185 | } 186 | */ 187 | } 188 | -------------------------------------------------------------------------------- /src/main/kotlin/Expr.kt: -------------------------------------------------------------------------------- 1 | package dceu 2 | 3 | fun type (dcl: Expr.Dcl, src: Expr): Scope { 4 | //println(listOf(dcl.idtag.first.str, dcl.tk.pos.lin, src.tk.pos.lin)) 5 | val blk = dcl.to_blk() 6 | val up = src.up_first { it is Expr.Proto || it.n==blk.n } 7 | return when { 8 | (blk.n == G.outer!!.n) -> Scope.GLOBAL 9 | (blk.n == up?.n) -> Scope.LOCAL 10 | else -> { 11 | up as Expr.Proto 12 | val nst = (blk.up_first { it is Expr.Proto } != null) && 13 | up.up_all_until { it.n == blk.n } 14 | .filter { it is Expr.Proto } 15 | .let { it as List } 16 | .all { it.nst } 17 | // 1. requires proto enclosing dcl blk 18 | // 2. requires that all protos in betwenn blk<-src are nested 19 | when { 20 | (nst && (up.tk.str=="func'" || (blk.up_first { it is Expr.Proto } == null))) -> Scope.LOCAL 21 | //(up.tk.str == "func'") -> Scope.LOCAL 22 | !nst -> Scope.UPVAL 23 | else -> Scope.NESTED 24 | } 25 | } 26 | } 27 | } 28 | 29 | fun Expr.base (): Expr.Acc? { 30 | return when (this) { 31 | is Expr.Acc -> this 32 | is Expr.Index -> this.col.base() 33 | is Expr.Pub -> TODO() //this.tsk?.base(ups) ?: ups.first(this) { it is Expr.Proto }!! 34 | else -> null 35 | } 36 | } 37 | 38 | fun Expr.Call.main (): Expr.Proto { 39 | assert(this.tk.str == "main") 40 | return this.clo as Expr.Proto 41 | } 42 | 43 | fun Expr.Proto.id (outer: Expr.Do): String { 44 | return this.fupx().let { 45 | when { 46 | (it !is Expr.Dcl) -> this.n.toString() 47 | (it.src!!.n != this.n) -> error("bug found") 48 | else -> it.idtag.first.str.idc() + (this.up_first() { it is Expr.Do } !== outer).cond { "_${this.n}" } 49 | } 50 | } 51 | } 52 | 53 | fun Expr.is_dst (): Boolean { 54 | return this.fup().let { it is Expr.Set && it.dst==this } 55 | } 56 | 57 | fun Expr.is_drop (): Boolean { 58 | return LEX && this.fupx().let { it is Expr.Drop && it.e==this } 59 | } 60 | 61 | fun Expr.is_constructor (): Boolean { 62 | return when { 63 | this.is_static() -> true 64 | else -> when (this) { 65 | is Expr.Tuple, is Expr.Vector, is Expr.Dict -> true 66 | else -> false 67 | } 68 | } 69 | } 70 | 71 | fun Expr.is_static (): Boolean { 72 | return when (this) { 73 | is Expr.Nil, is Expr.Tag, is Expr.Bool, is Expr.Char, is Expr.Num -> true 74 | else -> false 75 | } 76 | } 77 | 78 | fun Expr.is_lval (): Boolean { 79 | return when (this) { 80 | is Expr.Acc -> true 81 | is Expr.Index -> true 82 | is Expr.Pub -> true 83 | else -> false 84 | } 85 | } 86 | 87 | fun Expr.is_mem (out: Boolean=false): Boolean { 88 | val proto = this.up_first { it is Expr.Proto }.let { 89 | when { 90 | (it === null) -> null 91 | (it.tk.str == "func'") -> null 92 | else -> it 93 | } 94 | } 95 | val up = this.up_first() { it is Expr.Do || it is Expr.Proto }!! 96 | return when { 97 | (!out && proto==null) -> false 98 | true -> true 99 | G.mems.contains(up) -> true 100 | else -> false 101 | } 102 | } 103 | 104 | fun Expr.Do.to_dcls (): List { 105 | return this.dn_collect { 106 | when (it) { 107 | this -> emptyList() 108 | is Expr.Proto -> null 109 | is Expr.Do -> null 110 | is Expr.Dcl -> listOf(it) 111 | else -> emptyList() 112 | } 113 | } 114 | } 115 | 116 | fun Expr.Dcl.to_blk (): Expr { 117 | return this.up_first { it is Expr.Do || it is Expr.Proto }!! // ?: outer /*TODO: remove outer*/ 118 | } 119 | 120 | fun Expr.data (): Pair? { 121 | return when (this) { 122 | is Expr.Acc -> { 123 | val dcl = this.id_to_dcl(this.tk.str)!! 124 | dcl.idtag.second.let { 125 | if (it === null) { 126 | null 127 | } else { 128 | Pair(null, G.datas[it.str]) 129 | } 130 | } 131 | } 132 | is Expr.Pub -> { 133 | if (this.tsk !== null) { 134 | this.tsk.data() 135 | } else { 136 | val task = this.up_first_task_outer() 137 | if (task?.tag === null) null else { 138 | Pair(null, G.datas[task.tag.str]!!) 139 | } 140 | } 141 | } 142 | is Expr.Index -> { 143 | val d = this.col.data() 144 | val l = d?.second 145 | when { 146 | (d == null) -> null 147 | (l == null) -> null 148 | (this.idx !is Expr.Tag) -> null 149 | else -> { 150 | val idx = l.indexOfFirst { it.first.str == this.idx.tk.str.drop(1) } 151 | val v = if (idx == -1) null else l[idx] 152 | when { 153 | (v === null) -> { 154 | err(this.idx.tk, "index error : undeclared data field ${this.idx.tk.str}") 155 | } 156 | (v.second === null) -> Pair(idx, null) 157 | else -> { 158 | Pair(idx, G.datas[v.second!!.str]!!) 159 | } 160 | } 161 | } 162 | } 163 | } 164 | else -> null 165 | } 166 | } 167 | 168 | fun Expr.id_to_dcl (id: String, cross: Boolean=true, but: ((Expr.Dcl)->Boolean)?=null): Expr.Dcl? { 169 | val up = this.fupx().up_first { it is Expr.Do || it is Expr.Proto } 170 | val dcl: Expr.Dcl? = when { 171 | (up is Expr.Proto) -> up.pars.firstOrNull { (but==null||!but(it)) && it.idtag.first.str==id } 172 | (up is Expr.Do) -> up.to_dcls().firstOrNull { (but==null||!but(it)) && it.idtag.first.str==id } 173 | else -> null 174 | } 175 | return when { 176 | (dcl != null) -> dcl 177 | (up!!.fup() == null) -> null 178 | (up is Expr.Proto && !cross) -> null 179 | else -> up!!.id_to_dcl(id, cross, but) 180 | } 181 | } 182 | 183 | fun Expr.Acc.idx (): String { 184 | val dcl = this.id_to_dcl(this.tk.str)!! 185 | return dcl.idx(this) 186 | } 187 | fun Expr.Dcl.idx (src: Expr): String { 188 | val id = this.idtag.first.str.idc() 189 | val blk = this.to_blk() 190 | val ismem = blk.is_mem() 191 | //println(listOf(src.tk.pos.lin, id)) 192 | 193 | return when (type(this,src)) { 194 | Scope.GLOBAL -> "ceu_glb_$id" 195 | Scope.LOCAL -> if (ismem) "(ceu_mem->${id}_${this.n})" else "ceu_loc_${id}_${this.n}" // idx b/c of "it" 196 | Scope.NESTED -> { 197 | val xups = src.up_all_until { it == blk } // all ups between src -> dcl 198 | //println(blk.tk) 199 | //println(blk.up_first { it is Expr.Proto }) 200 | val pid = (blk.up_first { it is Expr.Proto } as Expr.Proto).id(G.outer!!) 201 | val xn = xups.count { it is Expr.Proto && it.n!=blk.n } 202 | "((CEU_Pro_$pid*)ceux->exe_task->${"clo->up_nst->".repeat(xn)}mem)->${id}_${this.n}" 203 | } 204 | else -> { 205 | val proto = src.up_first { it is Expr.Proto } as Expr.Proto 206 | val i = G.nonlocs[proto.n]!!.indexOfFirst { it == this.n } 207 | assert(i != -1) 208 | "ceux->clo->upvs.buf[$i]" 209 | } 210 | } 211 | } 212 | fun Expr.idx (idc: String): String { 213 | return if (this.is_mem()) "(ceu_mem->$idc)" else "ceu_$idc" 214 | } 215 | 216 | fun Expr.dcl (tp: String="CEU_Value"): String { 217 | return if (this.is_mem()) "" else tp 218 | } 219 | 220 | fun Expr.depth (): Int { 221 | // closest outer prototype 222 | // TODO: :fake should be ignored? 223 | return this.up_all_until { it is Expr.Proto }.count { it is Expr.Do } 224 | } 225 | 226 | fun Expr.Acc.depth_diff (): Int { 227 | /* 228 | do { 229 | val x <---* 230 | do { | 231 | ... | 232 | x <---* 233 | } 234 | } 235 | */ 236 | val blk = this.id_to_dcl(this.tk.str)!!.to_blk() 237 | return this.up_all_until { it.n == blk.n }.filter { it is Expr.Do }.count() - 1 238 | } 239 | 240 | 241 | 242 | -------------------------------------------------------------------------------- /src/main/kotlin/Mem.kt: -------------------------------------------------------------------------------- 1 | package dceu 2 | 3 | import kotlin.math.max 4 | 5 | val union = "union" 6 | //val union = "struct" 7 | 8 | class Mem (val defers: MutableMap,String,String>>) { 9 | fun pub (e: Expr.Proto): String { 10 | return """ 11 | struct { // PROTO | ${e.dump()} 12 | ${e.pars.map { """ 13 | CEU_Value ${it.idtag.first.str.idc()}_${it.n}; 14 | """ }.joinToString("") } 15 | ${e.blk.mem()} 16 | }; 17 | """ 18 | } 19 | 20 | fun Expr.coexists (): Boolean { 21 | return when (this) { 22 | is Expr.Group -> this.es.any { it.coexists() } 23 | is Expr.Enclose -> this.es.any { it.coexists() } 24 | is Expr.Escape -> (this.e?.coexists() ?: false) 25 | is Expr.Dcl -> true 26 | is Expr.Set -> this.dst.coexists() || this.src.coexists() 27 | is Expr.If -> this.cnd.coexists() 28 | is Expr.Loop -> this.blk.coexists() 29 | is Expr.Drop -> this.e.coexists() 30 | 31 | is Expr.Yield -> this.e.coexists() 32 | is Expr.Resume -> this.co.coexists() || this.args.any { it.coexists() } 33 | 34 | is Expr.Spawn -> (this.tsks?.coexists() ?: false) || this.tsk.coexists() || this.args.any { it.coexists() } 35 | is Expr.Pub -> this.tsk?.coexists() ?: false 36 | is Expr.Toggle -> this.tsk.coexists() || this.on.coexists() 37 | 38 | is Expr.Tasks -> this.max.coexists() 39 | 40 | is Expr.Tuple -> this.args.any { it.coexists() } 41 | is Expr.Vector -> this.args.any { it.coexists() } 42 | is Expr.Dict -> this.args.any { it.first.coexists() || it.second.coexists() } 43 | is Expr.Index -> this.col.coexists() || this.idx.coexists() 44 | is Expr.Call -> this.clo.coexists() || this.args.any { it.coexists() } 45 | 46 | is Expr.Proto, is Expr.Do -> false 47 | is Expr.Data, is Expr.Catch, is Expr.Defer, is Expr.Delay -> false 48 | is Expr.Nat, is Expr.Acc, is Expr.Nil, is Expr.Tag, is Expr.Bool, is Expr.Char, is Expr.Num -> false 49 | } 50 | } 51 | 52 | fun Expr.union_or_struct (): String { 53 | return if (this.coexists()) "struct" else union 54 | } 55 | 56 | fun List.seq (i: Int): String { 57 | return (i != this.size).cond { 58 | """ 59 | ${this[i].union_or_struct()} { // SEQ | ${this[i].dump()} 60 | ${this[i].mem()} 61 | ${this.seq(i+1)} 62 | }; 63 | """ 64 | } 65 | } 66 | 67 | fun Expr.mem (): String { 68 | return when (this) { 69 | is Expr.Proto -> "" 70 | is Expr.Do -> this.is_mem().cond { 71 | """ 72 | struct { // BLOCK | ${this.dump()} 73 | ${(CEU >= 4).cond { """ 74 | CEU_Block block_$n; 75 | """ }} 76 | ${this.to_dcls().map { """ 77 | CEU_Value ${it.idtag.first.str.idc() + "_" + it.n }; 78 | """ }.joinToString("") } 79 | ${defers[this].cond { it.first.map { """ 80 | int defer_${it}; 81 | """ }.joinToString("")} } 82 | ${es.seq(0)} 83 | }; 84 | """ 85 | } 86 | is Expr.Group -> """ 87 | $union { 88 | ${this.es.map { it.mem() }.joinToString("")} 89 | }; 90 | """ 91 | is Expr.Enclose -> """ 92 | $union { 93 | ${this.es.map { it.mem() }.joinToString("")} 94 | }; 95 | """ 96 | is Expr.Escape -> this.e.cond { it.mem() } 97 | is Expr.Dcl -> this.src.cond { it.mem() } 98 | is Expr.Set -> """ 99 | struct { // SET 100 | CEU_Value src_$n; 101 | $union { 102 | ${this.dst.mem()} 103 | ${this.src.mem()} 104 | }; 105 | }; 106 | """ 107 | is Expr.If -> """ 108 | $union { // IF 109 | ${this.cnd.mem()} 110 | ${this.t.mem()} 111 | ${this.f.mem()} 112 | }; 113 | """ 114 | is Expr.Loop -> this.blk.mem() 115 | is Expr.Drop -> this.e.mem() 116 | 117 | is Expr.Catch -> this.blk.mem() 118 | is Expr.Defer -> this.blk.mem() 119 | 120 | is Expr.Yield -> this.e.mem() 121 | is Expr.Resume -> """ 122 | struct { 123 | CEU_Value coro_$n; // b/c of CEU=50 to chk_set depth 124 | CEU_Value args_$n[${this.args.size}]; 125 | $union { 126 | ${this.co.mem()} 127 | ${this.args.map { it.mem() }.joinToString("")} 128 | }; 129 | }; 130 | """ 131 | 132 | is Expr.Spawn -> """ 133 | struct { // SPAWN | ${this.dump()} 134 | ${this.tsks.cond { "CEU_Value tsks_${this.n};" }} 135 | CEU_Value args_$n[${this.args.size}]; 136 | $union { 137 | ${this.tsks.cond { it.mem() }} 138 | ${this.tsk.mem()} 139 | ${this.args.map { it.mem() }.joinToString("")} 140 | }; 141 | }; 142 | """ 143 | is Expr.Pub -> """ 144 | struct { // PUB 145 | ${this.is_dst().cond { """ 146 | CEU_Value val_$n; 147 | """ }} 148 | ${this.tsk?.mem() ?: ""} 149 | }; 150 | """.trimIndent() 151 | is Expr.Toggle -> """ 152 | struct { // TOGGLE 153 | CEU_Value tsk_${this.n}; 154 | $union { 155 | ${this.tsk.mem()} 156 | ${this.on.mem()} 157 | }; 158 | }; 159 | """ 160 | is Expr.Tasks -> this.max.mem() 161 | 162 | is Expr.Tuple -> """ 163 | struct { // TUPLE 164 | CEU_Value args_$n[${max(1,this.args.size)}]; 165 | $union { 166 | ${this.args.map { it.mem() }.joinToString("")} 167 | }; 168 | }; 169 | """ 170 | is Expr.Vector -> """ 171 | struct { // VECTOR 172 | CEU_Value vec_$n; 173 | $union { 174 | ${this.args.map { it.mem() }.joinToString("")} 175 | }; 176 | }; 177 | """ 178 | is Expr.Dict -> """ 179 | struct { // DICT 180 | CEU_Value dic_$n; 181 | CEU_Value key_$n; 182 | $union { 183 | ${this.args.map { 184 | listOf(it.first.mem(),it.second.mem()) 185 | }.flatten().joinToString("")} 186 | }; 187 | }; 188 | """ 189 | is Expr.Index -> """ 190 | struct { // INDEX 191 | CEU_Value col_$n; 192 | ${this.is_dst().cond { """ 193 | CEU_Value val_$n; 194 | """ }} 195 | $union { 196 | ${this.col.mem()} 197 | ${this.idx.mem()} 198 | }; 199 | }; 200 | """ 201 | is Expr.Call -> """ 202 | struct { // CALL 203 | CEU_Value args_$n[${this.args.size}]; 204 | $union { 205 | ${this.clo.mem()} 206 | ${this.args.map { it.mem() }.joinToString("")} 207 | }; 208 | }; 209 | """ 210 | 211 | is Expr.Nat, is Expr.Acc, is Expr.Nil, is Expr.Tag, is Expr.Bool, is Expr.Char, is Expr.Num -> "" 212 | is Expr.Data, is Expr.Delay -> "" 213 | } 214 | } 215 | } 216 | 217 | -------------------------------------------------------------------------------- /hold.txt: -------------------------------------------------------------------------------- 1 | - ceu_chk_set_col (col, v) 2 | 3 | 1. col[_] = F 4 | - ceu_hold_set_rec(v, col.typ, 0, col.blk); 5 | - set v.typ = col.typ 6 | - set v.blk = col.blk 7 | 2. col[F] = X 8 | - ceu_hold_set_rec(col, v.typ, 0, NULL); 9 | - set col.typ = X 10 | - set col.blk = keep / guaranteed to be deeper than blk(v) 11 | E1. col[_] = TRACK/TASK 12 | - "store error : cannot hold reference to track or task in pool" 13 | E2. col[X] = X 14 | - asr X=X 15 | - TODO: no error message 16 | - asr up_dn(v,col) 17 | - "store error : cannot assign reference to outer scope" 18 | - chk up_dn(col,v) 19 | - "store error : cannot hold alien reference" 20 | - alien scope 21 | - col[X]=evt 22 | - col[X]=pub(alien) 23 | 24 | - ceu_drop (v) 25 | 26 | 1. v 27 | - ceu_hold_set_rec(v, FLEET, 0, NULL); 28 | - v.typ = F 29 | - v.blk = keep 30 | E1. v.typ == IMMUT 31 | - "drop error : value is not movable" 32 | E2. v.refs > 1 33 | - "drop error : value contains multiple references" 34 | 35 | - ceu_broadcast (evt) 36 | 37 | 1. evt = F 38 | - ceu_hold_set_rec(evt, MUTAB, 0, NULL); 39 | - v.typ = M 40 | - v.blk = keep 41 | 42 | - ceu_create_exe_task_in (CEU_Value clo, CEU_Tasks* ts) 43 | 44 | X. ts[F] <- _ 45 | 1. ts[_] <- F 46 | - ceu_hold_set_rec(clo, MUTAB, 0, ts.blk); 47 | - clo.typ = M 48 | - clo.blk = ts.blk 49 | E1. ts[X] <- X 50 | - asr up_dn(clo,ts) 51 | - "spawn error : task pool outlives task prototype" 52 | 53 | - pass arg: f(v) -> func (v) 54 | 55 | 1. f(F) 56 | - ceu_hold_set_rec(v, MUTAB, 0, f.blk); 57 | - set v.typ = M 58 | - set v.blk = f.blk 59 | 2. f(X) 60 | - keep all 61 | E1. f([F][F]) 62 | - v.refs > 1 63 | - "argument error : cannot receive pending reference" 64 | 65 | - move up: { <- v } 66 | 67 | 1. { <- _ } 68 | - up_dn(up,v) 69 | - otherwise { <- evt }, do nothing 70 | - ceu_hold_set_rec(v, NONE, 1, up.blk); 71 | - set v.blk = up.blk [stop recursion] 72 | E1. { <- I } 73 | - !up_dn(v,up) 74 | - "block escape error : reference has immutable scope" 75 | E2. { <- trk } 76 | - !up_dn(v.tsk,up) 77 | - "block escape error : cannot expose track outside its task scope" 78 | E3. { <- tsk-in } 79 | - "block escape error : cannot expose reference to task in pool" 80 | 81 | - val x = v 82 | 1. x = F 83 | - ceu_hold_set_rec(v, MUTAB, 0, x.blk) 84 | - set v.typ = MUTAB 85 | - set v.blk = x.blk 86 | 2. x = X 87 | - keep as is 88 | E. x = evt 89 | - x=pub(alien) 90 | - !up_dn(evt,x) 91 | - "declaration error : cannot hold alien reference" 92 | 93 | - set pub(t) = v 94 | - need to change to IMMUT to prevent it escaping 95 | 1. pub(_) = F 96 | - ceu_hold_set_rec(v, IMMUT, 0, pub.tsk.blk) 97 | - set v.typ = IMMUT 98 | 2. pub(_) = X 99 | - ceu_hold_set_rec(v, IMMUT, 0, NULL) 100 | - set v.typ = IMMUT 101 | - set v.blk = keep 102 | 103 | 104 | // PUB - SET | ${this.dump()} 105 | // set pub = [] ;; FLEET ;; change to MUTAB type ;; change to pub blk 106 | // set pub = src ;; ELSE ;; keep ELSE type ;; keep block 107 | // NEW: in both cases, change to IMMUT 108 | // - Check for type=ELSE: 109 | // - blk(pub) >= blk(src) (deeper) 110 | // Also error: 111 | // set pub = evt 112 | #if CEU >= 5 113 | if ($src.type == CEU_VALUE_EXE_TASK_IN) { 114 | CEU_Value err = { CEU_VALUE_ERROR, {.Error="set error : cannot expose reference to task in pool"} }; 115 | CEU_ERROR_THR($bupc, "${this.tk.pos.file} : (lin ${this.tk.pos.lin}, col ${this.tk.pos.col})", err); 116 | } else 117 | #endif 118 | if ($src.Dyn->Any.hld.type == FLEET) { 119 | } else { 120 | if (!ceu_block_is_up_dn(CEU_HLD_BLOCK($src.Dyn), ceu_acc.Dyn->Exe_Task.dn_block)) { 121 | CEU_Value err = { CEU_VALUE_ERROR, {.Error="set error : cannot assign reference to outer scope"} }; 122 | CEU_ERROR_THR($bupc, "${this.tk.pos.file} : (lin ${this.tk.pos.lin}, col ${this.tk.pos.col})", err); 123 | } 124 | ceu_hold_set_rec($src, IMMUT, 0, NULL); 125 | } 126 | 127 | ceu_gc_inc_val($src); 128 | } 129 | ceu_gc_dec_val(ceu_acc.Dyn->Exe_Task.pub, 1); 130 | ceu_acc.Dyn->Exe_Task.pub = $src; 131 | 132 | 133 | 134 | 135 | // ACC - SET | ${this.dump()} 136 | if ($src.type > CEU_VALUE_DYNAMIC) { 137 | // set dst = [] ;; FLEET ;; change to MUTAB type ;; change to dst blk 138 | // set dst = src ;; ELSE ;; keep ELSE type ;; keep block 139 | // - Check for type=ELSE: 140 | // - blk(dst) >= blk(src) (deeper) 141 | // Also error: 142 | // set dst = evt 143 | // Also error: 144 | // set dst = trk(x) where block(dst) < block(x) 145 | // Also error: 146 | // set dst = detrack(x), where block(dst) < current block 147 | char* ceu_err_$n = NULL; 148 | #if CEU >= 5 149 | if ( 150 | $src.type == CEU_VALUE_EXE_TASK_IN && 151 | !ceu_block_is_up_dn($bupc, ${vblk.idc("block",nst)}) 152 | ) { 153 | ceu_err_$n = "set error : cannot expose reference to task in pool"; 154 | } else 155 | #endif 156 | if ($src.Dyn->Any.hld.type == FLEET) { 157 | #if CEU >= 5 158 | if ( 159 | $src.Dyn->Any.type == CEU_VALUE_TRACK && 160 | $src.Dyn->Track.task != NULL && 161 | !ceu_block_is_up_dn(CEU_HLD_BLOCK((CEU_Dyn*)$src.Dyn->Track.task), ${vblk.idc("block",nst)}) 162 | ) { 163 | ceu_err_$n = "set error : cannot expose track outside its task scope"; 164 | } 165 | else 166 | #endif 167 | { 168 | ceu_hold_set_rec($src, MUTAB, 0, ${vblk.idc("block",nst)}); 169 | } 170 | } else { 171 | if (!ceu_block_is_up_dn(CEU_HLD_BLOCK($src.Dyn), ${vblk.idc("block",nst)})) { 172 | ${ups.inexe(this,"task",true).cond { """ 173 | if (!ceu_block_is_up_dn(${vblk.idc("block",nst)}, CEU_HLD_BLOCK($src.Dyn))) { 174 | CEU_Value err = { CEU_VALUE_ERROR, {.Error="declaration error : cannot hold \"evt\" reference"} }; 175 | CEU_ERROR_THR($bupc, "${this.tk.pos.file} : (lin ${this.tk.pos.lin}, col ${this.tk.pos.col})", err); 176 | } else 177 | """ }} 178 | { 179 | CEU_Value err = { CEU_VALUE_ERROR, {.Error="set error : cannot assign reference to outer scope"} }; 180 | CEU_ERROR_THR($bupc, "${this.tk.pos.file} : (lin ${this.tk.pos.lin}, col ${this.tk.pos.col})", err); 181 | } 182 | } 183 | } 184 | 185 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /src/test/kotlin/tst_99/Book_99.kt: -------------------------------------------------------------------------------- 1 | package tst_99 2 | 3 | import dceu.* 4 | import org.junit.Test 5 | 6 | class Book_99 { 7 | 8 | // CHAPTER 1: Fundamental concepts 9 | 10 | // CHAPTER 1.1: Session and scripts 11 | 12 | @Test 13 | fun pg_2_square() { 14 | val out = test(""" 15 | val square = func (x) { 16 | x * x 17 | } 18 | println(square(5)) 19 | """, true) 20 | assert(out == "25\n") { out } 21 | } 22 | @Test 23 | fun pg_2_smaller() { 24 | val out = test(""" 25 | val smaller = func (x,y) { 26 | if x < y { x } else { y } 27 | } 28 | println(smaller(1,2)) 29 | println(smaller(1,1)) 30 | println(smaller(2,1)) 31 | """, true) 32 | assert(out == "1\n1\n1\n") { out } 33 | } 34 | @Test 35 | fun pg_3_square_smaller() { 36 | val out = test(""" 37 | func square (x) { 38 | x * x 39 | } 40 | func smaller (x,y) { 41 | ((x < y) and x) or y 42 | } 43 | println(square(smaller(3,5))) 44 | """, true) 45 | assert(out == "9\n") { out } 46 | } 47 | @Test 48 | fun pg_3_square_smaller_a() { 49 | val out = test(""" 50 | func square (x) { 51 | x * x 52 | } 53 | func smaller (x,y) { 54 | if (x < y) => x => y 55 | } 56 | println(square(smaller(3,5))) 57 | """, true) 58 | assert(out == "9\n") { out } 59 | } 60 | @Test 61 | fun pg_3_delta() { 62 | val out = test(""" 63 | func delta (a,b,c) { 64 | ((b**2) - (4*(a*c))) // 2 65 | } 66 | println(delta(4.2,7,2.3)) 67 | """, true) 68 | assert(out == "3.2187\n") { out } 69 | } 70 | 71 | // CHAPTER 1.2: Evaluation 72 | // ok 73 | 74 | // CHAPTER 1.3: Values 75 | // ok 76 | 77 | // CHAPTER 1.4: Functions 78 | 79 | @Test 80 | fun pg_11_currying() { 81 | val out = test(""" 82 | func smallerc (x) { 83 | func (y) { 84 | if x < y { x } else { y } 85 | } 86 | } 87 | func plusc (x) { 88 | func (y) { 89 | x+y 90 | } 91 | } 92 | println(smallerc(3)(5)) 93 | println(plusc(3)(5)) 94 | """, true) 95 | assert(out == "3\n8\n") { out } 96 | } 97 | @Test 98 | fun pg_12_twice_quad() { 99 | val out = test(""" 100 | func square (x) { 101 | x**2 102 | } 103 | func twice (f,x) { 104 | f(f(x)) 105 | } 106 | func quad (x) { 107 | twice(square,x) 108 | } 109 | println(twice(square, 2), quad(2)) 110 | """, true) 111 | assert(out == "16\t16\n") { out } 112 | } 113 | @Test 114 | fun pg_12_twicec() { 115 | val out = test(""" 116 | func square (x) { 117 | x**2 118 | } 119 | func twicec (f) { 120 | func (v) { 121 | f(f(v)) 122 | } 123 | } 124 | val quad = twicec(square) 125 | println(quad(2))""", true 126 | ) 127 | assert(out == "16\n") { out } 128 | } 129 | @Test 130 | fun pg_13_curry() { 131 | val out = test(""" 132 | func curry (f) { 133 | func (x) { 134 | func (y) { 135 | f(x,y) 136 | } 137 | } 138 | } 139 | val plusc = curry({{+}}) 140 | val b = plusc(1) 141 | val c = b(2) 142 | println((plusc(1))(-4)) 143 | """, true) 144 | assert(out == "-3\n") { out } 145 | } 146 | @Test 147 | fun pg_13_uncurry() { 148 | val out = test(""" 149 | func plusc (x) { 150 | func (y) { 151 | x + y 152 | } 153 | } 154 | func uncurry (f) { 155 | func (x,y) { 156 | f(x)(y) 157 | } 158 | } 159 | println(uncurry(plusc)(1,-4)) 160 | """, true) 161 | assert(out == "-3\n") { out } 162 | } 163 | @Test 164 | fun pg_13_ops() { 165 | val out = test(""" 166 | println({{*}}(1 + 3, 4)) 167 | """, true) 168 | assert(out == "16\n") { out } 169 | } 170 | @Test 171 | fun pg_15_compose() { 172 | val out = test(""" 173 | func compose (f,g) { 174 | func (v) { 175 | f(g(v)) 176 | } 177 | } 178 | func square (x) { 179 | x**2 180 | } 181 | val quad = compose(square,square) 182 | println(quad(2)) 183 | """, true) 184 | assert(out == "16\n") { out } 185 | } 186 | 187 | // CHAPTER 1.5: Definitions 188 | 189 | @Test 190 | fun todo_ifs_pg_18_signum() { 191 | val out = test(""" 192 | func signum (x) { 193 | ifs { 194 | x < 0 => -1 195 | x > 0 => 1 196 | else => 0 197 | } 198 | } 199 | println(signum(10), signum(-9), signum(0)) 200 | """, true) 201 | assert(out == "1\t-1\t0\n") { out } 202 | } 203 | @Test 204 | fun pg_19_fact() { 205 | val out = test(""" 206 | func fact (x) { 207 | if x == 0 { 208 | 1 209 | } else { 210 | x * fact(x - 1) 211 | } 212 | } 213 | println(fact(5)) 214 | """, true) 215 | assert(out == "120\n") { out } 216 | } 217 | 218 | // CHAPTER 1.6: Types 219 | 220 | @Test // 1.6.2: Type classes 221 | fun TODO_poly_mult() { 222 | val out = test(""" 223 | poly val mult 224 | println(10 {mult} 20) 225 | println([1,2] {mult} 2) 226 | println(2 {mult} [1,2]) 227 | func fact (x) { 228 | ifs { 229 | x < 0 => throw(:error) 230 | x == 0 => 1 231 | else => x * fact(x - 1) 232 | } 233 | } 234 | println(fact(-1)) 235 | """, true) 236 | assert(out == "anon : (lin 9, col 21) : fact({{-}}(1))\n" + 237 | "anon : (lin 4, col 31) : throw(:error)\n" + 238 | "throw error : uncaught exception\n" + 239 | ":error\n") { out } 240 | } 241 | 242 | // CHAPTER 1.7: Specifications 243 | // ok 244 | 245 | // CHAPTER 2: Simple datatypes 246 | 247 | // CHAPTER 2.1: Booleans 248 | 249 | @Test 250 | fun TODO_ifs_pg_30_bool() { 251 | val out = test(""" 252 | func fact (x) { 253 | ifs { 254 | x < 0 => error(:error) 255 | x == 0 => 1 256 | else => x * fact(x - 1) 257 | } 258 | } 259 | println(fact(-1)) 260 | """, true) 261 | assert(out == " | anon : (lin 9, col 21) : fact({{-}}(1))\n" + 262 | " | anon : (lin 4, col 31) : error(:error)\n" + 263 | " v error : :error\n") { out } 264 | } 265 | @Test 266 | fun pg_31_short() { 267 | val out = test(""" 268 | println((false and error(:error)) or true) 269 | println(true or error(:error)) 270 | """, true) 271 | assert(out == "true\ntrue\n") { out } 272 | } 273 | @Test 274 | fun TODO_pg_32_eq_poly() { 275 | // class Eq: === =/= (polymorphic, unlike ~= ~/=) 276 | } 277 | @Test 278 | fun TODO_pg_32_ord_poly() { 279 | // class Ord: === =/= (polymorphic, unlike ~= ~/=) 280 | } 281 | @Test 282 | fun pg_33_leap1() { 283 | val out = test(""" 284 | func leapyear? (y) { 285 | if (y % 100) == 0 { 286 | (y % 400) == 0 287 | } else { 288 | (y % 4) == 0 289 | } 290 | } 291 | println(leapyear?(1980)) 292 | println(leapyear?(1979)) 293 | """, true) 294 | assert(out == "true\nfalse\n") { out } 295 | } 296 | @Test 297 | fun pg_33_leap2() { 298 | val out = test(""" 299 | func leapyear? (y) { 300 | (((y % 100) == 0) and ((y % 400) == 0)) or ((y % 4) == 0) 301 | } 302 | println(leapyear?(1980)) 303 | println(leapyear?(1979)) 304 | """, true) 305 | assert(out == "true\nfalse\n") { out } 306 | } 307 | @Test 308 | fun pg_34_tri() { // :Triangle.Equilateral 309 | val out = test(""" 310 | data :Tri = [] { 311 | :Equ = [] 312 | :Iso = [] 313 | :Sca = [] 314 | :Err = [] 315 | } 316 | func analyse (x,y,z) { 317 | ifs { 318 | (x+y) <= z => :Tri.Err 319 | x == z => :Tri.Equ 320 | x == y => :Tri.Iso 321 | y == z => :Tri.Iso 322 | else => :Tri.Sca 323 | } 324 | } 325 | println(analyse(10,20,30)) 326 | println(analyse(10,20,25)) 327 | println(analyse(10,20,20)) 328 | println(analyse(10,10,10)) 329 | """, true) 330 | assert(out == ":Tri.Err\n:Tri.Sca\n:Tri.Iso\n:Tri.Equ\n") { out } 331 | } 332 | 333 | // CHAPTER 2.2: Characters 334 | // TODO 335 | 336 | } 337 | -------------------------------------------------------------------------------- /src/test/kotlin/tst_04/Parser_04.kt: -------------------------------------------------------------------------------- 1 | package tst_04 2 | 3 | import dceu.* 4 | import org.junit.Test 5 | 6 | class Parser_04 { 7 | // TASK 8 | 9 | @Test 10 | fun aa_01_task() { 11 | val l = lexer("task' (a,b) { 10 }") 12 | val parser = Parser(l) 13 | val e = parser.expr_prim() 14 | assert(e is Expr.Proto && e.pars.size==2) 15 | assert(e.to_str() == "(task' (a,b) {\n10;\n})") { e.to_str() } 16 | } 17 | @Test 18 | fun aa_02_task() { 19 | val l = lexer(""" 20 | set t = task' (v) { 21 | set v = yield((1)) ;;thus { it => nil } 22 | yield((2)) ;;thus { it => nil } 23 | } 24 | coroutine(t) 25 | set v = resume a(1) 26 | resume a(2) 27 | """) 28 | val parser = Parser(l) 29 | val e = parser.exprs() 30 | assert(e.to_str() == """ 31 | (set t = (task' (v) { 32 | (set v = yield(1)); 33 | yield(2); 34 | })); 35 | coroutine(t); 36 | (set v = (resume (a)(1))); 37 | (resume (a)(2)); 38 | 39 | """.trimIndent()) { e.to_str() } 40 | } 41 | 42 | // SPAWN 43 | 44 | @Test 45 | fun bb_01_spawn_err() { 46 | val l = lexer(""" 47 | spawn 48 | """) 49 | val parser = Parser(l) 50 | assert(trap { parser.exprs() } == "anon : (lin 3, col 9) : expected expression : have end of file") 51 | } 52 | @Test 53 | fun bb_02_spawn_err() { 54 | val l = lexer(""" 55 | spawn nil 56 | """) 57 | val parser = Parser(l) 58 | assert(trap { parser.exprs() } == "anon : (lin 3, col 9) : spawn error : expected call") 59 | } 60 | @Test 61 | fun bb_03_spawn() { 62 | val l = lexer(""" 63 | spawn T(2) 64 | """) 65 | val parser = Parser(l) 66 | val e = parser.exprs() 67 | assert(e.to_str() == "(spawn T(2));\n") { e.to_str() } 68 | } 69 | @Test 70 | fun bb_04_spawn_err() { 71 | val l = lexer(""" 72 | spawn task' () { nil } () 73 | """) 74 | val parser = Parser(l) 75 | val e = parser.exprs() 76 | assert(e.to_str() == "(spawn (task' () {\n" + 77 | "nil;\n" + 78 | "})());\n") { e.to_str() } 79 | //assert(trap { parser.exprs() } == "anon : (lin 2, col 19) : spawn error : unexpected \"task\"") 80 | } 81 | @Test 82 | fun bb_05_spawn() { 83 | val l = lexer(""" 84 | spawn (task' () { nil }) () 85 | """) 86 | val parser = Parser(l) 87 | val e = parser.exprs() 88 | assert(e.to_str() == "(spawn (task' () {\n" + 89 | "nil;\n" + 90 | "})());\n") { e.to_str() } 91 | } 92 | @Test 93 | fun todo_bb_06_spawn_dots_err() { // TODO: ... should be allowed 94 | val l = lexer(""" 95 | spawn T(...) 96 | """) 97 | val parser = Parser(l) 98 | assert(trap { parser.exprs() } == "anon : (lin 2, col 19) : spawn error : \"...\" is not allowed") 99 | } 100 | 101 | // DELAY 102 | 103 | @Test 104 | fun bj_01_delay() { 105 | val l = lexer(""" 106 | delay 107 | """) 108 | val parser = Parser(l) 109 | val e = parser.exprs() 110 | assert(e.to_str() == "delay;\n") { e.to_str() } 111 | } 112 | 113 | // BCAST 114 | 115 | @Test 116 | fun cc_01_bcast_err() { 117 | val l = lexer(""" 118 | broadcast 1 119 | """) 120 | val parser = Parser(l) 121 | //assert(trap { parser.exprs() } == "anon : (lin 3, col 9) : expected expression : have end of file") 122 | //assert(trap { parser.exprs() } == "anon : (lin 3, col 9) : expected \"in\" : have end of file") 123 | assert(trap { parser.exprs() } == "anon : (lin 2, col 23) : expected \"(\" : have \"1\"") 124 | } 125 | @Test 126 | fun cc_02_bcast_err() { 127 | val l = lexer(""" 128 | broadcast 129 | """) 130 | val parser = Parser(l) 131 | //assert(trap { parser.exprs() } == "anon : (lin 3, col 9) : expected expression : have end of file") 132 | //assert(trap { parser.exprs() } == "anon : (lin 3, col 9) : expected \"in\" : have end of file") 133 | assert(trap { parser.exprs() } == "anon : (lin 3, col 9) : expected \"(\" : have end of file") 134 | } 135 | @Test 136 | fun cc_03_bcast_err() { 137 | val l = lexer(""" 138 | broadcast (nil) in 139 | """) 140 | val parser = Parser(l) 141 | assert(trap { parser.exprs() } == "anon : (lin 3, col 9) : expected expression : have end of file") 142 | } 143 | @Test 144 | fun cc_04_bcast_err() { 145 | val l = lexer(""" 146 | broadcast in nil 147 | """) 148 | val parser = Parser(l) 149 | //assert(trap { parser.exprs() } == "anon : (lin 3, col 9) : expected \",\" : have end of file") 150 | assert(trap { parser.exprs() } == "anon : (lin 2, col 23) : expected \"(\" : have \"in\"") 151 | } 152 | @Test 153 | fun cc_0X_bcast_err() { 154 | val l = lexer(""" 155 | broadcast (nil) in nil, 156 | """) 157 | val parser = Parser(l) 158 | assert(trap { parser.exprs() } == "anon : (lin 2, col 35) : expected expression : have \",\"") 159 | } 160 | @Test 161 | fun cc_05_bcast_err() { 162 | val l = lexer(""" 163 | broadcast ([]) in nil 164 | """) 165 | val parser = Parser(l) 166 | val e = parser.exprs() 167 | assert(e.to_str() == "broadcast'(nil,[]);\n") { e.to_str() } 168 | } 169 | @Test 170 | fun cc_06_bcast() { 171 | val l = lexer(""" 172 | broadcast(nil) in t 173 | """) 174 | val parser = Parser(l) 175 | val e = parser.exprs() 176 | assert(e.to_str() == "broadcast'(t,nil);\n") { e.to_str() } 177 | } 178 | 179 | // PUB 180 | 181 | @Test 182 | fun dd_00_pub_a() { 183 | val l = lexer(""" 184 | pub 185 | """) 186 | val parser = Parser(l) 187 | val e = parser.exprs() 188 | assert(e.to_str() == "pub;\n") { e.to_str() } 189 | //assert(trap { parser.exprs() } == "anon : (lin 3, col 9) : expected \"(\" : have end of file") 190 | } 191 | @Test 192 | fun dd_00_pub_b() { 193 | val l = lexer(""" 194 | pub( 195 | """) 196 | val parser = Parser(l) 197 | assert(trap { parser.exprs() } == "anon : (lin 3, col 9) : expected expression : have end of file") 198 | } 199 | @Test 200 | fun dd_00_pub_c() { 201 | val l = lexer(""" 202 | pub(1 203 | """) 204 | val parser = Parser(l) 205 | assert(trap { parser.exprs() } == "anon : (lin 3, col 9) : expected \",\" : have end of file") 206 | } 207 | @Test 208 | fun dd_01_pub() { 209 | val l = lexer(""" 210 | pub 211 | set pub = 10 212 | """) 213 | val parser = Parser(l) 214 | val e = parser.exprs() 215 | assert(e.to_str() == "pub;\n(set pub = 10);\n") { e.to_str() } 216 | } 217 | @Test 218 | fun dd_02_pub() { 219 | val l = lexer(""" 220 | t.pub 221 | set t.pub = 10 222 | """) 223 | val parser = Parser(l) 224 | val e = parser.exprs() 225 | assert(e.to_str() == "t.pub;\n(set t.pub = 10);\n") { e.to_str() } 226 | } 227 | @Test 228 | fun dd_03_pub_tag() { 229 | val l = lexer(""" 230 | task' () :X { 231 | nil 232 | } 233 | """) 234 | val parser = Parser(l) 235 | val e = parser.exprs() 236 | assert(e.to_str() == "(task' () :X {\nnil;\n});\n") { e.to_str() } 237 | } 238 | @Test 239 | fun dd_04_pub() { 240 | val l = lexer(""" 241 | pub() 242 | """) 243 | val parser = Parser(l) 244 | val e = parser.exprs() 245 | assert(e.to_str() == "pub();\n") { e.to_str() } 246 | } 247 | @Test 248 | fun dd_05_pub() { 249 | val l = lexer(""" 250 | set pub() = 10 251 | """) 252 | val parser = Parser(l) 253 | assert(trap { parser.exprs() } == "anon : (lin 2, col 13) : set error : expected assignable destination") 254 | } 255 | @Test 256 | fun dd_06_pub() { 257 | val l = lexer("set pub = x.pub + pub") 258 | val parser = Parser(l) 259 | val e = parser.expr() 260 | assert(e.to_str() == "(set pub = {{+}}(x.pub,pub))") { e.to_str() } 261 | } 262 | 263 | // TOGGLE 264 | 265 | @Test 266 | fun ee_01_toggle() { 267 | val l = lexer("toggle t(true)") 268 | val parser = Parser(l) 269 | val e = parser.exprs() 270 | assert(e.to_str() == "(toggle t(true));\n") { e.to_str() } 271 | } 272 | @Test 273 | fun ee_02_toggle_err() { 274 | val l = lexer("toggle x") 275 | val parser = Parser(l) 276 | //assert(trap { parser.expr() } == "anon : (lin 1, col 8) : invalid toggle : expected argument") 277 | assert(trap { parser.expr() } == "anon : (lin 1, col 9) : expected \"(\" : have end of file") 278 | } 279 | @Test 280 | fun ee_03_toggle_err() { 281 | val l = lexer("toggle") 282 | val parser = Parser(l) 283 | //assert(trap { parser.expr() } == "anon : (lin 1, col 7) : expected expression : have end of file") 284 | assert(trap { parser.expr() } == "anon : (lin 1, col 7) : expected expression : have end of file") 285 | } 286 | @Test 287 | fun ee_04_toggle_err() { 288 | val l = lexer("toggle x(1,2)") 289 | val parser = Parser(l) 290 | //assert(trap { parser.expr() } == "anon : (lin 1, col 8) : invalid toggle : expected single argument") 291 | assert(trap { parser.expr() } == "anon : (lin 1, col 11) : expected \")\" : have \",\"") 292 | } 293 | @Test 294 | fun ee_05_toggle_err() { 295 | val l = lexer("toggle f()") 296 | val parser = Parser(l) 297 | //assert(trap { parser.expr() } == "anon : (lin 1, col 8) : invalid toggle : expected single argument") 298 | assert(trap { parser.expr() } == "anon : (lin 1, col 10) : expected expression : have \")\"") 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /src/main/kotlin/Main.kt: -------------------------------------------------------------------------------- 1 | package dceu 2 | 3 | import java.io.File 4 | import java.io.Reader 5 | import java.util.* 6 | 7 | var LEX = true 8 | var TEST = false 9 | var DUMP = true 10 | var DEBUG = true 11 | var CEU = 1 12 | 13 | // 1: dyn-lex ;; 26 "definitely lost" (errors or cycles) 14 | // 2: defer, throw/catch, do/escape, loop ;; 4 "definitely lost" (C errors) 15 | // 3: coro, yield, resume ;; 0 16 | // 4: task, pub, bcast, toggle, delay ;; 0 17 | // 5: tasks ;; 0 18 | // 50: lex, drop, val', var' ;; 6 "definitely lost" (C errors or cycles) 19 | // 99: sugar ;; 0 20 | // TODO: copy, underscore, self (coro/task) 21 | 22 | // search in tests output for 23 | // definitely|Invalid read|Invalid write|uninitialised|uninitialized|free'd|alloc'd 24 | // - definitely lost 25 | // - Invalid read of size 26 | // - uninitialised value 27 | val VALGRIND = "" 28 | //val VALGRIND = "valgrind " 29 | val THROW = false 30 | //val THROW = true 31 | 32 | val D = "\$" 33 | 34 | // VERSION 35 | const val MAJOR = 0 36 | const val MINOR = 5 37 | const val REVISION = 0 38 | const val VERSION = "v$MAJOR.$MINOR.$REVISION" 39 | 40 | val PATH = File(File(System.getProperty("java.class.path")).absolutePath).parent 41 | 42 | val KEYWORDS: SortedSet = ( 43 | setOf ( 44 | "data", "do", "else", 45 | "false", "func'", "group", "if", 46 | "nil", "set", "true", 47 | "val", "var", 48 | ) + (if (CEU < 2) setOf() else setOf ( 49 | "catch", "defer", "enclose'", "escape", "loop'", 50 | )) + (if (CEU < 3) setOf() else setOf( 51 | "coro'", "resume", "yield", 52 | )) + (if (CEU < 4) setOf() else setOf( 53 | "broadcast", "delay", "in", "pub", "spawn", "task'", "toggle", 54 | )) + (if (CEU < 5) setOf() else setOf( 55 | "tasks", 56 | )) + (if (CEU < 50) setOf() else setOf( 57 | "drop", "val'", "var'", 58 | )) + (if (CEU < 99) setOf() else setOf( 59 | "await", "break", "coro", "enum", "every", 60 | "func", "ifs", "loop", "match", 61 | "par", "par-and", "par-or", 62 | "resume-yield-all", "return", 63 | "skip", "task", "test", 64 | "thus", "until", "watching", 65 | "with", "where", "while", 66 | )) 67 | ).toSortedSet() 68 | 69 | val OPERATORS = setOf('+', '-', '*', '/', '%', '>', '<', '=', '|', '&', '~') 70 | val XOPERATORS = if (CEU < 99) setOf() else { 71 | setOf("and", "in?", "in-not?", "is?", "is-not?", "not", "or") 72 | } 73 | 74 | val TAGS = listOf ( 75 | ":nil", ":tag", ":bool", ":char", ":number", ":pointer", 76 | ":dynamic", // error before or after dynamic 77 | ":func", 78 | ) + (if (CEU < 3) listOf() else listOf( 79 | ":coro", 80 | )) + (if (CEU < 4) listOf() else listOf( 81 | ":task", 82 | )) + listOf ( 83 | ":tuple", ":vector", ":dict", 84 | ) + (if (CEU < 3) listOf() else listOf( 85 | ":exe-coro", 86 | )) + (if (CEU < 4) listOf() else listOf( 87 | ":exe-task", 88 | )) + (if (CEU < 5) listOf() else listOf( 89 | ":tasks", 90 | )) + (if (CEU < 3) listOf() else listOfNotNull( 91 | ":yielded", (if (CEU<4) null else ":toggled"), ":resumed", ":terminated" 92 | )) + (if (CEU < 4) listOf() else listOf( 93 | ":global", ":task", 94 | )) + (if (CEU < 50) listOf() else listOfNotNull( 95 | ":fake", ":nested", 96 | )) + listOf( 97 | ":ceu", ":error", ":pre", 98 | ) + (if (CEU < 99) listOf() else listOf( 99 | ":break", ":skip", ":return", 100 | ":h", ":min", ":s", ":ms", 101 | ":idx", ":key", ":val", 102 | )) 103 | 104 | val GLOBALS = setOf ( 105 | "dump", "error", "next-dict", "print", "println", 106 | "sup?", "tag", 107 | "to-string-number", "to-string-pointer", "to-string-tag", 108 | "to-tag-string", 109 | "tuple", "type", "{{#}}", "{{==}}", "{{/=}}", 110 | ) + (if (CEU < 3) setOf() else setOf( 111 | "coroutine", "status" 112 | )) + (if (CEU < 4) setOf() else setOf( 113 | "broadcast'" 114 | )) + (if (CEU < 5) setOf() else setOf( 115 | "next-tasks", 116 | )) 117 | 118 | typealias NExpr = Int 119 | 120 | object G { 121 | var N: Int = 1 122 | var outer: Expr.Do? = null 123 | var ns: MutableMap = mutableMapOf() 124 | var ups: MutableMap = mutableMapOf() 125 | var tags: MutableMap = mutableMapOf() 126 | val datas = mutableMapOf() 127 | val nats: MutableMap,String>> = mutableMapOf() 128 | var nonlocs: MutableMap> = mutableMapOf() 129 | val mems: MutableSet = mutableSetOf() 130 | 131 | fun reset () { 132 | N = 1 133 | outer = null 134 | ns.clear() 135 | ups.clear() 136 | tags.clear() 137 | datas.clear() 138 | nats.clear() 139 | nonlocs.clear() 140 | mems.clear() 141 | } 142 | } 143 | 144 | typealias LData = List 145 | 146 | enum class Scope { 147 | GLOBAL, LOCAL, NESTED, UPVAL 148 | } 149 | 150 | sealed class Patt (val id: Tk.Id, val tag: Tk.Tag?, val pos: Expr) { 151 | data class None (val id_: Tk.Id, val tag_: Tk.Tag?, val pos_: Expr): Patt(id_,tag_,pos_) 152 | data class One (val id_: Tk.Id, val tag_: Tk.Tag?, val e: Expr, val pos_: Expr): Patt(id_,tag_,pos_) 153 | data class Tup (val id_: Tk.Id, val tag_: Tk.Tag?, val l: List, val pos_: Expr): Patt(id_,tag_,pos_) 154 | } 155 | 156 | sealed class Tk (val str: String, val pos: Pos) { 157 | data class Eof (val pos_: Pos, val n_: Int=G.N++): Tk("", pos_) 158 | data class Fix (val str_: String, val pos_: Pos, val n_: Int=G.N++): Tk(str_, pos_) 159 | data class Tag (val str_: String, val pos_: Pos, val n_: Int=G.N++): Tk(str_, pos_) 160 | data class Op (val str_: String, val pos_: Pos, val n_: Int=G.N++): Tk(str_, pos_) 161 | data class Id (val str_: String, val pos_: Pos, val n_: Int=G.N++): Tk(str_, pos_) // up: 0=var, 1=upvar, 2=upref 162 | data class Num (val str_: String, val pos_: Pos, val n_: Int=G.N++): Tk(str_, pos_) 163 | data class Chr (val str_: String, val pos_: Pos, val n_: Int=G.N++): Tk(str_, pos_) 164 | data class Nat (val str_: String, val pos_: Pos, val tag: String?, val n_: Int=G.N++): Tk(str_, pos_) 165 | } 166 | 167 | typealias Id_Tag = Pair 168 | 169 | sealed class Expr (var n: Int, val tk: Tk) { 170 | data class Proto (val tk_: Tk.Fix, val nst: Boolean, val fake: Boolean, val tag: Tk.Tag?, val pars: List, val blk: Do): Expr(G.N++, tk_) 171 | data class Do (val tk_: Tk, val es: List) : Expr(G.N++, tk_) 172 | data class Group (val tk_: Tk.Fix, val es: List) : Expr(G.N++, tk_) 173 | data class Enclose (val tk_: Tk.Fix, val tag: Tk.Tag, val es: List): Expr(G.N++, tk_) 174 | data class Escape (val tk_: Tk.Fix, val tag: Tk.Tag, val e: Expr?): Expr(G.N++, tk_) 175 | data class Dcl (val tk_: Tk.Fix, val lex: Boolean, /*val poly: Boolean,*/ val idtag: Id_Tag, val src: Expr?): Expr(G.N++, tk_) 176 | data class Set (val tk_: Tk.Fix, val dst: Expr, /*val poly: Tk.Tag?,*/ val src: Expr): Expr(G.N++, tk_) 177 | data class If (val tk_: Tk.Fix, val cnd: Expr, val t: Expr, val f: Expr): Expr(G.N++, tk_) 178 | data class Loop (val tk_: Tk.Fix, val blk: Expr): Expr(G.N++, tk_) 179 | data class Data (val tk_: Tk.Tag, val ids: List): Expr(G.N++, tk_) 180 | data class Drop (val tk_: Tk.Fix, val e: Expr): Expr(G.N++, tk_) 181 | 182 | data class Catch (val tk_: Tk.Fix, val tag: Tk.Tag?, val blk: Expr.Do): Expr(G.N++, tk_) 183 | data class Defer (val tk_: Tk.Fix, val blk: Expr.Do): Expr(G.N++, tk_) 184 | 185 | data class Yield (val tk_: Tk.Fix, val e: Expr): Expr(G.N++, tk_) 186 | data class Resume (val tk_: Tk.Fix, val co: Expr, val args: List): Expr(G.N++, tk_) 187 | 188 | data class Spawn (val tk_: Tk.Fix, val tsks: Expr?, val tsk: Expr, val args: List): Expr(G.N++, tk_) 189 | data class Delay (val tk_: Tk.Fix): Expr(G.N++, tk_) 190 | data class Pub (val tk_: Tk, val tsk: Expr?): Expr(G.N++, tk_) 191 | data class Toggle (val tk_: Tk.Fix, val tsk: Expr, val on: Expr): Expr(G.N++, tk_) 192 | data class Tasks (val tk_: Tk.Fix, val max: Expr): Expr(G.N++, tk_) 193 | 194 | data class Nat (val tk_: Tk.Nat): Expr(G.N++, tk_) 195 | data class Acc (val tk_: Tk.Id, val ign: Boolean=false): Expr(G.N++, tk_) 196 | data class Nil (val tk_: Tk.Fix): Expr(G.N++, tk_) 197 | data class Tag (val tk_: Tk.Tag): Expr(G.N++, tk_) 198 | data class Bool (val tk_: Tk.Fix): Expr(G.N++, tk_) 199 | data class Char (val tk_: Tk.Chr): Expr(G.N++, tk_) 200 | data class Num (val tk_: Tk.Num): Expr(G.N++, tk_) 201 | data class Tuple (val tk_: Tk.Fix, val args: List): Expr(G.N++, tk_) 202 | data class Vector (val tk_: Tk.Fix, val args: List): Expr(G.N++, tk_) 203 | data class Dict (val tk_: Tk.Fix, val args: List>): Expr(G.N++, tk_) 204 | data class Index (val tk_: Tk, val col: Expr, val idx: Expr): Expr(G.N++, tk_) 205 | data class Call (val tk_: Tk, val clo: Expr, val args: List): Expr(G.N++, tk_) 206 | } 207 | 208 | fun exec (hold: Boolean, cmds: List): Pair { 209 | //System.err.println(cmds.joinToString(" ")) 210 | val (x,y) = if (hold) { 211 | Pair(ProcessBuilder.Redirect.PIPE, true) 212 | } else { 213 | Pair(ProcessBuilder.Redirect.INHERIT, false) 214 | } 215 | val p = ProcessBuilder(cmds) 216 | .redirectOutput(x) 217 | .redirectError(x) 218 | .redirectErrorStream(y) 219 | .start() 220 | val ret = p.waitFor() 221 | val str = p.inputStream.bufferedReader().readText() 222 | return Pair(ret==0, str) 223 | } 224 | fun exec (hold: Boolean, cmd: String): Pair { 225 | return exec(hold, cmd.split(' ')) 226 | } 227 | 228 | fun all (tst: Boolean, verbose: Boolean, inps: List, Reader>>, out: String, args: List): String { 229 | if (verbose) { 230 | System.err.println("... parsing ...") 231 | } 232 | G.reset() 233 | val lexer = Lexer(inps) 234 | val parser = Parser(lexer) 235 | val es = try { 236 | parser.exprs() 237 | } catch (e: Throwable) { 238 | if (THROW) { 239 | throw e 240 | } 241 | return e.message!! + "\n" 242 | } 243 | //println(es.to_str()) 244 | val c = try { 245 | if (verbose) { 246 | System.err.println("... analysing ...") 247 | } 248 | //readLine() 249 | val pos = Pos("anon", 0, 0, 0) 250 | val tk0 = Tk.Fix("", pos.copy()) 251 | 252 | val glbs = GLOBALS.map { 253 | Expr.Dcl ( 254 | Tk.Fix("val", pos.copy()), 255 | true, 256 | Pair(Tk.Id(it,pos.copy(),0), null), 257 | null 258 | ) 259 | } 260 | val xargs = Expr.Dcl ( 261 | Tk.Fix("val",pos.copy()), 262 | true, 263 | Pair(Tk.Id("ARGS",pos.copy()),null), 264 | null 265 | ) 266 | 267 | G.outer = Expr.Do(tk0, listOf(xargs)+glbs+es) 268 | //println(G.outer) 269 | cache_ns() 270 | cache_ups() 271 | cache_tags() 272 | check_tags() 273 | check_vars() 274 | cache_nonlocs() 275 | check_statics() 276 | //Static() 277 | G.outer = G.outer!!.prune() as Expr.Do 278 | //println(G.outer!!.to_str()) 279 | //rets.pub.forEach { println(listOf(it.value,it.key.javaClass.name,it.key.tk.pos.lin)) } 280 | if (verbose) { 281 | System.err.println("... ceu -> c ...") 282 | } 283 | val coder = Coder() 284 | coder.main() 285 | } catch (e: Throwable) { 286 | if (THROW) { 287 | throw e 288 | } 289 | return e.message!! + "\n" 290 | } 291 | if (verbose) { 292 | System.err.println("... c -> exe ...") 293 | } 294 | File("$out.c").writeText(c) 295 | val cmd = listOf("gcc", "-Werror", "$out.c", "-l", "m", "-o", "$out.exe") + args 296 | if (verbose) { 297 | System.err.println("\t" + cmd.joinToString(" ")) 298 | } 299 | val (ok2, out2) = exec(true, cmd) 300 | if (!ok2) { 301 | return out2 302 | } 303 | if (verbose) { 304 | System.err.println("... executing ...") 305 | } 306 | val (_, out3) = exec(tst, "$VALGRIND./$out.exe") 307 | //println(out3) 308 | return out3 309 | } 310 | 311 | fun test (inp: String, pre: Boolean=false): String { 312 | //println(inp) 313 | val prelude = if (CEU == 99) "build/prelude-x.ceu" else "build/prelude-0.ceu" 314 | val inps = listOf(Pair(Triple("anon",1,1), inp.reader())) + if (!pre) emptyList() else { 315 | listOf(Pair(Triple(prelude,1,1), File(prelude).reader())) 316 | } 317 | return all(true, false, inps, "out", emptyList()) 318 | } 319 | 320 | fun main (args: Array) { 321 | val (xs, ys) = args.cmds_opts() 322 | try { 323 | val xinp = if (xs.size > 0) xs[0] else null 324 | val xccs = (when { 325 | !ys.containsKey("--cc") -> emptyList() 326 | (ys["--cc"] == null) -> { 327 | throw Exception("argument error : --cc : expected \"=\"") 328 | } 329 | else -> ys["--cc"]!!.split(" ") 330 | }) + (when { 331 | !ys.containsKey("--lib") -> emptyList() 332 | (ys["--lib"] == null) -> { 333 | throw Exception("argument error : --lib : expected \"=\"") 334 | } 335 | else -> { 336 | File(PATH + "/" + ys["--lib"] + "/ceu.lib") 337 | .readText() 338 | .trim() 339 | .replace("@/",PATH+"/") 340 | .split(" ") 341 | } 342 | }) 343 | 344 | LEX = ys.containsKey("--lex") 345 | TEST = ys.containsKey("--test") 346 | DEBUG = ys.containsKey("--debug") 347 | DUMP = DEBUG 348 | 349 | when { 350 | ys.containsKey("--version") -> println("dceu " + VERSION) 351 | (xinp == null) -> println("expected filename") 352 | else -> { 353 | val f = File(xinp) 354 | val inps = listOf( 355 | Pair(Triple(xinp,1,1), f.reader()), 356 | Pair(Triple("prelude.ceu",1,1), FileX("@/prelude.ceu").reader()) 357 | ) 358 | val out = all(false, ys.containsKey("--verbose"), inps, f.nameWithoutExtension, xccs) 359 | print(out) 360 | } 361 | } 362 | } catch (e: Throwable) { 363 | println(e.message!!) 364 | } 365 | } 366 | -------------------------------------------------------------------------------- /src/main/kotlin/Lexer.kt: -------------------------------------------------------------------------------- 1 | package dceu 2 | 3 | import java.io.File 4 | import java.io.PushbackReader 5 | import java.io.Reader 6 | import java.io.StringReader 7 | 8 | data class Lex ( 9 | var file: String, 10 | var lin: Int, var col: Int, var brks: Int, 11 | var prv: Int, // previous col before \n to restore on unread 12 | val reader: PushbackReader 13 | ) 14 | data class Pos (val file: String, val lin: Int, val col: Int, val brks: Int) 15 | 16 | fun Lex.toPos (): Pos { 17 | return Pos(this.file, this.lin, this.col, this.brks) 18 | } 19 | 20 | fun FileX (path: String): File { 21 | val xpath = if (path[0] != '@') path else { 22 | PATH + "/" + path.drop(2) 23 | } 24 | return File(xpath) 25 | } 26 | 27 | class Lexer (inps: List,Reader>>, reset: Boolean=true) { 28 | val stack = ArrayDeque() 29 | val comms = ArrayDeque() 30 | 31 | init { 32 | for (inp in inps) { 33 | stack.addFirst(Lex(inp.first.first, inp.first.second, inp.first.third, 0, 0, PushbackReader(inp.second,2))) 34 | } 35 | } 36 | 37 | // TODO: reads 65535 after unreading -1 38 | fun iseof (n: Int): Boolean { 39 | return (n==-1 || n==65535) 40 | } 41 | 42 | fun read2 (): Pair { 43 | val pos = stack.first() 44 | val n = pos.reader.read() 45 | val x = n.toChar() 46 | pos.prv = pos.col 47 | when { 48 | (x == '\n') -> { pos.lin++; pos.col=1} 49 | (x == ';') -> { pos.col++ ; pos.brks++ } 50 | !iseof(n) -> pos.col++ 51 | } 52 | return Pair(n,x) 53 | } 54 | fun unread2 (n: Int) { 55 | val pos = stack.first() 56 | val x = n.toChar() 57 | pos.reader.unread(n) 58 | when { 59 | iseof(n) -> {} 60 | (x == ';') -> pos.brks-- 61 | (x == '\n') -> { pos.lin--; pos.col=pos.prv } 62 | else -> pos.col = pos.col-1 63 | } 64 | } 65 | fun read2Until (f: (x: Char)->Boolean): String? { 66 | var ret = "" 67 | while (true) { 68 | val (n,x) = read2() 69 | when { 70 | iseof(n) -> { 71 | unread2(n) 72 | return null 73 | } 74 | f(x) -> break 75 | else -> ret += x 76 | } 77 | } 78 | return ret 79 | } 80 | fun read2Until (x: Char): String? { 81 | return read2Until { it == x } 82 | } 83 | fun read2While (f: (x: Char)->Boolean): String { 84 | var ret = "" 85 | while (true) { 86 | val (n,x) = read2() 87 | when { 88 | f(x) -> ret += x 89 | else -> { 90 | unread2(n) 91 | break 92 | } 93 | } 94 | } 95 | return ret 96 | } 97 | fun read2While (x: Char): String { 98 | return read2While { it == x } 99 | } 100 | fun read2While2 (f: (x: Char, y: Char)->Boolean): String { 101 | var ret = "" 102 | while (true) { 103 | val (n1,x) = read2() 104 | val (n2,y) = read2() 105 | when { 106 | f(x,y) -> { 107 | unread2(n2) 108 | ret += x 109 | } 110 | else -> { 111 | unread2(n2) 112 | unread2(n1) 113 | break 114 | } 115 | } 116 | } 117 | return ret 118 | } 119 | 120 | fun next (): Pair { 121 | while (true) { 122 | val lex = this.stack.first() 123 | val pos = lex.toPos() 124 | val (n1,x1) = read2() 125 | when (x1) { 126 | ' ', '\t', '\n' -> {} 127 | ';' -> { 128 | val (n2,x2) = read2() 129 | if (x2 == ';') { 130 | val x3 = ";;" + read2While(';') 131 | if (x3 == ";;") { 132 | read2Until('\n') 133 | } else { 134 | var x4 = x3 135 | outer@ while (true) { 136 | if (this.comms.firstOrNull() == x4) { 137 | this.comms.removeFirst() 138 | if (this.comms.size == 0) { 139 | break 140 | } 141 | } else { 142 | this.comms.addFirst(x4) 143 | } 144 | do { 145 | if (read2Until(';') == null) { 146 | break@outer 147 | } 148 | x4 = ";" + read2While(';') 149 | } while (x4.length<=2 || x4.length { 157 | return if (iseof(n1)) { 158 | Pair(null, pos) 159 | } else { 160 | Pair(x1, pos) 161 | } 162 | } 163 | } 164 | } 165 | } 166 | 167 | fun lex (): Sequence = sequence { 168 | while (true) { 169 | val (x,pos) = next() 170 | when { 171 | (x == null) -> { 172 | if (stack.size > 1) { 173 | stack.removeFirst() 174 | } else { 175 | yield(Tk.Eof(pos)) 176 | break 177 | } 178 | } 179 | (CEU>=99 && x=='\\') -> yield(Tk.Fix(x.toString(), pos)) 180 | (x in listOf('}','(',')','[',']',',','\$','.')) -> yield(Tk.Fix(x.toString(), pos)) 181 | (x=='@' || x=='#') -> { 182 | val (n1,x1) = read2() 183 | when { 184 | (x1 == '[') -> yield(Tk.Fix("$x[", pos)) 185 | (x1 !in OPERATORS) -> { 186 | yield(Tk.Op(x.toString(), pos)) 187 | unread2(n1) 188 | } 189 | else -> { 190 | val op = x.toString() + x1 + read2While { it in OPERATORS } 191 | yield(Tk.Op(op, pos)) 192 | } 193 | } 194 | } 195 | (x in OPERATORS) -> { 196 | val op = x + read2While { it in OPERATORS } 197 | when { 198 | (op == "=") -> yield(Tk.Fix(op, pos)) 199 | (op == "=>") -> yield(Tk.Fix(op, pos)) 200 | (CEU>=99 && op=="->") -> yield(Tk.Fix(op, pos)) 201 | (CEU>=99 && op=="<-") -> yield(Tk.Fix(op, pos)) 202 | (CEU>=99 && op=="-->") -> yield(Tk.Fix(op, pos)) 203 | (CEU>=99 && op=="<--") -> yield(Tk.Fix(op, pos)) 204 | else -> yield(Tk.Op(op, pos)) 205 | } 206 | } 207 | (x == '{') -> { 208 | val (n1,x1) = read2() 209 | if (x1 != '{') { 210 | unread2(n1) 211 | yield(Tk.Fix("{", pos)) 212 | } else { 213 | val op = read2While { it != '}' } 214 | val (_,x2) = read2() 215 | val (_,x3) = read2() 216 | if (!(x2=='}' && x3=='}')) { 217 | err(pos, "operator error : expected \"}\"") 218 | } 219 | when { 220 | op in XOPERATORS -> yield(Tk.Id(op, pos)) 221 | op.all { it in OPERATORS } -> yield(Tk.Id("{{$op}}", pos)) 222 | //op.none { it in OPERATORS } -> yield(Tk.Op(op, pos, 0)) 223 | //else -> err(pos, "operator error : invalid identifier") 224 | else -> yield(Tk.Op(op, pos, 0)) // bc of '-' 225 | } 226 | } 227 | } 228 | (x == ':') -> { 229 | val tag = // no '_' b/c of C ids: X.Y -> X_Y 230 | x + read2While2 { a,b -> a.isLetterOrDigit() || a in listOf('\'','?','!') || ((a=='.' || a=='-') && b.isLetter()) } 231 | if (tag.length < 2) { 232 | err(pos, "tag error : expected identifier") 233 | } 234 | if (tag.count { it == '.' } > 3) { 235 | err(pos, "tag error : excess of '.' : max hierarchy of 4") 236 | } 237 | yield(Tk.Tag(tag, pos)) 238 | } 239 | (x.isLetter() || x=='_') -> { 240 | val id = x + read2While2 { a,b -> a.isLetterOrDigit() || a in listOf('_','\'','?','!') || (a=='-' && b.isLetter()) } 241 | when { 242 | XOPERATORS.contains(id) -> yield(Tk.Op(id, pos)) 243 | KEYWORDS.contains(id) -> yield(Tk.Fix(id, pos)) 244 | else -> yield(Tk.Id(id, pos)) 245 | } 246 | } 247 | x.isDigit() -> { 248 | val num = x + read2While { it=='.' || it.isLetterOrDigit() } 249 | yield(Tk.Num(num, pos)) 250 | } 251 | (x == '`') -> { 252 | val open = x + read2While('`') 253 | var nat = "" 254 | val tag = read2().let { (n2,x2) -> 255 | if (x2 != ':') { 256 | unread2(n2) 257 | null 258 | } else { 259 | val tag = x2 + read2While { it.isLetterOrDigit() || it=='.' } 260 | if (tag.length < 2) { 261 | err(pos, "tag error : expected identifier") 262 | } 263 | tag 264 | } 265 | } 266 | while (true) { 267 | val (n2,x2) = read2() 268 | when { 269 | iseof(n2) -> { 270 | err(stack.first().toPos(), "native error : expected \"$open\"") 271 | } 272 | (x2 == '`') -> { 273 | val xxx = stack.first().toPos().let { Pos(it.file,it.lin,it.col-1,it.brks) } 274 | val close = x2 + read2While('`') 275 | if (open == close) { 276 | break 277 | } else { 278 | err(xxx, "native error : expected \"$open\"") 279 | } 280 | } 281 | } 282 | nat += x2 283 | } 284 | //println(":$pay:") 285 | yield(Tk.Nat(nat, pos, tag)) 286 | } 287 | (x == '^') -> { 288 | val (_,x2) = read2() 289 | if (x2 != '[') { 290 | err(pos, "token ^ error : expected \"^[\"") 291 | } 292 | val (n3, x3) = read2() 293 | val file: String? = if (x3 == '"') { 294 | val f = read2Until('"') 295 | if (f == null) { 296 | err(pos, "token ^ error : unterminated \"") 297 | } 298 | f 299 | } else { 300 | unread2(n3) 301 | null 302 | } 303 | 304 | val lin: Int? = if (file == null) { 305 | read2While { it.isDigit() }.toIntOrNull() 306 | } else { 307 | val (n4, x4) = read2() 308 | if (x4 == ',') { 309 | read2While { it.isDigit() }.let { 310 | if (it.isEmpty()) { 311 | err(pos, "token ^ error : expected number") 312 | } 313 | it.toInt() 314 | } 315 | } else { 316 | unread2(n4) 317 | null 318 | } 319 | } 320 | val col: Int? = if (lin == null) { 321 | null 322 | } else { 323 | val (n5, x5) = read2() 324 | if (x5 == ',') { 325 | read2While { it.isDigit() }.let { 326 | if (it.isEmpty()) { 327 | err(pos, "token ^ error : expected number") 328 | } 329 | it.toInt() 330 | } 331 | } else { 332 | unread2(n5) 333 | null 334 | } 335 | } 336 | 337 | if (file == null && lin == null) { 338 | err(pos, "token ^ error") 339 | } 340 | 341 | val (_, x6) = read2() 342 | if (x6 != ']') { 343 | err(pos, "token ^ error : expected \"]\"") 344 | } 345 | val (n7, x7) = read2() 346 | when { 347 | iseof(n7) -> unread2(n7) 348 | (x7 == '\n') -> {} // skip leading \n 349 | else -> unread2(n7) //err(pos, "token ^ error : expected end of line") 350 | } 351 | 352 | when { 353 | (file != null && lin == null && col == null) -> { 354 | val f = FileX(file) 355 | if (!f.exists()) { 356 | err(pos, "token ^ error : file not found : $file") 357 | } 358 | stack.addFirst(Lex(file, 1, 1, 0, 0, PushbackReader(StringReader(f.readText()), 2))) 359 | } 360 | 361 | (lin != null) -> stack.first().let { 362 | it.file = if (file == null) it.file else file 363 | it.lin = lin 364 | it.col = if (col == null) 1 else col 365 | } 366 | else -> error("bug found") 367 | } 368 | } 369 | (x == '\'') -> { 370 | val (n2,x2) = read2() 371 | if (iseof(n2)) { 372 | err(stack.first().toPos(), "char error : expected '") 373 | } 374 | val c = if (x2 != '\\') x2.toString() else { 375 | val (n3,x3) = read2() 376 | if (iseof(n3)) { 377 | err(stack.first().toPos(), "char error : expected '") 378 | } 379 | x2.toString()+x3 380 | } 381 | val (n3,x3) = read2() 382 | if (iseof(n3) || x3!='\'') { 383 | err(stack.first().toPos(), "char error : expected '") 384 | } 385 | yield(Tk.Chr("'$c'", pos)) 386 | } 387 | (x == '"') -> { 388 | var n = 0 389 | val v = read2Until { 390 | val brk = (it=='"' && n%2==0) 391 | n = if (it == '\\') n+1 else 0 392 | brk 393 | } 394 | if (v == null) { 395 | err(pos, "string error : unterminated \"") 396 | } 397 | yield(Tk.Fix("#[", pos)) 398 | var i = 0 399 | while (i < v.length) { 400 | if (i > 0) { 401 | yield(Tk.Fix(",", pos)) 402 | } 403 | val z = v[i] 404 | val zz = when { 405 | (z == '\'') -> "\\'" 406 | (z != '\\') -> z.toString() 407 | else -> { 408 | i++ 409 | z.toString() + v[i] 410 | } 411 | } 412 | yield(Tk.Chr("'$zz'", pos)) 413 | i++ 414 | } 415 | yield(Tk.Fix("]", pos)) 416 | } 417 | else -> { 418 | err(pos, "token error : unexpected $x") 419 | } 420 | } 421 | } 422 | } 423 | } 424 | -------------------------------------------------------------------------------- /src/main/kotlin/Checks.kt: -------------------------------------------------------------------------------- 1 | package dceu 2 | 3 | fun check_tags () { 4 | for ((id,tk) in G.tags) { 5 | val issub = id.contains('.') 6 | //println(listOf(id,issub)) 7 | val sup = id.dropLastWhile { it != '.' }.dropLast(1) 8 | if (issub && !G.tags.containsKey(sup)) { 9 | err(tk, "tag error : parent tag $sup is not declared") 10 | } 11 | } 12 | } 13 | fun check_vars () { 14 | fun check (dcl: Expr.Dcl) { 15 | if (CEU>=50 && dcl.idtag.first.str=="it") { 16 | // ok 17 | } else { 18 | val xdcl = dcl.id_to_dcl(dcl.idtag.first.str, false, { it.n==dcl.n }) 19 | if (xdcl === null) { 20 | // ok 21 | } else { 22 | err(dcl.tk, "declaration error : variable \"${dcl.idtag.first.str}\" is already declared") 23 | } 24 | } 25 | } 26 | 27 | fun acc (e: Expr, id: String): Expr.Dcl { 28 | val dcl: Expr.Dcl? = e.id_to_dcl(id) 29 | if (dcl === null) { 30 | err(e.tk, "access error : variable \"${id}\" is not declared") 31 | } 32 | 33 | // add upval to all protos upwards 34 | // stop at declaration (orig) 35 | // use blk bc of args 36 | if (type(dcl,e) == Scope.UPVAL) { 37 | if (dcl.tk.str!="val" && dcl.tk.str!="val'") { 38 | err(e.tk, "access error : outer variable \"${dcl.idtag.first.str}\" must be immutable") 39 | } 40 | } 41 | 42 | return dcl 43 | } 44 | 45 | fun Expr.traverse () { 46 | when (this) { 47 | is Expr.Proto -> { 48 | if (this.tag !==null && !G.datas.containsKey(this.tag.str)) { 49 | err(this.tag, "declaration error : data ${this.tag.str} is not declared") 50 | } 51 | this.pars.forEach { check(it) } 52 | this.blk.traverse() 53 | } 54 | is Expr.Do -> this.es.forEach { it.traverse() } 55 | is Expr.Group -> this.es.forEach { it.traverse() } 56 | is Expr.Enclose -> this.es.forEach { it.traverse() } 57 | is Expr.Escape -> this.e?.traverse() 58 | is Expr.Dcl -> { 59 | this.src?.traverse() 60 | check(this) 61 | } 62 | is Expr.Set -> { 63 | this.dst.traverse() 64 | this.src.traverse() 65 | } 66 | is Expr.If -> { this.cnd.traverse() ; this.t.traverse() ; this.f.traverse() } 67 | is Expr.Loop -> this.blk.traverse() 68 | is Expr.Data -> { 69 | val sup = this.tk.str.dropLastWhile { it != '.' }.dropLast(1) 70 | if (G.datas.containsKey(this.tk.str)) { 71 | err(this.tk, "data error : data ${this.tk.str} is already declared") 72 | } 73 | val ids = (G.datas[sup] ?: emptyList()) + this.ids 74 | val xids = ids.map { it.first.str } 75 | if (xids.size != xids.distinct().size) { 76 | err(this.tk, "data error : found duplicate ids") 77 | } 78 | ids.forEach { (_,tag) -> 79 | if (tag!==null && !G.datas.containsKey(tag.str)) { 80 | err(tag, "data error : data ${tag.str} is not declared") 81 | } 82 | } 83 | G.datas[this.tk.str] = ids 84 | } 85 | is Expr.Drop -> this.e.traverse() 86 | 87 | is Expr.Catch -> this.blk.traverse() 88 | is Expr.Defer -> this.blk.traverse() 89 | 90 | is Expr.Yield -> this.e.traverse() 91 | is Expr.Resume -> { this.co.traverse() ; this.args.forEach { it.traverse() } } 92 | 93 | is Expr.Spawn -> { this.tsks?.traverse() ; this.tsk.traverse() ; this.args.forEach { it.traverse() } } 94 | is Expr.Delay -> {} 95 | is Expr.Pub -> this.tsk?.traverse() 96 | is Expr.Toggle -> { this.tsk.traverse() ; this.on.traverse() } 97 | is Expr.Tasks -> this.max.traverse() 98 | 99 | is Expr.Nat -> { 100 | G.nats[this.n] = this.tk.str.let { 101 | assert(!it.contains("XXX")) { "TODO: native cannot contain XXX"} 102 | val set = mutableListOf() 103 | var str = "" 104 | var i = 0 105 | 106 | var lin = 1 107 | var col = 1 108 | fun read (): Char { 109 | //assert(i < it.length) { "bug found" } 110 | if (i >= it.length) { 111 | err(tk, "native error : (lin $lin, col $col) : unterminated token") 112 | } 113 | val x = it[i++] 114 | if (x == '\n') { 115 | lin++; col=0 116 | } else { 117 | col++ 118 | } 119 | return x 120 | } 121 | 122 | while (i < it.length) { 123 | val x1 = read() 124 | str += if (x1 != '$') x1 else { 125 | val (l,c) = Pair(lin,col) 126 | var id = "" 127 | var no = "" 128 | while (i < it.length) { 129 | val x2 = read() 130 | if (x2.isLetterOrDigit() || x2=='_' || x2=='-') { 131 | id += x2 132 | } else { 133 | no += x2 134 | break 135 | } 136 | } 137 | if (id.length == 0) { 138 | err(tk, "native error : (lin $l, col $c) : invalid identifier") 139 | } 140 | val dcl = acc(this, id) 141 | set.add(dcl.n) 142 | "(XXX)$no" 143 | } 144 | } 145 | Pair(set, str) 146 | } 147 | } 148 | is Expr.Acc -> acc(this, this.tk.str) 149 | is Expr.Nil -> {} 150 | is Expr.Tag -> {} 151 | is Expr.Bool -> {} 152 | is Expr.Char -> {} 153 | is Expr.Num -> {} 154 | is Expr.Tuple -> this.args.forEach { it.traverse() } 155 | is Expr.Vector -> this.args.forEach { it.traverse() } 156 | is Expr.Dict -> this.args.forEach { it.first.traverse() ; it.second.traverse() } 157 | is Expr.Index -> { 158 | this.col.traverse() 159 | this.idx.traverse() 160 | this.data() 161 | } 162 | is Expr.Call -> { this.clo.traverse() ; this.args.forEach { it.traverse() } } 163 | } 164 | } 165 | G.outer!!.traverse() 166 | } 167 | 168 | fun check_statics () { 169 | G.outer!!.dn_visit { me -> 170 | when (me) { 171 | is Expr.Proto -> { 172 | if (me.nst) { 173 | when { 174 | (me.tk.str != "func'") -> { 175 | // OK: nested coro/task always ok b/c of ceux/MEM 176 | } 177 | (G.nonlocs[me.n]!!.isEmpty()) -> { 178 | // OK: no access to outer - unset :nested 179 | } 180 | me.up_any { it is Expr.Proto && it.tk.str != "func'" } -> { 181 | val proto = me.up_first { it is Expr.Proto && it.tk.str != "func'" }!! 182 | err(me.tk, "func :nested error : unexpected enclosing ${proto.tk.str}") 183 | } 184 | } 185 | } 186 | } 187 | is Expr.Escape -> { 188 | val fst = me.up_first { (it is Expr.Enclose && it.tag.str==me.tag.str) || (it is Expr.Proto && !it.nst)} 189 | if (fst !is Expr.Enclose) { 190 | err(me.tk, "escape error : expected matching enclosing block") 191 | } 192 | } 193 | is Expr.Group -> { 194 | val up = me.fupx() 195 | val ok = (up is Expr.Do) || (up is Expr.Group) || (up is Expr.Dcl) || (up is Expr.Set && up.src.n==me.n) 196 | if (!ok) { 197 | //err(me.tk, "group error : unexpected context") 198 | } 199 | } 200 | is Expr.Set -> { 201 | if (me.dst is Expr.Acc) { 202 | val dcl = me.dst.id_to_dcl(me.dst.tk.str)!! 203 | if (dcl.tk.str=="val" || dcl.tk.str=="val'") { 204 | err(me.tk, "set error : destination is immutable") 205 | } 206 | } 207 | } 208 | is Expr.Defer -> { 209 | val f = me.up_first { it is Expr.Proto && it.tk.str=="func'" } 210 | if (f !== null) { 211 | val co = f.up_any { it is Expr.Proto && it.tk.str!="func'" } 212 | if (co) { 213 | err(me.tk, "defer error : unexpected func with enclosing coro or task") 214 | } 215 | } 216 | } 217 | is Expr.Yield -> { 218 | when { 219 | me.up_any { defer -> (defer is Expr.Defer) } 220 | -> err(me.tk, "yield error : unexpected enclosing defer") 221 | me.up_first { it is Expr.Proto }.let { it?.tk?.str=="func'" } 222 | -> err(me.tk, "yield error : unexpected enclosing func") 223 | (me.up_exe() === null) 224 | -> err(me.tk, "yield error : expected enclosing coro" + (if (CEU <= 3) "" else " or task")) 225 | } 226 | } 227 | is Expr.Delay -> { 228 | if (!me.up_first { it is Expr.Proto }.let { it?.tk?.str=="task'" }) { 229 | err(me.tk, "delay error : expected enclosing task") 230 | } 231 | } 232 | is Expr.Pub -> { 233 | if (me.tsk === null) { 234 | val outer = me.up_first_task_outer() 235 | val ok = (outer !== null) && me.up_all_until { it == outer }.none { it is Expr.Proto && it.tk.str!="task'" } 236 | if (!ok) { 237 | err(me.tk, "pub error : expected enclosing task") 238 | } 239 | //(ups.first_task_outer(this) === null) -> err(this.tk, "pub error : expected enclosing task") 240 | } 241 | } 242 | else -> {} 243 | } 244 | } 245 | } 246 | 247 | class Static () { 248 | // protos_unused: const proto is not used: do not generate code 249 | /* 250 | val protos_use_unused: MutableSet = mutableSetOf() 251 | val protos_use_map: MutableMap> = mutableMapOf() 252 | fun protos_use_f (proto: Expr.Proto) { 253 | if (protos_use_unused.contains(proto)) { // if breaks recursion 254 | protos_use_unused.remove(proto) // found access, remove it 255 | protos_use_map[proto]!!.forEach { 256 | protos_use_f(it) 257 | } 258 | } 259 | } 260 | */ 261 | 262 | /* 263 | // void: block is innocuous -> should be a proxy to up block 264 | fun void (blk: Expr.Do): Boolean { 265 | // no declarations, no spawns, no tasks 266 | val dcls = blk.to_dcls() 267 | //println(listOf("-=-=-", blk.tk, G.ups[blk]?.javaClass?.name)) 268 | //println(blk.tostr()) 269 | return when { 270 | true -> false 271 | blk.is_mem(true) -> false 272 | (blk.tag !== null) -> false 273 | !dcls.isEmpty() -> false 274 | (G.ups[blk] is Expr.Proto) -> false 275 | this.defer_catch_spawn_tasks.contains(blk) -> false 276 | else -> true 277 | } 278 | } 279 | val defer_catch_spawn_tasks: MutableSet = mutableSetOf() 280 | */ 281 | 282 | init { 283 | G.outer!!.traverse() 284 | } 285 | 286 | fun Expr.traverse () { 287 | when (this) { 288 | is Expr.Proto -> { 289 | if (this.nst) { 290 | this.fupx().up_all_until { it is Expr.Proto } 291 | .filter { it is Expr.Do || it is Expr.Proto } // all blocks up to proto 292 | .forEach { G.mems.add(it) } 293 | /* 294 | val up1 = G.ups[this] 295 | val up2 = if (up1==null) null else G.ups[up1] 296 | when { 297 | (up1 !is Expr.Spawn) -> err(this.tk, "task :nested error : expected enclosing spawn") 298 | (up2 !is Expr.Do) -> err(up1.tk, "spawn task :nested error : expected immediate enclosing block") 299 | (G.ups[up2] == outer.clo) -> {} // OK: outer spawn 300 | (up2.es.last() == up1) -> err(up1.tk, "spawn task :nested error : cannot escape enclosing block") 301 | } 302 | */ 303 | } 304 | this.blk.traverse() 305 | } 306 | is Expr.Do -> this.es.forEach { it.traverse() } 307 | is Expr.Group -> this.es.forEach { it.traverse() } 308 | is Expr.Enclose -> this.es.forEach { it.traverse() } 309 | is Expr.Escape -> this.e?.traverse() 310 | is Expr.Dcl -> { 311 | if (this.src is Expr.Proto && (this.tk.str=="val" || this.tk.str=="val'")) { 312 | //protos_use_unused.add(this.src) 313 | //protos_use_map[this.src] = mutableSetOf() 314 | } 315 | this.src?.traverse() 316 | } 317 | is Expr.Set -> { 318 | this.dst.traverse() 319 | this.src.traverse() 320 | } 321 | is Expr.If -> { this.cnd.traverse() ; this.t.traverse() ; this.f.traverse() } 322 | is Expr.Loop -> this.blk.traverse() 323 | is Expr.Data -> {} 324 | is Expr.Drop -> this.e.traverse() 325 | 326 | is Expr.Catch -> this.blk.traverse() 327 | is Expr.Defer -> { 328 | //defer_catch_spawn_tasks.add(this.up_first { it is Expr.Do } as Expr.Do) 329 | this.blk.traverse() 330 | } 331 | 332 | is Expr.Yield -> { 333 | this.e.traverse() 334 | this.up_all_until { it is Expr.Proto } 335 | .filter { it is Expr.Do || it is Expr.Proto } // all blocks up to proto 336 | .forEach { G.mems.add(it) } 337 | } 338 | is Expr.Resume -> { 339 | this.co.traverse() 340 | this.args.forEach { it.traverse() } 341 | } 342 | 343 | is Expr.Spawn -> { 344 | this.tsks?.traverse() 345 | this.tsk.traverse() 346 | this.args.forEach { it.traverse() } 347 | if (this.tsks === null) { 348 | //defer_catch_spawn_tasks.add(this.up_first { it is Expr.Do } as Expr.Do) 349 | 350 | // tasks is the one relevant, not the spawn itself 351 | this.up_all_until { it is Expr.Proto } 352 | .filter { it is Expr.Do || it is Expr.Proto } // all blocks up to proto 353 | .forEach { G.mems.add(it) } 354 | } 355 | /* 356 | when { 357 | (ups.first(this) { f -> ((f is Expr.Proto) && f.tk.str == "func") } !== null) 358 | -> err(this.tk, "spawn error : unexpected enclosing func") 359 | } 360 | */ 361 | } 362 | is Expr.Delay -> {} 363 | is Expr.Pub -> this.tsk?.traverse() 364 | is Expr.Toggle -> { this.tsk.traverse() ; this.on.traverse() } 365 | is Expr.Tasks -> { 366 | this.max.traverse() 367 | this.up_all_until { it is Expr.Proto } 368 | .filter { it is Expr.Do || it is Expr.Proto } // all blocks up to proto 369 | .forEach { G.mems.add(it) } 370 | } 371 | 372 | is Expr.Nat -> {} 373 | is Expr.Acc -> { 374 | /* 375 | val dcl = this.id_to_dcl(this.tk.str)!! 376 | if (dcl.src is Expr.Proto && (dcl.tk.str=="val" || dcl.tk.str=="val'")) { 377 | // f is accessed 378 | // - from an enclosing const g 379 | // - g calls f 380 | // - add f to g such that f is ok if g is ok 381 | // - elsewhere 382 | // - f is ok and all fs' accessed from f 383 | val up_proto = this.up_first { it is Expr.Proto && G.ups[it].let { it is Expr.Dcl && (it.tk.str=="val" || it.tk.str=="val'") } } 384 | when { 385 | (up_proto === null) -> protos_use_f(dcl.src) 386 | //!protos_use_unused.contains(up_proto) -> protos_use_f(dcl.src) 387 | else -> protos_use_map[up_proto]!!.add(dcl.src) 388 | } 389 | } 390 | */ 391 | } 392 | is Expr.Nil -> {} 393 | is Expr.Tag -> {} 394 | is Expr.Bool -> {} 395 | is Expr.Char -> {} 396 | is Expr.Num -> {} 397 | is Expr.Tuple -> this.args.forEach { it.traverse() } 398 | is Expr.Vector -> this.args.forEach { it.traverse() } 399 | is Expr.Dict -> this.args.forEach { (k,v) -> k.traverse() ; v.traverse() } 400 | is Expr.Index -> { 401 | this.col.traverse() 402 | this.idx.traverse() 403 | } 404 | is Expr.Call -> { 405 | this.clo.traverse() 406 | this.args.forEach { it.traverse() } 407 | if (this.clo is Expr.Acc && this.clo.tk.str=="tasks") { 408 | //defer_catch_spawn_tasks.add(this.up_first { it is Expr.Do } as Expr.Do) 409 | } 410 | 411 | } 412 | } 413 | } 414 | } 415 | --------------------------------------------------------------------------------