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