From: cassowarii Date: Fri, 26 Jun 2026 19:47:35 +0000 (-0700) Subject: expression parser mostly complete X-Git-Url: https://git.cassowary.me/gitweb.cgi?a=commitdiff_plain;h=4e9b9048977d3e8ed065264276c0db3223ff4ad2;p=sarabande.git expression parser mostly complete --- diff --git a/src/parse/ast.h b/src/parse/ast.h index c552c4e..89db788 100644 --- a/src/parse/ast.h +++ b/src/parse/ast.h @@ -33,20 +33,29 @@ typedef enum sbAstType { typedef enum sbAstOp { AST_OP_NULL, - AST_OP_PLUS = '+', - AST_OP_MINUS = '-', - AST_OP_TIMES = '*', + AST_OP_ADD = '+', + AST_OP_SUB = '-', + AST_OP_MUL = '*', AST_OP_DIV = '/', AST_OP_MOD = '%', AST_OP_EQ = '=', AST_OP_LT = '<', AST_OP_GT = '>', + AST_OP_PIPE = '|', AST_OP_LE = 128, AST_OP_GE, AST_OP_NE, AST_OP_FLDIV, + AST_OP_POW, + AST_OP_RANGE, AST_OP_DIVBY, - AST_OP_PN, + AST_OP_INDEX, + AST_OP_SCOPE, + AST_OP_UNPLUS, + AST_OP_UNMINUS, + AST_OP_OR, + AST_OP_AND, + AST_OP_NOT, } sbAstOp; typedef struct sbAstNode { @@ -57,11 +66,11 @@ typedef struct sbAstNode { hInteger i; double fl; struct { - const struct sbAstNode *this; - const struct sbAstNode *next; + const struct sbAstNode *left; + const struct sbAstNode *right; } seq; struct { - sbTokenType type; + sbAstOp type; const struct sbAstNode *left; const struct sbAstNode *right; } op; diff --git a/src/parse/lexer.c b/src/parse/lexer.c index fb5ab94..d6101c0 100644 --- a/src/parse/lexer.c +++ b/src/parse/lexer.c @@ -219,6 +219,20 @@ static flag block_header_can_end_after(sbTokenType type) { || type == T_rDO; } +/* these are operators that look weird if they get captured inside + * of parentheses. */ +static flag close_invisible_parens_before(sbTokenType type) { + return type == T_rAND + || type == T_rOR + || type == T_DOUBLEEQUALS + || type == T_NOTEQUALS + || type == T_LESS + || type == T_GREATER + || type == T_LESSEQUALS + || type == T_GREATEREQUALS + || type == T_PIPE; +} + /*static flag is_reserved_word(sbTokenType type) { for (int i = 0; i < N_RESERVED_WORDS; i++) { if (type == reserved_words[i].token_type) return 1; @@ -379,6 +393,10 @@ static void compute_next_token(hLexer lx) { brackets_stack_push(lx, 'B'); } + if (close_invisible_parens_before(token.type)) { + unstack_all_invisible_parentheses(lx); + } + //printf("\nstack: %s\n", lx->brackets_stack.data); /* --- HERE IS WHERE THE TOKEN ACTUALLY GETS OUTPUT TO THE STREAM --- */ diff --git a/src/parse/parser.c b/src/parse/parser.c index 11fe90b..578aa52 100644 --- a/src/parse/parser.c +++ b/src/parse/parser.c @@ -38,8 +38,20 @@ typedef struct binop { sbTokenType type; u8 left_precedence; u8 right_precedence; + sbAstOp ast_op; } binop; +typedef struct unop { + sbTokenType type; + u8 precedence; + sbAstOp ast_op; +} unop; + +typedef struct opspelling { + sbAstOp ast_op; + const char *name; +} opspelling; + static const sbAstNode SENTINEL_VALUE = {0}; static sbAst NO_NODE = &SENTINEL_VALUE; @@ -88,7 +100,7 @@ static sbAst new_node(hParser pr, sbAstNode *n) { return new_node; } -static sbAst unop_node(hParser pr, sbTokenType operation, sbAstNode *child) { +static sbAst unop_node(hParser pr, sbAstOp operation, sbAst child) { sbAstNode n = (sbAstNode) { .type = AST_NODE_OP, .op.type = operation, @@ -97,7 +109,7 @@ static sbAst unop_node(hParser pr, sbTokenType operation, sbAstNode *child) { return new_node(pr, &n); } -static sbAst binop_node(hParser pr, sbTokenType operation, sbAst left, sbAst right) { +static sbAst binop_node(hParser pr, sbAstOp operation, sbAst left, sbAst right) { sbAstNode n = (sbAstNode) { .type = AST_NODE_OP, .op.type = operation, @@ -107,28 +119,86 @@ static sbAst binop_node(hParser pr, sbTokenType operation, sbAst left, sbAst rig return new_node(pr, &n); } +static sbAst seq_node(hParser pr, sbAstType type, sbAst left, sbAst right) { + sbAstNode n = (sbAstNode) { + .type = type, + .seq.left = left, + .seq.right = right, + }; + return new_node(pr, &n); +} + +static sbAst name_node(hParser pr, sbLexToken token) { + if (token.type != T_IDENTIFIER) { + PANIC("can't create name node with token of type %d\n", token.type); + } + + sbAstNode n = (sbAstNode) { + .type = AST_NODE_NAME, + .symb = token.symb, + }; + return new_node(pr, &n); +} + static binop binops[] = { - { T_rOR, 3, 4 }, - { T_rAND, 6, 7 }, - { T_DOUBLEEQUALS, 10, 11 }, - { T_GREATER, 10, 11 }, - { T_LESS, 10, 11 }, - { T_GREATEREQUALS, 10, 11 }, - { T_LESSEQUALS, 10, 11 }, - { T_NOTEQUALS, 10, 11 }, - { T_PLUS, 15, 16 }, - { T_MINUS, 15, 16 }, - { T_ASTERISK, 30, 31 }, - { T_SLASH, 30, 31 }, - { T_PERCENT, 30, 31 }, - { T_DOUBLESLASH, 30, 31 }, - { T_DOUBLEPERCENT, 30, 31 }, - { T_DOUBLEASTERISK, 51, 50 }, - { T_TWODOT, 60, 61 }, - { T_PAAMAYIM_NEKUDOTAYIM, 70, 71 }, + { T_PIPE, 6, 7, AST_OP_PIPE }, + { T_rOR, 10, 11, AST_OP_OR }, + { T_rAND, 20, 21, AST_OP_AND }, + { T_DOUBLEEQUALS, 30, 31, AST_OP_EQ }, + { T_GREATER, 30, 31, AST_OP_GT }, + { T_LESS, 30, 31, AST_OP_LT }, + { T_GREATEREQUALS, 30, 31, AST_OP_GE }, + { T_LESSEQUALS, 30, 31, AST_OP_LE }, + { T_NOTEQUALS, 30, 31, AST_OP_NE }, + { T_DOUBLEPERCENT, 30, 31, AST_OP_DIVBY }, + { T_PLUS, 45, 46, AST_OP_ADD }, + { T_MINUS, 45, 46, AST_OP_SUB }, + { T_ASTERISK, 60, 61, AST_OP_MUL }, + { T_SLASH, 60, 61, AST_OP_DIV }, + { T_PERCENT, 60, 61, AST_OP_MOD }, + { T_DOUBLESLASH, 60, 61, AST_OP_FLDIV }, + { T_DOUBLEASTERISK, 71, 70, AST_OP_POW }, + { T_TWODOT, 80, 81, AST_OP_RANGE }, + { T_DOT, 90, 91, AST_OP_NULL }, + { T_LPAREN, 90, 91, AST_OP_NULL }, + { T_LBRACKET, 90, 91, AST_OP_INDEX }, + { T_PAAMAYIM_NEKUDOTAYIM, 100, 101, AST_OP_SCOPE }, +}; + +static unop unops[] = { + { T_rNOT, 25, AST_OP_NOT }, + { T_PLUS, 85, AST_OP_UNPLUS }, + { T_MINUS, 85, AST_OP_UNMINUS }, +}; + +static opspelling op_spellings[] = { + { AST_OP_OR, "or" }, + { AST_OP_AND, "and" }, + { AST_OP_NOT, "not" }, + { AST_OP_EQ, "==" }, + { AST_OP_NE, "!=" }, + { AST_OP_GT, ">" }, + { AST_OP_LT, "<" }, + { AST_OP_GE, ">=" }, + { AST_OP_LE, "<=" }, + { AST_OP_DIVBY, "%%" }, + { AST_OP_ADD, "+" }, + { AST_OP_UNPLUS, "+@" }, + { AST_OP_SUB, "-" }, + { AST_OP_UNMINUS, "-@" }, + { AST_OP_MUL, "*" }, + { AST_OP_DIV, "/" }, + { AST_OP_MOD, "%" }, + { AST_OP_FLDIV, "//" }, + { AST_OP_POW, "**" }, + { AST_OP_RANGE, ".." }, + { AST_OP_INDEX, "[]" }, + { AST_OP_SCOPE, "::" }, }; const int NUM_BINOPS = sizeof(binops) / sizeof(binops[0]); +const int NUM_UNOPS = sizeof(unops) / sizeof(unops[0]); +const int NUM_SPELLINGS = sizeof(op_spellings) / sizeof(op_spellings[0]); /* https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html */ static sbAst parse_expr(hParser pr, u8 min_precedence) { @@ -146,63 +216,151 @@ static sbAst parse_expr(hParser pr, u8 min_precedence) { return NO_NODE; } } else { - fprintf(stderr, "expected identifier!\n"); - return NO_NODE; + sbLexToken op = peek_ahead(pr, 0); + unop *prefix = NULL; + for (int i = 0; i < NUM_UNOPS; i++) { + if (unops[i].type == op.type) { + prefix = &unops[i]; + break; + } + } + + if (!prefix) { + fprintf(stderr, "expected identifier, '(', or operator!\n"); + return NO_NODE; + } + + next_token(pr); + + sbAst content = parse_expr(pr, prefix->precedence); + + lhs = unop_node(pr, prefix->ast_op, content); } while (1) { sbLexToken op = peek_ahead(pr, 0); - if (op.type == T_EOF || op.type == T_SEMICOLON) { - break; - } else { - binop *infix = NULL; - for (int i = 0; i < NUM_BINOPS; i++) { - if (binops[i].type == op.type) { - infix = &binops[i]; - break; - } - } - - if (!infix) { - /* something that isn't an infix operator: means end of expr */ + sbAstType ast_type = AST_NODE_OP; + binop *infix = NULL; + for (int i = 0; i < NUM_BINOPS; i++) { + if (binops[i].type == op.type) { + infix = &binops[i]; break; } + } - if (infix->left_precedence < min_precedence) { - /* end of this expr because something with lower precedence */ - break; - } + if (!infix) { + /* something that isn't an infix operator: means end of expr */ + break; + } - next_token(pr); /* consume operator token */ + if (infix->left_precedence < min_precedence) { + /* end of this expr because something with lower precedence */ + break; + } - sbAst rhs = parse_expr(pr, infix->right_precedence); + next_token(pr); /* consume operator token */ + + sbAst rhs; + if (op.type == T_LPAREN) { + /* function call */ + rhs = parse_expr(pr, 0); + ast_type = AST_NODE_FUNCCALL; + if (!expect(pr, T_RPAREN, NULL)) { + /* TODO handle commas: we should have a "parse comma separated expr" thing */ + fprintf(stderr, "expected ')'\n"); + return NO_NODE; + } + } else if (op.type == T_LBRACKET) { + /* indexing */ + rhs = parse_expr(pr, 0); + if (!expect(pr, T_RBRACKET, NULL)) { + fprintf(stderr, "expected ']'\n"); + return NO_NODE; + } + } else if (op.type == T_DOT) { + sbLexToken method_name = {0}; + if (!expect(pr, T_IDENTIFIER, &method_name)) { + fprintf(stderr, "expected identifier after '.' token\n"); + return NO_NODE; + } + sbAst name = name_node(pr, method_name); + printf("method name: %s\n", sbSymbol_name(method_name.symb)); + if (!expect(pr, T_LPAREN, NULL)) { + fprintf(stderr, "expected opening-parenthesis after '.%s'\n", sbSymbol_name(method_name.symb)); + return NO_NODE; + } + // TODO handle commas + sbAst params = parse_expr(pr, 0); + if (!expect(pr, T_RPAREN, NULL)) { + fprintf(stderr, "expected closing-parenthesis after '.%s(...'\n", sbSymbol_name(method_name.symb)); + return NO_NODE; + } + ast_type = AST_NODE_METHODCALL; + rhs = seq_node(pr, AST_NODE_NEXT, name, params); + } else { + rhs = parse_expr(pr, infix->right_precedence); + } - lhs = binop_node(pr, infix->type, lhs, rhs); + if (ast_type == AST_NODE_OP) { + lhs = binop_node(pr, infix->ast_op, lhs, rhs); + } else { + lhs = seq_node(pr, ast_type, lhs, rhs); } } return lhs; } -static void print_ast_node(sbAst n) { +static void print_ast_node(sbAst n, int indent) { + printf("\n"); + for (int i = 0; i < indent; i++) { + printf(" "); + } + if (n->type == AST_NODE_NAME) { printf("%s", sbSymbol_name(n->symb)); } else { printf("("); if (n->type == AST_NODE_OP) { - printf("%c ", n->op.type); - print_ast_node(n->op.left); - printf(" "); - print_ast_node(n->op.right); + flag found = 0; + for (int i = 0; i < NUM_SPELLINGS; i++) { + if (op_spellings[i].ast_op == n->op.type) { + printf("%s", op_spellings[i].name); + found = 1; + break; + } + } + if (!found) printf("??"); + print_ast_node(n->op.left, indent + 1); + if (n->op.right) { + print_ast_node(n->op.right, indent + 1); + } + } else if (n->type == AST_NODE_FUNCCALL) { + print_ast_node(n->seq.left, indent + 1); + printf("("); + print_ast_node(n->seq.right, indent + 1); + printf(")"); + } else if (n->type == AST_NODE_METHODCALL) { + print_ast_node(n->seq.left, indent + 1); + printf("."); + print_ast_node(n->seq.right->seq.left, indent + 1); + printf("("); + print_ast_node(n->seq.right->seq.right, indent + 1); + printf(")"); } else { printf("?<%d>", n->type); } + + printf("\n"); + for (int i = 0; i < indent; i++) { + printf(" "); + } printf(")"); } } static sbAst do_parse(hParser pr) { sbAst program = parse_expr(pr, 0); - print_ast_node(program); + print_ast_node(program, 0); return program; }