diff --git a/docs/SPECIFICATION.org b/docs/SPECIFICATION.org
index 78caba1..25f58d5 100644
--- a/docs/SPECIFICATION.org
+++ b/docs/SPECIFICATION.org
@@ -33,7 +33,7 @@ type «token» {
}
}
-/* example */
+! example
type Vec3 {
init(x real, y real, z real) {
this.x = x;
@@ -128,7 +128,7 @@ described in "tunnel" section
:END:
The following is a list of global operators and their effect:
-- //
+- !
- comment
- ??
- unwrap or
@@ -225,8 +225,8 @@ also used for letting constants
coerces a type as another type if possible
#+begin_src zre
-let «token» = 0; // default is int
-some_functon(«token» as real); // needs a real
+let «token» = 0; ! default is int
+some_functon(«token» as real); ! needs a real
#+end_src zre
=in=
@@ -308,7 +308,7 @@ connected tunnel
filesystem or through the graph
#+begin_src zre
-/* client */
+! client
let endpoint = Client("tcp://path/to/source");
let tunnel = endpoint.attach(user, auth);
let data = tunnel.open("/some/resource").read();
@@ -316,7 +316,7 @@ std.write(data);
data.flush();
endpoint.clunk();
-/* server */
+! server
let server = Server("tcp://0.0.0.0:25565");
s.bind("/some/resource", fn () str {
return "hello world";
@@ -404,9 +404,9 @@ Error handling is much like in C/C++ where a try catch can be used.
#+begin_src zre
let rr = nil;
-let var = rr ?? 0; /* value is 0 */
+let var = rr ?? 0; ! value is 0
try {
- let other_var = 1 / rr; /* will panic */
+ let other_var = 1 / rr; ! will panic
} catch (e) {
print("Caught error ${e}");
@@ -455,7 +455,7 @@ use "./some_local_file.zre"
:CUSTOM_ID: assertion
:END:
#+begin_src zre
-assert(«expression», «expected output») /* returns «error or none» */
+assert(«expression», «expected output») ! returns «error or none»
#+end_src zre
** Measurements
diff --git a/docs/project-syntax-example/src/client.zre b/docs/project-syntax-example/src/client.zre
index 30d9fb5..1e5984c 100644
--- a/docs/project-syntax-example/src/client.zre
+++ b/docs/project-syntax-example/src/client.zre
@@ -1,18 +1,18 @@
use "common.ztl";
fn main(argc real, argv str[]) {
- let screen_width = 800; /* implied uint32 */
- let screen_height = 450; /* implied uint32 */
+ let screen_width = 800; ! implied uint32
+ let screen_height = 450; ! implied uint32
- let username = argv[0]; /* implied str */
- let password = argv[1]; /* implied str */
+ let username = argv[0]; ! implied str
+ let password = argv[1]; ! implied str
- let me = Player(username, Vec3(0.0, 1.0, 2.0), PURPLE); /* implied Player struct */
+ let me = Player(username, Vec3(0.0, 1.0, 2.0), PURPLE); ! implied Player struct
let running = true;
while (running) {
window("zwl client", screen_width, screen_height) {
- splitbox(parent.size * 0.25) {
+ splitbox(parent.size 0.25) {
canvas("2D") {
if (button("logout")) {
me.logout();
@@ -20,7 +20,7 @@ fn main(argc real, argv str[]) {
}
}
}
- splitbox(parent.size * 0.75) {
+ splitbox(parent.size 0.75) {
canvas("3D") {
model(Floor(Vec3(0, 0, 0), 30));
me.update();
diff --git a/docs/project-syntax-example/src/common.zre b/docs/project-syntax-example/src/common.zre
index f133264..478dadf 100644
--- a/docs/project-syntax-example/src/common.zre
+++ b/docs/project-syntax-example/src/common.zre
@@ -1,11 +1,11 @@
-/**
- * Note that these look like classes but act like structs
- * the methods actually have a implied struct as their first argument
- */
+!!
+! Note that these look like classes but act like structs
+! the methods actually have a implied struct as their first argument
+!!
-/**
- * Camera .
- */
+!!
+! Camera .
+!!
type Camera {
init(pos Vec3, look Vec3) {
this.setting = "CAMERA_PERSPECTIVE";
@@ -16,9 +16,9 @@ type Camera {
}
}
-/**
- * Player .
- */
+!!
+! Player .
+!!
type Player {
init(username str, pos Vec3, color Color) {
this.server = Client("tcp://localhost:25565");
diff --git a/src/compiler.c b/src/compiler.c
new file mode 100644
index 0000000..e15ddcc
--- /dev/null
+++ b/src/compiler.c
@@ -0,0 +1,148 @@
+/**
+ * ZRE - A lightweight: portable programming language for permacomputing.
+ * Copyright (C) 2025 zongor
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation: either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful:
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not: see .
+ */
+
+#include "compiler.h"
+#include "parser.h"
+
+#define MEMORY_SIZE 1024
+
+Code *demo_add_compile() {
+ Code *code = (Code *)malloc(sizeof(Code));
+ Word memory[MEMORY_SIZE] = {0}; /* Memory array */
+ code->memory = memory;
+ code->size = MEMORY_SIZE;
+
+ int i = 1;
+ memory[0].c[0] = 0;
+ memory[0].c[1] = 'z';
+ memory[0].c[2] = 'r';
+ memory[0].c[3] = 'e';
+ memory[i++].u = OP_ADD_REAL;
+ memory[i++].u = 102;
+ memory[i++].u = 103;
+ memory[i++].u = 103;
+ memory[i++].u = OP_SUB_UINT;
+ memory[i++].u = 100;
+ memory[i++].u = 101;
+ memory[i++].u = 100;
+ memory[i++].u = OP_JGT_UINT;
+ memory[i++].u = 100;
+ memory[i++].u = 99;
+ memory[i++].u = 1;
+ memory[i++].u = OP_REAL_TO_STRING;
+ memory[i++].u = 103;
+ memory[i++].u = 1;
+ memory[i++].u = 200;
+ memory[i++].u = OP_PRINT_STRING;
+ memory[i++].u = 201;
+ memory[i++].u = 1;
+ memory[i++].u = 1;
+ memory[i++].u = OP_REAL_TO_UINT;
+ memory[i++].u = 103;
+ memory[i++].u = 1;
+ memory[i++].u = 103;
+ memory[i++].u = OP_UINT_TO_STRING;
+ memory[i++].u = 103;
+ memory[i++].u = 1;
+ memory[i++].u = 104;
+ memory[i++].u = OP_PRINT_STRING;
+ memory[i++].u = 105;
+ memory[i++].u = 1;
+ memory[i++].u = 1;
+ memory[i++].u = OP_READ_STRING;
+ memory[i++].u = 1;
+ memory[i++].u = 1;
+ memory[i++].u = 109;
+ memory[i++].u = OP_PRINT_STRING;
+ memory[i++].u = 110;
+ memory[i++].u = 1;
+ memory[i++].u = 1;
+ memory[i++].u = OP_HALT;
+ memory[i++].u = 0;
+ memory[i++].u = 0;
+ memory[i++].u = 0;
+ memory[99].u = 0;
+ memory[100].u = 5;
+ memory[101].u = 1;
+ memory[102].q = FLOAT_TO_Q16_16(5.0f);
+ memory[103].q = FLOAT_TO_Q16_16(5.0f);
+
+ return code;
+}
+
+Code *compile(char *buffer) {
+ Code *code = (Code *)malloc(sizeof(Code));
+ Word memory[MEMORY_SIZE] = {0}; /* Memory array */
+ code->memory = memory;
+ code->size = MEMORY_SIZE;
+
+ /* char number[100]; */
+ memory[0].c[0] = 0;
+ memory[0].c[1] = 'z';
+ memory[0].c[2] = 'r';
+ memory[0].c[3] = 'e';
+
+ new_Tokenizer(buffer);
+ Token t = nextToken();
+ do {
+ debug_printToken(t);
+ switch (t.type) {
+ case TOKEN_PRINT:
+ case TOKEN_ADD:
+ case TOKEN_INT:
+ /* strncpy(number, t.start, t.length); */
+ /* number[t.length + 1] = '\0'; */
+ /* int i = strtol(number, NULL, 10); */
+ /* memory[ptr].i = i; */
+ /* break; */
+ case TOKEN_LEFT_PAREN:
+ case TOKEN_RIGHT_PAREN:
+ case TOKEN_LEFT_BRACE:
+ case TOKEN_RIGHT_BRACE:
+ case TOKEN_IDENTIFIER:
+ case TOKEN_STRING:
+ case TOKEN_FLOAT:
+ case TOKEN_UINT:
+ case TOKEN_SUB:
+ case TOKEN_MUL:
+ case TOKEN_DIV:
+ case TOKEN_FALSE:
+ case TOKEN_TRUE:
+ case TOKEN_EOF:
+ case TOKEN_ERROR:
+ case TOKEN_MOD:
+ case TOKEN_GT:
+ case TOKEN_LT:
+ case TOKEN_EQ:
+ case TOKEN_GE:
+ case TOKEN_LE:
+ case TOKEN_NE:
+ case TOKEN_NULL:
+ case TOKEN_AND:
+ case TOKEN_OR:
+ case TOKEN_XOR:
+ case TOKEN_SHIFTRIGHT:
+ case TOKEN_SHIFTLEFT:
+ default:
+ break;
+ }
+ t = nextToken();
+ } while (t.type != TOKEN_EOF);
+
+ return code;
+}
diff --git a/src/compiler.h b/src/compiler.h
new file mode 100644
index 0000000..25a406b
--- /dev/null
+++ b/src/compiler.h
@@ -0,0 +1,32 @@
+/**
+ * ZRE - A lightweight, portable programming language for permacomputing.
+ * Copyright (C) 2025 zongor
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#ifndef ZRE_COMPILER_H
+#define ZRE_COMPILER_H
+
+#include "opcodes.h"
+
+typedef struct Code Code;
+struct Code {
+ Word *memory;
+ uint32_t size;
+};
+
+Code* demo_add_compile ();
+Code* compile (char *buffer);
+
+#endif
diff --git a/src/main.c b/src/main.c
index 3241da1..654fd74 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,100 +1,46 @@
-/**
- * ZRE - A lightweight, portable programming language for permacomputing.
+/**
+ * ZRE - A lightweight, portable programming language for permacomputing.
* Copyright (C) 2025 zongor
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include "parser.h"
+#include "compiler.h"
#include "debug.h"
#include "vm.h"
#ifdef __EMSCRIPTEN__
#include
#endif
-/* #define MEMORY_SIZE 65536 /\* 64KB memory (adjustable) *\/ */
-#define MEMORY_SIZE 1024
+uint32_t pc = 1; /* Program counter */
-Word memory[MEMORY_SIZE] = {0}; /* Memory array */
-uint32_t pc = 1; /* Program counter */
+Code *code;
void mainloop() {
- pc = step_vm(memory, MEMORY_SIZE, pc);
+ pc = step_vm(code->memory, code->size, pc);
if (pc == 0) {
#ifdef __EMSCRIPTEN__
emscripten_cancel_main_loop(); /* this should "kill" the app. */
#else
- core_dump(memory, MEMORY_SIZE);
+ core_dump(code->memory, code->size);
exit(0);
#endif
}
}
-int main() {
-
- int i = 1;
- memory[0].c[0] = 'z';
- memory[0].c[1] = 'r';
- memory[0].c[2] = 'e';
- memory[0].c[3] = '0';
- memory[i++].u = OP_ADD_REAL;
- memory[i++].u = 102;
- memory[i++].u = 103;
- memory[i++].u = 103;
- memory[i++].u = OP_SUB_UINT;
- memory[i++].u = 100;
- memory[i++].u = 101;
- memory[i++].u = 100;
- memory[i++].u = OP_JGT_UINT;
- memory[i++].u = 100;
- memory[i++].u = 99;
- memory[i++].u = 1;
- memory[i++].u = OP_REAL_TO_STRING;
- memory[i++].u = 103;
- memory[i++].u = 1;
- memory[i++].u = 200;
- memory[i++].u = OP_PRINT_STRING;
- memory[i++].u = 201;
- memory[i++].u = 1;
- memory[i++].u = 1;
- memory[i++].u = OP_REAL_TO_UINT;
- memory[i++].u = 103;
- memory[i++].u = 1;
- memory[i++].u = 103;
- memory[i++].u = OP_UINT_TO_STRING;
- memory[i++].u = 103;
- memory[i++].u = 1;
- memory[i++].u = 104;
- memory[i++].u = OP_PRINT_STRING;
- memory[i++].u = 105;
- memory[i++].u = 1;
- memory[i++].u = 1;
- memory[i++].u = OP_READ_STRING;
- memory[i++].u = 1;
- memory[i++].u = 1;
- memory[i++].u = 109;
- memory[i++].u = OP_PRINT_STRING;
- memory[i++].u = 110;
- memory[i++].u = 1;
- memory[i++].u = 1;
- memory[i++].u = OP_HALT;
- memory[i++].u = 0;
- memory[i++].u = 0;
- memory[i++].u = 0;
- memory[99].u = 0;
- memory[100].u = 5;
- memory[101].u = 1;
- memory[102].q = FLOAT_TO_Q16_16(5.0f);
- memory[103].q = FLOAT_TO_Q16_16(5.0f);
+int main(int argc, char **argv) {
+ code = demo_add_compile();
#ifdef __EMSCRIPTEN__
emscripten_set_main_loop(mainloop, 0, 1);
diff --git a/src/opcodes.h b/src/opcodes.h
new file mode 100644
index 0000000..5473f8a
--- /dev/null
+++ b/src/opcodes.h
@@ -0,0 +1,84 @@
+/**
+ * ZRE - A lightweight, portable programming language for permacomputing.
+ * Copyright (C) 2025 zongor
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#ifndef ZRE_OPCODES_H
+#define ZRE_OPCODES_H
+
+#include "common.h"
+
+typedef union {
+ int32_t i; /* Integers */
+ int32_t q; /* Q16.16 (32-bit fixed-point) */
+ uint32_t u; /* Unsigned integers */
+ char c[4]; /* 4 Byte char array for string packing */
+} Word;
+
+typedef enum {
+ OP_HALT, /* terminate execution */
+ OP_ADD_INT, /* dest = src1 + src2 */
+ OP_SUB_INT, /* dest = src1 - src2 */
+ OP_MUL_INT, /* dest = src1 * src2 */
+ OP_DIV_INT, /* dest = src1 / src2 */
+ OP_JEQ_INT, /* jump to address dest if src1 as int == src2 as int */
+ OP_JGT_INT, /* jump to address dest if src1 as int > src2 as int*/
+ OP_JLT_INT, /* jump to address dest if src1 as int < src2 as int */
+ OP_JLE_INT, /* jump to address dest if src1 as int <= src2 as int */
+ OP_JGE_INT, /* jump to address dest if src1 as int >= src2 as int*/
+ OP_INT_TO_REAL, /* dest = src1 as f32 */
+ OP_ADD_UINT, /* dest = src1 + src2 */
+ OP_SUB_UINT, /* dest = src1 - src2 */
+ OP_MUL_UINT, /* dest = src1 * src2 */
+ OP_DIV_UINT, /* dest = src1 / src2 */
+ OP_JEQ_UINT, /* jump to address dest if src1 as int == src2 as uint */
+ OP_JGT_UINT, /* jump to address dest if src1 as int > src2 as uint*/
+ OP_JLT_UINT, /* jump to address dest if src1 as int < src2 as uint */
+ OP_JLE_UINT, /* jump to address dest if src1 as int <= src2 as uint */
+ OP_JGE_UINT, /* jump to address dest if src1 as int >= src2 as uint*/
+ OP_UINT_TO_REAL, /* dest = src1 as f32 */
+ OP_ADD_REAL, /* dest = src1 + src2 */
+ OP_SUB_REAL, /* dest = src1 - src2 */
+ OP_MUL_REAL, /* dest = src1 * src2 */
+ OP_DIV_REAL, /* dest = src1 / src2 */
+ OP_JEQ_REAL, /* jump to address dest if src1 as real == src2 as real */
+ OP_JGE_REAL, /* jump to address dest if src1 as real >= src2 as real */
+ OP_JGT_REAL, /* jump to address dest if src1 as real > src2 as real */
+ OP_JLT_REAL, /* jump to address dest if src1 as real < src2 as real */
+ OP_JLE_REAL, /* jump to address dest if src1 as real <= src2 as real */
+ OP_REAL_TO_INT, /* dest = src1 as int */
+ OP_REAL_TO_UINT, /* dest = src1 as int */
+ OP_MOV, /* dest = src1 */
+ OP_JMP, /* jump to address src1 unconditionally */
+ OP_INT_TO_STRING, /* dest = src1 as str */
+ OP_UINT_TO_STRING, /* dest = src1 as str */
+ OP_REAL_TO_STRING, /* dest = src1 as str */
+ OP_READ_STRING, /* dest = read as str */
+ OP_PRINT_STRING, /* write src1 to stdout */
+ OP_CMP_STRING, /* dest = src1 as str == src2 as str */
+} Opcode;
+
+/* Constants for Q16.16 */
+#define Q16_16_SCALE (1 << 16)
+#define Q16_16_SCALE_FLOAT 65536.0f
+#define Q16_16_FRACTION_MASK 0x0000FFFF
+#define Q16_16_INT_MASK 0xFFFF0000
+
+/* Convert float to Q16.16 (with rounding) */
+#define FLOAT_TO_Q16_16(f) ((int32_t)((f) * Q16_16_SCALE_FLOAT + 0.5f))
+/* Convert Q16.16 to float */
+#define Q16_16_TO_FLOAT(q) ((float)(q) / Q16_16_SCALE_FLOAT)
+
+#endif
diff --git a/src/parser.c b/src/parser.c
index ec927b7..b32d57e 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -1,17 +1,363 @@
-/**
- * ZRE - A lightweight, portable programming language for permacomputing.
+/**
+ * ZRE - A lightweight, portable programming language for permacomputing.
* Copyright (C) 2025 zongor
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "parser.h"
+
+#define HASHSIZE 150
+static TokenMap *hashtab_Token[HASHSIZE];
+
+unsigned int hash_Token(char *s) {
+ unsigned int hashval;
+ for (hashval = 0; *s != '\0'; s++)
+ hashval = *s + 31 * hashval;
+ return hashval % HASHSIZE;
+}
+
+TokenMap *lookup_Token(char *s) {
+ TokenMap *np;
+ for (np = hashtab_Token[hash_Token(s)]; np != NULL; np = np->next)
+ if (strcmp(s, np->keyword) == 0)
+ return np;
+ return NULL;
+}
+
+TokenType get_Token(char *s) {
+ TokenMap *np;
+ for (np = hashtab_Token[hash_Token(s)]; np != NULL; np = np->next)
+ if (strcmp(s, np->keyword) == 0)
+ return np->token;
+ return TOKEN_IDENTIFIER;
+}
+
+char *strdup(const char *s) {
+ size_t len = strlen(s) + 1;
+ char *copy = malloc(len);
+ if (copy) {
+ memcpy(copy, s, len);
+ }
+ return copy;
+}
+
+TokenMap *put_Token(char *keyword, TokenType token) {
+ TokenMap *np;
+ unsigned int hashval;
+ if ((np = lookup_Token(keyword)) == NULL) {
+ np = (TokenMap *)malloc(sizeof(*np));
+ if (np == NULL || (np->keyword = strdup(keyword)) == NULL)
+ return NULL;
+ hashval = hash_Token(keyword);
+ np->next = hashtab_Token[hashval];
+ hashtab_Token[hashval] = np;
+ }
+ np->token = token;
+ return np;
+}
+
+void new_TokenMap() {
+ put_Token("nil", TOKEN_NULL);
+ put_Token("and", TOKEN_AND);
+ put_Token("or", TOKEN_OR);
+ put_Token("xor", TOKEN_XOR);
+ put_Token("mod", TOKEN_MOD);
+ put_Token("eq", TOKEN_EQ);
+ put_Token("ge", TOKEN_GE);
+ put_Token("lt", TOKEN_LT);
+ put_Token("le", TOKEN_LE);
+ put_Token("ne", TOKEN_NE);
+ put_Token("gt", TOKEN_GT);
+ put_Token("ge", TOKEN_GE);
+ put_Token("srl", TOKEN_SHIFTRIGHT);
+ put_Token("sll", TOKEN_SHIFTLEFT);
+ put_Token("int", TOKEN_INT);
+ put_Token("print", TOKEN_PRINT);
+}
+
+typedef struct Tokenizer Tokenizer;
+struct Tokenizer {
+ char *start;
+ char *current;
+ int32_t line;
+};
+
+Tokenizer tokenizer;
+
+void new_Tokenizer(char *src) {
+ tokenizer.start = src;
+ tokenizer.current = src;
+ tokenizer.line = 1;
+}
+
+static bool isAlpha(char c) {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' ||
+ c == '\'' || c == '?';
+}
+
+static bool isDigit(char c) { return (c >= '0' && c <= '9') || c == '-'; }
+
+static bool isAtEnd() { return *tokenizer.current == '\0'; }
+
+static Token makeToken(TokenType type) {
+ Token token;
+ token.type = type;
+ token.start = tokenizer.start;
+ token.length = (int32_t)(tokenizer.current - tokenizer.start);
+ token.line = tokenizer.line;
+ return token;
+}
+
+static Token errorToken(char *msg) {
+ Token token;
+ token.type = TOKEN_ERROR;
+ token.start = msg;
+ token.length = (int32_t)strlen(msg);
+ token.line = tokenizer.line;
+ return token;
+}
+
+static char advance() {
+ tokenizer.current++;
+ return tokenizer.current[-1];
+}
+
+static char peek() { return *tokenizer.current; }
+
+static char peekNext() {
+ if (isAtEnd())
+ return '\0';
+ return tokenizer.current[1];
+}
+
+static void skipWhitespace() {
+ for (;;) {
+ char c = peek();
+ switch (c) {
+ case ' ':
+ case '\r':
+ case '\t':
+ advance();
+ break;
+ case '\n':
+ tokenizer.line++;
+ advance();
+ break;
+ case '!':
+ while (peek() != '\n' && !isAtEnd())
+ advance();
+ break;
+ default:
+ return;
+ }
+ }
+}
+
+static char *currentTokenToS() {
+ int32_t size = tokenizer.current - tokenizer.start;
+ char *str = (char *)malloc(sizeof(size));
+ strncpy(str, tokenizer.start, size);
+ str[size] = '\0';
+ return str;
+}
+
+static TokenType identifierType() {
+ char *check = currentTokenToS();
+ TokenType t = get_Token(check);
+ free(check);
+ return t;
+}
+
+static Token identifier() {
+ while (isAlpha(peek()) || isDigit(peek()))
+ advance();
+ return makeToken(identifierType());
+}
+
+static Token number() {
+ bool is_float = false;
+ while (isDigit(peek()))
+ advance();
+
+ /* Look for a fractional part. */
+ if (peek() == '.' && isDigit(peekNext())) {
+ is_float = true;
+ /* Consume the ".". */
+ advance();
+
+ while (isDigit(peek()))
+ advance();
+ }
+
+ return makeToken((is_float)
+ ? TOKEN_FLOAT
+ : TOKEN_INT); /* or measure if ends in postscript */
+}
+
+static Token string() {
+ while (peek() != '"' && !isAtEnd()) {
+ if (peek() == '\n')
+ tokenizer.line++;
+ advance();
+ }
+
+ if (isAtEnd())
+ return errorToken("Unterminated string.");
+
+ /* The closing quote. */
+ advance();
+ return makeToken(TOKEN_STRING);
+}
+
+Token nextToken() {
+ skipWhitespace();
+ tokenizer.start = tokenizer.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_LEFT_PAREN);
+ case ')':
+ return makeToken(TOKEN_RIGHT_PAREN);
+ case '{':
+ return makeToken(TOKEN_LEFT_BRACE);
+ case '}':
+ return makeToken(TOKEN_RIGHT_BRACE);
+ case '+':
+ return makeToken(TOKEN_ADD);
+ case '/':
+ return makeToken(TOKEN_DIV);
+ case '-':
+ return makeToken(TOKEN_SUB);
+ case '*':
+ return makeToken(TOKEN_MUL);
+ case ';':
+ return makeToken(TOKEN_SEMICOLON);
+ case '"':
+ return string();
+ }
+
+ return errorToken("Unexpected character.");
+}
+
+void debug_printToken(Token t) {
+ char *str = currentTokenToS();
+
+ switch (t.type) {
+ case TOKEN_LEFT_PAREN:
+ printf("TOKEN_LEFT_PAREN %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_RIGHT_PAREN:
+ printf("TOKEN_RIGHT_PAREN %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_LEFT_BRACE:
+ printf("TOKEN_LEFT_BRACE %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_RIGHT_BRACE:
+ printf("TOKEN_RIGHT_BRACE %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_IDENTIFIER:
+ printf("TOKEN_IDENTIFIER %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_STRING:
+ printf("TOKEN_STRING %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_FLOAT:
+ printf("TOKEN_FLOAT %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_ERROR:
+ printf("TOKEN_ERROR %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_FALSE:
+ printf("TOKEN_FALSE %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_TRUE:
+ printf("TOKEN_TRUE %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_EOF:
+ printf("TOKEN_EOF %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_ADD:
+ printf("TOKEN_ADD %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_SUB:
+ printf("TOKEN_SUB %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_MUL:
+ printf("TOKEN_MUL %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_DIV:
+ printf("TOKEN_DIV %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_MOD:
+ printf("TOKEN_MOD %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_INT:
+ printf("TOKEN_INT %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_UINT:
+ printf("TOKEN_UINT %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_SHIFTRIGHT:
+ printf("TOKEN_SHIFTRIGHT %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_SHIFTLEFT:
+ printf("TOKEN_SHIFTLEFT %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_GT:
+ printf("TOKEN_GT %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_LT:
+ printf("TOKEN_LT %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_EQ:
+ printf("TOKEN_EQ %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_GE:
+ printf("TOKEN_GE %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_LE:
+ printf("TOKEN_LE %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_NE:
+ printf("TOKEN_NE %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_NULL:
+ printf("TOKEN_NULL %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_AND:
+ printf("TOKEN_AND %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_OR:
+ printf("TOKEN_OR %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_XOR:
+ printf("TOKEN_XOR %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_SEMICOLON:
+ printf("TOKEN_SEMICOLON %s line_no=%d\n", str, t.line);
+ break;
+ case TOKEN_PRINT:
+ printf("TOKEN_PRINT %s line_no=%d\n", str, t.line);
+ break;
+ }
+ free(str);
+}
diff --git a/src/parser.h b/src/parser.h
index ec927b7..7a0e1b1 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -15,3 +15,86 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
+#ifndef ZRE_PARSER_H
+#define ZRE_PARSER_H
+
+#include "common.h"
+#include
+
+typedef enum TokenType
+{
+ /* Single char tokens */
+ TOKEN_LEFT_PAREN,
+ TOKEN_RIGHT_PAREN,
+ TOKEN_LEFT_BRACE,
+ TOKEN_RIGHT_BRACE,
+ TOKEN_SEMICOLON,
+ /* Literals */
+ TOKEN_IDENTIFIER,
+ TOKEN_STRING,
+ TOKEN_FLOAT,
+ TOKEN_INT,
+ TOKEN_UINT,
+ /* TOKEN_ARRAY, */
+ /* TOKEN_MAP, */
+ TOKEN_FALSE,
+ TOKEN_TRUE,
+ TOKEN_EOF,
+ TOKEN_ERROR,
+ /* Keywords */
+ TOKEN_ADD,
+ TOKEN_SUB,
+ TOKEN_MUL,
+ TOKEN_DIV,
+ TOKEN_MOD,
+ TOKEN_GT,
+ TOKEN_LT,
+ TOKEN_EQ,
+ TOKEN_GE,
+ TOKEN_LE,
+ TOKEN_NE,
+ TOKEN_NULL,
+ TOKEN_AND,
+ TOKEN_OR,
+ TOKEN_XOR,
+ TOKEN_SHIFTRIGHT,
+ TOKEN_SHIFTLEFT,
+ TOKEN_PRINT,
+} TokenType;
+
+typedef struct Token Token;
+struct Token
+{
+ TokenType type;
+ char *start;
+ int32_t length;
+ int32_t line;
+};
+
+typedef struct SourceCode SourceCode;
+struct SourceCode
+{
+ size_t size;
+ char *buf;
+ int count;
+};
+
+typedef struct TokenMap TokenMap;
+struct TokenMap
+{
+ struct TokenMap* next;
+ char* keyword;
+ TokenType token;
+};
+
+unsigned int hash_Token(char* s);
+TokenMap* lookup_Token(char* s);
+TokenType get_Token(char* s);
+TokenMap* put_Token(char* keyword, TokenType token);
+
+void new_Tokenizer (char *src);
+void new_TokenMap ();
+void debug_printToken (Token t);
+Token nextToken();
+
+#endif
diff --git a/src/vm.h b/src/vm.h
index fdaf8ba..2e8ff93 100644
--- a/src/vm.h
+++ b/src/vm.h
@@ -18,68 +18,7 @@
#ifndef ZRE_VM_H
#define ZRE_VM_H
-#include "common.h"
-
-/* Constants for Q16.16 */
-#define Q16_16_SCALE (1 << 16)
-#define Q16_16_SCALE_FLOAT 65536.0f
-#define Q16_16_FRACTION_MASK 0x0000FFFF
-#define Q16_16_INT_MASK 0xFFFF0000
-
-/* Convert float to Q16.16 (with rounding) */
-#define FLOAT_TO_Q16_16(f) ((int32_t)((f) * Q16_16_SCALE_FLOAT + 0.5f))
-/* Convert Q16.16 to float */
-#define Q16_16_TO_FLOAT(q) ((float)(q) / Q16_16_SCALE_FLOAT)
-
-typedef union {
- int32_t i; /* Integers */
- int32_t q; /* Q16.16 (32-bit fixed-point) */
- uint32_t u; /* Unsigned integers */
- char c[4]; /* 4 Byte char array for string packing */
-} Word;
-
-typedef enum {
- OP_HALT, /* terminate execution */
- OP_ADD_INT, /* dest = src1 + src2 */
- OP_SUB_INT, /* dest = src1 - src2 */
- OP_MUL_INT, /* dest = src1 * src2 */
- OP_DIV_INT, /* dest = src1 / src2 */
- OP_JEQ_INT, /* jump to address dest if src1 as int == src2 as int */
- OP_JGT_INT, /* jump to address dest if src1 as int > src2 as int*/
- OP_JLT_INT, /* jump to address dest if src1 as int < src2 as int */
- OP_JLE_INT, /* jump to address dest if src1 as int <= src2 as int */
- OP_JGE_INT, /* jump to address dest if src1 as int >= src2 as int*/
- OP_INT_TO_REAL, /* dest = src1 as f32 */
- OP_ADD_UINT, /* dest = src1 + src2 */
- OP_SUB_UINT, /* dest = src1 - src2 */
- OP_MUL_UINT, /* dest = src1 * src2 */
- OP_DIV_UINT, /* dest = src1 / src2 */
- OP_JEQ_UINT, /* jump to address dest if src1 as int == src2 as uint */
- OP_JGT_UINT, /* jump to address dest if src1 as int > src2 as uint*/
- OP_JLT_UINT, /* jump to address dest if src1 as int < src2 as uint */
- OP_JLE_UINT, /* jump to address dest if src1 as int <= src2 as uint */
- OP_JGE_UINT, /* jump to address dest if src1 as int >= src2 as uint*/
- OP_UINT_TO_REAL, /* dest = src1 as f32 */
- OP_ADD_REAL, /* dest = src1 + src2 */
- OP_SUB_REAL, /* dest = src1 - src2 */
- OP_MUL_REAL, /* dest = src1 * src2 */
- OP_DIV_REAL, /* dest = src1 / src2 */
- OP_JEQ_REAL, /* jump to address dest if src1 as real == src2 as real */
- OP_JGE_REAL, /* jump to address dest if src1 as real >= src2 as real */
- OP_JGT_REAL, /* jump to address dest if src1 as real > src2 as real */
- OP_JLT_REAL, /* jump to address dest if src1 as real < src2 as real */
- OP_JLE_REAL, /* jump to address dest if src1 as real <= src2 as real */
- OP_REAL_TO_INT, /* dest = src1 as int */
- OP_REAL_TO_UINT, /* dest = src1 as int */
- OP_MOV, /* dest = src1 */
- OP_JMP, /* jump to address src1 unconditionally */
- OP_INT_TO_STRING, /* dest = src1 as str */
- OP_UINT_TO_STRING, /* dest = src1 as str */
- OP_REAL_TO_STRING, /* dest = src1 as str */
- OP_READ_STRING, /* dest = read as str */
- OP_PRINT_STRING, /* write src1 to stdout */
- OP_CMP_STRING, /* dest = src1 as str == src2 as str */
-} Opcode;
+#include "opcodes.h"
uint32_t step_vm(Word *memory, uint32_t memory_size, uint32_t pc);
diff --git a/test/add.ztl b/test/add.zre
similarity index 100%
rename from test/add.ztl
rename to test/add.zre
diff --git a/test/loop.ztl b/test/loop.zre
similarity index 100%
rename from test/loop.ztl
rename to test/loop.zre