hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 994e0ebfdb87796b0d02bd267eb9846ace2e999a
parent 0ef0a08f122392e3a93a6e1c12d3afb68df48489
Author: Sebastian <sebastian@sebsite.pw>
Date:   Fri, 29 Sep 2023 22:19:56 -0400

hare::parse: disallow binding/defer outside compound/for

Signed-off-by: Sebastian <sebastian@sebsite.pw>

Diffstat:
Mhare/parse/+test/expr_test.ha | 13++++++++-----
Mhare/parse/+test/loc.ha | 7+++----
Mhare/parse/expr.ha | 95++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
3 files changed, 66 insertions(+), 49 deletions(-)

diff --git a/hare/parse/+test/expr_test.ha b/hare/parse/+test/expr_test.ha @@ -185,7 +185,10 @@ }; @test fn defer_expr() void = { - roundtrip("export fn main() void = defer foo();\n"); + roundtrip("export fn main() void = { + defer foo(); +}; +"); }; @test fn for_expr() void = { @@ -258,9 +261,9 @@ case 1234, 4321 => return y; case 1337 => - return z; + let x = 0; case => - return q; + defer x; }; }; "); @@ -284,9 +287,9 @@ case let s: matchdata => return y; case str => - return z; + let x = 0; case => - return q; + defer x; }; }; "); diff --git a/hare/parse/+test/loc.ha b/hare/parse/+test/loc.ha @@ -43,8 +43,6 @@ fn expr_testloc(srcs: str...) void = for (let i = 0z; i < len(srcs); i += 1) { expr_testloc("foo is bar", "foo as bar"); expr_testloc("foo = bar"); expr_testloc("foo * bar", "foo && bar"); - expr_testloc("let foo: bar = baz", "let foo: bar = baz, quux = quuux", - "const foo: bar = baz", "const foo: bar = baz, quux = quuux"); expr_testloc("break", "break :foo"); expr_testloc("foo(bar)"); expr_testloc("foo: bar"); @@ -55,7 +53,6 @@ fn expr_testloc(srcs: str...) void = for (let i = 0z; i < len(srcs); i += 1) { expr_testloc("[foo, bar]"); expr_testloc("123", "-123.456", "123z", "123e+3"); expr_testloc("continue", "continue :foo"); - expr_testloc("defer foo"); expr_testloc("delete(foo[bar])", "delete(foo[bar..baz])"); expr_testloc("for (let foo = 0; bar; baz) quux", "for (let bar = 0; baz; quux) quuux"); @@ -63,7 +60,9 @@ fn expr_testloc(srcs: str...) void = for (let i = 0z; i < len(srcs); i += 1) { expr_testloc("if (foo) bar", "if (foo) bar else baz"); expr_testloc("insert(foo[0], bar)", "insert(foo[0], bar, baz)"); expr_testloc("len(foo)"); - expr_testloc("{ foo; bar; }"); + expr_testloc("{ foo; bar; }", "{ defer foo; }", + "{ let foo: bar = baz; }", "{ let foo: bar = baz, quux = quuux; }", + "{ const foo: bar = baz; }", "{ const foo: bar = baz, quux = quuux; }"); expr_testloc("match (foo) { case => bar; }"); expr_testloc("offset(foo)"); expr_testloc("foo?", "foo!"); diff --git a/hare/parse/expr.ha b/hare/parse/expr.ha @@ -21,8 +21,7 @@ export fn expr(lexer: *lex::lexer) (ast::expr | error) = { ]; const ex = match (peek(lexer, ltok::IF, ltok::FOR, ltok::BREAK, - ltok::CONTINUE, ltok::RETURN, ltok::LET, ltok::CONST, - ltok::YIELD)?) { + ltok::CONTINUE, ltok::RETURN, ltok::YIELD)?) { case void => yield binarithm(lexer, void, 0)?; case let tok: lex::token => @@ -33,8 +32,6 @@ export fn expr(lexer: *lex::lexer) (ast::expr | error) = { yield for_expr(lexer)?; case ltok::BREAK, ltok::CONTINUE, ltok::RETURN => yield control(lexer)?; - case ltok::LET, ltok::CONST => - yield binding(lexer, false)?; case ltok::YIELD => yield yield_expr(lexer)?; case => abort(); // Invariant @@ -367,7 +364,7 @@ fn builtin(lexer: *lex::lexer) (ast::expr | error) = { const tok = match (peek(lexer, ltok::ALIGN, ltok::ALLOC, ltok::APPEND, ltok::FREE, ltok::DELETE, ltok::ABORT, ltok::ASSERT, ltok::INSERT, ltok::STATIC, ltok::SIZE, ltok::LEN, ltok::OFFSET, - ltok::DEFER, ltok::VASTART, ltok::VAARG, ltok::VAEND)?) { + ltok::VASTART, ltok::VAARG, ltok::VAEND)?) { case let tok: lex::token => yield tok; case void => @@ -385,37 +382,10 @@ fn builtin(lexer: *lex::lexer) (ast::expr | error) = { case ltok::ABORT, ltok::ASSERT => return assert_expr(lexer, false); case ltok::STATIC => - want(lexer, ltok::STATIC)?; - let tok = match (peek(lexer, ltok::LET, ltok::CONST, - ltok::ABORT, ltok::ASSERT, ltok::APPEND, ltok::INSERT, - ltok::DELETE)?) { - case let tok: lex::token => - yield tok; - case void => - // TODO: The following is lame: - return syntaxerr(tok.2, "Expected let, const, or assert"); - }; - switch (tok.0) { - case ltok::LET, ltok::CONST => - return binding(lexer, true); - case ltok::ABORT, ltok::ASSERT => - return assert_expr(lexer, true); - case ltok::APPEND, ltok::INSERT => - return append_insert_expr(lexer, true); - case ltok::DELETE => - return delete_expr(lexer, true); - case => abort(); - }; + want(lexer, ltok::STATIC)!; + return static_expr(lexer); case ltok::ALIGN, ltok::SIZE, ltok::LEN, ltok::OFFSET => return measurement(lexer); - case ltok::DEFER => - want(lexer, ltok::DEFER)?; - let expr = alloc(expr(lexer)?); - return ast::expr { - start = tok.2, - end = lex::prevloc(lexer), - expr = expr: ast::defer_expr, - }; case ltok::VASTART => want(lexer, ltok::VASTART)?; want(lexer, ltok::LPAREN)?; @@ -644,8 +614,7 @@ fn compound_expr(lexer: *lex::lexer) (ast::expr | error) = { }; for (true) { - append(items, alloc(expr(lexer)?)); - want(lexer, ltok::SEMICOLON)?; + append(items, alloc(stmt(lexer)?)); if (try(lexer, ltok::RBRACE)? is lex::token) { break; }; @@ -661,6 +630,38 @@ fn compound_expr(lexer: *lex::lexer) (ast::expr | error) = { }; }; +fn stmt(lexer: *lex::lexer) (ast::expr | error) = { + const expr = match (try(lexer, ltok::DEFER, + ltok::LET, ltok::CONST, ltok::STATIC)?) { + case let tok: lex::token => + yield switch (tok.0) { + case ltok::DEFER => + let expr = alloc(expr(lexer)?); + yield ast::expr { + start = tok.2, + end = lex::prevloc(lexer), + expr = expr: ast::defer_expr, + }; + case ltok::LET, ltok::CONST => + lex::unlex(lexer, tok); + yield binding(lexer, false)?; + case ltok::STATIC => + yield match (peek(lexer, ltok::LET, ltok::CONST)?) { + case lex::token => + yield binding(lexer, true)?; + case void => + yield static_expr(lexer)?; + }; + case => abort(); // unreachable + }; + case void => + yield expr(lexer)?; + }; + + want(lexer, ltok::SEMICOLON)?; + return expr; +}; + fn for_expr(lexer: *lex::lexer) (ast::expr | error) = { const tok = want(lexer, ltok::FOR)?; want(lexer, ltok::LPAREN)?; @@ -1069,6 +1070,22 @@ fn postfix_dot( }; }; +fn static_expr(lexer: *lex::lexer) (ast::expr | error) = { + const tok = want(lexer, ltok::ABORT, ltok::ASSERT, ltok::APPEND, + ltok::INSERT, ltok::DELETE)?; + lex::unlex(lexer, tok); + + switch (tok.0) { + case ltok::ABORT, ltok::ASSERT => + return assert_expr(lexer, true); + case ltok::APPEND, ltok::INSERT => + return append_insert_expr(lexer, true); + case ltok::DELETE => + return delete_expr(lexer, true); + case => abort(); // unreachable + }; +}; + fn switch_expr(lexer: *lex::lexer) (ast::expr | error) = { const start = want(lexer, ltok::SWITCH)?; @@ -1098,8 +1115,7 @@ fn switch_expr(lexer: *lex::lexer) (ast::expr | error) = { let exprs: []*ast::expr = []; for (true) { - append(exprs, alloc(expr(lexer)?)); - want(lexer, ltok::SEMICOLON)?; + append(exprs, alloc(stmt(lexer)?)); match (peek(lexer, ltok::CASE, ltok::RBRACE)?) { case lex::token => break; @@ -1153,8 +1169,7 @@ fn match_case(lexer: *lex::lexer) (ast::match_case | error) = { want(lexer, ltok::ARROW)?; let exprs: []*ast::expr = []; for (true) { - append(exprs, alloc(expr(lexer)?)); - want(lexer, ltok::SEMICOLON)?; + append(exprs, alloc(stmt(lexer)?)); if (peek(lexer, ltok::CASE, ltok::RBRACE)? is lex::token) { break; };