about summary refs log tree commit diff
path: root/boot
diff options
context:
space:
mode:
Diffstat (limited to 'boot')
-rw-r--r--boot/parse.c46
-rw-r--r--boot/tests/parse/conditionals.cskt20
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))
+))