harec

[hare] Hare compiler, written in C11 for POSIX OSs
Log | Files | Refs | README | LICENSE

commit b815a0d1dc874df2f06ab680b5ceb74413ae4da8
parent 6cc3ee5c3dc09c4a4b0897e1f2abee120eb9d93d
Author: Sebastian <sebastian@sebsite.pw>
Date:   Sun,  5 Feb 2023 00:33:05 -0500

parse: only allow exprs which mutate scope in compound/for

For example, the expression `if (foo) let bar = 0` will now error. This
applies to binding expressions and defer expressions. The one exception
is that binding expressions are allowed as the initializer of a for
expression.

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

Diffstat:
Minclude/parse.h | 1+
Msrc/parse.c | 35+++++++++++++++++++++++------------
Mtests/14-switch.ha | 8++++++++
Mtests/16-defer.ha | 14++++++++++++++
Mtests/18-match.ha | 9+++++++++
Mtests/34-declarations.ha | 4++++
6 files changed, 59 insertions(+), 12 deletions(-)

diff --git a/include/parse.h b/include/parse.h @@ -11,5 +11,6 @@ void parse(struct lexer *lexer, struct ast_subunit *unit); bool parse_identifier(struct lexer *lexer, struct identifier *ident, bool trailing); struct ast_type *parse_type(struct lexer *lexer); struct ast_expression *parse_expression(struct lexer *lexer); +struct ast_expression *parse_statement(struct lexer *lexer); #endif diff --git a/src/parse.c b/src/parse.c @@ -1912,7 +1912,7 @@ parse_switch_expression(struct lexer *lexer) struct ast_expression_list *cur = &_case->exprs; struct ast_expression_list **next = &cur->next; while (exprs) { - cur->expr = parse_expression(lexer); + cur->expr = parse_statement(lexer); want(lexer, T_SEMICOLON, &tok); switch (lex(lexer, &tok)) { @@ -1997,7 +1997,7 @@ parse_match_expression(struct lexer *lexer) struct ast_expression_list *cur = &_case->exprs; struct ast_expression_list **next = &cur->next; while (exprs) { - cur->expr = parse_expression(lexer); + cur->expr = parse_statement(lexer); want(lexer, T_SEMICOLON, &tok); switch (lex(lexer, &tok)) { @@ -2251,7 +2251,7 @@ parse_compound_expression(struct lexer *lexer) bool more = true; while (more) { - cur->expr = parse_expression(lexer); + cur->expr = parse_statement(lexer); want(lexer, T_SEMICOLON, &tok); @@ -2289,17 +2289,12 @@ parse_expression(struct lexer *lexer) struct ast_expression *value; switch (lex(lexer, &tok)) { - case T_LET: - case T_CONST: - unlex(lexer, &tok); - return parse_binding_list(lexer, false); case T_STATIC: - return parse_static_expression(lexer, true); + return parse_static_expression(lexer, false); case T_BREAK: case T_CONTINUE: case T_RETURN: case T_YIELD: - case T_DEFER: case T_FOR: case T_IF: case T_LBRACE: @@ -2314,9 +2309,6 @@ parse_expression(struct lexer *lexer) unlex(lexer, &tok); value = parse_control_expression(lexer); break; - case T_DEFER: - value = parse_deferred_expression(lexer); - break; case T_FOR: unlex(lexer, &tok); value = parse_for_expression(lexer); @@ -2409,6 +2401,25 @@ parse_expression(struct lexer *lexer) } } +struct ast_expression * +parse_statement(struct lexer *lexer) +{ + struct token tok; + switch (lex(lexer, &tok)) { + case T_LET: + case T_CONST: + unlex(lexer, &tok); + return parse_binding_list(lexer, false); + case T_STATIC: + return parse_static_expression(lexer, true); + case T_DEFER: + return parse_deferred_expression(lexer); + default: + unlex(lexer, &tok); + return parse_expression(lexer); + } +} + static char * parse_attr_symbol(struct lexer *lexer) { diff --git a/tests/14-switch.ha b/tests/14-switch.ha @@ -61,10 +61,18 @@ fn str_switching() void = { assert(result == true); }; +fn binding() void = { + switch (1) { + case => + let x = 42; + }; +}; + export fn main() void = { basics(); termination(); tagged_result(); str_switching(); + binding(); // TODO: Test exhaustiveness and dupe detection }; diff --git a/tests/16-defer.ha b/tests/16-defer.ha @@ -1,4 +1,5 @@ use rt; +use rt::{compile, exited, EXIT_SUCCESS}; let x: int = 10; @@ -50,6 +51,18 @@ fn control() void = { assert(x == 1); }; +fn reject() void = { + let failures = [ + "export fn main() void = defer 0;", + "export fn main() void = { if (true) defer 0; };", + "export fn main() void = { for (defer 0; true; true) void; };", + ]; + + for (let i = 0z; i < len(failures); i += 1) { + assert(compile(failures[i]) as exited != EXIT_SUCCESS); + }; +}; + fn noreturn() void = { defer x = 30; for (true) { @@ -69,5 +82,6 @@ export fn main() void = { scope(); loops(); control(); + reject(); noreturn(); }; diff --git a/tests/18-match.ha b/tests/18-match.ha @@ -230,6 +230,14 @@ fn alignment_conversion() void = { assert(x as int == 4321); }; +fn binding() void = { + let x: (int | void) = void; + match (x) { + case => + let x = 42; + }; +}; + export fn main() void = { tagged(); termination(); @@ -241,5 +249,6 @@ export fn main() void = { transitivity(); numeric(); alignment_conversion(); + binding(); // TODO: Test exhaustiveness and dupe detection }; diff --git a/tests/34-declarations.ha b/tests/34-declarations.ha @@ -229,6 +229,10 @@ fn reject() void = { "let a: int = 4; type x = struct { y: str, z: a};", "let a: int = 4; type x = union { y: str, z: a};", "let a: int = 4; fn x(y: str, z: a) void = { void; };", + + // binding not directly in compound expression + "export fn main() void = let a = 4;", + "export fn main() void = { if (true) let a = 4; };", ]; for (let i = 0z; i < len(failures); i += 1) {