#include "bu-parser.h" #include "tree.h" #include void free_node(node *n) { if (n->lhs) free_node(n->lhs); if (n->rhs) free_node(n->rhs); free(n); } void fprint_stack(FILE *stream, struct expr_parser *ep) { for (size_t i = 0; i < ep->stack_idx; i++) { struct stackmember m = ep->stack[i]; fprintf(stream, "%zu: %d ", i, m.id); if (m.id == STACKMEMBER_EXPR) { fprintTree(stream, m.expr, 0); } else { fprintf(stream, "%c\n", m.terminal); } } } struct expr_parser expr_parser_init(lexer_fn src, void* lex_data) { return (struct expr_parser){ .lex = src, .lex_data = lex_data, .lahead = {0}, .st = PARSER_READY, .stack = {0}, .stack_idx = 0, }; } void expr_parser_finish(struct expr_parser *ep) { for (size_t i = 0; i < ep->stack_idx; i++) { if (ep->stack[i].id == STACKMEMBER_EXPR) free_node(ep->stack[i].expr); } } struct stackmember stack_pos(struct expr_parser *ep, size_t pos) { ssize_t npos = ep->stack_idx - pos - 1; if (npos < 0) { return (struct stackmember) { .id = (enum stackmember_id)TOKEN_OOB, .expr = NULL }; } return ep->stack[npos]; } void shift(struct expr_parser *ep) { struct chartoken lahead = ep->lahead; ep->stack[ep->stack_idx] = (struct stackmember){ .id = (enum stackmember_id)lahead.id, .terminal = lahead.c, }; ep->stack_idx++; ep->lahead = ep->lex(ep->lex_data); } #define BAIL_REDUCE_IF(COND, ERRMSG) \ { if(COND) { \ ep->st = PARSER_ERR; fprintf(stderr, "%s: " ERRMSG "\n", __func__); \ expr_parser_debug_print(stderr, ep); \ return; \ }} void var_expr(struct expr_parser *ep) { struct stackmember v = stack_pos(ep, 0); BAIL_REDUCE_IF(v.id != TOKEN_VARIABLE, "not a variable token"); node *n = malloc(sizeof(node)); *n = (struct node){0}; n->type = LTTR; n->el = v.terminal; struct stackmember newm = { .id = STACKMEMBER_EXPR, .expr = n, }; ep->stack[ep->stack_idx - 1] = newm; } void binary_expr(struct expr_parser *ep) { struct stackmember rhs, op, lhs; lhs = stack_pos(ep, 2); op = stack_pos(ep, 1); rhs = stack_pos(ep, 0); BAIL_REDUCE_IF( lhs.id != STACKMEMBER_EXPR || op.id != TOKEN_BINARY_OPERATOR || rhs.id != STACKMEMBER_EXPR, "incorrect token sequence"); node *n = malloc(sizeof(node)); *n = (struct node){0}; n->type = BIOP; n->el = op.terminal; n->lhs = lhs.expr; n->rhs = rhs.expr; struct stackmember newm = { .id = STACKMEMBER_EXPR, .expr = n, }; ep->stack_idx -= 2; ep->stack[ep->stack_idx - 1] = newm; } void unary_expr(struct expr_parser *ep) { struct stackmember op, val; op = stack_pos(ep, 1); val = stack_pos(ep, 0); BAIL_REDUCE_IF( op.id != TOKEN_UNARY_OPERATOR || val.id != STACKMEMBER_EXPR, "incorrect token sequence"); node *n = malloc(sizeof(node)); *n = (struct node){0}; n->type = UNOP; n->el = op.terminal; n->value = val.expr; struct stackmember newm = { .id = STACKMEMBER_EXPR, .expr = n, }; ep->stack_idx -= 1; ep->stack[ep->stack_idx - 1] = newm; } int expr_parser_run(struct expr_parser *ep) { // prime the look-ahead ep->lahead = ep->lex(ep->lex_data); int noshift = 0; while (ep->st != PARSER_ERR && ep->st != PARSER_ACCEPT) { if (!noshift) { shift(ep); } noshift = 0; struct stackmember top = stack_pos(ep, 0); switch (top.id) { case TOKEN_EOS: // the result and the EOS token only if (ep->stack_idx == 2) { ep->st = PARSER_ACCEPT; } else { ep->st = PARSER_ERR; } break; case TOKEN_VARIABLE: var_expr(ep); noshift = 1; // check if this enables any reductions first break; case STACKMEMBER_EXPR: { struct stackmember op = stack_pos(ep, 1); if (op.id == TOKEN_UNARY_OPERATOR) { unary_expr(ep); noshift = 1; } else if (op.id == TOKEN_BINARY_OPERATOR) { if (op.terminal == '+' && ep->lahead.c == '*') { break; } binary_expr(ep); noshift = 1; } } break; default: break; } } return ep->st; } void expr_parser_debug_print(FILE *stream, struct expr_parser *ep) { fprintf(stream, "PARSER DEBUG DUMP:\n" "state: %d\n" "stack: \n", ep->st); fprint_stack(stream, ep); }