the cascade operator now uses the precedence of the last message parsed to determine the recipient of the subsequent messages, reducing the need for parentheses and making the semantics of the operator more predictable. all messages in a cascade now inherit the precedence of the first message in the cascade.
1044 lines
29 KiB
C
1044 lines
29 KiB
C
#include "../node.h"
|
|
#include "expr.h"
|
|
|
|
#include <blue/object/string.h>
|
|
#include <ivy/lang/lex.h>
|
|
#include <ivy/lang/operator.h>
|
|
#include <stdio.h>
|
|
|
|
static void print_operand(struct ivy_ast_node *node)
|
|
{
|
|
switch (node->n_type) {
|
|
case IVY_AST_IDENT: {
|
|
struct ivy_ast_ident_node *ident
|
|
= (struct ivy_ast_ident_node *)node;
|
|
printf("%s", ident->n_content->t_str);
|
|
break;
|
|
}
|
|
case IVY_AST_INT: {
|
|
struct ivy_ast_int_node *v = (struct ivy_ast_int_node *)node;
|
|
printf("%llu", v->n_value->t_int);
|
|
break;
|
|
}
|
|
case IVY_AST_DOUBLE: {
|
|
struct ivy_ast_double_node *v = (struct ivy_ast_double_node *)node;
|
|
printf("%.2lf", v->n_value->t_double);
|
|
break;
|
|
}
|
|
case IVY_AST_OP: {
|
|
struct ivy_ast_op_node *v = (struct ivy_ast_op_node *)node;
|
|
printf("%s", ivy_operator_id_to_string(v->n_op->op_id));
|
|
break;
|
|
}
|
|
case IVY_AST_MSG: {
|
|
struct ivy_ast_msg_node *v = (struct ivy_ast_msg_node *)node;
|
|
if (v->n_sel->n_msg_name) {
|
|
printf("%s()", v->n_sel->n_msg_name->t_str);
|
|
} else {
|
|
printf("<keyword-msg>");
|
|
}
|
|
break;
|
|
}
|
|
case IVY_AST_STRING: {
|
|
struct ivy_ast_string_node *v = (struct ivy_ast_string_node *)node;
|
|
printf("\"%s\"", v->n_value->t_str);
|
|
break;
|
|
}
|
|
default:
|
|
printf("<node>");
|
|
break;
|
|
}
|
|
}
|
|
|
|
enum ivy_status arith_push_operand(struct expr_parser_state *state, struct ivy_token *tok)
|
|
{
|
|
switch (tok->t_type) {
|
|
case IVY_TOK_IDENT: {
|
|
struct ivy_ast_ident_node *v
|
|
= (struct ivy_ast_ident_node *)ast_node_create(
|
|
IVY_AST_IDENT);
|
|
v->n_content = tok;
|
|
b_queue_push_back(&state->s_output_queue, &v->n_base.n_entry);
|
|
break;
|
|
}
|
|
case IVY_TOK_INT: {
|
|
struct ivy_ast_int_node *v
|
|
= (struct ivy_ast_int_node *)ast_node_create(IVY_AST_INT);
|
|
v->n_value = tok;
|
|
b_queue_push_back(&state->s_output_queue, &v->n_base.n_entry);
|
|
break;
|
|
}
|
|
case IVY_TOK_DOUBLE: {
|
|
struct ivy_ast_double_node *v
|
|
= (struct ivy_ast_double_node *)ast_node_create(
|
|
IVY_AST_DOUBLE);
|
|
v->n_value = tok;
|
|
b_queue_push_back(&state->s_output_queue, &v->n_base.n_entry);
|
|
break;
|
|
}
|
|
case IVY_TOK_STRING: {
|
|
struct ivy_ast_string_node *v
|
|
= (struct ivy_ast_string_node *)ast_node_create(
|
|
IVY_AST_STRING);
|
|
v->n_value = tok;
|
|
b_queue_push_back(&state->s_output_queue, &v->n_base.n_entry);
|
|
break;
|
|
}
|
|
case IVY_TOK_SYMBOL: {
|
|
if (tok->t_symbol != IVY_SYM_UNDERSCORE) {
|
|
return IVY_ERR_BAD_SYNTAX;
|
|
}
|
|
|
|
struct ivy_ast_node *v = ast_node_create(IVY_AST_DISCARD);
|
|
b_queue_push_back(&state->s_output_queue, &v->n_entry);
|
|
break;
|
|
}
|
|
default:
|
|
return IVY_ERR_BAD_SYNTAX;
|
|
}
|
|
|
|
return IVY_OK;
|
|
}
|
|
|
|
static const struct ivy_operator *get_operator_from_token(struct ivy_token *tok)
|
|
{
|
|
switch (tok->t_type) {
|
|
case IVY_TOK_SYMBOL:
|
|
return ivy_operator_get_by_token(tok->t_symbol);
|
|
case IVY_TOK_KEYWORD:
|
|
return ivy_operator_get_by_token(tok->t_keyword);
|
|
default:
|
|
return ivy_operator_get_by_token(tok->t_type);
|
|
}
|
|
}
|
|
|
|
static const struct ivy_operator *get_operator_from_node(struct ivy_ast_node *n)
|
|
{
|
|
switch (n->n_type) {
|
|
case IVY_AST_OP: {
|
|
struct ivy_ast_op_node *op = (struct ivy_ast_op_node *)n;
|
|
return op->n_op;
|
|
}
|
|
case IVY_AST_MSG:
|
|
return ivy_operator_get_by_id(IVY_OP_MSG);
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static struct ivy_ast_selector_node *unary_selector_from_token(struct ivy_token *tok)
|
|
{
|
|
struct ivy_ast_selector_node *sel
|
|
= (struct ivy_ast_selector_node *)ast_node_create(IVY_AST_SELECTOR);
|
|
sel->n_msg_name = tok;
|
|
return sel;
|
|
}
|
|
|
|
static struct ivy_ast_node *create_operator_node_from_token(struct ivy_token *tok)
|
|
{
|
|
const struct ivy_operator *op = get_operator_from_token(tok);
|
|
if (!op) {
|
|
return NULL;
|
|
}
|
|
|
|
if (op->op_id == IVY_OP_MSG) {
|
|
struct ivy_ast_msg_node *new_msg_node
|
|
= (struct ivy_ast_msg_node *)ast_node_create(IVY_AST_MSG);
|
|
new_msg_node->n_sel = unary_selector_from_token(tok);
|
|
return (struct ivy_ast_node *)new_msg_node;
|
|
}
|
|
|
|
struct ivy_ast_op_node *new_op_node
|
|
= (struct ivy_ast_op_node *)ast_node_create(IVY_AST_OP);
|
|
new_op_node->n_op = op;
|
|
return (struct ivy_ast_node *)new_op_node;
|
|
}
|
|
|
|
static void print_expr_queues(struct expr_parser_state *state)
|
|
{
|
|
b_queue_iterator it = {0};
|
|
|
|
printf("operators:");
|
|
b_queue_foreach (&it, &state->s_operator_stack) {
|
|
struct ivy_ast_node *n
|
|
= b_unbox(struct ivy_ast_node, it.entry, n_entry);
|
|
fputc(' ', stdout);
|
|
print_operand(n);
|
|
}
|
|
|
|
printf("\noperands:");
|
|
b_queue_foreach (&it, &state->s_output_queue) {
|
|
struct ivy_ast_node *n
|
|
= b_unbox(struct ivy_ast_node, it.entry, n_entry);
|
|
fputc(' ', stdout);
|
|
print_operand(n);
|
|
}
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
void arith_push_operator(struct expr_parser_state *state, struct ivy_ast_node *node)
|
|
{
|
|
const struct ivy_operator *op = get_operator_from_node(node);
|
|
if (!op) {
|
|
return;
|
|
}
|
|
|
|
while (true) {
|
|
b_queue_entry *top = b_queue_last(&state->s_operator_stack);
|
|
if (!top) {
|
|
break;
|
|
}
|
|
|
|
struct ivy_ast_node *top_node
|
|
= b_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;
|
|
}
|
|
|
|
b_queue_delete(&state->s_operator_stack, top);
|
|
b_queue_push_back(&state->s_output_queue, top);
|
|
}
|
|
|
|
b_queue_push_back(&state->s_operator_stack, &node->n_entry);
|
|
print_expr_queues(state);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
enum ivy_status expr_finalise_arith(
|
|
struct expr_parser_state *state, struct ivy_ast_node **expr_tree,
|
|
enum ivy_operator_precedence minimum_precedence)
|
|
{
|
|
b_queue_iterator it = {0};
|
|
|
|
/* first, take all the operators still left on the operator stack and
|
|
* add them to the output queue.
|
|
*
|
|
* since each set of parentheses has its own expression context,
|
|
* we don't have to handle parentheses here */
|
|
while (true) {
|
|
b_queue_entry *entry = b_queue_pop_back(&state->s_operator_stack);
|
|
if (!entry) {
|
|
break;
|
|
}
|
|
|
|
struct ivy_ast_node *node
|
|
= b_unbox(struct ivy_ast_node, entry, n_entry);
|
|
if (!node) {
|
|
/* this should never happen */
|
|
return IVY_ERR_INTERNAL_FAILURE;
|
|
}
|
|
|
|
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 IVY_ERR_INTERNAL_FAILURE;
|
|
}
|
|
|
|
/* 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) {
|
|
b_queue_push_back(&state->s_operator_stack, entry);
|
|
break;
|
|
}
|
|
|
|
b_queue_push_back(&state->s_output_queue, entry);
|
|
}
|
|
|
|
#if 0
|
|
printf("** after linearisation:\n");
|
|
print_expr_queues(state);
|
|
#endif
|
|
|
|
/* next, step through the output queue and build a tree of ast nodes
|
|
* representing the expression. the queue is in RPM, where operators
|
|
* always follow their operands, so a queue of operands is needed
|
|
* for the conversion. */
|
|
|
|
b_queue q = B_QUEUE_INIT;
|
|
b_queue_entry *tmp = NULL;
|
|
b_queue_iterator_begin(&state->s_output_queue, &it);
|
|
int i = 0;
|
|
|
|
while (b_queue_iterator_is_valid(&it)) {
|
|
struct ivy_ast_node *item
|
|
= b_unbox(struct ivy_ast_node, it.entry, n_entry);
|
|
b_queue_iterator_erase(&it);
|
|
|
|
/* 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 */
|
|
b_queue_push_back(&q, &item->n_entry);
|
|
continue;
|
|
}
|
|
|
|
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 = b_queue_pop_back(&q);
|
|
msg->n_recipient = b_unbox(
|
|
struct ivy_ast_node, tmp, n_entry);
|
|
}
|
|
b_queue_push_back(&q, &msg->n_base.n_entry);
|
|
continue;
|
|
}
|
|
|
|
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)) {
|
|
b_queue_push_back(&q, &item->n_entry);
|
|
continue;
|
|
}
|
|
|
|
/* otherwise, pop the relevant operands from the operand
|
|
* queue... */
|
|
op = op_node->n_op;
|
|
tmp = b_queue_pop_back(&q);
|
|
op_node->n_right = b_unbox(struct ivy_ast_node, tmp, n_entry);
|
|
|
|
if (op->op_arity == IVY_OP_BINARY) {
|
|
tmp = b_queue_pop_back(&q);
|
|
op_node->n_left
|
|
= b_unbox(struct ivy_ast_node, tmp, n_entry);
|
|
}
|
|
|
|
/* ...and push the newly-completed operator node to the operand
|
|
* queue */
|
|
b_queue_push_back(&q, &op_node->n_base.n_entry);
|
|
}
|
|
|
|
#if 0
|
|
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. */
|
|
b_queue_foreach (&it, &state->s_operator_stack) {
|
|
b_queue_entry *entry = b_queue_pop_front(&q);
|
|
if (!entry) {
|
|
return IVY_ERR_INTERNAL_FAILURE;
|
|
}
|
|
|
|
b_queue_push_back(&state->s_output_queue, entry);
|
|
}
|
|
|
|
#if 0
|
|
printf("** after de-linearisation:\n");
|
|
print_expr_queues(state);
|
|
ivy_ast_node_print(*expr_tree);
|
|
printf("------\n");
|
|
#endif
|
|
|
|
/* the final node remaining on the temp operand stack is the root node
|
|
* of the new expression tree */
|
|
tmp = b_queue_pop_back(&q);
|
|
*expr_tree = b_unbox(struct ivy_ast_node, tmp, n_entry);
|
|
|
|
return IVY_OK;
|
|
}
|
|
|
|
struct token_parse_result arith_parse_operand(
|
|
struct ivy_parser *ctx, struct ivy_token *tok)
|
|
{
|
|
struct expr_parser_state *state
|
|
= parser_get_state(ctx, struct expr_parser_state);
|
|
|
|
if (state->s_type == EXPR_TYPE_STMT) {
|
|
return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0);
|
|
}
|
|
|
|
if (state->s_prev_component == EXPR_CMP_OPERAND) {
|
|
return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0);
|
|
}
|
|
|
|
state->s_type = EXPR_TYPE_ARITH;
|
|
|
|
arith_push_operand(state, tok);
|
|
// print_expr_queues(state);
|
|
state->s_prev_component = EXPR_CMP_OPERAND;
|
|
state->s_prev_token = tok->t_type;
|
|
|
|
return PARSE_RESULT(IVY_OK, 0);
|
|
}
|
|
|
|
struct token_parse_result arith_parse_operator(
|
|
struct ivy_parser *ctx, struct ivy_token *tok)
|
|
{
|
|
struct expr_parser_state *state
|
|
= parser_get_state(ctx, struct expr_parser_state);
|
|
|
|
if (state->s_type != EXPR_TYPE_ARITH) {
|
|
return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0);
|
|
}
|
|
|
|
switch (tok->t_type) {
|
|
case IVY_TOK_SYMBOL:
|
|
state->s_prev_token = tok->t_symbol;
|
|
break;
|
|
case IVY_TOK_KEYWORD:
|
|
state->s_prev_token = tok->t_keyword;
|
|
break;
|
|
default:
|
|
state->s_prev_token = tok->t_type;
|
|
break;
|
|
}
|
|
|
|
struct ivy_ast_node *op = create_operator_node_from_token(tok);
|
|
if (!op) {
|
|
return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0);
|
|
}
|
|
|
|
arith_push_operator(state, op);
|
|
state->s_prev_component = EXPR_CMP_OPERATOR;
|
|
|
|
print_expr_queues(state);
|
|
return PARSE_RESULT(IVY_OK, 0);
|
|
}
|
|
|
|
struct token_parse_result arith_parse_in(
|
|
struct ivy_parser *ctx, struct ivy_token *tok)
|
|
{
|
|
struct expr_parser_state *state
|
|
= parser_get_state(ctx, struct expr_parser_state);
|
|
|
|
if (state->s_terminator == IVY_KW_IN) {
|
|
state->s_prev_token = IVY_KW_IN;
|
|
return expr_finalise_and_return(ctx, state);
|
|
}
|
|
|
|
return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0);
|
|
}
|
|
|
|
struct token_parse_result arith_parse_ident(
|
|
struct ivy_parser *ctx, struct ivy_token *tok)
|
|
{
|
|
struct expr_parser_state *state
|
|
= parser_get_state(ctx, struct expr_parser_state);
|
|
|
|
struct token_parse_result result;
|
|
|
|
if (state->s_sub_type == EXPR_SUBTYPE_MSG) {
|
|
if (state->s_prev_component == EXPR_CMP_MSG) {
|
|
return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0);
|
|
}
|
|
|
|
|
|
result = arith_parse_operator(ctx, tok);
|
|
state->s_prev_component = EXPR_CMP_MSG;
|
|
return result;
|
|
}
|
|
|
|
if (state->s_prev_component == EXPR_CMP_OPERAND
|
|
|| state->s_prev_component == EXPR_CMP_MSG) {
|
|
result = arith_parse_operator(ctx, tok);
|
|
state->s_prev_component = EXPR_CMP_MSG;
|
|
} else {
|
|
result = arith_parse_operand(ctx, tok);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
struct token_parse_result arith_parse_left_paren(
|
|
struct ivy_parser *ctx, struct ivy_token *tok)
|
|
{
|
|
struct expr_parser_state *state
|
|
= parser_get_state(ctx, struct expr_parser_state);
|
|
|
|
if (state->s_type == EXPR_TYPE_NONE) {
|
|
state->s_type = EXPR_TYPE_ARITH;
|
|
}
|
|
|
|
if (state->s_type != EXPR_TYPE_ARITH) {
|
|
return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0);
|
|
}
|
|
|
|
if (state->s_prev_token == IVY_TOK_IDENT) {
|
|
/* this is the opening parenthesis for a complex message. */
|
|
b_queue_entry *msg_entry
|
|
= b_queue_pop_back(&state->s_operator_stack);
|
|
struct ivy_ast_node *msg
|
|
= b_unbox(struct ivy_ast_node, msg_entry, n_entry);
|
|
|
|
if (msg->n_type != IVY_AST_MSG) {
|
|
return PARSE_RESULT(IVY_ERR_INTERNAL_FAILURE, 0);
|
|
}
|
|
|
|
struct expr_parser_state *msg_expr
|
|
= (struct expr_parser_state *)parser_push_state(
|
|
ctx, IVY_AST_EXPR, 0);
|
|
|
|
msg_expr->s_msg = (struct ivy_ast_msg_node *)msg;
|
|
msg_expr->s_sub_type = EXPR_SUBTYPE_COMPLEX_MSG;
|
|
msg_expr->s_type = EXPR_TYPE_ARITH;
|
|
msg_expr->s_subexpr_depth = state->s_subexpr_depth + 1;
|
|
|
|
return PARSE_RESULT(IVY_OK, 0);
|
|
}
|
|
|
|
/* a sub-expression surrounded by parentheses is parsed by creating
|
|
* a sub-expression parser state */
|
|
struct expr_parser_state *sub_expr
|
|
= (struct expr_parser_state *)parser_push_state(
|
|
ctx, IVY_AST_EXPR, 0);
|
|
sub_expr->s_paren_depth = state->s_paren_depth + 1;
|
|
sub_expr->s_subexpr_depth = state->s_subexpr_depth + 1;
|
|
|
|
return PARSE_RESULT(IVY_OK, 0);
|
|
}
|
|
|
|
static struct ivy_ast_selector_node *keyword_selector_from_label_list(b_queue *labels)
|
|
{
|
|
struct ivy_ast_selector_node *sel
|
|
= (struct ivy_ast_selector_node *)ast_node_create(IVY_AST_SELECTOR);
|
|
|
|
b_queue_iterator it = {0};
|
|
|
|
b_queue_iterator_begin(labels, &it);
|
|
while (b_queue_iterator_is_valid(&it)) {
|
|
struct ivy_token *label
|
|
= b_unbox(struct ivy_token, it.entry, t_entry);
|
|
b_queue_iterator_erase(&it);
|
|
b_queue_push_back(&sel->n_arg_labels, &label->t_entry);
|
|
}
|
|
|
|
return sel;
|
|
}
|
|
|
|
static struct ivy_ast_cascade_node *expr_finalise_cascade(struct expr_parser_state *state)
|
|
{
|
|
struct ivy_ast_cascade_node *cascade
|
|
= (struct ivy_ast_cascade_node *)ast_node_create(IVY_AST_CASCADE);
|
|
|
|
cascade->n_recipient = state->s_recipient;
|
|
|
|
b_queue_iterator it = {0};
|
|
b_queue_iterator_begin(&state->s_cascade_msg, &it);
|
|
while (b_queue_iterator_is_valid(&it)) {
|
|
struct ivy_ast_node *msg
|
|
= b_unbox(struct ivy_ast_node, it.entry, n_entry);
|
|
b_queue_iterator_erase(&it);
|
|
b_queue_push_back(&cascade->n_msg, &msg->n_entry);
|
|
}
|
|
|
|
return cascade;
|
|
}
|
|
|
|
static struct ivy_ast_msg_node *expr_finalise_keyword_msg(struct expr_parser_state *state)
|
|
{
|
|
struct ivy_ast_msg_node *msg
|
|
= (struct ivy_ast_msg_node *)ast_node_create(IVY_AST_MSG);
|
|
|
|
msg->n_recipient = state->s_recipient;
|
|
msg->n_sel = keyword_selector_from_label_list(&state->s_labels);
|
|
|
|
b_queue_iterator it = {0};
|
|
|
|
b_queue_iterator_begin(&state->s_args, &it);
|
|
while (b_queue_iterator_is_valid(&it)) {
|
|
struct ivy_ast_node *arg
|
|
= b_unbox(struct ivy_ast_node, it.entry, n_entry);
|
|
b_queue_iterator_erase(&it);
|
|
|
|
b_queue_push_back(&msg->n_arg, &arg->n_entry);
|
|
}
|
|
|
|
return msg;
|
|
}
|
|
|
|
static struct ivy_ast_msg_node *expr_finalise_complex_msg(struct expr_parser_state *state)
|
|
{
|
|
struct ivy_ast_msg_node *msg = state->s_msg;
|
|
if (!msg) {
|
|
return NULL;
|
|
}
|
|
|
|
state->s_msg = NULL;
|
|
|
|
b_queue_iterator it = {0};
|
|
b_queue_iterator_begin(&state->s_labels, &it);
|
|
while (b_queue_iterator_is_valid(&it)) {
|
|
struct ivy_token *label
|
|
= b_unbox(struct ivy_token, it.entry, t_entry);
|
|
b_queue_iterator_erase(&it);
|
|
b_queue_push_back(&msg->n_sel->n_arg_labels, &label->t_entry);
|
|
}
|
|
|
|
b_queue_iterator_begin(&state->s_args, &it);
|
|
while (b_queue_iterator_is_valid(&it)) {
|
|
struct ivy_ast_node *arg
|
|
= b_unbox(struct ivy_ast_node, it.entry, n_entry);
|
|
b_queue_iterator_erase(&it);
|
|
|
|
b_queue_push_back(&msg->n_arg, &arg->n_entry);
|
|
}
|
|
|
|
return msg;
|
|
}
|
|
|
|
struct token_parse_result arith_parse_right_paren(
|
|
struct ivy_parser *ctx, struct ivy_token *tok)
|
|
{
|
|
struct expr_parser_state *state
|
|
= parser_get_state(ctx, struct expr_parser_state);
|
|
|
|
if (state->s_type != EXPR_TYPE_ARITH) {
|
|
return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0);
|
|
}
|
|
|
|
if (state->s_paren_depth == 0 && state->s_sub_type == EXPR_SUBTYPE_NONE) {
|
|
return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0);
|
|
}
|
|
|
|
state->s_prev_token = IVY_SYM_RIGHT_PAREN;
|
|
return expr_finalise_and_return(ctx, state);
|
|
}
|
|
|
|
static enum ivy_status begin_cascade_operation(struct ivy_parser *ctx)
|
|
{
|
|
struct expr_parser_state *state = parser_get_state(ctx, struct expr_parser_state);
|
|
|
|
/* this is the beginning of a cascade operation */
|
|
|
|
struct ivy_ast_msg_node *first_msg = NULL;
|
|
|
|
if (state->s_sub_type == EXPR_SUBTYPE_KEYWORD_MSG) {
|
|
first_msg = expr_finalise_keyword_msg(state);
|
|
parser_pop_state(ctx, 0);
|
|
} else {
|
|
/* we need to find who the first message in the cascade is being sent to. */
|
|
enum ivy_operator_precedence min_precedence = IVY_PRECEDENCE_CASCADE;
|
|
|
|
struct ivy_ast_node *prev = b_unbox(
|
|
struct ivy_ast_node,
|
|
b_queue_last(&state->s_operator_stack), n_entry);
|
|
if (prev && prev->n_type == IVY_AST_MSG) {
|
|
/* unary complex messages (which will be found on the operator stack) have a very high
|
|
* precedence (much higher than most arithmetic operators), so the recipient will be much
|
|
* narrower. this also means that any subsequent messages in the cascade inherit this high
|
|
* precedence, regardless of their type. */
|
|
min_precedence = IVY_PRECEDENCE_UNARY_MSG;
|
|
}
|
|
|
|
struct ivy_ast_node *expr = NULL;
|
|
enum ivy_status status
|
|
= expr_finalise_arith(state, &expr, min_precedence);
|
|
if (status != IVY_OK) {
|
|
return status;
|
|
}
|
|
|
|
if (expr->n_type != IVY_AST_MSG) {
|
|
/* the recipient and first message of the cascade operation is ambiguous due
|
|
* to operator precedence. this is usually resolved by adding parentheses
|
|
* (either around the recipient or around the cascade) to make the recipient
|
|
* and first message clear. */
|
|
return IVY_ERR_BAD_SYNTAX;
|
|
}
|
|
|
|
first_msg = (struct ivy_ast_msg_node *)expr;
|
|
}
|
|
|
|
struct expr_parser_state *cascade_expr
|
|
= (struct expr_parser_state *)parser_push_state(
|
|
ctx, IVY_AST_EXPR, 0);
|
|
|
|
if (!first_msg) {
|
|
return IVY_ERR_BAD_SYNTAX;
|
|
}
|
|
|
|
cascade_expr->s_recipient = first_msg->n_recipient;
|
|
first_msg->n_recipient = NULL;
|
|
|
|
cascade_expr->s_sub_type = EXPR_SUBTYPE_CASCADE;
|
|
cascade_expr->s_type = EXPR_TYPE_ARITH;
|
|
cascade_expr->s_subexpr_depth = state->s_subexpr_depth + 1;
|
|
|
|
b_queue_push_back(&cascade_expr->s_cascade_msg, &first_msg->n_base.n_entry);
|
|
|
|
struct expr_parser_state *msg_expr
|
|
= (struct expr_parser_state *)parser_push_state(
|
|
ctx, IVY_AST_EXPR, 0);
|
|
msg_expr->s_sub_type = EXPR_SUBTYPE_MSG;
|
|
msg_expr->s_type = EXPR_TYPE_ARITH;
|
|
msg_expr->s_subexpr_depth = state->s_subexpr_depth + 1;
|
|
|
|
return IVY_OK;
|
|
}
|
|
|
|
struct token_parse_result arith_parse_semicolon(
|
|
struct ivy_parser *ctx, struct ivy_token *tok)
|
|
{
|
|
struct expr_parser_state *state
|
|
= parser_get_state(ctx, struct expr_parser_state);
|
|
|
|
if (state->s_type != EXPR_TYPE_ARITH) {
|
|
return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0);
|
|
}
|
|
|
|
bool end_subexpr = false;
|
|
|
|
switch (state->s_sub_type) {
|
|
case EXPR_SUBTYPE_KEYWORD_ARG:
|
|
case EXPR_SUBTYPE_MSG:
|
|
end_subexpr = true;
|
|
break;
|
|
case EXPR_SUBTYPE_KEYWORD_MSG:
|
|
case EXPR_SUBTYPE_CASCADE:
|
|
case EXPR_SUBTYPE_NONE:
|
|
break;
|
|
default:
|
|
return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0);
|
|
break;
|
|
}
|
|
|
|
if (end_subexpr) {
|
|
/* finish parsing this expression and let the parent context handle the semicolon. */
|
|
struct ivy_ast_node *expr = NULL;
|
|
enum ivy_status status
|
|
= expr_finalise_arith(state, &expr, IVY_PRECEDENCE_ASSIGN);
|
|
if (status != IVY_OK) {
|
|
return PARSE_RESULT(status, 0);
|
|
}
|
|
|
|
parser_replace_current_node(ctx, expr);
|
|
parser_pop_state(ctx, STATE_ADD_NODE_TO_PARENT);
|
|
return PARSE_RESULT(IVY_OK, PARSE_REPEAT_TOKEN);
|
|
}
|
|
|
|
struct expr_parser_state *parent_state = parser_get_parent_state(ctx, IVY_AST_EXPR, struct expr_parser_state);
|
|
|
|
if (state->s_sub_type == EXPR_SUBTYPE_CASCADE) {
|
|
/* this is another message in a cascade series. */
|
|
struct expr_parser_state *msg_expr
|
|
= (struct expr_parser_state *)parser_push_state(
|
|
ctx, IVY_AST_EXPR, 0);
|
|
|
|
msg_expr->s_recipient = NULL;
|
|
msg_expr->s_sub_type = EXPR_SUBTYPE_MSG;
|
|
msg_expr->s_type = EXPR_TYPE_ARITH;
|
|
msg_expr->s_subexpr_depth = state->s_subexpr_depth + 1;
|
|
|
|
return PARSE_RESULT(IVY_OK, 0);
|
|
}
|
|
|
|
if (state->s_sub_type == EXPR_SUBTYPE_KEYWORD_MSG && !state->s_recipient) {
|
|
struct ivy_ast_msg_node *msg = expr_finalise_keyword_msg(state);
|
|
if (!msg) {
|
|
return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0);
|
|
}
|
|
|
|
parser_replace_current_node(ctx, (struct ivy_ast_node *)msg);
|
|
parser_pop_state(ctx, STATE_ADD_NODE_TO_PARENT);
|
|
return PARSE_RESULT(IVY_OK, PARSE_REPEAT_TOKEN);
|
|
}
|
|
|
|
enum ivy_status status = begin_cascade_operation(ctx);
|
|
|
|
return PARSE_RESULT(status, 0);
|
|
}
|
|
|
|
struct token_parse_result expr_finalise(
|
|
struct ivy_parser *ctx, struct expr_parser_state *state,
|
|
enum ivy_operator_precedence min_precedence, struct ivy_ast_node **result)
|
|
{
|
|
int flags = 0;
|
|
if (state->s_subexpr_depth > 0) {
|
|
flags = PARSE_REPEAT_TOKEN;
|
|
}
|
|
|
|
if (state->s_paren_depth > 0 && state->s_prev_token == IVY_SYM_RIGHT_PAREN) {
|
|
/* this is the ending right paren in a parenthesised expression, so we need to consume it. */
|
|
flags = 0;
|
|
}
|
|
|
|
if (state->s_sub_type == EXPR_SUBTYPE_MSG) {
|
|
/* this is the end of a unary message (probably in a cascade operation). */
|
|
struct ivy_ast_node *expr = NULL;
|
|
enum ivy_status status = expr_finalise_arith(
|
|
state, &expr, IVY_PRECEDENCE_CASCADE);
|
|
if (status != IVY_OK) {
|
|
return PARSE_RESULT(status, 0);
|
|
}
|
|
|
|
*result = expr;
|
|
return PARSE_RESULT(IVY_OK, PARSE_REPEAT_TOKEN);
|
|
}
|
|
|
|
if (state->s_sub_type == EXPR_SUBTYPE_CASCADE) {
|
|
/* this is the end of a cascade operation */
|
|
struct ivy_ast_cascade_node *cascade
|
|
= expr_finalise_cascade(state);
|
|
|
|
*result = (struct ivy_ast_node *)cascade;
|
|
return PARSE_RESULT(IVY_OK, flags);
|
|
}
|
|
|
|
if (state->s_sub_type == EXPR_SUBTYPE_COMPLEX_MSG) {
|
|
/* this is the end of a keyword-message */
|
|
struct ivy_ast_msg_node *msg = expr_finalise_complex_msg(state);
|
|
*result = (struct ivy_ast_node *)msg;
|
|
return PARSE_RESULT(IVY_OK, 0);
|
|
}
|
|
|
|
if (state->s_sub_type == EXPR_SUBTYPE_KEYWORD_MSG) {
|
|
/* this is the end of a keyword-message */
|
|
struct ivy_ast_msg_node *msg = expr_finalise_keyword_msg(state);
|
|
*result = (struct ivy_ast_node *)msg;
|
|
return PARSE_RESULT(IVY_OK, flags);
|
|
}
|
|
|
|
/* this is the end of a regular expression or sub-expression */
|
|
struct ivy_ast_node *expr = NULL;
|
|
enum ivy_status status
|
|
= expr_finalise_arith(state, &expr, min_precedence);
|
|
if (status != IVY_OK) {
|
|
return PARSE_RESULT(status, 0);
|
|
}
|
|
|
|
*result = expr;
|
|
return PARSE_RESULT(IVY_OK, flags);
|
|
}
|
|
|
|
struct token_parse_result expr_finalise_and_return(
|
|
struct ivy_parser *ctx, struct expr_parser_state *state)
|
|
{
|
|
struct ivy_ast_node *expr_node = NULL;
|
|
struct token_parse_result result = expr_finalise(ctx, state, IVY_PRECEDENCE_ASSIGN, &expr_node);
|
|
|
|
if (result.r_status != IVY_OK) {
|
|
return result;
|
|
}
|
|
|
|
parser_replace_current_node(ctx, expr_node);
|
|
parser_pop_state(ctx, STATE_ADD_NODE_TO_PARENT);
|
|
return result;
|
|
}
|
|
|
|
struct token_parse_result arith_parse_dot(
|
|
struct ivy_parser *ctx, struct ivy_token *tok)
|
|
{
|
|
struct expr_parser_state *state
|
|
= parser_get_state(ctx, struct expr_parser_state);
|
|
|
|
if (state->s_type != EXPR_TYPE_ARITH) {
|
|
return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0);
|
|
}
|
|
|
|
if (state->s_paren_depth > 0) {
|
|
/* end-of-expression with mismatched parentheses */
|
|
return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0);
|
|
}
|
|
|
|
state->s_prev_token = IVY_SYM_DOT;
|
|
return expr_finalise_and_return(ctx, state);
|
|
}
|
|
|
|
struct token_parse_result arith_parse_comma(
|
|
struct ivy_parser *ctx, struct ivy_token *tok)
|
|
{
|
|
struct expr_parser_state *state
|
|
= parser_get_state(ctx, struct expr_parser_state);
|
|
|
|
if (state->s_type != EXPR_TYPE_ARITH) {
|
|
return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0);
|
|
}
|
|
|
|
if (state->s_paren_depth > 0 && (state->s_prev_component == EXPR_CMP_OPERAND || state->s_prev_component == EXPR_CMP_MSG)) {
|
|
/* tuple. */
|
|
return PARSE_RESULT(IVY_ERR_NOT_SUPPORTED, 0);
|
|
}
|
|
|
|
state->s_prev_token = IVY_SYM_DOT;
|
|
return expr_finalise_and_return(ctx, state);
|
|
}
|
|
|
|
extern struct token_parse_result arith_parse_equal_right_angle(
|
|
struct ivy_parser *ctx, struct ivy_token *tok)
|
|
{
|
|
struct expr_parser_state *state
|
|
= parser_get_state(ctx, struct expr_parser_state);
|
|
|
|
state->s_prev_token = IVY_SYM_EQUAL_RIGHT_ANGLE;
|
|
struct token_parse_result result = expr_finalise_and_return(ctx, state);
|
|
|
|
result.r_flags |= PARSE_REPEAT_TOKEN;
|
|
return result;
|
|
}
|
|
|
|
struct token_parse_result arith_parse_label(
|
|
struct ivy_parser *ctx, struct ivy_token *tok)
|
|
{
|
|
struct expr_parser_state *state
|
|
= parser_get_state(ctx, struct expr_parser_state);
|
|
|
|
if (state->s_type != EXPR_TYPE_ARITH) {
|
|
return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, 0);
|
|
}
|
|
|
|
if (state->s_terminator == IVY_TOK_LABEL) {
|
|
/* we are currently parsing a keyword or complex message
|
|
* argument, and have just encountered the label denoting the
|
|
* next argument. terminate here and propagate this label to the
|
|
* parent keyword-message parser context. */
|
|
struct ivy_ast_node *expr = NULL;
|
|
enum ivy_status status
|
|
= expr_finalise_arith(state, &expr, IVY_PRECEDENCE_ASSIGN);
|
|
if (status != IVY_OK) {
|
|
return PARSE_RESULT(status, 0);
|
|
}
|
|
|
|
parser_replace_current_node(ctx, expr);
|
|
parser_pop_state(ctx, STATE_ADD_NODE_TO_PARENT);
|
|
return PARSE_RESULT(IVY_ERR_BAD_SYNTAX, PARSE_REPEAT_TOKEN);
|
|
}
|
|
|
|
if (state->s_sub_type != EXPR_SUBTYPE_KEYWORD_MSG
|
|
&& state->s_sub_type != EXPR_SUBTYPE_COMPLEX_MSG) {
|
|
/* this is the beginning of a new keyword-message */
|
|
struct expr_parser_state *msg_expr;
|
|
bool new_parser = true;
|
|
|
|
if (b_queue_empty(&state->s_operator_stack)
|
|
&& b_queue_empty(&state->s_output_queue)) {
|
|
new_parser = false;
|
|
}
|
|
|
|
if (new_parser) {
|
|
msg_expr = (struct expr_parser_state *)parser_push_state(
|
|
ctx, IVY_AST_EXPR, 0);
|
|
} else {
|
|
msg_expr = state;
|
|
}
|
|
|
|
/* the only operator with a lower precedence than
|
|
* keyword-messages is assignment. everything in the preceding
|
|
* expression, up to but not including any un-parenthesised
|
|
* assignments, is the recipient of the message. */
|
|
struct ivy_ast_node *expr = NULL;
|
|
|
|
if (state->s_sub_type != EXPR_SUBTYPE_MSG) {
|
|
enum ivy_status status
|
|
= expr_finalise_arith(state, &expr, IVY_PRECEDENCE_KEYWORD_MSG);
|
|
if (status != IVY_OK) {
|
|
return PARSE_RESULT(status, 0);
|
|
}
|
|
}
|
|
|
|
msg_expr->s_recipient = expr;
|
|
msg_expr->s_sub_type = EXPR_SUBTYPE_KEYWORD_MSG;
|
|
msg_expr->s_type = EXPR_TYPE_ARITH;
|
|
msg_expr->s_subexpr_depth = new_parser ? state->s_subexpr_depth + 1 : 0;
|
|
state = msg_expr;
|
|
}
|
|
|
|
if (state->s_sub_type == EXPR_SUBTYPE_KEYWORD_MSG
|
|
|| state->s_sub_type == EXPR_SUBTYPE_COMPLEX_MSG) {
|
|
/* we may have just finished parsing a keyword-message argument,
|
|
* and this label marks the start of a new one. store the label
|
|
* and create a new argument parsing context. */
|
|
b_queue_push_back(&state->s_labels, &tok->t_entry);
|
|
|
|
struct expr_parser_state *arg_expr
|
|
= (struct expr_parser_state *)parser_push_state(
|
|
ctx, IVY_AST_EXPR, 0);
|
|
arg_expr->s_terminator = IVY_TOK_LABEL;
|
|
arg_expr->s_sub_type = EXPR_SUBTYPE_KEYWORD_ARG;
|
|
arg_expr->s_subexpr_depth = state->s_subexpr_depth + 1;
|
|
}
|
|
|
|
return PARSE_RESULT(IVY_OK, 0);
|
|
}
|
|
|
|
enum ivy_status arith_add_child(
|
|
struct parser_state *parent, struct ivy_ast_node *child)
|
|
{
|
|
struct expr_parser_state *state = (struct expr_parser_state *)parent;
|
|
|
|
if (state->s_sub_type == EXPR_SUBTYPE_CASCADE) {
|
|
/* treat the child node as a cascaded message */
|
|
b_queue_push_back(&state->s_cascade_msg, &child->n_entry);
|
|
} else if (state->s_sub_type == EXPR_SUBTYPE_KEYWORD_MSG
|
|
|| state->s_sub_type == EXPR_SUBTYPE_COMPLEX_MSG) {
|
|
/* treat the child node as a keyword-message argument */
|
|
b_queue_push_back(&state->s_args, &child->n_entry);
|
|
} else if (state->s_sub_type == EXPR_SUBTYPE_KEYWORD_ARG) {
|
|
/* treat the child node as a sub-expression enclosed in
|
|
* parentheses (i.e. an operand). */
|
|
b_queue_push_back(&state->s_output_queue, &child->n_entry);
|
|
state->s_prev_component = EXPR_CMP_OPERAND;
|
|
} else if (child->n_type == IVY_AST_MSG) {
|
|
arith_push_operator(state, child);
|
|
state->s_prev_component = EXPR_CMP_MSG;
|
|
} else {
|
|
/* treat the child node as a sub-expression enclosed in
|
|
* parentheses (i.e. an operand). */
|
|
b_queue_push_back(&state->s_output_queue, &child->n_entry);
|
|
state->s_prev_component = EXPR_CMP_OPERAND;
|
|
}
|
|
|
|
return IVY_OK;
|
|
} |