#include "parser.h" #include "tokenizer.h" #include "scope.h" #include "types.h" #include "error.h" #include #include #include #include #define MAX_CODE_SIZE 65536 #define MAX_LABELS 256 #define MAX_FUNCTIONS 256 #define MAX_EXTERNAL_FUNCTIONS 256 #define MAX_IDENTIFIER_LENGTH 40 #define MAX_CONSTANTS 256 #define MAX_STRUCT_MEMBERS 10 /* struct t_variable { char name[MAX_IDENTIFIER_LENGTH]; int type; int length; int address; }; struct t_struct { char name[MAX_IDENTIFIER_LENGTH]; int num_members; t_variable members[MAX_STRUCT_MEMBERS]; }; struct t_scope { }; */ struct t_label { char name[MAX_IDENTIFIER_LENGTH]; word address; }; /*struct t_variable { char name[MAX_IDENTIFIER_LENGTH]; int index = 0; int size = 1; t_variable* next{ nullptr }; }; struct t_external_function { char name[MAX_IDENTIFIER_LENGTH]; int num_parameters = 0; }; struct t_string { unsigned char string[256]; word references[256]; int num_references = 1; }; struct t_constant { char name[MAX_IDENTIFIER_LENGTH]; byte value; };*/ static byte* code; // [MAX_CODE_SIZE]; static uint32_t codepos = 0; static uint32_t lines[32768]; static uint32_t current_line; //static t_label known_labels[MAX_LABELS]; //static int num_known_labels = 0; static t_label unknown_labels[MAX_LABELS]; static int num_unknown_labels = 0; static int num_anonymous_labels = 0; static char* breaklabel = nullptr; static char* contlabel = nullptr; /* static t_variable* variables = nullptr; static int num_variables = 0; static bool variable_is_array = false; static t_external_function external_functions[MAX_EXTERNAL_FUNCTIONS]; static int num_external_functions = 0; static t_string strings[256]; static int num_strings = 0; static t_constant constants[MAX_CONSTANTS]; static int num_constants; */ static bool parser_finished = false; /****************************************************************************************/ /* GENERIC FUNCTIONS */ /****************************************************************************************/ static void int_to_string(int value, char* label) { for (int i = 0; i < 7; i++) label[i] = '0'; label[7] = 0; int i = 6; while (value > 0) { label[i] = 48 + (value % 10); value = value / 10; i--; } } /****************************************************************************************/ /* BYTECODE GENERATOR */ /****************************************************************************************/ static void emmit(const byte value) { lines[codepos] = current_line; code[codepos++] = value; } static void emmit_w(const word value) { lines[codepos] = current_line; code[codepos++] = value & 255; lines[codepos] = current_line; code[codepos++] = value >> 8; } static void emmit_dw(const uint32_t value) { byte *v = (byte*)&value; lines[codepos] = current_line; code[codepos++] = *(v++); lines[codepos] = current_line; code[codepos++] = *(v++); lines[codepos] = current_line; code[codepos++] = *(v++); lines[codepos] = current_line; code[codepos++] = *(v++); } static void emmit_f(const float value) { byte *v = (byte*)&value; lines[codepos] = current_line; code[codepos++] = *(v++); lines[codepos] = current_line; code[codepos++] = *(v++); lines[codepos] = current_line; code[codepos++] = *(v++); lines[codepos] = current_line; code[codepos++] = *(v++); } static void patch(const uint32_t address, const word dest) { code[address] = dest & 255; code[address + 1] = dest >> 8; } static void patch_dw(const uint32_t address, const uint32_t dest) { byte *v = (byte*)&dest; code[address] = v[0]; code[address+1] = v[1]; code[address+2] = v[2]; code[address+3] = v[3]; } static const uint32_t get_current_address() { return codepos; } /****************************************************************************************/ /* LABEL MANAGEMENT */ /****************************************************************************************/ /* static const word get_label_address(const char* string) { for (int i = 0; i < num_known_labels; i++) { if (strcmp(known_labels[i].name, string) == 0) return known_labels[i].address; } //unknown_labels[num_unknown_labels].name = (char*)malloc(strlen(string) + 1); strcpy(unknown_labels[num_unknown_labels].name, string); unknown_labels[num_unknown_labels].address = get_current_address(); num_unknown_labels++; return 0; } static void register_label_address(const char* string) { // If the label already exists, vomit an error for (int i = 0; i < num_known_labels; i++) { if (strcmp(known_labels[i].name, string) == 0) { parser_finished = true; error_raise("Duplicate label"); return; } } // register new label //known_labels[num_known_labels].name = (char*)malloc(strlen(string) + 1); strcpy(known_labels[num_known_labels].name, string); known_labels[num_known_labels++].address = get_current_address(); // patch and remove any references awaiting for this label int i = 0; while (i < num_unknown_labels) { if (strcmp(unknown_labels[i].name, string) == 0) { patch(unknown_labels[i].address, get_current_address()); if (i < num_unknown_labels - 1) { //free(unknown_labels[i].name); unknown_labels[i].address = unknown_labels[num_unknown_labels - 1].address; strcpy(unknown_labels[i].name, unknown_labels[num_unknown_labels - 1].name); //unknown_labels[i].name = unknown_labels[num_unknown_labels - 1].name; } num_unknown_labels--; } else { i++; } } //num_known_labels++; } static void generate_anonymous_labelname(char* dest) { int_to_string(num_anonymous_labels++, dest); } */ /****************************************************************************************/ /* PARSER */ /****************************************************************************************/ static void parse_expression(); static void parse_statements(); /* XXX [RZC 27/04/2021] No usat en JailBasic //static void parse_strleft(); static void parse_str(); static void parse_chr(); static void parse_strlen(); static void parse_keypressed(); static void parse_anykey(); static void parse_getchar(); static void parse_getcolor(); */ #define EXPECT(X, Y) if (tkn_get_token() != X) { parser_finished = true; error_raise(Y); return; } #define EXPECT2(X, Y) if (tkn_get_token() != X) { parser_finished = true; error_raise(Y); return -1; } #define HALT(Y) { parser_finished = true; error_raise(Y); return; } #define HALT2(Y) { parser_finished = true; error_raise(Y); return -1; } /* static void parse_concat_atom() { if (tkn_get_token() == TOKEN_STRING) { emmit(OP_SETX); emmit_w(register_string(tkn_get_string())); emmit(OP_JSR); emmit_w(get_label_address("_sys_string_load")); tkn_next(); return; } if (tkn_get_token() == TOKEN_HEXSTRING) { emmit(OP_SETX); emmit_w(register_array(tkn_get_array())); emmit(OP_JSR); emmit_w(get_label_address("_sys_string_load")); tkn_next(); return; } if (tkn_get_token() == TOKEN_IDENTIFIER) { int ivar = get_variable_index(tkn_get_string()); if (!is_variable_array()) { parser_finished = true; error_raise("Expected string constant or variable"); return; } emmit(OP_SETX); emmit_w(ivar); emmit(OP_JSR); emmit_w(get_label_address("_sys_string_load")); tkn_next(); return; } if (tkn_get_token() == TOKEN_STRLEFT) { tkn_next(); parse_strleft(); return; } if (tkn_get_token() == TOKEN_STR) { tkn_next(); parse_str(); return; } if (tkn_get_token() == TOKEN_CHR) { tkn_next(); parse_chr(); return; } parser_finished = true; error_raise("Syntax error"); return; } static void parse_concatenation() { parse_concat_atom(); while (tkn_get_token() == TOKEN_COMMA) { tkn_next(); parse_concat_atom(); emmit(OP_CONCAT); } } */ static int get_variable_address() { int address = scope_get_variable_address(); uint32_t var_length = scope_get_variable_size(); uint32_t var_type = scope_get_variable_type(); while (true) { // Si es un array... if (var_length > 1) { tkn_next(); EXPECT2(TOKEN_LBRACKET, "Expected '['"); tkn_next(); EXPECT2(TOKEN_NUMBER, "Expected number"); const int index = (int)tkn_get_value(); if (tkn_get_value() > index) HALT2("Expected integer value"); if (index >= var_length) HALT2("Index out of bounds"); tkn_next(); EXPECT2(TOKEN_RBRACKET, "Expected ']'"); address += index * types_get_length(var_type); } // Si es un struct... if (var_type >= 2) { tkn_next(); EXPECT2(TOKEN_DOT, "Expected '.'"); tkn_next(); EXPECT2(TOKEN_IDENTIFIER, "Expected identifier"); const std::string member_name = tkn_get_string(); const int member_num = types_member_exists(var_type, member_name); if (member_num == -1) HALT2("Unknown member"); address += types_get_member_offset(var_type, member_num); var_length = types_get_member_length(var_type, member_num); var_type = types_get_member_type(var_type, member_num); } else { return address; } } } static void parse_expr_atom() { // NUM, VAR, UNARY, PAREN if (tkn_get_token() == TOKEN_NUMBER) { emmit(OP_PUSH); emmit_f(tkn_get_value()); tkn_next(); return; } if (tkn_get_token() == TOKEN_IDENTIFIER) { if (scope_variable_exists(tkn_get_string())) { const int address = get_variable_address(); if (address == -1) return; emmit(scope_is_local() ? OP_LDL : OP_LD); emmit(address); tkn_next(); return; } else { // TODO [RZC 03/05/2021] Revisar funcions parser_finished = true; error_raise("Unknown identifier"); return; } } if (tkn_get_token() == TOKEN_MINUS) { tkn_next(); parse_expr_atom(); emmit(OP_NEG); return; } if (tkn_get_token() == TOKEN_NOT) { tkn_next(); parse_expr_atom(); emmit(OP_NOT); return; } if (tkn_get_token() == TOKEN_LPAR) { tkn_next(); parse_expression(); EXPECT(TOKEN_RPAR, "Expected ')'"); tkn_next(); return; } parser_finished = true; error_raise("Syntax error"); return; } static void parse_expr_mul() { parse_expr_atom(); while (tkn_get_token() == TOKEN_ASTERISC || tkn_get_token() == TOKEN_SLASH || tkn_get_token() == TOKEN_MOD) { t_tokentype operat = tkn_get_token(); tkn_next(); parse_expr_atom(); switch (operat) { case TOKEN_ASTERISC: emmit(OP_MUL); break; case TOKEN_SLASH: emmit(OP_DIV); break; case TOKEN_MOD: emmit(OP_MOD); break; default: /* Impossible */ break; } } } static void parse_expr_sum() { parse_expr_mul(); while (tkn_get_token() == TOKEN_PLUS || tkn_get_token() == TOKEN_MINUS) { t_tokentype operat = tkn_get_token(); tkn_next(); parse_expr_mul(); switch (operat) { case TOKEN_PLUS: emmit(OP_ADD); break; case TOKEN_MINUS: emmit(OP_SUB); break; default: /* Impossible */ break; } } } static void parse_expr_bool() { parse_expr_sum(); while (tkn_get_token() == TOKEN_AND || tkn_get_token() == TOKEN_OR) { t_tokentype operat = tkn_get_token(); tkn_next(); parse_expr_sum(); switch (operat) { case TOKEN_AND: emmit(OP_AND); break; case TOKEN_OR: emmit(OP_OR); break; default: /* Impossible */ break; } } } static void parse_expression() { parse_expr_bool(); while (tkn_get_token() == TOKEN_EQ || tkn_get_token() == TOKEN_NEQ || tkn_get_token() == TOKEN_LT || tkn_get_token() == TOKEN_GT || tkn_get_token() == TOKEN_LEQ || tkn_get_token() == TOKEN_GEQ) { t_tokentype operat = tkn_get_token(); tkn_next(); parse_expr_bool(); switch (operat) { case TOKEN_EQ: emmit(OP_EQ); break; case TOKEN_NEQ: emmit(OP_NEQ); break; case TOKEN_LT: emmit(OP_LT); break; case TOKEN_GT: emmit(OP_GT); break; case TOKEN_LEQ: emmit(OP_LEQ); break; case TOKEN_GEQ: emmit(OP_GEQ); break; default: /* Impossible */ break; } } } static bool identifier_exists(const std::string identifier) { if (scope_variable_exists(identifier)) return true; if (types_exist(identifier) != -1) return true; return false; } static void parse_const() { EXPECT(TOKEN_IDENTIFIER, "Expected identifier"); char const_name[40]; strcpy(const_name, tkn_get_string()); if (identifier_exists(const_name)) HALT("Identifier already exists"); tkn_next(); EXPECT(TOKEN_AS, "Expected 'as'"); tkn_next(); EXPECT(TOKEN_IDENTIFIER, "Expected type identifier"); char type_name[40]; strcpy(type_name, tkn_get_string()); const int type_num = types_exist(type_name); if (type_num == -1) HALT("Unknown type"); if (type_num >= 2) HALT("Only 'number' or 'string' constants allowed"); tkn_next(); EXPECT(TOKEN_EQ, "Expected '='"); tkn_next(); parse_expression(); const int var_address = scope_declare_variable(const_name, type_num, 0); emmit(scope_is_local() ? OP_STL : OP_ST); emmit_dw(var_address); } static void parse_var() { int var_length = 1; EXPECT(TOKEN_IDENTIFIER, "Expected identifier"); const std::string var_name = tkn_get_string(); if (identifier_exists(var_name)) HALT("Identifier already exists"); tkn_next(); EXPECT(TOKEN_AS, "Expected 'as'"); tkn_next(); if (tkn_get_token() == TOKEN_ARRAY) { tkn_next(); EXPECT(TOKEN_OF, "Expected 'of'"); tkn_next(); EXPECT(TOKEN_NUMBER, "Expected number"); const float length = tkn_get_value(); var_length = (int)length; if (length > var_length) HALT("Expected integer value"); tkn_next(); } EXPECT(TOKEN_IDENTIFIER, "Expected type identifier"); const std::string type_name = tkn_get_string(); const int type_num = types_exist(type_name); if (type_num == -1) HALT("Unknown type"); tkn_next(); if (tkn_get_token() == TOKEN_EQ) { if (type_num >= 2) HALT("Only 'number' or 'string' variables can be initialized on declaration") if (var_length > 1) HALT("Array variables cannot be initialized on declaration") tkn_next(); parse_expression(); const int var_address = scope_declare_variable(var_name, type_num, var_length); emmit(scope_is_local() ? OP_STL : OP_ST); emmit_dw(var_address); } else { scope_declare_variable(var_name, type_num, var_length); //tkn_next(); } } static void parse_struct() { EXPECT(TOKEN_IDENTIFIER, "Expected identifier"); char struct_name[40]; strcpy(struct_name, tkn_get_string()); if (identifier_exists(struct_name)) HALT("Identifier already exists"); const int struct_num = types_add(struct_name); tkn_next(); while (tkn_get_token() != TOKEN_END) { EXPECT(TOKEN_IDENTIFIER, "Expected identifier"); char member_name[40]; strcpy(member_name, tkn_get_string()); tkn_next(); EXPECT(TOKEN_AS, "Expected 'as'"); // TODO [RZC 04/05/2021] Implement support for arrays tkn_next(); EXPECT(TOKEN_IDENTIFIER, "Expected type identifier"); char type_name[40]; strcpy(type_name, tkn_get_string()); const int type_num = types_exist(type_name); if (type_num == -1) HALT("Unknown type"); types_add_member(struct_num, member_name, type_num, 1); tkn_next(); } tkn_next(); } /*static void parse_dim() { EXPECT(TOKEN_IDENTIFIER, "Expected variable"); char varname[40]; strcpy(varname, tkn_get_string()); tkn_next(); EXPECT(TOKEN_LPAR, "Expected array size"); tkn_next(); EXPECT(TOKEN_NUMBER, "Expected integer constant as array size"); get_variable_index(varname, tkn_get_value()); // register variable with size, no need to keep ival tkn_next(); EXPECT(TOKEN_RPAR, "Expected ')'"); tkn_next(); }*/ // XXX [RZC 27/04/2021] Desactivat INC i DEC per ara /*static void parse_inc() { EXPECT(TOKEN_IDENTIFIER, "Expected variable"); int ivar = get_variable_index(tkn_get_string()); if (is_variable_array()) { tkn_next(); EXPECT(TOKEN_LPAR, "Expected array index"); tkn_next(); parse_expr_atom(); EXPECT(TOKEN_RPAR, "Expected ')'"); emmit(OP_SETY); emmit(OP_LOADI); emmit_w(ivar); emmit(OP_INC); emmit(OP_STOREI); emmit_w(ivar); } else { emmit(OP_LOAD); emmit_w(ivar); emmit(OP_INC); emmit(OP_STORE); emmit_w(ivar); } tkn_next(); } static void parse_dec() { EXPECT(TOKEN_IDENTIFIER, "Expected variable"); int ivar = get_variable_index(tkn_get_string()); if (is_variable_array()) { tkn_next(); EXPECT(TOKEN_LPAR, "Expected array index"); tkn_next(); parse_expr_atom(); EXPECT(TOKEN_RPAR, "Expected ')'"); emmit(OP_SETY); emmit(OP_LOADI); emmit_w(ivar); emmit(OP_DEC); emmit(OP_STOREI); emmit_w(ivar); } else { emmit(OP_LOAD); emmit_w(ivar); emmit(OP_DEC); emmit(OP_STORE); emmit_w(ivar); } tkn_next(); }*/ // XXX [RZC 27/04/2021] Desactivat, no se molt be ni què es /*static void parse_let() { EXPECT(TOKEN_IDENTIFIER, "Expected variable"); int ivar = get_variable_index(tkn_get_string()); if (is_variable_array()) { tkn_next(); if (tkn_get_token() == TOKEN_COLON) { tkn_next(); parse_concatenation(); emmit(OP_SETX); emmit_w(ivar); emmit(OP_JSR); emmit_w(get_label_address("_sys_string_store")); return; } else { EXPECT(TOKEN_LPAR, "Expected ':' or '('"); tkn_next(); parse_expr_atom(); EXPECT(TOKEN_RPAR, "Expected ')'"); emmit(OP_SETY); } } tkn_next(); EXPECT(TOKEN_EQ, "Expected '='"); tkn_next(); parse_expression(); if (is_variable_array()) { emmit(OP_STOREI); emmit_w(ivar); } else { emmit(OP_STORE); emmit_w(ivar); } }*/ /* static void parse_if() { char* previous_breaklabel = breaklabel; parse_expression(); EXPECT(TOKEN_THEN, "Expected 'THEN'"); tkn_next(); emmit(OP_JNT); char elselabel[8]; generate_anonymous_labelname(elselabel); char endlabel[8]; generate_anonymous_labelname(endlabel); breaklabel = endlabel; bool else_visited = false; emmit_w(get_label_address(elselabel)); parse_statements(); if (tkn_get_token() == TOKEN_ELSE) { else_visited = true; emmit(OP_JMP); emmit_w(get_label_address(endlabel)); register_label_address(elselabel); tkn_next(); parse_statements(); } if (tkn_get_token() == TOKEN_END) { if (!else_visited) register_label_address(elselabel); register_label_address(endlabel); tkn_next(); breaklabel = previous_breaklabel; return; } parser_finished = true; if (!else_visited) error_raise("Expected 'ELSE', 'END' or a statement"); else error_raise("Expected 'END' or a statement"); } static void parse_for() { char* previous_breaklabel = breaklabel; char* previous_contlabel = contlabel; EXPECT(TOKEN_IDENTIFIER, "Expected variable"); int ivar = get_variable_index(tkn_get_string()); tkn_next(); EXPECT(TOKEN_EQ, "Expected '='"); tkn_next(); parse_expression(); emmit(OP_STORE); emmit_w(ivar); char forlabel[8]; generate_anonymous_labelname(forlabel); char endlabel[8]; generate_anonymous_labelname(endlabel); char continuelabel[8]; generate_anonymous_labelname(continuelabel); breaklabel = endlabel; contlabel = continuelabel; register_label_address(forlabel); EXPECT(TOKEN_TO, "Expected 'TO'"); tkn_next(); parse_expression(); emmit(OP_LOAD); emmit_w(ivar); emmit(OP_EQ); emmit(OP_JTR); emmit_w(get_label_address(endlabel)); //int endLabel = GetCurrentAddress(); parse_statements(); EXPECT(TOKEN_END, "Expected 'END'"); tkn_next(); register_label_address(continuelabel); emmit(OP_LOAD); emmit_w(ivar); emmit(OP_INC); emmit(OP_STORE); emmit_w(ivar); emmit(OP_JMP); emmit_w(get_label_address(forlabel)); register_label_address(endlabel); //Patch(endLabel, GetCurrentAddress()); breaklabel = previous_breaklabel; contlabel = previous_contlabel; } static void parse_break() { if (breaklabel == nullptr) { parser_finished = true; error_raise("Can't break outside of a loop or condition"); return; } emmit(OP_JMP); emmit_w(get_label_address(breaklabel)); } static void parse_continue() { if (contlabel == nullptr) { parser_finished = true; error_raise("Can't continue outside of a loop"); return; } emmit(OP_JMP); emmit_w(get_label_address(contlabel)); } */ // XXX [RZC 27/04/2021] Ni GOTO ni GOSUB /*static void parse_goto() { emmit(OP_JMP); EXPECT(TOKEN_IDENTIFIER, "Expected label name"); emmit_w(get_label_address(tkn_get_string())); tkn_next(); } static void parse_gosub() { emmit(OP_JSR); EXPECT(TOKEN_IDENTIFIER, "Expected label name"); emmit_w(get_label_address(tkn_get_string())); tkn_next(); }*/ /* static void parse_return() { // TODO [RZC 27/04/2021] Falta afegir paràmetre de retorn emmit(OP_RET); } */ // XXX [RZC 27/04/2021] No hi ha labels /*static void parse_label() { register_label_address(tkn_get_string()); tkn_next(); }*/ /* static void parse_call() { for (int i = 0; i < num_external_functions; i++) { if (strcmp(external_functions[i].name, tkn_get_string()) == 0) { if (external_functions[i].num_parameters == -1) { tkn_next(); EXPECT(TOKEN_STRING, "Expected string"); emmit(OP_PUSH); emmit(register_string(tkn_get_string())); } else { tkn_next(); for (int j = 0; j < external_functions[i].num_parameters; j++) { //if (j != 0) EXPECT(TOKEN_COMMA, "Expected comma"); parse_expression(); } } emmit(OP_CALL); emmit(i); } } } static void parse_statements() { while (!parser_finished) { current_line = tkn_get_line(); switch (tkn_get_token()) { case TOKEN_CONST: tkn_next(); parse_const(); break; case TOKEN_DIM: tkn_next(); parse_dim(); break; case TOKEN_LET: tkn_next(); parse_let(); break; case TOKEN_INC: tkn_next(); parse_inc(); break; case TOKEN_DEC: tkn_next(); parse_dec(); break; case TOKEN_IF: tkn_next(); parse_if(); break; case TOKEN_FOR: tkn_next(); parse_for(); break; case TOKEN_BREAK: tkn_next(); parse_break(); break; case TOKEN_CONTINUE: tkn_next(); parse_continue(); break; case TOKEN_RETURN: tkn_next(); parse_return(); break; case TOKEN_REM: tkn_next(); break; case TOKEN_IDENTIFIER: for (int i = 0; i < num_external_functions; i++) { if (strcmp(external_functions[i].name, tkn_get_string()) == 0) { parse_call(); break; } } parse_let(); break; default: return; } } } */ static void parse_global_statements() { while (!parser_finished) { current_line = tkn_get_line(); switch (tkn_get_token()) { case TOKEN_CONST: tkn_next(); parse_const(); break; case TOKEN_VAR: tkn_next(); parse_var(); break; case TOKEN_STRUCT: tkn_next(); parse_struct(); break; /*case TOKEN_FUNCTION: tkn_next(); parse_function(); break;*/ case TOKEN_REM: tkn_next(); break; case TOKEN_ENDFILE: return; default: HALT("Syntax error"); return; } } } // XXX [RZC 27/04/2021] No usat en JailBasic /*static void include_labels() { FILE *f = fopen("rom.lbl", "rb"); byte num_labels; fread(&num_labels, 1, 1, f); for (int i = 0; i < num_labels; i++) { word address; fread(&address, 2, 1, f); byte len; fread(&len, 1, 1, f); char label[50]; fread(label, len, 1, f); label[len] = 0; strcpy(known_labels[num_known_labels].name, label); known_labels[num_known_labels++].address = address; } fclose(f); }*/ /****************************************************************************************/ /* INTERFACE */ /****************************************************************************************/ void parser_parse(const char* buffer, byte* mem) { tkn_init(buffer); scope_init(); types_init(); parser_finished = false; //include_labels(); code = mem; codepos = 2; // XXX [RZC 27/04/2021] Perquè 2? tkn_next(); parse_global_statements(); //append_strings(); // XXX [RZC 27/04/2021] Encara vull açò? si el bytecode no ha de ser independent, no veig que faça falta if (error_raised()) { // XXX [RZC 27/04/2021] Res, per ara que printf el error i pete error_print(NULL); //codepos = 0xA000; //emmit(OP_JMP); //emmit_w(0xA000); } //FILE *f = fopen("test.bin", "wb"); //fwrite(mem, codepos, 1, f); //fclose(f); //return code; } //const int parser_get_codesize() { return codepos; } //const int parser_get_memory_usage() { return num_variables; } //unsigned short* parser_get_lines() { return lines; } /* void parser_register_external_function(const char* name, const char* parameters, void (*fun)(void)) { strcpy(external_functions[num_external_functions].name, name); external_functions[num_external_functions++].num_parameters = num_parameters; } */