| /* |
| * ***************************************************************************** |
| * |
| * Copyright 2018 Gavin D. Howard |
| * |
| * Permission to use, copy, modify, and/or distribute this software for any |
| * purpose with or without fee is hereby granted. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH |
| * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY |
| * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, |
| * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM |
| * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR |
| * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| * |
| * ***************************************************************************** |
| * |
| * The parser. |
| * |
| */ |
| |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <data.h> |
| #include <lex.h> |
| #include <parse.h> |
| #include <instructions.h> |
| |
| // This is an array that corresponds to token types. An entry is |
| // true if the token is valid in an expression, false otherwise. |
| static const bool bc_token_exprs[] = { |
| |
| true, |
| true, |
| |
| true, |
| |
| true, |
| true, |
| true, |
| |
| true, |
| true, |
| |
| true, |
| true, |
| true, |
| true, |
| true, |
| true, |
| true, |
| |
| true, |
| true, |
| true, |
| true, |
| true, |
| true, |
| |
| true, |
| |
| true, |
| true, |
| |
| true, |
| |
| false, |
| |
| false, |
| |
| true, |
| true, |
| |
| false, |
| false, |
| |
| false, |
| false, |
| |
| false, |
| false, |
| |
| false, |
| true, |
| true, |
| |
| false, |
| false, |
| false, |
| false, |
| false, |
| false, |
| false, |
| true, |
| false, |
| true, |
| true, |
| true, |
| true, |
| false, |
| false, |
| true, |
| false, |
| true, |
| true, |
| false, |
| |
| false, |
| |
| false, |
| }; |
| |
| // This is an array of data for operators that correspond to token types. |
| // The last corresponds to BC_PARSE_OP_NEGATE_IDX since it doesn't have |
| // its own token type (it is the same token at the binary minus operator). |
| static const BcOp bc_ops[] = { |
| |
| { 0, false }, |
| { 0, false }, |
| |
| { 2, false }, |
| |
| { 3, true }, |
| { 3, true }, |
| { 3, true }, |
| |
| { 4, true }, |
| { 4, true }, |
| |
| { 5, false }, |
| { 5, false }, |
| { 5, false }, |
| { 5, false }, |
| { 5, false }, |
| { 5, false }, |
| { 5, false }, |
| |
| { 1, false }, |
| |
| { 6, true }, |
| { 6, true }, |
| { 6, true }, |
| { 6, true }, |
| { 6, true }, |
| { 6, true }, |
| |
| { 7, false }, |
| |
| { 8, true }, |
| { 8, true }, |
| |
| }; |
| |
| static const uint8_t bc_op_insts[] = { |
| |
| BC_INST_OP_POWER, |
| |
| BC_INST_OP_MULTIPLY, |
| BC_INST_OP_DIVIDE, |
| BC_INST_OP_MODULUS, |
| |
| BC_INST_OP_PLUS, |
| BC_INST_OP_MINUS, |
| |
| BC_INST_OP_REL_EQUAL, |
| BC_INST_OP_REL_LESS_EQ, |
| BC_INST_OP_REL_GREATER_EQ, |
| BC_INST_OP_REL_NOT_EQ, |
| BC_INST_OP_REL_LESS, |
| BC_INST_OP_REL_GREATER, |
| |
| BC_INST_OP_BOOL_NOT, |
| |
| BC_INST_OP_BOOL_NOT, |
| BC_INST_OP_BOOL_AND, |
| |
| BC_INST_OP_NEGATE, |
| |
| BC_INST_OP_ASSIGN_POWER, |
| BC_INST_OP_ASSIGN_MULTIPLY, |
| BC_INST_OP_ASSIGN_DIVIDE, |
| BC_INST_OP_ASSIGN_MODULUS, |
| BC_INST_OP_ASSIGN_PLUS, |
| BC_INST_OP_ASSIGN_MINUS, |
| BC_INST_OP_ASSIGN, |
| |
| }; |
| |
| static BcStatus bc_parse_func(BcParse* parse); |
| static BcStatus bc_parse_funcStart(BcParse* parse); |
| static BcStatus bc_parse_auto(BcParse* parse); |
| static BcStatus bc_parse_semicolonList(BcParse* parse, BcVec *code); |
| static BcStatus bc_parse_semicolonListEnd(BcParse* parse, BcVec *code); |
| static BcStatus bc_parse_stmt(BcParse* parse, BcVec *code); |
| static BcStatus bc_parse_operator(BcParse* parse, BcVec* code, BcVec* ops, |
| BcLexTokenType t, uint32_t* num_exprs, |
| bool next); |
| static BcStatus bc_parse_rightParen(BcParse* parse, BcVec* code, |
| BcVec* ops, uint32_t* nexs); |
| static BcStatus bc_parse_expr_name(BcParse* parse, BcVec* code, |
| BcExprType* type, bool posix_rel); |
| static BcStatus bc_parse_call(BcParse* parse, BcVec* code, |
| char* name, bool posix_rel); |
| static BcStatus bc_parse_params(BcParse* parse, BcVec* code, bool posix_rel); |
| static BcStatus bc_parse_read(BcParse* parse, BcVec* code); |
| static BcStatus bc_parse_builtin(BcParse* parse, BcVec* code, |
| BcExprType type, bool posix_rel); |
| static BcStatus bc_parse_scale(BcParse* parse, BcVec* code, |
| BcExprType* type, bool posix_rel); |
| static BcStatus bc_parse_incdec(BcParse* parse, BcVec* code, BcExprType* prev); |
| static BcStatus bc_parse_minus(BcParse* parse, BcVec* exs, BcVec* ops, |
| BcExprType* prev, bool rparen, uint32_t* nexprs); |
| static BcStatus bc_parse_string(BcParse* parse, BcVec* code); |
| static BcStatus bc_parse_return(BcParse* parse, BcVec* code); |
| static BcStatus bc_parse_print(BcParse* parse, BcVec* code); |
| static BcStatus bc_parse_if(BcParse* parse, BcVec* code); |
| static BcStatus bc_parse_else(BcParse* parse, BcVec* code); |
| static BcStatus bc_parse_noElse(BcParse* parse, BcVec* code); |
| static BcStatus bc_parse_while(BcParse* parse, BcVec* code); |
| static BcStatus bc_parse_for(BcParse* parse, BcVec* code); |
| static BcStatus bc_parse_startBody(BcParse* parse, BcVec* code, uint8_t flags); |
| static BcStatus bc_parse_loopExit(BcParse* parse, BcVec* code, |
| BcLexTokenType type); |
| static BcStatus bc_parse_rightBrace(BcParse* parse, BcVec* code); |
| static BcStatus bc_parse_pushName(BcVec* code, char *name); |
| static BcStatus bc_parse_pushIndex(BcVec* code, size_t idx); |
| static BcStatus bc_parse_endHeader(BcParse* parse, BcVec* code, BcFunc* func, |
| size_t idx, uint8_t flags); |
| |
| BcStatus bc_parse_init(BcParse* parse, BcProgram* program) { |
| |
| if (parse == NULL || program == NULL) { |
| return BC_STATUS_INVALID_PARAM; |
| } |
| |
| BcStatus status = bc_vec_init(&parse->flags, sizeof(uint8_t), NULL); |
| |
| if (status != BC_STATUS_SUCCESS) { |
| return status; |
| } |
| |
| status = bc_vec_init(&parse->exit_labels, sizeof(BcInstPtr), NULL); |
| |
| if (status) { |
| goto exit_label_err; |
| } |
| |
| status = bc_vec_init(&parse->cond_labels, sizeof(size_t), NULL); |
| |
| if (status) { |
| goto cond_label_err; |
| } |
| |
| uint8_t flags = 0; |
| |
| status = bc_vec_push(&parse->flags, &flags); |
| if (status != BC_STATUS_SUCCESS) { |
| goto push_err; |
| } |
| |
| status = bc_vec_init(&parse->ops, sizeof(BcLexTokenType), NULL); |
| |
| if (status) { |
| goto push_err; |
| } |
| |
| parse->program = program; |
| parse->func = 0; |
| parse->num_braces = 0; |
| parse->auto_part = false; |
| |
| return status; |
| |
| push_err: |
| |
| bc_vec_free(&parse->cond_labels); |
| |
| cond_label_err: |
| |
| bc_vec_free(&parse->exit_labels); |
| |
| exit_label_err: |
| |
| bc_vec_free(&parse->flags); |
| |
| return status; |
| } |
| |
| BcStatus bc_parse_file(BcParse* parse, const char* file) { |
| |
| if (parse == NULL || file == NULL) { |
| return BC_STATUS_INVALID_PARAM; |
| } |
| |
| return bc_lex_init(&parse->lex, file); |
| } |
| |
| BcStatus bc_parse_text(BcParse* parse, const char* text) { |
| |
| BcStatus status; |
| |
| if (parse == NULL || text == NULL) { |
| return BC_STATUS_INVALID_PARAM; |
| } |
| |
| status = bc_lex_text(&parse->lex, text); |
| |
| if (status) { |
| return status; |
| } |
| |
| return bc_lex_next(&parse->lex, &parse->token); |
| } |
| |
| BcStatus bc_parse_parse(BcParse* parse) { |
| |
| BcStatus status; |
| |
| if (parse == NULL) { |
| return BC_STATUS_INVALID_PARAM; |
| } |
| |
| switch (parse->token.type) { |
| |
| case BC_LEX_NEWLINE: |
| { |
| status = bc_lex_next(&parse->lex, &parse->token); |
| break; |
| } |
| |
| case BC_LEX_KEY_DEFINE: |
| { |
| if (!BC_PARSE_CAN_EXEC(parse)) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| status = bc_parse_func(parse); |
| |
| break; |
| } |
| |
| case BC_LEX_EOF: |
| { |
| status = BC_STATUS_PARSE_EOF; |
| break; |
| } |
| |
| default: |
| { |
| BcFunc* func; |
| |
| func = bc_vec_item(&parse->program->funcs, parse->func); |
| |
| if (!func) { |
| return BC_STATUS_EXEC_UNDEFINED_FUNC; |
| } |
| |
| status = bc_parse_stmt(parse, &func->code); |
| |
| break; |
| } |
| } |
| |
| return status; |
| } |
| |
| void bc_parse_free(BcParse* parse) { |
| |
| if (!parse) { |
| return; |
| } |
| |
| bc_vec_free(&parse->flags); |
| bc_vec_free(&parse->exit_labels); |
| bc_vec_free(&parse->cond_labels); |
| bc_vec_free(&parse->ops); |
| |
| switch (parse->token.type) { |
| |
| case BC_LEX_STRING: |
| case BC_LEX_NAME: |
| case BC_LEX_NUMBER: |
| { |
| if (parse->token.string) { |
| free(parse->token.string); |
| } |
| |
| break; |
| } |
| |
| default: |
| { |
| // We don't have have to free anything. |
| break; |
| } |
| } |
| } |
| |
| static BcStatus bc_parse_func(BcParse* parse) { |
| |
| BcLexTokenType type; |
| BcStatus status; |
| BcFunc func; |
| bool comma; |
| uint8_t flags; |
| char* name; |
| bool var; |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_NAME) { |
| return BC_STATUS_PARSE_INVALID_FUNC; |
| } |
| |
| if (parse->program->funcs.len != parse->program->func_map.vec.len) { |
| return BC_STATUS_PARSE_MISMATCH_NUM_FUNCS; |
| } |
| |
| status = bc_program_func_add(parse->program, parse->token.string, |
| &parse->func); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (!parse->func) { |
| return BC_STATUS_PARSE_BUG; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_LEFT_PAREN) { |
| return BC_STATUS_PARSE_INVALID_FUNC; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| comma = false; |
| |
| type = parse->token.type; |
| |
| while (!status && type != BC_LEX_RIGHT_PAREN) { |
| |
| if (type != BC_LEX_NAME) { |
| return BC_STATUS_PARSE_INVALID_FUNC; |
| } |
| |
| name = parse->token.string; |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type == BC_LEX_LEFT_BRACKET) { |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_RIGHT_BRACKET) { |
| return BC_STATUS_PARSE_INVALID_FUNC; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| var = false; |
| } |
| else { |
| var = true; |
| } |
| |
| if (parse->token.type == BC_LEX_COMMA) { |
| |
| comma = true; |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| } |
| else { |
| comma = false; |
| } |
| |
| status = bc_func_insertParam(&func, name, var); |
| |
| if (status) { |
| return status; |
| } |
| |
| type = parse->token.type; |
| } |
| |
| if (comma) { |
| return BC_STATUS_PARSE_INVALID_FUNC; |
| } |
| |
| flags = BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_FUNC_INNER | BC_PARSE_FLAG_HEADER; |
| status = bc_vec_push(&parse->flags, &flags); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_LEFT_BRACE) { |
| return bc_posix_error(BC_STATUS_POSIX_FUNC_HEADER_LEFT_BRACE, |
| parse->lex.file, parse->lex.line, NULL); |
| } |
| |
| return status; |
| } |
| |
| static BcStatus bc_parse_funcStart(BcParse* parse) { |
| |
| BcStatus status; |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| parse->auto_part = true; |
| |
| return BC_STATUS_SUCCESS; |
| } |
| |
| static BcStatus bc_parse_auto(BcParse* parse) { |
| |
| BcLexTokenType type; |
| BcStatus status; |
| bool comma; |
| char* name; |
| bool var; |
| bool one; |
| BcFunc* func; |
| |
| if (!parse->auto_part) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| parse->auto_part = false; |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| comma = false; |
| one = false; |
| |
| func = bc_vec_item(&parse->program->funcs, parse->func); |
| |
| if (!func) { |
| return BC_STATUS_EXEC_UNDEFINED_FUNC; |
| } |
| |
| type = parse->token.type; |
| |
| while (!status && type == BC_LEX_NAME) { |
| |
| name = parse->token.string; |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| break; |
| } |
| |
| one = true; |
| |
| if (parse->token.type == BC_LEX_LEFT_BRACKET) { |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| break; |
| } |
| |
| if (parse->token.type != BC_LEX_RIGHT_BRACKET) { |
| return BC_STATUS_PARSE_INVALID_FUNC; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| break; |
| } |
| |
| var = false; |
| } |
| else { |
| var = true; |
| } |
| |
| if (parse->token.type == BC_LEX_COMMA) { |
| |
| comma = true; |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) return status; |
| } |
| else { |
| comma = false; |
| } |
| |
| status = bc_func_insertAuto(func, name, var); |
| |
| if (status) { |
| free(name); |
| return status; |
| } |
| |
| type = parse->token.type; |
| } |
| |
| if (status) { |
| return status; |
| } |
| |
| if (comma) { |
| return BC_STATUS_PARSE_INVALID_FUNC; |
| } |
| |
| if (!one) { |
| return BC_STATUS_PARSE_NO_AUTO; |
| } |
| |
| if (type != BC_LEX_NEWLINE && type != BC_LEX_SEMICOLON) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| return bc_lex_next(&parse->lex, &parse->token); |
| } |
| |
| static BcStatus bc_parse_semicolonList(BcParse* parse, BcVec* code) { |
| |
| BcStatus status = BC_STATUS_SUCCESS; |
| |
| switch (parse->token.type) { |
| |
| case BC_LEX_OP_INC: |
| case BC_LEX_OP_DEC: |
| case BC_LEX_OP_MINUS: |
| case BC_LEX_OP_BOOL_NOT: |
| case BC_LEX_LEFT_PAREN: |
| case BC_LEX_STRING: |
| case BC_LEX_NAME: |
| case BC_LEX_NUMBER: |
| case BC_LEX_KEY_BREAK: |
| case BC_LEX_KEY_CONTINUE: |
| case BC_LEX_KEY_FOR: |
| case BC_LEX_KEY_HALT: |
| case BC_LEX_KEY_IBASE: |
| case BC_LEX_KEY_IF: |
| case BC_LEX_KEY_LAST: |
| case BC_LEX_KEY_LENGTH: |
| case BC_LEX_KEY_LIMITS: |
| case BC_LEX_KEY_OBASE: |
| case BC_LEX_KEY_PRINT: |
| case BC_LEX_KEY_QUIT: |
| case BC_LEX_KEY_READ: |
| case BC_LEX_KEY_RETURN: |
| case BC_LEX_KEY_SCALE: |
| case BC_LEX_KEY_SQRT: |
| case BC_LEX_KEY_WHILE: |
| { |
| status = bc_parse_stmt(parse, code); |
| break; |
| } |
| |
| case BC_LEX_NEWLINE: |
| { |
| status = bc_lex_next(&parse->lex, &parse->token); |
| break; |
| } |
| |
| case BC_LEX_SEMICOLON: |
| { |
| status = bc_parse_semicolonListEnd(parse, code); |
| break; |
| } |
| |
| case BC_LEX_EOF: |
| { |
| if (parse->flags.len > 0) { |
| status = BC_STATUS_PARSE_EOF; |
| } |
| |
| break; |
| } |
| |
| default: |
| { |
| status = BC_STATUS_PARSE_INVALID_TOKEN; |
| break; |
| } |
| } |
| |
| return status; |
| } |
| |
| static BcStatus bc_parse_semicolonListEnd(BcParse* parse, BcVec* code) { |
| |
| BcStatus status; |
| |
| if (parse->token.type == BC_LEX_SEMICOLON) { |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_semicolonList(parse, code); |
| } |
| else if (parse->token.type == BC_LEX_NEWLINE) { |
| status = bc_lex_next(&parse->lex, &parse->token); |
| } |
| else { |
| status = BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| return status; |
| } |
| |
| static BcStatus bc_parse_stmt(BcParse* parse, BcVec* code) { |
| |
| BcStatus status; |
| uint8_t* flag_ptr; |
| |
| switch (parse->token.type) { |
| |
| case BC_LEX_OP_INC: |
| case BC_LEX_OP_DEC: |
| case BC_LEX_OP_MINUS: |
| case BC_LEX_OP_BOOL_NOT: |
| case BC_LEX_LEFT_PAREN: |
| case BC_LEX_NAME: |
| case BC_LEX_NUMBER: |
| case BC_LEX_KEY_IBASE: |
| case BC_LEX_KEY_LAST: |
| case BC_LEX_KEY_LENGTH: |
| case BC_LEX_KEY_OBASE: |
| case BC_LEX_KEY_READ: |
| case BC_LEX_KEY_SCALE: |
| case BC_LEX_KEY_SQRT: |
| { |
| if (BC_PARSE_IF_END(parse)) { |
| status = bc_parse_noElse(parse, code); |
| if (status) return status; |
| } |
| |
| parse->auto_part = false; |
| |
| status = bc_parse_expr(parse, code, false, true); |
| |
| if (status) { |
| break; |
| } |
| |
| status = bc_parse_semicolonListEnd(parse, code); |
| |
| break; |
| } |
| |
| case BC_LEX_NEWLINE: |
| { |
| status = bc_lex_next(&parse->lex, &parse->token); |
| break; |
| } |
| |
| case BC_LEX_KEY_ELSE: |
| { |
| parse->auto_part = false; |
| |
| status = bc_parse_else(parse, code); |
| |
| break; |
| } |
| |
| case BC_LEX_LEFT_BRACE: |
| { |
| uint8_t flags; |
| |
| if (BC_PARSE_IF_END(parse)) { |
| status = bc_parse_noElse(parse, code); |
| if (status) return status; |
| } |
| |
| parse->auto_part = false; |
| |
| ++parse->num_braces; |
| |
| if (BC_PARSE_HEADER(parse)) { |
| |
| flag_ptr = bc_vec_top(&parse->flags); |
| flags = *flag_ptr & ~(BC_PARSE_FLAG_HEADER); |
| |
| if (flags & BC_PARSE_FLAG_FUNC_INNER) { |
| status = bc_parse_funcStart(parse); |
| } |
| else if (flags) { |
| status = bc_parse_startBody(parse, code, flags); |
| } |
| else { |
| status = BC_STATUS_PARSE_BUG; |
| } |
| } |
| else { |
| status = bc_parse_startBody(parse, code, 0); |
| } |
| |
| break; |
| } |
| |
| case BC_LEX_RIGHT_BRACE: |
| { |
| if (BC_PARSE_IF_END(parse)) { |
| status = bc_parse_noElse(parse, code); |
| if (status) return status; |
| } |
| |
| parse->auto_part = false; |
| |
| status = bc_parse_rightBrace(parse, code); |
| |
| break; |
| } |
| |
| case BC_LEX_STRING: |
| { |
| if (BC_PARSE_IF_END(parse)) { |
| status = bc_parse_noElse(parse, code); |
| if (status) return status; |
| } |
| |
| parse->auto_part = false; |
| |
| status = bc_parse_string(parse, code); |
| |
| break; |
| } |
| |
| case BC_LEX_KEY_AUTO: |
| { |
| if (BC_PARSE_IF_END(parse)) { |
| status = bc_parse_noElse(parse, code); |
| if (status) return status; |
| } |
| |
| status = bc_parse_auto(parse); |
| |
| break; |
| } |
| |
| case BC_LEX_KEY_BREAK: |
| case BC_LEX_KEY_CONTINUE: |
| { |
| if (BC_PARSE_IF_END(parse)) { |
| status = bc_parse_noElse(parse, code); |
| if (status) return status; |
| } |
| |
| parse->auto_part = false; |
| |
| status = bc_parse_loopExit(parse, code, parse->token.type); |
| |
| break; |
| } |
| |
| case BC_LEX_KEY_FOR: |
| { |
| if (BC_PARSE_IF_END(parse)) { |
| status = bc_parse_noElse(parse, code); |
| if (status) return status; |
| } |
| |
| parse->auto_part = false; |
| |
| status = bc_parse_for(parse, code); |
| |
| break; |
| } |
| |
| case BC_LEX_KEY_HALT: |
| { |
| if (BC_PARSE_IF_END(parse)) { |
| status = bc_parse_noElse(parse, code); |
| if (status) return status; |
| } |
| |
| parse->auto_part = false; |
| |
| status = bc_vec_pushByte(code, BC_INST_HALT); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| break; |
| } |
| |
| status = bc_parse_semicolonListEnd(parse, code); |
| |
| break; |
| } |
| |
| case BC_LEX_KEY_IF: |
| { |
| if (BC_PARSE_IF_END(parse)) { |
| status = bc_parse_noElse(parse, code); |
| if (status) return status; |
| } |
| |
| parse->auto_part = false; |
| |
| status = bc_parse_if(parse, code); |
| |
| break; |
| } |
| |
| case BC_LEX_KEY_LIMITS: |
| { |
| if (BC_PARSE_IF_END(parse)) { |
| status = bc_parse_noElse(parse, code); |
| if (status) return status; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status && status != BC_STATUS_LEX_EOF) { |
| break; |
| } |
| |
| status = bc_parse_semicolonListEnd(parse, code); |
| |
| if (status && status != BC_STATUS_LEX_EOF) { |
| break; |
| } |
| |
| status = BC_STATUS_PARSE_LIMITS; |
| |
| break; |
| } |
| |
| case BC_LEX_KEY_PRINT: |
| { |
| if (BC_PARSE_IF_END(parse)) { |
| status = bc_parse_noElse(parse, code); |
| if (status) return status; |
| } |
| |
| parse->auto_part = false; |
| |
| status = bc_parse_print(parse, code); |
| |
| break; |
| } |
| |
| case BC_LEX_KEY_QUIT: |
| { |
| if (BC_PARSE_IF_END(parse)) { |
| status = bc_parse_noElse(parse, code); |
| if (status) return status; |
| } |
| |
| parse->auto_part = false; |
| |
| // Quit is a compile-time command, |
| // so we send an exit command. We |
| // don't exit directly, so the vm |
| // can clean up. |
| status = BC_STATUS_PARSE_QUIT; |
| break; |
| } |
| |
| case BC_LEX_KEY_RETURN: |
| { |
| if (BC_PARSE_IF_END(parse)) { |
| status = bc_parse_noElse(parse, code); |
| if (status) return status; |
| } |
| |
| parse->auto_part = false; |
| |
| status = bc_parse_return(parse, code); |
| |
| if (status) { |
| break; |
| } |
| |
| status = bc_parse_semicolonListEnd(parse, code); |
| |
| break; |
| } |
| |
| case BC_LEX_KEY_WHILE: |
| { |
| if (BC_PARSE_IF_END(parse)) { |
| status = bc_parse_noElse(parse, code); |
| if (status) return status; |
| } |
| |
| parse->auto_part = false; |
| |
| status = bc_parse_while(parse, code); |
| |
| break; |
| } |
| |
| default: |
| { |
| if (BC_PARSE_IF_END(parse)) { |
| status = bc_parse_noElse(parse, code); |
| if (status) return status; |
| } |
| |
| parse->auto_part = false; |
| |
| status = BC_STATUS_PARSE_INVALID_TOKEN; |
| |
| break; |
| } |
| } |
| |
| return status; |
| } |
| |
| BcStatus bc_parse_expr(BcParse* parse, BcVec* code, bool posix_rel, bool print) |
| { |
| BcStatus status; |
| uint32_t nexprs; |
| uint32_t num_parens; |
| uint32_t ops_start_len; |
| bool paren_first; |
| bool paren_expr; |
| bool rparen; |
| bool done; |
| bool get_token; |
| uint32_t num_rel_ops; |
| BcExprType prev; |
| BcLexTokenType type; |
| BcLexTokenType* ptr; |
| BcLexTokenType top; |
| uint8_t inst; |
| |
| status = BC_STATUS_SUCCESS; |
| |
| ops_start_len = parse->ops.len; |
| |
| prev = BC_EXPR_PRINT; |
| |
| paren_first = parse->token.type == BC_LEX_LEFT_PAREN; |
| |
| nexprs = 0; |
| num_parens = 0; |
| paren_expr = false; |
| rparen = false; |
| done = false; |
| get_token = false; |
| num_rel_ops = 0; |
| |
| type = parse->token.type; |
| |
| while (!status && !done && bc_token_exprs[type]) { |
| |
| switch (type) { |
| |
| case BC_LEX_OP_INC: |
| case BC_LEX_OP_DEC: |
| { |
| status = bc_parse_incdec(parse, code, &prev); |
| rparen = false; |
| get_token = false; |
| break; |
| } |
| |
| case BC_LEX_OP_MINUS: |
| { |
| status = bc_parse_minus(parse, code, &parse->ops, &prev, |
| rparen, &nexprs); |
| rparen = false; |
| get_token = false; |
| break; |
| } |
| |
| case BC_LEX_OP_ASSIGN_POWER: |
| case BC_LEX_OP_ASSIGN_MULTIPLY: |
| case BC_LEX_OP_ASSIGN_DIVIDE: |
| case BC_LEX_OP_ASSIGN_MODULUS: |
| case BC_LEX_OP_ASSIGN_PLUS: |
| case BC_LEX_OP_ASSIGN_MINUS: |
| case BC_LEX_OP_ASSIGN: |
| if (prev != BC_EXPR_VAR && prev != BC_EXPR_ARRAY_ELEM && |
| prev != BC_EXPR_SCALE && prev != BC_EXPR_IBASE && |
| prev != BC_EXPR_OBASE && prev != BC_EXPR_LAST) |
| { |
| status = BC_STATUS_PARSE_INVALID_ASSIGN; |
| break; |
| } |
| // Fallthrough. |
| case BC_LEX_OP_POWER: |
| case BC_LEX_OP_MULTIPLY: |
| case BC_LEX_OP_DIVIDE: |
| case BC_LEX_OP_MODULUS: |
| case BC_LEX_OP_PLUS: |
| case BC_LEX_OP_REL_EQUAL: |
| case BC_LEX_OP_REL_LESS_EQ: |
| case BC_LEX_OP_REL_GREATER_EQ: |
| case BC_LEX_OP_REL_NOT_EQ: |
| case BC_LEX_OP_REL_LESS: |
| case BC_LEX_OP_REL_GREATER: |
| case BC_LEX_OP_BOOL_NOT: |
| case BC_LEX_OP_BOOL_OR: |
| case BC_LEX_OP_BOOL_AND: |
| { |
| if (type >= BC_LEX_OP_REL_EQUAL && type <= BC_LEX_OP_REL_GREATER) { |
| num_rel_ops += 1; |
| } |
| |
| prev = BC_PARSE_TOKEN_TO_EXPR(type); |
| status = bc_parse_operator(parse, code, &parse->ops, type, &nexprs, true); |
| rparen = false; |
| get_token = false; |
| |
| break; |
| } |
| |
| case BC_LEX_LEFT_PAREN: |
| { |
| ++num_parens; |
| paren_expr = false; |
| rparen = false; |
| get_token = true; |
| status = bc_vec_push(&parse->ops, &type); |
| break; |
| } |
| |
| case BC_LEX_RIGHT_PAREN: |
| { |
| if (num_parens == 0) { |
| status = BC_STATUS_SUCCESS; |
| done = true; |
| get_token = false; |
| break; |
| } |
| else if (!paren_expr) { |
| return BC_STATUS_PARSE_INVALID_EXPR; |
| } |
| |
| --num_parens; |
| paren_expr = true; |
| rparen = true; |
| get_token = false; |
| |
| status = bc_parse_rightParen(parse, code, &parse->ops, &nexprs); |
| |
| break; |
| } |
| |
| case BC_LEX_NAME: |
| { |
| paren_expr = true; |
| rparen = false; |
| get_token = false; |
| status = bc_parse_expr_name(parse, code, &prev, posix_rel); |
| ++nexprs; |
| break; |
| } |
| |
| case BC_LEX_NUMBER: |
| { |
| size_t idx; |
| |
| idx = parse->program->constants.len; |
| |
| status = bc_vec_push(&parse->program->constants, &parse->token.string); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_vec_pushByte(code, BC_INST_PUSH_NUM); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_pushIndex(code, idx); |
| |
| if (status) { |
| return status; |
| } |
| |
| paren_expr = true; |
| rparen = false; |
| get_token = true; |
| ++nexprs; |
| prev = BC_EXPR_NUMBER; |
| |
| break; |
| } |
| |
| case BC_LEX_KEY_IBASE: |
| { |
| status = bc_vec_pushByte(code, BC_INST_PUSH_IBASE); |
| |
| paren_expr = true; |
| rparen = false; |
| get_token = true; |
| ++nexprs; |
| prev = BC_EXPR_IBASE; |
| |
| break; |
| } |
| |
| case BC_LEX_KEY_LENGTH: |
| { |
| status = bc_parse_builtin(parse, code, BC_EXPR_LENGTH, posix_rel); |
| paren_expr = true; |
| rparen = false; |
| get_token = true; |
| ++nexprs; |
| prev = BC_EXPR_LENGTH; |
| break; |
| } |
| |
| case BC_LEX_KEY_OBASE: |
| { |
| status = bc_vec_pushByte(code, BC_INST_PUSH_OBASE); |
| |
| paren_expr = true; |
| rparen = false; |
| get_token = true; |
| ++nexprs; |
| prev = BC_EXPR_OBASE; |
| |
| break; |
| } |
| |
| case BC_LEX_KEY_READ: |
| { |
| status = bc_parse_read(parse, code); |
| paren_expr = true; |
| rparen = false; |
| get_token = false; |
| ++nexprs; |
| prev = BC_EXPR_READ; |
| break; |
| } |
| |
| case BC_LEX_KEY_SCALE: |
| { |
| status = bc_parse_scale(parse, code, &prev, posix_rel); |
| paren_expr = true; |
| rparen = false; |
| get_token = false; |
| ++nexprs; |
| prev = BC_EXPR_SCALE; |
| break; |
| } |
| |
| case BC_LEX_KEY_SQRT: |
| { |
| status = bc_parse_builtin(parse, code, BC_EXPR_SQRT, posix_rel); |
| paren_expr = true; |
| rparen = false; |
| get_token = false; |
| ++nexprs; |
| prev = BC_EXPR_SQRT; |
| break; |
| } |
| |
| default: |
| { |
| status = BC_STATUS_PARSE_INVALID_TOKEN; |
| break; |
| } |
| } |
| |
| if (get_token) { |
| status = bc_lex_next(&parse->lex, &parse->token); |
| } |
| |
| type = parse->token.type; |
| } |
| |
| if (status) { |
| return status; |
| } |
| |
| while (!status && parse->ops.len > ops_start_len) { |
| |
| ptr = bc_vec_top(&parse->ops); |
| top = *ptr; |
| |
| if (top == BC_LEX_LEFT_PAREN || top == BC_LEX_RIGHT_PAREN) { |
| return BC_STATUS_PARSE_INVALID_EXPR; |
| } |
| |
| inst = bc_op_insts[top - BC_LEX_OP_POWER]; |
| |
| status = bc_vec_pushByte(code, inst); |
| |
| if (status) { |
| return status; |
| } |
| |
| nexprs -= top != BC_LEX_OP_BOOL_NOT && top != BC_LEX_OP_NEGATE ? 1 : 0; |
| |
| status = bc_vec_pop(&parse->ops); |
| } |
| |
| if (nexprs != 1) { |
| return BC_STATUS_PARSE_INVALID_EXPR; |
| } |
| |
| if (!posix_rel && num_rel_ops && |
| (status = bc_posix_error(BC_STATUS_POSIX_REL_OUTSIDE, |
| parse->lex.file, parse->lex.line, NULL))) |
| { |
| return status; |
| } |
| else if (num_rel_ops > 1 && |
| (status = bc_posix_error(BC_STATUS_POSIX_REL_OUTSIDE, |
| parse->lex.file, parse->lex.line, NULL))) |
| { |
| return status; |
| } |
| |
| inst = *((uint8_t*) bc_vec_top(code)); |
| |
| if (print && (paren_first || |
| (inst != BC_INST_OP_ASSIGN_POWER && |
| inst != BC_INST_OP_ASSIGN_MULTIPLY && |
| inst != BC_INST_OP_ASSIGN_DIVIDE && |
| inst != BC_INST_OP_ASSIGN_MODULUS && |
| inst != BC_INST_OP_ASSIGN_PLUS && |
| inst != BC_INST_OP_ASSIGN_MINUS && |
| inst != BC_INST_OP_ASSIGN))) |
| { |
| status = bc_vec_pushByte(code, BC_INST_PRINT); |
| } |
| |
| return status; |
| } |
| |
| static BcStatus bc_parse_operator(BcParse* parse, BcVec* code, BcVec* ops, |
| BcLexTokenType t, uint32_t* num_exprs, |
| bool next) |
| { |
| BcStatus status; |
| BcLexTokenType top; |
| BcLexTokenType* ptr; |
| uint8_t lp; |
| uint8_t rp; |
| bool rleft; |
| |
| rp = bc_ops[t].prec; |
| rleft = bc_ops[t].left; |
| |
| if (ops->len != 0) { |
| |
| ptr = bc_vec_top(ops); |
| top = *ptr; |
| |
| if (top != BC_LEX_LEFT_PAREN) { |
| |
| lp = bc_ops[top - BC_LEX_OP_POWER].prec; |
| |
| while (lp < rp || (lp == rp && rleft)) { |
| |
| status = bc_vec_pushByte(code, bc_op_insts[top - BC_LEX_OP_POWER]); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_vec_pop(ops); |
| |
| if (status) { |
| return status; |
| } |
| |
| *num_exprs -= top != BC_LEX_OP_BOOL_NOT && |
| top != BC_LEX_OP_NEGATE ? 1 : 0; |
| |
| if (ops->len == 0) { |
| break; |
| } |
| |
| ptr = bc_vec_top(ops); |
| top = *ptr; |
| |
| if (top == BC_LEX_LEFT_PAREN) { |
| break; |
| } |
| |
| lp = bc_ops[top].prec; |
| } |
| } |
| } |
| |
| status = bc_vec_push(ops, &t); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (next) { |
| status = bc_lex_next(&parse->lex, &parse->token); |
| } |
| |
| return status; |
| } |
| |
| static BcStatus bc_parse_rightParen(BcParse* parse, BcVec* code, |
| BcVec* ops, uint32_t* nexs) |
| { |
| BcStatus status; |
| BcLexTokenType top; |
| BcLexTokenType* ptr; |
| |
| if (ops->len == 0) { |
| return BC_STATUS_PARSE_INVALID_EXPR; |
| } |
| |
| ptr = bc_vec_top(ops); |
| top = *ptr; |
| |
| while (top != BC_LEX_LEFT_PAREN) { |
| |
| status = bc_vec_pushByte(code, bc_op_insts[top - BC_LEX_OP_POWER]); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_vec_pop(ops); |
| |
| if (status) { |
| return status; |
| } |
| |
| *nexs -= top != BC_LEX_OP_BOOL_NOT && |
| top != BC_LEX_OP_NEGATE ? 1 : 0; |
| |
| if (ops->len == 0) { |
| return BC_STATUS_PARSE_INVALID_EXPR; |
| } |
| |
| ptr = bc_vec_top(ops); |
| top = *ptr; |
| } |
| |
| status = bc_vec_pop(ops); |
| |
| if (status) { |
| return status; |
| } |
| |
| return bc_lex_next(&parse->lex, &parse->token); |
| } |
| |
| static BcStatus bc_parse_expr_name(BcParse* parse, BcVec* code, |
| BcExprType* type, bool posix_rel) |
| { |
| BcStatus status; |
| char* name; |
| |
| name = parse->token.string; |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type == BC_LEX_LEFT_BRACKET) { |
| |
| *type = BC_EXPR_ARRAY_ELEM; |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_expr(parse, code, posix_rel, false); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_RIGHT_BRACKET) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| status = bc_vec_pushByte(code, BC_INST_PUSH_ARRAY); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_pushName(code, name); |
| } |
| else if (parse->token.type == BC_LEX_LEFT_PAREN) { |
| |
| *type = BC_EXPR_FUNC_CALL; |
| |
| status = bc_parse_call(parse, code, name, posix_rel); |
| } |
| else { |
| |
| *type = BC_EXPR_VAR; |
| |
| status = bc_vec_pushByte(code, BC_INST_PUSH_VAR); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_pushName(code, name); |
| } |
| |
| if (status) { |
| return status; |
| } |
| |
| return status; |
| } |
| |
| static BcStatus bc_parse_call(BcParse* parse, BcVec* code, |
| char* name, bool posix_rel) |
| { |
| BcStatus status; |
| BcEntry entry; |
| size_t idx; |
| |
| entry.name = name; |
| |
| status = bc_parse_params(parse, code, posix_rel); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_RIGHT_PAREN) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| idx = bc_veco_index(&parse->program->func_map, &entry); |
| |
| if (idx == BC_INVALID_IDX) { |
| return BC_STATUS_EXEC_UNDEFINED_FUNC; |
| } |
| |
| status = bc_parse_pushIndex(code, idx); |
| |
| if (status) { |
| return status; |
| } |
| |
| return bc_lex_next(&parse->lex, &parse->token); |
| } |
| |
| static BcStatus bc_parse_params(BcParse* parse, BcVec* code, bool posix_rel) { |
| |
| BcStatus status; |
| bool comma; |
| size_t nparams; |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type == BC_LEX_RIGHT_PAREN) { |
| |
| status = bc_vec_pushByte(code, BC_INST_CALL); |
| |
| if (status) { |
| return status; |
| } |
| |
| return bc_vec_pushByte(code, 0); |
| } |
| |
| nparams = 0; |
| |
| do { |
| |
| ++nparams; |
| |
| status = bc_parse_expr(parse, code, posix_rel, false); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type == BC_LEX_COMMA) { |
| |
| comma = true; |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| } |
| else { |
| comma = false; |
| } |
| |
| } while (!status && parse->token.type != BC_LEX_RIGHT_PAREN); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (comma) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| status = bc_vec_pushByte(code, BC_INST_CALL); |
| |
| if (status) { |
| return status; |
| } |
| |
| return bc_parse_pushIndex(code, nparams); |
| } |
| |
| static BcStatus bc_parse_read(BcParse* parse, BcVec* code) { |
| |
| BcStatus status; |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_LEFT_PAREN) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_RIGHT_PAREN) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| status = bc_vec_pushByte(code, BC_INST_READ); |
| |
| if (status) { |
| return status; |
| } |
| |
| return bc_lex_next(&parse->lex, &parse->token); |
| } |
| |
| static BcStatus bc_parse_builtin(BcParse* parse, BcVec* code, |
| BcExprType type, bool posix_rel) |
| { |
| BcStatus status; |
| uint8_t inst; |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_LEFT_PAREN) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_expr(parse, code, posix_rel, false); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_RIGHT_PAREN) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| inst = type == BC_EXPR_LENGTH ? BC_INST_LENGTH : BC_INST_SQRT; |
| |
| status = bc_vec_pushByte(code, inst); |
| |
| if (status) { |
| return status; |
| } |
| |
| return bc_lex_next(&parse->lex, &parse->token); |
| } |
| |
| static BcStatus bc_parse_scale(BcParse* parse, BcVec* code, |
| BcExprType* type, bool posix_rel) |
| { |
| BcStatus status; |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_LEFT_PAREN) { |
| |
| *type = BC_EXPR_SCALE; |
| |
| return bc_vec_pushByte(code, BC_INST_PUSH_SCALE); |
| } |
| |
| *type = BC_EXPR_SCALE_FUNC; |
| |
| status = bc_parse_expr(parse, code, posix_rel, false); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_RIGHT_PAREN) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| status = bc_vec_pushByte(code, BC_INST_SCALE_FUNC); |
| |
| if (status) { |
| return status; |
| } |
| |
| return bc_lex_next(&parse->lex, &parse->token); |
| |
| return status; |
| } |
| |
| static BcStatus bc_parse_incdec(BcParse* parse, BcVec* code, BcExprType* prev) |
| { |
| BcStatus status; |
| BcLexTokenType type; |
| BcExprType etype; |
| uint8_t inst; |
| |
| etype = *prev; |
| |
| inst = parse->token.type == BC_LEX_OP_INC ? BC_INST_INC : BC_INST_DEC; |
| |
| if (etype == BC_EXPR_VAR || etype == BC_EXPR_ARRAY_ELEM || |
| etype == BC_EXPR_SCALE || etype == BC_EXPR_LAST || |
| etype == BC_EXPR_IBASE || etype == BC_EXPR_OBASE) |
| { |
| *prev = parse->token.type == BC_LEX_OP_INC ? |
| BC_EXPR_INC_POST : BC_EXPR_DEC_POST; |
| |
| status = bc_vec_pushByte(code, BC_INST_DUP_NUM); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_vec_pushByte(code, inst); |
| } |
| else { |
| |
| *prev = parse->token.type == BC_LEX_OP_INC ? |
| BC_EXPR_INC_PRE : BC_EXPR_DEC_PRE; |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| type = parse->token.type; |
| |
| switch (type) { |
| |
| case BC_LEX_NAME: |
| { |
| char* name; |
| |
| name = parse->token.string; |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type == BC_LEX_LEFT_PAREN) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| if (parse->token.type == BC_LEX_LEFT_BRACKET) { |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_expr(parse, code, false, false); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_RIGHT_BRACKET) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| status = bc_vec_pushByte(code, BC_INST_PUSH_ARRAY); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_pushName(code, name); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| } |
| else { |
| |
| status = bc_vec_pushByte(code, BC_INST_PUSH_VAR); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_pushName(code, name); |
| } |
| |
| break; |
| } |
| |
| case BC_LEX_KEY_IBASE: |
| { |
| status = bc_vec_pushByte(code, BC_INST_PUSH_IBASE); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| break; |
| } |
| |
| case BC_LEX_KEY_LAST: |
| { |
| status = bc_vec_pushByte(code, BC_INST_PUSH_LAST); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| break; |
| } |
| |
| case BC_LEX_KEY_OBASE: |
| { |
| status = bc_vec_pushByte(code, BC_INST_PUSH_OBASE); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| break; |
| } |
| |
| case BC_LEX_KEY_SCALE: |
| { |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type == BC_LEX_LEFT_PAREN) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| status = bc_vec_pushByte(code, BC_INST_PUSH_SCALE); |
| |
| break; |
| } |
| |
| default: |
| { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| } |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_vec_pushByte(code, inst); |
| } |
| |
| return status; |
| } |
| |
| static BcStatus bc_parse_minus(BcParse* parse, BcVec* exs, BcVec* ops, |
| BcExprType* prev, bool rparen, uint32_t* nexprs) |
| { |
| BcStatus status; |
| BcLexTokenType type; |
| BcExprType etype; |
| |
| etype = *prev; |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| type = parse->token.type; |
| |
| if (type != BC_LEX_NAME && type != BC_LEX_NUMBER && |
| type != BC_LEX_KEY_SCALE && type != BC_LEX_KEY_LAST && |
| type != BC_LEX_KEY_IBASE && type != BC_LEX_KEY_OBASE && |
| type != BC_LEX_LEFT_PAREN) |
| { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| type = rparen || etype == BC_EXPR_NUMBER || |
| etype == BC_EXPR_VAR || etype == BC_EXPR_ARRAY_ELEM || |
| etype == BC_EXPR_SCALE || etype == BC_EXPR_LAST || |
| etype == BC_EXPR_IBASE || etype == BC_EXPR_OBASE ? |
| BC_LEX_OP_MINUS : BC_LEX_OP_NEGATE; |
| |
| *prev = BC_PARSE_TOKEN_TO_EXPR(type); |
| |
| if (type == BC_LEX_OP_MINUS) { |
| status = bc_parse_operator(parse, exs, ops, type, nexprs, false); |
| } |
| else { |
| |
| // We can just push onto the op stack because this is the largest |
| // precedence operator that gets pushed. Inc/dec does not. |
| status = bc_vec_push(ops, &type); |
| } |
| |
| return status; |
| } |
| |
| static BcStatus bc_parse_string(BcParse* parse, BcVec* code) { |
| |
| BcStatus status; |
| size_t len; |
| |
| len = parse->program->strings.len; |
| |
| status = bc_vec_push(&parse->program->strings, &parse->token.string); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_vec_pushByte(code, BC_INST_STR); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_pushIndex(code, len); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| return bc_parse_semicolonListEnd(parse, code); |
| } |
| |
| static BcStatus bc_parse_return(BcParse* parse, BcVec* code) { |
| |
| BcStatus status; |
| |
| if (!BC_PARSE_FUNC(parse)) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_NEWLINE && |
| parse->token.type != BC_LEX_SEMICOLON && |
| parse->token.type != BC_LEX_LEFT_PAREN && |
| (status = bc_posix_error(BC_STATUS_POSIX_RETURN_PARENS, |
| parse->lex.file, parse->lex.line, NULL))) |
| { |
| return status; |
| } |
| |
| if (parse->token.type == BC_LEX_NEWLINE || |
| parse->token.type == BC_LEX_SEMICOLON) |
| { |
| status = bc_vec_pushByte(code, BC_INST_RETURN_ZERO); |
| } |
| else { |
| status = bc_parse_expr(parse, code, false, false); |
| } |
| |
| return status; |
| } |
| |
| static BcStatus bc_parse_print(BcParse* parse, BcVec* code) { |
| |
| BcStatus status; |
| BcLexTokenType type; |
| bool comma; |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| type = parse->token.type; |
| |
| if (type == BC_LEX_SEMICOLON || type == BC_LEX_NEWLINE) { |
| return BC_STATUS_PARSE_INVALID_PRINT; |
| } |
| |
| comma = false; |
| |
| while (!status && type != BC_LEX_SEMICOLON && type != BC_LEX_NEWLINE) { |
| |
| if (type == BC_LEX_STRING) { |
| |
| size_t len; |
| |
| len = parse->program->strings.len; |
| |
| status = bc_vec_push(&parse->program->strings, &parse->token.string); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_vec_pushByte(code, BC_INST_PRINT_STR); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_pushIndex(code, len); |
| } |
| else { |
| |
| status = bc_parse_expr(parse, code, false, false); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_vec_pushByte(code, BC_INST_PRINT); |
| } |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type == BC_LEX_COMMA) { |
| comma = true; |
| status = bc_lex_next(&parse->lex, &parse->token); |
| } |
| else { |
| comma = false; |
| } |
| |
| type = parse->token.type; |
| } |
| |
| if (status) { |
| return status; |
| } |
| |
| if (comma) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| return bc_lex_next(&parse->lex, &parse->token); |
| } |
| |
| static BcStatus bc_parse_if(BcParse* parse, BcVec* code) { |
| |
| BcStatus status; |
| BcInstPtr ip; |
| BcFunc* func; |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_LEFT_PAREN) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| func = bc_vec_item(&parse->program->funcs, parse->func); |
| |
| if (!func) { |
| return BC_STATUS_EXEC_UNDEFINED_FUNC; |
| } |
| |
| ip.idx = func->labels.len; |
| ip.func = 0; |
| |
| status = bc_vec_push(&parse->exit_labels, &ip); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_vec_push(&func->labels, &ip.idx); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_expr(parse, code, true, false); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_RIGHT_PAREN) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_vec_pushByte(code, BC_INST_JUMP_ZERO); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_pushIndex(code, ip.idx); |
| |
| if (status) { |
| return status; |
| } |
| |
| return bc_parse_endHeader(parse, code, func, ip.idx, BC_PARSE_FLAG_IF); |
| } |
| |
| static BcStatus bc_parse_else(BcParse* parse, BcVec* code) { |
| |
| BcStatus status; |
| BcInstPtr ip; |
| BcFunc* func; |
| |
| if (!BC_PARSE_IF_END(parse)) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| func = bc_vec_item(&parse->program->funcs, parse->func); |
| |
| if (!func) { |
| return BC_STATUS_PARSE_BUG; |
| } |
| |
| ip.idx = func->labels.len; |
| ip.func = 0; |
| |
| status = bc_vec_pushByte(code, BC_INST_JUMP); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_pushIndex(code, ip.idx); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_noElse(parse, code); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_vec_push(&parse->exit_labels, &ip); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| return bc_parse_endHeader(parse, code, func, ip.idx, BC_PARSE_FLAG_ELSE); |
| } |
| |
| static BcStatus bc_parse_noElse(BcParse* parse, BcVec* code) { |
| |
| uint8_t* flag_ptr; |
| BcInstPtr* ip; |
| BcFunc* func; |
| size_t* label; |
| |
| flag_ptr = BC_PARSE_TOP_FLAG_PTR(parse); |
| *flag_ptr = (*flag_ptr & ~(BC_PARSE_FLAG_IF_END)); |
| |
| ip = bc_vec_top(&parse->exit_labels); |
| |
| if (!ip || ip->func) { |
| return BC_STATUS_PARSE_BUG; |
| } |
| |
| func = bc_vec_item(&parse->program->funcs, parse->func); |
| |
| if (!func || code != &func->code) { |
| return BC_STATUS_PARSE_BUG; |
| } |
| |
| label = bc_vec_item(&func->labels, ip->idx); |
| |
| if (!label) { |
| return BC_STATUS_PARSE_BUG; |
| } |
| |
| *label = code->len; |
| |
| return bc_vec_pop(&parse->exit_labels); |
| } |
| |
| static BcStatus bc_parse_while(BcParse* parse, BcVec* code) { |
| |
| BcStatus status; |
| uint8_t flags; |
| BcFunc* func; |
| BcInstPtr ip; |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_LEFT_PAREN) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| func = bc_vec_item(&parse->program->funcs, parse->func); |
| |
| if (!func) { |
| return BC_STATUS_EXEC_UNDEFINED_FUNC; |
| } |
| |
| ip.idx = func->labels.len; |
| |
| status = bc_vec_push(&func->labels, &code->len); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_vec_push(&parse->cond_labels, &ip.idx); |
| |
| if (status) { |
| return status; |
| } |
| |
| ip.idx = func->labels.len; |
| ip.func = 1; |
| |
| status = bc_vec_push(&parse->exit_labels, &ip); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_vec_push(&func->labels, &ip.idx); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_expr(parse, code, true, false); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_RIGHT_PAREN) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_vec_pushByte(code, BC_INST_JUMP_ZERO); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_pushIndex(code, ip.idx); |
| |
| if (status) { |
| return status; |
| } |
| |
| flags = BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER; |
| |
| return bc_parse_endHeader(parse, code, func, ip.idx, flags); |
| } |
| |
| static BcStatus bc_parse_for(BcParse* parse, BcVec* code) { |
| |
| BcStatus status; |
| BcFunc* func; |
| BcInstPtr ip; |
| size_t cond_idx; |
| uint8_t flags; |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_LEFT_PAREN) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_SEMICOLON) { |
| status = bc_parse_expr(parse, code, false, false); |
| } |
| else { |
| status = bc_posix_error(BC_STATUS_POSIX_MISSING_FOR_INIT, |
| parse->lex.file, parse->lex.line, NULL); |
| } |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_SEMICOLON) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| func = bc_vec_item(&parse->program->funcs, parse->func); |
| |
| if (!func) { |
| return BC_STATUS_EXEC_UNDEFINED_FUNC; |
| } |
| |
| cond_idx = func->labels.len; |
| |
| status = bc_vec_push(&func->labels, &code->len); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_SEMICOLON) { |
| status = bc_parse_expr(parse, code, true, false); |
| } |
| else { |
| status = bc_posix_error(BC_STATUS_POSIX_MISSING_FOR_COND, |
| parse->lex.file, parse->lex.line, NULL); |
| } |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_SEMICOLON) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_vec_pushByte(code, BC_INST_JUMP_ZERO); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_pushIndex(code, cond_idx + 3); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_vec_pushByte(code, BC_INST_JUMP); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_pushIndex(code, cond_idx + 2); |
| |
| if (status) { |
| return status; |
| } |
| |
| ip.idx = func->labels.len; |
| |
| status = bc_vec_push(&parse->cond_labels, &ip.idx); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_vec_push(&func->labels, &code->len); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_RIGHT_PAREN) { |
| status = bc_parse_expr(parse, code, false, false); |
| } |
| else { |
| status = bc_posix_error(BC_STATUS_POSIX_MISSING_FOR_UPDATE, |
| parse->lex.file, parse->lex.line, NULL); |
| } |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_RIGHT_PAREN) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| status = bc_vec_pushByte(code, BC_INST_JUMP); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_pushIndex(code, cond_idx); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_vec_push(&func->labels, &code->len); |
| |
| if (status) { |
| return status; |
| } |
| |
| ip.idx = func->labels.len; |
| ip.func = 1; |
| |
| status = bc_vec_push(&parse->exit_labels, &ip); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_vec_push(&func->labels, &ip.idx); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| flags = BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER; |
| |
| return bc_parse_endHeader(parse, code, func, ip.idx, flags); |
| } |
| |
| static BcStatus bc_parse_startBody(BcParse* parse, BcVec* code, uint8_t flags) { |
| |
| BcStatus status; |
| uint8_t* flag_ptr; |
| |
| if (parse->token.type == BC_LEX_LEFT_BRACE) { |
| |
| flag_ptr = BC_PARSE_TOP_FLAG_PTR(parse); |
| |
| if (!flag_ptr) { |
| return BC_STATUS_PARSE_BUG; |
| } |
| |
| flags |= (*flag_ptr & (BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_LOOP)); |
| |
| status = bc_vec_push(&parse->flags, &flags); |
| |
| if (status) { |
| return status; |
| } |
| |
| ++parse->num_braces; |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| } |
| else { |
| |
| while (parse->token.type == BC_LEX_NEWLINE) { |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| } |
| } |
| |
| return bc_parse_stmt(parse, code); |
| } |
| |
| static BcStatus bc_parse_loopExit(BcParse* parse, BcVec* code, |
| BcLexTokenType type) |
| { |
| BcStatus status; |
| size_t idx; |
| |
| if (!BC_PARSE_LOOP(parse)) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| if (type == BC_LEX_KEY_BREAK) { |
| |
| size_t top; |
| BcInstPtr* ip; |
| |
| top = parse->exit_labels.len - 1; |
| ip = bc_vec_item(&parse->exit_labels, top); |
| |
| while (top < parse->exit_labels.len && ip && !ip->func) { |
| --top; |
| } |
| |
| if (top >= parse->exit_labels.len || !ip) { |
| return BC_STATUS_PARSE_BUG; |
| } |
| |
| idx = ip->idx; |
| } |
| else { |
| idx = *((size_t*) bc_vec_top(&parse->cond_labels)); |
| } |
| |
| status = bc_vec_pushByte(code, BC_INST_JUMP); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_pushIndex(code, idx); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type != BC_LEX_SEMICOLON && |
| parse->token.type != BC_LEX_NEWLINE) |
| { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| return bc_lex_next(&parse->lex, &parse->token); |
| } |
| |
| static BcStatus bc_parse_rightBrace(BcParse* parse, BcVec* code) { |
| |
| BcStatus status; |
| |
| if (parse->flags.len <= 1 || parse->num_braces == 0) { |
| return BC_STATUS_PARSE_INVALID_TOKEN; |
| } |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (BC_PARSE_IF(parse)) { |
| |
| while (parse->token.type == BC_LEX_NEWLINE) { |
| |
| status = bc_lex_next(&parse->lex, &parse->token); |
| |
| if (status) { |
| return status; |
| } |
| } |
| |
| if (parse->token.type == BC_LEX_KEY_ELSE) { |
| status = bc_parse_else(parse, code); |
| } |
| else { |
| |
| uint8_t* flag_ptr; |
| |
| status = bc_vec_pop(&parse->flags); |
| |
| if (status) { |
| return status; |
| } |
| |
| flag_ptr = BC_PARSE_TOP_FLAG_PTR(parse); |
| |
| *flag_ptr = (*flag_ptr | BC_PARSE_FLAG_IF_END); |
| |
| return BC_STATUS_SUCCESS; |
| } |
| } |
| else if (BC_PARSE_FUNC_INNER(parse)) { |
| |
| parse->func = 0; |
| |
| status = bc_vec_pushByte(code, BC_INST_RETURN_ZERO); |
| } |
| else { |
| |
| BcInstPtr* ip; |
| BcFunc* func; |
| size_t* label; |
| |
| ip = bc_vec_top(&parse->exit_labels); |
| |
| if (!ip) { |
| return BC_STATUS_PARSE_BUG; |
| } |
| |
| func = bc_vec_item(&parse->program->funcs, parse->func); |
| |
| if (!func) { |
| return BC_STATUS_PARSE_BUG; |
| } |
| |
| label = bc_vec_item(&func->labels, ip->idx); |
| |
| if (!label) { |
| return BC_STATUS_PARSE_BUG; |
| } |
| |
| *label = code->len; |
| } |
| |
| if (status) { |
| return status; |
| } |
| |
| --parse->num_braces; |
| |
| return bc_vec_pop(&parse->flags); |
| } |
| |
| static BcStatus bc_parse_pushName(BcVec* code, char* name) { |
| |
| BcStatus status; |
| size_t len; |
| |
| status = BC_STATUS_SUCCESS; |
| len = strlen(name); |
| |
| for (size_t i = 0; !status && i < len; ++i) { |
| status = bc_vec_pushByte(code, (uint8_t) name[i]); |
| } |
| |
| if (status) { |
| return status; |
| } |
| |
| free(name); |
| |
| return bc_vec_pushByte(code, (uint8_t) ':'); |
| } |
| |
| static BcStatus bc_parse_pushIndex(BcVec* code, size_t idx) { |
| |
| BcStatus status; |
| uint8_t amt; |
| uint8_t nums[sizeof(size_t)]; |
| uint32_t i; |
| |
| amt = 0; |
| i = 0; |
| |
| while (idx) { |
| nums[amt] = (uint8_t) idx; |
| idx &= ~(0xFF); |
| idx >>= sizeof(uint8_t); |
| ++amt; |
| } |
| |
| status = bc_vec_pushByte(code, amt); |
| |
| if (status) { |
| return status; |
| } |
| |
| while (!status && i < amt) { |
| status = bc_vec_pushByte(code, nums[idx]); |
| ++i; |
| } |
| |
| return status; |
| } |
| |
| static BcStatus bc_parse_endHeader(BcParse* parse, BcVec* code, BcFunc* func, |
| size_t idx, uint8_t flags) |
| { |
| BcStatus status; |
| |
| status = bc_vec_pushByte(code, BC_INST_JUMP_ZERO); |
| |
| if (status) { |
| return status; |
| } |
| |
| status = bc_parse_pushIndex(code, idx); |
| |
| if (status) { |
| return status; |
| } |
| |
| if (parse->token.type == BC_LEX_LEFT_BRACE) { |
| status = bc_parse_startBody(parse, code, flags); |
| } |
| else if (parse->token.type == BC_LEX_NEWLINE) { |
| |
| flags |= BC_PARSE_FLAG_HEADER; |
| |
| status = bc_vec_push(&parse->flags, &flags); |
| } |
| else { |
| |
| BcInstPtr* ip_ptr; |
| |
| status = bc_parse_stmt(parse, code); |
| |
| if (status) { |
| return status; |
| } |
| |
| ip_ptr = bc_vec_item(&func->labels, idx); |
| |
| if (!ip_ptr) { |
| return BC_STATUS_PARSE_BUG; |
| } |
| |
| ip_ptr->idx = code->len; |
| |
| status = bc_vec_pop(&parse->exit_labels); |
| } |
| |
| return status; |
| } |