1
0
Fork 0
undar-lang-register/src/tools/assembler/assembler.c

2478 lines
75 KiB
C

#include "../../vm/common.h"
#include "../../vm/fixed.h"
#include "../../vm/libc.h"
#include "../../vm/opcodes.h"
#include "assembler.h"
#include "lexer.h"
/* FIXME: remove these and replace with libc.h instead */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* FIXME: technically this is not allowed in C89 find another way */
const char *opcode_to_string(Opcode op) {
static const char *names[] = {[OP_EXIT] = "exit",
[OP_JMP] = "jump",
[OP_JMPF] = "jump_if_flag",
[OP_CALL] = "call",
[OP_RETURN] = "return",
[OP_LOAD_IMM] = "load_immediate",
/* Register_indirect loads */
[OP_LOAD_IND_8] = "load_indirect_8",
[OP_LOAD_IND_16] = "load_indirect_16",
[OP_LOAD_IND_32] = "load_indirect_32",
/* Absolute address loads */
[OP_LOAD_ABS_8] = "load_absolute_8",
[OP_LOAD_ABS_16] = "load_absolute_16",
[OP_LOAD_ABS_32] = "load_absolute_32",
/* Base+offset loads */
[OP_LOAD_OFF_8] = "load_offset_8",
[OP_LOAD_OFF_16] = "load_offset_16",
[OP_LOAD_OFF_32] = "load_offset_32",
/* Absolute address stores */
[OP_STORE_ABS_8] = "store_absolute_8",
[OP_STORE_ABS_16] = "store_absolute_16",
[OP_STORE_ABS_32] = "store_absolute_32",
/* Register_indirect stores */
[OP_STORE_IND_8] = "store_indirect_8",
[OP_STORE_IND_16] = "store_indirect_16",
[OP_STORE_IND_32] = "store_indirect_32",
/* Base+offset stores */
[OP_STORE_OFF_8] = "store_offset_8",
[OP_STORE_OFF_16] = "store_offset_16",
[OP_STORE_OFF_32] = "store_offset_32",
/* Memory operations */
[OP_MALLOC] = "malloc",
[OP_MEMSET_8] = "memset_8",
[OP_MEMSET_16] = "memset_16",
[OP_MEMSET_32] = "memset_32",
/* Register operations */
[OP_REG_MOV] = "register_move",
[OP_SYSCALL] = "syscall",
/* Bit operations */
[OP_BIT_SHIFT_LEFT] = "bit_shift_left",
[OP_BIT_SHIFT_RIGHT] = "bit_shift_right",
[OP_BIT_SHIFT_R_EXT] = "bit_shift_re",
[OP_BAND] = "bit_and",
[OP_BOR] = "bit_or",
[OP_BXOR] = "bit_xor",
/* Integer arithmetic */
[OP_ADD_INT] = "add_int",
[OP_SUB_INT] = "sub_int",
[OP_MUL_INT] = "mul_int",
[OP_DIV_INT] = "div_int",
/* Natural number arithmetic */
[OP_ADD_NAT] = "add_nat",
[OP_SUB_NAT] = "sub_nat",
[OP_MUL_NAT] = "mul_nat",
[OP_DIV_NAT] = "div_nat",
/* Floating point operations */
[OP_ADD_REAL] = "add_real",
[OP_SUB_REAL] = "sub_real",
[OP_MUL_REAL] = "mul_real",
[OP_DIV_REAL] = "div_real",
/* Type conversions */
[OP_INT_TO_REAL] = "int_to_real",
[OP_NAT_TO_REAL] = "nat_to_real",
[OP_REAL_TO_INT] = "real_to_int",
[OP_REAL_TO_NAT] = "real_to_nat",
/* Integer comparisons */
[OP_JEQ_INT] = "jump_eq_int",
[OP_JNEQ_INT] = "jump_neq_int",
[OP_JGT_INT] = "jump_gt_int",
[OP_JLT_INT] = "jump_lt_int",
[OP_JLE_INT] = "jump_le_int",
[OP_JGE_INT] = "jump_ge_int",
/* Natural number comparisons */
[OP_JEQ_NAT] = "jump_eq_nat",
[OP_JNEQ_NAT] = "jump_neq_nat",
[OP_JGT_NAT] = "jump_gt_nat",
[OP_JLT_NAT] = "jump_lt_nat",
[OP_JLE_NAT] = "jump_le_nat",
[OP_JGE_NAT] = "jump_ge_nat",
/* Floating point comparisons */
[OP_JEQ_REAL] = "jump_eq_real",
[OP_JNEQ_REAL] = "jump_neq_real",
[OP_JGE_REAL] = "jump_ge_real",
[OP_JGT_REAL] = "jump_gt_real",
[OP_JLT_REAL] = "jump_lt_real",
[OP_JLE_REAL] = "jump_le_real",
/* String operations */
[OP_STRLEN] = "string_length",
[OP_STREQ] = "string_eq",
[OP_STRCAT] = "string_concat",
[OP_STR_GET_CHAR] = "string_get_char",
[OP_STR_FIND_CHAR] = "string_find_char",
[OP_STR_SLICE] = "string_slice",
/* String conversions */
[OP_INT_TO_STRING] = "int_to_string",
[OP_NAT_TO_STRING] = "nat_to_string",
[OP_REAL_TO_STRING] = "real_to_string",
[OP_STRING_TO_INT] = "string_to_int",
[OP_STRING_TO_NAT] = "string_to_nat",
[OP_STRING_TO_REAL] = "string_to_real"};
if (op < 0 || op >= (int)(sizeof(names) / sizeof(names[0]))) {
return "<invalid-opcode>";
}
const char *name = names[op];
return name ? name : "<unknown-opcode>";
}
void emit_op(VM *vm, u8 byte) {
#ifdef DEBUG_PRINT
printf("code[%d] = %s\n", vm->cp, opcode_to_string(byte));
#endif
vm->code[vm->cp] = byte;
}
void emit_byte(VM *vm, u8 byte) {
#ifdef DEBUG_PRINT
printf("code[%d] = %d\n", vm->cp, byte);
#endif
vm->code[vm->cp] = byte;
}
void emit_u32(VM *vm, u32 value) {
#ifdef DEBUG_PRINT
printf("code[%d..%d] = %d\n", vm->cp, vm->cp + 3, value);
#endif
write_u32(vm, code, vm->cp, value);
}
Symbol *symbol_table_lookup(ScopeTable *table, const char *name, u32 length,
i32 scope_ref) {
SymbolTable st = table->scopes[scope_ref];
for (u32 i = 0; i < st.count; i++) {
if (st.symbols[i].name_length == length) {
if (strleq(st.symbols[i].name, name, length)) {
return &table->scopes[scope_ref].symbols[i];
}
}
}
if (st.parent < 0)
return nil;
return symbol_table_lookup(table, name, length, st.parent);
}
u8 symbol_table_add(ScopeTable *table, Symbol s) {
Symbol *sym =
symbol_table_lookup(table, s.name, s.name_length, table->scope_ref);
if (sym != nil) {
fprintf(stderr,
"Error: Symbol '%.*s' already defined, in this scope"
" please pick a different variable name or create a new scope.\n",
s.name_length, s.name);
exit(1);
}
if (table->scopes[table->scope_ref].count + 1 > 255) {
fprintf(stderr, "Error: Only 255 symbols are allowed per scope"
" first off: impressive; secondly:"
" just create a new scope and keep going.\n");
exit(1);
}
if (!table_realloc(table)) {
fprintf(stderr,
"Error: Symbol table is out of memory! This is likely because you "
" built the assembler in static mode, increase the static size."
" if you built using malloc, that means your computer is out of"
" memory. Close a few tabs in your web browser and try again."
" Count was %d, while capacity was %d\n",
table->count, table->capacity);
exit(1);
}
#ifdef DEBUG_PRINT
if (s.scope == VAR) {
printf("$%d = %s\n", s.ref, s.name);
} else if (s.scope == GLOBAL) {
printf("memory[%d] = %s\n", s.ref, s.name);
} else {
printf("code[%d] = %s\n", s.ref, s.name);
}
#endif
table->scopes[table->scope_ref].symbols[table->scopes[table->scope_ref].count] = s;
u8 index = table->scopes[table->scope_ref].count;
table->scopes[table->scope_ref].count++;
return index;
}
u32 get_ref(ScopeTable *st, const char *name, u32 length) {
Symbol *sym = symbol_table_lookup(st, name, length, st->scope_ref);
if (!sym) {
fprintf(stderr, "Error: Assembler has no idea what Symbol '%.*s' means.\n",
length, name);
exit(1);
return 0;
}
return sym->ref;
}
u32 get_ptr(Token token, ScopeTable *st) {
if (token.type == TOKEN_IDENTIFIER) {
return get_ref(st, token.start, token.length);
}
if (token.type == TOKEN_LITERAL_INT) {
return atoi(token.start);
}
if (token.type == TOKEN_LITERAL_NAT) {
char *endptr;
u32 out = (u32)strtoul(token.start, &endptr, 10);
if (endptr == token.start || *endptr != '\0') {
fprintf(stderr, "Invalid decimal literal at line %d: %.*s\n", token.line,
token.length, token.start);
exit(1);
}
return out;
}
fprintf(stderr, "Error: Not a pointer or symbol at line %d: %.*s\n",
token.line, token.length, token.start);
exit(1);
}
u32 get_reg(Token token, ScopeTable *st) {
if (token.type == TOKEN_IDENTIFIER) {
return get_ref(st, token.start, token.length);
}
if (token.type == TOKEN_BIG_MONEY) {
token = next_token();
return atoi(token.start);
}
fprintf(stderr, "Error: Not a register or symbol at line %d: %.*s\n",
token.line, token.length, token.start);
exit(1);
}
Token next_id_or_reg() {
Token token = next_token();
if (token.type == TOKEN_IDENTIFIER) {
return token;
}
if (token.type == TOKEN_BIG_MONEY) {
token = next_token();
return token;
}
printf("Not an ID or register at line %d: %.*s\n", token.line, token.length,
token.start);
exit(1);
return token;
}
Token next_id_or_ptr() {
Token token = next_token();
if (token.type != TOKEN_IDENTIFIER && token.type != TOKEN_LITERAL_NAT &&
token.type != TOKEN_LITERAL_INT && token.type != TOKEN_LITERAL_REAL) {
printf("Not an ID or register at line %d: %.*s\n", token.line, token.length,
token.start);
exit(1);
}
return token;
}
Token next_token_is(TokenType type) {
Token token = next_token();
if (token.type != type) {
printf("ERROR at line %d: %.*s\n", token.line, token.length, token.start);
exit(1);
}
return token;
}
/**
* Global .
*/
bool define_global(VM *vm, ScopeTable *st) {
Symbol s;
Token token_type = next_token();
switch (token_type.type) {
case TOKEN_TYPE_BOOL:
s.type = BOOL;
s.size = 1;
break;
case TOKEN_TYPE_I8:
s.type = I8;
s.size = 1;
break;
case TOKEN_TYPE_U8:
s.type = U8;
s.size = 1;
break;
case TOKEN_TYPE_I16:
s.type = I16;
s.size = 2;
break;
case TOKEN_TYPE_U16:
s.type = U16;
s.size = 2;
break;
case TOKEN_TYPE_INT:
s.type = I32;
s.size = 4;
break;
case TOKEN_TYPE_NAT:
s.type = U32;
s.size = 4;
break;
case TOKEN_TYPE_REAL:
s.type = F32;
s.size = 4;
break;
case TOKEN_TYPE_STR:
s.type = STR;
break;
case TOKEN_IDENTIFIER:
break;
default:
return false;
}
Token name = next_token_is(TOKEN_IDENTIFIER);
if (name.length > MAX_SYMBOL_NAME_LENGTH) {
return false;
}
memcpy(s.name, name.start, name.length);
s.name_length = name.length;
s.name[name.length] = '\0';
u32 addr = vm->mp;
s.ref = addr;
s.scope = GLOBAL;
next_token_is(TOKEN_EQ);
Token value = next_token();
switch (value.type) {
case TOKEN_KEYWORD_TRUE: {
u32 addr = vm->mp;
write_u8(vm, memory, addr, 1);
vm->mp += s.size;
vm->frames[vm->fp].end += s.size;
break;
}
case TOKEN_KEYWORD_FALSE: {
u32 addr = vm->mp;
write_u8(vm, memory, addr, 0);
vm->mp += s.size;
vm->frames[vm->fp].end += s.size;
break;
}
case TOKEN_LITERAL_INT: {
i32 out = atoi(value.start);
u32 addr = vm->mp;
write_u32(vm, memory, addr, out);
vm->mp += s.size;
vm->frames[vm->fp].end += s.size;
break;
}
case TOKEN_LITERAL_NAT: {
char *endptr;
u32 out = (u32)strtoul(value.start, &endptr, 10);
if (endptr == value.start || *endptr != '\0') {
fprintf(stderr, "Invalid decimal literal: %s\n", value.start);
exit(1);
}
u32 addr = vm->mp;
write_u32(vm, memory, addr, out);
vm->mp += s.size;
vm->frames[vm->fp].end += s.size;
break;
}
case TOKEN_LITERAL_REAL: {
fixed_t out = float_to_fixed(atof(value.start));
u32 addr = vm->mp;
write_u32(vm, memory, addr, out);
vm->mp += s.size;
vm->frames[vm->fp].end += s.size;
break;
}
case TOKEN_LITERAL_STR: {
const char *src = value.start;
i32 len = 0;
i32 i = 0;
while (i < value.length) {
char c = src[i++];
if (c == '"') {
continue;
}
if (c == '\\' && i < value.length) {
switch (src[i++]) {
case 'n':
c = '\n';
break;
case 't':
c = '\t';
break;
case 'r':
c = '\r';
break;
case '\\':
case '"':
case '\'':
break;
default:
i--; /* Rewind for unknown escapes */
}
}
write_u8(vm, memory, addr + 4 + len, c);
len++;
}
u32 size = len + 5; /* 4 (len) + dst_len + 1 (null) */
s.size = size;
vm->mp += size;
vm->frames[vm->fp].end += size;
write_u32(vm, memory, addr, len);
write_u8(vm, memory, addr + 4 + len, '\0');
break;
}
default:
return false;
}
next_token_is(TOKEN_SEMICOLON);
symbol_table_add(st, s);
return true;
}
/**
* Var .
*/
void define_var(ScopeTable *st, Token regType) {
Symbol s;
s.scope = VAR;
switch (regType.type) {
case TOKEN_KEYWORD_PLEX: {
s.type = PLEX;
s.size = 4; /* not really this type, pointer alias which is 4 */
break;
}
case TOKEN_TYPE_I8: {
s.type = I8;
s.size = 1;
break;
}
case TOKEN_TYPE_I16: {
s.type = I16;
s.size = 2;
break;
}
case TOKEN_TYPE_INT: {
s.type = I32;
s.size = 4;
break;
}
case TOKEN_TYPE_U8: {
s.type = U8;
s.size = 1;
break;
}
case TOKEN_TYPE_U16: {
s.type = U16;
s.size = 2;
break;
}
case TOKEN_TYPE_NAT: {
s.type = U32;
s.size = 4;
break;
}
case TOKEN_TYPE_REAL: {
s.type = F32;
s.size = 4;
break;
}
case TOKEN_TYPE_BOOL: {
s.type = BOOL;
s.size = 1;
break;
}
case TOKEN_TYPE_STR: {
s.type = STR;
s.size = 4; /* not really this type, pointer alias which is 4 */
break;
}
default:
printf("ERROR at line %d: %.*s\n", regType.line, regType.length,
regType.start);
exit(1);
}
Token name = next_token_is(TOKEN_IDENTIFIER);
if (name.length > MAX_SYMBOL_NAME_LENGTH) {
printf("VARIABLE NAME TOO LONG at line %d: %.*s\n", regType.line,
regType.length, regType.start);
exit(1);
}
memcpy(s.name, name.start, name.length);
s.name[name.length] = '\0';
s.name_length = name.length;
next_token_is(TOKEN_BIG_MONEY);
Token reg_num = next_token_is(TOKEN_LITERAL_INT);
s.ref = atoi(reg_num.start);
symbol_table_add(st, s);
}
/**
* function .
*/
void define_function(VM *vm, ScopeTable *st) {
Symbol s;
s.scope = LOCAL;
s.type = FUNCTION;
Token name = next_token_is(TOKEN_IDENTIFIER);
if (name.length > MAX_SYMBOL_NAME_LENGTH) {
printf("FUNCITON NAME TOO LONG at line %d: %.*s\n", name.line, name.length,
name.start);
exit(1);
}
memcpy(s.name, name.start, name.length);
s.name[name.length] = '\0';
s.name_length = name.length;
next_token_is(TOKEN_LPAREN);
i32 temp = st->scope_ref;
st->count++;
st->scopes[st->count].parent = st->scope_ref;
st->scope_ref = (i32)st->count;
Token next = next_token();
while (next.type != TOKEN_RPAREN) {
define_var(st, next);
next = next_token();
if (next.type == TOKEN_COMMA) {
next = next_token();
continue;
} else if (next.type == TOKEN_RPAREN) {
break;
} else {
printf("ERROR at line %d: %.*s\n", next.line, next.length, next.start);
exit(1);
}
}
s.ref = vm->cp;
next = next_token_is(TOKEN_LBRACE);
st->scope_ref = temp; // need to add to the parents scope
symbol_table_add(st, s);
st->scope_ref = (i32)st->count;
}
/**
* Branch.
*/
void define_branch(VM *vm, ScopeTable *st) {
Symbol s;
s.scope = LOCAL;
s.type = VOID;
Token name = next_token_is(TOKEN_IDENTIFIER);
if (name.length > MAX_SYMBOL_NAME_LENGTH) {
printf("BRANCH NAME TOO LONG at line %d: %.*s\n", name.line, name.length,
name.start);
exit(1);
}
memcpy(s.name, name.start, name.length);
s.name_length = name.length;
s.name[name.length] = '\0';
s.ref = vm->cp;
symbol_table_add(st, s);
}
int get_instruction_byte_size(const char *opname) {
if (strcmp(opname, "return") == 0) {
return 2;
}
if (strcmp(opname, "neg_int") == 0 || strcmp(opname, "abs_int") == 0 ||
strcmp(opname, "neg_nat") == 0 || strcmp(opname, "abs_nat") == 0 ||
strcmp(opname, "neg_real") == 0 || strcmp(opname, "abs_real") == 0 ||
strcmp(opname, "int_to_string") == 0 ||
strcmp(opname, "load_indirect_8") == 0 ||
strcmp(opname, "nat_to_string") == 0 ||
strcmp(opname, "load_indirect_16") == 0 ||
strcmp(opname, "real_to_string") == 0 ||
strcmp(opname, "load_indirect_32") == 0 ||
strcmp(opname, "int_to_real") == 0 ||
strcmp(opname, "store_indirect_8") == 0 ||
strcmp(opname, "nat_to_real") == 0 ||
strcmp(opname, "store_indirect_16") == 0 ||
strcmp(opname, "real_to_int") == 0 ||
strcmp(opname, "store_indirect_32") == 0 ||
strcmp(opname, "real_to_nat") == 0 || strcmp(opname, "nat_to_int") == 0 ||
strcmp(opname, "int_to_nat") == 0 ||
strcmp(opname, "string_length") == 0 || strcmp(opname, "memset") == 0 ||
strcmp(opname, "memset") == 0 || strcmp(opname, "memset_8") == 0 ||
strcmp(opname, "memset_16") == 0 ||
strcmp(opname, "register_move") == 0 || strcmp(opname, "malloc") == 0) {
return 3;
}
if (strcmp(opname, "add_int") == 0 || strcmp(opname, "sub_int") == 0 ||
strcmp(opname, "mul_int") == 0 || strcmp(opname, "div_int") == 0 ||
strcmp(opname, "add_nat") == 0 || strcmp(opname, "sub_nat") == 0 ||
strcmp(opname, "mul_nat") == 0 || strcmp(opname, "div_nat") == 0 ||
strcmp(opname, "add_real") == 0 || strcmp(opname, "sub_real") == 0 ||
strcmp(opname, "bit_shift_left") == 0 ||
strcmp(opname, "bit_shift_right") == 0 ||
strcmp(opname, "bit_shift_r_ext") == 0 ||
strcmp(opname, "bit_and") == 0 || strcmp(opname, "bit_or") == 0 ||
strcmp(opname, "bit_xor") == 0 || strcmp(opname, "mul_real") == 0 ||
strcmp(opname, "div_real") == 0) {
return 4;
}
if (strcmp(opname, "halt") == 0 || strcmp(opname, "jump_if_flag") == 0 ||
strcmp(opname, "jump") == 0) {
return 5;
}
if (strcmp(opname, "load_absolute_32") == 0 ||
strcmp(opname, "load_immediate") == 0 ||
strcmp(opname, "load_address") == 0 ||
strcmp(opname, "load_absolute_16") == 0 ||
strcmp(opname, "load_absolute_8") == 0 ||
strcmp(opname, "store_absolute_32") == 0 ||
strcmp(opname, "store_absolute_8") == 0 ||
strcmp(opname, "store_absolute_16") == 0) {
return 6;
}
if (strcmp(opname, "jump_eq_int") == 0 ||
strcmp(opname, "jump_neq_int") == 0 ||
strcmp(opname, "jump_gt_int") == 0 ||
strcmp(opname, "jump_lt_int") == 0 ||
strcmp(opname, "jump_le_int") == 0 ||
strcmp(opname, "jump_ge_int") == 0 ||
strcmp(opname, "jump_eq_nat") == 0 ||
strcmp(opname, "jump_neq_nat") == 0 ||
strcmp(opname, "jump_gt_nat") == 0 ||
strcmp(opname, "jump_lt_nat") == 0 ||
strcmp(opname, "jump_le_nat") == 0 ||
strcmp(opname, "jump_ge_nat") == 0 ||
strcmp(opname, "jump_eq_real") == 0 ||
strcmp(opname, "jump_neq_real") == 0 ||
strcmp(opname, "jump_gt_real") == 0 ||
strcmp(opname, "jump_lt_real") == 0 ||
strcmp(opname, "jump_le_real") == 0 ||
strcmp(opname, "jump_ge_real") == 0 ||
strcmp(opname, "store_offset_8") == 0 ||
strcmp(opname, "store_offset_16") == 0 ||
strcmp(opname, "store_offset_32") == 0 ||
strcmp(opname, "load_offset_8") == 0 ||
strcmp(opname, "load_offset_16") == 0 ||
strcmp(opname, "load_offset_32") == 0) {
return 7;
}
fprintf(stderr, "Unknown opcode for sizing: %s\n", opname);
exit(-1);
}
#define FAKE_OP(op) \
} \
else if (strleq(token.start, op, token.length)) { \
do { \
while (token.type != TOKEN_SEMICOLON) { \
token = next_token(); \
} \
/*printf("code[%d]=%s\n %d + %d = %d\n", vm->cp, op, \
* get_instruction_byte_size(op), vm->cp, vm->cp + \
* get_instruction_byte_size(op)); */ \
vm->cp += get_instruction_byte_size(op); \
} while (0);
/**
* Build the symbol table and calculate the types/size/offsets of all values.
*/
void build_symbol_table(VM *vm, char *source, ScopeTable *st) {
Token token;
init_lexer(source);
do {
token = next_token();
if (token.type == TOKEN_ERROR) {
printf("ERROR at line %d: %.*s\n", token.line, token.length, token.start);
exit(1);
}
if (token.type != TOKEN_EOF) {
if (token.type == TOKEN_LBRACE) {
st->count++;
st->scopes[st->count].parent = st->scope_ref;
st->scope_ref = (i32)st->count;
continue;
}
if (token.type == TOKEN_RBRACE) {
i32 current_scope = st->scope_ref;
i32 parent = st->scopes[current_scope].parent;
if (parent < 0) parent = 0;
st->scope_ref = parent;
continue;
}
if (token.type == TOKEN_KEYWORD_GLOBAL) {
define_global(vm, st);
continue;
}
if (token.type == TOKEN_KEYWORD_FN) {
define_function(vm, st);
continue;
}
if (token.type == TOKEN_KEYWORD_PLEX || token.type == TOKEN_TYPE_I8 ||
token.type == TOKEN_TYPE_I16 || token.type == TOKEN_TYPE_INT ||
token.type == TOKEN_TYPE_U8 || token.type == TOKEN_TYPE_U16 ||
token.type == TOKEN_TYPE_NAT || token.type == TOKEN_TYPE_REAL ||
token.type == TOKEN_TYPE_STR || token.type == TOKEN_TYPE_BOOL) {
define_var(st, token);
next_token_is(TOKEN_SEMICOLON);
continue;
}
if (token.type == TOKEN_KEYWORD_LOOP || token.type == TOKEN_KEYWORD_IF ||
token.type == TOKEN_KEYWORD_ELSE || token.type == TOKEN_KEYWORD_DO ||
token.type == TOKEN_KEYWORD_FOR) {
define_branch(vm, st);
continue;
}
if (token.type == TOKEN_KEYWORD_RETURN) {
vm->cp++;
Token next = next_token();
if (next.type == TOKEN_SEMICOLON) {
/* put 0xFF as return register */
vm->cp++;
continue;
}
get_reg(next, st);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
continue;
}
#ifdef DEBUG_PRINT
printf("-- %.*s --\n", token.length, token.start);
#endif
if (token.type == TOKEN_IDENTIFIER) {
/* check to see if it is an opcode first */
if (strleq(token.start, "exit", token.length)) {
vm->cp++;
next_token();
vm->cp += 4;
#ifdef DEBUG_PRINT
printf("code[%d] = exit\n", vm->cp);
#endif
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "call", token.length)) {
vm->cp++;
next_token_is(TOKEN_IDENTIFIER);
vm->cp += 4;
vm->cp++;
Token next = next_token_is(TOKEN_LPAREN);
next = next_token();
while (next.type != TOKEN_RPAREN) {
get_reg(next, st);
vm->cp++;
next = next_token();
}
next = next_token();
if (next.type == TOKEN_SEMICOLON) {
vm->cp++;
} else {
next = next_token();
get_reg(next, st);
vm->cp++;
}
#ifdef DEBUG_PRINT
printf("code[%d] = call\n", vm->cp);
#endif
continue;
} else if (strleq(token.start, "syscall", token.length)) {
vm->cp++;
Token next = next_token();
vm->cp += 4;
next = next_token();
while (next.type != TOKEN_SEMICOLON) {
get_reg(next, st);
vm->cp++;
next = next_token();
}
#ifdef DEBUG_PRINT
printf("code[%d] = syscall\n", vm->cp);
#endif
continue;
FAKE_OP("load_immediate")
FAKE_OP("load_address")
FAKE_OP("malloc")
FAKE_OP("memset_8")
FAKE_OP("memset_16")
FAKE_OP("memset_32")
FAKE_OP("load_offset_8")
FAKE_OP("load_offset_16")
FAKE_OP("load_offset_32")
FAKE_OP("load_indirect_8")
FAKE_OP("load_indirect_16")
FAKE_OP("load_indirect_32")
FAKE_OP("load_absolute_8")
FAKE_OP("load_absolute_16")
FAKE_OP("load_absolute_32")
FAKE_OP("store_absolute_8")
FAKE_OP("store_absolute_16")
FAKE_OP("store_absolute_32")
FAKE_OP("store_indirect_8")
FAKE_OP("store_indirect_16")
FAKE_OP("store_indirect_32")
FAKE_OP("store_offset_8")
FAKE_OP("store_offset_16")
FAKE_OP("store_offset_32")
FAKE_OP("register_move")
FAKE_OP("add_int")
FAKE_OP("sub_int")
FAKE_OP("mul_int")
FAKE_OP("div_int")
FAKE_OP("abs_int")
FAKE_OP("neg_int")
FAKE_OP("add_nat")
FAKE_OP("sub_nat")
FAKE_OP("mul_nat")
FAKE_OP("div_nat")
FAKE_OP("abs_nat")
FAKE_OP("neg_nat")
FAKE_OP("add_real")
FAKE_OP("sub_real")
FAKE_OP("mul_real")
FAKE_OP("div_real")
FAKE_OP("abs_real")
FAKE_OP("neg_real")
FAKE_OP("int_to_real")
FAKE_OP("nat_to_real")
FAKE_OP("real_to_int")
FAKE_OP("real_to_nat")
FAKE_OP("bit_shift_left")
FAKE_OP("bit_shift_right")
FAKE_OP("bit_shift_r_ext")
FAKE_OP("bit_and")
FAKE_OP("bit_or")
FAKE_OP("bit_xor")
FAKE_OP("jump")
FAKE_OP("jump_if_flag")
FAKE_OP("jump_eq_int")
FAKE_OP("jump_neq_int")
FAKE_OP("jump_gt_int")
FAKE_OP("jump_lt_int")
FAKE_OP("jump_le_int")
FAKE_OP("jump_ge_int")
FAKE_OP("jump_eq_nat")
FAKE_OP("jump_neq_nat")
FAKE_OP("jump_gt_nat")
FAKE_OP("jump_lt_nat")
FAKE_OP("jump_le_nat")
FAKE_OP("jump_ge_nat")
FAKE_OP("jump_eq_real")
FAKE_OP("jump_neq_real")
FAKE_OP("jump_ge_real")
FAKE_OP("jump_gt_real")
FAKE_OP("jump_lt_real")
FAKE_OP("jump_le_real")
FAKE_OP("string_length")
FAKE_OP("int_to_string")
FAKE_OP("nat_to_string")
FAKE_OP("real_to_string")
FAKE_OP("string_eq")
FAKE_OP("string_concat")
FAKE_OP("string_get_char")
FAKE_OP("string_find_char")
FAKE_OP("string_slice")
FAKE_OP("string_to_int")
FAKE_OP("string_to_nat")
FAKE_OP("string_to_real")
} else {
/* some other identifier */
printf("Unknown id at line %d: %.*s\n", token.line, token.length,
token.start);
exit(1);
}
}
}
} while (token.type != TOKEN_EOF);
}
/**
* 2nd pass, emit the bytecode
*/
void emit_bytecode(VM *vm, char *source, ScopeTable *st) {
Token token;
init_lexer(source);
do {
token = next_token();
if (token.type == TOKEN_ERROR) {
printf("ERROR at line %d: %.*s\n", token.line, token.length, token.start);
break;
}
if (token.type != TOKEN_EOF) {
if (token.type == TOKEN_LBRACE) {
st->count++;
st->scopes[st->count].parent = st->scope_ref;
st->scope_ref = (i32)st->count;
continue;
}
if (token.type == TOKEN_RBRACE) {
i32 current_scope = st->scope_ref;
i32 parent = st->scopes[current_scope].parent;
if (parent < 0) parent = 0;
st->scope_ref = parent;
continue;
}
if (token.type == TOKEN_KEYWORD_GLOBAL) {
/* ignore, already processed */
next_token(); /* type */
next_token(); /* var */
next_token(); /* eq */
next_token(); /* value */
next_token(); /* ; */
continue;
}
if (token.type == TOKEN_KEYWORD_FN) {
/* ignore, already processed */
Token next = next_token();
while (next.type != TOKEN_RPAREN) {
next = next_token();
}
continue;
}
if (token.type == TOKEN_KEYWORD_PLEX || token.type == TOKEN_TYPE_I8 ||
token.type == TOKEN_TYPE_I16 || token.type == TOKEN_TYPE_INT ||
token.type == TOKEN_TYPE_U8 || token.type == TOKEN_TYPE_U16 ||
token.type == TOKEN_TYPE_NAT || token.type == TOKEN_TYPE_REAL ||
token.type == TOKEN_TYPE_STR) {
/* ignore, already processed */
next_token(); /* type */
next_token(); /* var */
next_token(); /* reg */
next_token(); /* ; */
continue;
}
if (token.type == TOKEN_KEYWORD_LOOP || token.type == TOKEN_KEYWORD_IF ||
token.type == TOKEN_KEYWORD_ELSE || token.type == TOKEN_KEYWORD_DO ||
token.type == TOKEN_KEYWORD_FOR) {
/* ignore, already processed */
next_token(); /* id */
}
if (token.type == TOKEN_KEYWORD_RETURN) {
emit_op(vm, OP_RETURN);
vm->cp++;
Token next = next_token();
if (next.type == TOKEN_SEMICOLON) {
/* put 0xFF as return register */
emit_byte(vm, 0xFF);
vm->cp++;
continue;
}
u32 reg = get_reg(next, st);
emit_byte(vm, reg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
continue;
}
#ifdef DEBUG_PRINT
printf("-- %.*s --\n", token.length, token.start);
#endif
if (token.type == TOKEN_IDENTIFIER) {
/* check to see if it is an opcode first */
if (strleq(token.start, "exit", token.length)) {
emit_op(vm, OP_EXIT);
vm->cp++;
Token next = next_token();
u32 ptr = get_ptr(next, st);
emit_u32(vm, ptr);
vm->cp += 4;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "call", token.length)) {
emit_op(vm, OP_CALL);
vm->cp++;
Token id = next_token_is(TOKEN_IDENTIFIER);
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
u8 arg_count = 0;
u32 arg_pos = vm->cp++;
Token next = next_token_is(TOKEN_LPAREN);
next = next_token();
while (next.type != TOKEN_RPAREN) {
u8 arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
arg_count++;
next = next_token();
}
vm->code[arg_pos] = arg_count;
#ifdef DEBUG_PRINT
printf("^code[%d] = %d\n", arg_pos, arg_count);
#endif
next = next_token();
if (next.type == TOKEN_SEMICOLON) {
emit_byte(vm, 255);
vm->cp++;
} else {
next = next_token();
u8 arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
}
continue;
} else if (strleq(token.start, "syscall", token.length)) {
emit_op(vm, OP_SYSCALL);
vm->cp++;
Token next = next_token();
u32 syscall_id = 0;
const char *syscall_name = next.start;
if (strleq(syscall_name, "EXIT", next.length))
syscall_id = SYSCALL_EXIT;
else if (strleq(syscall_name, "OPEN", next.length))
syscall_id = SYSCALL_DEVICE_OPEN;
else if (strleq(syscall_name, "READ", next.length))
syscall_id = SYSCALL_DEVICE_READ;
else if (strleq(syscall_name, "WRITE", next.length))
syscall_id = SYSCALL_DEVICE_WRITE;
else if (strleq(syscall_name, "CLOSE", next.length))
syscall_id = SYSCALL_DEVICE_CLOSE;
else if (strleq(syscall_name, "IOCTL", next.length))
syscall_id = SYSCALL_DEVICE_IOCTL;
else if (strleq(syscall_name, "REFRESH", next.length))
syscall_id = SYSCALL_DEVICE_REFRESH;
emit_u32(vm, syscall_id);
vm->cp += 4;
next = next_token();
while (next.type != TOKEN_SEMICOLON &&
next.type != TOKEN_ARROW_RIGHT) {
u8 arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next = next_token();
}
if (next.type == TOKEN_ARROW_RIGHT) {
next = next_token();
u8 arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
}
} else if (strleq(token.start, "load_immediate", token.length)) {
emit_op(vm, OP_LOAD_IMM);
vm->cp++;
Token value = next_token();
switch (value.type) {
case TOKEN_KEYWORD_TRUE: {
emit_u32(vm, 1);
break;
}
case TOKEN_KEYWORD_FALSE: {
emit_u32(vm, 0);
break;
}
case TOKEN_LITERAL_INT: {
i32 out = atoi(value.start);
emit_u32(vm, out);
break;
}
case TOKEN_LITERAL_NAT: {
char *endptr;
u32 out = (u32)strtoul(value.start, &endptr, 10);
if (endptr == value.start || *endptr != '\0') {
fprintf(stderr, "Invalid 'real' number: '%.*s'\n", token.length,
token.start);
exit(1);
}
emit_u32(vm, out);
break;
}
case TOKEN_LITERAL_REAL: {
fixed_t out = float_to_fixed(atof(value.start));
emit_u32(vm, out);
break;
}
default: {
fprintf(stderr, "Unknown immediate: '%.*s'\n", token.length,
token.start);
exit(1);
}
}
vm->cp += 4;
next_token_is(TOKEN_ARROW_RIGHT);
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "load_address", token.length)) {
emit_op(vm, OP_LOAD_IMM);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
next_token_is(TOKEN_ARROW_RIGHT);
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "malloc", token.length)) {
emit_op(vm, OP_MALLOC);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "memset_8", token.length)) {
emit_op(vm, OP_MEMSET_8);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "memset_16", token.length)) {
emit_op(vm, OP_MEMSET_16);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "memset_32", token.length)) {
emit_op(vm, OP_MEMSET_32);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "load_offset_8", token.length)) {
emit_op(vm, OP_LOAD_OFF_8);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "load_offset_16", token.length)) {
emit_op(vm, OP_LOAD_OFF_16);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "load_offset_32", token.length)) {
emit_op(vm, OP_LOAD_OFF_32);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "load_indirect_8", token.length)) {
emit_op(vm, OP_LOAD_IND_8);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
next_token_is(TOKEN_ARROW_RIGHT);
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "load_indirect_16", token.length)) {
emit_op(vm, OP_LOAD_IND_16);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
next_token_is(TOKEN_ARROW_RIGHT);
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "load_indirect_32", token.length)) {
emit_op(vm, OP_LOAD_IND_32);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
next_token_is(TOKEN_ARROW_RIGHT);
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "load_absolute_8", token.length)) {
emit_op(vm, OP_LOAD_ABS_8);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
next_token_is(TOKEN_ARROW_RIGHT);
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "load_absolute_16", token.length)) {
emit_op(vm, OP_LOAD_ABS_16);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
next_token_is(TOKEN_ARROW_RIGHT);
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "load_absolute_32", token.length)) {
emit_op(vm, OP_LOAD_ABS_32);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
next_token_is(TOKEN_ARROW_RIGHT);
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "store_absolute_8", token.length)) {
emit_op(vm, OP_STORE_ABS_8);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "store_absolute_16", token.length)) {
emit_op(vm, OP_STORE_ABS_16);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "store_absolute_32", token.length)) {
emit_op(vm, OP_STORE_ABS_32);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "store_indirect_8", token.length)) {
emit_op(vm, OP_STORE_IND_8);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "store_indirect_16", token.length)) {
emit_op(vm, OP_STORE_IND_16);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "store_indirect_32", token.length)) {
emit_op(vm, OP_STORE_IND_32);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "store_offset_8", token.length)) {
emit_op(vm, OP_STORE_OFF_8);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "store_offset_16", token.length)) {
emit_op(vm, OP_STORE_OFF_16);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "store_offset_32", token.length)) {
emit_op(vm, OP_STORE_OFF_32);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "register_move", token.length)) {
emit_op(vm, OP_REG_MOV);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "add_int", token.length)) {
emit_op(vm, OP_ADD_INT);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "sub_int", token.length)) {
emit_op(vm, OP_SUB_INT);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "mul_int", token.length)) {
emit_op(vm, OP_MUL_INT);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "div_int", token.length)) {
emit_op(vm, OP_DIV_INT);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "abs_int", token.length)) {
emit_op(vm, OP_ABS_INT);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "neg_int", token.length)) {
emit_op(vm, OP_NEG_INT);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "add_nat", token.length)) {
emit_op(vm, OP_ADD_NAT);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "sub_nat", token.length)) {
emit_op(vm, OP_SUB_NAT);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "mul_nat", token.length)) {
emit_op(vm, OP_MUL_NAT);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "div_nat", token.length)) {
emit_op(vm, OP_DIV_NAT);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "abs_nat", token.length)) {
emit_op(vm, OP_ABS_NAT);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "neg_nat", token.length)) {
emit_op(vm, OP_NEG_NAT);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "add_real", token.length)) {
emit_op(vm, OP_ADD_REAL);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
;
} else if (strleq(token.start, "sub_real", token.length)) {
emit_op(vm, OP_SUB_REAL);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "mul_real", token.length)) {
emit_op(vm, OP_MUL_REAL);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "div_real", token.length)) {
emit_op(vm, OP_DIV_REAL);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "abs_real", token.length)) {
emit_op(vm, OP_ABS_REAL);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "neg_real", token.length)) {
emit_op(vm, OP_NEG_REAL);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "int_to_real", token.length)) {
emit_op(vm, OP_INT_TO_REAL);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "nat_to_real", token.length)) {
emit_op(vm, OP_NAT_TO_REAL);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "real_to_int", token.length)) {
emit_op(vm, OP_REAL_TO_INT);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "real_to_nat", token.length)) {
emit_op(vm, OP_REAL_TO_NAT);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "bit_shift_left", token.length)) {
emit_op(vm, OP_BIT_SHIFT_LEFT);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "bit_shift_right", token.length)) {
emit_op(vm, OP_BIT_SHIFT_RIGHT);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "bit_shift_r_ext", token.length)) {
emit_op(vm, OP_BIT_SHIFT_R_EXT);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "bit_and", token.length)) {
emit_op(vm, OP_BAND);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "bit_or", token.length)) {
emit_op(vm, OP_BOR);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "bit_xor", token.length)) {
emit_op(vm, OP_BXOR);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
Token next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
next = next_token();
arg = get_reg(next, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump", token.length)) {
emit_op(vm, OP_JMP);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump_if_flag", token.length)) {
emit_op(vm, OP_JMPF);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump_eq_int", token.length)) {
emit_op(vm, OP_JEQ_INT);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump_neq_int", token.length)) {
emit_op(vm, OP_JNEQ_INT);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump_gt_int", token.length)) {
emit_op(vm, OP_JGT_INT);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump_lt_int", token.length)) {
emit_op(vm, OP_JLT_INT);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump_le_int", token.length)) {
emit_op(vm, OP_JLE_INT);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump_ge_int", token.length)) {
emit_op(vm, OP_JGE_INT);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump_eq_nat", token.length)) {
emit_op(vm, OP_JEQ_NAT);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump_neq_nat", token.length)) {
emit_op(vm, OP_JNEQ_NAT);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump_gt_nat", token.length)) {
emit_op(vm, OP_JGT_NAT);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump_lt_nat", token.length)) {
emit_op(vm, OP_JLT_NAT);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump_le_nat", token.length)) {
emit_op(vm, OP_JLE_NAT);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump_ge_nat", token.length)) {
emit_op(vm, OP_JGE_NAT);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump_eq_real", token.length)) {
emit_op(vm, OP_JEQ_REAL);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump_neq_real", token.length)) {
emit_op(vm, OP_JNEQ_REAL);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump_ge_real", token.length)) {
emit_op(vm, OP_JGE_REAL);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump_gt_real", token.length)) {
emit_op(vm, OP_JGT_REAL);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump_lt_real", token.length)) {
emit_op(vm, OP_JLT_REAL);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "jump_le_real", token.length)) {
emit_op(vm, OP_JLE_REAL);
vm->cp++;
Token id = next_token();
u32 ptr = get_ptr(id, st);
emit_u32(vm, ptr);
vm->cp += 4;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "string_length", token.length)) {
emit_op(vm, OP_STRLEN);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "int_to_string", token.length)) {
emit_op(vm, OP_INT_TO_STRING);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "nat_to_string", token.length)) {
emit_op(vm, OP_NAT_TO_STRING);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
} else if (strleq(token.start, "real_to_string", token.length)) {
emit_op(vm, OP_REAL_TO_STRING);
vm->cp++;
Token reg = next_token();
u8 arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_ARROW_RIGHT);
reg = next_token();
arg = get_reg(reg, st);
emit_byte(vm, arg);
vm->cp++;
next_token_is(TOKEN_SEMICOLON);
;
} else if (strleq(token.start, "string_eq", token.length)) {
} else if (strleq(token.start, "string_concat", token.length)) {
} else if (strleq(token.start, "string_get_char", token.length)) {
} else if (strleq(token.start, "string_find_char", token.length)) {
} else if (strleq(token.start, "string_slice", token.length)) {
} else if (strleq(token.start, "string_to_int", token.length)) {
} else if (strleq(token.start, "string_to_nat", token.length)) {
} else if (strleq(token.start, "string_to_real", token.length)) {
} else {
/* some other identifier */
printf("Unknown id at line %d: %.*s\n", token.line, token.length,
token.start);
exit(1);
}
}
}
} while (token.type != TOKEN_EOF);
}
/**
* Emit bytecode to the VM from the source string.
*/
void assemble(VM *vm, ScopeTable *st, char *source) {
build_symbol_table(vm, source, st);
vm->cp = 0; /* actually start emitting code */
st->count = 0;
emit_bytecode(vm, source, st);
}