diff options
| author | Mel <mel@rnrd.eu> | 2025-07-22 05:16:06 +0200 |
|---|---|---|
| committer | Mel <mel@rnrd.eu> | 2025-07-22 05:16:06 +0200 |
| commit | 52a6c142d7c85520b09e300283154b7ec9c6b9f3 (patch) | |
| tree | 999da0d1f036731ffcbf6274d4961a19cf697ddd /boot | |
| parent | a37262c61fd577e0e620e024007e0139929e3926 (diff) | |
| download | catskill-52a6c142d7c85520b09e300283154b7ec9c6b9f3.tar.zst catskill-52a6c142d7c85520b09e300283154b7ec9c6b9f3.zip | |
Remove ambiguity from construct expressions and if/for/while blocks
Signed-off-by: Mel <mel@rnrd.eu>
Diffstat (limited to 'boot')
| -rw-r--r-- | boot/parse.c | 46 | ||||
| -rw-r--r-- | boot/tests/parse/conditionals.cskt | 20 |
2 files changed, 65 insertions, 1 deletions
diff --git a/boot/parse.c b/boot/parse.c index 4403024..091a6f7 100644 --- a/boot/parse.c +++ b/boot/parse.c @@ -21,6 +21,9 @@ parse; \ if (!parser_error_is_none(error)) return (ret){ 0 }; +#define CONTEXT_START(context_name) p->context.context_name = true; +#define CONTEXT_END(context_name) p->context.context_name = false; + enum Parser_Error_Kind { PARSER_ERROR_NONE, @@ -152,6 +155,20 @@ parser_error_display(const struct Parser_Error* error, struct Source_File source fprintf(stderr, "\n" ANSI_RESET); } +// a parser context descibes the current surrounding structure +// of a node we're currently parsing. +struct Parser_Context +{ + // is the parser currently in a statement clause? + // used to mark that we're currently expecting a block to open + // and to avoid interpreting tokens such as `{` as an expression part. + // e.g.: + // * `if statement_clause_expression {}` + // * `while statement_clause_expression {}` + // * for expression, expression, statement_clause_expression {} + bool in_statement_clause; +}; + struct Parser { struct Lexer* lexer; @@ -159,6 +176,7 @@ struct Parser struct Source_File source_file; + struct Parser_Context context; bool had_errors; }; @@ -178,6 +196,12 @@ parser_reached_end(struct Parser* p) } bool +parser_in_statement_clause(struct Parser* p) +{ + return p->context.in_statement_clause; +} + +bool parser_lookahead_pop(struct Parser* p, struct Token* token) { struct Token head = p->lookahead[0]; @@ -461,6 +485,8 @@ parser_function_header_node(struct Parser* p, struct Parser_Error* error) // inline structure as a return type that is too long to fit on a single line. // TODO: to allow single line functions, introduce a `:` token as a replacement for the // opening brace. `fun (x int): x + 1` + // TODO: check if the statement clause logic applies here, and we can generalize this + // check away. if (token_is(&next, TOKEN_CURLY_OPEN) && token_is(&further, TOKEN_NEWLINE)) return header; header.return_type = CHECK_RETURN(parser_node_type(p, error), struct Function_Header_Node); @@ -1141,6 +1167,8 @@ parser_expression_postfix(struct Parser* p, struct Parser_Error* error) expression = CHECK(parser_expression_postfix_call(p, expression, error)); continue; case TOKEN_CURLY_OPEN: + // construct expressions are not allowed in statement clauses. + if (parser_in_statement_clause(p)) break; expression = CHECK(parser_expression_postfix_construct(p, expression, error)); continue; case TOKEN_SQUARE_OPEN: @@ -1324,7 +1352,10 @@ parser_statement_conditional(struct Parser* p, struct Parser_Error* error) struct Token if_token = parser_need(p, TOKEN_WORD_IF, error); // primary if condition + block. + CONTEXT_START(in_statement_clause); struct Expression* if_condition = CHECK(parser_expression(p, error)); + CONTEXT_END(in_statement_clause); + struct Block_Node then_block = CHECK(parser_block_node(p, error)); conditional.conditions[conditional.condition_count++] = (struct Statement_Conditional_Branch){ .when = if_condition, @@ -1341,7 +1372,11 @@ parser_statement_conditional(struct Parser* p, struct Parser_Error* error) if (parser_probe(p, TOKEN_WORD_IF)) { // else if condition + block. parser_next(p); + + CONTEXT_START(in_statement_clause); struct Expression* else_condition = CHECK(parser_expression(p, error)); + CONTEXT_END(in_statement_clause); + struct Block_Node else_block = CHECK(parser_block_node(p, error)); branch = (struct Statement_Conditional_Branch){ @@ -1376,7 +1411,10 @@ parser_statement_for(struct Parser* p, struct Parser_Error* error) // * `for i u8 = 0, i < 10, i++ {}`, as a c-style semi-semi loop // a declaration without a signifier like `var` or `let`. + CONTEXT_START(in_statement_clause); struct Bare_Declaration_Node declaration = CHECK(parser_bare_declaration_node(p, error)); + CONTEXT_END(in_statement_clause); + enum Statement_Loop_Style style = STATEMENT_LOOP_STYLE_FOR_EACH; // c-style semi-semi loop. @@ -1385,7 +1423,10 @@ parser_statement_for(struct Parser* p, struct Parser_Error* error) parser_next(p); condition = CHECK(parser_expression(p, error)); CHECK(parser_need(p, TOKEN_COMMA, error)); + + CONTEXT_START(in_statement_clause); iteration = CHECK(parser_expression(p, error)); + CONTEXT_END(in_statement_clause) style = STATEMENT_LOOP_STYLE_C; } @@ -1413,7 +1454,10 @@ parser_statement_while(struct Parser* p, struct Parser_Error* error) enum Statement_Loop_Style style = STATEMENT_LOOP_STYLE_ENDLESS; struct Expression* condition = nil; if (!parser_probe(p, TOKEN_CURLY_OPEN)) { + CONTEXT_START(in_statement_clause); condition = CHECK(parser_expression(p, error)); + CONTEXT_END(in_statement_clause); + style = STATEMENT_LOOP_STYLE_WHILE; } @@ -1601,4 +1645,4 @@ parser_do_your_thing(struct Parser* p, struct Tree* tree) } #undef CHECK -#undef CHECK_RETURN \ No newline at end of file +#undef CHECK_RETURN diff --git a/boot/tests/parse/conditionals.cskt b/boot/tests/parse/conditionals.cskt new file mode 100644 index 0000000..bf314fa --- /dev/null +++ b/boot/tests/parse/conditionals.cskt @@ -0,0 +1,20 @@ + +<<< + +if x == 5 { + return 1 +} else if x == 4 { + return 2 +} else { + return 0 +} + +>>> + +(conditional (when (expr (binary == (expr (name x)) (expr 5)))) (block + (return (expr 1)) +) (when (expr (binary == (expr (name x)) (expr 4)))) (block + (return (expr 2)) +) (always) (block + (return (expr 0)) +)) |
