#include "ctx.h" #include enum expr_component { EXPR_C_NONE = 0, EXPR_C_BINARY_OP, EXPR_C_UNARY_OP, EXPR_C_OPERAND, }; struct expr_parse_ctx { fx_queue expr_operator_stack, expr_out_queue; enum expr_component expr_prev; enum expr_parse_flags expr_flags; }; static bool finalise_expr( struct expr_parse_ctx *ctx, struct ivy_ast_node **out, enum ivy_operator_precedence minimum_precedence); bool peek_expr_start(struct ivy_parser *parser) { struct ivy_token *tok = peek_token(parser); if (!tok) { return false; } switch (tok->t_type) { case IVY_TOK_INT: case IVY_TOK_ATOM: case IVY_TOK_DOUBLE: case IVY_TOK_IDENT: case IVY_TOK_STRING: case IVY_TOK_STR_START: return true; case IVY_TOK_KEYWORD: switch (tok->t_keyword) { case IVY_KW_IF: case IVY_KW_WHILE: case IVY_KW_FOR: case IVY_KW_TRY: case IVY_KW_UNLESS: case IVY_KW_CONTINUE: case IVY_KW_BREAK: case IVY_KW_MATCH: case IVY_KW_TRUE: case IVY_KW_FALSE: case IVY_KW_NULL: return true; default: return false; } break; case IVY_TOK_SYMBOL: switch (tok->t_symbol) { case IVY_SYM_LEFT_BRACE: case IVY_SYM_LEFT_BRACKET: case IVY_SYM_LEFT_PAREN: case IVY_SYM_CARET: case IVY_SYM_UNDERSCORE: return true; default: return false; } break; default: return false; } } static struct ivy_ast_node *pop_operand( struct expr_parse_ctx *expr, enum ivy_operator_precedence min_precedence) { struct ivy_ast_node *out = NULL; bool ok = finalise_expr(expr, &out, min_precedence); if (!ok) { parse_trace(NULL, "sub-expression is invalid"); return NULL; } return out; } static bool parse_simple_operand( struct ivy_parser *parser, struct expr_parse_ctx *expr) { if (expr->expr_prev == EXPR_C_OPERAND) { parse_trace(parser, "encountered two operands in a row"); return false; } expr->expr_prev = EXPR_C_OPERAND; struct ivy_token *tok = peek_token(parser); switch (tok->t_type) { case IVY_TOK_INT: { struct ivy_ast_int_node *v = create_node(IVY_AST_INT); v->n_value = parse_token(parser); fx_queue_push_back(&expr->expr_out_queue, &v->n_base.n_entry); return true; } case IVY_TOK_DOUBLE: { struct ivy_ast_double_node *v = create_node(IVY_AST_DOUBLE); v->n_value = parse_token(parser); fx_queue_push_back(&expr->expr_out_queue, &v->n_base.n_entry); return true; } case IVY_TOK_STRING: { struct ivy_ast_string_node *v = create_node(IVY_AST_STRING); v->n_value = parse_token(parser); fx_queue_push_back(&expr->expr_out_queue, &v->n_base.n_entry); return true; } case IVY_TOK_IDENT: { struct ivy_ast_ident_node *v = create_node(IVY_AST_IDENT); v->n_content = parse_token(parser); fx_queue_push_back(&expr->expr_out_queue, &v->n_base.n_entry); return true; } case IVY_TOK_ATOM: { struct ivy_ast_atom_node *v = create_node(IVY_AST_ATOM); v->n_content = parse_token(parser); fx_queue_push_back(&expr->expr_out_queue, &v->n_base.n_entry); return true; } case IVY_TOK_SYMBOL: switch (tok->t_symbol) { case IVY_SYM_UNDERSCORE: { struct ivy_ast_discard_node *v = create_node(IVY_AST_DISCARD); discard_token(parser); fx_queue_push_back( &expr->expr_out_queue, &v->n_base.n_entry); return true; } default: parse_trace( parser, "token %s is not a simple operand", ivy_symbol_to_string(tok->t_symbol)); return false; } break; default: parse_trace( parser, "token %s is not a simple operand", ivy_lex_token_type_to_string(tok->t_type)); return false; } } void arith_push_operator(struct expr_parse_ctx *state, struct ivy_ast_node *node) { const struct ivy_operator *op = get_operator_from_node(node); if (!op) { return; } while (true) { fx_queue_entry *top = fx_queue_last(&state->expr_operator_stack); if (!top) { break; } struct ivy_ast_node *top_node = fx_unbox(struct ivy_ast_node, top, n_entry); const struct ivy_operator *top_op = NULL; switch (top_node->n_type) { case IVY_AST_OP: { struct ivy_ast_op_node *op_node = (struct ivy_ast_op_node *)top_node; top_op = op_node->n_op; break; } case IVY_AST_MSG: { struct ivy_ast_msg_node *msg_node = (struct ivy_ast_msg_node *)top_node; top_op = ivy_operator_get_by_id(IVY_OP_MSG); break; } default: return; } if (top_op->op_precedence < op->op_precedence || (top_op->op_precedence == op->op_precedence && op->op_associativity != IVY_ASSOCIATIVITY_LEFT)) { break; } fx_queue_delete(&state->expr_operator_stack, top); fx_queue_push_back(&state->expr_out_queue, top); } fx_queue_push_back(&state->expr_operator_stack, &node->n_entry); } static bool parse_binary_operator( struct ivy_parser *parser, struct expr_parse_ctx *expr) { struct ivy_token *tok = parse_token(parser); if (expr->expr_prev != EXPR_C_OPERAND) { parse_trace(parser, "expected operand before binary operator"); return false; } expr->expr_prev = EXPR_C_BINARY_OP; struct ivy_ast_node *op_node = create_operator_node_from_token(tok); if (!op_node) { parse_trace(parser, "unrecognised binary operator"); return false; } arith_push_operator(expr, op_node); return true; } static bool expr_is_empty(struct expr_parse_ctx *expr) { return fx_queue_empty(&expr->expr_out_queue) && fx_queue_empty(&expr->expr_operator_stack); } static bool op_node_is_complete(struct ivy_ast_op_node *node) { if (!node->n_op) { return false; } switch (node->n_op->op_arity) { case IVY_OP_UNARY: return node->n_right != NULL; case IVY_OP_BINARY: return (node->n_left != NULL && node->n_right != NULL); default: return false; } } static bool finalise_expr( struct expr_parse_ctx *ctx, struct ivy_ast_node **out, enum ivy_operator_precedence minimum_precedence) { fx_queue_entry *entry = NULL; while (true) { entry = fx_queue_pop_back(&ctx->expr_operator_stack); if (!entry) { break; } struct ivy_ast_node *node = fx_unbox(struct ivy_ast_node, entry, n_entry); if (!node) { /* this should never happen */ return false; } const struct ivy_operator *op = NULL; switch (node->n_type) { case IVY_AST_OP: { struct ivy_ast_op_node *n = (struct ivy_ast_op_node *)node; op = n->n_op; break; } case IVY_AST_MSG: /* all unary message operators have the same * pre-defined precedence */ op = ivy_operator_get_by_id(IVY_OP_MSG); break; default: return false; } /* if we aren't processing operators below a certain precedence * then leave them on the stack and stop here. */ if (op->op_precedence < minimum_precedence) { fx_queue_push_back(&ctx->expr_operator_stack, entry); break; } fx_queue_push_back(&ctx->expr_out_queue, entry); } fx_queue q = FX_QUEUE_INIT; fx_queue_entry *tmp = NULL; entry = fx_queue_first(&ctx->expr_out_queue); int i = 0; while (entry) { struct ivy_ast_node *item = fx_unbox(struct ivy_ast_node, entry, n_entry); fx_queue_entry *next = fx_queue_next(entry); fx_queue_delete(&ctx->expr_out_queue, entry); /* if the node is an operand, just push it to a * temporary queue and come back to it later. */ if (item->n_type != IVY_AST_OP && item->n_type != IVY_AST_MSG) { /* operand */ fx_queue_push_back(&q, &item->n_entry); goto next; } const struct ivy_operator *op = NULL; if (item->n_type == IVY_AST_MSG) { struct ivy_ast_msg_node *msg = (struct ivy_ast_msg_node *)item; /* if the message has no recipient, it is a unary message, * and the recipient is located on the operand stack. * * if the message DOES have a recipient, it is a * self-contained keyword message, and can be pushed to * the operand queue as-is. */ if (!msg->n_recipient) { tmp = fx_queue_pop_back(&q); msg->n_recipient = fx_unbox( struct ivy_ast_node, tmp, n_entry); } fx_queue_push_back(&q, &msg->n_base.n_entry); goto next; } struct ivy_ast_op_node *op_node = (struct ivy_ast_op_node *)item; /* if an operator node is already complete (i.e. it * already has all the operands it needs, it can be * pushed to the operand queue as-is */ if (op_node_is_complete(op_node)) { fx_queue_push_back(&q, &item->n_entry); goto next; } /* otherwise, pop the relevant operands from the operand * queue... */ op = op_node->n_op; tmp = fx_queue_pop_back(&q); op_node->n_right = fx_unbox(struct ivy_ast_node, tmp, n_entry); if (op_node->n_right) { op_node->n_right->n_parent = (struct ivy_ast_node *)op_node; ivy_ast_node_extend_bounds_recursive( (struct ivy_ast_node *)op_node, (struct ivy_ast_node *)tmp); } if (op->op_arity == IVY_OP_BINARY) { tmp = fx_queue_pop_back(&q); op_node->n_left = fx_unbox(struct ivy_ast_node, tmp, n_entry); if (op_node->n_left) { op_node->n_left->n_parent = (struct ivy_ast_node *)op_node; ivy_ast_node_extend_bounds_recursive( (struct ivy_ast_node *)op_node, (struct ivy_ast_node *)tmp); } } /* ...and push the newly-completed operator node to the * operand queue */ fx_queue_push_back(&q, &op_node->n_base.n_entry); next: entry = next; } #if 0 debug_printf("** after hierarchisation:\n"); print_expr_queues(state); #endif /* if we are not processing operators below a certain precedence, * i.e. when determining the recipient of a keyword-message), these * operators will still be on the parser state's operator stack, but * their operands have just been moved to the temporary operand stack * used above. move them back to the parser state's output queue here * so they can be used later. */ entry = fx_queue_first(&ctx->expr_operator_stack); while (entry) { fx_queue_entry *entry2 = fx_queue_pop_front(&q); if (!entry2) { return false; } fx_queue_push_back(&ctx->expr_out_queue, entry2); entry = fx_queue_next(entry); } #if 0 debug_printf("** after de-linearisation:\n"); print_expr_queues(state); ivy_ast_node_print(*expr_tree); debug_printf("------\n"); #endif /* the final node remaining on the temp operand stack is the * root node of the new expression tree */ tmp = fx_queue_pop_back(&q); *out = fx_unbox(struct ivy_ast_node, tmp, n_entry); return true; } static bool parse_call( struct ivy_parser *parser, struct ivy_ast_node *target, struct ivy_ast_node **out) { if (!parse_symbol(parser, IVY_SYM_LEFT_PAREN)) { parse_trace(parser, "expected `(` before call arguments"); } struct ivy_ast_msg_node *call = create_node(IVY_AST_MSG); call->n_recipient = target; call->n_sel = create_node(IVY_AST_SELECTOR); call->n_sel->n_msg_name = ivy_token_create_ident("call"); bool ok = true; while (ok) { if (parse_symbol(parser, IVY_SYM_RIGHT_PAREN)) { break; } if (!fx_queue_empty(&call->n_arg) && !parse_symbol(parser, IVY_SYM_COMMA)) { parse_trace( parser, "expected `,` or `)` after call argument"); ok = false; break; } struct ivy_ast_node *arg = NULL; if (!parse_expr(parser, EXPR_PARSE_STOP_COMMA, &arg)) { parse_trace(parser, "invalid call argument"); ok = false; break; } struct ivy_token *label = ivy_token_create_discard(); fx_queue_push_back(&call->n_sel->n_arg_labels, &label->t_entry); fx_queue_push_back(&call->n_arg, &arg->n_entry); } *out = generic_node(call); return true; } static bool cleanup_expr(struct expr_parse_ctx *ctx) { return false; } bool parse_expr( struct ivy_parser *parser, enum expr_parse_flags flags, struct ivy_ast_node **out) { struct expr_parse_ctx ctx = {0}; ctx.expr_flags = flags; bool ok = true; bool done = false; bool return_expr = false; bool end_without_dot = false; while (ok && !done) { struct ivy_token *tok = peek_token(parser); if (!tok) { break; } if ((flags & EXPR_PARSE_STOP_TUPLE_TERMINATOR) && is_tuple_terminator(tok)) { done = true; break; } switch (tok->t_type) { case IVY_TOK_LINEFEED: discard_token(parser); break; case IVY_TOK_IDENT: if (ctx.expr_prev == EXPR_C_OPERAND) { struct ivy_ast_node *msg = NULL; ok = parse_unary_msg(parser, NULL, &msg); if (!ok) { break; } fx_queue_push_back( &ctx.expr_out_queue, &msg->n_entry); ctx.expr_prev = EXPR_C_OPERAND; } else { ok = parse_simple_operand(parser, &ctx); } break; case IVY_TOK_INT: case IVY_TOK_STRING: case IVY_TOK_DOUBLE: case IVY_TOK_ATOM: ok = parse_simple_operand(parser, &ctx); break; case IVY_TOK_STR_START: { struct ivy_ast_node *v = NULL; ok = parse_fstring(parser, &v); if (!ok) { break; } fx_queue_push_back(&ctx.expr_out_queue, &v->n_entry); ctx.expr_prev = EXPR_C_OPERAND; break; } case IVY_TOK_LABEL: { if (flags & EXPR_PARSE_STOP_LABEL) { done = true; break; } struct ivy_ast_node *recipient = pop_operand(&ctx, IVY_PRECEDENCE_KEYWORD_MSG); if (!recipient) { parse_trace( parser, "no valid recipient for keyword msg"); ok = false; break; } struct ivy_ast_node *v = NULL; ok = parse_keyword_msg( parser, recipient, ctx.expr_flags, &v); if (!ok) { break; } fx_queue_push_back(&ctx.expr_out_queue, &v->n_entry); ctx.expr_prev = EXPR_C_OPERAND; break; } case IVY_TOK_KEYWORD: { struct ivy_ast_node *operand = NULL; if (expr_is_empty(&ctx)) { end_without_dot = true; } switch (tok->t_keyword) { case IVY_KW_ELSE: case IVY_KW_CATCH: done = true; break; case IVY_KW_MATCH: ok = parse_match(parser, &operand); break; case IVY_KW_DO: { discard_token(parser); struct ivy_ast_do_node *do_node = create_node(IVY_AST_DO); ok = parse_block(parser, &do_node->n_block); if (!ok) { break; } operand = generic_node(do_node); break; } case IVY_KW_IF: if (flags & EXPR_PARSE_STOP_KW_CONTROL) { done = true; break; } if (ctx.expr_prev == EXPR_C_OPERAND) { struct ivy_ast_node *body = pop_operand( &ctx, IVY_PRECEDENCE_IF_ELSE); if (!body) { parse_trace( parser, "invalid " "inline-if " "body"); } ok = parse_if_inline(parser, body, &operand); } else { ok = parse_if(parser, &operand); } break; case IVY_KW_FOR: if (flags & EXPR_PARSE_STOP_KW_CONTROL) { done = true; break; } if (ctx.expr_prev == EXPR_C_OPERAND) { struct ivy_ast_node *body = pop_operand( &ctx, IVY_PRECEDENCE_IF_ELSE); if (!body) { parse_trace( parser, "invalid " "inline-for " "body"); } ok = parse_for_inline( parser, body, &operand); } else { ok = parse_for(parser, &operand); } break; case IVY_KW_WHILE: if (flags & EXPR_PARSE_STOP_KW_CONTROL) { done = true; break; } if (ctx.expr_prev == EXPR_C_OPERAND) { struct ivy_ast_node *body = pop_operand( &ctx, IVY_PRECEDENCE_IF_ELSE); if (!body) { parse_trace( parser, "invalid " "inline-while " "body"); } ok = parse_while_inline( parser, body, &operand); } else { ok = parse_while(parser, &operand); } break; case IVY_KW_IN: if (flags & EXPR_PARSE_STOP_KW_IN) { done = true; break; } case IVY_KW_IS: ok = parse_binary_operator(parser, &ctx); break; case IVY_KW_TRY: if (flags & EXPR_PARSE_STOP_KW_CONTROL) { done = true; break; } ok = parse_try(parser, &operand); break; default: parse_trace( parser, "unrecognised keyword %s", ivy_keyword_to_string(tok->t_keyword)); ok = false; break; } if (ok && operand) { fx_queue_push_back( &ctx.expr_out_queue, &operand->n_entry); ctx.expr_prev = EXPR_C_OPERAND; if (end_without_dot && parse_linefeed(parser)) { done = true; } } break; } case IVY_TOK_SYMBOL: switch (tok->t_symbol) { case IVY_SYM_DOUBLE_COLON: case IVY_SYM_PLUS: case IVY_SYM_HYPHEN: case IVY_SYM_FORWARD_SLASH: case IVY_SYM_ASTERISK: case IVY_SYM_PERCENT: case IVY_SYM_AMPERSAND: case IVY_SYM_EQUAL: case IVY_SYM_HYPHEN_RIGHT_ANGLE: case IVY_SYM_DOUBLE_EQUAL: case IVY_SYM_LEFT_ANGLE: case IVY_SYM_RIGHT_ANGLE: case IVY_SYM_DOUBLE_LEFT_ANGLE: case IVY_SYM_DOUBLE_RIGHT_ANGLE: case IVY_SYM_LEFT_ANGLE_EQUAL: case IVY_SYM_RIGHT_ANGLE_EQUAL: case IVY_SYM_DOUBLE_LEFT_ANGLE_EQUAL: case IVY_SYM_DOUBLE_RIGHT_ANGLE_EQUAL: case IVY_SYM_PLUS_EQUAL: case IVY_SYM_HYPHEN_EQUAL: case IVY_SYM_FORWARD_SLASH_EQUAL: case IVY_SYM_ASTERISK_EQUAL: case IVY_SYM_AMPERSAND_EQUAL: case IVY_SYM_PIPE_EQUAL: case IVY_SYM_PERCENT_EQUAL: case IVY_SYM_CARET_EQUAL: case IVY_SYM_BANG_EQUAL: ok = parse_binary_operator(parser, &ctx); break; case IVY_SYM_EQUAL_RIGHT_ANGLE: done = true; break; case IVY_SYM_UNDERSCORE: ok = parse_simple_operand(parser, &ctx); break; case IVY_SYM_COMMA: { if (flags & EXPR_PARSE_STOP_COMMA) { done = true; break; } struct ivy_ast_node *first = pop_operand( &ctx, IVY_PRECEDENCE_KEYWORD_MSG); if (!first) { parse_trace( parser, "no valid item for tuple"); return false; } struct ivy_ast_node *v = NULL; ok = parse_tuple(parser, first, &v); if (!ok) { break; } fx_queue_push_back(&ctx.expr_out_queue, &v->n_entry); ctx.expr_prev = EXPR_C_OPERAND; break; } case IVY_SYM_SEMICOLON: { if (flags & EXPR_PARSE_STOP_SEMICOLON) { done = true; break; } struct ivy_ast_node *first = pop_operand( &ctx, IVY_PRECEDENCE_CASCADE); if (!first) { parse_trace( parser, "no valid recipient for " "cascade"); return false; } struct ivy_ast_node *v = NULL; ok = parse_cascade(parser, first, &v); if (!ok) { break; } fx_queue_push_back(&ctx.expr_out_queue, &v->n_entry); ctx.expr_prev = EXPR_C_OPERAND; break; } case IVY_SYM_LEFT_PAREN: { if (ctx.expr_prev == EXPR_C_OPERAND) { fx_queue_entry *entry = fx_queue_pop_back( &ctx.expr_out_queue); if (!entry) { parse_trace( parser, "no valid recipient " "for complex msg"); return false; } struct ivy_ast_node *target = fx_unbox( struct ivy_ast_node, entry, n_entry); if (target->n_type == IVY_AST_MSG) { ok = parse_complex_msg( parser, target, &target); } else { ok = parse_call( parser, target, &target); } if (!ok) { break; } fx_queue_push_back( &ctx.expr_out_queue, &target->n_entry); ctx.expr_prev = EXPR_C_OPERAND; break; } struct ivy_ast_node *subexpr = NULL; discard_token(parser); ok = parse_expr(parser, EXPR_PARSE_NORMAL, &subexpr); if (!ok) { break; } if (!parse_symbol(parser, IVY_SYM_RIGHT_PAREN)) { parse_trace( parser, "expected `)` after " "parenthesized " "expression"); ok = false; break; } fx_queue_push_back( &ctx.expr_out_queue, &subexpr->n_entry); ctx.expr_prev = EXPR_C_OPERAND; break; } case IVY_SYM_LEFT_BRACKET: { if (flags & EXPR_PARSE_STOP_LEFT_BRACKET) { done = true; break; } if (ctx.expr_prev == EXPR_C_OPERAND) { struct ivy_ast_node *container = pop_operand( &ctx, IVY_PRECEDENCE_SUBSCRIPT); if (!container) { parse_trace( parser, "invalid subscript " "container"); return false; } struct ivy_ast_node *v = NULL; ok = parse_index(parser, container, &v); if (!ok) { break; } fx_queue_push_back( &ctx.expr_out_queue, &v->n_entry); ctx.expr_prev = EXPR_C_OPERAND; break; } struct ivy_ast_node *block = NULL; ok = parse_block(parser, &block); if (ok) { fx_queue_push_back( &ctx.expr_out_queue, &block->n_entry); ctx.expr_prev = EXPR_C_OPERAND; } break; } case IVY_SYM_LEFT_BRACE: { if (ctx.expr_prev == EXPR_C_OPERAND) { parse_trace( parser, "package cannot be " "used after " "operand"); ok = false; break; } struct ivy_ast_node *pkg = NULL; ok = parse_package(parser, &pkg); if (ok) { fx_queue_push_back( &ctx.expr_out_queue, &pkg->n_entry); ctx.expr_prev = EXPR_C_OPERAND; } break; } case IVY_SYM_DOT: { struct ivy_token *dot = tok; struct ivy_token *next = peek_next_token(parser); if (next && next->t_type == IVY_TOK_LINEFEED) { if (flags & EXPR_PARSE_CONSUME_TERMINATOR) { discard_token(parser); } done = true; break; } ok = parse_binary_operator(parser, &ctx); break; } case IVY_SYM_CARET: { if (expr_is_empty(&ctx)) { if (return_expr) { parse_trace( parser, "`^` can only " "be used " "once in " "an " "expression"); ok = false; break; } discard_token(parser); return_expr = true; break; } ok = parse_binary_operator(parser, &ctx); break; } case IVY_SYM_RIGHT_PAREN: case IVY_SYM_RIGHT_BRACE: case IVY_SYM_RIGHT_BRACKET: done = true; break; default: parse_trace( parser, "unrecognised symbol %s", ivy_symbol_to_string(tok->t_symbol)); ok = false; break; } break; default: parse_trace( parser, "unrecognised token %s", ivy_lex_token_type_to_string(tok->t_type)); ok = false; break; } if (!ok) { break; } } if (!ok) { cleanup_expr(&ctx); return false; } ok = finalise_expr(&ctx, out, IVY_PRECEDENCE_ASSIGN); if (!ok) { cleanup_expr(&ctx); return false; } if (return_expr) { struct ivy_ast_return_node *ret = create_node(IVY_AST_RETURN); ret->n_val = *out; *out = generic_node(ret); } return true; }