├── 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 |
4 |
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 |
5 |
6 |
7 |
8 |
9 |
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 |
5 |
6 |
7 |
8 |
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