From f9d0fe5ad641e453724dabc2b17634a44e198ce0 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Tue, 18 Jun 2024 19:47:39 +0200 Subject: Add enum parsing --- src/lexer.c | 12 +++++++++- src/main.c | 55 ++++++++++++++++++++++++++++++++++++-------- tests/conditionals.bad | 62 ++++++++++++++++++++++++++++++++++---------------- 3 files changed, 99 insertions(+), 30 deletions(-) diff --git a/src/lexer.c b/src/lexer.c index 36a5636..2d2b6fc 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -23,11 +23,13 @@ typedef enum TokenKind { // Keywords. TOK_BREAK, // break TOK_CASE, // case + TOK_COND, // match TOK_CONTINUE, // continue + TOK_ELSE, // else + TOK_ENUM, // enum TOK_FALSE, // false TOK_FUN, // fun TOK_IF, // if - TOK_ELSE, // else TOK_LET, // let TOK_MATCH, // match TOK_NIL, // nil @@ -93,8 +95,10 @@ Str token_str[] = { // Keywords. [TOK_BREAK] = cstr("BREAK"), [TOK_CASE] = cstr("CASE"), + [TOK_COND] = cstr("COND"), [TOK_CONTINUE] = cstr("CONTINUE"), [TOK_ELSE] = cstr("ELSE"), + [TOK_ENUM] = cstr("ENUM"), [TOK_FALSE] = cstr("FALSE"), [TOK_FUN] = cstr("FUN"), [TOK_IF] = cstr("IF"), @@ -540,11 +544,17 @@ scan_token(Scanner *scanner) { if (str_has_prefix(val, cstr("continue"))) { return emit_token(current, scanner, TOK_CONTINUE); } + if (str_has_prefix(val, cstr("cond"))) { + return emit_token(current, scanner, TOK_COND); + } } break; case 'e': { if (str_has_prefix(val, cstr("else"))) { return emit_token(current, scanner, TOK_ELSE); } + if (str_has_prefix(val, cstr("enum"))) { + return emit_token(current, scanner, TOK_ENUM); + } } break; case 'f': { if (str_has_prefix(val, cstr("false"))) { diff --git a/src/main.c b/src/main.c index ccd4877..f73c7cc 100644 --- a/src/main.c +++ b/src/main.c @@ -78,12 +78,14 @@ typedef enum NodeKind { NODE_IF, NODE_MATCH, NODE_CASE, + NODE_COND, + NODE_ENUM, // Helpers. NODE_SYMBOL_IDX, NODE_TYPE, NODE_ARR_TYPE, NODE_COMPOUND_TYPE, - NODE_STRUCT_FIELD, + NODE_VAL_FIELD, NODE_BLOCK, } NodeKind; @@ -127,12 +129,14 @@ Str node_str[] = { [NODE_IF] = cstr("IF"), [NODE_MATCH] = cstr("MATCH"), [NODE_CASE] = cstr("CASE"), + [NODE_COND] = cstr("COND"), + [NODE_ENUM] = cstr("ENUM"), // 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_VAL_FIELD] = cstr("FIELD"), [NODE_BLOCK] = cstr("BLOCK"), }; @@ -290,8 +294,10 @@ ParseRule parse_rules[] = { [TOK_LET] = {parse_keyword, NULL, PREC_NONE}, [TOK_SET] = {parse_keyword, NULL, PREC_NONE}, [TOK_STRUCT] = {parse_keyword, NULL, PREC_NONE}, + [TOK_ENUM] = {parse_keyword, NULL, PREC_NONE}, [TOK_IF] = {parse_keyword, NULL, PREC_NONE}, [TOK_MATCH] = {parse_keyword, NULL, PREC_NONE}, + [TOK_COND] = {parse_keyword, NULL, PREC_NONE}, [TOK_WHILE] = {parse_keyword, NULL, PREC_NONE}, [TOK_CONTINUE] = {parse_keyword, NULL, PREC_NONE}, [TOK_BREAK] = {parse_keyword, NULL, PREC_NONE}, @@ -535,14 +541,15 @@ parse_keyword(Parser *parser) { while (!parse_match(parser, TOK_RCURLY) && !parser->panic) { Node *field = - node_alloc(parser, NODE_STRUCT_FIELD, parser->current); + node_alloc(parser, NODE_VAL_FIELD, parser->current); if (!field) return; parse_consume( parser, TOK_SYMBOL, cstr("expected symbol name on struct definition")); parse_symbol(parser); field->field_name = array_pop(parser->nodes); - if (field->field_name->next) { + if (field->field_name->next || + field->field_name->kind == NODE_SYMBOL_IDX) { parse_emit_err(parser, prev, cstr("invalid symbol name in struct field")); return; @@ -589,12 +596,12 @@ parse_keyword(Parser *parser) { // Are we on the default case. if (!parse_match(parser, TOK_ELSE)) { parse_consume(parser, TOK_CASE, - cstr("expected case statement")); + cstr("expected case statement")); parse_expr(parser, PREC_LOW); tmp->case_value = array_pop(parser->nodes); } parse_consume(parser, TOK_ASSIGN, - cstr("malformed case statement")); + cstr("malformed case statement")); parse_expr(parser, PREC_LOW); tmp->case_expr = array_pop(parser->nodes); array_push(node->match_cases, tmp, parser->storage); @@ -604,6 +611,36 @@ parse_keyword(Parser *parser) { // TODO: Check that there are no multiple default or duplicated // cases. } break; + case TOK_ENUM: { + node = node_alloc(parser, NODE_ENUM, prev); + if (!node) return; + parse_consume(parser, TOK_SYMBOL, + cstr("expected symbol name on enum definition")); + // Just consume this to avoid conflicts with struct literals. + node->value.sym = parser->previous.val; + parse_consume(parser, TOK_LCURLY, + cstr("expected '{' on enum definition")); + while (!parse_match(parser, TOK_RCURLY) && !parser->panic) { + Node *field = + node_alloc(parser, NODE_VAL_FIELD, parser->current); + if (!field) return; + parse_consume(parser, TOK_SYMBOL, + cstr("expected symbol name on enum definition")); + parse_symbol(parser); + field->field_name = array_pop(parser->nodes); + if (field->field_name->next || + field->field_name->kind == NODE_SYMBOL_IDX) { + parse_emit_err(parser, prev, + cstr("invalid symbol name in enum field")); + return; + } + if (parse_match(parser, TOK_ASSIGN)) { + parse_expr(parser, PREC_LOW); + field->field_val = array_pop(parser->nodes); + } + array_push(node->struct_field, field, parser->storage); + } + } break; default: return; // Unreachable. } array_push(parser->nodes, node, parser->storage); @@ -741,8 +778,7 @@ parse_symbol(Parser *parser) { // Struct literal. node->kind = NODE_STRUCT_LIT; while (!parse_match(parser, TOK_RCURLY) && !parser->panic) { - Node *field = - node_alloc(parser, NODE_STRUCT_FIELD, parser->current); + Node *field = node_alloc(parser, NODE_VAL_FIELD, parser->current); parse_consume(parser, TOK_SYMBOL, cstr("expected symbol name")); parse_symbol(parser); field->field_name = array_pop(parser->nodes); @@ -831,6 +867,7 @@ graph_node(Node *node) { graph_node(next); } } break; + case NODE_ENUM: case NODE_STRUCT: { for (sz i = 0; i < array_size(node->struct_field); i++) { Node *next = node->struct_field[i]; @@ -852,7 +889,7 @@ graph_node(Node *node) { graph_node(node->cond_else); } } break; - case NODE_STRUCT_FIELD: + case NODE_VAL_FIELD: case NODE_SET: case NODE_LET: { if (node->var_name) { diff --git a/tests/conditionals.bad b/tests/conditionals.bad index b1c6a89..44467a8 100644 --- a/tests/conditionals.bad +++ b/tests/conditionals.bad @@ -1,28 +1,50 @@ -; Basic if expressions. -if true "hello" +; ; Basic if expressions. +; if true "hello" -; These can produce values and the result bound to a name. -let a = if (2 + 2 >= 4) 42 +; ; 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" +; ; 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 +; ; ... 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" +; ; 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" +; } + +; ; Match cases should only apply to literal values, for example `case 1 + 2` +; ; isn't allowed. They should generally convert to a lookup table. +; match a = { +; case 8 = "hello" +; case 9 = "world" +; else = "what" +; } + +; cond { +; case a == b = "hello" +; case a != c = "world" +; else = "what" +; } + +; Enums are integers with values that increase automatically or that can be set +; with a constexpr. +enum flags { + node_add = 1 << 0 + node_sub = 1 << 1 + node_div = 1 << 2 + node_mul } +let a:flags = flags.node_add -; Match cases should only apply to literal values, for example `case 1 + 2` -; isn't allowed. They should generally convert to a lookup table. +; No need to qualify enum fields inside match-case. match a = { - case 8 = "hello" - case 9 = "world" - else = "what" + case node_add = "adding something..." + case node_add = "subbing something..." + else = "default case" } - -- cgit v1.2.1