diff --git a/README.md b/README.md index 73e2160..2e9c603 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,12 @@ language that cross-compiles to C. Tomo is designed to anticipate and influence the language design decisions of the future. ``` -func greeting(name:Text)->Text +func greeting(name:Text)->Text: greeting := "hello {name}!" words := greeting:split(" ") return " ":join([w:capitalize() for w in words]) -func main(name="World") +func main(name="World"): to_say := greeting(name) say(to_say) ``` diff --git a/parse.c b/parse.c index e325ebf..e3fdbb4 100644 --- a/parse.c +++ b/parse.c @@ -781,8 +781,10 @@ PARSER(parse_reduction) { } ast_t *fallback = NULL; - if (match_word(&pos, "else")) + if (match_word(&pos, "else")) { + expect_str(ctx, start, &pos, ":", "I expected a ':' here"); fallback = expect(ctx, pos-4, &pos, parse_expr, "I couldn't parse the expression after this 'else'"); + } return NewAST(ctx->file, start, pos, Reduction, .iter=iter, .combination=combination, .fallback=fallback); } @@ -838,7 +840,7 @@ PARSER(parse_if) { if (!condition) condition = expect(ctx, start, &pos, parse_expr, "I expected to find an expression for this 'if'"); - match_word(&pos, "then"); // optional + expect_str(ctx, start, &pos, ":", "I expected a ':' here"); ast_t *body = expect(ctx, start, &pos, parse_opt_indented_block, "I expected a body for this 'if' statement"); @@ -848,6 +850,7 @@ PARSER(parse_if) { const char *else_start = pos; if (get_indent(ctx->file, tmp) == starting_indent && match_word(&tmp, "else")) { pos = tmp; + expect_str(ctx, start, &pos, ":", "I expected a ':' here"); else_body = expect(ctx, else_start, &pos, parse_opt_indented_block, "I expected a body for this 'else'"); } return NewAST(ctx->file, start, pos, If, .condition=condition, .body=body, .else_body=else_body); @@ -881,7 +884,8 @@ PARSER(parse_when) { var = NULL; } - match_word(&pos, "then"); // optional + expect_str(ctx, start, &pos, ":", "I expected a ':' here"); + ast_t *body = expect(ctx, start, &pos, parse_opt_indented_block, "I expected a body for this 'when' clause"); clauses = new(when_clause_t, .var=var, .tag_name=tag_name, .body=body, .next=clauses); tmp = pos; @@ -893,6 +897,7 @@ PARSER(parse_when) { const char *else_start = pos; if (get_indent(ctx->file, tmp) == starting_indent && match_word(&tmp, "else")) { pos = tmp; + expect_str(ctx, start, &pos, ":", "I expected a ':' here"); else_body = expect(ctx, else_start, &pos, parse_opt_indented_block, "I expected a body for this 'else'"); } return NewAST(ctx->file, start, pos, When, .subject=subject, .clauses=clauses, .else_body=else_body); @@ -911,6 +916,7 @@ PARSER(parse_for) { } expect_str(ctx, start, &pos, "in", "I expected an 'in' for this 'for'"); ast_t *iter = expect(ctx, start, &pos, parse_expr, "I expected an iterable value for this 'for'"); + expect_str(ctx, start, &pos, ":", "I expected a ':' here"); match(&pos, "do"); // optional ast_t *body = expect(ctx, start, &pos, parse_opt_indented_block, "I expected a body for this 'for'"); @@ -919,6 +925,7 @@ PARSER(parse_for) { ast_t *empty = NULL; if (match_word(&else_start, "else") && get_indent(ctx->file, else_start) == starting_indent) { pos = else_start; + expect_str(ctx, start, &pos, ":", "I expected a ':' here"); empty = expect(ctx, pos, &pos, parse_opt_indented_block, "I expected a body for this 'else'"); } return NewAST(ctx->file, start, pos, For, .index=value ? index : NULL, .value=value ? value : index, .iter=iter, .body=body, .empty=empty); @@ -929,7 +936,7 @@ PARSER(parse_while) { const char *start = pos; if (!match_word(&pos, "while")) return NULL; ast_t *condition = expect(ctx, start, &pos, parse_expr, "I don't see a viable condition for this 'while'"); - match(&pos, "do"); // optional + expect_str(ctx, start, &pos, ":", "I expected a ':' here"); ast_t *body = expect(ctx, start, &pos, parse_opt_indented_block, "I expected a body for this 'while'"); const char *tmp = pos; whitespace(&tmp); @@ -1603,7 +1610,7 @@ PARSER(parse_file_body) { } PARSER(parse_struct_def) { - // struct Foo(...) \n body + // struct Foo(...) [: \n body] const char *start = pos; if (!match_word(&pos, "struct")) return NULL; @@ -1636,13 +1643,15 @@ PARSER(parse_struct_def) { expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this struct"); - const char *ns_pos = pos; - whitespace(&ns_pos); - int64_t ns_indent = get_indent(ctx->file, ns_pos); ast_t *namespace = NULL; - if (ns_indent > starting_indent) { - pos = ns_pos; - namespace = optional(ctx, &pos, parse_namespace); + if (match(&pos, ":")) { + const char *ns_pos = pos; + whitespace(&ns_pos); + int64_t ns_indent = get_indent(ctx->file, ns_pos); + if (ns_indent > starting_indent) { + pos = ns_pos; + namespace = optional(ctx, &pos, parse_namespace); + } } if (!namespace) namespace = NewAST(ctx->file, pos, pos, Block, .statements=NULL); @@ -1650,7 +1659,7 @@ PARSER(parse_struct_def) { } ast_t *parse_enum_def(parse_ctx_t *ctx, const char *pos) { - // tagged union: enum Foo[a, b(x:Int,y:Int)=5, ...] \n namespace + // tagged union: enum Foo(a, b(x:Int,y:Int)=5, ...) [: \n namespace] const char *start = pos; if (!match_word(&pos, "enum")) return NULL; int64_t starting_indent = get_indent(ctx->file, pos); @@ -1713,13 +1722,15 @@ ast_t *parse_enum_def(parse_ctx_t *ctx, const char *pos) { REVERSE_LIST(tags); - const char *ns_pos = pos; - whitespace(&ns_pos); - int64_t ns_indent = get_indent(ctx->file, ns_pos); ast_t *namespace = NULL; - if (ns_indent > starting_indent) { - pos = ns_pos; - namespace = optional(ctx, &pos, parse_namespace); + if (match(&pos, ":")) { + const char *ns_pos = pos; + whitespace(&ns_pos); + int64_t ns_indent = get_indent(ctx->file, ns_pos); + if (ns_indent > starting_indent) { + pos = ns_pos; + namespace = optional(ctx, &pos, parse_namespace); + } } if (!namespace) namespace = NewAST(ctx->file, pos, pos, Block, .statements=NULL); @@ -1729,7 +1740,7 @@ ast_t *parse_enum_def(parse_ctx_t *ctx, const char *pos) { PARSER(parse_lang_def) { const char *start = pos; - // lang Name [namespace...] + // lang Name: [namespace...] if (!match_word(&pos, "lang")) return NULL; int64_t starting_indent = get_indent(ctx->file, pos); spaces(&pos); @@ -1738,13 +1749,15 @@ PARSER(parse_lang_def) { parser_err(ctx, start, pos, "I expected a name for this lang"); spaces(&pos); - const char *ns_pos = pos; - whitespace(&ns_pos); - int64_t ns_indent = get_indent(ctx->file, ns_pos); ast_t *namespace = NULL; - if (ns_indent > starting_indent) { - pos = ns_pos; - namespace = optional(ctx, &pos, parse_namespace); + if (match(&pos, ":")) { + const char *ns_pos = pos; + whitespace(&ns_pos); + int64_t ns_indent = get_indent(ctx->file, ns_pos); + if (ns_indent > starting_indent) { + pos = ns_pos; + namespace = optional(ctx, &pos, parse_namespace); + } } if (!namespace) namespace = NewAST(ctx->file, pos, pos, Block, .statements=NULL); @@ -1843,9 +1856,10 @@ PARSER(parse_func_def) { type_ast_t *ret_type = NULL; spaces(&pos); - if (match(&pos, "->") || match(&pos, ":")) + if (match(&pos, "->")) ret_type = optional(ctx, &pos, parse_type); + expect_str(ctx, start, &pos, ":", "I expected a ':' here"); ast_t *body = expect(ctx, start, &pos, parse_opt_indented_block, "This function needs a body block"); return NewAST(ctx->file, start, pos, FunctionDef, diff --git a/test/arrays.tm b/test/arrays.tm index be767fa..de1771b 100644 --- a/test/arrays.tm +++ b/test/arrays.tm @@ -1,9 +1,9 @@ -func main() - if yes +func main(): + if yes: >> [:Num32] = [] : [Num32] - if yes + if yes: >> arr := [10, 20, 30] = [10, 20, 30] @@ -16,18 +16,18 @@ func main() = 3 sum := 0 - for x in arr + for x in arr: sum += x >> sum = 60 str := "" - for i,x in arr + for i,x in arr: str ++= "({i},{x})" >> str = "(1,10)(2,20)(3,30)" - if yes + if yes: >> arr := [10, 20] ++ [30, 40] = [10, 20, 30, 40] @@ -39,7 +39,7 @@ func main() >> arr = [10, 20, 30, 40, 50, 60, 70] - if yes + if yes: >> arr := [10, 20] >> copy := arr >> arr ++= 30 @@ -48,7 +48,7 @@ func main() >> copy = [10, 20] - if yes + if yes: >> [10*i for i in 5] = [10, 20, 30, 40, 50] @@ -61,7 +61,7 @@ func main() >> [x for x in y if x > 1 for y in [3, 4, 5] if y < 5] = [2, 3, 2, 3, 4] - if yes + if yes: >> arr := @[10, 20] >> copy := arr[] >> arr:insert(30) @@ -70,12 +70,12 @@ func main() >> copy = [10, 20] - if yes + if yes: >> arr := [10, 20, 30] >> arr:reversed() = [30, 20, 10] - if yes + if yes: >> nums := [10, -20, 30] // Sorted function doesn't mutate original: >> nums:sorted() @@ -96,20 +96,20 @@ func main() >> ["A", "B", "C"]:sample(10, [1.0, 0.5, 0.0]) - if yes + if yes: >> heap := [Int.random(max=50) for _ in 10] >> heap:heapify() >> heap sorted := [:Int] - while #heap > 0 + while #heap > 0: sorted:insert(heap:heap_pop()) >> sorted == sorted:sorted() = yes - for _ in 10 + for _ in 10: heap:heap_push(Int.random(max=50)) >> heap sorted = [:Int] - while #heap > 0 + while #heap > 0: sorted:insert(heap:heap_pop()) >> sorted == sorted:sorted() = yes diff --git a/test/corecursive_func.tm b/test/corecursive_func.tm index 22ffd62..a5c13dd 100644 --- a/test/corecursive_func.tm +++ b/test/corecursive_func.tm @@ -1,15 +1,15 @@ -func ping(x:Int)->[Text] - if x > 0 +func ping(x:Int)->[Text]: + if x > 0: return ["ping: {x}"] ++ pong(x-1) - else + else: return ["ping: {x}"] -func pong(x:Int)->[Text] - if x > 0 +func pong(x:Int)->[Text]: + if x > 0: return ["pong: {x}"] ++ ping(x-1) - else + else: return ["pong: {x}"] -func main() +func main(): >> ping(3) = ["ping: 3", "pong: 2", "ping: 1", "pong: 0"] diff --git a/test/enums.tm b/test/enums.tm index c06862a..1bc2620 100644 --- a/test/enums.tm +++ b/test/enums.tm @@ -1,6 +1,6 @@ enum Foo(Zero, One(x:Int), Two(x,y:Int)) -func main() +func main(): >> Foo.Zero = Foo.Zero >> Foo.One(123) @@ -27,9 +27,9 @@ func main() >> t[Foo.Zero] = "missing" - when x is o:One + when x is o:One: >> o.x = 123 - else + else: fail("Oops") diff --git a/test/extern.tm b/test/extern.tm index 896c573..da3b182 100644 --- a/test/extern.tm +++ b/test/extern.tm @@ -1,5 +1,5 @@ extern CORD_cat:func(a:Text, b:Text)->Text -func main() +func main(): >> CORD_cat("hello ", "world") = "hello world" diff --git a/test/for.tm b/test/for.tm index 6365638..a050c89 100644 --- a/test/for.tm +++ b/test/for.tm @@ -1,35 +1,35 @@ -func all_nums(nums:[Int])->Text +func all_nums(nums:[Int])->Text: result := "" - for num in nums + for num in nums: result ++= "{num}," - else + else: return "EMPTY" return result -func labeled_nums(nums:[Int])->Text +func labeled_nums(nums:[Int])->Text: result := "" - for i,num in nums + for i,num in nums: result ++= "{i}:{num}," - else + else: return "EMPTY" return result -func table_str(t:{Text:Text})->Text +func table_str(t:{Text:Text})->Text: str := "" - for k,v in t + for k,v in t: str ++= "{k}:{v}," - else return "EMPTY" + else: return "EMPTY" return str -func table_key_str(t:{Text:Text})->Text +func table_key_str(t:{Text:Text})->Text: str := "" - for k in t + for k in t: str ++= "{k}," - else return "EMPTY" + else: return "EMPTY" return str -func main() +func main(): >> all_nums([10,20,30]) = "10,20,30," >> all_nums([:Int]) diff --git a/test/functions.tm b/test/functions.tm index 13cdee5..426c33b 100644 --- a/test/functions.tm +++ b/test/functions.tm @@ -1,10 +1,10 @@ -func add(x:Int, y:Int)->Int +func add(x:Int, y:Int)->Int: return x + y -func cached_heap(x:Int; cached)->@Int +func cached_heap(x:Int; cached)->@Int: return @x -func main() +func main(): >> add(3, 5) = 8 diff --git a/test/integers.tm b/test/integers.tm index 9b573aa..94ab8ac 100644 --- a/test/integers.tm +++ b/test/integers.tm @@ -1,4 +1,4 @@ -func main() +func main(): >> 2 + 3 = 5 @@ -27,7 +27,7 @@ func main() = 1 nums := "" - for x in 5 + for x in 5: nums ++= "{x}," >> nums = "1,2,3,4,5," diff --git a/test/lambdas.tm b/test/lambdas.tm index a445540..0ec0826 100644 --- a/test/lambdas.tm +++ b/test/lambdas.tm @@ -1,13 +1,13 @@ -func make_adder(x:Int)-> func(y:Int)->Int +func make_adder(x:Int)-> func(y:Int)->Int: return func(y:Int) x + y -func suffix_fn(fn:func(t:Text)->Text, suffix:Text)->func(t:Text)->Text +func suffix_fn(fn:func(t:Text)->Text, suffix:Text)->func(t:Text)->Text: return func(t:Text) fn(t)++suffix -func mul_func(n:Int, fn:func(x:Int)->Int)-> func(x:Int)->Int +func mul_func(n:Int, fn:func(x:Int)->Int)-> func(x:Int)->Int: return func(x:Int) n*fn(x) -func main() +func main(): >> add_one := func(x:Int) x + 1 >> add_one(10) = 11 diff --git a/test/lang.tm b/test/lang.tm index ad09b60..3dea6de 100644 --- a/test/lang.tm +++ b/test/lang.tm @@ -1,6 +1,6 @@ -lang HTML +lang HTML: HEADER := $HTML{}"" - func escape(t:Text)->HTML + func escape(t:Text)->HTML: t = t:replace("&", "&") t = t:replace("<", "<") t = t:replace(">", ">") @@ -8,13 +8,13 @@ lang HTML t = t:replace("'", "'") return HTML.from_unsafe_text(t) - func escape_int(i:Int)->HTML + func escape_int(i:Int)->HTML: return HTML.from_unsafe_text("{i}") - func paragraph(content:HTML)->HTML + func paragraph(content:HTML)->HTML: return $HTML{}"
{content}
" -func main() +func main(): >> HTML.HEADER = $HTML"" diff --git a/test/minmax.tm b/test/minmax.tm index c3af68d..c2f070f 100644 --- a/test/minmax.tm +++ b/test/minmax.tm @@ -1,9 +1,9 @@ -struct Foo(x:Int, y:Int) - func len(f:Foo)->Num +struct Foo(x:Int, y:Int): + func len(f:Foo)->Num: return Num.sqrt(f.x*f.x + f.y*f.y) -func main() +func main(): >> 3 _min_ 5 = 3 >> 5 _min_ 3 diff --git a/test/nums.tm b/test/nums.tm index 4e9c81a..9495974 100644 --- a/test/nums.tm +++ b/test/nums.tm @@ -1,4 +1,4 @@ -func main() +func main(): >> n := 1.5 = 1.5 diff --git a/test/reductions.tm b/test/reductions.tm index 6ed7823..97ec4af 100644 --- a/test/reductions.tm +++ b/test/reductions.tm @@ -1,6 +1,6 @@ struct Foo(x,y:Int) -func main() +func main(): >> (+) [10, 20, 30] = 60 diff --git a/test/structs.tm b/test/structs.tm index 9fe2f6a..840ee84 100644 --- a/test/structs.tm +++ b/test/structs.tm @@ -4,7 +4,7 @@ struct Mixed(x:Int, text:Text) struct LinkedList(x:Int, next=!LinkedList) struct Password(text:Text; secret) -func test_literals() +func test_literals(): >> x := Pair(10, 20) = Pair(x=10, y=20) >> y := Pair(y=20, 10) @@ -14,7 +14,7 @@ func test_literals() >> x == Pair(-1, -2) = no -func test_metamethods() +func test_metamethods(): >> x := Pair(10, 20) >> y := Pair(100, 200) >> x == y @@ -32,7 +32,7 @@ func test_metamethods() >> t2[y] = "missing" -func test_mixed() +func test_mixed(): >> x := Mixed(10, "Hello") >> y := Mixed(99, "Hello") >> x == y @@ -49,7 +49,7 @@ func test_mixed() >> t[y] = "missing" -func main() +func main(): test_literals() test_metamethods() test_mixed() diff --git a/test/tables.tm b/test/tables.tm index 2ba4271..8609e52 100644 --- a/test/tables.tm +++ b/test/tables.tm @@ -1,4 +1,4 @@ -func main() +func main(): >> t := {"one":1, "two":2; default=999} = {"one":1, "two":2; default=999} @@ -10,7 +10,7 @@ func main() = 999 t_str := "" - for k,v in t + for k,v in t: t_str ++= "({k}:{v})" >> t_str = "(one:1)(two:2)" @@ -45,7 +45,7 @@ func main() = ?%{"one":1, "two":2; default=999} t2_str := "" - for k,v in t2 + for k,v in t2: t2_str ++= "({k}:{v})" >> t2_str = "(three:3)" diff --git a/test/text.tm b/test/text.tm index bb2cc14..9c451b8 100644 --- a/test/text.tm +++ b/test/text.tm @@ -1,4 +1,4 @@ -func main() +func main(): >> str := "Hello Amélie!" >> str:upper() = "HELLO AMÉLIE!" diff --git a/test/use.tm b/test/use.tm index a759180..95ab3c7 100644 --- a/test/use.tm +++ b/test/use.tm @@ -1,9 +1,9 @@ imported := use ./use_import -func asdf()->imported.ImportedType +func asdf()->imported.ImportedType: return imported.get_value() -func main() +func main(): >> [:imported.ImportedType] >> asdf() = ImportedType(name="Hello") diff --git a/test/use_import.tm b/test/use_import.tm index fb5f71e..3b8e674 100644 --- a/test/use_import.tm +++ b/test/use_import.tm @@ -1,7 +1,7 @@ struct ImportedType(name:Text) -func get_value()->ImportedType +func get_value()->ImportedType: return ImportedType("Hello") -func main() +func main(): pass