about summary refs log tree commit diff
path: root/boot
diff options
context:
space:
mode:
Diffstat (limited to 'boot')
-rw-r--r--boot/parse.c75
-rw-r--r--boot/tree.c37
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)
 {
@@ -618,6 +635,50 @@ parser_statement_conditional(struct Parser* p, struct Parser_Error* error)
 }
 
 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)
 {
     struct Token token = parser_peek(p);
@@ -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;