From c80020d23599a9ea7b60f83449dbb93762ca9770 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Tue, 18 Jun 2024 16:57:24 +0200 Subject: Add parsing support for basic conditionals and blocks --- Makefile | 2 +- src/lexer.c | 7 +++++++ src/main.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++- tests/conditionals.bad | 19 +++++++++++++++++ 4 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 tests/conditionals.bad diff --git a/Makefile b/Makefile index fabc1c7..b6419fd 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ SRC_DIR := src BUILD_DIR := build SRC_MAIN := $(SRC_DIR)/main.c -SRC_BAD := tests/variables.bad +SRC_BAD := tests/conditionals.bad WATCH_SRC := $(shell find $(SRC_DIR) -name "*.c" -or -name "*.s" -or -name "*.h") INC_DIRS := $(shell find $(SRC_DIR) -type d) INC_FLAGS := $(addprefix -I,$(INC_DIRS)) diff --git a/src/lexer.c b/src/lexer.c index e5bea2e..36a5636 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -27,6 +27,7 @@ typedef enum TokenKind { TOK_FALSE, // false TOK_FUN, // fun TOK_IF, // if + TOK_ELSE, // else TOK_LET, // let TOK_MATCH, // match TOK_NIL, // nil @@ -93,6 +94,7 @@ Str token_str[] = { [TOK_BREAK] = cstr("BREAK"), [TOK_CASE] = cstr("CASE"), [TOK_CONTINUE] = cstr("CONTINUE"), + [TOK_ELSE] = cstr("ELSE"), [TOK_FALSE] = cstr("FALSE"), [TOK_FUN] = cstr("FUN"), [TOK_IF] = cstr("IF"), @@ -539,6 +541,11 @@ scan_token(Scanner *scanner) { return emit_token(current, scanner, TOK_CONTINUE); } } break; + case 'e': { + if (str_has_prefix(val, cstr("else"))) { + return emit_token(current, scanner, TOK_ELSE); + } + } break; case 'f': { if (str_has_prefix(val, cstr("false"))) { return emit_token(current, scanner, TOK_FALSE); diff --git a/src/main.c b/src/main.c index 13bd469..0679558 100644 --- a/src/main.c +++ b/src/main.c @@ -75,12 +75,14 @@ typedef enum NodeKind { NODE_LET, NODE_SET, NODE_STRUCT, + NODE_IF, // Helpers. NODE_SYMBOL_IDX, NODE_TYPE, NODE_ARR_TYPE, NODE_COMPOUND_TYPE, NODE_STRUCT_FIELD, + NODE_BLOCK, } NodeKind; Str node_str[] = { @@ -120,12 +122,14 @@ Str node_str[] = { [NODE_LET] = cstr("LET"), [NODE_SET] = cstr("SET"), [NODE_STRUCT] = cstr("STRUCT DEF"), + [NODE_IF] = cstr("IF"), // Helpers. [NODE_TYPE] = cstr("TYPE"), [NODE_ARR_TYPE] = cstr("TYPE (ARR)"), [NODE_SYMBOL_IDX] = cstr("SYMBOL[IDX]"), [NODE_COMPOUND_TYPE] = cstr("TYPE (COMPOUND)"), [NODE_STRUCT_FIELD] = cstr("FIELD"), + [NODE_BLOCK] = cstr("BLOCK"), }; typedef struct Node { @@ -155,6 +159,11 @@ typedef struct Node { struct Node *var_type; struct Node *var_val; }; + struct { + struct Node *cond_if; + struct Node *cond_expr; + struct Node *cond_else; + }; struct { struct Node *field_name; struct Node *field_type; @@ -162,6 +171,7 @@ typedef struct Node { }; struct Node **struct_field; struct Node **elements; + struct Node **statements; }; bool is_ptr; } Node; @@ -222,10 +232,11 @@ void parse_string(Parser *parser); void parse_symbol(Parser *parser); void parse_keyword(Parser *parser); void parse_type(Parser *parser); +void parse_block(Parser *parser); ParseRule parse_rules[] = { [TOK_LPAREN] = {parse_grouping, NULL, PREC_NONE}, - [TOK_LCURLY] = {NULL, NULL, PREC_NONE}, + [TOK_LCURLY] = {parse_block, NULL, PREC_NONE}, [TOK_AT] = {parse_symbol, NULL, PREC_NONE}, // Arithmetic. @@ -334,6 +345,22 @@ parse_consume(Parser *parser, TokenKind kind, Str msg) { parse_emit_err(parser, parser->current, msg); } +void +parse_block(Parser *parser) { +#if DEBUG == 1 + print("parsing block "); + print_token(prev); +#endif + Node *block = node_alloc(parser, NODE_BLOCK, parser->previous); + if (!block) return; + while (!parse_match(parser, TOK_RCURLY) && !parser->panic) { + parse_expr(parser, PREC_LOW); + Node *next = array_pop(parser->nodes); + array_push(block->statements, next, parser->storage); + } + array_push(parser->nodes, block, parser->storage); +} + void parse_expr(Parser *parser, ParsePrecedence precedence) { parse_advance(parser); @@ -524,6 +551,18 @@ parse_keyword(Parser *parser) { array_push(node->struct_field, field, parser->storage); } } break; + case TOK_IF: { + node = node_alloc(parser, NODE_IF, prev); + if (!node) return; + parse_expr(parser, PREC_LOW); + node->cond_if = array_pop(parser->nodes); + parse_expr(parser, PREC_LOW); + node->cond_expr = array_pop(parser->nodes); + if (parse_match(parser, TOK_ELSE)) { + parse_expr(parser, PREC_LOW); + node->cond_else = array_pop(parser->nodes); + } + } break; default: return; // Unreachable. } array_push(parser->nodes, node, parser->storage); @@ -731,6 +770,7 @@ graph_node(Node *node) { println("\"];"); switch (node->kind) { + case NODE_BLOCK: case NODE_STRUCT_LIT: case NODE_COMPOUND_TYPE: { for (sz i = 0; i < array_size(node->elements); i++) { @@ -746,6 +786,20 @@ graph_node(Node *node) { graph_node(next); } } break; + case NODE_IF: { + if (node->cond_if) { + println("%d:e->%d:w;", node->id, node->cond_if->id); + graph_node(node->cond_if); + } + if (node->cond_expr) { + println("%d:e->%d:w;", node->id, node->cond_expr->id); + graph_node(node->cond_expr); + } + if (node->cond_else) { + println("%d:e->%d:w;", node->id, node->cond_else->id); + graph_node(node->cond_else); + } + } break; case NODE_STRUCT_FIELD: case NODE_SET: case NODE_LET: { diff --git a/tests/conditionals.bad b/tests/conditionals.bad new file mode 100644 index 0000000..45e1bd2 --- /dev/null +++ b/tests/conditionals.bad @@ -0,0 +1,19 @@ +; Basic if expressions. +if true "hello" + +; These can produce values and the result bound to a name. +let a = if (2 + 2 >= 4) 42 + +; We support a single if expression. +let b = if 0xff == 255 "hello" else "world" + +; ... but these can compound on each other +if 1 < 2 6 +else if 1 > 2 7 +else 8 + +; A block is an expression, and if raise the scope level regardless if a block +; is used or not. +if true != false { + let a = "yo" +} -- cgit v1.2.1