Compare commits

...

12 Commits

Author SHA1 Message Date
be75ccd6f8 parser: apparently i don't know how realloc works 2026-06-02 21:04:34 +02:00
269d2c8ae9 documented the vm more 2026-05-27 15:43:13 +02:00
20f9ee0a14 basic VM stuff 2026-05-26 20:36:14 +02:00
8e8b01b7d9 main.c: remake cli tool, helper.c: add strcmp, use builtins in some paths
Co-authored-by: tema5002 <tema5002@tuta.io>
Reviewed-on: #4
Co-committed-by: tema5002 <tema5002@tuta.io>
2026-05-26 18:37:00 +02:00
4fbb006072 build.lua: changes 2026-05-24 14:23:12 +02:00
09ac2a17f9 build.lua: allow fail on rmdir, rm -rf to ignore nonexistent 2026-05-24 14:12:46 +02:00
6d57f1fe6b build.lua: assert to suppress lsp warning
lol
2026-05-24 12:45:35 +02:00
2a22544464 error: add the missing errors and some nitpicks 2026-05-24 12:43:37 +02:00
fbbe2e8285 error: add proper error strings
Co-authored-by: tema5002 <tema5002@tuta.io>
Reviewed-on: #3
Co-committed-by: tema5002 <tema5002@tuta.io>
2026-05-24 12:08:07 +02:00
0d6b1c6729 build.lua: add clean & run for first argument
Co-authored-by: tema5002 <tema5002@tuta.io>
Reviewed-on: #2
Co-committed-by: tema5002 <tema5002@tuta.io>
2026-05-23 21:00:20 +02:00
9acff5f893 main, parser: fix memory leak/unitialized mem 2026-05-23 20:28:56 +02:00
bb73054e2c updated gitignore 2026-05-23 20:17:26 +02:00
14 changed files with 665 additions and 111 deletions

5
.gitignore vendored
View File

@@ -1,4 +1,7 @@
/build /build
/.zig-cache /.zig-cache
/zig-out /zig-out
noom .idea
/noom
/noom.*
Makefile

47
build.lua Normal file → Executable file
View File

@@ -1,5 +1,5 @@
#!/usr/bin/env lua
-- i love build scripts i love build scripts i love build scripts scripts build love i script build love me i love script build i script love build -- i love build scripts i love build scripts i love build scripts scripts build love i script build love me i love script build i script love build
local isBlendi = os.getenv("USER") == "blendi" local isBlendi = os.getenv("USER") == "blendi"
local separator = package.config:sub(1,1) local separator = package.config:sub(1,1)
@@ -13,9 +13,12 @@ local function filename(path)
return path:sub(s,e) return path:sub(s,e)
end end
local function runCommand(cmd) local function runCommand(cmd, ignore_fail)
print("> " .. cmd) print("> " .. cmd)
return os.execute(cmd) local result = os.execute(cmd)
if (result ~= true and result ~= 0) and (not ignore_fail) then
os.exit(1)
end
end end
local function fixPath(path) local function fixPath(path)
@@ -28,17 +31,43 @@ local function fixPath(path)
return new return new
end end
if arg[1] == "clean" then
if separator == '\\' then
runCommand("rmdir /s /q build 2>nul", true)
else
runCommand("rm -rf build")
end
return
end
local needsDir = false
if separator == '\\' then
needsDir = true
else
-- TODO: maybe use exit code instead of the first return value?
local ok, _, code = os.execute('test -d build');
local exists = (ok == 0 or (ok == true and code == 0))
needsDir = not exists;
end
if needsDir then
if separator == '\\' then if separator == '\\' then
runCommand('mkdir build 2>nul') runCommand('mkdir build 2>nul')
else else
runCommand('mkdir -p build') runCommand('mkdir -p build')
end end
end
local files = { local files = {
'src/error.c',
'src/helper.c', 'src/helper.c',
'src/lexer.c', 'src/lexer.c',
'src/parser.c', 'src/parser.c',
'src/compiler.c', 'src/compiler.c',
'src/vm.c',
'src/main.c', 'src/main.c',
} }
@@ -47,7 +76,7 @@ local objects = {}
local coolArgs = {} local coolArgs = {}
local function getTime(path) local function getTime(path)
local handle = io.popen('stat -c %Y "' .. path .. '" 2>/dev/null') local handle = assert(io.popen('stat -c %Y "' .. path .. '" 2>/dev/null'))
local result = handle:read("*a") local result = handle:read("*a")
handle:close() handle:close()
return tonumber(result) or 0 return tonumber(result) or 0
@@ -73,14 +102,18 @@ for i = 1,#files do
if needsRebuild(fname, out) then if needsRebuild(fname, out) then
needsLinking = true needsLinking = true
runCommand('clang -c -o ' .. out .. ' ' .. fname .. ' ' .. table.concat(coolArgs, ' ')) runCommand('clang -g -c -o ' .. out .. ' ' .. fname .. ' ' .. table.concat(coolArgs, ' '))
end end
objects[#objects+1] = out; objects[#objects+1] = out;
end end
local exe = separator == '\\' and "noom.exe" or "noom" local exe = separator == '\\' and ".\\noom.exe" or "./noom"
if needsLinking then if needsLinking then
runCommand('clang -o ' .. exe .. ' ' .. table.concat(objects, ' ') .. ' ' .. table.concat(coolArgs, ' ')) runCommand('clang -g -o ' .. exe .. ' ' .. table.concat(objects, ' ') .. ' ' .. table.concat(coolArgs, ' '))
end
if arg[1] == "run" then
runCommand(exe)
end end

View File

@@ -21,6 +21,7 @@ pub fn build(b: *std.Build) void {
"src/lexer.c", "src/lexer.c",
"src/parser.c", "src/parser.c",
"src/compiler.c", "src/compiler.c",
"src/vm.c",
"src/main.c", "src/main.c",
} }
}); });

1
shitass.lua Normal file
View File

@@ -0,0 +1 @@
local t <close> = {6; 2}

159
src/error.c Normal file
View File

@@ -0,0 +1,159 @@
#include "error.h"
#include "helper.h"
noom_uint_t noom_format_error(const noomP_Parser* parser, char* buffer, noom_uint_t buffer_size) {
struct noom_error {
const char* s;
int near; // 0:none 1: near '%s'\n 2: '%s'\n
};
static const struct noom_error parser_errors[] = {
[NOOMP_ERROR_NONE] = {0, 0},
[NOOMP_ERROR_OOM] = {"Whoops! Out of memory :(\n", 0},
[NOOMP_ERROR_EXPECTED_LCURLY] = {"expected '{'", 1},
[NOOMP_ERROR_EXPECTED_RCURLY] = {"expected '}' to close table literal", 1},
[NOOMP_ERROR_EXPECTED_RBRACKET_AFTER_KEY] = {"expected ']' to close computed key", 1},
[NOOMP_ERROR_EXPECTED_EQUALS_AFTER_KEY] = {"expected '=' after table key", 1},
[NOOMP_ERROR_EXPECTED_FIELD_IDENTIFIER] = {"expected identifier after '.' for field access", 1},
[NOOMP_ERROR_EXPECTED_METHOD_CALL] = {"expected identifier after ':' for method call", 1},
[NOOMP_ERROR_EXPECTED_RBRACKET_AFTER_INDEX] = {"expected ']' after index expression", 1},
[NOOMP_ERROR_EXPECTED_RPAREN_AFTER_EXPRESSION] = {"expected ')' after expression", 1},
[NOOMP_ERROR_EXPECTED_LPAREN_FOR_PARAMETERS] = {"expected '(' for function parameters", 1},
[NOOMP_ERROR_EXPECTED_RPAREN_FOR_PARAMETERS] = {"expected ')' for function parameters", 1},
[NOOMP_ERROR_EXPECTED_IDENTIFIER_AFTER_LOCAL_FUNCTION] = {"expected identifier after 'local function'\n", 0},
[NOOMP_ERROR_EXPECTED_IDENTIFIER_AFTER_FUNCTION] = {"expected identifier after 'function'\n", 0},
[NOOMP_ERROR_EXPECTED_END_AFTER_LOCAL_FUNCTION] = {"expected 'end' to close local function declaration", 1},
[NOOMP_ERROR_EXPECTED_END_AFTER_FUNCTION] = {"expected 'end' to close function declaration", 1},
[NOOMP_ERROR_EXPECTED_IDENTIFIER_AFTER_LOCAL] = {"expected identifier after 'local'\n", 0},
[NOOMP_ERROR_EXPECTED_IDENTIFIER_AFTER_LANGLE] = {"expected identifier after '<' for attribute\n", 0},
[NOOMP_ERROR_EXPECTED_RANGLE_TO_CLOSE_ATTRIBUTE] = {"expected '>' to close attribute after identifier", 1},
[NOOMP_ERROR_EXPECTED_THEN_AFTER_EXPRESSION] = {"expected 'then' after expression", 1},
[NOOMP_ERROR_EXPECTED_ELSE_ELSEIF_OR_END] = {"expected 'else', 'elseif', or 'end'", 1},
[NOOMP_ERROR_EXPECTED_END_AFTER_IF] = {"expected 'end' to close if statement", 1},
[NOOMP_ERROR_EXPECTED_DO_AFTER_EXPRESSION] = {"expected 'do' after expression", 1},
[NOOMP_ERROR_EXPECTED_END_AFTER_WHILE] = {"expected 'end' to close while statement", 1},
[NOOMP_ERROR_EXPECTED_END_AFTER_FOR] = {"expected 'end' to close for statement", 1},
[NOOMP_ERROR_EXPECTED_END_AFTER_DO] = {"expected 'end' to close do statement", 1},
[NOOMP_ERROR_EXPECTED_IDENTIFIER_AFTER_FOR] = {"expected identifier after 'for'\n", 1},
[NOOMP_ERROR_EXPECTED_VARIABLE_AFTER_COMMA_IN_FOR] = {"expected variable name after ',' in for loop\n", 0},
[NOOMP_ERROR_EXPECTED_IN] = {"expected 'in'", 1},
[NOOMP_ERROR_EXPECTED_UNTIL] = {"expected 'until' to close repeat expression", 1},
[NOOMP_ERROR_EXPECTED_IDENTIFIER_AFTER_GOTO] = {"expected identifier after goto\n", 0},
[NOOMP_ERROR_EXPECTED_COLONCOLON] = {"expected :: to end label identifier", 1},
[NOOMP_ERROR_INVALID_STATEMENT] = {"expected statement, got", 2},
// I want someone smarter than me [tema5002] to give these a proper description
// ^ alrighty then
[NOOMP_ERROR_EXPECTED_ASSIGNABLE] = {"expected assignable expression after comma in assignment", 1},
[NOOMP_ERROR_NOT_ASSIGNABLE] = {"expression in assignment is not assignable", 1},
[NOOMP_ERROR_EXPECTED_COMMA_OR_EQUAL_IN_ASSIGNMENT] = {"expected a comma or equals after assignable in assignment", 1},
[NOOMP_ERROR_EXPRESSION_NOT_STATEMENT] = {"loose expression is not a valid statement", 1},
[NOOMP_ERROR_FAKEATTRIBUTE] = {"invalid attribute", 2},
[NOOMP_ERROR_RETURN_NOT_END] = {"'return' must be the last statement in a block\n", 0},
[NOOMP_ERROR_FOR_WRONG_AMOUNT] = {"'for' initializer must have 2 or 3 expressions\n", 0}
};
static const struct noom_error lexer_errors[] = {
[NOOML_ERROR_NONE] = {0, 0},
[NOOML_ERROR_UNKNOWN] = {"unknown token", 1},
[NOOML_ERROR_MALFORMED_NUM] = {"malformed number", 1},
[NOOML_ERROR_UNFINISHED_COMMENT] = {"unfinished comment", 1},
[NOOML_ERROR_UNFINISHED_STRING] = {"unfinished string", 1},
[NOOML_ERROR_UNFINISHED_LONG_STRING] = {"unfinished long string", 1},
[NOOML_ERROR_DECIMAL_ESCAPE_TOO_BIG] = {"decimal escape sequence too big (max 255)", 1},
[NOOML_ERROR_HEX_ESCAPE_INVALID] = {"invalid hexadecimal escape sequence", 1},
[NOOML_ERROR_UNICODE_ESCAPE_UNOPENED] = {"expected '{' after '\\u' for Unicode escape", 1},
[NOOML_ERROR_UNICODE_ESCAPE_UNCLOSED] = {"expected '}' to close Unicode escape sequence", 1},
[NOOML_ERROR_UNICODE_ESCAPE_TOO_BIG] = {"Unicode escape sequence exceeds maximum (0x10FFFF)", 1},
};
noomP_Error base_err = parser->error_state;
noom_uint_t lexer_code = 0;
noom_uint_t pos = 0;
if (parser->error_state & NOOMP_ERROR_LEXER) {
base_err = NOOMP_ERROR_LEXER;
lexer_code = parser->error_state & ~NOOMP_ERROR_LEXER;
}
struct noom_error err = (base_err == NOOMP_ERROR_LEXER) ? lexer_errors[lexer_code] : parser_errors[base_err];
noom_uint_t row = 1, column = 1;
for (noom_uint_t i = 0; i < parser->lex_offset; i++) {
if (parser->code[i] == '\n') {
row++;
column = 1;
}
else {
column++;
}
}
if (buffer == 0) {
noom_uint_t linedig = 0;
for (noom_uint_t n = row; n; n /= 10, linedig++);
noom_uint_t space = 0;
space =
sizeof("noom: ") - 1 +
noom_strlen(parser->filename) +
sizeof(":") - 1 +
linedig +
sizeof(":") - 1 +
noom_strlen(err.s) +
+ 1; // \0;
if (err.near) {
space += (
(err.near == 1 ? sizeof(" near") - 1 : 0) +
sizeof(" '") - 1 +
parser->last_token_length +
sizeof("'\n") - 1
);
}
return space;
}
noom_safe_strcpy(buffer, &pos, buffer_size, "noom: ");
noom_safe_strcpy(buffer, &pos, buffer_size, parser->filename);
noom_safe_strcpy(buffer, &pos, buffer_size, ":");
char num_buf[20];
noom_uint_t num_len = 0;
if (row == 0) {
num_buf[num_len++] = '0';
} else {
noom_uint_t temp = row;
noom_uint_t divisor = 1;
while (temp / divisor >= 10) divisor *= 10;
while (divisor > 0) {
num_buf[num_len++] = '0' + (temp / divisor);
temp %= divisor;
divisor /= 10;
}
}
for (noom_uint_t i = 0; i < num_len && pos < buffer_size - 1; i++) {
buffer[pos++] = num_buf[i];
}
noom_safe_strcpy(buffer, &pos, buffer_size, ":");
noom_safe_strcpy(buffer, &pos, buffer_size, err.s);
if (err.near) {
if (err.near == 1) {
noom_safe_strcpy(buffer, &pos, buffer_size, " near");
}
noom_safe_strcpy(buffer, &pos, buffer_size, " '");
for (noom_uint_t i = 0; i < parser->last_token_length && pos < buffer_size - 1; i++) {
buffer[pos++] = parser->code[parser->last_token_offset + i];
}
noom_safe_strcpy(buffer, &pos, buffer_size, "'\n");
}
if (pos < buffer_size) buffer[pos] = '\0';
return pos;
}

6
src/error.h Normal file
View File

@@ -0,0 +1,6 @@
// js let me use include guards 🥀🥀🥀🥀🥀🥀🥀
// ^ header guards r 4 noobz!
#include "types.h"
#include "parser.h"
noom_uint_t noom_format_error(const noomP_Parser* parser, char* buffer, noom_uint_t buffer_size);

View File

@@ -2,6 +2,11 @@
#include "types.h" #include "types.h"
int noom_startswith(const char* str, const char* compare) { int noom_startswith(const char* str, const char* compare) {
#ifdef __has_builtin
#if __has_builtin(__builtin_strncmp)
return __builtin_strncmp(compare, str, noom_strlen(compare)) == 0;
#endif
#endif
noom_uint_t i = 0; noom_uint_t i = 0;
while (1) { while (1) {
if (compare[i] == '\0') return 1; // we did it if (compare[i] == '\0') return 1; // we did it
@@ -21,6 +26,33 @@ int noom_streql(const char* stra, noom_uint_t lena, const char* strb, noom_uint_
return 1; return 1;
} }
noom_uint_t noom_strlen(const char *s) {
#ifdef __has_builtin
#if __has_builtin(__builtin_strlen)
return __builtin_strlen(s);
#endif
#endif
const char *a = s;
while (*s) s++;
return s - a;
}
int noom_strcmp(const char *a, const char *b) {
#ifdef __has_builtin
#if __has_builtin(__builtin_strcmp)
return __builtin_strcmp(a, b);
#endif
#endif
for (; *a && *a == *b; a++, b++);
return *(const unsigned char*)a - *(const unsigned char*)b;
}
void noom_safe_strcpy(char* buffer, noom_uint_t* pos, noom_uint_t buffer_size, const char* src) {
while (*src && *pos < buffer_size - 1) {
buffer[(*pos)++] = *src++;
}
}
#include <stdlib.h> // TODO: remove #include <stdlib.h> // TODO: remove
void* noom_alloc(noom_uint_t size) { void* noom_alloc(noom_uint_t size) {

View File

@@ -2,6 +2,9 @@
int noom_startswith(const char* str, const char* compare); int noom_startswith(const char* str, const char* compare);
int noom_streql(const char* stra, noom_uint_t lena, const char* strb, noom_uint_t lenb); // rename to something better? int noom_streql(const char* stra, noom_uint_t lena, const char* strb, noom_uint_t lenb); // rename to something better?
noom_uint_t noom_strlen(const char *s);
int noom_strcmp(const char *a, const char* b);
void noom_safe_strcpy(char* buffer, noom_uint_t* pos, noom_uint_t buffer_size, const char* src);
void* noom_alloc(noom_uint_t size); void* noom_alloc(noom_uint_t size);
void noom_free(void* ptr); void noom_free(void* ptr);

View File

@@ -1,7 +1,7 @@
#include <stdio.h> // for now #include <stdio.h> // for now
// #include "lexer.h"
#include "parser.h"
#include "helper.h" #include "helper.h"
#include "error.h"
#include "types.h"
void tab(noom_uint_t amount) { void tab(noom_uint_t amount) {
amount *= 2; amount *= 2;
@@ -10,7 +10,7 @@ void tab(noom_uint_t amount) {
} }
} }
void print_node(noomP_Node* node, noom_uint_t depth) { void print_node(const noomP_Node *node, noom_uint_t depth) {
tab(depth); tab(depth);
printf("{\n"); printf("{\n");
@@ -31,39 +31,65 @@ void print_node(noomP_Node* node, noom_uint_t depth) {
printf("}\n"); printf("}\n");
} }
int main(int argc, char** argv) { int the_theoretical_function_to_execute_your_code_that_should_be_replaced_later(const char *code, const char *filename) {
// uhh uhhh uhhhhh noomP_Parser parser;
const char* code = "local t = {'a'; 2; 6}"; noomP_Node *program;
// goodbye "shitass" you will be missed
int success = noomP_parse(code, filename, NOOM_VERSION_54, &program, &parser);
if (success == 0) {
puts("LEX OUTPUT:");
fputs("\x1b[48;2;10;10;10m", stdout);
noom_uint_t pos = 0; noom_uint_t pos = 0;
printf("LEX OUTPUT:\n");
noomL_Token token;
while (1) { while (1) {
noomL_lex(code, pos, &token, NOOM_VERSION_54); noomL_Token token;
noomL_ErrorType err = noomL_lex(code, pos, &token, NOOM_VERSION_54);
if (err) break;
if (token.type == NOOML_TOKEN_KEYWORD) {
fputs("\x1b[38;2;207;142;109m", stdout);
for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]);
} else if (token.type == NOOML_TOKEN_WHITESPACE) {
for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]);
} else if (token.type == NOOML_TOKEN_IDENTIFIER) {
fputs("\x1b[38;2;255;255;255m", stdout);
for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]);
} else if (token.type == NOOML_TOKEN_SYMBOL) {
fputs("\x1b[38;2;0;255;255m", stdout);
for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]);
} else if (token.type == NOOML_TOKEN_STRING) {
fputs("\x1b[38;2;255;0;0m", stdout);
for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]);
} else if (token.type == NOOML_TOKEN_NUMBER) {
fputs("\x1b[38;2;0;255;0m", stdout);
for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]);
} else {
fputs("\x1b[0m\n", stdout);
printf("%s ", noomL_formatTokenType(token.type)); printf("%s ", noomL_formatTokenType(token.type));
for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]); for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]);
fputs("\x1b[48;2;10;10;10m", stdout);
putchar('\n'); putchar('\n');
}
pos += token.length; pos += token.length;
if (token.type == NOOML_TOKEN_EOF) break; if (token.type == NOOML_TOKEN_EOF) break;
} }
puts("\x1b[0m");
// time for parser testing puts("PARSE OUTPUT:");
printf("\nPARSE OUTPUT:\n");
noomP_Node *program;
noomP_Node *last_node;
int success = noomP_parse(code, "shitass", NOOM_VERSION_54, &program, &last_node);
if (success != 0) return success;
print_node(program, 0); print_node(program, 0);
}
else {
noom_uint_t bleh = noom_format_error(&parser, NULL, 0);
char* buf = noom_alloc(bleh);
noom_format_error(&parser, buf, bleh);
fputs(buf, stdout);
noom_free(buf);
}
// freeing time // freeing time
noomP_Node *last_node = parser.last_node;
while (last_node) { while (last_node) {
noomP_Node *next = last_node->previous_node; noomP_Node *next = last_node->previous_node;
// subnodes could be null if we OOM'd during a realloc of it // subnodes could be null if we OOM'd during a realloc of it
@@ -71,6 +97,169 @@ int main(int argc, char** argv) {
noom_free(last_node); noom_free(last_node);
last_node = next; last_node = next;
} }
return success;
}
static char* read_file(const char* filename) {
FILE* file = fopen(filename, "r");
if (file == 0) {
fprintf(stderr, "Failed to open '%s'.\n", filename);
return 0; return 0;
} }
fseek(file, 0, SEEK_END);
const unsigned long filesize = ftell(file);
fseek(file, 0, SEEK_SET);
char* buffer = noom_alloc(filesize + 1);
if (fread(buffer, 1, filesize, file) != filesize) {
fprintf(stderr, "Reached the end of the file\n");
return 0;
}
buffer[filesize] = '\0';
fclose(file);
return buffer;
}
static char* read_stdin() {
noom_uint_t capacity = 4096;
noom_uint_t size = 0;
char* buffer = noom_alloc(capacity);
size_t n;
while ((n = fread(buffer + size, 1, capacity - size, stdin)) > 0) {
size += n;
if (size == capacity) {
capacity *= 2;
buffer = noom_realloc(buffer, capacity);
}
}
buffer[size] = '\0';
return buffer;
}
// code stolen from my different project
static int read_prompt(char* buf, int buf_size, char* prompt, const int required) {
do {
printf("%s", prompt);
fflush(stdout);
if (!fgets(buf, buf_size, stdin)) return 1;
const size_t len = noom_strlen(buf);
if (len > 0 && buf[len - 1] != '\n') {
while (getchar() != '\n' && !feof(stdin)) ;
}
else if (len > 0) {
buf[len - 1] = '\0';
}
} while (buf[0] == '\0' && required);
return 0;
}
int main(int argc, char **argv) {
const char *err = 0;
struct {
noom_bool_t enter_repl;
noom_bool_t use_stdin;
const char* script_exec;
const char* script_path;
noom_bool_t do_i_already_know_what_to_do;
} params = {0};
if (argc < 2) {
params.enter_repl = 1;
params.do_i_already_know_what_to_do = 1;
}
for (int i = 1; i < argc; i++) {
if (noom_strcmp(argv[i], "-") == 0) {
params.use_stdin = 1;
params.do_i_already_know_what_to_do = 1;
continue;
}
if (noom_strcmp(argv[i], "--") == 0) {
if (++i >= argc) break;
if (params.do_i_already_know_what_to_do) {
err = "too many arguments";
goto die;
}
params.script_exec = argv[i];
params.do_i_already_know_what_to_do = 1;
continue;
}
if (argv[i][0] != '-') {
if (params.do_i_already_know_what_to_do) {
err = "too many arguments";
goto die;
}
params.script_path = argv[i];
params.do_i_already_know_what_to_do = 1;
continue;
}
if (argv[i][1] == 'e') {
if (params.do_i_already_know_what_to_do) {
goto die;
}
/* "-estat" or "-e stat" */
if (argv[i][2] != '\0') {
params.script_exec = argv[i] + 2;
params.do_i_already_know_what_to_do = 1;
continue;
}
if (++i >= argc) {
err = "-e needs an argument";
goto die;
}
params.script_exec = argv[i];
params.do_i_already_know_what_to_do = 1;
}
if (argv[i][1] == 'v') {
puts(NOOM_VERSION_TEXT);
return 0;
}
err = "unknown option";
goto die;
}
if (!params.do_i_already_know_what_to_do) {
err = "script not set";
goto die;
}
if (params.script_exec || params.script_path) {
if (params.script_exec) {
return the_theoretical_function_to_execute_your_code_that_should_be_replaced_later(params.script_exec, "(command line)");
}
char* code = read_file(params.script_path);
if (code == 0) return 1;
return the_theoretical_function_to_execute_your_code_that_should_be_replaced_later(code, params.script_path);
}
if (params.use_stdin) {
char* code = read_stdin();
if (code == 0) return 1;
return the_theoretical_function_to_execute_your_code_that_should_be_replaced_later(code, "stdin");
}
if (params.enter_repl) {
puts(NOOM_VERSION_TEXT);
for (;;) {
char code[4096];
if (read_prompt(code, sizeof(code), "> ", 1)) return 0;
the_theoretical_function_to_execute_your_code_that_should_be_replaced_later(code, "(noom input)");
}
}
die:
fprintf(stderr, "%s: %s\n"
"usage: %s [options] [script [args]]\n"
"Available options are:\n"
" - execute stdin\n"
" -e stat execute string 'stat'\n"
" -v show version\n",
argv[0], err, argv[0]);
return 1;
}

View File

@@ -3,6 +3,19 @@
#ifndef NOOM_H #ifndef NOOM_H
#define NOOM_H #define NOOM_H
#define NN_STR(x) #x
#define NN_XSTR(x) NN_STR(x)
#define NOOM_VERSION_MAJOR 0
#define NOOM_VERSION_MINOR 0
#define NOOM_VERSION_PATCH 0
#if NOOM_VERSION_PATCH==0
#define NOOM_VERSION_FULL NN_XSTR(NOOM_VERSION_MAJOR) "." NN_XSTR(NOOM_VERSION_MINOR)
#else
#define NOOM_VERSION_FULL NN_XSTR(NOOM_VERSION_MAJOR) "." NN_XSTR(NOOM_VERSION_MINOR) "." NN_XSTR(NOOM_VERSION_PATCH)
#endif
#define NOOM_VERSION_TEXT "Noom " NOOM_VERSION_FULL " (C) 2026 NeoFlock and Noom contributors"
typedef enum noom_LuaVersion { typedef enum noom_LuaVersion {
// no 5.0, at least for now, cause it doesn't seem to be used much and is a bit *weird* // no 5.0, at least for now, cause it doesn't seem to be used much and is a bit *weird*
NOOM_VERSION_51, NOOM_VERSION_51,
@@ -45,4 +58,7 @@ typedef struct noom_LuaVM noom_LuaVM;
typedef noom_Exit noom_CFunction(noom_LuaVM *vm); typedef noom_Exit noom_CFunction(noom_LuaVM *vm);
typedef noom_Exit noom_KFunction(noom_LuaVM *vm, noom_Exit status, void *ctx); typedef noom_Exit noom_KFunction(noom_LuaVM *vm, noom_Exit status, void *ctx);
noom_LuaVM *noom_createVM(noom_LuaVersion version);
void noom_destroyVM(noom_LuaVM *vm);
#endif #endif

View File

@@ -110,6 +110,11 @@ int noomP_peek(noomP_Parser* parser, noomL_Token* token) {
continue; continue;
} }
if (token->type != NOOML_TOKEN_EOF) {
parser->last_token_offset = token->offset;
parser->last_token_length = token->length;
}
return 0; return 0;
} }
} }
@@ -127,8 +132,6 @@ noomP_Node* noomP_allocNode(noomP_Parser* parser) {
return 0; return 0;
} }
node->previous_node = parser->last_node;
node->subnodec = 0; node->subnodec = 0;
node->subnodes = noom_alloc(sizeof(noomP_Node*) * 2); node->subnodes = noom_alloc(sizeof(noomP_Node*) * 2);
node->subnode_cap = 2; node->subnode_cap = 2;
@@ -138,6 +141,7 @@ noomP_Node* noomP_allocNode(noomP_Parser* parser) {
return 0; return 0;
} }
node->previous_node = parser->last_node;
parser->last_node = node; parser->last_node = node;
return node; return node;
@@ -145,13 +149,15 @@ noomP_Node* noomP_allocNode(noomP_Parser* parser) {
int noomP_addSubnode(noomP_Parser* parser, noomP_Node* node, noomP_Node* subnode) { int noomP_addSubnode(noomP_Parser* parser, noomP_Node* node, noomP_Node* subnode) {
if (node->subnodec == node->subnode_cap) { if (node->subnodec == node->subnode_cap) {
node->subnode_cap = node->subnode_cap * 2; noomP_Node** new = noom_realloc(node->subnodes, sizeof(noomP_Node*) * node->subnode_cap * 2);
node->subnodes = noom_realloc(node->subnodes, sizeof(noomP_Node*) * node->subnode_cap);
if (node->subnodes == 0) { if (new == 0) {
parser->error_state = NOOMP_ERROR_OOM; parser->error_state = NOOMP_ERROR_OOM; // well fuck
return 1; return 1;
} }
node->subnodes = new;
node->subnode_cap = node->subnode_cap * 2;
} }
node->subnodes[node->subnodec++] = subnode; node->subnodes[node->subnodec++] = subnode;
@@ -164,7 +170,7 @@ noomP_Node* noomP_parseTableLiteral(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "{", 1)) { if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "{", 1)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_LCURLY;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -195,14 +201,14 @@ noomP_Node* noomP_parseTableLiteral(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "]", 1)) { if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "]", 1)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_RBRACKET_AFTER_KEY;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "=", 1)) { if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "=", 1)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_EQUALS_AFTER_KEY;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -279,7 +285,7 @@ noomP_Node* noomP_parseTableLiteral(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "}", 1)) { if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "}", 1)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_RCURLY;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -301,7 +307,7 @@ noomP_Node* noomP_parseComplexExpression(noomP_Parser* parser, noomP_Node* snode
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_IDENTIFIER) { if (token.type != NOOML_TOKEN_IDENTIFIER) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_FIELD_IDENTIFIER;
return 0; return 0;
} }
noomP_skip(parser, &token); // skip the field name noomP_skip(parser, &token); // skip the field name
@@ -332,7 +338,7 @@ noomP_Node* noomP_parseComplexExpression(noomP_Parser* parser, noomP_Node* snode
if (noomP_peek(parser, &token)) return 0; // look for ] if (noomP_peek(parser, &token)) return 0; // look for ]
if (token.type != NOOML_TOKEN_SYMBOL || (!noom_streql(parser->code + token.offset, token.length, "]", 1))) { if (token.type != NOOML_TOKEN_SYMBOL || (!noom_streql(parser->code + token.offset, token.length, "]", 1))) {
// damn it :( // damn it :(
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_RBRACKET_AFTER_INDEX;
return 0; return 0;
} }
noomP_skip(parser, &token); // skip ] noomP_skip(parser, &token); // skip ]
@@ -379,7 +385,7 @@ noomP_Node* noomP_parseComplexExpression(noomP_Parser* parser, noomP_Node* snode
// check for ) // check for )
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_SYMBOL || (!noom_streql(parser->code + token.offset, token.length, ")", 1))) { if (token.type != NOOML_TOKEN_SYMBOL || (!noom_streql(parser->code + token.offset, token.length, ")", 1))) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_RPAREN_AFTER_EXPRESSION;
return 0; return 0;
} }
@@ -449,7 +455,7 @@ noomP_Node* noomP_parseComplexExpression(noomP_Parser* parser, noomP_Node* snode
// check for ) // check for )
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_SYMBOL || (!noom_streql(parser->code + token.offset, token.length, ")", 1))) { if (token.type != NOOML_TOKEN_SYMBOL || (!noom_streql(parser->code + token.offset, token.length, ")", 1))) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_RPAREN_AFTER_EXPRESSION;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -602,7 +608,7 @@ noomP_Node* noomP_parseRawExpression(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, ")", 1)) { if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, ")", 1)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_RPAREN_AFTER_EXPRESSION;
return 0; // unexpected return 0; // unexpected
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -838,7 +844,7 @@ noomP_Node* noomP_parseFunctionParameters(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "(", 1)) { if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "(", 1)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_LPAREN_FOR_PARAMETERS;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -890,7 +896,7 @@ noomP_Node* noomP_parseFunctionParameters(noomP_Parser* parser) {
// closing paren // closing paren
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, ")", 1)) { if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, ")", 1)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_RPAREN_FOR_PARAMETERS;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -954,7 +960,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_IDENTIFIER) { if (token.type != NOOML_TOKEN_IDENTIFIER) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_IDENTIFIER_AFTER_LOCAL_FUNCTION;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -977,7 +983,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "end", 3)) { if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "end", 3)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_END_AFTER_LOCAL_FUNCTION;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -995,7 +1001,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_IDENTIFIER) { if (token.type != NOOML_TOKEN_IDENTIFIER) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_IDENTIFIER_AFTER_LOCAL;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -1016,7 +1022,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
// the attribute is an identifier. // the attribute is an identifier.
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_IDENTIFIER) { if (token.type != NOOML_TOKEN_IDENTIFIER) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_IDENTIFIER_AFTER_LANGLE;
return 0; // unexpected return 0; // unexpected
} }
noom_uint_t attr = token.offset; noom_uint_t attr = token.offset;
@@ -1029,7 +1035,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, ">", 1)) { if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, ">", 1)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_RANGLE_TO_CLOSE_ATTRIBUTE;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -1105,7 +1111,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "then", 4)) { if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "then", 4)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_THEN_AFTER_EXPRESSION;
return 0; // unexpected return 0; // unexpected
} }
@@ -1120,7 +1126,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_KEYWORD) { if (token.type != NOOML_TOKEN_KEYWORD) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_ELSE_ELSEIF_OR_END;
return 0; // unexpected return 0; // unexpected
} }
@@ -1135,7 +1141,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "then", 4)) { if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "then", 4)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_THEN_AFTER_EXPRESSION;
return 0; // unexpected return 0; // unexpected
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -1161,14 +1167,14 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
break; // will check for end outside the loop because else and things break; // will check for end outside the loop because else and things
} else { } else {
// unexpected // unexpected
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_ELSE_ELSEIF_OR_END;
return 0; return 0;
} }
} }
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "end", 3)) { if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "end", 3)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_END_AFTER_IF;
return 0; // unexpected return 0; // unexpected
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -1191,7 +1197,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "do", 2)) { if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "do", 2)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_DO_AFTER_EXPRESSION;
return 0; // unexpected return 0; // unexpected
} }
noomP_skip(parser, &token); // skip `do` noomP_skip(parser, &token); // skip `do`
@@ -1203,7 +1209,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "end", 3)) { if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "end", 3)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_END_AFTER_WHILE;
return 0; // unexpected return 0; // unexpected
} }
noomP_skip(parser, &token); // skip `end` noomP_skip(parser, &token); // skip `end`
@@ -1241,7 +1247,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
fname->source_offset = token.offset; fname->source_offset = token.offset;
if (token.type != NOOML_TOKEN_IDENTIFIER) { if (token.type != NOOML_TOKEN_IDENTIFIER) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_IDENTIFIER_AFTER_FUNCTION;
return 0; // unex. return 0; // unex.
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -1266,7 +1272,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_IDENTIFIER) { if (token.type != NOOML_TOKEN_IDENTIFIER) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_FIELD_IDENTIFIER;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -1283,7 +1289,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_IDENTIFIER) { if (token.type != NOOML_TOKEN_IDENTIFIER) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_METHOD_CALL;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -1317,7 +1323,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
// remove `end` // remove `end`
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "end", 3)) { if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "end", 3)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_END_AFTER_FUNCTION;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -1419,7 +1425,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_IDENTIFIER) { if (token.type != NOOML_TOKEN_IDENTIFIER) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_IDENTIFIER_AFTER_FOR;
return 0; return 0;
} }
noom_uint_t vname = token.offset; noom_uint_t vname = token.offset;
@@ -1469,7 +1475,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
while (1) { while (1) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_IDENTIFIER) { if (token.type != NOOML_TOKEN_IDENTIFIER) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_VARIABLE_AFTER_COMMA_IN_FOR;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -1495,7 +1501,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
// okay. that took a while. now for the in and the expressions // okay. that took a while. now for the in and the expressions
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "in", 2)) { if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "in", 2)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_IN;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -1520,7 +1526,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
// making this the same for all of them: do [block] end // making this the same for all of them: do [block] end
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "do", 2)) { if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "do", 2)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_DO_AFTER_EXPRESSION;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -1532,7 +1538,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "end", 3)) { if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "end", 3)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_END_AFTER_FOR;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -1549,7 +1555,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_IDENTIFIER) { if (token.type != NOOML_TOKEN_IDENTIFIER) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_IDENTIFIER_AFTER_GOTO;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -1579,7 +1585,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "until", 5)) { if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "until", 5)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_UNTIL;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -1607,7 +1613,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "end", 3)) { if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "end", 3)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_END_AFTER_DO;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -1638,7 +1644,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
if (noomP_peek(parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "::", 2)) { if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "::", 2)) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_COLONCOLON;
return 0; return 0;
} }
noomP_skip(parser, &token); noomP_skip(parser, &token);
@@ -1681,7 +1687,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
if (token.type != NOOML_TOKEN_IDENTIFIER && (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "(", 1))) { if (token.type != NOOML_TOKEN_IDENTIFIER && (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "(", 1))) {
// unexpected // unexpected
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_ASSIGNABLE;
return 0; return 0;
} }
@@ -1690,7 +1696,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
if (item == 0) return 0; if (item == 0) return 0;
if (item->type != NOOMP_NODE_INDEX && item->type != NOOMP_NODE_GETFIELD && item->type != NOOMP_NODE_VARIABLE) { if (item->type != NOOMP_NODE_INDEX && item->type != NOOMP_NODE_GETFIELD && item->type != NOOMP_NODE_VARIABLE) {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_NOT_ASSIGNABLE;
return 0; // unexpected return 0; // unexpected
} }
@@ -1709,7 +1715,7 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
break; break;
} }
} else { } else {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPECTED_COMMA_OR_EQUAL_IN_ASSIGNMENT;
return 0; // unexpected return 0; // unexpected
} }
} }
@@ -1738,12 +1744,12 @@ noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) {
// this expression is now a statement. // this expression is now a statement.
return base; // no need to eat any more. return base; // no need to eat any more.
} else { } else {
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_EXPRESSION_NOT_STATEMENT;
return 0; // unexpected. e.g. random string or whatever return 0; // unexpected. e.g. random string or whatever
} }
} }
parser->error_state = NOOMP_ERROR_UNEXPECTED; parser->error_state = NOOMP_ERROR_INVALID_STATEMENT;
return 0; return 0;
} }
@@ -1767,29 +1773,27 @@ noomP_Node* noomP_parseStatement(noomP_Parser* parser) {
return stmt; return stmt;
} }
int noomP_parse(const char* code, const char* filename, noom_LuaVersion version, noomP_Node** outpointer, noomP_Node** last_node) { int noomP_parse(const char* code, const char* filename, noom_LuaVersion version, noomP_Node** outpointer, noomP_Parser* parser) {
noomP_Parser parser; noomP_initParser(parser, code, filename, version);
noomP_initParser(&parser, code, filename, version);
noomL_Token token; noomL_Token token;
noomP_Node* node = noomP_allocNode(&parser); noomP_Node* node = noomP_allocNode(parser);
if (node == 0) return -1; if (node == 0) return -1;
node->source_offset = parser.lex_offset; node->source_offset = parser->lex_offset;
node->type = NOOMP_NODE_PROGRAM; node->type = NOOMP_NODE_PROGRAM;
while (1) { while (1) {
if (noomP_peek(&parser, &token)) return 0; if (noomP_peek(parser, &token)) return 0;
if (token.type == NOOML_TOKEN_EOF) break; if (token.type == NOOML_TOKEN_EOF) break;
noomP_Node* child = noomP_parseStatement(&parser); noomP_Node* child = noomP_parseStatement(parser);
if (child == 0) return -1; if (child == 0) return -1;
if (noomP_addSubnode(&parser, node, child)) return 0; if (noomP_addSubnode(parser, node, child)) return 0;
} }
*outpointer = node; *outpointer = node;
*last_node = parser.last_node;
return 0; return 0;
} }
@@ -1798,6 +1802,7 @@ int noomP_initParser(noomP_Parser* parser, const char* code, const char* filenam
parser->code = code; parser->code = code;
parser->filename = filename; parser->filename = filename;
parser->lex_offset = 0; parser->lex_offset = 0;
parser->last_token_length = 0;
parser->last_node = (void *)0; parser->last_node = (void *)0;
parser->version = version; parser->version = version;

View File

@@ -72,12 +72,57 @@ typedef enum noomP_Error {
NOOMP_ERROR_NONE = 0, NOOMP_ERROR_NONE = 0,
NOOMP_ERROR_OOM, NOOMP_ERROR_OOM,
NOOMP_ERROR_UNEXPECTED, // TODO: maybe split into multiple for better errors? NOOMP_ERROR_EXPECTED_LCURLY, // table start
NOOMP_ERROR_EXPECTED_RCURLY, // table end
NOOMP_ERROR_EXPECTED_RBRACKET_AFTER_KEY,
NOOMP_ERROR_EXPECTED_EQUALS_AFTER_KEY,
NOOMP_ERROR_EXPECTED_FIELD_IDENTIFIER,
NOOMP_ERROR_EXPECTED_METHOD_CALL,
NOOMP_ERROR_EXPECTED_RBRACKET_AFTER_INDEX,
NOOMP_ERROR_EXPECTED_RPAREN_AFTER_EXPRESSION,
NOOMP_ERROR_EXPECTED_LPAREN_FOR_PARAMETERS,
NOOMP_ERROR_EXPECTED_RPAREN_FOR_PARAMETERS,
NOOMP_ERROR_EXPECTED_IDENTIFIER_AFTER_LOCAL_FUNCTION,
NOOMP_ERROR_EXPECTED_IDENTIFIER_AFTER_FUNCTION,
NOOMP_ERROR_EXPECTED_END_AFTER_LOCAL_FUNCTION,
NOOMP_ERROR_EXPECTED_END_AFTER_FUNCTION,
NOOMP_ERROR_EXPECTED_IDENTIFIER_AFTER_LOCAL,
NOOMP_ERROR_EXPECTED_IDENTIFIER_AFTER_LANGLE,
NOOMP_ERROR_EXPECTED_RANGLE_TO_CLOSE_ATTRIBUTE,
NOOMP_ERROR_EXPECTED_THEN_AFTER_EXPRESSION,
NOOMP_ERROR_EXPECTED_ELSE_ELSEIF_OR_END,
NOOMP_ERROR_EXPECTED_END_AFTER_IF,
NOOMP_ERROR_EXPECTED_DO_AFTER_EXPRESSION,
NOOMP_ERROR_EXPECTED_END_AFTER_WHILE,
NOOMP_ERROR_EXPECTED_END_AFTER_FOR,
NOOMP_ERROR_EXPECTED_END_AFTER_DO,
NOOMP_ERROR_EXPECTED_IDENTIFIER_AFTER_FOR,
NOOMP_ERROR_EXPECTED_VARIABLE_AFTER_COMMA_IN_FOR,
NOOMP_ERROR_EXPECTED_IN,
NOOMP_ERROR_EXPECTED_UNTIL,
NOOMP_ERROR_EXPECTED_IDENTIFIER_AFTER_GOTO,
NOOMP_ERROR_EXPECTED_COLONCOLON,
NOOMP_ERROR_INVALID_STATEMENT,
NOOMP_ERROR_EXPECTED_ASSIGNABLE,
NOOMP_ERROR_NOT_ASSIGNABLE,
NOOMP_ERROR_EXPECTED_COMMA_OR_EQUAL_IN_ASSIGNMENT,
NOOMP_ERROR_EXPRESSION_NOT_STATEMENT,
NOOMP_ERROR_FAKEATTRIBUTE, NOOMP_ERROR_FAKEATTRIBUTE,
NOOMP_ERROR_RETURN_NOT_END, NOOMP_ERROR_RETURN_NOT_END,
NOOMP_ERROR_FOR_WRONG_AMOUNT, NOOMP_ERROR_FOR_WRONG_AMOUNT,
NOOMP_ERROR_LEXER = 1 << 31, // bitwise or'd with the lexer error number NOOMP_ERROR_LEXER = 1 << 31, // bitwise or's with the lexer error number
} noomP_Error; } noomP_Error;
typedef struct noomP_Node { typedef struct noomP_Node {
@@ -97,6 +142,9 @@ typedef struct noomP_Parser { // todo: track location in code with line/column?
const char* code; const char* code;
const char* filename; const char* filename;
noom_uint_t last_token_offset;
noom_uint_t last_token_length;
noom_uint_t lex_offset; noom_uint_t lex_offset;
noom_uint_t error_state; noom_uint_t error_state;
@@ -117,7 +165,7 @@ noomP_Node* noomP_parseBlock(noomP_Parser* parser);
noomP_Node* noomP_parseExpression(noomP_Parser* parser); noomP_Node* noomP_parseExpression(noomP_Parser* parser);
noomP_Node* noomP_parseStatement(noomP_Parser* parser); noomP_Node* noomP_parseStatement(noomP_Parser* parser);
int noomP_parse(const char* code, const char* filename, noom_LuaVersion version, noomP_Node** outpointer, noomP_Node** last_node); int noomP_parse(const char* code, const char* filename, noom_LuaVersion version, noomP_Node** outpointer, noomP_Parser* parser);
int noomP_initParser(noomP_Parser* parser, const char* code, const char* filename, noom_LuaVersion version); int noomP_initParser(noomP_Parser* parser, const char* code, const char* filename, noom_LuaVersion version);

42
src/vm.c Normal file
View File

@@ -0,0 +1,42 @@
#include "vm.h"
#include "helper.h"
noomV_Object *noomV_allocObj(noom_LuaVM *vm, noomV_ObjTag tag, noom_uint_t size) {
noomV_Object *o = noom_alloc(size);
if(o == 0) return o;
o->tag = tag;
o->marked = 0;
o->next = vm->heap;
o->nextGray = 0;
vm->heap = o;
return o;
}
void noomV_freeObj(noomV_Object *obj) {
noom_free(obj);
}
noom_LuaVM *noom_createVM(noom_LuaVersion version) {
noom_LuaVM *vm = noom_alloc(sizeof(*vm));
if(vm == 0) return 0;
// initialize the universe to NULL, handles partial OOMs nicely
vm->heap = 0;
vm->graySet = 0;
vm->globals = 0;
vm->registry = 0;
vm->mainThread = 0;
vm->currentThread = 0;
vm->version = version;
return vm;
}
void noom_destroyVM(noom_LuaVM *vm) {
noomV_Object *iter = vm->heap;
while(iter) {
noomV_Object *cur = iter;
iter = iter->next;
noomV_freeObj(cur);
}
noom_free(vm);
}

View File

@@ -25,7 +25,7 @@ typedef enum noomV_ObjTag {
typedef struct noomV_Object { typedef struct noomV_Object {
noomV_ObjTag tag; noomV_ObjTag tag;
bool marked; noom_bool_t marked;
struct noomV_Object *next; struct noomV_Object *next;
struct noomV_Object *nextGray; struct noomV_Object *nextGray;
} noomV_Object; } noomV_Object;
@@ -43,14 +43,15 @@ typedef enum noomV_ValueTag : unsigned char {
typedef struct noomV_Value { typedef struct noomV_Value {
noomV_ValueTag tag; noomV_ValueTag tag;
// for stack slots // for stack slots
bool autoclose; noom_bool_t autoclose;
// pointer to value // pointer to value
bool isptr; noom_bool_t isptr;
union { union {
noom_bool_t boolean; noom_bool_t boolean;
noom_int_t integer; noom_int_t integer;
noom_float_t number; noom_float_t number;
noomV_Object *obj; noomV_Object *obj;
struct noomV_Pointer *ptr;
}; };
} noomV_Value; } noomV_Value;
@@ -64,8 +65,15 @@ typedef struct noomV_String {
typedef struct noomV_Table { typedef struct noomV_Table {
noomV_Object obj; noomV_Object obj;
struct noomV_Table *meta; struct noomV_Table *meta;
// amount of entries allocated
noom_uint_t entries; noom_uint_t entries;
// how many entries are defined.
// Note, this includes keys with null values, which are still scanned.
noom_uint_t used;
// cache of #t, for faster subsequent fetches
noom_uint_t len; noom_uint_t len;
// actual values. Given index i, V[i] is the key and V[i+entries] is the value.
// This is because we mostly read the key, so we should put more keys in cache.
noomV_Value *entrydata; noomV_Value *entrydata;
} noomV_Table; } noomV_Table;
@@ -166,6 +174,7 @@ typedef struct noomV_Inst {
typedef struct noomV_UpvalDesc { typedef struct noomV_UpvalDesc {
char *name; char *name;
unsigned char idx; unsigned char idx;
// whether the index is a stack index
noom_bool_t isStack; noom_bool_t isStack;
} noomV_UpvalDesc; } noomV_UpvalDesc;
@@ -184,11 +193,13 @@ typedef struct noomV_Function {
noomV_String *chunkname; noomV_String *chunkname;
noomV_Inst *code; noomV_Inst *code;
noomV_Value *consts; noomV_Value *consts;
noomV_Function **protos; struct noomV_Function **protos;
noomV_UpvalDesc *upvals; noomV_UpvalDesc *upvals;
noomV_LocalDesc *locals; noomV_LocalDesc *locals;
unsigned int codesize; unsigned int codesize;
// line of function
unsigned int linedefined; unsigned int linedefined;
// line of end
unsigned int lastlinedefined; unsigned int lastlinedefined;
// very size limited // very size limited
// ░░░░░░░░░ ░ ▒░░▒ ▒░ ░░ ░ // ░░░░░░░░░ ░ ▒░░▒ ▒░ ░░ ░
@@ -260,7 +271,7 @@ typedef struct noomV_Function {
typedef struct noomV_CallFrame { typedef struct noomV_CallFrame {
// stack index of function // stack index of function
unsigned int funcIdx; unsigned int funcIdx;
bool isC; noom_bool_t isC;
union { union {
struct { struct {
// //
@@ -293,6 +304,11 @@ struct noom_LuaVM {
noomV_Table *registry; noomV_Table *registry;
noomV_Thread *mainThread; noomV_Thread *mainThread;
noomV_Thread *currentThread; noomV_Thread *currentThread;
noom_LuaVersion version;
}; };
// Allocating objects
noomV_Object *noomV_allocObj(noom_LuaVM *vm, noomV_ObjTag tag, noom_uint_t size);
void noomV_freeObj(noomV_Object *obj);
#endif #endif