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:
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;
};