Compare commits
1 Commits
main
...
experiemen
| Author | SHA1 | Date |
|---|---|---|
|
|
c961edeb00 |
|
|
@ -1,65 +1,50 @@
|
|||
#include "../../../vm/vm.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#define FRAMES_SIZE 128
|
||||
#define CODE_SIZE 8192
|
||||
#define MEMORY_SIZE 65536
|
||||
u8 lmem[MEMORY_SIZE] = {0};
|
||||
u32 lcode[CODE_SIZE] = {0};
|
||||
Frame lframes[FRAMES_SIZE] = {0};
|
||||
|
||||
bool init_vm() {
|
||||
mem = lmem;
|
||||
code = lcode;
|
||||
lc = 0;
|
||||
mp = 0;
|
||||
cp = 0;
|
||||
pc = 0;
|
||||
interrupt = 0;
|
||||
status = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 syscall(u32 id, u32 device, u32 mem_ptr) {
|
||||
USED(device);
|
||||
u32 syscall(u32 id, u32 size, u32 mem_ptr) {
|
||||
USED(size);
|
||||
switch(id) {
|
||||
case SYSCALL_CONSOLE_WRITE: {
|
||||
u32 size = *(u32*)&mem[mem_ptr];
|
||||
u8 *ptr = &mem[mem_ptr + 4];
|
||||
u32 size = *(u32 *)(mem + mem_ptr);
|
||||
u32 ptr = mem_ptr + 4;
|
||||
for (u32 i = 0; i < size; i++) {
|
||||
putchar(*(ptr++));
|
||||
putchar(mem[ptr + i]);
|
||||
}
|
||||
putchar('\n');
|
||||
return 0;
|
||||
}
|
||||
case SYSCALL_CONSOLE_READ: {
|
||||
u32 size;
|
||||
u8 *ptr = &mem[mp];
|
||||
mcpy(ptr, &size, sizeof(u32));
|
||||
ptr += 4;
|
||||
for (u32 i = 0; i < size; i++) {
|
||||
u8 ch = getchar();
|
||||
if (ch == '\0')
|
||||
break;
|
||||
if (ch == '\n')
|
||||
break;
|
||||
*(ptr++) = ch;
|
||||
}
|
||||
ptr[size] = '\0';
|
||||
mp += 4 + size + 1;
|
||||
mem[mem_ptr + i] = ch;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
return 1; // generic error
|
||||
}
|
||||
|
||||
void test_add_two_num() {
|
||||
i32 main_local_count = 5;
|
||||
i32 main_local_count = 4;
|
||||
mp += (4 * main_local_count);
|
||||
code[cp++] = ENCODE_B(OP_LOAD_IMM, 0, 1);
|
||||
code[cp++] = ENCODE_B(OP_PARG, 0, 0);
|
||||
code[cp++] = ENCODE_B(OP_PUSH, 0, 0);
|
||||
code[cp++] = ENCODE_B(OP_LOAD_IMM, 1, 1);
|
||||
code[cp++] = ENCODE_B(OP_PARG, 1, 0);
|
||||
code[cp++] = ENCODE_B(OP_PUSH, 1, 0);
|
||||
i32 add = cp + 5;
|
||||
code[cp++] = ENCODE_B(OP_LOAD_IMM, 2, add);
|
||||
|
||||
code[cp++] = ENCODE_A(OP_CALL, 2, 3, 0);
|
||||
code[cp++] = ENCODE_A(OP_INT_TO_STR, 4, 3, 0);
|
||||
code[cp++] = ENCODE_A(OP_SYSCALL, SYSCALL_CONSOLE_WRITE, 1, 4);
|
||||
|
|
@ -71,55 +56,49 @@ void test_add_two_num() {
|
|||
}
|
||||
|
||||
void test_fibonacci() {
|
||||
/* function main() */
|
||||
i32 fib = 7;
|
||||
i32 base_case = 21;
|
||||
/* function main() */
|
||||
i32 main_local_count = 4;
|
||||
mp += (4 * main_local_count);
|
||||
/* fib(35) */
|
||||
code[cp++] = ENCODE_B(OP_LOAD_IMM, 0, 35);
|
||||
code[cp++] = ENCODE_B(OP_PARG, 0, 0);
|
||||
code[cp++] = ENCODE_B(OP_LOAD_IMM, 1, fib);
|
||||
code[cp++] = ENCODE_A(OP_CALL, 1, 9, 2);
|
||||
/* print */
|
||||
code[cp++] = ENCODE_A(OP_INT_TO_STR, 3, 2, 0);
|
||||
code[cp++] = ENCODE_A(OP_SYSCALL, SYSCALL_CONSOLE_WRITE, 1, 3);
|
||||
code[cp++] = ENCODE_A(OP_HALT, 0, 0, 0);
|
||||
/* function fib (int n) int */
|
||||
code[cp++] = ENCODE_B(OP_LOAD_IMM, 8, fib);
|
||||
code[cp++] = ENCODE_B(OP_LOAD_IMM, 1, 2);
|
||||
code[cp++] = ENCODE_B(OP_LOAD_IMM, 2, base_case);
|
||||
code[cp++] = ENCODE_A(OP_JLTS, 2, 0, 1);
|
||||
code[cp++] = ENCODE_B(OP_LOAD_IMM, 3, 2);
|
||||
code[cp++] = ENCODE_A(OP_SUB_INT, 4, 0, 3);
|
||||
code[cp++] = ENCODE_B(OP_PARG, 4, 0);
|
||||
code[cp++] = ENCODE_A(OP_CALL, 8, 9, 5);
|
||||
code[cp++] = ENCODE_B(OP_LOAD_IMM, 3, 1);
|
||||
code[cp++] = ENCODE_A(OP_SUB_INT, 4, 0, 3);
|
||||
code[cp++] = ENCODE_B(OP_PARG, 4, 0);
|
||||
code[cp++] = ENCODE_A(OP_CALL, 8, 9, 6);
|
||||
code[cp++] = ENCODE_A(OP_ADD_INT, 7, 6, 5);
|
||||
code[cp++] = ENCODE_B(OP_RETURN, 7, 0);
|
||||
code[cp++] = ENCODE_B(OP_RETURN, 0, 0);
|
||||
}
|
||||
|
||||
void test_hello() {
|
||||
u32 hello =str_alloc("nuqneH 'u'?", 12);
|
||||
u32 new_line = str_alloc("\n", 1);
|
||||
fp = mp;
|
||||
/* function main() */
|
||||
i32 main_local_count = 3;
|
||||
mp += (4 * main_local_count);
|
||||
code[cp++] = ENCODE_B(OP_LOAD_IMM, 0, hello);
|
||||
code[cp++] = ENCODE_A(OP_SYSCALL, SYSCALL_CONSOLE_WRITE, 0, 0);
|
||||
code[cp++] = ENCODE_B(OP_LOAD_IMM, 0, new_line);
|
||||
code[cp++] = ENCODE_A(OP_SYSCALL, SYSCALL_CONSOLE_WRITE, 0, 0);
|
||||
code[cp++] = ENCODE_A(OP_HALT, 0, 0, 0);
|
||||
/*0*/ code[cp++] = ENCODE_B(OP_LOAD_IMM, 0, 35);
|
||||
/*1*/ code[cp++] = ENCODE_B(OP_PUSH, 0, 0);
|
||||
/*2*/ code[cp++] = ENCODE_B(OP_LOAD_IMM, 1, fib);
|
||||
/*3*/ code[cp++] = ENCODE_A(OP_CALL, 1, 2, 0);
|
||||
/**/ /* print */
|
||||
/*4*/ code[cp++] = ENCODE_A(OP_INT_TO_STR, 3, 2, 0);
|
||||
/*5*/ code[cp++] = ENCODE_A(OP_SYSCALL, SYSCALL_CONSOLE_WRITE, 1, 3);
|
||||
/*6*/ code[cp++] = ENCODE_A(OP_HALT, 0, 0, 0);
|
||||
/**/ /* function fib (int n) int */
|
||||
/*9*/ code[cp++] = ENCODE_B(OP_LOAD_IMM, 8, fib);
|
||||
/*10*/ code[cp++] = ENCODE_B(OP_LOAD_IMM, 1, 2);
|
||||
/*11*/ code[cp++] = ENCODE_B(OP_LOAD_IMM, 2, base_case);
|
||||
/*12*/ code[cp++] = ENCODE_A(OP_JLT_INT, 2, 0, 1);
|
||||
/*13*/ code[cp++] = ENCODE_B(OP_LOAD_IMM, 3, 1);
|
||||
/*14*/ code[cp++] = ENCODE_A(OP_SUB_INT, 4, 0, 3);
|
||||
/*15*/ code[cp++] = ENCODE_B(OP_PUSH, 4, 0);
|
||||
/*16*/ code[cp++] = ENCODE_A(OP_CALL, 8, 5, 0);
|
||||
/*17*/ code[cp++] = ENCODE_B(OP_LOAD_IMM, 3, 2);
|
||||
/*18*/ code[cp++] = ENCODE_A(OP_SUB_INT, 4, 0, 3);
|
||||
/*19*/ code[cp++] = ENCODE_B(OP_PUSH, 4, 0);
|
||||
/*20*/ code[cp++] = ENCODE_A(OP_CALL, 8, 6, 0);
|
||||
/*21*/ code[cp++] = ENCODE_A(OP_ADD_INT, 7, 6, 5);
|
||||
/*22*/ code[cp++] = ENCODE_B(OP_RETURN, 7, 0);
|
||||
/*23*/ code[cp++] = ENCODE_B(OP_RETURN, 0, 0);
|
||||
}
|
||||
|
||||
i32 main() {
|
||||
init_vm();
|
||||
mem = lmem;
|
||||
code = lcode;
|
||||
frames = lframes;
|
||||
lc = 0;
|
||||
mp = 0;
|
||||
cp = 0;
|
||||
pc = 0;
|
||||
fp = 0;
|
||||
interrupt = 0;
|
||||
status = 0;
|
||||
|
||||
/* test_add_two_num(); */
|
||||
test_fibonacci();
|
||||
|
||||
while(step_vm()) {
|
||||
|
|
@ -127,3 +106,5 @@ i32 main() {
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
4
build
4
build
|
|
@ -66,10 +66,10 @@ fi
|
|||
# setup the build flags based on the build mode
|
||||
case $MODE in
|
||||
"debug")
|
||||
BUILD_FLAGS="-g -Wall -Wextra -Werror -pedantic"
|
||||
BUILD_FLAGS="-g -pg -Wall -Wextra -Werror -pedantic"
|
||||
;;
|
||||
"release")
|
||||
BUILD_FLAGS="-Ofast -Wall -Wextra -Werror -pedantic"
|
||||
BUILD_FLAGS="-O2 -Wall -Wextra -Werror -pedantic"
|
||||
;;
|
||||
esac
|
||||
|
||||
|
|
|
|||
29
test/add.ul
29
test/add.ul
|
|
@ -1,29 +0,0 @@
|
|||
/**
|
||||
* Plexes
|
||||
*/
|
||||
plex Terminal {
|
||||
nat handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function
|
||||
*/
|
||||
function main() {
|
||||
pln(add(1, 1) as str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add two numbers together
|
||||
*/
|
||||
function add(int a, int b) int {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print with a newline
|
||||
*/
|
||||
function pln(str message) {
|
||||
Terminal term = open("term:///0", 0);
|
||||
term.write(message);
|
||||
term.write("\n");
|
||||
}
|
||||
30
test/fib.ul
30
test/fib.ul
|
|
@ -1,30 +0,0 @@
|
|||
/**
|
||||
* Plexes
|
||||
*/
|
||||
plex Terminal {
|
||||
nat handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function
|
||||
*/
|
||||
function main() {
|
||||
pln(fib(35) as str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively calculate fibonacci
|
||||
*/
|
||||
function fib(int n) int {
|
||||
if (n < 2) { return n; }
|
||||
return fib(n - 2) + fib(n - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print with a newline
|
||||
*/
|
||||
function pln(str message) {
|
||||
Terminal term = open("term:///0", 0);
|
||||
term.write(message);
|
||||
term.write("\n");
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
/**
|
||||
* Plexes
|
||||
*/
|
||||
plex Terminal {
|
||||
nat handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function
|
||||
*/
|
||||
function main() {
|
||||
pln("nuqneH 'u'?");
|
||||
}
|
||||
|
||||
/**
|
||||
* Print with a newline
|
||||
*/
|
||||
function pln(str message) {
|
||||
Terminal term = open("term:///0", 0);
|
||||
term.write(message);
|
||||
term.write("\n");
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/**
|
||||
* Plexes
|
||||
*/
|
||||
plex Terminal {
|
||||
nat handle;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Main function
|
||||
*/
|
||||
function main() {
|
||||
Terminal term = open("term:///0", 0);
|
||||
pln("Enter a string: ");
|
||||
pln(term.read(32) as str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print with a newline
|
||||
*/
|
||||
function pln(str message) {
|
||||
Terminal term = open("term:///0", 0);
|
||||
term.write(message);
|
||||
term.write("\n");
|
||||
}
|
||||
108
test/paint.ul
108
test/paint.ul
|
|
@ -1,108 +0,0 @@
|
|||
/**
|
||||
* Constants
|
||||
*/
|
||||
const byte BLACK = 0;
|
||||
const byte WHITE = 255;
|
||||
const byte DARK_GRAY = 73;
|
||||
const byte GRAY = 146;
|
||||
const byte LIGHT_GRAY = 182;
|
||||
|
||||
byte selected_color = 255;
|
||||
|
||||
trait Device {
|
||||
nat handle;
|
||||
}
|
||||
|
||||
plex Screen implements Device {
|
||||
nat handle;
|
||||
nat width;
|
||||
nat height;
|
||||
byte[] buffer;
|
||||
|
||||
draw() {
|
||||
this.write(this.buffer);
|
||||
}
|
||||
}
|
||||
|
||||
plex Mouse implements Device {
|
||||
nat handle;
|
||||
nat x;
|
||||
nat y;
|
||||
bool left;
|
||||
bool right;
|
||||
bool middle;
|
||||
bool btn4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function
|
||||
*/
|
||||
function main() {
|
||||
Screen screen = open("screen:///0", 0);
|
||||
Mouse mouse = open("mouse:///0", 0);
|
||||
|
||||
outline_swatch(screen, BLACK, 1, 1);
|
||||
outline_swatch(screen, WHITE, 21, 1);
|
||||
screen.draw();
|
||||
|
||||
loop {
|
||||
if (!mouse.left) continue;
|
||||
|
||||
int box_size = 20;
|
||||
int x = 1;
|
||||
int y = 1;
|
||||
byte color = BLACK;
|
||||
outlined_swatch(screen, color, x, y);
|
||||
set_color(box_size, x, y, mouse.x, mouse.y, color);
|
||||
|
||||
color = WHITE;
|
||||
x = 21;
|
||||
outlined_swatch(screen, color, x, y);
|
||||
set_color(box_size, x, y, mouse.x, mouse.y, color);
|
||||
screen.draw();
|
||||
|
||||
rectangle(screen, selected_color, x, y, 5, 5);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the click is within the bound and update the selected color if so.
|
||||
*/
|
||||
function set_color(int box_size, int bx, int by, int mx, int my, byte color) {
|
||||
int right = bx + box_size;
|
||||
int bottom = by + box_size;
|
||||
|
||||
if (mx < bx) return;
|
||||
if (mx > right) return;
|
||||
if (my < by) return;
|
||||
if (my > bottom) return;
|
||||
|
||||
selected_color = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a color box with a grey outline, if selected use a darker color
|
||||
*/
|
||||
function outline_swatch(Device screen, byte color, int x, int y) {
|
||||
byte bg_color = GRAY;
|
||||
if (selected_color == color) {
|
||||
bg_color = DARK_GRAY;
|
||||
}
|
||||
|
||||
rectangle(screen, bg_color, x, y, 20, 20);
|
||||
rectangle(screen, color, x + 2, y + 2, 17, 17);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a rectangle
|
||||
*/
|
||||
function rectangle(Device screen, byte color, int x, int y, int width, int height) {
|
||||
int base = y * screen.width + x + screen.buffer.ptr + 4;
|
||||
|
||||
do (int i = height; i > 0; i--) {
|
||||
int row = base + width;
|
||||
memset(screen.buffer, row, color, width);
|
||||
base += screen.width;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
/**
|
||||
* Constants
|
||||
*/
|
||||
const str nl = "\n";
|
||||
|
||||
plex Terminal {
|
||||
nat handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function
|
||||
*/
|
||||
function main() {
|
||||
pln((1.0 + 1.0) as str);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print with a newline
|
||||
*/
|
||||
function pln(str message) {
|
||||
Terminal term = open("term:///0", 0);
|
||||
write(term, message, message.length);
|
||||
write(term, nl, nl.length);
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
/**
|
||||
* Constants
|
||||
*/
|
||||
const str screen_namespace = "screen:///0"
|
||||
const str mouse_namespace = "mouse:///0"
|
||||
const str terminal_namespace = "term:///0"
|
||||
const str new_line = "\n"
|
||||
const byte WHITE = 255
|
||||
|
||||
/**
|
||||
* Devices
|
||||
*/
|
||||
plex Terminal {
|
||||
nat handle;
|
||||
}
|
||||
|
||||
plex Screen {
|
||||
nat handle;
|
||||
nat width;
|
||||
nat height;
|
||||
byte[] buffer;
|
||||
|
||||
draw() {
|
||||
write(this, this.buffer, this.buffer_size);
|
||||
}
|
||||
}
|
||||
|
||||
plex Mouse {
|
||||
nat handle;
|
||||
nat x;
|
||||
nat y;
|
||||
bool left;
|
||||
bool right;
|
||||
bool middle;
|
||||
bool btn4;
|
||||
nat size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function
|
||||
*/
|
||||
function main() {
|
||||
Screen screen = open(screen_namespace, 0);
|
||||
pln(screen.handle as str);
|
||||
pln(screen.width as str);
|
||||
pln(screen.size as str);
|
||||
unsafe {
|
||||
pln(screen.screen_buffer.ptr as str);
|
||||
}
|
||||
|
||||
Mouse mouse = open(mouse_namespace, 0);
|
||||
screen.draw();
|
||||
|
||||
loop {
|
||||
if (mouse.left) {
|
||||
unsafe {
|
||||
screen.buffer[mouse.y * width + mouse.x +
|
||||
screen.buffer.ptr + 4] = WHITE;
|
||||
screen.draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print with a newline
|
||||
*/
|
||||
function pln(str message) {
|
||||
Terminal term = open(terminal_namespace, 0);
|
||||
write(term, message, message.length);
|
||||
write(term, nl, nl.length);
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#include "assembler.h"
|
||||
/**
|
||||
* Emit bytecode to the VM from the source string.
|
||||
*/
|
||||
void assemble(char *source, ScopeTable *st) {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
#ifndef UNDAR_IR_ASSEMBLER_H
|
||||
#define UNDAR_IR_ASSEMBLER_H
|
||||
|
||||
#include "../../vm/libc.h"
|
||||
#include "lexer.h"
|
||||
|
||||
typedef enum { GLOBAL, LOCAL, VAR } ScopeType;
|
||||
typedef enum {
|
||||
VOID,
|
||||
BOOL,
|
||||
I8,
|
||||
I16,
|
||||
I32,
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
F8,
|
||||
F16,
|
||||
F32,
|
||||
STR,
|
||||
PLEX,
|
||||
ARRAY,
|
||||
FUNCTION
|
||||
} SymbolType;
|
||||
|
||||
typedef struct symbol_s Symbol;
|
||||
typedef struct symbol_tab_s SymbolTable;
|
||||
typedef struct scope_tab_s ScopeTable;
|
||||
|
||||
#define MAX_SYMBOL_NAME_LENGTH 64
|
||||
struct symbol_s {
|
||||
char name[MAX_SYMBOL_NAME_LENGTH];
|
||||
u8 name_length;
|
||||
SymbolType type;
|
||||
ScopeType scope;
|
||||
u32 ref; // vm->mp if global, vm->pc local, register if var
|
||||
u32 size; // size of symbol
|
||||
};
|
||||
|
||||
struct symbol_tab_s {
|
||||
Symbol symbols[256];
|
||||
u8 count;
|
||||
i32 parent;
|
||||
};
|
||||
|
||||
struct scope_tab_s {
|
||||
SymbolTable *scopes;
|
||||
u32 count;
|
||||
u32 capacity;
|
||||
i32 scope_ref;
|
||||
};
|
||||
|
||||
void assemble(char *source, ScopeTable *st);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,401 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "../../vm/libc.h"
|
||||
#include "lexer.h"
|
||||
|
||||
typedef struct {
|
||||
const char *start;
|
||||
const char *current;
|
||||
i32 line;
|
||||
} Lexer;
|
||||
|
||||
Lexer lexer;
|
||||
|
||||
void init_lexer(const char *source) {
|
||||
lexer.start = source;
|
||||
lexer.current = source;
|
||||
lexer.line = 1;
|
||||
}
|
||||
|
||||
static bool is_alpha(char c) {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
|
||||
}
|
||||
|
||||
static bool is_digit(char c) { return c >= '0' && c <= '9'; }
|
||||
|
||||
static bool is_at_end() { return *lexer.current == '\0'; }
|
||||
|
||||
static char advance() {
|
||||
lexer.current++;
|
||||
return lexer.current[-1];
|
||||
}
|
||||
|
||||
static char peek() { return *lexer.current; }
|
||||
|
||||
static char peek_next() {
|
||||
if (is_at_end())
|
||||
return '\0';
|
||||
return lexer.current[1];
|
||||
}
|
||||
|
||||
static bool match(char expected) {
|
||||
if (is_at_end())
|
||||
return false;
|
||||
if (*lexer.current != expected)
|
||||
return false;
|
||||
lexer.current++;
|
||||
return true;
|
||||
}
|
||||
|
||||
static Token make_token(TokenType type) {
|
||||
Token token;
|
||||
token.type = type;
|
||||
token.start = lexer.start;
|
||||
token.length = (i32)(lexer.current - lexer.start);
|
||||
token.line = lexer.line;
|
||||
return token;
|
||||
}
|
||||
|
||||
static Token error_token(const char *message) {
|
||||
Token token;
|
||||
token.type = TOKEN_ERROR;
|
||||
token.start = message;
|
||||
token.length = (i32)strlen(message);
|
||||
token.line = lexer.line;
|
||||
return token;
|
||||
}
|
||||
|
||||
static void skip_whitespace() {
|
||||
for (;;) {
|
||||
char c = peek();
|
||||
switch (c) {
|
||||
case ' ':
|
||||
case '\r':
|
||||
case '\t':
|
||||
advance();
|
||||
break;
|
||||
case '\n':
|
||||
lexer.line++;
|
||||
advance();
|
||||
break;
|
||||
case '/':
|
||||
if (peek_next() == '/') {
|
||||
// Single-line comment: skip until newline or end of file
|
||||
advance();
|
||||
while (peek() != '\n' && !is_at_end())
|
||||
advance();
|
||||
} else if (peek_next() == '*') {
|
||||
// Multi-line comment: skip until '*/' or end of file
|
||||
advance();
|
||||
advance();
|
||||
while (!is_at_end()) {
|
||||
if (peek() == '\n')
|
||||
lexer.line++;
|
||||
if (peek() == '*' && peek_next() == '/') {
|
||||
advance();
|
||||
advance();
|
||||
break; // Exit loop, comment ended
|
||||
}
|
||||
advance();
|
||||
}
|
||||
} else {
|
||||
return; // Not a comment, let tokenization handle it
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static TokenType check_keyword(i32 start, i32 length, const char *rest,
|
||||
TokenType type) {
|
||||
if (lexer.current - lexer.start == start + length &&
|
||||
memcmp(lexer.start + start, rest, length) == 0) {
|
||||
return type;
|
||||
}
|
||||
|
||||
return TOKEN_IDENTIFIER;
|
||||
}
|
||||
|
||||
static TokenType identifierType() {
|
||||
switch (lexer.start[0]) {
|
||||
case 'a':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'n':
|
||||
return check_keyword(2, 1, "d", TOKEN_OPERATOR_AND);
|
||||
case 's':
|
||||
return check_keyword(2, 0, "", TOKEN_KEYWORD_AS);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'l':
|
||||
return check_keyword(2, 3, "ose", TOKEN_KEYWORD_CLOSE);
|
||||
case 'o':
|
||||
return check_keyword(2, 3, "nst", TOKEN_KEYWORD_CONST);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
return check_keyword(1, 3, "lse", TOKEN_KEYWORD_ELSE);
|
||||
case 'f':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'a':
|
||||
return check_keyword(2, 3, "lse", TOKEN_KEYWORD_FALSE);
|
||||
case 'o':
|
||||
return check_keyword(2, 1, "r", TOKEN_KEYWORD_FOR);
|
||||
case '3':
|
||||
return check_keyword(1, 1, "2", TOKEN_TYPE_REAL);
|
||||
}
|
||||
return check_keyword(1, 7, "unction", TOKEN_KEYWORD_FN);
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'f':
|
||||
return check_keyword(2, 0, "", TOKEN_KEYWORD_IF);
|
||||
case 's':
|
||||
return check_keyword(2, 0, "", TOKEN_KEYWORD_IS);
|
||||
case '8':
|
||||
return check_keyword(2, 0, "", TOKEN_TYPE_I8);
|
||||
case '1':
|
||||
return check_keyword(2, 1, "6", TOKEN_TYPE_I16);
|
||||
case '3':
|
||||
return check_keyword(2, 1, "2", TOKEN_TYPE_INT);
|
||||
case 'n':
|
||||
if (lexer.current - lexer.start > 2) {
|
||||
switch (lexer.start[2]) {
|
||||
case 'i':
|
||||
return check_keyword(3, 2, "t", TOKEN_KEYWORD_INIT);
|
||||
case 't':
|
||||
return check_keyword(3, 0, "", TOKEN_TYPE_INT);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'a':
|
||||
return check_keyword(2, 1, "t", TOKEN_TYPE_NAT);
|
||||
case 'i':
|
||||
return check_keyword(2, 1, "l", TOKEN_KEYWORD_NIL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'p':
|
||||
return check_keyword(2, 2, "en", TOKEN_KEYWORD_OPEN);
|
||||
case 'r':
|
||||
return check_keyword(2, 0, "", TOKEN_OPERATOR_OR);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'l':
|
||||
return check_keyword(2, 2, "ex", TOKEN_KEYWORD_PLEX);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'e':
|
||||
if (lexer.current - lexer.start > 2) {
|
||||
switch (lexer.start[2]) {
|
||||
case 'f':
|
||||
return check_keyword(3, 4, "resh", TOKEN_KEYWORD_REFRESH);
|
||||
case 't':
|
||||
return check_keyword(3, 3, "urn", TOKEN_KEYWORD_RETURN);
|
||||
case 'a':
|
||||
if (lexer.current - lexer.start > 3) {
|
||||
switch(lexer.start[3]) {
|
||||
case 'd':
|
||||
return check_keyword(4, 0, "", TOKEN_KEYWORD_READ);
|
||||
case 'l':
|
||||
return check_keyword(4, 0, "", TOKEN_TYPE_REAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 't':
|
||||
return check_keyword(2, 1, "r", TOKEN_TYPE_STR);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'h':
|
||||
return check_keyword(2, 2, "is", TOKEN_KEYWORD_THIS);
|
||||
case 'r':
|
||||
return check_keyword(2, 2, "ue", TOKEN_KEYWORD_TRUE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 's':
|
||||
return check_keyword(2, 1, "e", TOKEN_KEYWORD_USE);
|
||||
case '8':
|
||||
return check_keyword(2, 0, "", TOKEN_TYPE_U8);
|
||||
case '1':
|
||||
return check_keyword(2, 1, "6", TOKEN_TYPE_U16);
|
||||
case '3':
|
||||
return check_keyword(2, 1, "2", TOKEN_TYPE_NAT);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'h':
|
||||
return check_keyword(2, 3, "ile", TOKEN_KEYWORD_WHILE);
|
||||
case 'r':
|
||||
return check_keyword(2, 3, "ite", TOKEN_KEYWORD_WRITE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'y':
|
||||
return check_keyword(2, 2, "te", TOKEN_TYPE_U8);
|
||||
case 'o':
|
||||
return check_keyword(2, 2, "ol", TOKEN_TYPE_U8);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'g':
|
||||
return check_keyword(1, 5, "lobal", TOKEN_KEYWORD_GLOBAL);
|
||||
case 'l':
|
||||
return check_keyword(1, 3, "oop", TOKEN_KEYWORD_LOOP);
|
||||
case 'd':
|
||||
return check_keyword(1, 1, "o", TOKEN_KEYWORD_DO);
|
||||
case 'v':
|
||||
return check_keyword(1, 3, "oid", TOKEN_TYPE_VOID);
|
||||
}
|
||||
|
||||
return TOKEN_IDENTIFIER;
|
||||
}
|
||||
|
||||
static Token identifier() {
|
||||
while (is_alpha(peek()) || is_digit(peek()))
|
||||
advance();
|
||||
return make_token(identifierType());
|
||||
}
|
||||
|
||||
static Token number() {
|
||||
while (is_digit(peek()))
|
||||
advance();
|
||||
|
||||
/* Look for a fractional part. */
|
||||
if (peek() == '.' && is_digit(peek_next())) {
|
||||
/* Consume the ".". */
|
||||
advance();
|
||||
|
||||
while (is_digit(peek()))
|
||||
advance();
|
||||
|
||||
return make_token(TOKEN_LITERAL_REAL);
|
||||
}
|
||||
|
||||
return make_token(TOKEN_LITERAL_INT);
|
||||
}
|
||||
|
||||
static Token string() {
|
||||
while (peek() != '"' && !is_at_end()) {
|
||||
if (peek() == '\n')
|
||||
lexer.line++;
|
||||
advance();
|
||||
}
|
||||
|
||||
if (is_at_end())
|
||||
return error_token("Unterminated string.");
|
||||
|
||||
/* The closing quote. */
|
||||
advance();
|
||||
return make_token(TOKEN_LITERAL_STR);
|
||||
}
|
||||
|
||||
Token next_token() {
|
||||
skip_whitespace();
|
||||
lexer.start = lexer.current;
|
||||
|
||||
if (is_at_end())
|
||||
return make_token(TOKEN_EOF);
|
||||
|
||||
char c = advance();
|
||||
if (is_alpha(c))
|
||||
return identifier();
|
||||
char next = peek();
|
||||
if ((c == '-' && is_digit(next)) || is_digit(c))
|
||||
return number();
|
||||
|
||||
switch (c) {
|
||||
case '(':
|
||||
return make_token(TOKEN_LPAREN);
|
||||
case ')':
|
||||
return make_token(TOKEN_RPAREN);
|
||||
case '{':
|
||||
return make_token(TOKEN_LBRACE);
|
||||
case '}':
|
||||
return make_token(TOKEN_RBRACE);
|
||||
case '[':
|
||||
return make_token(TOKEN_LBRACKET);
|
||||
case ']':
|
||||
return make_token(TOKEN_RBRACKET);
|
||||
case ';':
|
||||
return make_token(TOKEN_SEMICOLON);
|
||||
case ',':
|
||||
return make_token(TOKEN_COMMA);
|
||||
case '.':
|
||||
return make_token(TOKEN_DOT);
|
||||
case '-':
|
||||
return make_token(match('>') ? TOKEN_ARROW_RIGHT : TOKEN_MINUS);
|
||||
case '+':
|
||||
return make_token(TOKEN_PLUS);
|
||||
case '/':
|
||||
return make_token(TOKEN_SLASH);
|
||||
case '&':
|
||||
return make_token(match('&') ? TOKEN_AND_AND : TOKEN_AND);
|
||||
case '#':
|
||||
return make_token(TOKEN_MESH);
|
||||
case '$':
|
||||
return make_token(TOKEN_BIG_MONEY);
|
||||
case '*':
|
||||
return make_token(TOKEN_STAR);
|
||||
case '!':
|
||||
return make_token(match('=') ? TOKEN_BANG_EQ : TOKEN_BANG);
|
||||
case '=':
|
||||
return make_token(match('=') ? TOKEN_EQ_EQ : TOKEN_EQ);
|
||||
case '<':
|
||||
return make_token(match('=') ? TOKEN_LTE : TOKEN_LT);
|
||||
case '>':
|
||||
return make_token(match('=') ? TOKEN_GTE : TOKEN_GT);
|
||||
case '"':
|
||||
return string();
|
||||
}
|
||||
|
||||
return error_token("Unexpected character.");
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
#ifndef UNDAR_LEXER_H
|
||||
#define UNDAR_LEXER_H
|
||||
|
||||
#include "../vm/libc.h"
|
||||
|
||||
typedef enum {
|
||||
TOKEN_EOF,
|
||||
TOKEN_IDENTIFIER,
|
||||
|
|
@ -37,7 +35,7 @@ typedef enum {
|
|||
TOKEN_KEYWORD_OPEN,
|
||||
TOKEN_KEYWORD_READ,
|
||||
TOKEN_KEYWORD_WRITE,
|
||||
TOKEN_KEYWORD_STAT,
|
||||
TOKEN_KEYWORD_REFRESH,
|
||||
TOKEN_KEYWORD_CLOSE,
|
||||
TOKEN_KEYWORD_LOOP,
|
||||
TOKEN_KEYWORD_DO,
|
||||
|
|
@ -86,6 +84,5 @@ typedef struct {
|
|||
|
||||
void init_lexer(const char *source);
|
||||
Token next_token();
|
||||
const char* token_type_to_string(TokenType type);
|
||||
|
||||
#endif
|
||||
751
tools/compiler.c
751
tools/compiler.c
|
|
@ -1,751 +0,0 @@
|
|||
#include "compiler.h"
|
||||
#include "parser.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DEBUG_COMPILER
|
||||
|
||||
void emit_byte(u8 byte) {
|
||||
#ifdef DEBUG_COMPILER
|
||||
printf("code[%d] = %d\n", cp, byte);
|
||||
#endif
|
||||
code[cp] = byte;
|
||||
}
|
||||
|
||||
void emit_u32(u32 value) {
|
||||
#ifdef DEBUG_COMPILER
|
||||
printf("code[%d..%d] = %d\n", cp, cp + 3, value);
|
||||
#endif
|
||||
u32 *c = (u32 *)(&code[cp / 4]);
|
||||
c[0] = 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 (sleq(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);
|
||||
}
|
||||
|
||||
u8 current_index = table->scopes[table->scope_ref].count;
|
||||
|
||||
if (current_index + 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);
|
||||
}
|
||||
|
||||
/* set ref to current count for local */
|
||||
s.ref = current_index;
|
||||
|
||||
#ifdef DEBUG_COMPILER
|
||||
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[current_index] = s;
|
||||
table->scopes[table->scope_ref].count++;
|
||||
return current_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(ScopeTable *st, Token regType) {
|
||||
u32 *globals = (u32 *)(mem);
|
||||
Symbol s;
|
||||
|
||||
switch (regType.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;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
Token name = next_token_is(TOKEN_IDENTIFIER);
|
||||
if (name.length > MAX_SYMBOL_NAME_LENGTH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mcpy(s.name, (char *)name.start, name.length);
|
||||
s.name_length = name.length;
|
||||
s.name[name.length] = '\0';
|
||||
|
||||
u32 addr = 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 = mp;
|
||||
WRITE_U8(addr, 1);
|
||||
|
||||
mp += s.size;
|
||||
break;
|
||||
}
|
||||
case TOKEN_KEYWORD_FALSE: {
|
||||
u32 addr = mp;
|
||||
WRITE_U8(addr, 0);
|
||||
|
||||
mp += s.size;
|
||||
break;
|
||||
}
|
||||
case TOKEN_LITERAL_INT: {
|
||||
i32 out = atoi(value.start);
|
||||
|
||||
u32 addr = mp;
|
||||
WRITE_U32(addr, out);
|
||||
|
||||
mp += 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 = mp;
|
||||
WRITE_U32(addr, out);
|
||||
|
||||
mp += s.size;
|
||||
break;
|
||||
}
|
||||
case TOKEN_LITERAL_REAL: {
|
||||
i32 out = FLOAT_TO_REAL(atof(value.start));
|
||||
|
||||
u32 addr = mp;
|
||||
WRITE_U32(addr, out);
|
||||
|
||||
mp += 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(addr + 4 + len, c);
|
||||
len++;
|
||||
}
|
||||
|
||||
u32 size = len + 5; /* 4 (len) + dst_len + 1 (null) */
|
||||
s.size = size;
|
||||
|
||||
mp += size;
|
||||
|
||||
WRITE_U32(addr, len);
|
||||
WRITE_U8(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_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);
|
||||
}
|
||||
|
||||
mcpy(s.name, (void *)name.start, name.length);
|
||||
s.name[name.length] = '\0';
|
||||
s.name_length = name.length;
|
||||
|
||||
symbol_table_add(st, s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function.
|
||||
*/
|
||||
void define_function(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);
|
||||
}
|
||||
mcpy(s.name, (void *)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 = 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plex.
|
||||
*/
|
||||
void define_plex(ScopeTable *st) {
|
||||
Symbol s;
|
||||
s.scope = GLOBAL;
|
||||
s.type = PLEX;
|
||||
|
||||
Token name = next_token_is(TOKEN_IDENTIFIER);
|
||||
if (name.length > MAX_SYMBOL_NAME_LENGTH) {
|
||||
printf("PLEX NAME TOO LONG at line %d: %.*s\n", name.line, name.length,
|
||||
name.start);
|
||||
exit(1);
|
||||
}
|
||||
mcpy(s.name, (void *)name.start, name.length);
|
||||
s.name[name.length] = '\0';
|
||||
s.name_length = name.length;
|
||||
|
||||
next_token_is(TOKEN_LPAREN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Branch.
|
||||
*/
|
||||
void define_branch(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);
|
||||
}
|
||||
mcpy(s.name, (void *)name.start, name.length);
|
||||
s.name_length = name.length;
|
||||
s.name[name.length] = '\0';
|
||||
|
||||
s.ref = cp;
|
||||
symbol_table_add(st, s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a loop
|
||||
*/
|
||||
void define_loop(ScopeTable *st) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an expression using the shunting yard algorithem.
|
||||
* This will be useful because it will make it trivial to track types.
|
||||
* If the type is a literal, we just read it, if it is a variable we read the variable type from the info.
|
||||
*
|
||||
* During the first pass we count the number of variables. We assign a local to each variable.
|
||||
*
|
||||
* When parsing a expression, we assign any function call or literal to a temp variable slot,
|
||||
* (2 maybe?) First one goes in 0, then 2nd in 1, then do operation. Store the operation in 0.
|
||||
* If it is a function call, use 1 to load and push the args, then use 1 for the return variable.
|
||||
* Then do the operation on 1 and 0 and store in 0.
|
||||
*/
|
||||
Symbol value_stack[MAX_SYMBOLS];
|
||||
u8 vsp;
|
||||
Symbol operator_stack[MAX_SYMBOLS];
|
||||
u8 osp;
|
||||
|
||||
void parse_expression(ScopeTable *st) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the symbol table and calculate the types/size/offsets of all values.
|
||||
*/
|
||||
void build_symbol_table(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;
|
||||
st->depth++;
|
||||
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;
|
||||
st->depth--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token.type == TOKEN_KEYWORD_PLEX) {
|
||||
if (st->depth != 0) {
|
||||
printf("I'm letting it slide, but generally plexes are declared "
|
||||
"outside of a scope %d: %.*s\n",
|
||||
token.line, token.length, token.start);
|
||||
}
|
||||
define_plex(st);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token.type == TOKEN_KEYWORD_FN) {
|
||||
if (st->depth != 0) {
|
||||
printf("Functions can only be declared outside of a scope %d: %.*s\n",
|
||||
token.line, token.length, token.start);
|
||||
exit(1);
|
||||
}
|
||||
define_function(st);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token.type == TOKEN_KEYWORD_CONST) {
|
||||
// FIXME: add consts, for now just make everything
|
||||
next_token();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (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) {
|
||||
if (st->depth == 0) {
|
||||
define_global(st, token);
|
||||
continue;
|
||||
}
|
||||
|
||||
define_var(st, token);
|
||||
next_token_is(TOKEN_SEMICOLON);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token.type == TOKEN_KEYWORD_IF) {
|
||||
define_loop(st);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token.type == TOKEN_KEYWORD_LOOP || token.type == TOKEN_KEYWORD_DO ||
|
||||
token.type == TOKEN_KEYWORD_FOR) {
|
||||
define_branch(st);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token.type == TOKEN_KEYWORD_RETURN) {
|
||||
Token next = next_token();
|
||||
if (next.type == TOKEN_SEMICOLON) {
|
||||
/* put 0xFF as return register */
|
||||
cp++;
|
||||
continue;
|
||||
}
|
||||
|
||||
get_reg(next, st);
|
||||
cp++;
|
||||
next_token_is(TOKEN_SEMICOLON);
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_COMPILER
|
||||
printf("-- %.*s --\n", token.length, token.start);
|
||||
#endif
|
||||
}
|
||||
} while (token.type != TOKEN_EOF);
|
||||
}
|
||||
|
||||
/**
|
||||
* 2nd pass, emit the bytecode
|
||||
*/
|
||||
void emit_bytecode(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;
|
||||
st->depth++;
|
||||
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;
|
||||
st->depth--;
|
||||
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) {
|
||||
/* ignore, already processed */
|
||||
Token next = next_token();
|
||||
while (next.type != TOKEN_RPAREN) {
|
||||
next = next_token();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token.type == TOKEN_KEYWORD_CONST) {
|
||||
/* ignore, already processed */
|
||||
next_token(); /* type */
|
||||
next_token(); /* var */
|
||||
next_token(); /* reg */
|
||||
next_token(); /* ; */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (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(); /* 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) {
|
||||
Token next = next_token();
|
||||
if (next.type == TOKEN_SEMICOLON) {
|
||||
/* put 0xFF as return register */
|
||||
code[cp++] = ENCODE_B(OP_RETURN, 255, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
u32 reg = get_reg(next, st);
|
||||
code[cp++] = ENCODE_B(OP_RETURN, reg, 0);
|
||||
next_token_is(TOKEN_SEMICOLON);
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_COMPILER
|
||||
printf("-- %.*s --\n", token.length, token.start);
|
||||
#endif
|
||||
if (token.type == TOKEN_IDENTIFIER) {
|
||||
/*} 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile.
|
||||
*/
|
||||
bool compile(ScopeTable *st, char *source) {
|
||||
build_symbol_table(source, st);
|
||||
cp = 0; /* actually start emitting code */
|
||||
st->count = 0;
|
||||
emit_bytecode(source, st);
|
||||
return true;
|
||||
}
|
||||
109
tools/compiler.h
109
tools/compiler.h
|
|
@ -1,109 +0,0 @@
|
|||
#ifndef UNDAR_COMPILER_H
|
||||
#define UNDAR_COMPILER_H
|
||||
|
||||
#include "../vm/libc.h"
|
||||
#include "../vm/vm.h"
|
||||
|
||||
typedef enum { GLOBAL, LOCAL, VAR } ScopeType;
|
||||
typedef enum {
|
||||
VOID,
|
||||
BOOL,
|
||||
I8,
|
||||
I16,
|
||||
I32,
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
F8,
|
||||
F16,
|
||||
F32,
|
||||
STR,
|
||||
PLEX,
|
||||
ARRAY,
|
||||
FUNCTION
|
||||
} SymbolType;
|
||||
|
||||
typedef struct symbol_s Symbol;
|
||||
typedef struct symbol_tab_s SymbolTable;
|
||||
typedef struct value_type_s ValueType;
|
||||
typedef struct plex_fields_tab_s PlexFieldsTable;
|
||||
typedef struct plex_def_s PlexDef;
|
||||
typedef struct plex_tab_s PlexTable;
|
||||
typedef struct scope_s Scope;
|
||||
typedef struct scope_tab_s ScopeTable;
|
||||
|
||||
struct value_type_s {
|
||||
SymbolType type;
|
||||
u32 name;
|
||||
u32 size;
|
||||
u32 table_ref; // if it is a heap object
|
||||
};
|
||||
|
||||
struct plex_def_s {
|
||||
u32 name;
|
||||
u32 size;
|
||||
u32 field_ref_start;
|
||||
u32 field_count;
|
||||
};
|
||||
|
||||
struct plex_fields_tab_s {
|
||||
u32 *plex_refs;
|
||||
ValueType *fields;
|
||||
u32 count;
|
||||
u32 capacity;
|
||||
};
|
||||
|
||||
struct plex_tab_s {
|
||||
PlexDef *symbols;
|
||||
u32 count;
|
||||
u32 capacity;
|
||||
};
|
||||
|
||||
#define MAX_SYMBOL_NAME_LENGTH 64
|
||||
struct symbol_s {
|
||||
char name[MAX_SYMBOL_NAME_LENGTH];
|
||||
u8 name_length;
|
||||
SymbolType type;
|
||||
ScopeType scope;
|
||||
u32 ref; // vm->mp if global, vm->pc local, register if var
|
||||
u32 size; // size of symbol
|
||||
};
|
||||
|
||||
#define MAX_SYMBOLS 256
|
||||
struct symbol_tab_s {
|
||||
Symbol symbols[MAX_SYMBOLS];
|
||||
u8 count;
|
||||
i32 parent;
|
||||
};
|
||||
|
||||
struct scope_tab_s {
|
||||
SymbolTable *scopes;
|
||||
u32 count;
|
||||
u32 capacity;
|
||||
i32 scope_ref;
|
||||
u32 depth;
|
||||
};
|
||||
|
||||
#define EMIT_U8(value) \
|
||||
do { \
|
||||
code[cp++] = (value) & 0xFF; \
|
||||
} while (0)
|
||||
|
||||
#define EMIT_U16(value) \
|
||||
do { \
|
||||
code[cp++] = (value) & 0xFF; \
|
||||
code[cp++] = ((value) >> 8) & 0xFF; \
|
||||
} while (0)
|
||||
|
||||
#define EMIT_U32(value) \
|
||||
do { \
|
||||
code[cp++] = (value) & 0xFF; \
|
||||
code[cp++] = ((value) >> 8) & 0xFF; \
|
||||
code[cp++] = ((value) >> 16) & 0xFF; \
|
||||
code[cp++] = ((value) >> 24) & 0xFF; \
|
||||
} while (0)
|
||||
|
||||
bool compile(ScopeTable *st, char *source);
|
||||
extern bool table_realloc(ScopeTable *table);/* implement this in arch/ not here */
|
||||
|
||||
#endif
|
||||
512
tools/parser.c
512
tools/parser.c
|
|
@ -1,512 +0,0 @@
|
|||
#include "parser.h"
|
||||
|
||||
typedef struct {
|
||||
const char *start;
|
||||
const char *current;
|
||||
int line;
|
||||
} Lexer;
|
||||
|
||||
Lexer lexer;
|
||||
|
||||
void initLexer(const char *source) {
|
||||
lexer.start = source;
|
||||
lexer.current = source;
|
||||
lexer.line = 1;
|
||||
}
|
||||
|
||||
static bool isAlpha(char c) {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
|
||||
}
|
||||
|
||||
static bool isDigit(char c) { return c >= '0' && c <= '9'; }
|
||||
|
||||
static bool isAtEnd() { return *lexer.current == '\0'; }
|
||||
|
||||
static char advance() {
|
||||
lexer.current++;
|
||||
return lexer.current[-1];
|
||||
}
|
||||
|
||||
static char peek() { return *lexer.current; }
|
||||
|
||||
static char peekNext() {
|
||||
if (isAtEnd())
|
||||
return '\0';
|
||||
return lexer.current[1];
|
||||
}
|
||||
|
||||
static bool match(char expected) {
|
||||
if (isAtEnd())
|
||||
return false;
|
||||
if (*lexer.current != expected)
|
||||
return false;
|
||||
lexer.current++;
|
||||
return true;
|
||||
}
|
||||
|
||||
static Token makeToken(TokenType type) {
|
||||
Token token;
|
||||
token.type = type;
|
||||
token.start = lexer.start;
|
||||
token.length = (int)(lexer.current - lexer.start);
|
||||
token.line = lexer.line;
|
||||
return token;
|
||||
}
|
||||
|
||||
static Token errorToken(const char *message) {
|
||||
Token token;
|
||||
token.type = TOKEN_ERROR;
|
||||
token.start = message;
|
||||
token.length = (int)slen(message);
|
||||
token.line = lexer.line;
|
||||
return token;
|
||||
}
|
||||
|
||||
static void skipWhitespace() {
|
||||
for (;;) {
|
||||
char c = peek();
|
||||
switch (c) {
|
||||
case ' ':
|
||||
case '\r':
|
||||
case '\t':
|
||||
advance();
|
||||
break;
|
||||
case '\n':
|
||||
lexer.line++;
|
||||
advance();
|
||||
break;
|
||||
case '/':
|
||||
if (peekNext() == '/') {
|
||||
// Single-line comment: skip until newline or end of file
|
||||
advance();
|
||||
while (peek() != '\n' && !isAtEnd())
|
||||
advance();
|
||||
} else if (peekNext() == '*') {
|
||||
// Multi-line comment: skip until '*/' or end of file
|
||||
advance();
|
||||
advance();
|
||||
while (!isAtEnd()) {
|
||||
if (peek() == '\n')
|
||||
lexer.line++;
|
||||
if (peek() == '*' && peekNext() == '/') {
|
||||
advance();
|
||||
advance();
|
||||
break; // Exit loop, comment ended
|
||||
}
|
||||
advance();
|
||||
}
|
||||
} else {
|
||||
return; // Not a comment, let tokenization handle it
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static TokenType checkKeyword(int start, int length, const char *rest,
|
||||
TokenType type) {
|
||||
if (lexer.current - lexer.start == start + length &&
|
||||
sleq(lexer.start + start, rest, length)) {
|
||||
return type;
|
||||
}
|
||||
|
||||
return TOKEN_IDENTIFIER;
|
||||
}
|
||||
|
||||
static TokenType identifierType() {
|
||||
switch (lexer.start[0]) {
|
||||
case 'a':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'n':
|
||||
return checkKeyword(2, 1, "d", TOKEN_OPERATOR_AND);
|
||||
case 's':
|
||||
return checkKeyword(2, 0, "", TOKEN_KEYWORD_AS);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'l':
|
||||
return checkKeyword(2, 3, "ose", TOKEN_KEYWORD_CLOSE);
|
||||
case 'o':
|
||||
return checkKeyword(2, 3, "nst", TOKEN_KEYWORD_CONST);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
return checkKeyword(1, 3, "lse", TOKEN_KEYWORD_ELSE);
|
||||
case 'f':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'a':
|
||||
return checkKeyword(2, 3, "lse", TOKEN_KEYWORD_FALSE);
|
||||
case 'o':
|
||||
return checkKeyword(2, 1, "r", TOKEN_KEYWORD_FOR);
|
||||
case '3':
|
||||
return checkKeyword(2, 1, "2", TOKEN_TYPE_REAL);
|
||||
}
|
||||
return checkKeyword(1, 7, "unction", TOKEN_KEYWORD_FN);
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'f':
|
||||
return checkKeyword(2, 0, "", TOKEN_KEYWORD_IF);
|
||||
case 's':
|
||||
return checkKeyword(2, 0, "", TOKEN_KEYWORD_IS);
|
||||
case '8':
|
||||
return checkKeyword(2, 0, "", TOKEN_TYPE_I8);
|
||||
case '1':
|
||||
return checkKeyword(2, 1, "6", TOKEN_TYPE_I16);
|
||||
case '3':
|
||||
return checkKeyword(2, 1, "2", TOKEN_TYPE_INT);
|
||||
case 'n':
|
||||
if (lexer.current - lexer.start > 2) {
|
||||
switch (lexer.start[2]) {
|
||||
case 'i':
|
||||
return checkKeyword(3, 2, "t", TOKEN_KEYWORD_INIT);
|
||||
case 't':
|
||||
return checkKeyword(3, 0, "", TOKEN_TYPE_INT);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'a':
|
||||
return checkKeyword(2, 1, "t", TOKEN_TYPE_NAT);
|
||||
case 'i':
|
||||
return checkKeyword(2, 1, "l", TOKEN_KEYWORD_NIL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'p':
|
||||
return checkKeyword(2, 2, "en", TOKEN_KEYWORD_OPEN);
|
||||
case 'r':
|
||||
return checkKeyword(2, 0, "", TOKEN_OPERATOR_OR);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'l':
|
||||
return checkKeyword(2, 2, "ex", TOKEN_KEYWORD_PLEX);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'e':
|
||||
if (lexer.current - lexer.start > 2) {
|
||||
switch (lexer.start[2]) {
|
||||
case 'a':
|
||||
return checkKeyword(3, 1, "d", TOKEN_KEYWORD_READ);
|
||||
case 't':
|
||||
return checkKeyword(3, 3, "urn", TOKEN_KEYWORD_RETURN);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 't':
|
||||
if (lexer.current - lexer.start > 2) {
|
||||
switch (lexer.start[2]) {
|
||||
case 'r':
|
||||
return checkKeyword(2, 0, "", TOKEN_TYPE_STR);
|
||||
case 'a':
|
||||
return checkKeyword(2, 1, "t", TOKEN_KEYWORD_STAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'h':
|
||||
return checkKeyword(2, 2, "is", TOKEN_KEYWORD_THIS);
|
||||
case 'r':
|
||||
return checkKeyword(2, 2, "ue", TOKEN_KEYWORD_TRUE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 's':
|
||||
return checkKeyword(2, 1, "e", TOKEN_KEYWORD_USE);
|
||||
case '8':
|
||||
return checkKeyword(2, 0, "", TOKEN_TYPE_U8);
|
||||
case '1':
|
||||
return checkKeyword(2, 1, "6", TOKEN_TYPE_U16);
|
||||
case '3':
|
||||
return checkKeyword(2, 1, "2", TOKEN_TYPE_NAT);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
if (lexer.current - lexer.start > 1) {
|
||||
switch (lexer.start[1]) {
|
||||
case 'h':
|
||||
return checkKeyword(2, 3, "ile", TOKEN_KEYWORD_WHILE);
|
||||
case 'r':
|
||||
return checkKeyword(2, 3, "ite", TOKEN_KEYWORD_WRITE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'g':
|
||||
return checkKeyword(1, 5, "lobal", TOKEN_KEYWORD_GLOBAL);
|
||||
}
|
||||
|
||||
return TOKEN_IDENTIFIER;
|
||||
}
|
||||
|
||||
static Token identifier() {
|
||||
while (isAlpha(peek()) || isDigit(peek()))
|
||||
advance();
|
||||
return makeToken(identifierType());
|
||||
}
|
||||
|
||||
static Token number() {
|
||||
while (isDigit(peek()))
|
||||
advance();
|
||||
|
||||
/* Look for a fractional part. */
|
||||
if (peek() == '.' && isDigit(peekNext())) {
|
||||
/* Consume the ".". */
|
||||
advance();
|
||||
|
||||
while (isDigit(peek()))
|
||||
advance();
|
||||
|
||||
return makeToken(TOKEN_LITERAL_REAL);
|
||||
}
|
||||
|
||||
return makeToken(TOKEN_LITERAL_INT);
|
||||
}
|
||||
|
||||
static Token string() {
|
||||
while (peek() != '"' && !isAtEnd()) {
|
||||
if (peek() == '\n')
|
||||
lexer.line++;
|
||||
advance();
|
||||
}
|
||||
|
||||
if (isAtEnd())
|
||||
return errorToken("Unterminated string.");
|
||||
|
||||
/* The closing quote. */
|
||||
advance();
|
||||
return makeToken(TOKEN_LITERAL_STR);
|
||||
}
|
||||
|
||||
Token next_token() {
|
||||
skipWhitespace();
|
||||
lexer.start = lexer.current;
|
||||
|
||||
if (isAtEnd())
|
||||
return makeToken(TOKEN_EOF);
|
||||
|
||||
char c = advance();
|
||||
if (isAlpha(c))
|
||||
return identifier();
|
||||
if (isDigit(c))
|
||||
return number();
|
||||
|
||||
switch (c) {
|
||||
case '(':
|
||||
return makeToken(TOKEN_LPAREN);
|
||||
case ')':
|
||||
return makeToken(TOKEN_RPAREN);
|
||||
case '{':
|
||||
return makeToken(TOKEN_LBRACE);
|
||||
case '}':
|
||||
return makeToken(TOKEN_RBRACE);
|
||||
case '[':
|
||||
return makeToken(TOKEN_LBRACKET);
|
||||
case ']':
|
||||
return makeToken(TOKEN_RBRACKET);
|
||||
case ';':
|
||||
return makeToken(TOKEN_SEMICOLON);
|
||||
case ',':
|
||||
return makeToken(TOKEN_COMMA);
|
||||
case '.':
|
||||
return makeToken(TOKEN_DOT);
|
||||
case '-':
|
||||
return makeToken(match('>') ? TOKEN_ARROW_RIGHT : TOKEN_MINUS);
|
||||
case '+':
|
||||
return makeToken(TOKEN_PLUS);
|
||||
case '/':
|
||||
return makeToken(TOKEN_SLASH);
|
||||
case '&':
|
||||
return makeToken(match('&') ? TOKEN_AND_AND : TOKEN_AND);
|
||||
case '#':
|
||||
return makeToken(TOKEN_MESH);
|
||||
case '$':
|
||||
return makeToken(TOKEN_BIG_MONEY);
|
||||
case '*':
|
||||
return makeToken(TOKEN_STAR);
|
||||
case '!':
|
||||
return makeToken(match('=') ? TOKEN_BANG_EQ : TOKEN_BANG);
|
||||
case '=':
|
||||
return makeToken(match('=') ? TOKEN_EQ_EQ : TOKEN_EQ);
|
||||
case '<':
|
||||
return makeToken(match('=') ? TOKEN_LTE : TOKEN_LT);
|
||||
case '>':
|
||||
return makeToken(match('=') ? TOKEN_GTE : TOKEN_GT);
|
||||
case '"':
|
||||
return string();
|
||||
}
|
||||
|
||||
return errorToken("Unexpected character.");
|
||||
}
|
||||
|
||||
const char *token_type_to_string(TokenType type) {
|
||||
switch (type) {
|
||||
case TOKEN_EOF:
|
||||
return "EOF";
|
||||
case TOKEN_IDENTIFIER:
|
||||
return "IDENTIFIER";
|
||||
case TOKEN_LITERAL_INT:
|
||||
return "LITERAL_INT";
|
||||
case TOKEN_LITERAL_NAT:
|
||||
return "LITERAL_NAT";
|
||||
case TOKEN_LITERAL_REAL:
|
||||
return "LITERAL_REAL";
|
||||
case TOKEN_LITERAL_STR:
|
||||
return "LITERAL_STR";
|
||||
case TOKEN_TYPE_INT:
|
||||
return "TYPE_INT";
|
||||
case TOKEN_TYPE_NAT:
|
||||
return "TYPE_NAT";
|
||||
case TOKEN_TYPE_REAL:
|
||||
return "TYPE_REAL";
|
||||
case TOKEN_TYPE_STR:
|
||||
return "TYPE_STR";
|
||||
case TOKEN_KEYWORD_PLEX:
|
||||
return "KEYWORD_PLEX";
|
||||
case TOKEN_KEYWORD_FN:
|
||||
return "KEYWORD_FN";
|
||||
case TOKEN_KEYWORD_CONST:
|
||||
return "KEYWORD_CONST";
|
||||
case TOKEN_KEYWORD_IF:
|
||||
return "KEYWORD_IF";
|
||||
case TOKEN_KEYWORD_IS:
|
||||
return "IS";
|
||||
case TOKEN_KEYWORD_AS:
|
||||
return "AS";
|
||||
case TOKEN_KEYWORD_ELSE:
|
||||
return "KEYWORD_ELSE";
|
||||
case TOKEN_KEYWORD_WHILE:
|
||||
return "KEYWORD_WHILE";
|
||||
case TOKEN_KEYWORD_FOR:
|
||||
return "KEYWORD_FOR";
|
||||
case TOKEN_KEYWORD_RETURN:
|
||||
return "KEYWORD_RETURN";
|
||||
case TOKEN_KEYWORD_USE:
|
||||
return "KEYWORD_USE";
|
||||
case TOKEN_KEYWORD_INIT:
|
||||
return "KEYWORD_INIT";
|
||||
case TOKEN_KEYWORD_THIS:
|
||||
return "KEYWORD_THIS";
|
||||
case TOKEN_KEYWORD_OPEN:
|
||||
return "TOKEN_KEYWORD_OPEN";
|
||||
case TOKEN_KEYWORD_READ:
|
||||
return "TOKEN_KEYWORD_READ";
|
||||
case TOKEN_KEYWORD_WRITE:
|
||||
return "TOKEN_KEYWORD_WRITE";
|
||||
case TOKEN_KEYWORD_STAT:
|
||||
return "TOKEN_KEYWORD_STAT";
|
||||
case TOKEN_KEYWORD_CLOSE:
|
||||
return "TOKEN_KEYWORD_CLOSE";
|
||||
case TOKEN_KEYWORD_NIL:
|
||||
return "KEYWORD_NIL";
|
||||
case TOKEN_KEYWORD_TRUE:
|
||||
return "KEYWORD_TRUE";
|
||||
case TOKEN_KEYWORD_FALSE:
|
||||
return "KEYWORD_FALSE";
|
||||
case TOKEN_KEYWORD_GLOBAL:
|
||||
return "KEYWORD_GLOBAL";
|
||||
case TOKEN_OPERATOR_NOT:
|
||||
return "OPERATOR_NOT";
|
||||
case TOKEN_OPERATOR_AND:
|
||||
return "OPERATOR_AND";
|
||||
case TOKEN_OPERATOR_OR:
|
||||
return "OPERATOR_OR";
|
||||
case TOKEN_BANG:
|
||||
return "BANG";
|
||||
case TOKEN_BANG_EQ:
|
||||
return "BANG_EQ";
|
||||
case TOKEN_EQ:
|
||||
return "EQ";
|
||||
case TOKEN_EQ_EQ:
|
||||
return "EQ_EQ";
|
||||
case TOKEN_GT:
|
||||
return "GT";
|
||||
case TOKEN_LT:
|
||||
return "LT";
|
||||
case TOKEN_GTE:
|
||||
return "GTE";
|
||||
case TOKEN_LTE:
|
||||
return "LTE";
|
||||
case TOKEN_DOT:
|
||||
return "DOT";
|
||||
case TOKEN_COMMA:
|
||||
return "COMMA";
|
||||
case TOKEN_COLON:
|
||||
return "COLON";
|
||||
case TOKEN_SEMICOLON:
|
||||
return "SEMICOLON";
|
||||
case TOKEN_PLUS:
|
||||
return "PLUS";
|
||||
case TOKEN_MINUS:
|
||||
return "MINUS";
|
||||
case TOKEN_STAR:
|
||||
return "STAR";
|
||||
case TOKEN_SLASH:
|
||||
return "SLASH";
|
||||
case TOKEN_LPAREN:
|
||||
return "LPAREN";
|
||||
case TOKEN_RPAREN:
|
||||
return "RPAREN";
|
||||
case TOKEN_LBRACE:
|
||||
return "LBRACE";
|
||||
case TOKEN_RBRACE:
|
||||
return "RBRACE";
|
||||
case TOKEN_LBRACKET:
|
||||
return "LBRACKET";
|
||||
case TOKEN_RBRACKET:
|
||||
return "RBRACKET";
|
||||
case TOKEN_ARROW_RIGHT:
|
||||
return "ARROW_RIGHT";
|
||||
case TOKEN_MESH:
|
||||
return "MESH";
|
||||
case TOKEN_BIG_MONEY:
|
||||
return "BIG_MONEY";
|
||||
case TOKEN_AND:
|
||||
return "AND";
|
||||
case TOKEN_AND_AND:
|
||||
return "AND_AND";
|
||||
case TOKEN_ERROR:
|
||||
return "ERROR";
|
||||
default:
|
||||
return "UNKNOWN_TOKEN";
|
||||
}
|
||||
}
|
||||
12
vm/libc.c
12
vm/libc.c
|
|
@ -1,14 +1,10 @@
|
|||
#include "libc.h"
|
||||
|
||||
void mcpy(void *to, void *from, u32 length) {
|
||||
u8 *src, *dest;
|
||||
void mcpy(u8 *to, const u8 *from, u32 length) {
|
||||
u32 i;
|
||||
if (to == nil || from == nil) return;
|
||||
|
||||
src = (u8 *)from;
|
||||
dest = (u8 *)to;
|
||||
|
||||
while (length-- > 0) {
|
||||
*(dest++) = *(src++);
|
||||
for (i = 0; i < length; i++) {
|
||||
to[i] = from[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
22
vm/libc.h
22
vm/libc.h
|
|
@ -18,29 +18,15 @@ typedef float f32;
|
|||
|
||||
#define nil NULL
|
||||
|
||||
#define I8_MIN -128
|
||||
#define I8_MAX 127
|
||||
#define U8_MAX 255
|
||||
|
||||
#define I16_MIN -32768
|
||||
#define I16_MAX 32767
|
||||
#define U16_MAX 65535
|
||||
|
||||
#define I32_MIN -2147483648
|
||||
#define I32_MAX 2147483647
|
||||
#define U32_MAX 4294967295
|
||||
|
||||
#define FIXED_CONST 65536.0f
|
||||
#define USED(x) ((void)(x))
|
||||
|
||||
#define AS_INT(v) ((i32)(v))
|
||||
#define AS_NAT(v) ((u32)(v))
|
||||
#define AS_REAL(v) ((i32)(v))
|
||||
#define FLOAT_TO_REAL(v) (((i32)(v)) * FIXED_CONST)
|
||||
#define REAL_TO_FLOAT(v) (((f32)(v)) / FIXED_CONST)
|
||||
#define FLOAT_TO_REAL(v) (((i32)(v)) * 65536.0f)
|
||||
#define REAL_TO_FLOAT(v) (((f32)(v)) / 65536.0f)
|
||||
|
||||
#define USED(x) ((void)(x))
|
||||
|
||||
void mcpy(void *dest, void *src, u32 n);
|
||||
void mcpy(u8 *dest, const u8 *src, u32 n);
|
||||
i32 scpy(char* to, const char *from, u32 length);
|
||||
bool seq(const char *s1, const char *s2);
|
||||
bool sleq(const char *s1, const char *s2, u32 length);
|
||||
|
|
|
|||
461
vm/vm.c
461
vm/vm.c
|
|
@ -10,25 +10,32 @@ u8 status; /* status flag */
|
|||
u8 interrupt; /* device interrupt */
|
||||
u32 *code; /* code */
|
||||
u8 *mem; /* memory */
|
||||
Frame *frames;/* stack frame */
|
||||
|
||||
#define MAX_LEN_INT32 11
|
||||
#define MAX_INT32 2147483647
|
||||
#define MIN_INT32 -2147483648
|
||||
const char radix_set[11] = "0123456789";
|
||||
|
||||
u32 str_alloc(char *str, u32 length) {
|
||||
u32 str_alloc(const char *str, u32 length) {
|
||||
u32 str_addr = mp;
|
||||
u8 *ptr = &mem[mp];
|
||||
mcpy(ptr, &length, sizeof(u32));
|
||||
ptr += 4;
|
||||
mcpy(ptr, str, length);
|
||||
ptr[length] = '\0';
|
||||
mp += 4 + length;
|
||||
u32 i = 0;
|
||||
mp += 4;
|
||||
while (i < length) {
|
||||
mem[mp++] = str[i++];
|
||||
}
|
||||
mem[mp++] = '\0';
|
||||
|
||||
WRITE_U32(str_addr, length);
|
||||
return str_addr;
|
||||
}
|
||||
|
||||
bool step_vm() {
|
||||
|
||||
u32 instruction = code[pc++];
|
||||
u8 opcode = DECODE_OP(instruction);
|
||||
u32 *locals = (u32*)(&mem[fp]);
|
||||
|
||||
Frame *frame = &frames[fp];
|
||||
u32 *globals = (u32*)(mem);
|
||||
|
||||
switch (opcode) {
|
||||
|
|
@ -38,289 +45,301 @@ bool step_vm() {
|
|||
}
|
||||
case OP_CALL: {
|
||||
DECODE_A(instruction)
|
||||
Frame *child_frame = &frames[fp + 1];
|
||||
/* function to jump to */
|
||||
u32 fn_ptr = locals[dest];
|
||||
/* get mp in 'global indexing mode' */
|
||||
u32 *header = &globals[mp / 4];
|
||||
u32 fn_ptr = frame->local[dest];
|
||||
/* reset child locals counter */
|
||||
lc = 0;
|
||||
/* push parents frame value to reset the heap to */
|
||||
(*header++) = fp;
|
||||
/* push return address to child frame */
|
||||
(*header++) = pc;
|
||||
child_frame->return_address = pc;
|
||||
/* push local address to return the value to */
|
||||
(*header++) = fp + (src2 * 4);
|
||||
/* increase the mp to new size */
|
||||
mp += FRAME_HEADER_SIZE;
|
||||
child_frame->parent_return_local = src1;
|
||||
/* now set the frame pointer, where the locals start */
|
||||
fp = mp;
|
||||
/* move mp forward by count many locals */
|
||||
mp += (src1 * 4);
|
||||
child_frame->start_mp = mp;
|
||||
/* increment frame pointer */
|
||||
fp++;
|
||||
/* jump to dest_ptr */
|
||||
pc = fn_ptr;
|
||||
USED(src2);
|
||||
return true;
|
||||
}
|
||||
case OP_RETURN: {
|
||||
DECODE_B(instruction)
|
||||
u32 size = 0;
|
||||
u32 return_value = locals[dest];
|
||||
bool is_ptr = (((u32)(1)) << 15) & imm;
|
||||
bool replaces_value = (((u32)(1)) << 14) & imm;
|
||||
u32 i, size = 0;
|
||||
u32 return_value = frame->local[dest];
|
||||
Frame *child = frame;
|
||||
Frame *parent = &frames[fp - 1];
|
||||
u32 parent_local_return_address = frame->parent_return_local;
|
||||
USED(imm);
|
||||
|
||||
/* reset mp to saved mp, use header size to get "real" start of frame */
|
||||
u32 *frame_start = &globals[(fp / 4) - 3];
|
||||
u32 parent_fp = *frame_start++;
|
||||
u32 return_address = *frame_start++;
|
||||
u32 parent_local_return_address = *frame_start++;
|
||||
if (child->parent_return_local != 0xFF) {
|
||||
|
||||
USED(replaces_value);
|
||||
/* reset memory to parents end of memory */
|
||||
mp = fp - FRAME_HEADER_SIZE;
|
||||
/* reset the frame pointer */
|
||||
fp = parent_fp;
|
||||
}
|
||||
|
||||
if (parent_local_return_address != 255) {
|
||||
if (is_ptr) {
|
||||
if (false) {
|
||||
/* copy value to end of mp if it is a pointer */
|
||||
globals[parent_local_return_address/4] = mp;
|
||||
size = globals[return_value/4];
|
||||
globals[mp/4] = size;
|
||||
parent->local[parent_local_return_address] = mp;
|
||||
size = READ_U32(return_value);
|
||||
WRITE_U32(mp, size);
|
||||
mp += 4;
|
||||
mcpy(&mem[mp], &mem[return_value], size);
|
||||
mp += size;
|
||||
for (i = 0; i < size; i++) {
|
||||
u8 value = READ_U8(return_value + i);
|
||||
WRITE_U8(mp, value);
|
||||
mp++;
|
||||
}
|
||||
} else {
|
||||
/* otherwise just write the return value to its location */
|
||||
globals[(parent_local_return_address / 4)] = return_value;
|
||||
}
|
||||
parent->local[parent_local_return_address] = return_value;
|
||||
}
|
||||
|
||||
/* jump to parent frame */
|
||||
pc = return_address;
|
||||
pc = frame->return_address;
|
||||
/* reset memory to parents end of memory */
|
||||
mp = frame->start_mp;
|
||||
/* reset the frame pointer */
|
||||
fp--;
|
||||
return true;
|
||||
}
|
||||
case OP_SYSCALL: {
|
||||
DECODE_A(instruction)
|
||||
u32 rd = locals[src2]; /* the pointer */
|
||||
status = syscall(dest, src1, rd);
|
||||
u32 id = dest; /* syscall id */
|
||||
u32 size = src1; /* size of heap at that pointer */
|
||||
u32 rd = frame->local[src2]; /* the pointer */
|
||||
status = syscall(id, size, rd);
|
||||
return true;
|
||||
}
|
||||
case OP_PARG: {
|
||||
case OP_PUSH: {
|
||||
DECODE_B(instruction)
|
||||
USED(imm);
|
||||
globals[(mp / 4) + lc + 3] = locals[dest];
|
||||
Frame *child_frame = &frames[fp + 1];
|
||||
child_frame->local[lc] = frame->local[dest];
|
||||
lc++;
|
||||
USED(imm);
|
||||
return true;
|
||||
}
|
||||
case OP_POP: {
|
||||
DECODE_C(instruction)
|
||||
USED(imm);
|
||||
lc--;
|
||||
return true;
|
||||
}
|
||||
case OP_LOAD_IMM: {
|
||||
DECODE_B(instruction)
|
||||
locals[dest] = ((u32)(imm));
|
||||
frame->local[dest] = imm;
|
||||
return true;
|
||||
}
|
||||
case OP_LOAD_UPPER_IMM: {
|
||||
DECODE_B(instruction)
|
||||
u32 value = locals[dest];
|
||||
locals[dest] = (value | (((u32)(imm)) << 16));
|
||||
u32 value = frame->local[dest];
|
||||
frame->local[dest] = (value | (((u32)(imm)) << 16));
|
||||
return true;
|
||||
}
|
||||
case OP_LOAD_IND_8: {
|
||||
DECODE_A(instruction)
|
||||
USED(src2);
|
||||
locals[dest] = READ_U8(locals[src1]);
|
||||
return true;
|
||||
}
|
||||
case OP_LOAD_IND_16: {
|
||||
DECODE_A(instruction)
|
||||
USED(src2);
|
||||
locals[dest] = READ_U16(locals[src1]);
|
||||
return true;
|
||||
}
|
||||
case OP_LOAD_IND_32: {
|
||||
DECODE_A(instruction)
|
||||
USED(src2);
|
||||
locals[dest] = READ_U32(locals[src1]);
|
||||
return true;
|
||||
}
|
||||
case OP_LOAD_ABS_8: {
|
||||
}
|
||||
case OP_LOAD_ABS_16: {
|
||||
}
|
||||
case OP_LOAD_ABS_32: {
|
||||
}
|
||||
case OP_LOAD_OFF_8: {
|
||||
DECODE_A(instruction)
|
||||
locals[dest] = READ_U8((locals[src1] + locals[src2]));
|
||||
return true;
|
||||
}
|
||||
case OP_LOAD_OFF_16: {
|
||||
DECODE_A(instruction)
|
||||
locals[dest] = READ_U16((locals[src1] + locals[src2]));
|
||||
return true;
|
||||
}
|
||||
case OP_LOAD_OFF_32: {
|
||||
DECODE_A(instruction)
|
||||
locals[dest] = READ_U32((locals[src1] + locals[src2]));
|
||||
return true;
|
||||
}
|
||||
case OP_STORE_ABS_8: {
|
||||
}
|
||||
case OP_STORE_ABS_16: {
|
||||
}
|
||||
case OP_STORE_ABS_32: {
|
||||
}
|
||||
case OP_STORE_IND_8: {
|
||||
DECODE_A(instruction)
|
||||
USED(src2);
|
||||
WRITE_U8(locals[dest], locals[src1]);
|
||||
return true;
|
||||
}
|
||||
case OP_STORE_IND_16: {
|
||||
DECODE_A(instruction)
|
||||
USED(src2);
|
||||
WRITE_U16(locals[dest], locals[src1]);
|
||||
return true;
|
||||
}
|
||||
case OP_STORE_IND_32: {
|
||||
DECODE_A(instruction)
|
||||
USED(src2);
|
||||
WRITE_U32(locals[dest], locals[src1]);
|
||||
return true;
|
||||
}
|
||||
case OP_STORE_OFF_8: {
|
||||
DECODE_A(instruction)
|
||||
WRITE_U8((locals[dest] + locals[src2]), locals[src1]);
|
||||
return true;
|
||||
}
|
||||
case OP_STORE_OFF_16: {
|
||||
DECODE_A(instruction)
|
||||
WRITE_U16((locals[dest] + locals[src2]), locals[src1]);
|
||||
return true;
|
||||
}
|
||||
case OP_STORE_OFF_32: {
|
||||
DECODE_A(instruction)
|
||||
WRITE_U32((locals[dest] + locals[src2]), locals[src1]);
|
||||
return true;
|
||||
}
|
||||
case OP_MEM_ALLOC: {
|
||||
DECODE_A(instruction)
|
||||
u32 size = locals[src1];
|
||||
locals[dest] = mp;
|
||||
WRITE_U32(mp, size);
|
||||
u32 size, ldest;
|
||||
u32 rd = fp + (dest * 4);
|
||||
u32 r1 = fp + (src1 * 4);
|
||||
USED(src2);
|
||||
mp += (size + 4);
|
||||
ldest = READ_U32(rd);
|
||||
WRITE_U32(ldest, mp);
|
||||
size = READ_U32(r1);
|
||||
WRITE_U32(mp, size);
|
||||
mp += (4 * (size + 4));
|
||||
return true;
|
||||
}
|
||||
case OP_MEM_CPY_8: {
|
||||
DECODE_A(instruction)
|
||||
|
||||
u8 *ptr_src;
|
||||
u8 *ptr_dest;
|
||||
u32 mdest = locals[dest];
|
||||
u32 msrc = locals[src1];
|
||||
u32 count = locals[src2];
|
||||
u32 i = 0;
|
||||
u32 mdest = frame->local[dest];
|
||||
u32 msrc = frame->local[src1];
|
||||
u32 count = frame->local[src2];
|
||||
|
||||
if (mdest + count >= mp) {
|
||||
status = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
ptr_dest = &mem[mdest];
|
||||
ptr_src = &mem[msrc];
|
||||
mcpy(ptr_dest, ptr_src, count*sizeof(u8));
|
||||
for (i = 0; i < count; i++) {
|
||||
mem[msrc + i] = mem[mdest + i];
|
||||
}
|
||||
|
||||
status = 0;
|
||||
return true;
|
||||
}
|
||||
case OP_MEM_CPY_16: {
|
||||
DECODE_A(instruction)
|
||||
|
||||
u8 *ptr_src;
|
||||
u8 *ptr_dest;
|
||||
u32 mdest = locals[dest];
|
||||
u32 msrc = locals[src1];
|
||||
u32 count = locals[src2];
|
||||
u32 i = 0;
|
||||
u32 mdest = frame->local[dest];
|
||||
u32 msrc = frame->local[src1];
|
||||
u32 count = frame->local[src2] * 2;
|
||||
|
||||
if (mdest + count >= mp) {
|
||||
status = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
ptr_dest = &mem[mdest];
|
||||
ptr_src = &mem[msrc];
|
||||
mcpy(ptr_dest, ptr_src, count*sizeof(u16));
|
||||
for (i = 0; i < count; i++) {
|
||||
u16 value = READ_U16(mdest + i);
|
||||
WRITE_U16(msrc + i, value);
|
||||
}
|
||||
|
||||
status = 0;
|
||||
return true;
|
||||
}
|
||||
case OP_MEM_CPY_32: {
|
||||
DECODE_A(instruction)
|
||||
|
||||
u8 *ptr_src;
|
||||
u8 *ptr_dest;
|
||||
u32 mdest = locals[dest];
|
||||
u32 msrc = locals[src1];
|
||||
u32 count = locals[src2];
|
||||
u32 i = 0;
|
||||
u32 mdest = frame->local[dest];
|
||||
u32 msrc = frame->local[src1];
|
||||
u32 count = frame->local[src2];
|
||||
|
||||
if (mdest + count >= mp) {
|
||||
status = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
ptr_dest = &mem[mdest];
|
||||
ptr_src = &mem[msrc];
|
||||
mcpy(ptr_dest, ptr_src, count*sizeof(u32));
|
||||
for (i = 0; i < count; i++) {
|
||||
globals[msrc + i] = globals[mdest + i];
|
||||
}
|
||||
|
||||
status = 0;
|
||||
return true;
|
||||
}
|
||||
case OP_MEM_SET_8: {
|
||||
DECODE_A(instruction)
|
||||
u32 i, start, end;
|
||||
|
||||
u8 *ptr_dest;
|
||||
u8 value = (u8)locals[src1];
|
||||
u32 count = locals[src2];
|
||||
u32 mdest = locals[dest];
|
||||
u32 rd = fp + (dest * 4);
|
||||
u32 r1 = fp + (src1 * 4);
|
||||
u32 r2 = fp + (src2 * 4);
|
||||
|
||||
if (mdest + count >= mp) {
|
||||
u8 value = (u8)READ_U32(r1);
|
||||
u32 count = READ_U32(r2);
|
||||
|
||||
if (r2 == 0) {
|
||||
status = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
ptr_dest = &mem[mdest];
|
||||
mcpy(ptr_dest, &value, count*sizeof(u8));
|
||||
start = READ_U32(rd);
|
||||
end = start + count;
|
||||
|
||||
if (start >= mp || r2 > mp || end > mp) {
|
||||
status = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = start; i < end; i++) {
|
||||
mem[i] = value;
|
||||
}
|
||||
|
||||
status = 0;
|
||||
return true;
|
||||
}
|
||||
case OP_MEM_SET_16: {
|
||||
DECODE_A(instruction)
|
||||
u32 i, start, end;
|
||||
|
||||
u8 *ptr_dest;
|
||||
u16 value = (u16)locals[src1];
|
||||
u32 count = locals[src2];
|
||||
u32 mdest = locals[dest];
|
||||
u32 rd = fp + (dest * 4);
|
||||
u32 r1 = fp + (src1 * 4);
|
||||
u32 r2 = fp + (src2 * 4);
|
||||
|
||||
if (mdest + count >= mp) {
|
||||
u16 value = (u16)READ_U32(r1);
|
||||
u32 count = READ_U32(r2);
|
||||
|
||||
if (r2 == 0) {
|
||||
status = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
ptr_dest = &mem[mdest];
|
||||
mcpy(ptr_dest, &value, count*sizeof(u16));
|
||||
start = READ_U32(rd);
|
||||
end = start + count;
|
||||
|
||||
if (start >= mp || r2 > mp || end > mp) {
|
||||
status = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = start; i < end; i += 2) {
|
||||
WRITE_U16(i, value);
|
||||
}
|
||||
|
||||
status = 0;
|
||||
return true;
|
||||
}
|
||||
case OP_MEM_SET_32: {
|
||||
DECODE_A(instruction)
|
||||
u32 i, start, end;
|
||||
|
||||
u8 *ptr_dest;
|
||||
u32 value = locals[src1];
|
||||
u32 count = locals[src2];
|
||||
u32 mdest = locals[dest];
|
||||
u32 rd = fp + (dest * 4);
|
||||
u32 r1 = fp + (src1 * 4);
|
||||
u32 r2 = fp + (src2 * 4);
|
||||
|
||||
if (mdest + count >= mp) {
|
||||
u32 value = READ_U32(r1);
|
||||
u32 count = READ_U32(r2);
|
||||
|
||||
if (r2 == 0) {
|
||||
status = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
ptr_dest = &mem[mdest];
|
||||
mcpy(ptr_dest, &value, count*sizeof(u32));
|
||||
start = READ_U32(rd);
|
||||
end = start + count;
|
||||
|
||||
if (start >= mp || r2 > mp || end > mp) {
|
||||
status = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = start; i < end; i += 4) {
|
||||
WRITE_U32(i, value);
|
||||
}
|
||||
|
||||
status = 0;
|
||||
return true;
|
||||
}
|
||||
case OP_MOV: {
|
||||
DECODE_A(instruction)
|
||||
u32 rd = fp + (dest * 4);
|
||||
u32 r1 = fp + (src1 * 4);
|
||||
u32 value = READ_U32(r1);
|
||||
USED(src2);
|
||||
locals[dest] = locals[src1];
|
||||
WRITE_U32(rd, value);
|
||||
return true;
|
||||
}
|
||||
case OP_ADD_INT: {
|
||||
|
|
@ -356,25 +375,25 @@ bool step_vm() {
|
|||
case OP_MUL_REAL: {
|
||||
DECODE_A(instruction)
|
||||
|
||||
i32 src1_whole = (i32)locals[src1] >> 16;
|
||||
i32 src2_whole = (i32)locals[src2] >> 16;
|
||||
i32 src1_whole = (i32)frame->local[src1] >> 16;
|
||||
i32 src2_whole = (i32)frame->local[src2] >> 16;
|
||||
|
||||
i32 src1_decimal = (i32)locals[src1] & 16;
|
||||
i32 src2_decimal = (i32)locals[src2] & 16;
|
||||
i32 src1_decimal = (i32)frame->local[src1] & 16;
|
||||
i32 src2_decimal = (i32)frame->local[src2] & 16;
|
||||
|
||||
i32 result = 0;
|
||||
result += (src1_whole * src2_whole) << 16;
|
||||
result += (src1_whole * src2_decimal);
|
||||
result += (src1_decimal * src2_whole);
|
||||
result += ((src1_decimal * src2_decimal) >> 16) & 16;
|
||||
locals[dest] = result;
|
||||
frame->local[dest] = result;
|
||||
return true;
|
||||
}
|
||||
case OP_DIV_REAL: {
|
||||
DECODE_A(instruction)
|
||||
i32 result;
|
||||
i32 src1_val = (i32)locals[src1];
|
||||
i32 src2_val = (i32)locals[src2];
|
||||
i32 src1_val = (i32)frame->local[src1];
|
||||
i32 src2_val = (i32)frame->local[src2];
|
||||
|
||||
u32 src2_reciprocal = 1;
|
||||
src2_reciprocal <<= 31;
|
||||
|
|
@ -383,49 +402,61 @@ bool step_vm() {
|
|||
result = src1_val * src2_reciprocal;
|
||||
result <<= 1;
|
||||
|
||||
locals[dest] = result;
|
||||
frame->local[dest] = result;
|
||||
return true;
|
||||
}
|
||||
case OP_INT_TO_REAL: {
|
||||
DECODE_A(instruction)
|
||||
i32 result = (i32)locals[src1] << 16;
|
||||
u32 rd = fp + (dest * 4);
|
||||
u32 r1 = fp + (src1 * 4);
|
||||
i32 result = (i32)READ_U32(r1) << 16;
|
||||
USED(src2);
|
||||
locals[dest] = result;
|
||||
WRITE_U32(rd, result);
|
||||
return true;
|
||||
}
|
||||
case OP_INT_TO_NAT: {
|
||||
DECODE_A(instruction)
|
||||
u32 result = (u32)locals[src1];
|
||||
u32 rd = fp + (dest * 4);
|
||||
u32 r1 = fp + (src1 * 4);
|
||||
u32 result = (u32)READ_U32(r1);
|
||||
USED(src2);
|
||||
locals[dest] = result;
|
||||
WRITE_U32(rd, result);
|
||||
return true;
|
||||
}
|
||||
case OP_NAT_TO_REAL: {
|
||||
DECODE_A(instruction)
|
||||
i32 result = (i32)locals[src1] << 16;
|
||||
u32 rd = fp + (dest * 4);
|
||||
u32 r1 = fp + (src1 * 4);
|
||||
i32 result = ((i32)READ_U32(r1) << 16);
|
||||
USED(src2);
|
||||
locals[dest] = result;
|
||||
WRITE_U32(rd, result);
|
||||
return true;
|
||||
}
|
||||
case OP_NAT_TO_INT: {
|
||||
DECODE_A(instruction)
|
||||
i32 result = (i32)locals[src1];
|
||||
u32 rd = fp + (dest * 4);
|
||||
u32 r1 = fp + (src1 * 4);
|
||||
i32 result = ((i32)READ_U32(r1));
|
||||
USED(src2);
|
||||
locals[dest] = result;
|
||||
WRITE_U32(rd, result);
|
||||
return true;
|
||||
}
|
||||
case OP_REAL_TO_INT: {
|
||||
DECODE_A(instruction)
|
||||
i32 result = (i32)locals[src1] >> 16;
|
||||
u32 rd = fp + (dest * 4);
|
||||
u32 r1 = fp + (src1 * 4);
|
||||
i32 result = ((i32)READ_U32(r1) >> 16);
|
||||
USED(src2);
|
||||
locals[dest] = result;
|
||||
WRITE_U32(rd, result);
|
||||
return true;
|
||||
}
|
||||
case OP_REAL_TO_NAT: {
|
||||
DECODE_A(instruction)
|
||||
u32 result = (u32)locals[src1] >> 16;
|
||||
u32 rd = fp + (dest * 4);
|
||||
u32 r1 = fp + (src1 * 4);
|
||||
u32 result = ((u32)READ_U32(r1) >> 16);
|
||||
USED(src2);
|
||||
locals[dest] = result;
|
||||
WRITE_U32(rd, result);
|
||||
return true;
|
||||
}
|
||||
case OP_BIT_SHIFT_LEFT: {
|
||||
|
|
@ -446,9 +477,15 @@ bool step_vm() {
|
|||
case OP_BIT_XOR: {
|
||||
MATH_OP_NO_CAST(^);
|
||||
}
|
||||
case OP_JMP_IMM: {
|
||||
DECODE_C(instruction)
|
||||
pc = imm;
|
||||
return true;
|
||||
}
|
||||
case OP_JMP_ABS: {
|
||||
DECODE_A(instruction)
|
||||
u32 jmp_dest = locals[dest];
|
||||
u32 rd = fp + (dest * 4);
|
||||
u32 jmp_dest = READ_U32(rd);
|
||||
if (jmp_dest > cp) {
|
||||
status = 1;
|
||||
return true;
|
||||
|
|
@ -461,7 +498,10 @@ bool step_vm() {
|
|||
}
|
||||
case OP_JMP_OFF: {
|
||||
DECODE_A(instruction)
|
||||
u32 jmp_dest = locals[dest] + locals[src1];
|
||||
u32 rd = fp + (dest * 4);
|
||||
u32 r1 = fp + (src1 * 4);
|
||||
|
||||
u32 jmp_dest = READ_U32(rd) + READ_U32(r1);
|
||||
if (jmp_dest > cp) {
|
||||
status = 1;
|
||||
return true;
|
||||
|
|
@ -474,7 +514,7 @@ bool step_vm() {
|
|||
case OP_JMP_FLAG: {
|
||||
DECODE_A(instruction)
|
||||
u32 mask;
|
||||
u32 jmp_dest = locals[dest];
|
||||
u32 jmp_dest = frame->local[dest];
|
||||
if (jmp_dest > cp) {
|
||||
status = 1;
|
||||
return true;
|
||||
|
|
@ -486,80 +526,65 @@ bool step_vm() {
|
|||
pc = (jmp_dest & mask) | (pc & ~mask);
|
||||
return true;
|
||||
}
|
||||
case OP_JEQS: {
|
||||
case OP_JEQ_INT: {
|
||||
COMPARE_AND_JUMP(i32, ==);
|
||||
}
|
||||
case OP_JNES: {
|
||||
case OP_JNE_INT: {
|
||||
COMPARE_AND_JUMP(i32, !=);
|
||||
}
|
||||
case OP_JGTS: {
|
||||
case OP_JGT_INT: {
|
||||
COMPARE_AND_JUMP(i32, >);
|
||||
}
|
||||
case OP_JLTS: {
|
||||
case OP_JLT_INT: {
|
||||
COMPARE_AND_JUMP(i32, <);
|
||||
}
|
||||
case OP_JLES: {
|
||||
case OP_JLE_INT: {
|
||||
COMPARE_AND_JUMP(i32, <=);
|
||||
}
|
||||
case OP_JGES: {
|
||||
case OP_JGE_INT: {
|
||||
COMPARE_AND_JUMP(i32, >=);
|
||||
}
|
||||
case OP_JEQU: {
|
||||
case OP_JEQ_NAT: {
|
||||
COMPARE_AND_JUMP(u32, ==);
|
||||
}
|
||||
case OP_JNEU: {
|
||||
case OP_JNE_NAT: {
|
||||
COMPARE_AND_JUMP(u32, !=);
|
||||
}
|
||||
case OP_JGTU: {
|
||||
case OP_JGT_NAT: {
|
||||
COMPARE_AND_JUMP(u32, >);
|
||||
}
|
||||
case OP_JLTU: {
|
||||
case OP_JLT_NAT: {
|
||||
COMPARE_AND_JUMP(u32, <);
|
||||
}
|
||||
case OP_JLEU: {
|
||||
case OP_JLE_NAT: {
|
||||
COMPARE_AND_JUMP(u32, <=);
|
||||
}
|
||||
case OP_EQS: {
|
||||
MATH_OP(i32, ==);
|
||||
case OP_JGE_NAT: {
|
||||
COMPARE_AND_JUMP(u32, >=);
|
||||
}
|
||||
case OP_NES: {
|
||||
MATH_OP(i32, !=);
|
||||
case OP_JEQ_REAL: {
|
||||
COMPARE_AND_JUMP(i32, ==);
|
||||
}
|
||||
case OP_GTS: {
|
||||
MATH_OP(i32, >);
|
||||
case OP_JNE_REAL: {
|
||||
COMPARE_AND_JUMP(i32, !=);
|
||||
}
|
||||
case OP_LTS: {
|
||||
MATH_OP(i32, <);
|
||||
case OP_JGE_REAL: {
|
||||
COMPARE_AND_JUMP(i32, >=);
|
||||
}
|
||||
case OP_LES: {
|
||||
MATH_OP(i32, <=);
|
||||
case OP_JGT_REAL: {
|
||||
COMPARE_AND_JUMP(i32, >);
|
||||
}
|
||||
case OP_GES: {
|
||||
MATH_OP(i32, >=);
|
||||
case OP_JLT_REAL: {
|
||||
COMPARE_AND_JUMP(i32, <);
|
||||
}
|
||||
case OP_EQU: {
|
||||
MATH_OP(u32, ==);
|
||||
}
|
||||
case OP_NEU: {
|
||||
MATH_OP(u32, !=);
|
||||
}
|
||||
case OP_GTU: {
|
||||
MATH_OP(u32, >);
|
||||
}
|
||||
case OP_LTU: {
|
||||
MATH_OP(u32, <);
|
||||
}
|
||||
case OP_LEU: {
|
||||
MATH_OP(u32, <=);
|
||||
}
|
||||
case OP_GEU: {
|
||||
MATH_OP(u32, >=);
|
||||
case OP_JLE_REAL: {
|
||||
COMPARE_AND_JUMP(i32, <=);
|
||||
}
|
||||
case OP_INT_TO_STR: {
|
||||
DECODE_A(instruction)
|
||||
|
||||
u32 i = MAX_LEN_INT32;
|
||||
i32 v = (i32)locals[src1];
|
||||
i32 v = (i32)frame->local[src1];
|
||||
char buffer[MAX_LEN_INT32];
|
||||
i32 n = v;
|
||||
bool neg = n < 0;
|
||||
|
|
@ -579,14 +604,14 @@ bool step_vm() {
|
|||
buffer[--i] = '0';
|
||||
|
||||
/* Copy from buffer[i] to buffer + MAX_LEN_INT32 */
|
||||
locals[dest] = str_alloc(buffer + i, MAX_LEN_INT32 - i);
|
||||
frame->local[dest] = str_alloc(buffer + i, MAX_LEN_INT32 - i);
|
||||
|
||||
return pc;
|
||||
}
|
||||
case OP_NAT_TO_STR: {
|
||||
DECODE_A(instruction)
|
||||
|
||||
u32 v = (i32)locals[src1];
|
||||
u32 v = (i32)frame->local[src1];
|
||||
char buffer[MAX_LEN_INT32];
|
||||
u32 n = v;
|
||||
u32 i = MAX_LEN_INT32;
|
||||
|
|
@ -599,7 +624,7 @@ bool step_vm() {
|
|||
if (v == 0)
|
||||
buffer[--i] = '0';
|
||||
/* Copy from buffer[i] to buffer + MAX_LEN_INT32 */
|
||||
locals[dest] = str_alloc(buffer + i, MAX_LEN_INT32 - i);
|
||||
frame->local[dest] = str_alloc(buffer + i, MAX_LEN_INT32 - i);
|
||||
|
||||
return pc;
|
||||
}
|
||||
|
|
@ -607,8 +632,8 @@ bool step_vm() {
|
|||
DECODE_A(instruction)
|
||||
|
||||
u32 i = 0, j = 0;
|
||||
i32 q = (i32)locals[src1];
|
||||
char buffer[12];
|
||||
i32 q = (i32)frame->local[src1];
|
||||
char buffer[MAX_LEN_INT32];
|
||||
u32 int_part, frac_part;
|
||||
|
||||
if (q < 0) {
|
||||
|
|
@ -641,13 +666,15 @@ bool step_vm() {
|
|||
frac_part &= 0xFFFF;
|
||||
}
|
||||
|
||||
locals[dest] = str_alloc(buffer + i, 12 - i);
|
||||
frame->local[dest] = str_alloc(buffer + i, MAX_LEN_INT32 - i);
|
||||
|
||||
return pc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* something went very wrong */
|
||||
status = 255;
|
||||
return false;
|
||||
}
|
||||
|
||||
142
vm/vm.h
142
vm/vm.h
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
* A : [8:opcode][8:dest][8:src1][8:src2]
|
||||
* B : [8:opcode][8:dest][16:immediate]
|
||||
* C : [8:opcode][24:immediate]
|
||||
*/
|
||||
#define DECODE_OP(instruction) ((((u32)(instruction)) >> 24) & 0xFF)
|
||||
|
||||
|
|
@ -28,25 +29,36 @@
|
|||
u8 dest = (((u32)(instruction)) >> 16) & 0xFF; \
|
||||
u16 imm = ((u32)(instruction)) & 0xFFFF;
|
||||
|
||||
#define ENCODE_C(opcode, imm) ((((u32)(opcode) & 0xFF) << 24) | \
|
||||
(((u32)(imm)) & 0xFFFFFF))
|
||||
#define DECODE_C(instruction) \
|
||||
u32 imm = ((u32)(instruction)) & 0xFFFFFF;
|
||||
|
||||
typedef enum {
|
||||
OP_HALT, /* halt : A : all zeros : halt execution */
|
||||
OP_CALL, /* call : A : dest args return : creates a new frame */
|
||||
OP_RETURN, /* return : B : dest return_flags : returns from a frame to the parent frame */
|
||||
OP_SYSCALL, /* syscall : A : id device mem_ptr : does a system call based on id with args */
|
||||
OP_RETURN, /* return : B : dest return_flags: returns from a frame to the parent frame */
|
||||
OP_SYSCALL, /* syscall : A : id args mem_ptr : does a system call based on id with args */
|
||||
OP_LOAD_IMM, /* load_immediate : B : locals[dest] = const as u16 */
|
||||
OP_LOAD_UPPER_IMM, /* load_upper_immediate : B : locals[dest] = const as u32 << 16 | u16 */
|
||||
OP_LOAD_IND_8, /* load_indirect_8 : A : locals[dest] = memory[locals[src1]] as u8 */
|
||||
OP_LOAD_IND_16, /* load_indirect_16 : A : locals[dest] = memory[locals[src1]] as u16 */
|
||||
OP_LOAD_IND_32, /* load_indirect_32 : A : locals[dest] = memory[locals[src1]] as u32 */
|
||||
OP_LOAD_OFF_8, /* load_offset_8 : A : locals[dest] = memory[locals[src1] + locals[src2]] as u8 */
|
||||
OP_LOAD_OFF_16, /* load_offset_16 : A : locals[dest] = memory[locals[src1] + locals[src2]] as u16 */
|
||||
OP_LOAD_OFF_32, /* load_offset_32 : A : locals[dest] = memory[locals[src1] + locals[src2]] as u32 */
|
||||
OP_STORE_IND_8, /* store_indirect_8 : A : memory[locals[dest]] = locals[src1] && 0xFF */
|
||||
OP_STORE_IND_16, /* store_indirect_16 : A : memory[locals[dest]] = locals[src1] && 0xFFFF*/
|
||||
OP_STORE_IND_32, /* store_indirect_32 : A : memory[locals[dest]] = locals[src1] */
|
||||
OP_STORE_OFF_8, /* store_offset_8 : A : memory[locals[dest] + locals[src2]] = locals[src1] && 0xFF */
|
||||
OP_STORE_OFF_16, /* store_offset_16 : A : memory[locals[dest] + locals[src2]] = locals[src1] && 0xFFFF */
|
||||
OP_STORE_OFF_32, /* store_offset_32 : A : memory[locals[dest] + locals[src2]] = locals[src1] */
|
||||
OP_LOAD_ABS_8, /* load_absolute_8 : A : locals[dest] = memory[src1] as u8 */
|
||||
OP_LOAD_ABS_16, /* load_absolute_16 : A : locals[dest] = memory[src1] as u16 */
|
||||
OP_LOAD_ABS_32, /* load_absolute_32 : A : locals[dest] = memory[src1] as u32 */
|
||||
OP_LOAD_OFF_8, /* load_offset_8 : A : locals[dest] = memory[locals[src1] + src2] as u8 */
|
||||
OP_LOAD_OFF_16, /* load_offset_16 : A : locals[dest] = memory[locals[src1] + src2] as u16 */
|
||||
OP_LOAD_OFF_32, /* load_offset_32 : A : locals[dest] = memory[locals[src1] + src2] as u32 */
|
||||
OP_STORE_ABS_8, /* store_absolute_8 : A : memory[dest] = src1 && 0xFF */
|
||||
OP_STORE_ABS_16, /* store_absolute_16 : A : memory[dest] = src1 && 0xFFFF */
|
||||
OP_STORE_ABS_32, /* store_absolute_32 : A : memory[dest] = src1 */
|
||||
OP_STORE_IND_8, /* store_indirect_8 : A : memory[dest] = locals[src1] && 0xFF */
|
||||
OP_STORE_IND_16, /* store_indirect_16 : A : memory[dest] = locals[src1] && 0xFFFF*/
|
||||
OP_STORE_IND_32, /* store_indirect_32 : A : memory[dest] = locals[src1] */
|
||||
OP_STORE_OFF_8, /* store_offset_8 : A : memory[locals[dest] + src2] = locals[src1] && 0xFF */
|
||||
OP_STORE_OFF_16, /* store_offset_16 : A : memory[locals[dest] + src2] = locals[src1] && 0xFFFF */
|
||||
OP_STORE_OFF_32, /* store_offset_32 : A : memory[locals[dest] + src2] = locals[src1] */
|
||||
OP_MEM_ALLOC, /* alloc : A : memory[dest] = [locals[src1] as size + 4] */
|
||||
OP_MEM_CPY_8, /* memcpy_8 : A : memory[src1..src1+src2] = memory[dest..dest+src2] */
|
||||
OP_MEM_CPY_16, /* memcpy_16 : A : memory[src1..src1+src2] = memory[dest..dest+src2] */
|
||||
|
|
@ -55,7 +67,8 @@ typedef enum {
|
|||
OP_MEM_SET_16, /* memset_16 : A : memory[dest..dest+src2] = local[src1] as u16 */
|
||||
OP_MEM_SET_32, /* memset_32 : A : memory[dest..dest+src2] = local[src1] as u32 */
|
||||
OP_MOV, /* mov : A : locals[dest] = locals[src1] */
|
||||
OP_PARG, /* push_arg : A : dest : push u32 value onto the childs locals */
|
||||
OP_PUSH, /* push : B : push u32 value onto the childs locals */
|
||||
OP_POP, /* pop : C : pop u32 value off the stack (move MP back) */
|
||||
OP_ADD_INT, /* add_int : A : locals[dest] = locals[src1] + locals[src2] */
|
||||
OP_SUB_INT, /* sub_int : A : locals[dest] = locals[src1] - locals[src2] */
|
||||
OP_MUL_INT, /* mul_int : A : locals[dest] = locals[src1] * locals[src2] */
|
||||
|
|
@ -68,52 +81,44 @@ typedef enum {
|
|||
OP_SUB_REAL, /* sub_real : A : locals[dest] = locals[src1] - locals[src2] */
|
||||
OP_MUL_REAL, /* mul_real : A : locals[dest] = locals[src1] * locals[src2] */
|
||||
OP_DIV_REAL, /* div_real : A : locals[dest] = locals[src1] / locals[src2] */
|
||||
OP_BIT_SHIFT_LEFT, /* bit_shift_left : A : locals[dest] = locals[src1] << locals[src2] */
|
||||
OP_BIT_SHIFT_RIGHT,/* bit_shift_right : A : locals[dest] = locals[src1] >> locals[src2] */
|
||||
OP_BIT_SHIFT_R_EXT,/* bit_shift_r_ext : A : locals[dest] as i32 = locals[src1] >> locals[src2] */
|
||||
OP_BIT_AND, /* bit_and : A : locals[dest] = locals[src1] & locals[src2] */
|
||||
OP_BIT_OR, /* bit_or : A : locals[dest] = locals[src1] | locals[src2] */
|
||||
OP_BIT_XOR, /* bit_xor : A : locals[dest] = locals[src1] ^ locals[src2] */
|
||||
OP_EQS, /* eq_signed : A : locals[dest] = locals[src1] == locals[src2] */
|
||||
OP_NES, /* ne_signed : A : locals[dest] = locals[src1] != locals[src2] */
|
||||
OP_GTS, /* gt_signed : A : locals[dest] = locals[src1] > locals[src2] */
|
||||
OP_LTS, /* lt_signed : A : locals[dest] = locals[src1] < locals[src2] */
|
||||
OP_LES, /* le_signed : A : locals[dest] = locals[src1] <= locals[src2] */
|
||||
OP_GES, /* ge_signed : A : locals[dest] = locals[src1] >= locals[src2] */
|
||||
OP_EQU, /* eq_unsigned : A : locals[dest] = locals[src1] == locals[src2] */
|
||||
OP_NEU, /* ne_unsigned : A : locals[dest] = locals[src1] != locals[src2] */
|
||||
OP_GTU, /* gt_unsigned : A : locals[dest] = locals[src1] > locals[src2] */
|
||||
OP_LTU, /* lt_unsigned : A : locals[dest] = locals[src1] < locals[src2] */
|
||||
OP_LEU, /* le_unsigned : A : locals[dest] = locals[src1] <= locals[src2] */
|
||||
OP_GEU, /* ge_unsigned : A : locals[dest] = locals[src1] >= locals[src2] */
|
||||
OP_JMP_FLAG, /* jump_if_flag : A : jump to locals[dest] if flag > 0 */
|
||||
OP_JMP_ABS, /* jump_absolute : A : jump to locals[dest] if locals[src1] != 0 */
|
||||
OP_JMP_OFF, /* jump_offset : A : jump to locals[dest] + locals[src2] if locals[src1] != 0 */
|
||||
OP_JEQS, /* jump_eq_signed : A : jump to locals[dest] if locals[src1] as i32 == locals[src2] as i32 */
|
||||
OP_JNES, /* jump_neq_signed : A : jump to locals[dest] if locals[src1] as i32 != locals[src2] as i32 */
|
||||
OP_JGTS, /* jump_gt_signed : A : jump to locals[dest] if locals[src1] as i32 > locals[src2] as i32 */
|
||||
OP_JLTS, /* jump_lt_signed : A : jump to locals[dest] if locals[src1] as i32 < locals[src2] as i32 */
|
||||
OP_JLES, /* jump_le_signed : A : jump to locals[dest] if locals[src1] as i32 <= locals[src2] as i32 */
|
||||
OP_JGES, /* jump_ge_signed : A : jump to locals[dest] if locals[src1] as i32 >= locals[src2] as i32 */
|
||||
OP_JEQU, /* jump_eq_unsigned : A : jump to locals[dest] if locals[src1] as u32 == locals[src2] as u32 */
|
||||
OP_JNEU, /* jump_neq_unsigned : A : jump to locals[dest] if locals[src1] as u32 != locals[src2] as u32 */
|
||||
OP_JGTU, /* jump_gt_unsigned : A : jump to locals[dest] if locals[src1] as u32 > locals[src2] as u32 */
|
||||
OP_JLTU, /* jump_lt_unsigned : A : jump to locals[dest] if locals[src1] as u32 < locals[src2] as u32 */
|
||||
OP_JLEU, /* jump_le_unsigned : A : jump to locals[dest] if locals[src1] as u32 <= locals[src2] as u32 */
|
||||
OP_JGEU, /* jump_ge_unsigned : A : jump to locals[dest] if locals[src1] as u32 >= locals[src2] as u32 */
|
||||
OP_INT_TO_REAL, /* int_to_real : A : locals[dest] = locals[src1] as real */
|
||||
OP_INT_TO_NAT, /* int_to_nat : A : locals[dest] = locals[src1] as nat */
|
||||
OP_NAT_TO_REAL, /* nat_to_real : A : locals[dest] = locals[src1] as real */
|
||||
OP_NAT_TO_INT, /* nat_to_int : A : locals[dest] = locals[src1] as int */
|
||||
OP_REAL_TO_INT, /* real_to_int : A : locals[dest] = locals[src1] as int */
|
||||
OP_REAL_TO_NAT, /* real_to_nat : A : locals[dest] = locals[src1] as nat */
|
||||
OP_INT_TO_STR, /* int_to_str : A : locals[dest] = &mem[mp..] <~ locals[src1] as str */
|
||||
OP_NAT_TO_STR, /* nat_to_str : A : locals[dest] = &mem[mp..] <~ locals[src1] as str */
|
||||
OP_REAL_TO_STR, /* real_to_str : A : locals[dest] = &mem[mp..] <~ locals[src1] as str */
|
||||
OP_STR_TO_INT, /* str_to_int : A : locals[dest] = mem[locals[src1]..] ~> int */
|
||||
OP_STR_TO_NAT, /* str_to_nat : A : locals[dest] = mem[locals[src1]..] ~> nat */
|
||||
OP_STR_TO_REAL, /* str_to_real : A : locals[dest] = mem[locals[src1]..] ~> real */
|
||||
OP_MAX_OPCODE /* not an opcode just a count of instructions */
|
||||
OP_BIT_SHIFT_LEFT, /* bit_shift_left : A : locals[dest] = locals[src1] << locals[src2] */
|
||||
OP_BIT_SHIFT_RIGHT,/* bit_shift_right : A : locals[dest] = locals[src1] >> locals[src2] */
|
||||
OP_BIT_SHIFT_R_EXT,/* bit_shift_r_ext : A : locals[dest] as i32 = locals[src1] >> locals[src2] */
|
||||
OP_BIT_AND, /* bit_and : A : locals[dest] = locals[src1] & locals[src2] */
|
||||
OP_BIT_OR, /* bit_or : A : locals[dest] = locals[src1] | locals[src2] */
|
||||
OP_BIT_XOR, /* bit_xor : A : locals[dest] = locals[src1] ^ locals[src2] */
|
||||
OP_JMP_IMM, /* jump_immediate : C : jump to imm unconditionally */
|
||||
OP_JMP_ABS, /* jump_absolute : A : jump to locals[dest] unconditionally */
|
||||
OP_JMP_OFF, /* jump_offset : A : jump to locals[dest] + locals[src1] unconditionally */
|
||||
OP_JMP_FLAG, /* jump_if_flag : A : jump to locals[dest] if flag > 0 */
|
||||
OP_JEQ_INT, /* jump_eq_int : A : jump to locals[dest] if locals[src1] as int == locals[src2] as int */
|
||||
OP_JNE_INT, /* jump_neq_int : A : jump to locals[dest] if locals[src1] as int != locals[src2] as int */
|
||||
OP_JGT_INT, /* jump_gt_int : A : jump to locals[dest] if locals[src1] as int > locals[src2] as int */
|
||||
OP_JLT_INT, /* jump_lt_int : A : jump to locals[dest] if locals[src1] as int < locals[src2] as int */
|
||||
OP_JLE_INT, /* jump_le_int : A : jump to locals[dest] if locals[src1] as int <= locals[src2] as int */
|
||||
OP_JGE_INT, /* jump_ge_int : A : jump to locals[dest] if locals[src1] as int >= locals[src2] as int */
|
||||
OP_JEQ_NAT, /* jump_eq_nat : A : jump to locals[dest] if locals[src1] as nat == locals[src2] as nat */
|
||||
OP_JNE_NAT, /* jump_neq_nat : A : jump to locals[dest] if locals[src1] as nat != locals[src2] as nat */
|
||||
OP_JGT_NAT, /* jump_gt_nat : A : jump to locals[dest] if locals[src1] as nat > locals[src2] as nat */
|
||||
OP_JLT_NAT, /* jump_lt_nat : A : jump to locals[dest] if locals[src1] as nat < locals[src2] as nat */
|
||||
OP_JLE_NAT, /* jump_le_nat : A : jump to locals[dest] if locals[src1] as nat <= locals[src2] as nat */
|
||||
OP_JGE_NAT, /* jump_ge_nat : A : jump to locals[dest] if locals[src1] as nat >= locals[src2] as nat */
|
||||
OP_JEQ_REAL, /* jump_eq_real : A : jump to locals[dest] if locals[src1] as real == locals[src2] as real */
|
||||
OP_JNE_REAL, /* jump_neq_real : A : jump to locals[dest] if locals[src1] as real != locals[src2] as real */
|
||||
OP_JGE_REAL, /* jump_ge_real : A : jump to locals[dest] if locals[src1] as real >= locals[src2] as real */
|
||||
OP_JGT_REAL, /* jump_gt_real : A : jump to locals[dest] if locals[src1] as real > locals[src2] as real */
|
||||
OP_JLT_REAL, /* jump_lt_real : A : jump to locals[dest] if locals[src1] as real < locals[src2] as real */
|
||||
OP_JLE_REAL, /* jump_le_real : A : jump to locals[dest] if locals[src1] as real <= locals[src2] as real */
|
||||
OP_INT_TO_STR,
|
||||
OP_NAT_TO_STR,
|
||||
OP_REAL_TO_STR,
|
||||
OP_MAX_OPCODE /* not an opcode count of instructions */
|
||||
} Opcode;
|
||||
|
||||
typedef enum {
|
||||
|
|
@ -122,6 +127,16 @@ typedef enum {
|
|||
SYSCALL_MAX
|
||||
} Syscall;
|
||||
|
||||
#define MAX_LOCALS 32
|
||||
typedef struct frame_s Frame;
|
||||
struct frame_s {
|
||||
u32 return_address;
|
||||
u32 parent_return_local;
|
||||
u32 start_mp;
|
||||
u32 local_is_ptr;
|
||||
u32 local[MAX_LOCALS];
|
||||
};
|
||||
|
||||
extern u32 pc; /* program counter */
|
||||
extern u32 cp; /* code pointer */
|
||||
extern u32 mp; /* memory pointer */
|
||||
|
|
@ -131,13 +146,15 @@ extern u8 status; /* status flag */
|
|||
extern u8 interrupt; /* device interrupt */
|
||||
extern u32 *code; /* code */
|
||||
extern u8 *mem; /* memory */
|
||||
extern Frame *frames; /* stack frame */
|
||||
|
||||
#define READ_U8(addr) (mem[addr])
|
||||
|
||||
#define READ_U16(addr) \
|
||||
(((u16)mem[(addr) + 1] << 8) | ((u16)mem[(addr)]))
|
||||
|
||||
#define READ_U32(addr) (((u32)mem[(addr) + 3] << 24) | \
|
||||
#define READ_U32(addr) \
|
||||
(((u32)mem[(addr) + 3] << 24) | \
|
||||
((u32)mem[(addr) + 2] << 16) | \
|
||||
((u32)mem[(addr) + 1] << 8) | ((u32)mem[(addr)]))
|
||||
|
||||
|
|
@ -154,23 +171,23 @@ extern u8 *mem; /* memory */
|
|||
|
||||
#define WRITE_U32(addr, value) \
|
||||
do { \
|
||||
mem[(addr)] = (value) & 0xFF; \
|
||||
mem[(addr) + 1] = ((value) >> 8) & 0xFF; \
|
||||
mem[(addr) + 2] = ((value) >> 16) & 0xFF; \
|
||||
mem[(addr) + 3] = ((value) >> 24) & 0xFF; \
|
||||
mem[addr] = (value) & 0xFF; \
|
||||
mem[addr + 1] = ((value) >> 8) & 0xFF; \
|
||||
mem[addr + 2] = ((value) >> 16) & 0xFF; \
|
||||
mem[addr + 3] = ((value) >> 24) & 0xFF; \
|
||||
} while (0)
|
||||
|
||||
#define MATH_OP(type, op) \
|
||||
do { \
|
||||
DECODE_A(instruction) \
|
||||
locals[dest] = ((type)locals[src1] op (type)locals[src2]); \
|
||||
frame->local[dest] = ((type)frame->local[src1] op (type)frame->local[src2]); \
|
||||
return true; \
|
||||
} while (0)
|
||||
|
||||
#define MATH_OP_NO_CAST(op) \
|
||||
do { \
|
||||
DECODE_A(instruction) \
|
||||
locals[dest] = (locals[src1] op locals[src2]); \
|
||||
frame->local[dest] = (frame->local[src1] op frame->local[src2]); \
|
||||
return true; \
|
||||
} while (0)
|
||||
|
||||
|
|
@ -179,9 +196,9 @@ extern u8 *mem; /* memory */
|
|||
DECODE_A(instruction) \
|
||||
i32 cond; \
|
||||
u32 mask; \
|
||||
u32 target = locals[dest]; \
|
||||
type value = (type)locals[src1]; \
|
||||
type value2 = (type)locals[src2]; \
|
||||
u32 target = frame->local[dest]; \
|
||||
type value = (type)frame->local[src1]; \
|
||||
type value2 = (type)frame->local[src2]; \
|
||||
cond = !!(value op value2); \
|
||||
mask = -(u32)cond; \
|
||||
pc = (target & mask) | (pc & ~mask); \
|
||||
|
|
@ -191,6 +208,5 @@ extern u8 *mem; /* memory */
|
|||
extern bool init_vm();
|
||||
extern u32 syscall(u32 id, u32 args, u32 mem_ptr);
|
||||
bool step_vm();
|
||||
u32 str_alloc(char *str, u32 length);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue