static struct ast_node* access_node(char *name) { struct ast_node_stub *stub_node; struct ast_node_access *ac_node; struct ast_node *idx; struct symbol *sym; sym = symbol_table_lookup_all(name); if (sym == NULL) { error_msg("error: no such symbol"); sync_stream(); stub_node = ast_node_stub(); return AST_NODE(stub_node); } /* `[' */ consume_token(); ac_node = ast_node_access(sym->v_type, sym->name); do { idx = or_expr(); if (idx == NULL) { error_msg("error: empty index"); sync_stream(); goto ac_error; } ast_node_access_add(ac_node, idx); if (current_token != TOKEN_RBRACKET) { error_msg("error: missed `]'"); sync_stream(); goto ac_error; } /* `]' */ consume_token(); } while (match(TOKEN_LBRACKET)); return AST_NODE(ac_node); ac_error: ast_node_unref(AST_NODE(ac_node)); stub_node = ast_node_stub(); return AST_NODE(stub_node); }
/** * Print a string constant or a variable value (no newline). * PUT <expression> */ void cmd_put(char *args) { int number_value; char *string_value; unsigned char token; while (! error) { token = next_token(args); if (token == TOKEN_STRING || token == TOKEN_VAR_STRING) { if (args = parse_string_expression(args, &string_value)) { lcd_puts(string_value); } } else if (token == TOKEN_DIGITS || token == TOKEN_PLUS || token == TOKEN_MINUS || token == TOKEN_VAR_NUMBER) { if (args = parse_number_expression(args, &number_value)) { sprintf(print_buffer, "%d", number_value); lcd_puts(print_buffer); } } else if (token == TOKEN_COMMA) { args = consume_token(args, token); } else if (token == TOKEN_END) { return; } else { syntax_error(); return; } } }
static struct ast_node* rest_or(struct ast_node *node) { struct ast_node *ret_node; struct ast_node *left; struct ast_node *right; ret_node = node; while (TRUE) { switch(current_token) { case TOKEN_OR : consume_token(); left = ret_node; right = and_expr(); if (right == NULL) { error_msg("error: expression expected after ||"); right = (struct ast_node *)ast_node_stub(); sync_stream(); } ret_node = (struct ast_node *)ast_node_logic_op(OPCODE_OR, left, right); continue; default: goto exit_or; } } exit_or: return ret_node; }
static struct ast_node* rest_and(struct ast_node *node) { struct ast_node *ret_node; struct ast_node *left; struct ast_node *right; ret_node = node; while (TRUE) { switch (current_token) { case TOKEN_AND: consume_token(); left = ret_node; right = rel_expr(); if (right == NULL) { error_msg("error: missed expression"); right = (struct ast_node *)ast_node_stub(); sync_stream(); } ret_node = (struct ast_node *)ast_node_logic_op(OPCODE_AND, left, right); continue; default: goto exit_and; } } exit_and: return ret_node; }
// Consume an expected semicolon, complaining if one was not found. void gobble_semicolon(std::vector<unique_ptr<Token>>::iterator* it, bool* ok) { *ok = consume_token(it, token::semiColon); if (!*ok) { cerr << "Expecting a semicolon but found " << (**it)->show() << " at line " << (**it)->line << std::endl; return; } }
/** * Conditional execution of a command. * IF <condition> THEN <command> */ void cmd_if(char *args) { int condition; if (args = parse_number_expression(args, &condition)) { if (condition) { if (args = consume_token(args, TOKEN_THEN)) { execute(args); } } } }
static void process_args(char *name) { struct function *func; struct symbol *arg; /*(*/ consume_token(); func = function_table_lookup(name); /* Delete an old function and make a new one */ if (func != NULL) function_table_delete_function(name); func = function_new(name); while (!match(TOKEN_RPARENTH)) { if (match(TOKEN_EOL)) { error_msg("error: new line in function definition"); return; } if (!match(TOKEN_ID)) { error_msg("error: unexpected symbol in definition"); sync_stream(); ufree(func); return; } arg = symbol_new(lex_prev.id, VALUE_TYPE_UNKNOWN); function_add_arg(func, arg); if (current_token != TOKEN_RPARENTH && !match(TOKEN_COMMA)) { error_msg("error: comma expected"); sync_stream(); return; } } switch(current_token) { case TOKEN_LBRACE: function_table_insert(func); process_function_body(name); break; default: error_msg("error: `{' expected"); sync_stream(); break; } }
/** * Set the cursor to a specific screen position. * AT <x>,<y> */ void cmd_at(char *args) { int x; int y; unsigned char token; args = parse_number_expression(args, &x); if (!args || x < 0 || x > 39) { syntax_error_invalid_argument(); return; } args = consume_token(args, TOKEN_COMMA); if (! args) { return; } args = parse_number_expression(args, &y); if (!args || y < 0 || y > 3) { syntax_error_invalid_argument(); return; } if (next_token(args) == TOKEN_COMMA) { args = consume_token(args, TOKEN_COMMA); if ((token = next_token(args)) == TOKEN_VAR_STRING) { unsigned int var_name; unsigned char var_type; if (args = parse_variable(args, &var_name, &var_type)) { char c = lcd_getc(x, y); sprintf (print_buffer, "%c", c); create_variable(var_name, VAR_TYPE_STRING, print_buffer); } } else { syntax_error_invalid_token(token); } } else { lcd_goto(x, y); } }
int programme(struct ast_node **root, int *eof) { struct ast_node *tree; errors = 0; consume_token(); tree = stmts(NULL); *root = (struct ast_node *)ast_node_root(tree); check_eof(eof); return errors; }
static struct ast_node* rel_expr(void) { struct ast_node *left; struct ast_node *right; struct ast_node_op *op_node; opcode_type_t opcode; left = sum_expr(); if (left == NULL) return NULL; switch(current_token) { case TOKEN_EQ: consume_token(); opcode = OPCODE_EQ; break; case TOKEN_LT: consume_token(); opcode = OPCODE_LT; break; case TOKEN_GT: consume_token(); opcode = OPCODE_GT; break; case TOKEN_LE: consume_token(); opcode = OPCODE_LE; break; case TOKEN_GE: consume_token(); opcode = OPCODE_GE; break; case TOKEN_NE: consume_token(); opcode = OPCODE_NE; break; default: return left; } right = sum_expr(); if (right == NULL) { error_msg("error: expression expected"); right = (struct ast_node *)ast_node_stub(); sync_stream(); } op_node = ast_node_rel_op(opcode, left, right); return AST_NODE(op_node); }
/** * Assign a value to a variable or delete the variable if no assignment is given. * List all variables if no arguments are given. */ void cmd_let(char *args) { unsigned char token; unsigned int var_name; unsigned char var_type; skip_whitespace(args); if (*args == '\0') { print_all_variables(); print_ready(); return; } if (args = parse_variable(args, &var_name, &var_type)) { if (next_token(args) == TOKEN_ASSIGN) { token = next_token(args); args = consume_token(args, token); switch (var_type) { case VAR_TYPE_INTEGER: { int value; if (parse_number_expression(args, &value)) { create_variable(var_name, var_type, &value); } break; } case VAR_TYPE_STRING: { char *value; if (parse_string_expression(args, &value)) { create_variable(var_name, var_type, value); } break; } } } else { if (*args == '\0') { delete_variable(var_name, var_type); } } } else { syntax_error(); } }
static struct ast_node* stmts(void *opaque) { struct ast_node *ret_node; struct ast_node *stmt_head; struct ast_node *prev_node; stmt_head = NULL; prev_node = NULL; while (current_token != TOKEN_EOF) { if (opaque != NULL && current_token == TOKEN_EOL) consume_token(); if (opaque == NULL && current_token == TOKEN_EOL) break; ret_node = stmt(opaque); if (errors) break; if (ret_node == NULL) continue; if (stmt_head == NULL) stmt_head = ret_node; if (prev_node != NULL) { prev_node->next = ret_node; ret_node->parent = prev_node; } prev_node = ret_node; if (ret_node->type == NODE_TYPE_END_SCOPE) break; } return stmt_head; }
static struct ast_node* rest_mult(struct ast_node *node) { struct ast_node *expr_node; struct ast_node *prev_node; struct ast_node *ret_node; char op; prev_node = node; while (TRUE) { switch(current_token) { case TOKEN_ASTERIK: op = '*'; break; case TOKEN_SLASH: op = '/'; break; default: goto exit_mult; } consume_token(); expr_node = exp_expr(); if (expr_node == NULL) { error_msg("error: syntax error"); expr_node = (struct ast_node *)ast_node_stub(); } prev_node = (struct ast_node *)ast_node_op(op, prev_node, expr_node); } exit_mult: ret_node = prev_node; return ret_node; }
/** * Input a variable from the keyboard. * INPUT <variable> [ONERROR <command] */ void cmd_input(char *args) { unsigned int var_name; unsigned char var_type; args = parse_variable(args, &var_name, &var_type); if (args) { if (var_type == VAR_TYPE_STRING) { char *line = readline(INTERRUPTIBLE); create_variable(var_name, var_type, line); } else if (var_type == VAR_TYPE_INTEGER) { for (;;) { int value = 0; char *line = readline(INTERRUPTIBLE); if (is_interrupted()) { break; } if (parse_integer(line, &value)) { create_variable(var_name, var_type, &value); break; } else { if (next_token(args) == TOKEN_ONERROR) { args = consume_token(args, TOKEN_ONERROR); execute(args); break; } else { syntax_error_invalid_number(); lcd_puts("Enter again: "); } } } } else { syntax_error_invalid_argument(); } } else { syntax_error_invalid_argument(); } }
static struct ast_node* rest_sum(struct ast_node *node) { struct ast_node *prev_node; struct ast_node *ret_node; struct ast_node *mult_node; char op; prev_node = node; while (TRUE) { switch(current_token) { case TOKEN_PLUS: op = '+'; break; case TOKEN_MINUS: op = '-'; break; default: goto exit_sum; } consume_token(); mult_node = mult_expr(); if (mult_node == NULL) { error_msg("error: syntax error"); mult_node = (struct ast_node *)ast_node_stub(); } prev_node = (struct ast_node *)ast_node_op(op, prev_node, mult_node); } exit_sum: ret_node = prev_node; return ret_node; }
static void process_function_body(char *name) { struct scope_ctx helper; struct ast_node *body; struct function *func_ctx; struct symbol_table *scope; int i; consume_token(); func_ctx = function_table_lookup(name); symbol_table_push(); /* insert arguments in function scope */ for (i = 0; i < func_ctx->nargs; i++) symbol_table_put_symbol(func_ctx->args[i]); memset(&helper, 0, sizeof(helper)); helper.is_func = 1; body = stmts(&helper); scope = symbol_table_get_current_table(); func_ctx->body = body; func_ctx->scope = scope; symbol_table_pop(); if(errors) { error_msg("->redefine your function"); function_table_delete_function(name); } }
static struct ast_node* rest_exp(struct ast_node *node) { struct ast_node *expr_node; struct ast_node *prev_node; struct ast_node *ret_node; char op; prev_node = node; while(TRUE) { switch(current_token) { case TOKEN_CARET: op = '^'; break; default: goto exit_exp; } consume_token(); expr_node = term_expr(); if (expr_node == NULL) { error_msg("error: syntax error"); expr_node = (struct ast_node *)ast_node_stub(); sync_stream(); goto exit_exp; } prev_node = (struct ast_node *)ast_node_op(op, prev_node, expr_node); } exit_exp: ret_node = prev_node; return ret_node; }
static struct ast_node* expr(void) { struct ast_node *lvalue; struct ast_node *rvalue; struct ast_node_assign *assign; lvalue = or_expr(); if (current_token != TOKEN_EQUALITY) return lvalue; /* `=' */ consume_token(); switch(lvalue->type) { case NODE_TYPE_ID: case NODE_TYPE_ACCESS: break; default: error_msg("error: rvalue assignmet"); sync_stream(); return lvalue; } rvalue = or_expr(); if (!rvalue) { error_msg("error: expression expected `='"); sync_stream(); rvalue = (struct ast_node *)ast_node_stub(); } assign = ast_node_assign(lvalue, rvalue); return AST_NODE(assign); }
/** * Parse a number expression 's' (that contains only number values) and return its * resulting value in 'value'. * Return a pointer behind the last character of the expression. * Return NULL if a syntax error occurred. */ char *parse_number_expression(char *s, int *value) { unsigned char token = next_token(s); if (token == TOKEN_DIGITS || token == TOKEN_PLUS || token == TOKEN_MINUS || token == TOKEN_VAR_NUMBER) { int operand; if (s = parse_number_term(s, value)) { for (;;) { token = next_token(s); switch (token) { case TOKEN_PLUS: case TOKEN_MINUS: case TOKEN_MUL: case TOKEN_DIV: case TOKEN_MOD: case TOKEN_EQUAL: case TOKEN_LESS: case TOKEN_LESSEQUAL: case TOKEN_GREATER: case TOKEN_GREATEREQUAL: s = consume_token(s, token); if (s = parse_number_term(s, &operand)) { switch (token) { case TOKEN_PLUS: *value += operand; break; case TOKEN_MINUS: *value -= operand; break; case TOKEN_MUL: *value *= operand; break; case TOKEN_DIV: *value /= operand; break; case TOKEN_MOD: *value %= operand; break; case TOKEN_EQUAL: *value = *value == operand; break; case TOKEN_NOTEQUAL: *value = *value != operand; break; case TOKEN_LESS: *value = *value < operand; break; case TOKEN_LESSEQUAL: *value = *value <= operand; break; case TOKEN_GREATER: *value = *value > operand; break; case TOKEN_GREATEREQUAL: *value = *value >= operand; break; } } break; default: return s; } } return s; } } else if (token == TOKEN_STRING || token == TOKEN_VAR_STRING) { char *string; if (s = parse_string_expression(s, &string)) { token = next_token(s); if (token == TOKEN_EQUAL || token == TOKEN_NOTEQUAL || token == TOKEN_LESS || token == TOKEN_LESSEQUAL || token == TOKEN_GREATER || token == TOKEN_GREATEREQUAL) { s = consume_token(s, token); strcpy(tmpbuf, string); if (s = parse_string_expression(s, &string)) { switch (token) { case TOKEN_EQUAL: *value = strcmp(tmpbuf, string) == 0; break; case TOKEN_NOTEQUAL: *value = strcmp(tmpbuf, string) != 0; break; case TOKEN_LESS: *value = strcmp(tmpbuf, string) < 0; break; case TOKEN_LESSEQUAL: *value = strcmp(tmpbuf, string) <= 0; break; case TOKEN_GREATER: *value = strcmp(tmpbuf, string) > 0; break; case TOKEN_GREATEREQUAL: *value = strcmp(tmpbuf, string) >= 0; break; } return s; } } else { syntax_error_invalid_token(token); } } } return NULL; }
int yylex(void) { int c; while ((c = scanc()) != EOF) { /* special handling for quoted string */ if (instring) { if (escaped) { escaped = 0; /* if newline, just eat and forget it */ if (c == '\n') continue; if (strchr("xXd01234567", c)) { unscanc(c); unscanc(esc_char); return (get_wide()); } yylval.wc = get_escaped(c); return (T_CHAR); } if (c == esc_char) { escaped = 1; continue; } switch (c) { case '<': return (get_symbol()); case '>': /* oops! should generate syntax error */ return (T_GT); case '"': instring = 0; return (T_QUOTE); default: yylval.wc = c; return (T_CHAR); } } /* escaped characters first */ if (escaped) { escaped = 0; if (c == '\n') { /* eat the newline */ continue; } hadtok = 1; if (tokidx) { /* an escape mid-token is nonsense */ return (T_NULL); } /* numeric escapes are treated as wide characters */ if (strchr("xXd01234567", c)) { unscanc(c); unscanc(esc_char); return (get_wide()); } add_tok(get_escaped(c)); continue; } /* if it is the escape charter itself note it */ if (c == esc_char) { escaped = 1; continue; } /* remove from the comment char to end of line */ if (c == com_char) { while (c != '\n') { if ((c = scanc()) == EOF) { /* end of file without newline! */ return (EOF); } } assert(c == '\n'); if (!hadtok) { /* * If there were no tokens on this line, * then just pretend it didn't exist at all. */ continue; } hadtok = 0; return (T_NL); } if (strchr(" \t\n;()<>,\"", c) && (tokidx != 0)) { /* * These are all token delimiters. If there * is a token already in progress, we need to * process it. */ unscanc(c); return (consume_token()); } switch (c) { case '\n': if (!hadtok) { /* * If the line was completely devoid of tokens, * then just ignore it. */ continue; } /* we're starting a new line, reset the token state */ hadtok = 0; return (T_NL); case ',': hadtok = 1; return (T_COMMA); case ';': hadtok = 1; return (T_SEMI); case '(': hadtok = 1; return (T_LPAREN); case ')': hadtok = 1; return (T_RPAREN); case '>': hadtok = 1; return (T_GT); case '<': /* symbol start! */ hadtok = 1; return (get_symbol()); case ' ': case '\t': /* whitespace, just ignore it */ continue; case '"': hadtok = 1; instring = 1; return (T_QUOTE); default: hadtok = 1; add_tok(c); continue; } } return (EOF); }
static struct ast_node* process_matrix(void) { struct ast_node_stub *stub_node; struct ast_node **elem; struct ast_node *node; int row, col, len; int prev_col; int i; elem = NULL; node = NULL; row = col = 1; len = prev_col = 0; while (TRUE) { node = or_expr(); if (node == NULL) { error_msg("expr expected"); sync_stream(); goto err; } elem = urealloc(elem, ++len*sizeof(struct ast_node *)); elem[len - 1] = node; switch(current_token) { case TOKEN_COMMA: consume_token(); col++; continue; case TOKEN_SEMICOLON: consume_token(); if (prev_col == 0) prev_col = col; else if (prev_col != col) { error_msg("incompatible column count"); sync_stream(); goto err; } col = 1; row++; continue; case TOKEN_RBRACKET: consume_token(); break; default: error_msg("syntax error"); sync_stream(); goto err; } break; } if (row == 1 || col == 1) node = (struct ast_node *)ast_node_vector(elem, len); else node = (struct ast_node *) ast_node_matrix(elem, row, col); return node; err: if (node != NULL) ast_node_unref(node); if (elem != NULL) for (i = 0; i < len; i++) ast_node_unref(elem[i]); stub_node = ast_node_stub(); return AST_NODE(stub_node); }
/* Check if current token matches any in given set and consume on match. * Args: * terminating is the description of the structure this token terminates, * NULL for none. Used only for error messages. * id_set is a TK_NONE terminated list. * make_ast specifies whether to construct an AST node on match or discard * consumed token. * out_found reports whether an optional token was found. Only set on * success. May be set to NULL if this information is not needed. * * Returns: * PARSE_OK on success. * PARSE_ERROR to propogate a lexer error. * RULE_NOT_FOUND if current token is not is specified set. * NULL to propogate a restarted error. */ ast_t* parse_token_set(parser_t* parser, rule_state_t* state, const char* desc, const char* terminating, const token_id* id_set, bool make_ast, bool* out_found) { assert(parser != NULL); assert(state != NULL); assert(id_set != NULL); token_id id = current_token_id(parser); if(id == TK_LEX_ERROR) return propogate_error(parser, state); if(desc == NULL) desc = token_id_desc(id_set[0]); if(trace_enable) { printf("Rule %s: Looking for %s token%s %s. Found %s. ", state->fn_name, (state->deflt_id == TK_LEX_ERROR) ? "required" : "optional", (id_set[1] == TK_NONE) ? "" : "s", desc, token_print(parser->token)); } for(const token_id* p = id_set; *p != TK_NONE; p++) { // Match new line if the next token is the first on a line if(*p == TK_NEWLINE) { assert(parser->token != NULL); size_t last_token_line = parser->last_token_line; size_t next_token_line = token_line_number(parser->token); bool is_newline = (next_token_line != last_token_line); if(out_found != NULL) *out_found = is_newline; if(trace_enable) printf("\\n %smatched\n", is_newline ? "" : "not "); state->deflt_id = TK_LEX_ERROR; return PARSE_OK; } if(id == *p) { // Current token matches one in set if(trace_enable) printf("Compatible\n"); parser->last_matched = token_print(parser->token); if(make_ast) return handle_found(parser, state, consume_token(parser), default_builder, out_found); // AST not needed, discard token consume_token_no_ast(parser); return handle_found(parser, state, NULL, NULL, out_found); } } // Current token does not match any in current set if(trace_enable) printf("Not compatible\n"); return handle_not_found(parser, state, desc, terminating, out_found); }
void parse_member_specification(std::vector<unique_ptr<Token>>::iterator* it, ClassSpecification* class_spec, bool* ok) { MemberSpecification member_specification; *ok = true; member_specification.annotationType = parse_annotation_type(it); if (!parse_access_flags(it, member_specification.requiredSetAccessFlags, member_specification.requiredUnsetAccessFlags)) { // There was a problem parsing the access flags. Return an empty class spec // for now. cerr << "Problem parsing access flags for member specification.\n"; *ok = false; skip_to_semicolon(it); return; } // The next token better be an identifier. if ((**it)->type != token::identifier) { cerr << "Expecting field or member specification but got " << (**it)->show() << " at line " << (**it)->line << endl; *ok = false; skip_to_semicolon(it); return; } std::string ident = static_cast<Identifier*>((*it)->get())->ident; // Check for "*". if (ident == "*") { member_specification.name = ""; member_specification.descriptor = ""; ++(*it); gobble_semicolon(it, ok); class_spec->methodSpecifications.push_back(member_specification); class_spec->fieldSpecifications.push_back(member_specification); return; } // Check for <methods> if (ident == "<methods>") { member_specification.name = ""; member_specification.descriptor = ""; ++(*it); gobble_semicolon(it, ok); class_spec->methodSpecifications.push_back(member_specification); return; } // Check for <fields> if (ident == "<fields>") { member_specification.name = ""; member_specification.descriptor = ""; ++(*it); gobble_semicolon(it, ok); class_spec->fieldSpecifications.push_back(member_specification); return; } // Check for <init> if (ident == "<init>") { member_specification.name = "<init>"; member_specification.descriptor = "V"; set_access_flag(member_specification.requiredSetAccessFlags, ACC_CONSTRUCTOR); ++(*it); } else { // This token is the type for the member specification. if ((**it)->type != token::identifier) { cerr << "Expecting type identifier but got " << (**it)->show() << " at line " << (**it)->line << endl; *ok = false; skip_to_semicolon(it); return; } std::string typ = static_cast<Identifier*>((*it)->get())->ident; ++(*it); member_specification.descriptor = convert_wildcard_type(typ); if ((**it)->type != token::identifier) { cerr << "Expecting identifier name for class member but got " << (**it)->show() << " at line " << (**it)->line << endl; *ok = false; skip_to_semicolon(it); return; } member_specification.name = static_cast<Identifier*>((*it)->get())->ident; ++(*it); } // Check to see if this is a method specification. if ((**it)->type == token::openBracket) { consume_token(it, token::openBracket); std::string arg = "("; while (true) { // If there is a ")" next we are done. if ((**it)->type == token::closeBracket) { consume_token(it, token::closeBracket); break; } if ((**it)->type != token::identifier) { std::cerr << "Expecting type identifier but got " << (**it)->show() << " at line " << (**it)->line << std::endl; *ok = false; return; } std::string typ = static_cast<Identifier*>((*it)->get())->ident; consume_token(it, token::identifier); arg += convert_wildcard_type(typ); // The next token better be a comma or a closing bracket. if ((**it)->type != token::comma && (**it)->type != token::closeBracket) { std::cerr << "Expecting comma or ) but got " << (**it)->show() << " at line " << (**it)->line << std::endl; *ok = false; return; } // If the next token is a comma (rather than closing bracket) consume // it and check that it is followed by an identifier. if ((**it)->type == token::comma) { consume_token(it, token::comma); if ((**it)->type != token::identifier) { std::cerr << "Expecting type identifier after comma but got " << (**it)->show() << " at line " << (**it)->line << std::endl; *ok = false; return; } } } arg += ")"; arg += member_specification.descriptor; member_specification.descriptor = arg; } // Make sure member specification ends with a semicolon. gobble_semicolon(it, ok); if (!ok) { return; } if (member_specification.descriptor[0] == '(') { class_spec->methodSpecifications.push_back(member_specification); } else { class_spec->fieldSpecifications.push_back(member_specification); } return; }
static struct ast_node* function_call(char *name) { struct function *func_ctx; struct ast_node_func_call *func_call; struct ast_node_stub *stub_node; struct ast_node *arg; int nargs = 0; func_ctx = function_table_lookup(name); consume_token(); if (func_ctx == NULL) { error_msg("unknown function"); sync_stream(); stub_node = ast_node_stub(); return AST_NODE(stub_node); } func_call = ast_node_func_call(func_ctx->name); while (!match(TOKEN_RPARENTH)) { if (match(TOKEN_EOL)) { error_msg("EOL in function call"); sync_stream(); goto free; } arg = or_expr(); if (arg == NULL) { error_msg("error: function argument expected"); sync_stream(); goto free; } nargs++; ast_node_func_call_add_arg(func_call, arg); if (current_token != TOKEN_RPARENTH && !match(TOKEN_COMMA)) { error_msg("error: missed comma"); sync_stream(); goto free; } } if (func_ctx->nargs != nargs) { error_msg("error: unmatched arguments count"); sync_stream(); goto free; } return AST_NODE(func_call); free: ast_node_unref(AST_NODE(func_call)); stub_node = ast_node_stub(); return AST_NODE(stub_node); }