Files
jailbasic/parser.cpp

801 lines
22 KiB
C++

#include "parser.h"
#include "tokenizer.h"
#include "error.h"
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#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);
}
/****************************************************************************************/
/* STRING MANAGEMENT */
/****************************************************************************************/
static const bool same_string(const unsigned char* a, const unsigned char*b) {
for (int i = 0; i <= a[0]; i++) if (a[i] != b[i]) return false;
return true;
}
static void copy_string(unsigned char* dest, const unsigned char* src) {
memcpy(dest, src, src[0] + 1);
//for (int i = 0; i <= src[0]; i++) dest[i] = src[i];
}
static void to_basic_string(unsigned char* dest, const char* src) {
int len = strlen(src);
*dest = len; dest++;
memcpy(dest, src, len);
}
static int register_array(const unsigned char* string) {
for (int i = 0; i < num_strings; i++) {
if (same_string(strings[i].string, string)) { strings[i].references[strings[i].num_references++] = get_current_address(); return 0; }
}
copy_string(strings[num_strings].string, string);
strings[num_strings++].references[0] = get_current_address();
return 0;
}
static int register_string(const char* string) {
unsigned char new_str[255]; to_basic_string(new_str, string);
for (int i = 0; i < num_strings; i++) {
if (same_string(strings[i].string, new_str)) { strings[i].references[strings[i].num_references++] = get_current_address(); return 0; }
}
copy_string(strings[num_strings].string, new_str);
strings[num_strings++].references[0] = get_current_address();
return 0;
}
static void append_strings() {
code[0] = codepos & 255;
code[1] = codepos >> 8;
emmit(num_strings);
for (int i = 0; i < num_strings; i++) {
for (int j = 0; j < strings[i].num_references; j++) { patch(strings[i].references[j], get_current_address()); }
//char len = strings[i].string[0];
//emmit(len);
for (int j = 0; j <= strings[i].string[0]; j++) { emmit(strings[i].string[j]); }
}
}
/****************************************************************************************/
/* PARSER */
/****************************************************************************************/
static void parse_expression();
static void parse_statements();
/* [RZC 27/04/2021] No usat en JailScript
//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; }
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 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) {
// [RZC 27/04/2021] Per ara llevem les constants
/*int constvalue = get_constant(tkn_get_string());
if (constvalue != -1) {
emmit(OP_PUSH);
emmit(constvalue);
tkn_next();
return;
}*/
// [RZC 27/04/2021] Per ara llevem les variables
/*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);
} else {
emmit(OP_LOAD);
emmit_w(ivar);
}*/
// [RZC 27/04/2021] Atenció! Ara les variables han de estar declarades abans, pel que pot haver error ací si no s'ha trobat.
tkn_next();
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;
}
// [RZC 27/04/2021] No usat en JailScript, pero hi haurà que afegir les funcions
/*
if (tkn_get_token() == TOKEN_STRLEN) { tkn_next(); parse_strlen(); return; }
if (tkn_get_token() == TOKEN_KEYPRESSED) { tkn_next(); parse_keypressed(); return; }
if (tkn_get_token() == TOKEN_ANYKEY) { tkn_next(); parse_anykey(); return; }
if (tkn_get_token() == TOKEN_GETCHAR) { tkn_next(); parse_getchar(); return; }
if (tkn_get_token() == TOKEN_GETCOLOR) { tkn_next(); parse_getcolor(); 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 void parse_const() {
EXPECT(TOKEN_IDENTIFIER, "Expected identifier");
char const_name[40];
strcpy(const_name, tkn_get_string());
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());
tkn_next(); EXPECT(TOKEN_EQ, "Expected '='");
tkn_next(); EXPECT(TOKEN_NUMBER, "Expected integer constant");
int constvalue = tkn_get_value();
parser_register_constant(constname, constvalue);
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();
}
// [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();
}*/
// [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));
}
// [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() {
// [RZC 27/04/2021] [TODO] Falta afegir paràmetre de retorn
emmit(OP_RET);
}
// [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;
default:
return;
}
}
}
// [RZC 27/04/2021] No usat en JailScript
/*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);
parser_finished = false;
//include_labels();
code = mem;
codepos = 2; // [RZC 27/04/2021] [TODO] Perquè 2?
tkn_next();
parse_global_statements();
append_strings(); // [RZC 27/04/2021] [TODO] Encara vull açò? si el bytecode no ha de ser independent, no veig que faça falta
if (error_raised()) { // [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;
}