From a9b5fef2eb126500974a1cfe9ce4a8f7cc6e0490 Mon Sep 17 00:00:00 2001 From: Mel Date: Tue, 3 Jun 2025 01:20:50 +0200 Subject: Parse all 4 types of for-loops Signed-off-by: Mel --- boot/parse.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++--------- boot/tree.c | 37 ++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 11 deletions(-) diff --git a/boot/parse.c b/boot/parse.c index 3870e6c..07b6e58 100644 --- a/boot/parse.c +++ b/boot/parse.c @@ -180,6 +180,23 @@ parser_end_statement(struct Parser* p, struct Parser_Error* error) parser_next(p); } +bool +parser_could_be_variable_declaration(struct Parser* p) +{ + // NOTE: these can be a variable declaration: + // x uint = 123 + // me, them Obj = create() + // otherwise without a type, it is instead counted as an assignment: + // a = "hi!" + + struct Token first = parser_peek(p); + struct Token second = parser_peek_further(p); + + bool first_matches = token_is(&first, TOKEN_NAME); + bool second_matches = token_is(&second, TOKEN_COMMA) || token_is(&second, TOKEN_NAME); + return first_matches && second_matches; +} + struct Block_Node parser_block_node(struct Parser* p, struct Parser_Error* error) { @@ -617,6 +634,50 @@ parser_statement_conditional(struct Parser* p, struct Parser_Error* error) if_token.location); } +struct Statement* +parser_statement_loop(struct Parser* p, struct Parser_Error* error) +{ + struct Token for_token = CHECK(parser_need(p, TOKEN_WORD_FOR, error)); + + // these are the possible for loop variants: + // * `for String name = collection {}`, as iteration over container + // * `for check() {}`, as a simple while-style loop + // * `for u8 i = 0, i < 10, i++ {}`, as a c-style semi-semi loop + // * `for {}`, as an infinite loop + + struct Statement* declaration = nil; + struct Expression *condition = nil, *iteration = nil; + + // c-style or iterator-style + if (parser_could_be_variable_declaration(p)) { + declaration = CHECK(parser_statement_declaration(p, error)); + + // c-style + if (parser_probe(p, TOKEN_COMMA)) { + parser_next(p); + condition = CHECK(parser_expression(p, error)); + CHECK(parser_need(p, TOKEN_COMMA, error)); + + iteration = CHECK(parser_expression(p, error)); + } + } + + // while-style + if (!parser_probe(p, TOKEN_CURLY_OPEN)) condition = CHECK(parser_expression(p, error)); + + struct Block_Node body = CHECK(parser_block_node(p, error)); + struct Span span = span_merge(for_token.span, body.span); + union Statement_Value value = { + .loop = { + .declaration = declaration, + .condition = condition, + .iteration = iteration, + .body = body, + }, + }; + return statement_new(STATEMENT_LOOP, value, span, for_token.location); +} + struct Statement* parser_statement(struct Parser* p, struct Parser_Error* error) { @@ -628,20 +689,12 @@ parser_statement(struct Parser* p, struct Parser_Error* error) token = parser_peek(p); } - if (token_is(&token, TOKEN_NAME)) { - // NOTE: these can be a variable declaration: - // x uint = 123 - // me, them Obj = create() - // otherwise without a type, it is counted as an assignment: - // a = "hi!" - - struct Token next_token = parser_peek_further(p); - if (next_token.kind == TOKEN_COMMA || next_token.kind == TOKEN_NAME) - return parser_statement_declaration(p, error); - } + if (parser_could_be_variable_declaration(p)) return parser_statement_declaration(p, error); if (token_is(&token, TOKEN_WORD_IF)) return parser_statement_conditional(p, error); + if (token_is(&token, TOKEN_WORD_FOR)) return parser_statement_loop(p, error); + if (token_is(&token, TOKEN_CURLY_OPEN)) { // a block statement. struct Block_Node block = CHECK(parser_block_node(p, error)); diff --git a/boot/tree.c b/boot/tree.c index 64eb23b..3ae90b8 100644 --- a/boot/tree.c +++ b/boot/tree.c @@ -592,6 +592,7 @@ enum Statement_Kind // NOTE: a block could be an expression in the future. STATEMENT_BLOCK, STATEMENT_CONDITIONAL, + STATEMENT_LOOP, }; struct Statement_Value_Expression @@ -624,12 +625,25 @@ struct Statement_Value_Conditional uint condition_count; }; +struct Statement_Value_Loop +{ + // exists for iterator-style + c-style loops. + struct Statement* declaration; + // exists for all loop types, except for infinite loops. + struct Expression* condition; + // exists for c-style loops. + struct Expression* iteration; + + struct Block_Node body; +}; + union Statement_Value { struct Statement_Value_Expression expression; struct Statement_Value_Declaration declaration; struct Statement_Value_Block block; struct Statement_Value_Conditional conditional; + struct Statement_Value_Loop loop; }; struct Statement @@ -731,6 +745,29 @@ statement_print(const struct Statement* statement) printf(")"); break; } + case STATEMENT_LOOP: { + printf("(loop "); + if (statement->value.loop.declaration) { + printf("(declaration "); + statement_print(statement->value.loop.declaration); + printf(") "); + } + + if (statement->value.loop.condition) { + printf("(condition "); + expression_print(statement->value.loop.condition); + printf(") "); + } + + if (statement->value.loop.iteration) { + printf("(iteration "); + expression_print(statement->value.loop.iteration); + printf(") "); + } + + block_node_print(&statement->value.loop.body); + break; + } default: failure("unexpected statement kind passed to `statement_print`"); break; -- cgit 1.4.1