diff --git a/.gitignore b/.gitignore index cbda421..170088a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,8 @@ +a.out +coverage_html/ +test +test.exe +test_cov wl -*.exe \ No newline at end of file +wl.exe +wl_cov \ No newline at end of file diff --git a/Makefile b/Makefile index 24195cc..4f71308 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,29 @@ -all: WL.c - gcc WL.c main.c -o wl -Wall -Wextra -O0 -g3 -WL.c: $(wildcard src/*.c src/*.h) - python amalg.py \ No newline at end of file +.PHONY: all coverage coverage_report install uninstall + +all: wl + +wl: wl.c wl.h main.c + gcc main.c wl.c -o wl -g3 -O0 + +test: wl.c wl.h test.c + gcc test.c wl.c -o test -g3 -O0 + +coverage: + gcc main.c wl.c -o wl_cov -g3 -O0 --coverage -fprofile-arcs -ftest-coverage + gcc test.c wl.c -o test_cov -g3 -O0 --coverage -fprofile-arcs -ftest-coverage + +coverage_report: + gcov wl.c main.c test.c + lcov --capture --directory . --output-file coverage.info + lcov --remove coverage.info '/usr/*' --output-file coverage_filtered.info + genhtml coverage_filtered.info --output-directory coverage_html --title "WL Template Engine Coverage" + +install: wl + sudo cp wl /usr/local/bin + +uninstall: + sudo rm /usr/local/bin/wl + +clean: + rm *.gcda *.gcno *.info diff --git a/README.md b/README.md index 0fb0f4b..d5de21d 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,10 @@ # WL -WL is the proof of concept for a powerful and flexible templating language for the web. All functionality is implemented, but is still missing some polish. +WL is an HTML templating engine written in and for C. -Here's an example template in WL with a recursive comment view element: -```html -let posts = [ - { - author: "UserA", - title: "I'm the first post", - content: "Sup everyone!", - date: "1 Jan 2025", - comments: [ - { - author: "UserB", - content: "Hello! This is a comment!", - comments: [ - { - author: "UserA", - content: "Hello to you!", - comments: [] - } - ] - } - ] - } -] - -fun comment(c) -
- \c.author -

\c.content

- \for subc in c.comments: - comment(subc) -
- - - - List of posts - - - \for post in posts: -
-

- \post.title -

- - Posted on \post.date by \post.author - -

- \post.content -

-
- \for c in post.comments: - comment(c) -
-
- - -``` \ No newline at end of file +It supports: +1. Zero dependencies +2. Single-file implementation +3. HTML-first design +4. Complete scripting language +5. Easy integration +6. Performs no I/O or dynamic allocations \ No newline at end of file diff --git a/WL.c b/WL.c index 21e8635..5108ec7 100644 --- a/WL.c +++ b/WL.c @@ -1,127 +1,81 @@ -#include "WL.h" - -//////////////////////////////////////////////////////////////////////////////////////// -// src/includes.h -//////////////////////////////////////////////////////////////////////////////////////// - -#ifndef WL_INCLUDES_INCLUDED -#define WL_INCLUDES_INCLUDED - #include #include #include -#include #include -#include #include +#include "wl.h" -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#endif - -#ifndef _WIN32 -#include -#include -#include -#include -#include -#endif - -#endif // WL_INCLUDES_INCLUDED - -//////////////////////////////////////////////////////////////////////////////////////// -// src/basic.h -//////////////////////////////////////////////////////////////////////////////////////// - -#ifndef WL_BASIC_INCLUDED -#define WL_BASIC_INCLUDED - -#ifndef WL_AMALGAMATION -#include "public.h" -#endif +///////////////////////////////////////////////////////////////////////// +// BASIC +///////////////////////////////////////////////////////////////////////// typedef struct { char *ptr; int len; } String; -#ifdef _WIN32 -#define LLU "llu" -#define LLD "lld" -#else -#define LLU "lu" -#define LLD "ld" -#endif +typedef struct { + char *buf; + int cap; + bool yes; +} Error; -#define S(X) (String) { (X), (int) sizeof(X)-1 } +#define S(X) (String) { (X), SIZEOF(X)-1 } + +#ifdef _WIN32 +#define LLD "lld" +#define LLU "llu" +#else +#define LLD "ld" +#define LLU "lu" +#endif #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) #define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) +#define SIZEOF(X) (int) sizeof(X) +#define ALIGNOF(X) (int) _Alignof(X) -#define COUNT(X) (int) (sizeof(X) / sizeof((X)[0])) - -bool is_space(char c); -bool is_digit(char c); -bool is_alpha(char c); -bool is_printable(char c); -char to_lower(char c); -bool is_hex_digit(char c); -int hex_digit_to_int(char c); - -bool streq(String a, String b); -bool streqcase(String a, String b); -String copystr(String s, WL_Arena *a); - -void *alloc(WL_Arena *a, int len, int align); -bool grow_alloc(WL_Arena *a, char *p, int new_len); - -#endif // WL_BASIC_INCLUDED - -//////////////////////////////////////////////////////////////////////////////////////// -// src/basic.c -//////////////////////////////////////////////////////////////////////////////////////// - - -#ifndef WL_AMALGAMATION -#include "includes.h" -#include "basic.h" -#include "public.h" +#ifndef NDEBUG +#define UNREACHABLE __builtin_trap() +#define ASSERT(X) if (!(X)) __builtin_trap(); +#else +#define UNREACHABLE {} +#define ASSERT(X) {} #endif -bool is_space(char c) +static bool is_space(char c) { return c == ' ' || c == '\t' || c == '\r' || c == '\n'; } -bool is_digit(char c) +static bool is_digit(char c) { return c >= '0' && c <= '9'; } -bool is_alpha(char c) +static bool is_alpha(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } -bool is_printable(char c) +static bool is_printable(char c) { return c >= ' ' && c <= '~'; } -bool is_hex_digit(char c) +static bool is_hex_digit(char c) { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } -char to_lower(char c) +static char to_lower(char c) { if (c >= 'A' && c <= 'Z') - return c - 'A' + 10; + return c - 'A' + 'a'; return c; } -int hex_digit_to_int(char c) +static int hex_digit_to_int(char c) { if (c >= 'a' && c <= 'f') return c - 'a' + 10; @@ -132,8 +86,7 @@ int hex_digit_to_int(char c) return c - '0'; } - -bool streq(String a, String b) +static bool streq(String a, String b) { if (a.len != b.len) return false; @@ -143,7 +96,7 @@ bool streq(String a, String b) return true; } -bool streqcase(String a, String b) +static bool streqcase(String a, String b) { if (a.len != b.len) return false; @@ -153,7 +106,40 @@ bool streqcase(String a, String b) return true; } -void *alloc(WL_Arena *a, int len, int align) + +#define REPORT(err, fmt, ...) report((err), __FILE__, __LINE__, fmt, ## __VA_ARGS__) +static void report(Error *err, char *file, int line, char *fmt, ...) +{ + if (err->yes) return; + + if (err->cap > 0) { + + va_list args; + va_start(args, fmt); + int len = vsnprintf(err->buf, err->cap, fmt, args); + va_end(args); + ASSERT(len >= 0); + + if (err->cap > len) { + int ret = snprintf(err->buf + len, err->cap - len, + " (reported at %s:%d)", file, line); + ASSERT(ret >= 0); + len += ret; + } + + if (len > err->cap) + len = err->cap-1; + err->buf[len] = '\0'; + } + + err->yes = true; +} + +///////////////////////////////////////////////////////////////////////// +// ARENA +///////////////////////////////////////////////////////////////////////// + +static void *alloc(WL_Arena *a, int len, int align) { int pad = -(intptr_t) (a->ptr + a->cur) & (align-1); if (a->len - a->cur < len + pad) @@ -163,7 +149,7 @@ void *alloc(WL_Arena *a, int len, int align) return ret; } -bool grow_alloc(WL_Arena *a, char *p, int new_len) +static bool grow_alloc(WL_Arena *a, char *p, int new_len) { int new_cur = (p - a->ptr) + new_len; if (new_cur > a->len) @@ -172,7 +158,7 @@ bool grow_alloc(WL_Arena *a, char *p, int new_len) return true; } -String copystr(String s, WL_Arena *a) +static String copystr(String s, WL_Arena *a) { char *p = alloc(a, s.len, 1); if (p == NULL) @@ -181,170 +167,140 @@ String copystr(String s, WL_Arena *a) return (String) { p, s.len }; } -//////////////////////////////////////////////////////////////////////////////////////// -// src/file.h -//////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// WRITER +///////////////////////////////////////////////////////////////////////// -#ifndef WL_FILE_INCLUDED -#define WL_FILE_INCLUDED +typedef struct { + char *dst; + int cap; + int len; +} Writer; -#ifndef WL_AMALGAMATION -#include "includes.h" -#include "basic.h" -#endif - -#ifdef _WIN32 -typedef HANDLE File; -#else -typedef int File; -#endif - -int file_open(String path, File *handle, int *size); -void file_close(File file); -int file_read(File file, char *dst, int max); -int file_read_all(String path, String *dst); - -#endif // WL_FILE_INCLUDED - -//////////////////////////////////////////////////////////////////////////////////////// -// src/file.c -//////////////////////////////////////////////////////////////////////////////////////// - -#ifndef WL_AMALGAMATION -#include "includes.h" -#include "file.h" -#endif - -int file_open(String path, File *handle, int *size) +static void write_raw_mem(Writer *w, void *ptr, int len) { - char zt[1<<10]; - if (path.len >= COUNT(zt)) - return -1; - memcpy(zt, path.ptr, path.len); - zt[path.len] = '\0'; - -#ifdef _WIN32 - *handle = CreateFileA( - zt, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - if (*handle == INVALID_HANDLE_VALUE) { - DWORD error = GetLastError(); - if (error == ERROR_FILE_NOT_FOUND || - error == ERROR_ACCESS_DENIED) - return 1; - return -1; + if (w->cap > w->len) { + int cpy = MIN(w->cap - w->len, len); + if (ptr && w->dst) + memcpy(w->dst + w->len, ptr, cpy); } - LARGE_INTEGER fileSize; - if (!GetFileSizeEx(*handle, &fileSize)) { - CloseHandle(*handle); - return -1; - } - if (fileSize.QuadPart > INT_MAX) { - CloseHandle(*handle); - return -1; - } - *size = (int) fileSize.QuadPart; -#else - *handle = open(zt, O_RDONLY); - if (*handle < 0) { - if (errno == ENOENT) - return 1; - return -1; - } - struct stat info; - if (fstat(*handle, &info) < 0) { - close(*handle); - return -1; - } - if (S_ISDIR(info.st_mode)) { - close(*handle); - return 1; - } - if (info.st_size > INT_MAX) { - close(*handle); - return -1; - } - *size = (int) info.st_size; -#endif - return 0; + w->len += len; } -void file_close(File file) +static void write_raw_u8 (Writer *w, uint8_t x) { write_raw_mem(w, &x, SIZEOF(x)); } +static void write_raw_u16(Writer *w, uint16_t x) { write_raw_mem(w, &x, SIZEOF(x)); } +static void write_raw_u32(Writer *w, uint32_t x) { write_raw_mem(w, &x, SIZEOF(x)); } +static void write_raw_u64(Writer *w, uint64_t x) { write_raw_mem(w, &x, SIZEOF(x)); } +static void write_raw_s8 (Writer *w, int8_t x) { write_raw_mem(w, &x, SIZEOF(x)); } +static void write_raw_s16(Writer *w, int16_t x) { write_raw_mem(w, &x, SIZEOF(x)); } +static void write_raw_s32(Writer *w, int32_t x) { write_raw_mem(w, &x, SIZEOF(x)); } +static void write_raw_s64(Writer *w, int64_t x) { write_raw_mem(w, &x, SIZEOF(x)); } +static void write_raw_f32(Writer *w, float x) { write_raw_mem(w, &x, SIZEOF(x)); } +static void write_raw_f64(Writer *w, double x) { write_raw_mem(w, &x, SIZEOF(x)); } + +static void write_text(Writer *w, String str) { -#ifdef _WIN32 - CloseHandle(file); -#else - close(file); -#endif + write_raw_mem(w, str.ptr, str.len); } -int file_read(File file, char *dst, int max) -{ -#ifdef _WIN32 - DWORD num; - BOOL ok = ReadFile(file, dst, max, &num, NULL); - if (!ok) - return -1; - return (int) num; -#else - return read(file, dst, max); -#endif -} - -int file_read_all(String path, String *dst) +static void write_text_s64(Writer *w, int64_t n) { int len; - File handle; - if (file_open(path, &handle, &len) < 0) - return -1; - - char *ptr = malloc(len+1); - if (ptr == NULL) { - file_close(handle); - return -1; - } - - for (int copied = 0; copied < len; ) { - int ret = file_read(handle, ptr + copied, len - copied); - if (ret <= 0) { - free(ptr); - file_close(handle); - return -1; - } - copied += ret; - } - - *dst = (String) { ptr, len }; - file_close(handle); - return 0; + if (w->len < w->cap) + len = snprintf(w->dst + w->len, w->cap - w->len, "%" LLD, n); + else + len = snprintf(NULL, 0, "%" LLD, n); + ASSERT(len >= 0); + w->len += len; } -//////////////////////////////////////////////////////////////////////////////////////// -// src/parse.h -//////////////////////////////////////////////////////////////////////////////////////// +static void write_text_f64(Writer *w, double n) +{ + int len; + if (w->len < w->cap) + len = snprintf(w->dst + w->len, w->cap - w->len, "%2.2f", n); + else + len = snprintf(NULL, 0, "%2.2f", n); + ASSERT(len >= 0); + w->len += len; +} -#ifndef WL_PARSE_INCLUDED -#define WL_PARSE_INCLUDED +static void patch_mem(Writer *w, void *src, int off, int len) +{ + ASSERT(off + len <= w->len); + if (off < w->cap) { + int cpy = MIN(w->cap - off, len); + memcpy(w->dst + off, src, cpy); + } +} -#ifndef WL_AMALGAMATION -#include "includes.h" -#include "basic.h" -#endif +///////////////////////////////////////////////////////////////////////// +// PARSER +///////////////////////////////////////////////////////////////////////// + +typedef struct { + char *src; + int len; + int cur; +} Scanner; typedef enum { - NODE_FUNC_DECL, - NODE_FUNC_ARG, - NODE_FUNC_CALL, + TOKEN_END, + TOKEN_ERROR, + TOKEN_IDENT, + TOKEN_KWORD_IF, + TOKEN_KWORD_ELSE, + TOKEN_KWORD_WHILE, + TOKEN_KWORD_FOR, + TOKEN_KWORD_IN, + TOKEN_KWORD_PROCEDURE, + TOKEN_KWORD_LET, + TOKEN_KWORD_NONE, + TOKEN_KWORD_TRUE, + TOKEN_KWORD_FALSE, + TOKEN_KWORD_INCLUDE, + TOKEN_KWORD_LEN, + TOKEN_VALUE_FLOAT, + TOKEN_VALUE_INT, + TOKEN_VALUE_STR, + TOKEN_OPER_EQL, + TOKEN_OPER_NQL, + TOKEN_OPER_LSS, + TOKEN_OPER_GRT, + TOKEN_OPER_ADD, + TOKEN_OPER_SUB, + TOKEN_OPER_MUL, + TOKEN_OPER_DIV, + TOKEN_OPER_MOD, + TOKEN_OPER_ASS, + TOKEN_OPER_SHOVEL, + TOKEN_PAREN_OPEN, + TOKEN_PAREN_CLOSE, + TOKEN_BRACKET_OPEN, + TOKEN_BRACKET_CLOSE, + TOKEN_CURLY_OPEN, + TOKEN_CURLY_CLOSE, + TOKEN_DOT, + TOKEN_COMMA, + TOKEN_COLON, + TOKEN_DOLLAR, + TOKEN_NEWLINE, +} TokType; + +typedef struct { + TokType type; + int64_t ival; + double fval; + String sval; +} Token; + +typedef enum { + NODE_PROCEDURE_DECL, + NODE_PROCEDURE_ARG, + NODE_PROCEDURE_CALL, NODE_VAR_DECL, - NODE_PRINT, - NODE_BLOCK, - NODE_GLOBAL_BLOCK, + NODE_COMPOUND, + NODE_GLOBAL, NODE_IFELSE, NODE_FOR, NODE_WHILE, @@ -364,6 +320,7 @@ typedef enum { NODE_OPER_MUL, NODE_OPER_DIV, NODE_OPER_MOD, + NODE_OPER_SHOVEL, NODE_VALUE_INT, NODE_VALUE_FLOAT, NODE_VALUE_STR, @@ -375,7 +332,6 @@ typedef enum { NODE_VALUE_HTML, NODE_VALUE_ARRAY, NODE_VALUE_MAP, - NODE_HTML_PARAM, } NodeType; typedef struct Node Node; @@ -388,30 +344,34 @@ struct Node { Node *left; Node *right; + Node *child; + uint64_t ival; - double dval; + double fval; String sval; - Node *params; - Node *child; - bool no_body; + String html_tag; + Node* html_attr; + Node* html_child; + bool html_body; - Node *cond; + Node *if_cond; + Node *if_branch1; + Node *if_branch2; - String tagname; - String attr_name; - Node *attr_value; + Node *while_cond; + Node *while_body; String for_var1; String for_var2; - Node *for_set; + Node* for_set; - String func_name; - Node *func_args; - Node *func_body; + String proc_name; + Node* proc_args; + Node* proc_body; String var_name; - Node *var_value; + Node* var_value; String include_path; Node* include_next; @@ -424,82 +384,9 @@ typedef struct { int errlen; } ParseResult; -void print_node(Node *node); -ParseResult parse(String src, WL_Arena *a, char *errbuf, int errmax); - -#endif // WL_PARSE_INCLUDED - -//////////////////////////////////////////////////////////////////////////////////////// -// src/parse.c -//////////////////////////////////////////////////////////////////////////////////////// - - -#ifndef WL_AMALGAMATION -#include "parse.h" -#endif - -typedef struct { - char *src; - int len; - int cur; -} Scanner; - -typedef enum { - TOKEN_END, - TOKEN_ERROR, - TOKEN_IDENT, - TOKEN_KWORD_IF, - TOKEN_KWORD_ELSE, - TOKEN_KWORD_WHILE, - TOKEN_KWORD_FOR, - TOKEN_KWORD_IN, - TOKEN_KWORD_FUN, - TOKEN_KWORD_LET, - TOKEN_KWORD_PRINT, - TOKEN_KWORD_NONE, - TOKEN_KWORD_TRUE, - TOKEN_KWORD_FALSE, - TOKEN_KWORD_INCLUDE, - TOKEN_KWORD_LEN, - TOKEN_VALUE_FLOAT, - TOKEN_VALUE_INT, - TOKEN_VALUE_STR, - TOKEN_OPER_EQL, - TOKEN_OPER_NQL, - TOKEN_OPER_LSS, - TOKEN_OPER_GRT, - TOKEN_OPER_ADD, - TOKEN_OPER_SUB, - TOKEN_OPER_MUL, - TOKEN_OPER_DIV, - TOKEN_OPER_MOD, - TOKEN_OPER_ASS, - TOKEN_PAREN_OPEN, - TOKEN_PAREN_CLOSE, - TOKEN_BRACKET_OPEN, - TOKEN_BRACKET_CLOSE, - TOKEN_CURLY_OPEN, - TOKEN_CURLY_CLOSE, - TOKEN_DOT, - TOKEN_COMMA, - TOKEN_COLON, - TOKEN_DOLLAR, - TOKEN_NEWLINE, -} TokType; - -typedef struct { - TokType type; - union { - int64_t ival; - uint64_t uval; - double dval; - String sval; - }; -} Token; - typedef struct { Scanner s; - WL_Arena* a; + WL_Arena* arena; char* errbuf; int errmax; int errlen; @@ -507,7 +394,7 @@ typedef struct { Node** include_tail; } Parser; -bool consume_str(Scanner *s, String x) +static bool consume_str(Scanner *s, String x) { if (x.len == 0) return false; @@ -523,90 +410,61 @@ bool consume_str(Scanner *s, String x) return true; } -String tok2str(Token token, char *buf, int max) +static void write_token(Writer *w, Token token) { switch (token.type) { - case TOKEN_END: - return S("EOF"); - - case TOKEN_ERROR: - return S("ERROR"); - - case TOKEN_IDENT: - { - int len = snprintf(buf, max, "%.*s", token.sval.len, token.sval.ptr); - return (String) { buf, len }; - } - break; - - case TOKEN_KWORD_IF: return S("if"); - case TOKEN_KWORD_ELSE: return S("else"); - case TOKEN_KWORD_WHILE: return S("while"); - case TOKEN_KWORD_FOR: return S("for"); - case TOKEN_KWORD_IN: return S("in"); - case TOKEN_KWORD_FUN: return S("fun"); - case TOKEN_KWORD_LET: return S("let"); - case TOKEN_KWORD_PRINT: return S("print"); - case TOKEN_KWORD_NONE: return S("none"); - case TOKEN_KWORD_TRUE: return S("true"); - case TOKEN_KWORD_FALSE: return S("false"); - case TOKEN_KWORD_INCLUDE: return S("include"); - case TOKEN_KWORD_LEN: return S("len"); - - case TOKEN_VALUE_FLOAT: - { - int len = snprintf(buf, max, "%lf", token.dval); - return (String) { buf, len }; - } - break; - - case TOKEN_VALUE_INT: - { - int len = snprintf(buf, max, "%" LLU, token.uval); - return (String) { buf, len }; - } - break; + default : write_text(w, S("???")); break; + case TOKEN_END : write_text(w, S("")); break; + case TOKEN_ERROR : write_text(w, S("")); break; + case TOKEN_IDENT : write_text(w, token.sval); break; + case TOKEN_KWORD_IF : write_text(w, S("if")); break; + case TOKEN_KWORD_ELSE : write_text(w, S("else")); break; + case TOKEN_KWORD_WHILE : write_text(w, S("while")); break; + case TOKEN_KWORD_FOR : write_text(w, S("for")); break; + case TOKEN_KWORD_IN : write_text(w, S("in")); break; + case TOKEN_KWORD_PROCEDURE: write_text(w, S("procedure")); break; + case TOKEN_KWORD_LET : write_text(w, S("let")); break; + case TOKEN_KWORD_NONE : write_text(w, S("none")); break; + case TOKEN_KWORD_TRUE : write_text(w, S("true")); break; + case TOKEN_KWORD_FALSE : write_text(w, S("false")); break; + case TOKEN_KWORD_INCLUDE: write_text(w, S("include")); break; + case TOKEN_KWORD_LEN : write_text(w, S("len")); break; + case TOKEN_VALUE_FLOAT : write_text_f64(w, token.fval); break; + case TOKEN_VALUE_INT : write_text_s64(w, token.ival); break; + case TOKEN_OPER_ASS : write_text(w, S("=")); break; + case TOKEN_OPER_EQL : write_text(w, S("==")); break; + case TOKEN_OPER_NQL : write_text(w, S("!=")); break; + case TOKEN_OPER_LSS : write_text(w, S("<")); break; + case TOKEN_OPER_GRT : write_text(w, S(">")); break; + case TOKEN_OPER_ADD : write_text(w, S("+")); break; + case TOKEN_OPER_SUB : write_text(w, S("-")); break; + case TOKEN_OPER_MUL : write_text(w, S("*")); break; + case TOKEN_OPER_DIV : write_text(w, S("/")); break; + case TOKEN_OPER_MOD : write_text(w, S("%")); break; + case TOKEN_OPER_SHOVEL : write_text(w, S("<<")); break; + case TOKEN_PAREN_OPEN : write_text(w, S("(")); break; + case TOKEN_PAREN_CLOSE : write_text(w, S(")")); break; + case TOKEN_BRACKET_OPEN : write_text(w, S("[")); break; + case TOKEN_BRACKET_CLOSE: write_text(w, S("]")); break; + case TOKEN_CURLY_OPEN : write_text(w, S("{")); break; + case TOKEN_CURLY_CLOSE : write_text(w, S("}")); break; + case TOKEN_DOT : write_text(w, S(".")); break; + case TOKEN_COMMA : write_text(w, S(",")); break; + case TOKEN_COLON : write_text(w, S(":")); break; + case TOKEN_DOLLAR : write_text(w, S("$")); break; + case TOKEN_NEWLINE : write_text(w, S("\\n")); break; case TOKEN_VALUE_STR: - { - int len = snprintf(buf, max, "\"%.*s\"", token.sval.len, token.sval.ptr); - return (String) { buf, len }; - } + write_text(w, S("\"")); + write_text(w, token.sval); // TODO: Escape + write_text(w, S("\"")); break; - case TOKEN_OPER_ASS: return S("=="); - case TOKEN_OPER_EQL: return S("=="); - case TOKEN_OPER_NQL: return S("!="); - case TOKEN_OPER_LSS: return S("<"); - case TOKEN_OPER_GRT: return S(">"); - case TOKEN_OPER_ADD: return S("+"); - case TOKEN_OPER_SUB: return S("-"); - case TOKEN_OPER_MUL: return S("*"); - case TOKEN_OPER_DIV: return S("/"); - case TOKEN_OPER_MOD: return S("%"); - - case TOKEN_PAREN_OPEN: return S("("); - case TOKEN_PAREN_CLOSE: return S(")"); - - case TOKEN_BRACKET_OPEN: return S("["); - case TOKEN_BRACKET_CLOSE: return S("]"); - - case TOKEN_CURLY_OPEN: return S("{"); - case TOKEN_CURLY_CLOSE: return S("}"); - - case TOKEN_DOT: return S("."); - case TOKEN_COMMA: return S(","); - case TOKEN_COLON: return S(":"); - case TOKEN_DOLLAR: return S("$"); - - case TOKEN_NEWLINE: return S("\\n"); } - - return S("???"); } -void parser_report(Parser *p, char *fmt, ...) +static void parser_report(Parser *p, char *fmt, ...) { if (p->errmax == 0 || p->errlen > 0) return; @@ -620,25 +478,21 @@ void parser_report(Parser *p, char *fmt, ...) } int len = snprintf(p->errbuf, p->errmax, "Error (line %d): ", line); - if (len < 0) { - // TODO - } + ASSERT(len >= 0); va_list args; va_start(args, fmt); int ret = vsnprintf(p->errbuf + len, p->errmax - len, fmt, args); va_end(args); - if (ret < 0) { - // TODO - } + ASSERT(ret >= 0); len += ret; p->errlen = len; } -Node *alloc_node(Parser *p) +static Node *alloc_node(Parser *p) { - Node *n = alloc(p->a, sizeof(Node), _Alignof(Node)); + Node *n = alloc(p->arena, sizeof(Node), _Alignof(Node)); if (n == NULL) { parser_report(p, "Out of memory"); return NULL; @@ -647,7 +501,7 @@ Node *alloc_node(Parser *p) return n; } -Token next_token(Parser *p) +static Token next_token(Parser *p) { for (;;) { while (p->s.cur < p->s.len && is_space(p->s.src[p->s.cur])) @@ -684,9 +538,8 @@ Token next_token(Parser *p) if (streq(kword, S("while"))) return (Token) { .type=TOKEN_KWORD_WHILE }; if (streq(kword, S("for"))) return (Token) { .type=TOKEN_KWORD_FOR }; if (streq(kword, S("in"))) return (Token) { .type=TOKEN_KWORD_IN }; - if (streq(kword, S("fun"))) return (Token) { .type=TOKEN_KWORD_FUN }; + if (streq(kword, S("procedure"))) return (Token) { .type=TOKEN_KWORD_PROCEDURE }; if (streq(kword, S("let"))) return (Token) { .type=TOKEN_KWORD_LET }; - if (streq(kword, S("print"))) return (Token) { .type=TOKEN_KWORD_PRINT }; if (streq(kword, S("none"))) return (Token) { .type=TOKEN_KWORD_NONE }; if (streq(kword, S("true"))) return (Token) { .type=TOKEN_KWORD_TRUE }; if (streq(kword, S("false"))) return (Token) { .type=TOKEN_KWORD_FALSE }; @@ -720,7 +573,7 @@ Token next_token(Parser *p) buf += q * d; } while (p->s.cur < p->s.len && is_digit(p->s.src[p->s.cur])); - return (Token) { .type=TOKEN_VALUE_FLOAT, .dval=buf }; + return (Token) { .type=TOKEN_VALUE_FLOAT, .fval=buf }; } else { @@ -734,7 +587,7 @@ Token next_token(Parser *p) buf = buf * 10 + d; } while (p->s.cur < p->s.len && is_digit(p->s.src[p->s.cur])); - return (Token) { .type=TOKEN_VALUE_INT, .uval=buf }; + return (Token) { .type=TOKEN_VALUE_INT, .ival=buf }; } } @@ -756,9 +609,9 @@ Token next_token(Parser *p) int substr_len = p->s.cur - substr_off; if (buf == NULL) - buf = alloc(p->a, substr_len+1, 1); + buf = alloc(p->arena, substr_len+1, 1); else - if (!grow_alloc(p->a, buf, len + substr_len+1)) + if (!grow_alloc(p->arena, buf, len + substr_len+1)) buf = NULL; if (buf == NULL) { @@ -827,6 +680,7 @@ Token next_token(Parser *p) return (Token) { .type=TOKEN_VALUE_STR, .sval=(String) { .ptr=buf, .len=len } }; } + if (consume_str(&p->s, S("<<"))) return (Token) { .type=TOKEN_OPER_SHOVEL }; if (consume_str(&p->s, S("=="))) return (Token) { .type=TOKEN_OPER_EQL }; if (consume_str(&p->s, S("!="))) return (Token) { .type=TOKEN_OPER_NQL }; if (consume_str(&p->s, S("<"))) return (Token) { .type=TOKEN_OPER_LSS }; @@ -853,7 +707,7 @@ Token next_token(Parser *p) return (Token) { .type=TOKEN_ERROR }; } -Token next_token_or_newline(Parser *p) +static Token next_token_or_newline(Parser *p) { int peek = p->s.cur; while (peek < p->s.len && is_space(p->s.src[peek]) && p->s.src[peek] != '\n') @@ -873,104 +727,92 @@ enum { IGNORE_DIV = 1 << 2, }; -Node *parse_stmt(Parser *p, int opflags); -Node *parse_expr(Parser *p, int opflags); +static Node *parse_stmt(Parser *p, int opflags); +static Node *parse_expr(Parser *p, int opflags); -Node *parse_html(Parser *p) +static Node *parse_html(Parser *p) { // NOTE: The first < was already consumed - + Token t = next_token(p); if (t.type != TOKEN_IDENT) { - char buf[1<<8]; - String ts = tok2str(t, buf, COUNT(buf)); - parser_report(p, "HTML tag doesn't start with a name (got '%.*s' instead)", ts.len, ts.ptr); + parser_report(p, "HTML tag doesn't start with a name"); return NULL; } String tagname = t.sval; - Node *param_head; - Node **param_tail = ¶m_head; + Node *attr_head; + Node **attr_tail = &attr_head; bool no_body = false; + Scanner *s = &p->s; for (;;) { - String attr_name; - Node *attr_value; + int off = s->cur; - t = next_token(p); + bool quotes = false; + while (s->cur < s->len && s->src[s->cur] != '\\' && (quotes || (s->src[s->cur] != '/' && s->src[s->cur] != '>'))) { + if (s->src[s->cur] == '"') + quotes = !quotes; + s->cur++; + } - if (t.type == TOKEN_OPER_GRT) + if (s->cur > off) { + + Node *child = alloc_node(p); + if (child == NULL) + return NULL; + + child->type = NODE_VALUE_STR; + child->sval = (String) { p->s.src + off, p->s.cur - off }; + + *attr_tail = child; + attr_tail = &child->next; + } + + if (s->cur == s->len) { + ASSERT(0); // TODO + } + s->cur++; + + if (s->src[s->cur-1] == '>') break; - if (t.type == TOKEN_OPER_DIV) { - t = next_token(p); - if (t.type != TOKEN_OPER_GRT) { - parser_report(p, "Invalid token '/' inside an HTML tag"); - return NULL; + if (s->src[s->cur-1] == '/') { + while (s->cur < s->len && is_space(s->src[s->cur])) + s->cur++; + if (s->cur == s->len || s->src[s->cur] != '>') { + ASSERT(0); // TODO } + s->cur++; no_body = true; break; } - if (t.type != TOKEN_IDENT) { - parser_report(p, "Invalid token inside HTML tag"); - return NULL; - } - attr_name = t.sval; + ASSERT(s->src[s->cur-1] == '\\'); - Scanner saved = p->s; - t = next_token(p); - if (t.type == TOKEN_OPER_ASS) { - - attr_value = parse_expr(p, IGNORE_GRT | IGNORE_DIV); - if (attr_value == NULL) - return NULL; - - } else { - p->s = saved; - attr_value = NULL; - } - - Node *child = alloc_node(p); + Node *child = parse_stmt(p, IGNORE_GRT | IGNORE_DIV); if (child == NULL) return NULL; - child->type = NODE_HTML_PARAM; - child->attr_name = attr_name; - child->attr_value = attr_value; - - *param_tail = child; - param_tail = &child->next; + *attr_tail = child; + attr_tail = &child->next; } - *param_tail = NULL; + *attr_tail = NULL; - Node *head; - Node **tail = &head; - - if (!no_body) for (;;) { + Node *child_head; + Node **child_tail = &child_head; + if (no_body == false) for (;;) { - int off = p->s.cur; + int off = s->cur; - for (;;) { + while (s->cur < s->len && s->src[s->cur] != '\\' && s->src[s->cur] != '<') + s->cur++; - while (p->s.cur < p->s.len && p->s.src[p->s.cur] != '<' && p->s.src[p->s.cur] != '\\') - p->s.cur++; - - if (!consume_str(&p->s, S(""))) - break; - p->s.cur++; - } - } - - if (p->s.cur > off) { + if (s->cur > off) { Node *child = alloc_node(p); if (child == NULL) @@ -979,71 +821,75 @@ Node *parse_html(Parser *p) child->type = NODE_VALUE_STR; child->sval = (String) { p->s.src + off, p->s.cur - off }; - *tail = child; - tail = &child->next; + *child_tail = child; + child_tail = &child->next; } - if (p->s.cur == p->s.len || p->s.src[p->s.cur] == '<') - break; + if (s->cur == s->len) { + ASSERT(0); // TODO + } + s->cur++; - p->s.cur++; // Consume "\" + if (s->src[s->cur-1] == '<') { + + Scanner saved = *s; + t = next_token(p); + if (t.type == TOKEN_OPER_DIV) { + + t = next_token(p); + if (t.type != TOKEN_IDENT) { + ASSERT(0); // TODO + } + String closing_tagname = t.sval; + + if (!streq(closing_tagname, tagname)) { + ASSERT(0); // TODO + } + + t = next_token(p); + if (t.type != TOKEN_OPER_GRT) { + ASSERT(0); + } + + break; + } + + *s = saved; + + Node *child = parse_html(p); + if (child == NULL) + return NULL; + + *child_tail = child; + child_tail = &child->next; + + } else { - { Node *child = parse_stmt(p, IGNORE_LSS); if (child == NULL) return NULL; - *tail = child; - tail = &child->next; + *child_tail = child; + child_tail = &child->next; } } - if (p->s.cur == p->s.len) { - parser_report(p, "Missing closing HTML tag"); - return NULL; - } - p->s.cur++; // Consume < - - Scanner saved = p->s; - t = next_token(p); - if (t.type == TOKEN_OPER_DIV) { - t = next_token(p); - if (t.type == TOKEN_IDENT && streqcase(t.sval, tagname)) { - t = next_token(p); - if (t.type != TOKEN_OPER_GRT) { - parser_report(p, "Unexpected token in closing HTML tag"); - return NULL; - } - break; - } - } - - p->s = saved; - - Node *child = parse_html(p); - if (child == NULL) - return NULL; - - *tail = child; - tail = &child->next; - } - - *tail = NULL; + *child_tail = NULL; Node *parent = alloc_node(p); if (parent == NULL) return NULL; parent->type = NODE_VALUE_HTML; - parent->tagname = tagname; - parent->params = param_head; - parent->child = head; - parent->no_body = no_body; + parent->html_tag = tagname; + parent->html_attr = attr_head; + parent->html_child = child_head; + parent->html_body = !no_body; return parent; } -Node *parse_array(Parser *p) +static Node *parse_array(Parser *p) { // Left bracket already consumed @@ -1091,7 +937,7 @@ Node *parse_array(Parser *p) return parent; } -Node *parse_map(Parser *p) +static Node *parse_map(Parser *p) { // Left bracket already consumed @@ -1167,13 +1013,18 @@ Node *parse_map(Parser *p) return parent; } -int precedence(Token t, int flags) +static int precedence(Token t, int flags) { switch (t.type) { case TOKEN_OPER_ASS: return 1; + case TOKEN_OPER_SHOVEL: + if (flags & IGNORE_LSS) + return -1; + return 1; + case TOKEN_OPER_EQL: case TOKEN_OPER_NQL: return 2; @@ -1208,12 +1059,12 @@ int precedence(Token t, int flags) return -1; } -bool right_associative(Token t) +static bool right_associative(Token t) { return t.type == TOKEN_OPER_ASS; } -Node *parse_atom(Parser *p) +static Node *parse_atom(Parser *p) { Token t = next_token(p); @@ -1290,7 +1141,7 @@ Node *parse_atom(Parser *p) return NULL; node->type = NODE_VALUE_INT; - node->ival = t.uval; + node->ival = t.ival; ret = node; } @@ -1303,7 +1154,7 @@ Node *parse_atom(Parser *p) return NULL; node->type = NODE_VALUE_FLOAT; - node->dval = t.dval; + node->fval = t.fval; ret = node; } @@ -1434,9 +1285,7 @@ Node *parse_atom(Parser *p) default: { - char buf[1<<8]; - String str = tok2str(t, buf, COUNT(buf)); - parser_report(p, "Invalid token \'%.*s\' inside expression", str.len, str.ptr); + parser_report(p, "Invalid token inside expression"); } return NULL; } @@ -1516,7 +1365,7 @@ Node *parse_atom(Parser *p) break; if (t.type != TOKEN_COMMA) { - parser_report(p, "Expected ',' after argument in function call"); + parser_report(p, "Expected ',' after argument in procedure call"); return NULL; } } @@ -1528,7 +1377,7 @@ Node *parse_atom(Parser *p) if (parent == NULL) return NULL; - parent->type = NODE_FUNC_CALL; + parent->type = NODE_PROCEDURE_CALL; parent->left = ret; parent->right = arg_head; @@ -1543,7 +1392,7 @@ Node *parse_atom(Parser *p) return ret; } -Node *parse_expr_inner(Parser *p, Node *left, int min_prec, int flags) +static Node *parse_expr_inner(Parser *p, Node *left, int min_prec, int flags) { for (;;) { @@ -1595,7 +1444,8 @@ Node *parse_expr_inner(Parser *p, Node *left, int min_prec, int flags) case TOKEN_OPER_MUL: parent->type = NODE_OPER_MUL; break; case TOKEN_OPER_DIV: parent->type = NODE_OPER_DIV; break; case TOKEN_OPER_MOD: parent->type = NODE_OPER_MOD; break; - default: + case TOKEN_OPER_SHOVEL: parent->type = NODE_OPER_SHOVEL; break; + default: parser_report(p, "Operator not implemented"); return NULL; } @@ -1606,7 +1456,7 @@ Node *parse_expr_inner(Parser *p, Node *left, int min_prec, int flags) return left; } -Node *parse_expr(Parser *p, int flags) +static Node *parse_expr(Parser *p, int flags) { Node *left = parse_atom(p); if (left == NULL) @@ -1615,7 +1465,7 @@ Node *parse_expr(Parser *p, int flags) return parse_expr_inner(p, left, 0, flags); } -Node *parse_expr_stmt(Parser *p, int opflags) +static Node *parse_expr_stmt(Parser *p, int opflags) { Node *e = parse_expr(p, opflags); if (e == NULL) @@ -1624,7 +1474,7 @@ Node *parse_expr_stmt(Parser *p, int opflags) return e; } -Node *parse_ifelse_stmt(Parser *p, int opflags) +static Node *parse_ifelse_stmt(Parser *p, int opflags) { Token t = next_token(p); if (t.type != TOKEN_KWORD_IF) { @@ -1665,14 +1515,14 @@ Node *parse_ifelse_stmt(Parser *p, int opflags) return NULL; parent->type = NODE_IFELSE; - parent->left = if_stmt; - parent->right = else_stmt; - parent->cond = cond; + parent->if_cond = cond; + parent->if_branch1 = if_stmt; + parent->if_branch2 = else_stmt; return parent; } -Node *parse_for_stmt(Parser *p, int opflags) +static Node *parse_for_stmt(Parser *p, int opflags) { Token t = next_token(p); if (t.type != TOKEN_KWORD_FOR) { @@ -1734,7 +1584,7 @@ Node *parse_for_stmt(Parser *p, int opflags) return parent; } -Node *parse_while_stmt(Parser *p, int opflags) +static Node *parse_while_stmt(Parser *p, int opflags) { Token t = next_token(p); if (t.type != TOKEN_KWORD_WHILE) { @@ -1762,17 +1612,18 @@ Node *parse_while_stmt(Parser *p, int opflags) parent->type = NODE_WHILE; parent->left = stmt; - parent->cond = cond; + parent->while_cond = cond; + parent->while_body = stmt; return parent; } -Node *parse_block_stmt(Parser *p, bool curly) +static Node *parse_compound_stmt(Parser *p, bool global) { - if (curly) { + if (!global) { Token t = next_token(p); if (t.type != TOKEN_CURLY_OPEN) { - parser_report(p, "Missing '{' at the start of a block statement"); + parser_report(p, "Missing '{' at the start of a compound statement"); return NULL; } } @@ -1784,7 +1635,7 @@ Node *parse_block_stmt(Parser *p, bool curly) Scanner saved = p->s; Token t = next_token(p); - if (curly) { + if (!global) { if (t.type == TOKEN_CURLY_CLOSE) break; } else { @@ -1807,30 +1658,30 @@ Node *parse_block_stmt(Parser *p, bool curly) if (parent == NULL) return NULL; - parent->type = NODE_BLOCK; + parent->type = global ? NODE_GLOBAL : NODE_COMPOUND; parent->left = head; return parent; } -Node *parse_func_decl(Parser *p, int opflags) +static Node *parse_proc_decl(Parser *p, int opflags) { Token t = next_token(p); - if (t.type != TOKEN_KWORD_FUN) { - parser_report(p, "Missing keyword 'fun' at the start of a function declaration"); + if (t.type != TOKEN_KWORD_PROCEDURE) { + parser_report(p, "Missing keyword 'procedure' at the start of a procedure declaration"); return NULL; } t = next_token(p); if (t.type != TOKEN_IDENT) { - parser_report(p, "Missing function name after 'fun' keyword"); + parser_report(p, "Missing procedure name after 'procedure' keyword"); return NULL; } String name = t.sval; t = next_token(p); if (t.type != TOKEN_PAREN_OPEN) { - parser_report(p, "Missing '(' after function name in declaration"); + parser_report(p, "Missing '(' after procedure name in declaration"); return NULL; } @@ -1846,7 +1697,7 @@ Node *parse_func_decl(Parser *p, int opflags) t = next_token(p); if (t.type != TOKEN_IDENT) { - parser_report(p, "Missing argument name in function declaration"); + parser_report(p, "Missing argument name in procedure declaration"); return NULL; } String argname = t.sval; @@ -1855,7 +1706,7 @@ Node *parse_func_decl(Parser *p, int opflags) if (node == NULL) return NULL; - node->type = NODE_FUNC_ARG; + node->type = NODE_PROCEDURE_ARG; node->sval = argname; *arg_tail = node; @@ -1884,15 +1735,15 @@ Node *parse_func_decl(Parser *p, int opflags) if (parent == NULL) return NULL; - parent->type = NODE_FUNC_DECL; - parent->func_name = name; - parent->func_args = arg_head; - parent->func_body = body; + parent->type = NODE_PROCEDURE_DECL; + parent->proc_name = name; + parent->proc_args = arg_head; + parent->proc_body = body; return parent; } -Node *parse_var_decl(Parser *p, int opflags) +static Node *parse_var_decl(Parser *p, int opflags) { Token t = next_token(p); if (t.type != TOKEN_KWORD_LET) { @@ -1933,29 +1784,7 @@ Node *parse_var_decl(Parser *p, int opflags) return parent; } -Node *parse_print_stmt(Parser *p, int opflags) -{ - Token t = next_token(p); - if (t.type != TOKEN_KWORD_PRINT) { - parser_report(p, "Missing keyword 'print' at the start of a print statement"); - return NULL; - } - - Node *arg = parse_expr(p, opflags); - if (arg == NULL) - return NULL; - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->type = NODE_PRINT; - parent->left = arg; - - return parent; -} - -Node *parse_include_stmt(Parser *p) +static Node *parse_include_stmt(Parser *p) { Token t = next_token(p); if (t.type != TOKEN_KWORD_INCLUDE) { @@ -1984,7 +1813,7 @@ Node *parse_include_stmt(Parser *p) return parent; } -Node *parse_stmt(Parser *p, int opflags) +static Node *parse_stmt(Parser *p, int opflags) { Scanner saved = p->s; Token t = next_token(p); @@ -1995,11 +1824,8 @@ Node *parse_stmt(Parser *p, int opflags) case TOKEN_KWORD_INCLUDE: return parse_include_stmt(p); - case TOKEN_KWORD_PRINT: - return parse_print_stmt(p, opflags); - - case TOKEN_KWORD_FUN: - return parse_func_decl(p, opflags); + case TOKEN_KWORD_PROCEDURE: + return parse_proc_decl(p, opflags); case TOKEN_KWORD_LET: return parse_var_decl(p, opflags); @@ -2014,7 +1840,7 @@ Node *parse_stmt(Parser *p, int opflags) return parse_for_stmt(p, opflags); case TOKEN_CURLY_OPEN: - return parse_block_stmt(p, true); + return parse_compound_stmt(p, false); default: break; @@ -2023,357 +1849,313 @@ Node *parse_stmt(Parser *p, int opflags) return parse_expr_stmt(p, opflags); } -void print_node(Node *node) +static void write_node(Writer *w, Node *node) { switch (node->type) { - case NODE_VALUE_NONE: - printf("none"); - break; - - case NODE_VALUE_TRUE: - printf("true"); - break; - - case NODE_VALUE_FALSE: - printf("false"); - break; + case NODE_VALUE_NONE : write_text(w, S("none")); break; + case NODE_VALUE_TRUE : write_text(w, S("true")); break; + case NODE_VALUE_FALSE: write_text(w, S("false")); break; case NODE_NESTED: - { - printf("("); - print_node(node->left); - printf(")"); - } + write_text(w, S("(nested ")); + write_node(w, node->left); + write_text(w, S(")")); break; - case NODE_PRINT: + case NODE_COMPOUND: { - printf("print "); - print_node(node->left); - } - break; - - case NODE_BLOCK: - { - printf("{"); + write_text(w, S("(compound ")); Node *cur = node->left; while (cur) { - print_node(cur); - printf(";"); + write_node(w, cur); cur = cur->next; + if (cur) + write_text(w, S(" ")); } - printf("}"); + write_text(w, S(")")); + } + break; + + case NODE_GLOBAL: + { + write_text(w, S("(global ")); + Node *cur = node->left; + while (cur) { + write_node(w, cur); + cur = cur->next; + if (cur) + write_text(w, S(" ")); + } + write_text(w, S(")")); } break; case NODE_OPER_LEN: - printf("len("); - print_node(node->left); - printf(")"); + write_text(w, S("(len ")); + write_node(w, node->left); + write_text(w, S(")")); break; case NODE_OPER_POS: - printf("("); - printf("+"); - print_node(node->left); - printf(")"); + write_text(w, S("(+")); + write_node(w, node->left); + write_text(w, S(")")); break; case NODE_OPER_NEG: - printf("("); - printf("-"); - print_node(node->left); - printf(")"); + write_text(w, S("(")); + write_text(w, S("-")); + write_node(w, node->left); + write_text(w, S(")")); break; case NODE_OPER_ASS: - printf("("); - print_node(node->left); - printf("="); - print_node(node->right); - printf(")"); + write_text(w, S("(")); + write_node(w, node->left); + write_text(w, S("=")); + write_node(w, node->right); + write_text(w, S(")")); break; case NODE_OPER_EQL: - printf("("); - print_node(node->left); - printf("=="); - print_node(node->right); - printf(")"); + write_text(w, S("(")); + write_node(w, node->left); + write_text(w, S("==")); + write_node(w, node->right); + write_text(w, S(")")); break; case NODE_OPER_NQL: - printf("("); - print_node(node->left); - printf("!="); - print_node(node->right); - printf(")"); + write_text(w, S("(")); + write_node(w, node->left); + write_text(w, S("!=")); + write_node(w, node->right); + write_text(w, S(")")); break; case NODE_OPER_LSS: - printf("("); - print_node(node->left); - printf("<"); - print_node(node->right); - printf(")"); + write_text(w, S("(")); + write_node(w, node->left); + write_text(w, S("<")); + write_node(w, node->right); + write_text(w, S(")")); break; case NODE_OPER_GRT: - printf("("); - print_node(node->left); - printf(">"); - print_node(node->right); - printf(")"); + write_text(w, S("(")); + write_node(w, node->left); + write_text(w, S(">")); + write_node(w, node->right); + write_text(w, S(")")); break; case NODE_OPER_ADD: - printf("("); - print_node(node->left); - printf("+"); - print_node(node->right); - printf(")"); + write_text(w, S("(")); + write_node(w, node->left); + write_text(w, S("+")); + write_node(w, node->right); + write_text(w, S(")")); break; case NODE_OPER_SUB: - printf("("); - print_node(node->left); - printf("-"); - print_node(node->right); - printf(")"); + write_text(w, S("(")); + write_node(w, node->left); + write_text(w, S("-")); + write_node(w, node->right); + write_text(w, S(")")); break; case NODE_OPER_MUL: - printf("("); - print_node(node->left); - printf("*"); - print_node(node->right); - printf(")"); + write_text(w, S("(")); + write_node(w, node->left); + write_text(w, S("*")); + write_node(w, node->right); + write_text(w, S(")")); break; case NODE_OPER_DIV: - printf("("); - print_node(node->left); - printf("/"); - print_node(node->right); - printf(")"); + write_text(w, S("(")); + write_node(w, node->left); + write_text(w, S("/")); + write_node(w, node->right); + write_text(w, S(")")); break; case NODE_OPER_MOD: - printf("("); - print_node(node->left); - printf("%%"); - print_node(node->right); - printf(")"); + write_text(w, S("(")); + write_node(w, node->left); + write_text(w, S("%%")); + write_node(w, node->right); + write_text(w, S(")")); break; case NODE_VALUE_INT: - printf("%" LLU, node->ival); + write_text_s64(w, node->ival); break; case NODE_VALUE_FLOAT: - printf("%f", node->dval); + write_text_f64(w, node->fval); break; case NODE_VALUE_STR: - printf("\"%.*s\"", node->sval.len, node->sval.ptr); + write_text(w, S("\"")); + write_text(w, node->sval); + write_text(w, S("\"")); break; case NODE_VALUE_VAR: - printf("%.*s", node->sval.len, node->sval.ptr); + write_text(w, node->sval); break; case NODE_VALUE_SYSVAR: - printf("$%.*s", node->sval.len, node->sval.ptr); + write_text(w, S("$")); + write_text(w, node->sval); break; case NODE_IFELSE: - printf("if "); - print_node(node->cond); - printf(":"); - print_node(node->left); - if (node->right) { - printf(" else "); - print_node(node->right); + write_text(w, S("(if ")); + write_node(w, node->if_cond); + write_text(w, S(" ")); + write_node(w, node->if_branch1); + if (node->if_branch2) { + write_text(w, S(" else ")); + write_node(w, node->if_branch2); } + write_text(w, S(")")); break; case NODE_WHILE: - printf("while "); - print_node(node->cond); - printf(":"); - print_node(node->left); + write_text(w, S("(while ")); + write_node(w, node->while_cond); + write_text(w, S(" ")); + write_node(w, node->while_body); + write_text(w, S(")")); break; case NODE_VALUE_HTML: { - printf("<%.*s", - node->tagname.len, - node->tagname.ptr - ); + write_text(w, S("(html ")); + write_text(w, node->html_tag); - Node *param = node->params; - while (param) { - if (param->attr_value) { - printf(" %.*s=", - param->attr_name.len, - param->attr_name.ptr); - print_node(param->attr_value); - } else { - printf(" %.*s", - param->attr_name.len, - param->attr_name.ptr - ); - } - param = param->next; - } - printf(">"); - - Node *child = node->child; + Node *child = node->html_child; while (child) { - print_node(child); + write_text(w, S(" ")); + write_node(w, child); child = child->next; } - printf("", - node->tagname.len, - node->tagname.ptr - ); + write_text(w, S(")")); } break; case NODE_FOR: - { - printf("for %.*s", - node->for_var1.len, - node->for_var1.ptr - ); - if (node->for_var2.len > 0) { - printf(", %.*s", - node->for_var2.len, - node->for_var2.ptr - ); - } - printf(" in "); - print_node(node->for_set); - printf(": "); - print_node(node->left); + write_text(w, S("(for ")); + write_text(w, node->for_var1); + if (node->for_var2.len > 0) { + write_text(w, S(", ")); + write_text(w, node->for_var2); } + write_text(w, S(" in ")); + write_node(w, node->for_set); + write_text(w, S(": ")); + write_node(w, node->left); break; case NODE_SELECT: - { - print_node(node->left); - printf("["); - print_node(node->right); - printf("]"); - } + write_node(w, node->left); + write_text(w, S("[")); + write_node(w, node->right); + write_text(w, S("]")); break; case NODE_VALUE_ARRAY: { - printf("["); + write_text(w, S("[")); Node *child = node->child; while (child) { - print_node(child); - printf(", "); + write_node(w, child); + write_text(w, S(", ")); child = child->next; } - printf("]"); + write_text(w, S("]")); } break; case NODE_VALUE_MAP: { - printf("{"); + write_text(w, S("{")); Node *child = node->child; while (child) { - print_node(child->key); - printf(": "); - print_node(child); - printf(", "); + write_node(w, child->key); + write_text(w, S(": ")); + write_node(w, child); + write_text(w, S(", ")); child = child->next; } - printf("}"); + write_text(w, S("}")); } break; - case NODE_HTML_PARAM: + case NODE_PROCEDURE_DECL: { - printf("???"); - } - break; - - case NODE_FUNC_DECL: - { - printf("fun %.*s(", - node->func_name.len, - node->func_name.ptr); - Node *arg = node->func_args; + write_text(w, S("(proc ")); + write_text(w, node->proc_name); + write_text(w, S("(")); + Node *arg = node->proc_args; while (arg) { - print_node(arg); + write_node(w, arg); arg = arg->next; if (arg) - printf(", "); + write_text(w, S(", ")); } - printf(")"); - print_node(node->func_body); + write_text(w, S(")")); + write_node(w, node->proc_body); } break; - case NODE_FUNC_ARG: - { - printf("%.*s", node->sval.len, node->sval.ptr); - } + case NODE_PROCEDURE_ARG: + write_text(w, node->sval); break; - case NODE_FUNC_CALL: + case NODE_PROCEDURE_CALL: { - print_node(node->left); - printf("("); + write_node(w, node->left); + write_text(w, S("(")); Node *arg = node->right; while (arg) { - print_node(arg); + write_node(w, arg); arg = arg->next; if (arg) - printf(", "); + write_text(w, S(", ")); } - printf(")"); + write_text(w, S(")")); } break; case NODE_VAR_DECL: - { - printf("let %.*s", - node->var_name.len, - node->var_name.ptr); - if (node->var_value) { - printf(" = "); - print_node(node->var_value); - } - //printf(";"); + write_text(w, S("(let ")); + write_text(w, node->var_name); + if (node->var_value) { + write_text(w, S(" = ")); + write_node(w, node->var_value); } + write_text(w, S(")")); break; case NODE_INCLUDE: - { - printf("include \"%.*s\"", - node->include_path.len, - node->include_path.ptr); - } - break; - - default: - printf("(invalid node type %x)", node->type); + write_text(w, S("include \"")); + write_text(w, node->include_path); + write_text(w, S("\"")); break; } } -ParseResult parse(String src, WL_Arena *a, char *errbuf, int errmax) +static ParseResult parse(String src, WL_Arena *arena, char *errbuf, int errmax) { Parser p = { .s={ src.ptr, src.len, 0 }, - .a=a, + .arena=arena, .errbuf=errbuf, .errmax=errmax, .errlen=0, @@ -2381,291 +2163,203 @@ ParseResult parse(String src, WL_Arena *a, char *errbuf, int errmax) p.include_tail = &p.include_head; - Node *node = parse_block_stmt(&p, false); + Node *node = parse_compound_stmt(&p, true); if (node == NULL) return (ParseResult) { .node=NULL, .includes=NULL, .errlen=p.errlen }; - assert(node->type == NODE_BLOCK); - node->type = NODE_GLOBAL_BLOCK; - *p.include_tail = NULL; return (ParseResult) { .node=node, .includes=p.include_head, .errlen=-1 }; } -//////////////////////////////////////////////////////////////////////////////////////// -// src/assemble.h -//////////////////////////////////////////////////////////////////////////////////////// - -#ifndef WL_ASSEMBLE_INCLUDED -#define WL_ASSEMBLE_INCLUDED - -#ifndef WL_AMALGAMATION -#include "public.h" -#include "parse.h" -#endif +///////////////////////////////////////////////////////////////////////// +// CODEGEN +///////////////////////////////////////////////////////////////////////// enum { - OPCODE_NOPE = 0x00, - OPCODE_EXIT = 0x23, - OPCODE_GROUP = 0x25, - OPCODE_GPOP = 0x26, - OPCODE_GPRINT = 0x27, - OPCODE_GTRUNC = 0x28, - OPCODE_GCOALESCE = 0x29, - OPCODE_GOVERWRITE = 0x2A, - OPCODE_GPACK = 0x2B, - OPCODE_PUSHI = 0x01, - OPCODE_PUSHF = 0x02, - OPCODE_PUSHS = 0x03, - OPCODE_PUSHV = 0x04, - OPCODE_PUSHA = 0x05, - OPCODE_PUSHM = 0x06, - OPCODE_PUSHN = 0x21, - OPCODE_POP = 0x07, - OPCODE_NEG = 0x08, - OPCODE_EQL = 0x09, - OPCODE_NQL = 0x0A, - OPCODE_LSS = 0x0B, - OPCODE_GRT = 0x0C, - OPCODE_ADD = 0x0D, - OPCODE_SUB = 0x0E, - OPCODE_MUL = 0x0F, - OPCODE_DIV = 0x10, - OPCODE_MOD = 0x11, - OPCODE_SETV = 0x12, - OPCODE_JUMP = 0x13, - OPCODE_JIFP = 0x14, - OPCODE_CALL = 0x15, - OPCODE_RET = 0x16, - OPCODE_APPEND = 0x17, - OPCODE_INSERT1 = 0x18, - OPCODE_INSERT2 = 0x19, - OPCODE_SELECT = 0x20, - OPCODE_PRINT = 0x24, - OPCODE_SYSVAR = 0x2C, - OPCODE_SYSCALL = 0x2D, - OPCODE_FOR = 0x2E, - OPCODE_PUSHT = 0x2F, - OPCODE_PUSHFL = 0x30, - OPCODE_LEN = 0x31, + OPCODE_NOPE, + OPCODE_JUMP, + OPCODE_JIFP, + OPCODE_OUTPUT, + OPCODE_SYSVAR, + OPCODE_SYSCALL, + OPCODE_CALL, + OPCODE_RET, + OPCODE_GROUP, + OPCODE_PACK, + OPCODE_GPOP, + OPCODE_FOR, + OPCODE_EXIT, + OPCODE_VARS, + OPCODE_POP, + OPCODE_SETV, + OPCODE_PUSHV, + OPCODE_PUSHI, + OPCODE_PUSHF, + OPCODE_PUSHS, + OPCODE_PUSHA, + OPCODE_PUSHM, + OPCODE_PUSHN, + OPCODE_PUSHT, + OPCODE_PUSHFL, + OPCODE_LEN, + OPCODE_NEG, + OPCODE_EQL, + OPCODE_NQL, + OPCODE_LSS, + OPCODE_GRT, + OPCODE_ADD, + OPCODE_SUB, + OPCODE_MUL, + OPCODE_DIV, + OPCODE_MOD, + OPCODE_APPEND, + OPCODE_INSERT1, + OPCODE_INSERT2, + OPCODE_SELECT, }; -typedef struct { - WL_Program program; - int errlen; -} AssembleResult; - -int parse_program_header(WL_Program p, String *code, String *data, char *errbuf, int errmax); -void print_program(WL_Program program); -char *print_instruction(char *p, char *data); -AssembleResult assemble(Node *root, WL_Arena *arena, char *errbuf, int errmax); - -#endif // WL_ASSEMBLE_INCLUDED - -//////////////////////////////////////////////////////////////////////////////////////// -// src/assemble.c -//////////////////////////////////////////////////////////////////////////////////////// - - -#ifndef WL_AMALGAMATION -#include "includes.h" -#include "parse.h" -#include "assemble.h" -#endif - -#define MAX_SCOPES 32 -#define MAX_SYMBOLS 1024 -#define MAX_DEPTH 128 - -typedef struct FunctionCall FunctionCall; -struct FunctionCall { - FunctionCall *next; - String name; - int off; +typedef struct UnpatchedCall UnpatchedCall; +struct UnpatchedCall { + UnpatchedCall *next; + String name; + int off; }; typedef enum { - SYMBOL_VAR, - SYMBOL_FUNC, + SYMBOL_VARIABLE, + SYMBOL_PROCEDURE, } SymbolType; typedef struct { SymbolType type; String name; + bool cnst; int off; } Symbol; typedef enum { - SCOPE_GLOBAL, - SCOPE_FUNC, - SCOPE_FOR, - SCOPE_WHILE, SCOPE_IF, SCOPE_ELSE, - SCOPE_BLOCK, - SCOPE_HTML, + SCOPE_FOR, + SCOPE_WHILE, + SCOPE_PROC, + SCOPE_COMPOUND, + SCOPE_GLOBAL, + SCOPE_ASSIGNMENT, } ScopeType; typedef struct { - ScopeType type; - int sym_base; - int max_vars; - FunctionCall* calls; + ScopeType type; + int idx_syms; + int max_vars; + UnpatchedCall *calls; } Scope; -typedef struct { - char *ptr; - int len; - int cap; - bool err; -} OutputBuffer; +#define MAX_SYMBOLS 1024 +#define MAX_SCOPES 128 +#define MAX_UNPATCHED_CALLS 32 typedef struct { - WL_Arena *a; - - OutputBuffer out; - - int num_syms; - Symbol syms[MAX_SYMBOLS]; + Writer code; + Writer data; int num_scopes; Scope scopes[MAX_SCOPES]; - int strings_len; - int strings_cap; - char *strings; + int num_syms; + Symbol syms[MAX_SYMBOLS]; - char *errbuf; - int errmax; - int errlen; + UnpatchedCall *free_list_calls; + UnpatchedCall calls[MAX_UNPATCHED_CALLS]; -} Assembler; + bool err; + char *errmsg; + int errcap; -void assembler_report(Assembler *a, char *fmt, ...) + int data_off; + +} Codegen; + +static void cg_report(Codegen *cg, char *fmt, ...) { - if (a->errmax == 0 || a->errlen > 0) - return; - - int len = snprintf(a->errbuf, a->errmax, "Error: "); - if (len < 0) { - // TODO - } + if (cg->err) return; va_list args; va_start(args, fmt); - int ret = vsnprintf(a->errbuf + len, a->errmax - len, fmt, args); + int len = vsnprintf(cg->errmsg, cg->errcap, fmt, args); va_end(args); - if (ret < 0) { - // TODO - } - len += ret; - a->errlen = len; + if (len > cg->errcap) + len = cg->errcap-1; + + cg->errmsg[len] = '\0'; + cg->err = true; } -int add_string_literal(Assembler *a, String str) +static int cg_write_u8(Codegen *cg, uint8_t x) { - if (a->strings_cap - a->strings_len < str.len) { - int c = MAX(2 * a->strings_cap, a->strings_len + str.len); - char *p = malloc(c); - if (p == NULL) { - assembler_report(a, "Out of memory"); - return -1; - } - if (a->strings_cap) { - memcpy(p, a->strings, a->strings_len); - free(a->strings); - } - - a->strings = p; - a->strings_cap = c; - } - - int off = a->strings_len; - memcpy(a->strings + a->strings_len, str.ptr, str.len); - a->strings_len += str.len; + if (cg->err) return -1; + int off = cg->code.len; + write_raw_u8(&cg->code, x); return off; } -void append_mem(OutputBuffer *out, void *ptr, int len) +static int cg_write_u32(Codegen *cg, uint32_t x) { - if (out->err) - return; + if (cg->err) return -1; - if (out->cap - out->len < len) { - - int new_cap = MAX(out->len + len, 2 * out->cap); - char *new_ptr = malloc(new_cap); - if (new_ptr == NULL) { - out->err = true; - return; - } - - if (out->cap) { - memcpy(new_ptr, out->ptr, out->len); - free(out->ptr); - } - - out->ptr = new_ptr; - out->cap = new_cap; - } - - memcpy(out->ptr + out->len, ptr, len); - out->len += len; -} - -void patch_mem(OutputBuffer *out, int off, void *ptr, int len) -{ - if (out->err) - return; - - memcpy(out->ptr + off, ptr, len); -} - -int append_u8(OutputBuffer *out, uint8_t x) -{ - int off = out->len; - append_mem(out, &x, (int) sizeof(x)); + int off = cg->code.len; + write_raw_u32(&cg->code, x); return off; } -int append_u32(OutputBuffer *out, uint32_t x) +static int cg_write_s64(Codegen *cg, int64_t x) { - int off = out->len; - append_mem(out, &x, (int) sizeof(x)); + if (cg->err) return -1; + + int off = cg->code.len; + write_raw_s64(&cg->code, x); return off; } -int append_s64(OutputBuffer *out, int64_t x) +static int cg_write_f64(Codegen *cg, double x) { - int off = out->len; - append_mem(out, &x, (int) sizeof(x)); + if (cg->err) return -1; + + int off = cg->code.len; + write_raw_f64(&cg->code, x); return off; } -int append_f64(OutputBuffer *out, double x) +static void cg_write_str(Codegen *cg, String x) { - int off = out->len; - append_mem(out, &x, (int) sizeof(x)); - return off; + if (cg->err) return; + + int off = cg->data.len; + write_text(&cg->data, x); + write_raw_u32(&cg->code, off); + write_raw_u32(&cg->code, x.len); } -void patch_with_current_offset(OutputBuffer *out, int off) +static void cg_patch_u8(Codegen *cg, int off, uint8_t x) { - uint32_t x = out->len; - patch_mem(out, off, &x, (int) sizeof(x)); + if (cg->err) return; + + patch_mem(&cg->code, &x, off, SIZEOF(x)); } -void patch_u32(OutputBuffer *out, int off, uint32_t x) +static void cg_patch_u32(Codegen *cg, int off, uint32_t x) { - patch_mem(out, off, &x, (int) sizeof(x)); + if (cg->err) return; + + patch_mem(&cg->code, &x, off, SIZEOF(x)); } -int current_offset(OutputBuffer *out) +static uint32_t cg_current_offset(Codegen *cg) { - return out->len; + return cg->code.len; } int count_nodes(Node *head) @@ -2679,724 +2373,258 @@ int count_nodes(Node *head) return n; } -Scope *parent_scope(Assembler *a) +static Scope *parent_scope(Codegen *cg) { - assert(a->num_scopes > 0); + ASSERT(cg->num_scopes > 0); - int parent = a->num_scopes-1; - while (a->scopes[parent].type != SCOPE_FUNC - && a->scopes[parent].type != SCOPE_GLOBAL) + int parent = cg->num_scopes-1; + while (cg->scopes[parent].type != SCOPE_PROC && cg->scopes[parent].type != SCOPE_GLOBAL) parent--; - Scope *scope = &a->scopes[parent]; - - assert(scope->type == SCOPE_GLOBAL - || scope->type == SCOPE_FUNC); - - return scope; + return &cg->scopes[parent]; } -bool global_scope(Assembler *a) +static bool inside_assignment(Codegen *cg) { - return parent_scope(a)->type == SCOPE_GLOBAL; + ASSERT(cg->num_scopes > 0); + + int parent = cg->num_scopes-1; + while (cg->scopes[parent].type != SCOPE_PROC + && cg->scopes[parent].type != SCOPE_GLOBAL + && cg->scopes[parent].type != SCOPE_ASSIGNMENT) + parent--; + + return cg->scopes[parent].type == SCOPE_ASSIGNMENT; } -Symbol *find_symbol_in_local_scope(Assembler *a, String name) -{ - if (name.len == 0) - return NULL; - - Scope *scope = &a->scopes[a->num_scopes-1]; - for (int i = a->num_syms-1; i >= scope->sym_base; i--) - if (streq(a->syms[i].name, name)) - return &a->syms[i]; - return NULL; -} - -Symbol *find_symbol_in_function(Assembler *a, String name) -{ - if (name.len == 0) - return NULL; - - Scope *scope = parent_scope(a); - for (int i = a->num_syms-1; i >= scope->sym_base; i--) - if (streq(a->syms[i].name, name)) - return &a->syms[i]; - return NULL; -} - -int count_local_vars(Assembler *a) +static int count_function_vars(Codegen *cg) { int n = 0; - Scope *scope = parent_scope(a); - for (int i = scope->sym_base; i < a->num_syms; i++) - if (a->syms[i].type == SYMBOL_VAR) + Scope *scope = parent_scope(cg); + for (int i = scope->idx_syms; i < cg->num_syms; i++) + if (cg->syms[i].type == SYMBOL_VARIABLE) n++; return n; } -int declare_variable(Assembler *a, String name) +static Symbol *cg_find_symbol(Codegen *cg, String name, bool local) { - if (a->num_syms == MAX_SYMBOLS) { - assembler_report(a, "Symbol limit reached"); + if (cg->err) return NULL; + + if (name.len == 0) return NULL; + ASSERT(cg->num_scopes > 0); + Scope *scope = local ? &cg->scopes[cg->num_scopes-1] : parent_scope(cg); + for (int i = cg->num_syms-1; i >= scope->idx_syms; i--) + if (streq(cg->syms[i].name, name)) + return &cg->syms[i]; + return NULL; +} + +static int cg_declare_variable(Codegen *cg, String name, bool cnst) +{ + if (cg->err) return -1; + + Symbol *sym = cg_find_symbol(cg, name, true); + if (sym) { + cg_report(cg, "Variable declared twice"); return -1; } - if (find_symbol_in_local_scope(a, name)) { - assembler_report(a, "Symbol '%.*s' already declared in this scope", - name.len, name.ptr); + if (cg->num_syms == MAX_SYMBOLS) { + cg_report(cg, "Symbol count limit reached"); return -1; } - int off = count_local_vars(a); - a->syms[a->num_syms++] = (Symbol) { SYMBOL_VAR, name, off }; + int off = count_function_vars(cg); - Scope *scope = parent_scope(a); - - if (scope->max_vars < off + 1) - scope->max_vars = off + 1; + Scope *parent = parent_scope(cg); + parent->max_vars = MAX(parent->max_vars, off+1); + cg->syms[cg->num_syms++] = (Symbol) { + .type = SYMBOL_VARIABLE, + .name = name, + .cnst = cnst, + .off = off, + }; return off; } -int declare_function(Assembler *a, String name, int off) +static void cg_declare_procedure(Codegen *cg, String name, int off) { - if (a->num_syms == MAX_SYMBOLS) { - assembler_report(a, "Symbol limit reached"); - return -1; + if (cg->err) return; + + Symbol *sym = cg_find_symbol(cg, name, true); + if (sym) { + cg_report(cg, "Procedure declared twice"); + return; } - if (find_symbol_in_local_scope(a, name)) { - assembler_report(a, "Symbol '%.*s' already declared in this scope", name.len, name.ptr); - return -1; + if (cg->num_syms == MAX_SYMBOLS) { + cg_report(cg, "Symbol count limit reached"); + return; } - a->syms[a->num_syms++] = (Symbol) { SYMBOL_FUNC, name, off }; - return 0; + cg->syms[cg->num_syms++] = (Symbol) { + .type = SYMBOL_PROCEDURE, + .name = name, + .cnst = true, + .off = off, + }; } -bool is_expr(Node *node) +static void cg_push_scope(Codegen *cg, ScopeType type) { - switch (node->type) { + if (cg->err) return; - default: - break; - - case NODE_SELECT: - case NODE_NESTED: - case NODE_FUNC_CALL: - case NODE_OPER_LEN: - case NODE_OPER_POS: - case NODE_OPER_NEG: - case NODE_OPER_ASS: - case NODE_OPER_EQL: - case NODE_OPER_NQL: - case NODE_OPER_LSS: - case NODE_OPER_GRT: - case NODE_OPER_ADD: - case NODE_OPER_SUB: - case NODE_OPER_MUL: - case NODE_OPER_DIV: - case NODE_OPER_MOD: - case NODE_VALUE_INT: - case NODE_VALUE_FLOAT: - case NODE_VALUE_STR: - case NODE_VALUE_NONE: - case NODE_VALUE_TRUE: - case NODE_VALUE_FALSE: - case NODE_VALUE_VAR: - case NODE_VALUE_SYSVAR: - case NODE_VALUE_HTML: - case NODE_VALUE_ARRAY: - case NODE_VALUE_MAP: - return true; + if (cg->num_scopes == MAX_SCOPES) { + cg_report(cg, "Scope limit reached"); + return; } - return false; -} - -int push_scope(Assembler *a, ScopeType type) -{ - if (a->num_scopes == MAX_SCOPES) { - assembler_report(a, "Scope limit reached"); - return -1; - } - Scope *scope = &a->scopes[a->num_scopes++]; + Scope *scope = &cg->scopes[cg->num_scopes++]; scope->type = type; - scope->sym_base = a->num_syms; + scope->idx_syms = cg->num_syms; scope->max_vars = 0; scope->calls = NULL; - return 0; } -int pop_scope(Assembler *a) +static void cg_pop_scope(Codegen *cg) { - Scope *scope = &a->scopes[a->num_scopes-1]; + if (cg->err) return; - FunctionCall *call = scope->calls; - FunctionCall **prev = &scope->calls; - while (call) { + ASSERT(cg->num_scopes > 0); + Scope *scope = &cg->scopes[cg->num_scopes-1]; - Symbol *sym = find_symbol_in_local_scope(a, call->name); + Scope *parent_scope = NULL; + if (cg->num_scopes > 1) + parent_scope = &cg->scopes[cg->num_scopes-2]; + + while (scope->calls) { + + UnpatchedCall *call = scope->calls; + scope->calls = call->next; + + ASSERT(call - cg->calls >= 0 && call - cg->calls < MAX_UNPATCHED_CALLS); + + Symbol *sym = cg_find_symbol(cg, call->name, true); if (sym == NULL) { - prev = &call->next; - call = call->next; + if (parent_scope == NULL) { + cg_report(cg, "Undefined function '%.*s'", + scope->calls->name.len, + scope->calls->name.ptr); + return; + } + call->next = parent_scope->calls; + parent_scope->calls = call; continue; } - if (sym->type != SYMBOL_FUNC) { - assembler_report(a, "Symbol '%.*s' is not a function", call->name.len, call->name.ptr); - return -1; + if (sym->type != SYMBOL_PROCEDURE) { + cg_report(cg, "Symbol '%.*s' is not a procedure", call->name.len, call->name.ptr); + return; } - patch_u32(&a->out, call->off, sym->off); + cg_patch_u32(cg, call->off, sym->off); - *prev = call->next; - call = call->next; + call->next = cg->free_list_calls; + cg->free_list_calls = call; + + // TODO: remove + ASSERT(cg->scopes[cg->num_scopes-1].calls == NULL || (cg->scopes[cg->num_scopes-1].calls - cg->calls >= 0 && cg->scopes[cg->num_scopes-1].calls - cg->calls < MAX_UNPATCHED_CALLS)); } - if (scope->calls) { - - if (a->num_scopes == 1) { - assembler_report(a, "Undefined function '%.*s'", - scope->calls->name.len, - scope->calls->name.ptr); - return -1; - } - - Scope *parent_scope = &a->scopes[a->num_scopes-2]; - *prev = parent_scope->calls; - parent_scope->calls = scope->calls; - } - - a->num_syms = scope->sym_base; - a->num_scopes--; - return 0; + cg->num_syms = scope->idx_syms; + cg->num_scopes--; } -void assemble_statement(Assembler *a, Node *node, bool pop_expr); - -typedef struct { - OutputBuffer tmp; -} HTMLAssembler; - -void write_buffered_html(Assembler *a, HTMLAssembler *ha) +static void cg_append_unpatched_call(Codegen *cg, String name, int p) { - if (ha->tmp.len == 0) + if (cg->err) return; + + if (cg->free_list_calls == NULL) { + cg_report(cg, "Out of memory"); return; + } + UnpatchedCall *call = cg->free_list_calls; + cg->free_list_calls = call->next; - int off = add_string_literal(a, (String) { ha->tmp.ptr, ha->tmp.len }); - append_u8(&a->out, OPCODE_PUSHS); - append_u32(&a->out, off); - append_u32(&a->out, ha->tmp.len); + ASSERT(call - cg->calls >= 0 && call - cg->calls < MAX_UNPATCHED_CALLS); - free(ha->tmp.ptr); - ha->tmp.ptr = NULL; - ha->tmp.len = 0; - ha->tmp.cap = 0; + call->name = name; + call->off = p; + call->next = NULL; + + ASSERT(cg->num_scopes > 0); + Scope *scope = &cg->scopes[cg->num_scopes-1]; + + call->next = scope->calls; + scope->calls = call; } -void assemble_html_2(Assembler *a, HTMLAssembler *ha, Node *node) +static bool cg_global_scope(Codegen *cg) { - append_u8(&ha->tmp, '<'); - append_mem(&ha->tmp, node->tagname.ptr, node->tagname.len); + Scope *scope = parent_scope(cg); + return scope->type == SCOPE_GLOBAL; +} - Node *attr = node->params; - while (attr) { - - String name = attr->attr_name; - Node *value = attr->attr_value; - - append_u8(&ha->tmp, ' '); - append_mem(&ha->tmp, name.ptr, name.len); - - if (value) { - append_u8(&ha->tmp, '='); - append_u8(&ha->tmp, '"'); - - if (value->type == NODE_VALUE_STR) { - append_mem(&ha->tmp, - value->sval.ptr, // TODO: escape - value->sval.len - ); - } else { - write_buffered_html(a, ha); - assemble_statement(a, value, false); - } - - append_u8(&ha->tmp, '"'); +static void cg_flush_pushs(Codegen *cg) +{ + if (cg->data_off != -1) { + if (cg->data_off < cg->data.len) { + cg_write_u8(cg, OPCODE_PUSHS); + cg_write_u32(cg, cg->data_off); + cg_write_u32(cg, cg->data.len - cg->data_off); } - attr = attr->next; + cg->data_off = -1; } +} - if (node->no_body) { - append_u8(&ha->tmp, ' '); - append_u8(&ha->tmp, '/'); - append_u8(&ha->tmp, '>'); +static int cg_write_opcode(Codegen *cg, uint8_t opcode) +{ + ASSERT(opcode != OPCODE_PUSHS); + cg_flush_pushs(cg); + return cg_write_u8(cg, opcode); +} + +static void cg_write_pushs(Codegen *cg, String str, bool dont_group) +{ + if (dont_group) { + cg_flush_pushs(cg); + cg_write_u8(cg, OPCODE_PUSHS); + cg_write_str(cg, str); } else { - - append_u8(&ha->tmp, '>'); - - Node *child = node->child; - while (child) { - if (child->type == NODE_VALUE_STR) - append_mem(&ha->tmp, child->sval.ptr, child->sval.len); - else if (child->type == NODE_VALUE_HTML) - assemble_html_2(a, ha, child); - else { - write_buffered_html(a, ha); - assemble_statement(a, child, false); - } - child = child->next; - } - - append_u8(&ha->tmp, '<'); - append_u8(&ha->tmp, '/'); - append_mem(&ha->tmp, node->tagname.ptr, node->tagname.len); - append_u8(&ha->tmp, '>'); + if (cg->data_off == -1) + cg->data_off = cg->data.len; + write_raw_mem(&cg->data, str.ptr, str.len); } } -void assemble_html(Assembler *a, Node *node) -{ - HTMLAssembler ha = { - .tmp={.ptr=NULL,.len=0,.cap=0,.err=false}, - }; - assemble_html_2(a, &ha, node); - write_buffered_html(a, &ha); -} +static void walk_node(Codegen *cg, Node *node); -void assemble_expr(Assembler *a, Node *node, int num_results) +static void walk_expr_node(Codegen *cg, Node *node, bool one) { + // TODO: remove + ASSERT(cg->scopes[cg->num_scopes-1].calls == NULL || (cg->scopes[cg->num_scopes-1].calls - cg->calls >= 0 && cg->scopes[cg->num_scopes-1].calls - cg->calls < MAX_UNPATCHED_CALLS)); + switch (node->type) { - default: - assert(0); - break; - - case NODE_FUNC_CALL: - { - Node *func = node->left; - Node *args = node->right; - - append_u8(&a->out, OPCODE_GROUP); - - int arg_count = 0; - Node *arg = args; - while (arg) { - assemble_expr(a, arg, 1); - arg_count++; - arg = arg->next; - } - - if (func->type == NODE_VALUE_SYSVAR) { - - String name = func->sval; - int off = add_string_literal(a, name); - - append_u8(&a->out, OPCODE_SYSCALL); - append_u32(&a->out, off); - append_u32(&a->out, name.len); - - } else { - - assert(func->type == NODE_VALUE_VAR); - - append_u8(&a->out, OPCODE_CALL); - int p = append_u32(&a->out, 0); - - FunctionCall *call = alloc(a->a, sizeof(FunctionCall), _Alignof(FunctionCall)); - if (call == NULL) { - assembler_report(a, "Out of memory"); - return; - } - call->name = func->sval; - call->off = p; - - Scope *scope = &a->scopes[a->num_scopes-1]; - - call->next = scope->calls; - scope->calls = call; - } - - if (num_results == 0) - append_u8(&a->out, OPCODE_GPOP); - else if (num_results != -1) { - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results); - } - - append_u8(&a->out, OPCODE_GCOALESCE); - } + case NODE_NESTED: + walk_expr_node(cg, node->left, one); break; case NODE_OPER_LEN: - assemble_expr(a, node->left, 1); - append_u8(&a->out, OPCODE_LEN); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } + walk_expr_node(cg, node->left, true); + cg_write_opcode(cg, OPCODE_LEN); break; case NODE_OPER_POS: - assemble_expr(a, node->left, num_results); + walk_expr_node(cg, node->left, one); break; case NODE_OPER_NEG: - assemble_expr(a, node->left, 1); - append_u8(&a->out, OPCODE_NEG); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_EQL: - assemble_expr(a, node->left, 1); - assemble_expr(a, node->right, 1); - append_u8(&a->out, OPCODE_EQL); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_NQL: - assemble_expr(a, node->left, 1); - assemble_expr(a, node->right, 1); - append_u8(&a->out, OPCODE_NQL); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_LSS: - assemble_expr(a, node->left, 1); - assemble_expr(a, node->right, 1); - append_u8(&a->out, OPCODE_LSS); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_GRT: - assemble_expr(a, node->left, 1); - assemble_expr(a, node->right, 1); - append_u8(&a->out, OPCODE_GRT); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_ADD: - assemble_expr(a, node->left, 1); - assemble_expr(a, node->right, 1); - append_u8(&a->out, OPCODE_ADD); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_SUB: - assemble_expr(a, node->left, 1); - assemble_expr(a, node->right, 1); - append_u8(&a->out, OPCODE_SUB); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_MUL: - assemble_expr(a, node->left, 1); - assemble_expr(a, node->right, 1); - append_u8(&a->out, OPCODE_MUL); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_DIV: - assemble_expr(a, node->left, 1); - assemble_expr(a, node->right, 1); - append_u8(&a->out, OPCODE_DIV); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_MOD: - assemble_expr(a, node->left, 1); - assemble_expr(a, node->right, 1); - append_u8(&a->out, OPCODE_MOD); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_VALUE_INT: - append_u8(&a->out, OPCODE_PUSHI); - append_s64(&a->out, node->ival); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_VALUE_FLOAT: - append_u8 (&a->out, OPCODE_PUSHF); - append_f64(&a->out, node->dval); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_VALUE_STR: - { - int off = add_string_literal(a, node->sval); - append_u8(&a->out, OPCODE_PUSHS); - append_u32(&a->out, off); - append_u32(&a->out, node->sval.len); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - } - break; - - case NODE_VALUE_NONE: - { - append_u8(&a->out, OPCODE_PUSHN); - } - break; - - case NODE_VALUE_TRUE: - { - append_u8(&a->out, OPCODE_PUSHT); - } - break; - - case NODE_VALUE_FALSE: - { - append_u8(&a->out, OPCODE_PUSHFL); - } - break; - - case NODE_VALUE_VAR: - { - String name = node->sval; - Symbol *sym = find_symbol_in_function(a, name); - if (sym == NULL) { - assembler_report(a, "Reference to undefined variable '%.*s'", name.len, name.ptr); - return; - } - if (sym->type != SYMBOL_VAR) { - assembler_report(a, "Symbol '%.*s' is not a variable", sym->name.len, sym->name.ptr); - return; - } - append_u8(&a->out, OPCODE_PUSHV); - append_u8(&a->out, sym->off); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - } - break; - - case NODE_VALUE_SYSVAR: - { - String name = node->sval; - int off = add_string_literal(a, name); - - append_u8(&a->out, OPCODE_SYSVAR); - append_u32(&a->out, off); - append_u32(&a->out, name.len); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - } - break; - - case NODE_VALUE_HTML: - { - if (num_results != -1) - append_u8(&a->out, OPCODE_GROUP); - - assemble_html(a, node); - - if (num_results != -1) { - - append_u8(&a->out, OPCODE_GPACK); - - if (num_results > 1) { - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - } - } - break; - - case NODE_VALUE_ARRAY: - { - append_u8(&a->out, OPCODE_PUSHA); - append_u32(&a->out, count_nodes(node->child)); - - Node *child = node->child; - while (child) { - assemble_expr(a, child, 1); - append_u8(&a->out, OPCODE_APPEND); - child = child->next; - } - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - } - break; - - case NODE_VALUE_MAP: - { - append_u8(&a->out, OPCODE_PUSHM); - append_u32(&a->out, count_nodes(node->child)); - - Node *child = node->child; - while (child) { - assemble_expr(a, child, 1); - assemble_expr(a, child->key, 1); - append_u8(&a->out, OPCODE_INSERT1); - child = child->next; - } - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - } - break; - - case NODE_SELECT: - { - Node *set = node->left; - Node *key = node->right; - - assemble_expr(a, set, 1); - assemble_expr(a, key, 1); - append_u8(&a->out, OPCODE_SELECT); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - } - break; - - case NODE_NESTED: - assemble_expr(a, node->left, num_results); + walk_expr_node(cg, node->left, true); + cg_write_opcode(cg, OPCODE_NEG); break; case NODE_OPER_ASS: @@ -3407,154 +2635,356 @@ void assemble_expr(Assembler *a, Node *node, int num_results) if (dst->type == NODE_VALUE_VAR) { String name = dst->sval; - - Symbol *sym = find_symbol_in_function(a, name); + Symbol *sym = cg_find_symbol(cg, name, false); if (sym == NULL) { - assembler_report(a, "Undeclared variable '%.*s'", name.len, name.ptr); + cg_report(cg, "Write to undeclared variable"); + return; + } + if (sym->type == SYMBOL_PROCEDURE) { + cg_report(cg, "Symbol is not a variable"); + return; + } + if (sym->cnst) { + cg_report(cg, "Variable is constant"); return; } - if (sym->type != SYMBOL_VAR) { - assembler_report(a, "Symbol '%.*s' can't be assigned to", name.len, name.ptr); - return; - } + cg_push_scope(cg, SCOPE_ASSIGNMENT); + walk_expr_node(cg, src, true); + cg_pop_scope(cg); - assemble_expr(a, src, 1); - append_u8(&a->out, OPCODE_SETV); - append_u8(&a->out, sym->off); + cg_write_opcode(cg, OPCODE_SETV); + cg_write_u8(cg, sym->off); + + if (!one) + cg_write_opcode(cg, OPCODE_POP); } else if (dst->type == NODE_SELECT) { - assemble_expr(a, src, 1); - assemble_expr(a, dst->left, 1); - assemble_expr(a, dst->right, 1); - append_u8(&a->out, OPCODE_INSERT2); + cg_push_scope(cg, SCOPE_ASSIGNMENT); + walk_expr_node(cg, src, true); + cg_pop_scope(cg); + + walk_expr_node(cg, dst->left, true); + walk_expr_node(cg, dst->right, true); + cg_write_opcode(cg, OPCODE_INSERT2); + + if (!one) + cg_write_opcode(cg, OPCODE_POP); } else { - assembler_report(a, "Assignment left side can't be assigned to"); + cg_report(cg, "Assignment left side can't be assigned to"); + return; + } + } + break; + + case NODE_OPER_SHOVEL: + { + Node *dst = node->left; + Node *src = node->right; + + walk_expr_node(cg, node->left, true); + + cg_push_scope(cg, SCOPE_ASSIGNMENT); + walk_expr_node(cg, node->right, true); + cg_pop_scope(cg); + + cg_write_opcode(cg, OPCODE_APPEND); + if (!one) + cg_write_opcode(cg, OPCODE_POP); + } + break; + + case NODE_OPER_EQL: + walk_expr_node(cg, node->left, true); + walk_expr_node(cg, node->right, true); + cg_write_opcode(cg, OPCODE_EQL); + break; + + case NODE_OPER_NQL: + walk_expr_node(cg, node->left, true); + walk_expr_node(cg, node->right, true); + cg_write_opcode(cg, OPCODE_NQL); + break; + + case NODE_OPER_LSS: + walk_expr_node(cg, node->left, true); + walk_expr_node(cg, node->right, true); + cg_write_opcode(cg, OPCODE_LSS); + break; + + case NODE_OPER_GRT: + walk_expr_node(cg, node->left, true); + walk_expr_node(cg, node->right, true); + cg_write_opcode(cg, OPCODE_GRT); + break; + + case NODE_OPER_ADD: + walk_expr_node(cg, node->left, true); + walk_expr_node(cg, node->right, true); + cg_write_opcode(cg, OPCODE_ADD); + break; + + case NODE_OPER_SUB: + walk_expr_node(cg, node->left, true); + walk_expr_node(cg, node->right, true); + cg_write_opcode(cg, OPCODE_SUB); + break; + + case NODE_OPER_MUL: + walk_expr_node(cg, node->left, true); + walk_expr_node(cg, node->right, true); + cg_write_opcode(cg, OPCODE_MUL); + break; + + case NODE_OPER_DIV: + walk_expr_node(cg, node->left, true); + walk_expr_node(cg, node->right, true); + cg_write_opcode(cg, OPCODE_DIV); + break; + + case NODE_OPER_MOD: + walk_expr_node(cg, node->left, true); + walk_expr_node(cg, node->right, true); + cg_write_opcode(cg, OPCODE_MOD); + break; + + case NODE_VALUE_INT: + cg_write_opcode(cg, OPCODE_PUSHI); + cg_write_s64(cg, node->ival); + break; + + case NODE_VALUE_FLOAT: + cg_write_opcode(cg, OPCODE_PUSHF); + cg_write_f64(cg, node->fval); + break; + + case NODE_VALUE_STR: + cg_write_pushs(cg, node->sval, one); + break; + + case NODE_VALUE_NONE: + cg_write_opcode(cg, OPCODE_PUSHN); + break; + + case NODE_VALUE_TRUE: + cg_write_opcode(cg, OPCODE_PUSHT); + break; + + case NODE_VALUE_FALSE: + cg_write_opcode(cg, OPCODE_PUSHFL); + break; + + case NODE_VALUE_VAR: + { + String name = node->sval; + Symbol *sym = cg_find_symbol(cg, name, false); + if (sym == NULL) { + cg_report(cg, "Access to undeclared variable '%.*s'", name.len, name.ptr); + return; + } + if (sym->type == SYMBOL_PROCEDURE) { + cg_report(cg, "Symbol is not a variable"); return; } - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); + cg_write_opcode(cg, OPCODE_PUSHV); + cg_write_u8(cg, sym->off); + } + break; + + case NODE_VALUE_SYSVAR: + cg_write_opcode(cg, OPCODE_SYSVAR); + cg_write_str(cg, node->sval); + break; + + case NODE_VALUE_HTML: + { + if (one) + cg_write_opcode(cg, OPCODE_GROUP); + + cg_write_pushs(cg, S("<"), false); + cg_write_pushs(cg, node->html_tag, false); + + Node *child = node->html_attr; + while (child) { + walk_node(cg, child); + child = child->next; + } + + if (!node->html_body) { + cg_write_pushs(cg, S("/>"), false); + } else { + cg_write_pushs(cg, S(">"), false); + Node *child = node->html_child; + while (child) { + walk_node(cg, child); + child = child->next; + } + cg_write_pushs(cg, S("html_tag, false); + cg_write_pushs(cg, S(">"), false); + } + + if (one) + cg_write_opcode(cg, OPCODE_PACK); + } + break; + + case NODE_VALUE_ARRAY: + { + cg_write_opcode(cg, OPCODE_PUSHA); + cg_write_u32(cg, count_nodes(node->child)); + + Node *child = node->child; + while (child) { + walk_expr_node(cg, child, true); + cg_write_opcode(cg, OPCODE_APPEND); + child = child->next; } } break; - } -} -void assemble_statement(Assembler *a, Node *node, bool pop_expr) -{ - switch (node->type) { - - case NODE_INCLUDE: + case NODE_VALUE_MAP: { - assert(node->include_root); - assemble_statement(a, node->include_root, pop_expr); + cg_write_opcode(cg, OPCODE_PUSHM); + cg_write_u32(cg, count_nodes(node->child)); + + Node *child = node->child; + while (child) { + walk_expr_node(cg, child, true); + walk_expr_node(cg, child->key, true); + cg_write_opcode(cg, OPCODE_INSERT1); + child = child->next; + } } break; - case NODE_PRINT: + case NODE_SELECT: { - append_u8(&a->out, OPCODE_GROUP); - assemble_expr(a, node->left, -1); - append_u8(&a->out, OPCODE_GPRINT); - append_u8(&a->out, OPCODE_GPOP); + Node *set = node->left; + Node *key = node->right; + walk_expr_node(cg, set, true); + walk_expr_node(cg, key, true); + cg_write_opcode(cg, OPCODE_SELECT); } break; - case NODE_FUNC_DECL: + case NODE_PROCEDURE_CALL: { - append_u8(&a->out, OPCODE_JUMP); - int p1 = append_u32(&a->out, 0); + if (one) + cg_write_opcode(cg, OPCODE_GROUP); - int ret = declare_function(a, node->func_name, current_offset(&a->out)); - if (ret < 0) return; - - ret = push_scope(a, SCOPE_FUNC); - if (ret < 0) return; - - int arg_count = count_nodes(node->func_args); - - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, arg_count); - - append_u8(&a->out, OPCODE_GTRUNC); - int p = append_u32(&a->out, 0); - - Node *arg = node->func_args; - int idx = 0; + int count = 0; + Node *arg = node->right; while (arg) { - - int off = declare_variable(a, arg->sval); - if (off < 0) return; - - assert(off == idx); - - idx++; + walk_expr_node(cg, arg, true); + count++; arg = arg->next; } - append_u8(&a->out, OPCODE_GROUP); + Node *proc = node->left; + if (proc->type == NODE_VALUE_VAR) { + + cg_write_opcode(cg, OPCODE_CALL); + cg_write_u8(cg, count); + int p = cg_write_u32(cg, 0); + cg_append_unpatched_call(cg, proc->sval, p); - if (is_expr(node->func_body)) { - assemble_expr(a, node->func_body, -1); } else { - assemble_statement(a, node->func_body, true); - append_u8(&a->out, OPCODE_PUSHN); + + ASSERT(proc->type == NODE_VALUE_SYSVAR); + cg_write_opcode(cg, OPCODE_SYSCALL); + cg_write_u8(cg, count); + cg_write_str(cg, proc->sval); } - append_u8(&a->out, OPCODE_GOVERWRITE); - append_u8(&a->out, OPCODE_RET); + if (one) + cg_write_opcode(cg, OPCODE_PACK); + } + break; - patch_u32(&a->out, p, a->scopes[a->num_scopes-1].max_vars); + default: + UNREACHABLE; + } +} - ret = pop_scope(a); - if (ret < 0) return; +static void walk_node(Codegen *cg, Node *node) +{ + // TODO: remove + ASSERT(cg->scopes[cg->num_scopes-1].calls == NULL || (cg->scopes[cg->num_scopes-1].calls - cg->calls >= 0 && cg->scopes[cg->num_scopes-1].calls - cg->calls < MAX_UNPATCHED_CALLS)); - patch_with_current_offset(&a->out, p1); + switch (node->type) { + + case NODE_GLOBAL: + for (Node *child = node->left; + child; child = child->next) { + walk_node(cg, child); + } + break; + + case NODE_COMPOUND: + cg_push_scope(cg, SCOPE_COMPOUND); + for (Node *child = node->left; + child; child = child->next) + walk_node(cg, child); + cg_pop_scope(cg); + break; + + case NODE_PROCEDURE_DECL: + { + cg_push_scope(cg, SCOPE_PROC); + + cg_write_opcode(cg, OPCODE_JUMP); + int off0 = cg_write_u32(cg, 0); + + #define MAX_ARGS 128 + + int num_args = 0; + Node *args[MAX_ARGS]; + + Node *arg = node->proc_args; + while (arg) { + if (num_args == MAX_ARGS) { + cg_report(cg, "Procedure argument limit reached"); + return; + } + args[num_args++] = arg; + arg = arg->next; + } + + for (int i = num_args-1; i >= 0; i--) + cg_declare_variable(cg, args[i]->sval, false); + + int off1 = cg_write_opcode(cg, OPCODE_VARS); + int off2 = cg_write_u8(cg, 0); + + walk_node(cg, node->proc_body); + cg_write_opcode(cg, OPCODE_RET); + + cg_patch_u8 (cg, off2, count_function_vars(cg)); + cg_patch_u32(cg, off0, cg_current_offset(cg)); + + cg_pop_scope(cg); + + cg_declare_procedure(cg, node->proc_name, off1); } break; case NODE_VAR_DECL: { - int off = declare_variable(a, node->var_name); - if (off < 0) return; - - if (node->var_value) - assemble_expr(a, node->var_value, 1); - else - append_u8(&a->out, OPCODE_PUSHN); - - append_u8(&a->out, OPCODE_SETV); - append_u8(&a->out, off); - } - break; - - case NODE_BLOCK: - case NODE_GLOBAL_BLOCK: - { - if (node->type == NODE_BLOCK) { - int ret = push_scope(a, SCOPE_BLOCK); - if (ret < 0) return; - } - - Node *stmt = node->left; - while (stmt) { - assemble_statement(a, stmt, pop_expr); - stmt = stmt->next; - } - - if (node->type == NODE_BLOCK) { - int ret = pop_scope(a); - if (ret < 0) return; - } + int off = cg_declare_variable(cg, node->var_name, false); + if (node->var_value) { + cg_push_scope(cg, SCOPE_ASSIGNMENT); + walk_expr_node(cg, node->var_value, true); + cg_pop_scope(cg); + } else + cg_write_opcode(cg, OPCODE_PUSHN); + cg_write_opcode(cg, OPCODE_SETV); + cg_write_u8(cg, off); + cg_write_opcode(cg, OPCODE_POP); } break; @@ -3579,56 +3009,83 @@ void assemble_statement(Assembler *a, Node *node, bool pop_expr) // end: // ... - if (node->right) { + if (node->if_branch2) { - assemble_expr(a, node->cond, 1); + walk_expr_node(cg, node->if_cond, true); - append_u8(&a->out, OPCODE_JIFP); - int p1 = append_u32(&a->out, 0); + cg_write_opcode(cg, OPCODE_JIFP); + int p1 = cg_write_u32(cg, 0); - int ret = push_scope(a, SCOPE_IF); - if (ret < 0) return; + cg_push_scope(cg, SCOPE_IF); + walk_node(cg, node->if_branch1); + cg_pop_scope(cg); - assemble_statement(a, node->left, pop_expr); + cg_write_opcode(cg, OPCODE_JUMP); + int p2 = cg_write_u32(cg, 0); - ret = pop_scope(a); - if (ret < 0) return; + cg_flush_pushs(cg); + cg_patch_u32(cg, p1, cg_current_offset(cg)); - append_u8(&a->out, OPCODE_JUMP); - int p2 = append_u32(&a->out, 0); + cg_push_scope(cg, SCOPE_ELSE); + walk_node(cg, node->if_branch2); + cg_pop_scope(cg); - patch_with_current_offset(&a->out, p1); - - ret = push_scope(a, SCOPE_ELSE); - if (ret < 0) return; - - assemble_statement(a, node->right, pop_expr); - - ret = pop_scope(a); - if (ret < 0) return; - - patch_with_current_offset(&a->out, p2); + cg_flush_pushs(cg); + cg_patch_u32(cg, p2, cg_current_offset(cg)); } else { - assemble_expr(a, node->cond, 1); + walk_expr_node(cg, node->if_cond, true); - append_u8(&a->out, OPCODE_JIFP); - int p1 = append_u32(&a->out, 0); + cg_write_opcode(cg, OPCODE_JIFP); + int p1 = cg_write_u32(cg, 0); - int ret = push_scope(a, SCOPE_IF); - if (ret < 0) return; + cg_push_scope(cg, SCOPE_IF); + walk_node(cg, node->if_branch1); + cg_pop_scope(cg); - assemble_statement(a, node->left, pop_expr); - - ret = pop_scope(a); - if (ret < 0) return; - - patch_with_current_offset(&a->out, p1); + cg_flush_pushs(cg); + cg_patch_u32(cg, p1, cg_current_offset(cg)); } } break; + case NODE_FOR: + { + cg_push_scope(cg, SCOPE_FOR); + + int var_1 = cg_declare_variable(cg, node->for_var1, false); + int var_2 = cg_declare_variable(cg, node->for_var2, true); + int var_3 = cg_declare_variable(cg, (String) { NULL, 0 }, true); + + walk_expr_node(cg, node->for_set, true); + cg_write_opcode(cg, OPCODE_SETV); + cg_write_u8(cg, var_3); + cg_write_opcode(cg, OPCODE_POP); + + cg_write_opcode(cg, OPCODE_PUSHI); + cg_write_s64(cg, -1); + cg_write_opcode(cg, OPCODE_SETV); + cg_write_u8(cg, var_2); + cg_write_opcode(cg, OPCODE_POP); + + int start = cg_write_opcode(cg, OPCODE_FOR); + cg_write_u8(cg, var_3); + cg_write_u8(cg, var_1); + cg_write_u8(cg, var_2); + int p = cg_write_u32(cg, 0); + + walk_node(cg, node->left); + + cg_write_opcode(cg, OPCODE_JUMP); + cg_write_u32(cg, start); + + cg_patch_u32(cg, p, cg_current_offset(cg)); + + cg_pop_scope(cg); + } + break; + case NODE_WHILE: { // start: @@ -3639,562 +3096,598 @@ void assemble_statement(Assembler *a, Node *node, bool pop_expr) // end: // ... - int start = current_offset(&a->out); + int start = cg_current_offset(cg); - assemble_expr(a, node->cond, 1); + walk_expr_node(cg, node->while_cond, true); - append_u8(&a->out, OPCODE_JIFP); - int p = append_u32(&a->out, 0); + cg_write_opcode(cg, OPCODE_JIFP); + int p = cg_write_u32(cg, 0); - int ret = push_scope(a, SCOPE_WHILE); - if (ret < 0) return; + cg_push_scope(cg, SCOPE_WHILE); + walk_node(cg, node->left); + cg_pop_scope(cg); - assemble_statement(a, node->left, pop_expr); + cg_write_opcode(cg, OPCODE_JUMP); + cg_write_u32(cg, start); - ret = pop_scope(a); - if (ret < 0) return; - - append_u8(&a->out, OPCODE_JUMP); - append_u32(&a->out, start); - - patch_with_current_offset(&a->out, p); + cg_patch_u32(cg, p, cg_current_offset(cg)); } break; - case NODE_FOR: - { - int ret = push_scope(a, SCOPE_FOR); - if (ret < 0) return; - - int var_1 = declare_variable(a, node->for_var1); - int var_2 = declare_variable(a, node->for_var2); - int var_3 = declare_variable(a, (String) { NULL, 0 }); - - assemble_expr(a, node->for_set, 1); - append_u8(&a->out, OPCODE_SETV); - append_u8(&a->out, var_3); - - append_u8(&a->out, OPCODE_PUSHI); - append_s64(&a->out, 0); - append_u8(&a->out, OPCODE_SETV); - append_u8(&a->out, var_2); - - int start = append_u8(&a->out, OPCODE_FOR); - append_u8(&a->out, var_3); - append_u8(&a->out, var_1); - append_u8(&a->out, var_2); - int p = append_u32(&a->out, 0); - - assemble_statement(a, node->left, pop_expr); - - append_u8(&a->out, OPCODE_JUMP); - append_u32(&a->out, start); - - patch_with_current_offset(&a->out, p); - - ret = pop_scope(a); - if (ret < 0) return; - } + case NODE_INCLUDE: + walk_node(cg, node->include_root); break; default: - assemble_expr(a, node, pop_expr ? 0 : -1); + walk_expr_node(cg, node, false); + if (cg_global_scope(cg) && !inside_assignment(cg)) + cg_write_opcode(cg, OPCODE_OUTPUT); break; } } -typedef struct { - uint32_t magic; - uint32_t code_size; - uint32_t data_size; -} Header; +#define WL_MAGIC 0xFEEDBEEF -AssembleResult assemble(Node *root, WL_Arena *arena, char *errbuf, int errmax) +static int codegen(Node *node, char *dst, int cap, char *errmsg, int errcap) { - Assembler a = {0}; - a.errbuf = errbuf; - a.errmax = errmax; - a.a = arena; + char *hdr; + if (cap < SIZEOF(uint32_t) * 3) + hdr = NULL; + else { + hdr = dst; + dst += SIZEOF(uint32_t) * 3; + cap -= SIZEOF(uint32_t) * 3; + } - int ret = push_scope(&a, SCOPE_GLOBAL); - if (ret < 0) - return (AssembleResult) { (WL_Program) {0}, a.errlen }; + Codegen cg = { + .code = { dst, cap/2, 0 }, + .data = { dst + cap/2, cap/2, 0 }, + .num_scopes = 0, + .err = false, + .errmsg = errmsg, + .errcap = errcap, + .data_off = -1, + }; - append_u8(&a.out, OPCODE_GROUP); + cg.free_list_calls = cg.calls; + for (int i = 0; i < MAX_UNPATCHED_CALLS-1; i++) + cg.calls[i].next = &cg.calls[i+1]; + cg.calls[MAX_UNPATCHED_CALLS-1].next = NULL; - append_u8(&a.out, OPCODE_GTRUNC); - int p = append_u32(&a.out, 0); + cg_push_scope(&cg, SCOPE_GLOBAL); + cg_write_opcode(&cg, OPCODE_VARS); + int off = cg_write_u8(&cg, 0); + walk_node(&cg, node); + cg_write_opcode(&cg, OPCODE_EXIT); + cg_patch_u8(&cg, off, cg.scopes[0].max_vars); + cg_pop_scope(&cg); - append_u8(&a.out, OPCODE_GROUP); + if (cg.err) + return -1; - assemble_statement(&a, root, false); + if (hdr) { - append_u8(&a.out, OPCODE_GPRINT); - append_u8(&a.out, OPCODE_GPOP); + uint32_t magic = WL_MAGIC; + uint32_t code_len = cg.code.len; + uint32_t data_len = cg.data.len; + memcpy(hdr + 0, &magic , sizeof(uint32_t)); + memcpy(hdr + 4, &code_len, sizeof(uint32_t)); + memcpy(hdr + 8, &data_len, sizeof(uint32_t)); - append_u8(&a.out, OPCODE_GPOP); - append_u8(&a.out, OPCODE_EXIT); + if (cg.code.len + cg.data.len <= cap) + memmove(dst + cg.code.len, dst + cap/2, cg.data.len); + } - patch_u32(&a.out, p, a.scopes[a.num_scopes-1].max_vars); - - ret = pop_scope(&a); - if (ret < 0) - return (AssembleResult) { (WL_Program) {0}, a.errlen }; - - OutputBuffer out = {0}; - append_u32(&out, 0xFEEDBEEF); // magic - append_u32(&out, a.out.len); // code size - append_u32(&out, a.strings_len); // data size - append_mem(&out, a.out.ptr, a.out.len); - append_mem(&out, a.strings, a.strings_len); - - free(a.out.ptr); - return (AssembleResult) { (WL_Program) { out.ptr, out.len }, a.errlen }; + return cg.code.len + cg.data.len + SIZEOF(uint32_t) * 3; } -int parse_program_header(WL_Program p, String *code, String *data, char *errbuf, int errmax) +static int write_instr(Writer *w, char *src, int len, String data) { - if ((uint32_t) p.len < 3 * sizeof(uint32_t)) { - snprintf(errbuf, errmax, "Invalid program"); + if (len == 0) return -1; + + switch (src[0]) { + + uint8_t b0; + uint8_t b1; + uint8_t b2; + uint32_t w0; + uint32_t w1; + int64_t i; + double d; + + case OPCODE_NOPE: + write_text(w, S("NOPE\n")); + return 1; + + case OPCODE_JUMP: + if (len < 5) return -1; + memcpy(&w0, src + 1, sizeof(uint32_t)); + write_text(w, S("JUMP ")); + write_text_s64(w, w0); + write_text(w, S("\n")); + return 5; + + case OPCODE_JIFP: + if (len < 5) return -1; + memcpy(&w0, src + 1, sizeof(uint32_t)); + write_text(w, S("JIFP ")); + write_text_s64(w, w0); + write_text(w, S("\n")); + return 5; + + case OPCODE_OUTPUT: + write_text(w, S("OUTPUT\n")); + return 1; + + case OPCODE_SYSVAR: + if (len < 9) return -1; + memcpy(&w0, src + 1, sizeof(uint32_t)); + memcpy(&w1, src + 5, sizeof(uint32_t)); + write_text(w, S("SYSVAR \"")); + write_text(w, (String) { data.ptr + w0, w1 }); + write_text(w, S("\"\n")); + return 9; + + case OPCODE_SYSCALL: + if (len < 10) return -1; + memcpy(&b0, src + 1, sizeof(uint8_t)); + memcpy(&w0, src + 2, sizeof(uint32_t)); + memcpy(&w1, src + 6, sizeof(uint32_t)); + write_text(w, S("SYSCALL ")); + write_text_s64(w, b0); + write_text(w, S(" \"")); + write_text(w, (String) { data.ptr + w0, w1 }); + write_text(w, S("\"\n")); + return 10; + + case OPCODE_CALL: + if (len < 6) return -1; + memcpy(&b0, src + 1, sizeof(uint8_t)); + memcpy(&w0, src + 2, sizeof(uint32_t)); + write_text(w, S("CALL ")); + write_text_s64(w, b0); + write_text(w, S(" ")); + write_text_s64(w, w0); + write_text(w, S("\n")); + return 6; + + case OPCODE_RET: + write_text(w, S("RET\n")); + return 1; + + case OPCODE_GROUP: + write_text(w, S("GROUP\n")); + return 1; + + case OPCODE_PACK: + write_text(w, S("PACK\n")); + return 1; + + case OPCODE_GPOP: + write_text(w, S("GPOP\n")); + return 1; + + case OPCODE_FOR: + if (len < 8) return -1; + memcpy(&b0, src + 1, sizeof(b0)); + memcpy(&b1, src + 2, sizeof(b1)); + memcpy(&b2, src + 3, sizeof(b2)); + memcpy(&w0, src + 4, sizeof(w0)); + write_text(w, S("FOR ")); + write_text_s64(w, b0); + write_text(w, S(" ")); + write_text_s64(w, b1); + write_text(w, S(" ")); + write_text_s64(w, b2); + write_text(w, S(" ")); + write_text_s64(w, w0); + write_text(w, S("\n")); + return 8; + + case OPCODE_EXIT: + write_text(w, S("EXIT\n")); + return 1; + + case OPCODE_VARS: + if (len < 2) return -1; + memcpy(&b0, src + 1, sizeof(b0)); + write_text(w, S("VARS ")); + write_text_s64(w, b0); + write_text(w, S("\n")); + return 2; + + case OPCODE_POP: + write_text(w, S("POP\n")); + return 1; + + case OPCODE_SETV: + if (len < 2) return -1; + memcpy(&b0, src + 1, sizeof(uint8_t)); + write_text(w, S("SETV ")); + write_text_s64(w, b0); + write_text(w, S("\n")); + return 2; + + case OPCODE_PUSHV: + if (len < 2) return -1; + memcpy(&b0, src + 1, sizeof(uint8_t)); + write_text(w, S("PUSHV ")); + write_text_s64(w, b0); + write_text(w, S("\n")); + return 2; + + case OPCODE_PUSHI: + if (len < 9) return -1; + memcpy(&i, src + 1, sizeof(int64_t)); + write_text(w, S("PUSHI ")); + write_text_s64(w, i); + write_text(w, S("\n")); + return 9; + + case OPCODE_PUSHF: + if (len < 9) return -1; + memcpy(&d, src + 1, sizeof(double)); + write_text(w, S("PUSHF ")); + write_text_f64(w, d); + write_text(w, S("\n")); + return 9; + + case OPCODE_PUSHS: + if (len < 9) return -1; + memcpy(&w0, src + 1, sizeof(uint32_t)); + memcpy(&w1, src + 5, sizeof(uint32_t)); + write_text(w, S("PUSHS \"")); + write_text(w, (String) { data.ptr + w0, w1 }); + write_text(w, S("\"\n")); + return 9; + + case OPCODE_PUSHA: + if (len < 5) return -1; + memcpy(&w0, src + 1, sizeof(w0)); + write_text(w, S("PUSHA ")); + write_text_s64(w, w0); + write_text(w, S("\n")); + return 5; + + case OPCODE_PUSHM: + if (len < 5) return -1; + memcpy(&w0, src + 1, sizeof(w0)); + write_text(w, S("PUSHM ")); + write_text_s64(w, w0); + write_text(w, S("\n")); + return 5; + + case OPCODE_PUSHN: + write_text(w, S("PUSHN\n")); + return 1; + + case OPCODE_PUSHT: + write_text(w, S("PUSHT\n")); + return 1; + + case OPCODE_PUSHFL: + write_text(w, S("PUSHFL\n")); + return 1; + + case OPCODE_LEN: + write_text(w, S("LEN\n")); + return 1; + + case OPCODE_NEG: + write_text(w, S("NEG\n")); + return 1; + + case OPCODE_EQL: + write_text(w, S("EQL\n")); + return 1; + + case OPCODE_NQL: + write_text(w, S("NQL\n")); + return 1; + + case OPCODE_LSS: + write_text(w, S("LSS\n")); + return 1; + + case OPCODE_GRT: + write_text(w, S("GRT\n")); + return 1; + + case OPCODE_ADD: + write_text(w, S("ADD\n")); + return 1; + + case OPCODE_SUB: + write_text(w, S("SUB\n")); + return 1; + + case OPCODE_MUL: + write_text(w, S("MUL\n")); + return 1; + + case OPCODE_DIV: + write_text(w, S("DIV\n")); + return 1; + + case OPCODE_MOD: + write_text(w, S("MOD\n")); + return 1; + + case OPCODE_APPEND: + write_text(w, S("APPEND\n")); + return 1; + + case OPCODE_INSERT1: + write_text(w, S("INSERT1\n")); + return 1; + + case OPCODE_INSERT2: + write_text(w, S("INSERT2\n")); + return 1; + + case OPCODE_SELECT: + write_text(w, S("SELECT\n")); + return 1; + + default: + write_text(w, S("byte ")); + write_text_s64(w, src[0]); + return 1; } - uint32_t magic; - uint32_t code_size; - uint32_t data_size; - memcpy(&magic, p.ptr + 0, sizeof(uint32_t)); - memcpy(&code_size, p.ptr + 4, sizeof(uint32_t)); - memcpy(&data_size, p.ptr + 8, sizeof(uint32_t)); - - if (magic != 0xFEEDBEEF) { - snprintf(errbuf, errmax, "Invalid program"); - return -1; - } - - if (code_size + data_size + 3 * sizeof(uint32_t) != (uint32_t) p.len) { - snprintf(errbuf, errmax, "Invalid program"); - return -1; - } - - *code = (String) { p.ptr + 3 * sizeof(uint32_t), code_size }; - *data = (String) { p.ptr + 3 * sizeof(uint32_t) + code_size, data_size }; - return 0; + return -1; } -void print_program(WL_Program program) +static int write_program(WL_Program program, char *dst, int cap) { - String code; - String data; + if (program.len < 3 * sizeof(uint32_t)) + return -1; - char err[128]; - if (parse_program_header(program, &code, &data, err, COUNT(err)) < 0) { - printf("%s\n", err); + uint32_t magic; + uint32_t code_len; + uint32_t data_len; + + memcpy(&magic , program.ptr + 0, sizeof(uint32_t)); + memcpy(&code_len, program.ptr + 4, sizeof(uint32_t)); + memcpy(&data_len, program.ptr + 8, sizeof(uint32_t)); + + if (magic != WL_MAGIC) + return -1; + + if (code_len + data_len + 3 * sizeof(uint32_t) != program.len) + return -1; + + String code = { program.ptr + 3 * sizeof(uint32_t) , code_len }; + String data = { program.ptr + 3 * sizeof(uint32_t) + code_len, data_len }; + + Writer w = { dst, cap, 0 }; + + int cur = 0; + while (cur < code.len) { + write_text_s64(&w, cur); + write_text(&w, S(": ")); + int ret = write_instr(&w, code.ptr + cur, code.len - cur, data); + if (ret < 0) return -1; + cur += ret; + } + + return w.len; +} + +void wl_dump_program(WL_Program program) +{ + char buf[1<<10]; + int len = write_program(program, buf, SIZEOF(buf)); + + if (len < 0) { + printf("Invalid program\n"); return; } - char *p = code.ptr; - for (;;) { - printf(" %-3d: ", (int) (p - code.ptr)); - p = print_instruction(p, data.ptr); - printf("\n"); - if (p == code.ptr + code.len) - break; + if (len > SIZEOF(buf)) { + char *p = malloc(len+1); + if (p == NULL) { + printf("Out of memory\n"); + return; + } + write_program(program, p, len); + p[len] = '\0'; + fwrite(p, 1, len, stdout); + } else { + fwrite(buf, 1, len, stdout); } } -char *print_instruction(char *p, char *data) +///////////////////////////////////////////////////////////////////////// +// COMPILER +///////////////////////////////////////////////////////////////////////// + +#define FILE_LIMIT 128 + +typedef struct { + String file; + Node* root; + Node* includes; +} CompiledFile; + +struct WL_Compiler { + + WL_Arena* arena; + CompiledFile files[FILE_LIMIT]; + int num_files; + String waiting_file; + + bool err; + char msg[1<<8]; +}; + +WL_Compiler *wl_compiler_init(WL_Arena *arena) { - switch (*(p++)) { - - default: - printf("(unknown opcode 0x%x)", *p); - break; - - case OPCODE_NOPE: - printf("NOPE"); - break; - - case OPCODE_EXIT: - printf("EXIT"); - break; - - case OPCODE_GROUP: - { - printf("GROUP"); - } - break; - - case OPCODE_GPOP: - { - printf("GPOP"); - } - break; - - case OPCODE_GPRINT: - { - printf("GPRINT"); - } - break; - - case OPCODE_GTRUNC: - { - uint32_t off; - memcpy(&off, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("GTRUNC %u", off); - } - break; - - case OPCODE_GCOALESCE: - { - printf("GCOALESCE"); - } - break; - - case OPCODE_GOVERWRITE: - { - printf("GOVERWRITE"); - } - break; - - case OPCODE_GPACK: - { - printf("GPACK"); - } - break; - - case OPCODE_PUSHI: - { - int64_t x; - memcpy(&x, p, sizeof(int64_t)); - p += sizeof(int64_t); - - printf("PUSHI %" LLU, x); - } - break; - - case OPCODE_PUSHF: - { - double x; - memcpy(&x, p, sizeof(double)); - p += sizeof(double); - - printf("PUSHF %lf", x); - } - break; - - case OPCODE_PUSHS: - { - uint32_t off; - memcpy(&off, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - uint32_t len; - memcpy(&len, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("PUSHS \"%.*s\"", (int) len, (char*) data + off); - } - break; - - case OPCODE_PUSHV: - { - uint8_t idx; - memcpy(&idx, p, sizeof(uint8_t)); - p += sizeof(uint8_t); - - printf("PUSHV %u", idx); - } - break; - - case OPCODE_PUSHA: - { - uint32_t cap; - memcpy(&cap, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("PUSHA %u", cap); - } - break; - - case OPCODE_PUSHM: - { - uint32_t cap; - memcpy(&cap, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("PUSHM %u", cap); - } - break; - - case OPCODE_PUSHN: - { - printf("PUSHN"); - } - break; - - case OPCODE_POP: - printf("POP"); - break; - - case OPCODE_NEG: - printf("NEG"); - break; - - case OPCODE_EQL: - printf("EQL"); - break; - - case OPCODE_NQL: - printf("NQL"); - break; - - case OPCODE_LSS: - printf("LSS"); - break; - - case OPCODE_GRT: - printf("GRT"); - break; - - case OPCODE_ADD: - printf("ADD"); - break; - - case OPCODE_SUB: - printf("SUB"); - break; - - case OPCODE_MUL: - printf("MUL"); - break; - - case OPCODE_DIV: - printf("DIV"); - break; - - case OPCODE_MOD: - printf("MOD"); - break; - - case OPCODE_SETV: - { - uint8_t idx; - memcpy(&idx, p, sizeof(uint8_t)); - p += sizeof(uint8_t); - - printf("SETV %u", idx); - } - break; - - case OPCODE_JUMP: - { - uint32_t off; - memcpy(&off, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("JUMP %u", off); - } - break; - - case OPCODE_JIFP: - { - uint32_t off; - memcpy(&off, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("JIFP %u", off); - } - break; - - case OPCODE_CALL: - { - uint32_t off; - memcpy(&off, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("CALL %u", off); - } - break; - - case OPCODE_RET: - printf("RET"); - break; - - case OPCODE_APPEND: - printf("APPEND"); - break; - - case OPCODE_INSERT1: - printf("INSERT1"); - break; - - case OPCODE_INSERT2: - printf("INSERT2"); - break; - - case OPCODE_SELECT: - printf("SELECT"); - break; - - case OPCODE_PRINT: - printf("PRINT"); - break; - - case OPCODE_SYSVAR: - { - uint32_t off; - memcpy(&off, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - uint32_t len; - memcpy(&len, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("SYSVAR \"%.*s\"", (int) len, (char*) data + off); - } - break; - - case OPCODE_SYSCALL: - { - uint32_t off; - memcpy(&off, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - uint32_t len; - memcpy(&len, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("SYSCALL \"%.*s\"", (int) len, (char*) data + off); - } - break; - - case OPCODE_PUSHT: - printf("PUSHT"); - break; - - case OPCODE_PUSHFL: - printf("PUSHFL"); - break; - - case OPCODE_FOR: - { - uint8_t a; - memcpy(&a, p, sizeof(uint8_t)); - p += sizeof(uint8_t); - - uint8_t b; - memcpy(&b, p, sizeof(uint8_t)); - p += sizeof(uint8_t); - - uint8_t c; - memcpy(&c, p, sizeof(uint8_t)); - p += sizeof(uint8_t); - - uint32_t d; - memcpy(&d, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("FOR %u %u %u %u", a, b, c, d); - } - break; - } - - return p; + WL_Compiler *compiler = alloc(arena, SIZEOF(WL_Compiler), _Alignof(WL_Compiler)); + if (compiler == NULL) + return NULL; + compiler->arena = arena; + compiler->num_files = 0; + compiler->waiting_file = (String) { NULL, 0 }; + compiler->err = false; + return compiler; } -//////////////////////////////////////////////////////////////////////////////////////// -// src/value.h -//////////////////////////////////////////////////////////////////////////////////////// +WL_AddResult wl_compiler_add(WL_Compiler *compiler, WL_String content) +{ + if (compiler->err) + return (WL_AddResult) { .type=WL_ADD_ERROR }; -#ifndef WL_VALUE_INCLUDED -#define WL_VALUE_INCLUDED + ParseResult pres = parse((String) { content.ptr, content.len }, compiler->arena, compiler->msg, SIZEOF(compiler->msg)); + if (pres.node == NULL) { + compiler->err = true; + return (WL_AddResult) { .type=WL_ADD_ERROR }; + } -#ifndef WL_AMALGAMATION -#include "basic.h" -#include "includes.h" -#endif + CompiledFile compiled_file = { + .file = compiler->waiting_file, + .root = pres.node, + .includes = pres.includes, + }; + compiler->files[compiler->num_files++] = compiled_file; + compiler->waiting_file = (String) { NULL, 0 }; -#define VALUE_NONE ((Value) 0) -#define VALUE_TRUE ((Value) 1) -#define VALUE_FALSE ((Value) 2) -#define VALUE_ERROR ((Value) 6) + for (int i = 0; i < compiler->num_files; i++) { + + Node *include = compiler->files[i].includes; + while (include) { + + ASSERT(include->type == NODE_INCLUDE); + + if (include->include_root == NULL) { + for (int j = 0; j < compiler->num_files; j++) { + if (streq(include->include_path, compiler->files[j].file)) { + include->include_root = compiler->files[j].root; + break; + } + } + } + + if (include->include_root == NULL) { + + if (compiler->num_files == FILE_LIMIT) { + ASSERT(0); // TODO + } + + // TODO: Make the path relative to the compiled file + + compiler->waiting_file = include->include_path; + return (WL_AddResult) { .type=WL_ADD_AGAIN, .path={ include->include_path.ptr, include->include_path.len } }; + } + + include = include->include_next; + } + } + + return (WL_AddResult) { .type=WL_ADD_LINK }; +} + +int wl_compiler_link(WL_Compiler *compiler, WL_Program *program) +{ + if (compiler->err) return -1; + + if (compiler->num_files == 0 || compiler->waiting_file.len > 0) { + int len = snprintf(compiler->msg, SIZEOF(compiler->msg), "Missing files in compilation unit"); + if (len > SIZEOF(compiler->msg)) + len = SIZEOF(compiler->msg)-1; + compiler->msg[len] = '\0'; + compiler->err = true; + return -1; + } + + char *dst = compiler->arena->ptr + compiler->arena->cur; + int cap = compiler->arena->len - compiler->arena->cur; + + int len = codegen(compiler->files[0].root, dst, cap, compiler->msg, SIZEOF(compiler->msg)); + if (len < 0) { + compiler->err = true; + return -1; + } + if (len > cap) { + int len = snprintf(compiler->msg, SIZEOF(compiler->msg), "Out of memory"); + if (len > SIZEOF(compiler->msg)) + len = SIZEOF(compiler->msg)-1; + compiler->msg[len] = '\0'; + compiler->err = true; + return -1; + } + + *program = (WL_Program) { dst, len }; + + compiler->arena->cur += len; + return 0; +} + +WL_String wl_compiler_error(WL_Compiler *compiler) +{ + return compiler->err + ? (WL_String) { compiler->msg, strlen(compiler->msg) } + : (WL_String) { NULL, 0 }; +} + +int wl_dump_ast(WL_Compiler *compiler, char *dst, int cap) +{ + Writer w = { dst, cap, 0 }; + for (int i = 0; i < compiler->num_files; i++) { + write_text(&w, S("(file \"")); + write_text(&w, compiler->files[i].file); + write_text(&w, S("\" ")); + write_node(&w, compiler->files[i].root); + write_text(&w, S(")")); + } + return w.len; +} + +///////////////////////////////////////////////////////////////////////// +// OBJECT MODEL +///////////////////////////////////////////////////////////////////////// typedef enum { TYPE_NONE, TYPE_BOOL, TYPE_INT, TYPE_FLOAT, - TYPE_MAP, - TYPE_ARRAY, TYPE_STRING, + TYPE_ARRAY, + TYPE_MAP, TYPE_ERROR, } Type; +#define TAG_ERROR 0 +#define TAG_POSITIVE_INT 1 +#define TAG_NEGATIVE_INT 2 +#define TAG_BOOL 3 +#define TAG_NONE 4 +#define TAG_PTR 5 + +#define VALUE_NONE ((0 << 3) | TAG_NONE) +#define VALUE_TRUE ((0 << 3) | TAG_BOOL) +#define VALUE_FALSE ((1 << 3) | TAG_BOOL) +#define VALUE_ERROR ((0 << 3) | TAG_ERROR) + typedef uint64_t Value; -Type type_of (Value v); -int64_t get_int (Value v); -float get_float (Value v); -String get_str (Value v); -Value make_int (WL_Arena *a, int64_t x); -Value make_float (WL_Arena *a, float x); -Value make_str (WL_Arena *a, String x); -Value make_map (WL_Arena *a); -Value make_array (WL_Arena *a); -int map_select (Value map, Value key, Value *val); -Value* map_select_by_index(Value map, int key); -int map_insert (WL_Arena *a, Value map, Value key, Value val); -Value* array_select (Value array, int key); -int array_append (WL_Arena *a, Value array, Value val); -bool valeq (Value a, Value b); -bool valgrt (Value a, Value b); -int value_length (Value v); -int value_to_string(Value v, char *dst, int max); - -#endif // WL_VALUE_INCLUDED - -//////////////////////////////////////////////////////////////////////////////////////// -// src/value.c -//////////////////////////////////////////////////////////////////////////////////////// - - -#ifndef WL_AMALGAMATION -#include "value.h" -#endif - -#define ITEMS_PER_MAP_BATCH 8 -#define ITEMS_PER_ARRAY_BATCH 16 - -typedef struct MapItems MapItems; -struct MapItems { - MapItems *next; - Value keys [ITEMS_PER_MAP_BATCH]; - Value items[ITEMS_PER_MAP_BATCH]; +typedef struct Extension Extension; +struct Extension { + Extension *next; + int count; + int capacity; + Value vals[]; }; typedef struct { - Type type; - int count; - int tail_count; - MapItems head; - MapItems *tail; -} MapValue; - -typedef struct ArrayItems ArrayItems; -struct ArrayItems { - ArrayItems *next; - Value items[ITEMS_PER_ARRAY_BATCH]; -}; - -typedef struct { - Type type; - int count; - int tail_count; - ArrayItems head; - ArrayItems *tail; -} ArrayValue; + Type type; + int count; + int capacity; + Extension *ext; + Value vals[]; +} AggregateValue; typedef struct { Type type; @@ -4212,249 +3705,394 @@ typedef struct { char data[]; } StringValue; -Type type_of(Value v) +static int value_convert_to_str(Value v, char *dst, int cap); + +static Type value_type(Value v) { - // 000 none - // 001 true - // 010 false - // 011 int - // 100 - // 101 - // 110 error - // 111 pointer - switch (v & 7) { - case 0: return TYPE_NONE; - case 1: return TYPE_BOOL; - case 2: return TYPE_BOOL; - case 3: return TYPE_INT; - case 4: break; - case 5: break; - case 6: return TYPE_ERROR; - case 7: return *(Type*) ((uintptr_t) v & ~(uintptr_t) 7); + case TAG_ERROR: return TYPE_ERROR; + case TAG_POSITIVE_INT: return TYPE_INT; + case TAG_NEGATIVE_INT: return TYPE_INT; + case TAG_BOOL : return TYPE_BOOL; + case TAG_NONE : return TYPE_NONE; + case TAG_PTR : return *(Type*) (v & ~(Value) 7); break; } - - return TYPE_ERROR; + return TAG_ERROR; } -int64_t get_int(Value v) +static int64_t value_to_s64(Value v) { - if ((v & 7) == 3) + ASSERT(value_type(v) == TYPE_INT); + + if ((v & 7) == TAG_POSITIVE_INT) return (int64_t) (v >> 3); - IntValue *p = (IntValue*) v; + if ((v & 7) == TAG_NEGATIVE_INT) + return (int64_t) ((v >> 3) | ((Value) 7 << 61)); + + IntValue *p = (IntValue*) (v & ~(Value) 7); return p->raw; } -float get_float(Value v) +static double value_to_f64(Value v) { - FloatValue *p = (FloatValue*) v; + ASSERT(value_type(v) == TYPE_FLOAT); + + FloatValue *p = (FloatValue*) (v & ~(Value) 7); return p->raw; } -String get_str(Value v) +static String value_to_str(Value v) { - StringValue *p = (StringValue*) (v & ~(uintptr_t) 7); + ASSERT(value_type(v) == TYPE_STRING); + + StringValue *p = (StringValue*) (v & ~(Value) 7); return (String) { p->data, p->len }; } -static MapValue *get_map(Value v) -{ - return (MapValue*) (v & ~(uintptr_t) 7); -} +/* -static ArrayValue *get_array(Value v) -{ - return (ArrayValue*) (v & ~(uintptr_t) 7); -} +2 bits -> 2^2 = 4 -Value make_int(WL_Arena *a, int64_t x) -{ - if (x <= (int64_t) (1ULL << 60)-1 && x >= (int64_t) -(1ULL << 60)) - return ((Value) x << 3) | 3; +00000 0 . +00001 1 . +00010 2 . +00011 3 . +00100 4 . +00101 5 . +00110 6 . +00111 7 . +01000 8 +01001 9 +01010 10 +01011 11 +01100 12 +01101 13 +01110 14 +01111 15 +10000 -16 +10001 -15 +10010 -14 +10011 -13 +10100 -12 +10101 -11 +10110 -10 +10111 -9 +11000 -8 . +11001 -7 . +11010 -6 . +11011 -5 . +11100 -4 . +11101 -3 . +11110 -2 . +11111 -1 . - IntValue *v = alloc(a, (int) sizeof(IntValue), _Alignof(IntValue)); - if (v == NULL) +*/ + +static Value value_from_s64(int64_t x, WL_Arena *arena, Error *err) +{ + Value v = (Value) x; + Value upper3bits = v >> 61; + + if (upper3bits == 0) + return (v << 3) | TAG_POSITIVE_INT; + + if (upper3bits == 7) + return (v << 3) | TAG_NEGATIVE_INT; + + IntValue *p = alloc(arena, SIZEOF(IntValue), _Alignof(IntValue)); + if (p == NULL) { + REPORT(err, "Out of memory"); return VALUE_ERROR; + } - v->type = TYPE_INT; - v->raw = x; + p->type = TYPE_INT; + p->raw = x; - assert(((uintptr_t) v & 7) == 0); - return ((Value) v) | 7; + ASSERT(((Value) p & 7) == 0); + return ((Value) p) | TAG_PTR; } -Value make_float(WL_Arena *a, float x) +static Value value_from_f64(double x, WL_Arena *arena, Error *err) { - FloatValue *v = alloc(a, (int) sizeof(FloatValue), _Alignof(FloatValue)); - if (v == NULL) + FloatValue *v = alloc(arena, SIZEOF(FloatValue), _Alignof(FloatValue)); + if (v == NULL) { + REPORT(err, "Out of memory"); return VALUE_ERROR; + } v->type = TYPE_FLOAT; v->raw = x; - assert(((uintptr_t) v & 7) == 0); - return ((Value) v) | 7; + ASSERT(((uintptr_t) v & 7) == 0); + return ((Value) v) | TAG_PTR; } -Value make_str(WL_Arena *a, String x) // TODO: This should reuse the string contents when possible +static Value value_from_str(String x, WL_Arena *arena, Error *err) { - StringValue *v = alloc(a, (int) sizeof(StringValue) + x.len, 8); - if (v == NULL) + StringValue *v = alloc(arena, SIZEOF(StringValue) + x.len, 8); + if (v == NULL) { + REPORT(err, "Out of memory"); return VALUE_ERROR; + } v->type = TYPE_STRING; v->len = x.len; memcpy(v->data, x.ptr, x.len); - assert(((uintptr_t) v & 7) == 0); - return ((Value) v) | 7; + ASSERT(((uintptr_t) v & 7) == 0); + return ((Value) v) | TAG_PTR; } -Value make_map(WL_Arena *a) +static Value aggregate_empty(bool map, uint32_t cap, WL_Arena *arena, Error *err) { - MapValue *m = alloc(a, (int) sizeof(MapValue), _Alignof(MapValue)); - if (m == NULL) + AggregateValue *v = alloc(arena, SIZEOF(AggregateValue) + 2 * cap * SIZEOF(Value), MAX(_Alignof(AggregateValue), 8)); + if (v == NULL) { + REPORT(err, "Out of memory"); return VALUE_ERROR; + } - m->type = TYPE_MAP; - m->count = 0; - m->tail_count = 0; - m->tail = &m->head; - m->head.next = NULL; - - return (Value) m | 7; -} - -Value make_array(WL_Arena *a) -{ - ArrayValue *v = alloc(a, (int) sizeof(ArrayValue), _Alignof(ArrayValue)); - if (v == NULL) - return VALUE_ERROR; - - v->type = TYPE_ARRAY; + v->type = map ? TYPE_MAP : TYPE_ARRAY; v->count = 0; - v->tail_count = 0; - v->tail = &v->head; - v->head.next = NULL; + v->capacity = cap; + v->ext = NULL; - return (Value) v | 7; + ASSERT(((uintptr_t) v & 7) == 0); + return ((Value) v) | TAG_PTR; } -int map_select(Value map, Value key, Value *val) +static int64_t aggregate_length(AggregateValue *agg) { - MapValue *p = get_map(map); - MapItems *batch = &p->head; - while (batch) { + int64_t n = agg->count; - int num = ITEMS_PER_MAP_BATCH; - if (batch->next == NULL) - num = p->tail_count; - - for (int i = 0; i < num; i++) - if (valeq(batch->keys[i], key)) { - *val = batch->items[i]; - return 0; - } - - batch = batch->next; + Extension *ext = agg->ext; + while (ext) { + n += ext->count; + ext = ext->next; } - return -1; + return n; } -Value *map_select_by_index(Value map, int key) +static Value *aggregate_select_by_raw_index(AggregateValue *agg, int64_t idx) { - MapValue *p = get_map(map); - MapItems *batch = &p->head; - int cursor = 0; - while (batch) { + ASSERT(agg->type == TYPE_ARRAY || agg->type == TYPE_MAP); - int num = ITEMS_PER_MAP_BATCH; - if (batch->next == NULL) - num = p->tail_count; + if (idx < 0 || idx >= aggregate_length(agg)) + return NULL; - if (cursor <= key && key < cursor + num) - return &batch->keys[key - cursor]; + if (idx < agg->count) + return &agg->vals[idx]; - batch = batch->next; - cursor += num; + idx -= agg->count; + Extension *ext = agg->ext; + while (ext) { + if (idx < ext->count) + return &ext->vals[idx]; + idx -= ext->count; + ext = ext->next; } + UNREACHABLE; return NULL; } -int map_insert(WL_Arena *a, Value map, Value key, Value val) +static bool value_eql(Value a, Value b); + +static Value *aggregate_select(AggregateValue *agg, Value key) { - MapValue *p = get_map(map); - if (p->tail_count == ITEMS_PER_MAP_BATCH) { + if (agg->type == TYPE_MAP) { - MapItems *batch = alloc(a, (int) sizeof(MapItems), _Alignof(MapItems)); - if (batch == NULL) - return -1; + for (int i = 0; i < agg->count; i += 2) + if (value_eql(agg->vals[i], key)) + return &agg->vals[i+1]; - batch->next = NULL; - if (p->tail) - p->tail->next = batch; - p->tail = batch; - p->tail_count = 0; + Extension *ext = agg->ext; + while (ext) { + for (int i = 0; i < ext->count; i += 2) + if (value_eql(ext->vals[i], key)) { + return &ext->vals[i+1]; + } + ext = ext->next; + } + + return NULL; + + } else { + + ASSERT(agg->type == TYPE_ARRAY); + + if (value_type(key) != TYPE_INT) + return NULL; + int64_t idx = value_to_s64(key); + + return aggregate_select_by_raw_index(agg, idx); } - - p->tail->keys[p->tail_count] = key; - p->tail->items[p->tail_count] = val; - p->tail_count++; - p->count++; - return 0; } -Value *array_select(Value array, int key) +static bool aggregate_append(AggregateValue *agg, Value v1, Value v2, WL_Arena *arena) { - ArrayValue *p = get_array(array); - ArrayItems *batch = &p->head; - int cursor = 0; - while (batch) { - - int num = ITEMS_PER_ARRAY_BATCH; - if (batch->next == NULL) - num = p->tail_count; - - if (cursor <= key && key < cursor + num) - return &batch->items[key - cursor]; - - batch = batch->next; - cursor += num; + if (agg->count < agg->capacity) { + agg->vals[agg->count++] = v1; + if (v2 != VALUE_ERROR) + agg->vals[agg->count++] = v2; + return true; } - return NULL; + Extension *tail = agg->ext; + if (tail) + while (tail->next) + tail = tail->next; + + Extension *ext; + if (tail == NULL || tail->count == tail->capacity) { + + int cap = 8; + ext = alloc(arena, SIZEOF(Extension) + cap * sizeof(Value), ALIGNOF(Extension)); + if (ext == NULL) + return false; + + ext->count = 0; + ext->capacity = cap; + ext->next = NULL; + + if (tail) + tail->next = ext; + else + agg->ext = ext; + + } else + ext = tail; + + ext->vals[ext->count++] = v1; + if (v2 != VALUE_ERROR) + ext->vals[ext->count++] = v2; + return true; } -int array_append(WL_Arena *a, Value array, Value val) +static Value value_empty_map(uint32_t cap, WL_Arena *arena, Error *err) { - ArrayValue *p = get_array(array); - if (p->tail_count == ITEMS_PER_ARRAY_BATCH) { + return aggregate_empty(true, 2 * cap, arena, err); +} - ArrayItems *batch = alloc(a, (int) sizeof(ArrayItems), _Alignof(ArrayItems)); - if (batch == NULL) - return -1; +static Value value_empty_array(uint32_t cap, WL_Arena *arena, Error *err) +{ + return aggregate_empty(false, cap, arena, err); +} - batch->next = NULL; +static int64_t value_length(Value set) +{ + ASSERT(value_type(set) == TYPE_MAP || value_type(set) == TYPE_ARRAY); + AggregateValue *agg = (void*) (set & ~(Value) 7); + int64_t len = aggregate_length(agg); + if (agg->type == TYPE_MAP) + len /= 2; + return len; +} - if (p->tail) - p->tail->next = batch; - p->tail = batch; - p->tail_count = 0; +static bool value_insert(Value set, Value key, Value val, WL_Arena *arena, Error *err) +{ + Type t = value_type(set); + if (t != TYPE_MAP && t != TYPE_ARRAY) { + REPORT(err, "Invalid insertion on non-map and non-array value"); + return false; + } + AggregateValue *agg = (void*) (set & ~(Value) 7); + + Value *dst = aggregate_select(agg, key); + if (dst != NULL) { + *dst = val; + return true; } - p->tail->items[p->tail_count] = val; - p->tail_count++; - p->count++; - return 0; + if (agg->type == TYPE_ARRAY && value_type(key) != TYPE_INT) { + REPORT(err, "Invalid index used in array access"); + return false; + } + + if (!aggregate_append(agg, key, val, arena)) { + REPORT(err, "Out of memory"); + return false; + } + + return true; } -bool valeq(Value a, Value b) +static Value value_select(Value set, Value key, Error *err) { - Type t1 = type_of(a); - Type t2 = type_of(b); + Type t = value_type(set); + if (t != TYPE_MAP && t != TYPE_ARRAY) { + REPORT(err, "Invalid selection from non-map and non-array value"); + return VALUE_ERROR; + } + AggregateValue *agg = (void*) (set & ~(Value) 7); + + Value *dst = aggregate_select(agg, key); + if (dst) return *dst; + + if (agg->type == TYPE_ARRAY && value_type(key) != TYPE_INT) { + REPORT(err, "Invalid index used in array access"); + return VALUE_ERROR; + } + + char key_buf[1<<8]; + int key_len = value_convert_to_str(key, key_buf, SIZEOF(key_buf)); + if (key_len > SIZEOF(key_buf)-1) + key_len = SIZEOF(key_buf)-1; + key_buf[key_len] = '\0'; + + char set_buf[1<<8]; + int set_len = value_convert_to_str(set, set_buf, SIZEOF(set_buf)); + if (set_len > SIZEOF(set_buf)-1) + set_len = SIZEOF(set_buf)-1; + set_buf[set_len] = '\0'; + + REPORT(err, "Invalid key '%s' used in access to map '%s'", key_buf, set_buf); + return VALUE_ERROR; +} + +static Value value_select_by_index(Value set, int64_t idx, Error *err) +{ + Type t = value_type(set); + if (t != TYPE_MAP && t != TYPE_ARRAY) { + REPORT(err, "Invalid selection from non-map and non-array value"); + return VALUE_ERROR; + } + AggregateValue *agg = (void*) (set & ~(Value) 7); + + if (agg->type == TYPE_MAP) + idx *= 2; + + Value *src = aggregate_select_by_raw_index(agg, idx); + if (src == NULL) { + REPORT(err, "Invalid selection from non-map and non-array value"); + return VALUE_ERROR; + } + + return *src; +} + +static bool value_append(Value set, Value val, WL_Arena *arena, Error *err) +{ + Type t = value_type(set); + if (t != TYPE_ARRAY) { + REPORT(err, "Invalid append on non-array value"); + return false; + } + AggregateValue *agg = (void*) (set & ~(Value) 7); + + if (!aggregate_append(agg, val, VALUE_ERROR, arena)) { + REPORT(err, "Out of memory"); + return false; + } + + return true; +} + +static bool value_eql(Value a, Value b) +{ + Type t1 = value_type(a); + Type t2 = value_type(b); if (t1 != t2) return false; @@ -4462,16 +4100,16 @@ bool valeq(Value a, Value b) switch (t1) { case TYPE_NONE: - return VALUE_TRUE; + return true; case TYPE_BOOL: return a == b; case TYPE_INT: - return get_int(a) == get_int(b); + return value_to_s64(a) == value_to_s64(b); case TYPE_FLOAT: - return get_float(a) == get_float(b); + return value_to_f64(a) == value_to_f64(b); case TYPE_MAP: return false; // TODO @@ -4480,7 +4118,7 @@ bool valeq(Value a, Value b) return false; // TODO case TYPE_STRING: - return streq(get_str(a), get_str(b)); + return streq(value_to_str(a), value_to_str(b)); case TYPE_ERROR: return true; @@ -4489,1712 +4127,1509 @@ bool valeq(Value a, Value b) return false; } -bool valgrt(Value a, Value b) +static bool value_nql(Value a, Value b) { - Type t1 = type_of(a); - Type t2 = type_of(b); + return !value_eql(a, b); +} - if (t1 != t2) - return false; +#define TYPE_PAIR(X, Y) (((uint16_t) (X) << 16) | (uint16_t) (Y)) - switch (t1) { - - case TYPE_NONE: - return VALUE_FALSE; - - case TYPE_BOOL: - return VALUE_FALSE; - - case TYPE_INT: - return get_int(a) > get_int(b); - - case TYPE_FLOAT: - return get_float(a) > get_float(b); - - case TYPE_MAP: - return false; - - case TYPE_ARRAY: - return false; - - case TYPE_STRING: - return false; - - case TYPE_ERROR: - return false; +bool value_greater(Value a, Value b, Error *err) +{ + Type t1 = value_type(a); + Type t2 = value_type(b); + switch (TYPE_PAIR(t1, t2)) { + case TYPE_PAIR(TYPE_INT , TYPE_INT ): return value_to_s64(a) > value_to_s64(b); + case TYPE_PAIR(TYPE_INT , TYPE_FLOAT): return value_to_s64(a) > value_to_f64(b); + case TYPE_PAIR(TYPE_FLOAT, TYPE_INT ): return value_to_f64(a) > value_to_s64(b); + case TYPE_PAIR(TYPE_FLOAT, TYPE_FLOAT): return value_to_f64(a) > value_to_f64(b); + default:break; } - + REPORT(err, "Invalid '>' operation on non-numeric type"); return false; } -int value_length(Value v) +bool value_lower(Value a, Value b, Error *err) { - Type type = type_of(v); - - if (type == TYPE_ARRAY) - return get_array(v)->count; - - if (type == TYPE_MAP) - return get_map(v)->count; - - return -1; -} - -typedef struct { - char *dst; - int max; - int len; -} ToStringContext; - -static void tostr_appends(ToStringContext *tostr, String x) -{ - if (tostr->max > tostr->len) { - int cpy = tostr->max - tostr->len; - if (cpy > x.len) - cpy = x.len; - memcpy(tostr->dst + tostr->len, x.ptr, cpy); + Type t1 = value_type(a); + Type t2 = value_type(b); + switch (TYPE_PAIR(t1, t2)) { + case TYPE_PAIR(TYPE_INT , TYPE_INT ): return value_to_s64(a) < value_to_s64(b); + case TYPE_PAIR(TYPE_INT , TYPE_FLOAT): return value_to_s64(a) < value_to_f64(b); + case TYPE_PAIR(TYPE_FLOAT, TYPE_INT ): return value_to_f64(a) < value_to_s64(b); + case TYPE_PAIR(TYPE_FLOAT, TYPE_FLOAT): return value_to_f64(a) < value_to_f64(b); + default:break; } - tostr->len += x.len; + REPORT(err, "Invalid '<' operation on non-numeric type"); + return false; } -static void tostr_appendi(ToStringContext *tostr, int64_t x) +static Value value_neg(Value v, WL_Arena *arena, Error *err) { - int len; - if (tostr->max >= tostr->len) - len = snprintf(tostr->dst + tostr->len, tostr->max - tostr->len, "%" LLD, x); - else - len = snprintf(NULL, 0, "%" LLD, x); - tostr->len += len; + Type t = value_type(v); + if (t == TYPE_INT) + return value_from_s64(-value_to_s64(v), arena, err); // TODO: overflow + + if (t == TYPE_FLOAT) + return value_from_f64(-value_to_f64(v), arena, err); + + REPORT(err, "Invalid '-' operation on non-numeric type"); + return VALUE_ERROR; } -static void tostr_appendf(ToStringContext *tostr, double x) +static Value value_add(Value v1, Value v2, WL_Arena *arena, Error *err) { - int len; - if (tostr->max >= tostr->len) - len = snprintf(tostr->dst + tostr->len, tostr->max - tostr->len, "%f", x); - else - len = snprintf(NULL, 0, "%f", x); - tostr->len += len; + Type t1 = value_type(v1); + Type t2 = value_type(v2); + + Value r; + switch (TYPE_PAIR(t1, t2)) { + + case TYPE_PAIR(TYPE_INT, TYPE_INT): + { + int64_t u = value_to_s64(v1); + int64_t v = value_to_s64(v2); + // TODO: check overflow and underflow + r = value_from_s64(u + v, arena, err); + } + break; + + case TYPE_PAIR(TYPE_INT, TYPE_FLOAT): + { + double u = (double) value_to_s64(v1); + double v = value_to_f64(v2); + r = value_from_f64(u + v, arena, err); + } + break; + + case TYPE_PAIR(TYPE_FLOAT, TYPE_INT): + { + double u = value_to_f64(v1); + double v = (double) value_to_s64(v2); + r = value_from_f64(u + v, arena, err); + } + break; + + case TYPE_PAIR(TYPE_FLOAT, TYPE_FLOAT): + { + double u = value_to_f64(v1); + double v = value_to_f64(v2); + // TODO: check overflow and underflow + r = value_from_f64(u + v, arena, err); + } + break; + + default: + REPORT(err, "Invalid operation '+' on non-numeric value"); + return VALUE_ERROR; + } + + return r; } -static void value_to_string_inner(Value v, ToStringContext *tostr) +static Value value_sub(Value v1, Value v2, WL_Arena *arena, Error *err) { - switch (type_of(v)) { + Type t1 = value_type(v1); + Type t2 = value_type(v2); + + Value r; + switch (TYPE_PAIR(t1, t2)) { + + case TYPE_PAIR(TYPE_INT, TYPE_INT): + { + int64_t u = value_to_s64(v1); + int64_t v = value_to_s64(v2); + // TODO: check overflow and underflow + r = value_from_s64(u - v, arena, err); + } + break; + + case TYPE_PAIR(TYPE_INT, TYPE_FLOAT): + { + double u = (double) value_to_s64(v1); + double v = value_to_f64(v2); + r = value_from_f64(u - v, arena, err); + } + break; + + case TYPE_PAIR(TYPE_FLOAT, TYPE_INT): + { + double u = value_to_f64(v1); + double v = (double) value_to_s64(v2); + r = value_from_f64(u - v, arena, err); + } + break; + + case TYPE_PAIR(TYPE_FLOAT, TYPE_FLOAT): + { + double u = value_to_f64(v1); + double v = value_to_f64(v2); + // TODO: check overflow and underflow + r = value_from_f64(u - v, arena, err); + } + break; + + default: + REPORT(err, "Invalid operation '-' on non-numeric value"); + return VALUE_ERROR; + } + + return r; +} + +static Value value_mul(Value v1, Value v2, WL_Arena *arena, Error *err) +{ + Type t1 = value_type(v1); + Type t2 = value_type(v2); + + Value r; + switch (TYPE_PAIR(t1, t2)) { + + case TYPE_PAIR(TYPE_INT, TYPE_INT): + { + int64_t u = value_to_s64(v1); + int64_t v = value_to_s64(v2); + // TODO: check overflow and underflow + r = value_from_s64(u * v, arena, err); + } + break; + + case TYPE_PAIR(TYPE_INT, TYPE_FLOAT): + { + double u = (double) value_to_s64(v1); + double v = value_to_f64(v2); + r = value_from_f64(u * v, arena, err); + } + break; + + case TYPE_PAIR(TYPE_FLOAT, TYPE_INT): + { + double u = value_to_f64(v1); + double v = (double) value_to_s64(v2); + r = value_from_f64(u * v, arena, err); + } + break; + + case TYPE_PAIR(TYPE_FLOAT, TYPE_FLOAT): + { + double u = value_to_f64(v1); + double v = value_to_f64(v2); + // TODO: check overflow and underflow + r = value_from_f64(u * v, arena, err); + } + break; + + default: + REPORT(err, "Invalid operation '*' on non-numeric value"); + return VALUE_ERROR; + } + + return r; +} + +static Value value_div(Value v1, Value v2, WL_Arena *arena, Error *err) +{ + Type t1 = value_type(v1); + Type t2 = value_type(v2); + + Value r; + switch (TYPE_PAIR(t1, t2)) { + + case TYPE_PAIR(TYPE_INT, TYPE_INT): + { + // TODO: check division by 0 + + int64_t u = value_to_s64(v1); + int64_t v = value_to_s64(v2); + r = value_from_s64(u / v, arena, err); + } + break; + + case TYPE_PAIR(TYPE_INT, TYPE_FLOAT): + { + // TODO: check division by 0 + + double u = (double) value_to_s64(v1); + double v = value_to_f64(v2); + r = value_from_f64(u / v, arena, err); + } + break; + + case TYPE_PAIR(TYPE_FLOAT, TYPE_INT): + { + // TODO: check division by 0 + + double u = value_to_f64(v1); + double v = (double) value_to_s64(v2); + r = value_from_f64(u / v, arena, err); + } + break; + + case TYPE_PAIR(TYPE_FLOAT, TYPE_FLOAT): + { + double u = value_to_f64(v1); + double v = value_to_f64(v2); + r = value_from_f64(u / v, arena, err); + } + break; + + default: + REPORT(err, "Invalid operation '/' on non-numeric value"); + return VALUE_ERROR; + } + + return r; +} + +static Value value_mod(Value v1, Value v2, WL_Arena *arena, Error *err) +{ + Type t1 = value_type(v1); + Type t2 = value_type(v2); + + if (t1 != TYPE_INT || t2 != TYPE_INT) { + REPORT(err, "Invalid operation '%%' on non-integer value"); + return VALUE_ERROR; + } + + int64_t u = value_to_s64(v1); + int64_t v = value_to_s64(v2); + Value r = value_from_s64(u % v, arena, err); + return r; +} + +static void value_convert_to_str_inner(Writer *w, Value v) +{ + Type t = value_type(v); + switch (t) { case TYPE_NONE: - //tostr_appends(tostr, S("none")); break; case TYPE_BOOL: - // TODO - //tostr_appends(tostr, get_bool(v) ? S("true") : S("false")); + write_text(w, v == VALUE_TRUE ? S("true") : S("false")); break; case TYPE_INT: - tostr_appendi(tostr, get_int(v)); + write_text_s64(w, value_to_s64(v)); break; case TYPE_FLOAT: - tostr_appendf(tostr, get_float(v)); + write_text_f64(w, value_to_f64(v)); break; - case TYPE_MAP: - { - tostr_appends(tostr, S("{ ")); - MapValue *m = get_map(v); - MapItems *batch = &m->head; - while (batch) { - - int num = ITEMS_PER_MAP_BATCH; - if (batch->next == NULL) - num = m->tail_count; - - for (int i = 0; i < num; i++) { - value_to_string_inner(batch->keys[i], tostr); - tostr_appends(tostr, S(": ")); - value_to_string_inner(batch->items[i], tostr); - - if (batch->next != NULL || i+1 < num) - tostr_appends(tostr, S(", ")); - } - - batch = batch->next; - } - tostr_appends(tostr, S(" }")); - } + case TYPE_STRING: + write_text(w, value_to_str(v)); break; case TYPE_ARRAY: { - ArrayValue *a = get_array(v); - ArrayItems *batch = &a->head; - int cursor = 0; - while (batch) { - - int num = ITEMS_PER_ARRAY_BATCH; - if (batch->next == NULL) - num = a->tail_count; - - for (int i = 0; i < num; i++) - value_to_string_inner(batch->items[i], tostr); - - batch = batch->next; - cursor += num; + AggregateValue *agg = (void*) (v & ~(Value) 7); + for (int i = 0; i < agg->count; i++) + value_convert_to_str_inner(w, agg->vals[i]); + Extension *ext = agg->ext; + while (ext) { + for (int i = 0; i < ext->count; i++) + value_convert_to_str_inner(w, ext->vals[i]); + ext = ext->next; } } break; - case TYPE_STRING: - tostr_appends(tostr, get_str(v)); + case TYPE_MAP: + { + write_text(w, S("{")); + AggregateValue *agg = (void*) (v & ~(Value) 7); + for (int i = 0; i < agg->count; i += 2) { + value_convert_to_str_inner(w, agg->vals[i+0]); + write_text(w, S(": ")); + value_convert_to_str_inner(w, agg->vals[i+1]); + if (i+2 < agg->count || agg->ext) + write_text(w, S(", ")); + } + Extension *ext = agg->ext; + while (ext) { + for (int i = 0; i < ext->count; i += 2) { + value_convert_to_str_inner(w, ext->vals[i+0]); + write_text(w, S(": ")); + value_convert_to_str_inner(w, ext->vals[i+1]); + if (i+2 < ext->count || ext->next) + write_text(w, S(", ")); + } + ext = ext->next; + } + write_text(w, S("}")); + } break; case TYPE_ERROR: - tostr_appends(tostr, S("error")); break; } } -int value_to_string(Value v, char *dst, int max) +static int value_convert_to_str(Value v, char *dst, int cap) { - ToStringContext tostr = { dst, max, 0 }; - value_to_string_inner(v, &tostr); - return tostr.len; + Writer w = { dst, cap, 0}; + value_convert_to_str_inner(&w, v); + return w.len; } -//////////////////////////////////////////////////////////////////////////////////////// -// src/eval.h -//////////////////////////////////////////////////////////////////////////////////////// +#undef TYPE_PAIR -#ifndef WL_EVAL_INCLUDED -#define WL_EVAL_INCLUDED +///////////////////////////////////////////////////////////////////////// +// RUNTIME +///////////////////////////////////////////////////////////////////////// -#ifndef WL_AMALGAMATION -#include "assemble.h" -#endif - -// TODO: pretty sure this is unused -int eval(WL_Program p, WL_Arena *a, char *errbuf, int errmax); - -#endif // WL_EVAL_INCLUDED - -//////////////////////////////////////////////////////////////////////////////////////// -// src/eval.c -//////////////////////////////////////////////////////////////////////////////////////// - - -#ifndef WL_AMALGAMATION -#include "includes.h" -#include "value.h" -#include "eval.h" -#endif - -#define FRAME_LIMIT 128 -#define EVAL_STACK_LIMIT 128 -#define GROUP_LIMIT 128 +#define MAX_STACK 1024 +#define MAX_FRAMES 1024 +#define MAX_GROUPS 8 typedef struct { - int group; - int return_addr; + int retaddr; + int varbase; } Frame; -struct WL_State { +typedef enum { + RUNTIME_BEGIN, + RUNTIME_LOOP, + RUNTIME_DONE, + RUNTIME_ERROR, + RUNTIME_OUTPUT, + RUNTIME_SYSVAR, + RUNTIME_SYSCALL, +} RuntimeState; + +struct WL_Runtime { + + RuntimeState state; String code; String data; int off; - bool trace; - - WL_Arena *a; - - char *errbuf; - int errmax; - int errlen; + int vars; + int stack; + Value values[MAX_STACK]; int num_frames; - Frame frames[FRAME_LIMIT]; - - int eval_depth; - Value eval_stack[EVAL_STACK_LIMIT]; + Frame frames[MAX_FRAMES]; int num_groups; - int groups[GROUP_LIMIT]; + int groups[MAX_GROUPS]; - int cur_print; - int num_prints; + WL_Arena *arena; + + char msg[128]; + Error err; - String sysvar; - String syscall; - bool syscall_error; int stack_before_user; - int stack_base_for_user; + String str_for_user; + int num_output; + int cur_output; + char buf[128]; }; -void eval_report(WL_State *state, char *fmt, ...) +WL_Runtime *wl_runtime_init(WL_Arena *arena, WL_Program program) { - if (state->errmax == 0 || state->errlen > 0) + if (program.len < 3 * sizeof(uint32_t)) + return NULL; + + uint32_t magic; + uint32_t code_len; + uint32_t data_len; + + memcpy(&magic , program.ptr + 0, sizeof(uint32_t)); + memcpy(&code_len, program.ptr + 4, sizeof(uint32_t)); + memcpy(&data_len, program.ptr + 8, sizeof(uint32_t)); + + if (magic != WL_MAGIC) + return NULL; + + String code = { program.ptr + sizeof(uint32_t) * 3 , code_len }; + String data = { program.ptr + sizeof(uint32_t) * 3 + code_len, data_len }; + + WL_Runtime *rt = alloc(arena, SIZEOF(WL_Runtime), ALIGNOF(WL_Runtime)); + if (rt == NULL) + return NULL; + + *rt = (WL_Runtime) { + .state = RUNTIME_BEGIN, + .code = code, + .data = data, + .off = 0, + .stack = 0, + .vars = MAX_STACK-1, + .num_frames = 0, + .arena = arena, + .err = { NULL, 0, false }, + }; + rt->err.buf = rt->msg; + rt->err.cap = SIZEOF(rt->msg); + + rt->frames[rt->num_frames++] = (Frame) { + .retaddr = 0, + .varbase = rt->vars, + }; + + return rt; +} + +WL_String wl_runtime_error(WL_Runtime *rt) +{ + return rt->err.yes + ? (WL_String) { rt->msg, strlen(rt->msg) } + : (WL_String) { NULL, 0 }; +} + +static void rt_read_mem(WL_Runtime *r, void *dst, int len) +{ + ASSERT(r->off + len <= r->code.len); + memcpy(dst, r->code.ptr + r->off, len); + r->off += len; +} + +static uint8_t rt_read_u8(WL_Runtime *rt) +{ + ASSERT(rt->state == RUNTIME_LOOP); + + uint8_t x; + rt_read_mem(rt, &x, SIZEOF(x)); + + return x; +} + +static uint32_t rt_read_u32(WL_Runtime *rt) +{ + ASSERT(rt->state == RUNTIME_LOOP); + + uint32_t x; + rt_read_mem(rt, &x, SIZEOF(x)); + + return x; +} + +static int64_t rt_read_s64(WL_Runtime *rt) +{ + ASSERT(rt->state == RUNTIME_LOOP); + + int64_t x; + rt_read_mem(rt, &x, SIZEOF(x)); + + return x; +} + +static double rt_read_f64(WL_Runtime *rt) +{ + ASSERT(rt->state == RUNTIME_LOOP); + + double x; + rt_read_mem(rt, &x, SIZEOF(x)); + + return x; +} + +static String rt_read_str(WL_Runtime *rt) +{ + ASSERT(rt->state == RUNTIME_LOOP); + uint32_t off = rt_read_u32(rt); + uint32_t len = rt_read_u32(rt); + ASSERT(off + len <= rt->data.len); + return (String) { rt->data.ptr + off, len }; +} + +static Value *rt_variable(WL_Runtime *rt, uint8_t x) +{ + ASSERT(rt->num_frames > 0); + + Frame *frame = &rt->frames[rt->num_frames-1]; + + ASSERT(frame->varbase - x >= 0 + && frame->varbase - x < MAX_STACK); + + return &rt->values[frame->varbase - x]; +} + +static int values_usage(WL_Runtime *rt) +{ + int num_vars = (MAX_STACK - rt->vars - 1); + return rt->stack + num_vars; +} + +static bool rt_check_stack(WL_Runtime *rt, int min) +{ + if (MAX_STACK - values_usage(rt) < min) { + REPORT(&rt->err, "Out of stack"); + rt->state = RUNTIME_ERROR; + return false; + } + return true; +} + +static bool rt_push_frame(WL_Runtime *rt, uint8_t args) +{ + if (rt->num_frames == MAX_FRAMES) { + REPORT(&rt->err, "Call stack limit reached"); + rt->state = RUNTIME_ERROR; + return false; + } + + if (MAX_STACK - values_usage(rt) < args) { + REPORT(&rt->err, "Stack limit reached"); + rt->state = RUNTIME_ERROR; + return false; + } + + Frame *frame = &rt->frames[rt->num_frames++]; + frame->retaddr = rt->off; + frame->varbase = rt->vars; + + for (int i = 0; i < args; i++) + rt->values[rt->vars--] = rt->values[--rt->stack]; + + return true; +} + +static void rt_pop_frame(WL_Runtime *rt) +{ + ASSERT(rt->num_frames > 0); + Frame *frame = &rt->frames[rt->num_frames-1]; + rt->off = frame->retaddr; + rt->vars = frame->varbase; + rt->num_frames--; +} + +static void rt_set_frame_vars(WL_Runtime *rt, uint8_t num) +{ + ASSERT(rt->num_frames > 0); + Frame *frame = &rt->frames[rt->num_frames-1]; + int num_vars = frame->varbase - rt->vars; + if (num_vars < num) + for (int i = 0; i < num - num_vars; i++) + rt->values[rt->vars - i] = VALUE_NONE; + rt->vars = frame->varbase - num; +} + +static void rt_push_group(WL_Runtime *rt) +{ + if (rt->num_groups == MAX_GROUPS) { + REPORT(&rt->err, "Out of memory"); + rt->state = RUNTIME_ERROR; + return; + } + rt->groups[rt->num_groups++] = rt->stack; +} + +static void rt_pack_group(WL_Runtime *rt) +{ + if (!rt_check_stack(rt, 1)) return; - int len = snprintf(state->errbuf, state->errmax, "Error: "); - if (len < 0) { - // TODO + ASSERT(rt->num_groups > 0); + int start = rt->groups[--rt->num_groups]; + int end = rt->stack; + + if (end - start > 1) { + + Value set = value_empty_array(end - start, rt->arena, &rt->err); + if (set == VALUE_ERROR) + return; + + for (int i = start; i < end; i++) + if (!value_append(set, rt->values[i], rt->arena, &rt->err)) + return; + + rt->stack = start; + rt->values[rt->stack++] = set; } +} - va_list args; - va_start(args, fmt); - int ret = vsnprintf(state->errbuf + len, state->errmax - len, fmt, args); - va_end(args); - if (ret < 0) { - // TODO +static void rt_pop_group(WL_Runtime *rt) +{ + ASSERT(rt->num_groups > 0); + rt->stack = rt->groups[--rt->num_groups]; +} + +static void value_print(Value v) +{ + char buf[1<<8]; + int len = value_convert_to_str(v, buf, SIZEOF(buf)); + if (len < SIZEOF(buf)) + fwrite(buf, 1, len, stdout); + else { + len = SIZEOF(buf)-1; + fwrite(buf, 1, len, stdout); + fprintf(stdout, " [...]"); } - len += ret; - - state->errlen = len; + putc('\n', stdout); + fflush(stdout); } -static uint8_t read_u8(WL_State *state) +static void step(WL_Runtime *rt) { - assert(state->off >= 0); - assert(state->off < state->code.len); - return state->code.ptr[state->off++]; -} + switch (rt_read_u8(rt)) { -static void read_mem(WL_State *state, void *dst, int len) -{ - memcpy(dst, (uint8_t*) state->code.ptr + state->off, len); - state->off += len; -} - -static uint32_t read_u32(WL_State *state) -{ - uint32_t x; - read_mem(state, &x, (int) sizeof(x)); - return x; -} - -static int64_t read_s64(WL_State *state) -{ - int64_t x; - read_mem(state, &x, (int) sizeof(x)); - return x; -} - -static double read_f64(WL_State *state) -{ - double x; - read_mem(state, &x, (int) sizeof(x)); - return x; -} - -int step(WL_State *state) -{ - uint8_t opcode = read_u8(state); - - if (state->trace) { - printf("%-3d: ", state->off-1); - print_instruction(state->code.ptr + state->off - 1, state->data.ptr); - printf("\n"); - } - - switch (opcode) { + Type t; + Value v1; + Value v2; + Value v3; + uint32_t o; + uint8_t b1; + uint8_t b2; + uint8_t b3; + int64_t i; + double f; + String s; case OPCODE_NOPE: - { - // Do nothing - } - break; - - case OPCODE_EXIT: - { - return 1; - } - break; - - case OPCODE_GROUP: - { - state->groups[state->num_groups++] = state->eval_depth; - } - break; - - case OPCODE_GPOP: - { - int group = state->groups[--state->num_groups]; - state->eval_depth = group; - } - break; - - case OPCODE_GPRINT: - { - state->num_prints = state->eval_depth - state->groups[state->num_groups-1]; - } - break; - - case OPCODE_GCOALESCE: - { - state->num_groups--; - } - break; - - case OPCODE_GTRUNC: - { - uint32_t num = read_u32(state); - - int group_size = state->eval_depth - state->groups[state->num_groups-1]; - - if (group_size < (int) num) - for (int i = 0; i < (int) num - group_size; i++) - state->eval_stack[state->eval_depth + i] = VALUE_NONE; - - state->eval_depth = state->groups[state->num_groups-1] + num; - } - break; - - case OPCODE_GOVERWRITE: - { - int current = state->groups[state->num_groups-1]; - int parent = state->groups[state->num_groups-2]; - - int current_size = state->eval_depth - current; - - for (int i = 0; i < current_size; i++) - state->eval_stack[parent + i] = state->eval_stack[current + i]; - - state->num_groups--; - state->eval_depth = parent + current_size; - } - break; - - case OPCODE_GPACK: - { - Value array = make_array(state->a); - if (array == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - for (int i = state->groups[state->num_groups-1]; i < state->eval_depth; i++) { - int ret = array_append(state->a, array, state->eval_stack[i]); - if (ret < 0) { - eval_report(state, "Out of memory"); - return -1; - } - } - - state->eval_depth = state->groups[--state->num_groups]; - state->eval_stack[state->eval_depth++] = array; - } - break; - - case OPCODE_PUSHN: - { - state->eval_stack[state->eval_depth++] = VALUE_NONE; - } - break; - - case OPCODE_PUSHI: - { - int64_t x = read_s64(state); - - Value v = make_int(state->a, x); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = v; - } - break; - - case OPCODE_PUSHF: - { - double x = read_f64(state); - - Value v = make_float(state->a, x); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = v; - } - break; - - case OPCODE_PUSHS: - { - uint32_t off = read_u32(state); - uint32_t len = read_u32(state); - - Value v = make_str(state->a, (String) { state->data.ptr + off, len }); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = v; - } - break; - - case OPCODE_PUSHT: - { - state->eval_stack[state->eval_depth++] = VALUE_TRUE; - } - break; - - case OPCODE_PUSHFL: - { - state->eval_stack[state->eval_depth++] = VALUE_FALSE; - } - break; - - case OPCODE_PUSHV: - { - uint8_t idx = read_u8(state); - - int group = state->frames[state->num_frames-1].group; - Value v = state->eval_stack[state->groups[group] + idx]; - - state->eval_stack[state->eval_depth++] = v; - } - break; - - case OPCODE_PUSHA: - { - uint32_t cap = read_u32(state); - (void) cap; - - Value v = make_array(state->a); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = v; - } - break; - - case OPCODE_PUSHM: - { - uint32_t cap = read_u32(state); - (void) cap; - - Value v = make_map(state->a); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = v; - } - break; - - case OPCODE_POP: - { - assert(state->num_groups == 0 || state->eval_depth > state->groups[state->num_groups-1]); - state->eval_depth--; - } - break; - - case OPCODE_NEG: - { - Value a = state->eval_stack[--state->eval_depth]; - Type t = type_of(a); - - Value r; - if (0) {} - else if (t == TYPE_INT) r = make_int(state->a, -get_int(a)); - else if (t == TYPE_FLOAT) r = make_float(state->a, -get_float(a)); - else { - eval_report(state, "Invalid operation on non-numeric value"); - return -1; - } - - if (r == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_EQL: - { - Value a = state->eval_stack[state->eval_depth-2]; - Value b = state->eval_stack[state->eval_depth-1]; - state->eval_depth -= 2; - - Value r = valeq(a, b) ? VALUE_TRUE : VALUE_FALSE; - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_NQL: - { - Value a = state->eval_stack[state->eval_depth-2]; - Value b = state->eval_stack[state->eval_depth-1]; - state->eval_depth -= 2; - - Value r = valeq(a, b) ? VALUE_FALSE : VALUE_TRUE; - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_LSS: - { - Value a = state->eval_stack[state->eval_depth-2]; - Value b = state->eval_stack[state->eval_depth-1]; - state->eval_depth -= 2; - - if (type_of(a) != TYPE_INT || type_of(b) != TYPE_INT) { - eval_report(state, "Invalid operation on non-numeric value"); - return -1; - } - - Value r = valgrt(a, b) || valeq(a, b) ? VALUE_FALSE : VALUE_TRUE; - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_GRT: - { - Value a = state->eval_stack[state->eval_depth-2]; - Value b = state->eval_stack[state->eval_depth-1]; - state->eval_depth -= 2; - - if (type_of(a) != TYPE_INT || type_of(b) != TYPE_INT) { - eval_report(state, "Invalid operation on non-numeric value"); - return -1; - } - - Value r = valgrt(a, b) ? VALUE_TRUE : VALUE_FALSE; - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_ADD: - { - Value a = state->eval_stack[state->eval_depth-2]; - Value b = state->eval_stack[state->eval_depth-1]; - state->eval_depth -= 2; - - #define TYPE_PAIR(X, Y) (((uint16_t) (X) << 16) | (uint16_t) (Y)) - - Type t1 = type_of(a); - Type t2 = type_of(b); - - Value r; - switch (TYPE_PAIR(t1, t2)) { - - case TYPE_PAIR(TYPE_INT, TYPE_INT): - { - int64_t u = get_int(a); - int64_t v = get_int(b); - // TODO: check overflow and underflow - r = make_int(state->a, u + v); - } - break; - - case TYPE_PAIR(TYPE_INT, TYPE_FLOAT): - { - float u = (float) get_int(a); - float v = get_float(b); - r = make_float(state->a, u + v); - } - break; - - case TYPE_PAIR(TYPE_FLOAT, TYPE_INT): - { - float u = get_float(a); - float v = (float) get_int(b); - r = make_float(state->a, u + v); - } - break; - - case TYPE_PAIR(TYPE_FLOAT, TYPE_FLOAT): - { - float u = get_float(a); - float v = get_float(b); - // TODO: check overflow and underflow - r = make_float(state->a, u + v); - } - break; - - default: - eval_report(state, "Invalid operation on non-numeric value"); - return -1; - } - - if (r == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_SUB: - { - Value a = state->eval_stack[state->eval_depth-2]; - Value b = state->eval_stack[state->eval_depth-1]; - state->eval_depth -= 2; - - Type t1 = type_of(a); - Type t2 = type_of(b); - - Value r; - switch (TYPE_PAIR(t1, t2)) { - - case TYPE_PAIR(TYPE_INT, TYPE_INT): - { - int64_t u = get_int(a); - int64_t v = get_int(b); - // TODO: check overflow and underflow - r = make_int(state->a, u - v); - } - break; - - case TYPE_PAIR(TYPE_INT, TYPE_FLOAT): - { - float u = (float) get_int(a); - float v = get_float(b); - r = make_float(state->a, u - v); - } - break; - - case TYPE_PAIR(TYPE_FLOAT, TYPE_INT): - { - float u = get_float(a); - float v = (float) get_int(b); - r = make_float(state->a, u - v); - } - break; - - case TYPE_PAIR(TYPE_FLOAT, TYPE_FLOAT): - { - float u = get_float(a); - float v = get_float(b); - // TODO: check overflow and underflow - r = make_float(state->a, u - v); - } - break; - - default: - eval_report(state, "Invalid operation on non-numeric value"); - return -1; - } - - if (r == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_MUL: - { - Value a = state->eval_stack[state->eval_depth-2]; - Value b = state->eval_stack[state->eval_depth-1]; - state->eval_depth -= 2; - - Type t1 = type_of(a); - Type t2 = type_of(b); - - Value r; - switch (TYPE_PAIR(t1, t2)) { - - case TYPE_PAIR(TYPE_INT, TYPE_INT): - { - int64_t u = get_int(a); - int64_t v = get_int(b); - // TODO: check overflow and underflow - r = make_int(state->a, u * v); - } - break; - - case TYPE_PAIR(TYPE_INT, TYPE_FLOAT): - { - float u = (float) get_int(a); - float v = get_float(b); - r = make_float(state->a, u * v); - } - break; - - case TYPE_PAIR(TYPE_FLOAT, TYPE_INT): - { - float u = get_float(a); - float v = (float) get_int(b); - r = make_float(state->a, u * v); - } - break; - - case TYPE_PAIR(TYPE_FLOAT, TYPE_FLOAT): - { - float u = get_float(a); - float v = get_float(b); - // TODO: check overflow and underflow - r = make_float(state->a, u * v); - } - break; - - default: - eval_report(state, "Invalid operation on non-numeric value"); - return -1; - } - - if (r == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_DIV: - { - Value a = state->eval_stack[state->eval_depth-2]; - Value b = state->eval_stack[state->eval_depth-1]; - state->eval_depth -= 2; - - Type t1 = type_of(a); - Type t2 = type_of(b); - - Value r; - switch (TYPE_PAIR(t1, t2)) { - - case TYPE_PAIR(TYPE_INT, TYPE_INT): - { - // TODO: check division by 0 - - int64_t u = get_int(a); - int64_t v = get_int(b); - r = make_int(state->a, u / v); - } - break; - - case TYPE_PAIR(TYPE_INT, TYPE_FLOAT): - { - // TODO: check division by 0 - - float u = (float) get_int(a); - float v = get_float(b); - r = make_float(state->a, u / v); - } - break; - - case TYPE_PAIR(TYPE_FLOAT, TYPE_INT): - { - // TODO: check division by 0 - - float u = get_float(a); - float v = (float) get_int(b); - r = make_float(state->a, u / v); - } - break; - - case TYPE_PAIR(TYPE_FLOAT, TYPE_FLOAT): - { - float u = get_float(a); - float v = get_float(b); - r = make_float(state->a, u / v); - } - break; - - default: - eval_report(state, "Invalid operation on non-numeric value"); - return -1; - } - - if (r == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_MOD: - { - Value a = state->eval_stack[state->eval_depth-2]; - Value b = state->eval_stack[state->eval_depth-1]; - state->eval_depth -= 2; - - Type t1 = type_of(a); - Type t2 = type_of(b); - - if (t1 != TYPE_INT || t2 != TYPE_INT) { - eval_report(state, "Invalid modulo operation on non-integer value"); - return -1; - } - - int64_t u = get_int(a); - int64_t v = get_int(b); - Value r = make_int(state->a, u % v); - if (r == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_SETV: - { - uint8_t x = read_u8(state); - - Frame *f = &state->frames[state->num_frames-1]; - state->eval_stack[state->groups[f->group] + x] = state->eval_stack[--state->eval_depth]; - } break; case OPCODE_JUMP: - { - uint32_t x = read_u32(state); - state->off = x; - } + rt->off = rt_read_u32(rt); break; case OPCODE_JIFP: - { - uint32_t x = read_u32(state); - Value a = state->eval_stack[--state->eval_depth]; - - if (a == VALUE_FALSE) - state->off = x; - else { - if (a != VALUE_TRUE) { - eval_report(state, "Invalid operation on non-boolean value"); - return -1; - } - } + ASSERT(rt->stack > 0); + o = rt_read_u32(rt); + v1 = rt->values[--rt->stack]; + if (v1 == VALUE_FALSE) + rt->off = o; + else if (value_type(v1) != TYPE_BOOL) { + REPORT(&rt->err, "Invalid non-boolean condition"); + rt->state = RUNTIME_ERROR; + break; } break; - case OPCODE_CALL: - { - uint32_t off = read_u32(state); - - if (state->num_frames == FRAME_LIMIT) { - eval_report(state, "Frame limit reached"); - return -1; - } - state->frames[state->num_frames++] = (Frame) {.return_addr=state->off, .group=state->num_groups-1}; - - state->off = off; - } + case OPCODE_VARS: + b1 = rt_read_u8(rt); + rt_set_frame_vars(rt, b1); break; - case OPCODE_RET: - { - state->off = state->frames[--state->num_frames].return_addr; - } - break; - - case OPCODE_APPEND: - { - Value val = state->eval_stack[state->eval_depth-1]; - Value set = state->eval_stack[state->eval_depth-2]; - state->eval_depth--; - - if (type_of(set) != TYPE_ARRAY) { - eval_report(state, "Invalid operation on non-array value"); - return -1; - } - - int ret = array_append(state->a, set, val); - if (ret < 0) { - eval_report(state, "Out of memory"); - return -1; - } - } - break; - - case OPCODE_INSERT1: - { - Value key = state->eval_stack[state->eval_depth-1]; - Value val = state->eval_stack[state->eval_depth-2]; - Value set = state->eval_stack[state->eval_depth-3]; - state->eval_depth -= 2; - - if (type_of(set) == TYPE_ARRAY) { - - if (type_of(key) != TYPE_INT) { - assert(0); // TODO - } - int64_t idx = get_int(key); - - Value *dst = array_select(set, idx); - if (dst == NULL) { - eval_report(state, "Index out of range"); - return -1; - } - *dst = val; - - } else if (type_of(set) == TYPE_MAP) { - - int ret = map_insert(state->a, set, key, val); - if (ret < 0) { - eval_report(state, "Out of memory"); - return -1; - } - - } else { - eval_report(state, "Invalid insertion on non-array and non-map value"); - return -1; - } - } - break; - - case OPCODE_INSERT2: - { - Value key = state->eval_stack[state->eval_depth-1]; - Value set = state->eval_stack[state->eval_depth-2]; - Value val = state->eval_stack[state->eval_depth-3]; - state->eval_depth -= 2; - - if (type_of(set) == TYPE_ARRAY) { - - if (type_of(key) != TYPE_INT) { - assert(0); // TODO - } - int64_t idx = get_int(key); - - Value *dst = array_select(set, idx); - if (dst == NULL) { - eval_report(state, "Index out of range"); - return -1; - } - *dst = val; - - } else if (type_of(set) == TYPE_MAP) { - - int ret = map_insert(state->a, set, key, val); - if (ret < 0) { - eval_report(state, "Out of memory"); - return -1; - } - - } else { - eval_report(state, "Invalid insertion on non-array and non-map value"); - return -1; - } - } - break; - - case OPCODE_SELECT: - { - Value key = state->eval_stack[state->eval_depth-1]; - Value set = state->eval_stack[state->eval_depth-2]; - state->eval_depth -= 2; - - Value r; - if (type_of(set) == TYPE_ARRAY) { - - if (type_of(key) != TYPE_INT) { - assert(0); // TODO - } - int64_t idx = get_int(key); - - Value *src = array_select(set, idx); - if (src == NULL) { - eval_report(state, "Index out of range"); - return -1; - } - r = *src; - - } else if (type_of(set) == TYPE_MAP) { - - int ret = map_select(set, key, &r); - if (ret < 0) { - eval_report(state, "Key not contained in map"); - return -1; - } - - } else { - eval_report(state, "Invalid selection from non-array and non-map value"); - return -1; - } - - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_PRINT: - { - state->num_prints = 1; + case OPCODE_OUTPUT: + if (rt->stack > 0) { + rt->cur_output = 0; + rt->num_output = rt->stack; + rt->state = RUNTIME_OUTPUT; } break; case OPCODE_SYSVAR: - { - uint32_t off = read_u32(state); - uint32_t len = read_u32(state); - String name = { state->data.ptr + off, len }; - - state->sysvar = name; - state->stack_before_user = state->eval_depth; - state->stack_base_for_user = state->groups[state->num_groups-1]; - } + s = rt_read_str(rt); + rt_push_frame(rt, 0); + rt->stack_before_user = rt->stack; + rt->str_for_user = s; + rt->state = RUNTIME_SYSVAR; break; case OPCODE_SYSCALL: - { - uint32_t off = read_u32(state); - uint32_t len = read_u32(state); - String name = { state->data.ptr + off, len }; + b1 = rt_read_u8(rt); + s = rt_read_str(rt); + rt_push_frame(rt, b1); + rt->stack_before_user = rt->stack; + rt->str_for_user = s; + rt->state = RUNTIME_SYSCALL; + break; - int num_args = state->eval_depth - state->groups[state->num_groups-1]; + case OPCODE_CALL: + b1 = rt_read_u8(rt); + o = rt_read_u32(rt); + rt_push_frame(rt, b1); + rt->off = o; + break; - Value v = make_int(state->a, num_args); - if (v == VALUE_ERROR) { - assert(0); // TODO - } - state->eval_stack[state->eval_depth++] = v; + case OPCODE_RET: + rt_pop_frame(rt); + break; - state->syscall = name; - state->stack_before_user = state->eval_depth; - state->stack_base_for_user = state->groups[state->num_groups-1]; - } + case OPCODE_GROUP: + rt_push_group(rt); + break; + + case OPCODE_PACK: + rt_pack_group(rt); + break; + + case OPCODE_GPOP: + rt_pop_group(rt); break; case OPCODE_FOR: - { - uint8_t var_3 = read_u8(state); - uint8_t var_1 = read_u8(state); - uint8_t var_2 = read_u8(state); - uint32_t end = read_u32(state); + b1 = rt_read_u8(rt); + b2 = rt_read_u8(rt); + b3 = rt_read_u8(rt); + o = rt_read_u32(rt); - int base; - { - int group = state->frames[state->num_frames-1].group; - base = state->groups[group]; - } + v1 = *rt_variable(rt, b3); + ASSERT(value_type(v1) == TYPE_INT); + i = value_to_s64(v1); - int64_t idx; - { - Value idx_val = state->eval_stack[base + var_2]; - if (type_of(idx_val) != TYPE_INT) { - assert(0); // TODO - } - idx = get_int(idx_val); - } + v2 = *rt_variable(rt, b1); - Value set = state->eval_stack[base + var_3]; - - Type set_type = type_of(set); - if (set_type == TYPE_ARRAY) { - - if (value_length(set) == idx) { - state->off = end; - break; - } - - state->eval_stack[base + var_1] = *array_select(set, idx); - - } else if (set_type == TYPE_MAP) { - - if (value_length(set) == idx) { - state->off = end; - break; - } - - state->eval_stack[base + var_1] = *map_select_by_index(set, idx); - - } else { - assert(0); // TODO - } - - Value v = make_int(state->a, idx + 1); - if (v == VALUE_ERROR) { - assert(0); // TODO - } - state->eval_stack[base + var_2] = v; + if (value_length(v2)-1 == i) { + rt->off = o; + break; } + i++; + + v1 = value_select_by_index(v2, i, &rt->err); + if (v1 == VALUE_ERROR) break; + + *rt_variable(rt, b2) = v1; + + v1 = value_from_s64(i, rt->arena, &rt->err); // TODO: this could be in-place + *rt_variable(rt, b3) = v1; + break; + + case OPCODE_EXIT: + rt->state = RUNTIME_DONE; + break; + + case OPCODE_POP: + ASSERT(rt->stack > 0); + rt->stack--; + break; + + case OPCODE_SETV: + ASSERT(rt->stack > 0); + b1 = rt_read_u8(rt); + *rt_variable(rt, b1) = rt->values[rt->stack-1]; + break; + + case OPCODE_PUSHV: + if (!rt_check_stack(rt, 1)) break; + b1 = rt_read_u8(rt); + rt->values[rt->stack++] = *rt_variable(rt, b1); + break; + + case OPCODE_PUSHI: + if (!rt_check_stack(rt, 1)) break; + i = rt_read_s64(rt); + v1 = value_from_s64(i, rt->arena, &rt->err); + rt->values[rt->stack++] = v1; + break; + + case OPCODE_PUSHF: + if (!rt_check_stack(rt, 1)) break; + f = rt_read_f64(rt); + v1 = value_from_f64(f, rt->arena, &rt->err); + rt->values[rt->stack++] = v1; + break; + + case OPCODE_PUSHS: + if (!rt_check_stack(rt, 1)) break; + s = rt_read_str(rt); + v1 = value_from_str(s, rt->arena, &rt->err); + rt->values[rt->stack++] = v1; + break; + + case OPCODE_PUSHA: + if (!rt_check_stack(rt, 1)) break; + o = rt_read_u32(rt); + v1 = value_empty_array(o, rt->arena, &rt->err); + rt->values[rt->stack++] = v1; + break; + + case OPCODE_PUSHM: + if (!rt_check_stack(rt, 1)) break; + o = rt_read_u32(rt); + v1 = value_empty_map(o, rt->arena, &rt->err); + rt->values[rt->stack++] = v1; + break; + + case OPCODE_PUSHN: + if (!rt_check_stack(rt, 1)) break; + rt->values[rt->stack++] = VALUE_NONE; + break; + + case OPCODE_PUSHT: + if (!rt_check_stack(rt, 1)) break; + rt->values[rt->stack++] = VALUE_TRUE; + break; + + case OPCODE_PUSHFL: + if (!rt_check_stack(rt, 1)) break; + rt->values[rt->stack++] = VALUE_FALSE; break; case OPCODE_LEN: - { - Value set = state->eval_stack[state->eval_depth-1]; - - Type type = type_of(set); - if (type != TYPE_ARRAY && type != TYPE_MAP) { - assert(0); // TODO - } - - Value len = make_int(state->a, value_length(set)); - if (len == VALUE_ERROR) { - assert(0); // TODO - } - - state->eval_stack[state->eval_depth++] = len; + ASSERT(rt->stack > 0); + v1 = rt->values[rt->stack-1]; + t = value_type(v1); + if (t != TYPE_ARRAY && t != TYPE_MAP) { + REPORT(&rt->err, "Invalid operation 'len' on non-aggregate value"); + rt->state = RUNTIME_ERROR; + break; } + v2 = value_from_s64(value_length(v1), rt->arena, &rt->err); + rt->values[rt->stack-1] = v2; + break; + + case OPCODE_NEG: + ASSERT(rt->stack > 0); + v1 = rt->values[rt->stack-1]; + v2 = value_neg(v1, rt->arena, &rt->err); + rt->values[rt->stack-1] = v2; + break; + + case OPCODE_EQL: + ASSERT(rt->stack > 1); + v1 = rt->values[--rt->stack]; + v2 = rt->values[--rt->stack]; + v3 = value_eql(v2, v1) ? VALUE_TRUE : VALUE_FALSE; + rt->values[rt->stack++] = v3; + break; + + case OPCODE_NQL: + ASSERT(rt->stack > 1); + v1 = rt->values[--rt->stack]; + v2 = rt->values[--rt->stack]; + v3 = value_nql(v2, v1) ? VALUE_TRUE : VALUE_FALSE; + rt->values[rt->stack++] = v3; + break; + + case OPCODE_LSS: + ASSERT(rt->stack > 1); + v1 = rt->values[--rt->stack]; + v2 = rt->values[--rt->stack]; + v3 = value_lower(v2, v1, &rt->err) ? VALUE_TRUE : VALUE_FALSE; + rt->values[rt->stack++] = v3; + break; + + case OPCODE_GRT: + ASSERT(rt->stack > 1); + v1 = rt->values[--rt->stack]; + v2 = rt->values[--rt->stack]; + v3 = value_greater(v2, v1, &rt->err) ? VALUE_TRUE : VALUE_FALSE; + rt->values[rt->stack++] = v3; + break; + + case OPCODE_ADD: + ASSERT(rt->stack > 1); + v1 = rt->values[--rt->stack]; + v2 = rt->values[--rt->stack]; + v3 = value_add(v2, v1, rt->arena, &rt->err); + rt->values[rt->stack++] = v3; + break; + + case OPCODE_SUB: + ASSERT(rt->stack > 1); + v1 = rt->values[--rt->stack]; + v2 = rt->values[--rt->stack]; + v3 = value_sub(v2, v1, rt->arena, &rt->err); + rt->values[rt->stack++] = v3; + break; + + case OPCODE_MUL: + ASSERT(rt->stack > 1); + v1 = rt->values[--rt->stack]; + v2 = rt->values[--rt->stack]; + v3 = value_mul(v2, v1, rt->arena, &rt->err); + rt->values[rt->stack++] = v3; + break; + + case OPCODE_DIV: + ASSERT(rt->stack > 1); + v1 = rt->values[--rt->stack]; + v2 = rt->values[--rt->stack]; + v3 = value_div(v2, v1, rt->arena, &rt->err); + rt->values[rt->stack++] = v3; + break; + + case OPCODE_MOD: + ASSERT(rt->stack > 1); + v1 = rt->values[--rt->stack]; + v2 = rt->values[--rt->stack]; + v3 = value_mod(v2, v1, rt->arena, &rt->err); + rt->values[rt->stack++] = v3; + break; + + case OPCODE_APPEND: + ASSERT(rt->stack > 1); + v2 = rt->values[--rt->stack]; + v1 = rt->values[rt->stack-1]; + value_append(v1, v2, rt->arena, &rt->err); + break; + + case OPCODE_INSERT1: + ASSERT(rt->stack > 2); + v1 = rt->values[--rt->stack]; + v2 = rt->values[--rt->stack]; + v3 = rt->values[rt->stack-1]; + value_insert(v3, v1, v2, rt->arena, &rt->err); + break; + + case OPCODE_INSERT2: + ASSERT(rt->stack > 2); + v1 = rt->values[--rt->stack]; + v2 = rt->values[--rt->stack]; + v3 = rt->values[rt->stack-1]; + value_insert(v2, v1, v3, rt->arena, &rt->err); + break; + + case OPCODE_SELECT: + ASSERT(rt->stack > 1); + v1 = rt->values[--rt->stack]; + v2 = rt->values[--rt->stack]; + v3 = value_select(v2, v1, &rt->err); + rt->values[rt->stack++] = v3; break; default: - eval_report(state, "Invalid opcode (offset %d)", state->off-1); - return -1; + UNREACHABLE; } - - return 0; } -WL_State *WL_State_init(WL_Arena *a, WL_Program p, char *err, int errmax) +WL_EvalResult wl_runtime_eval(WL_Runtime *rt) { - WL_State *state = alloc(a, (int) sizeof(WL_State), _Alignof(WL_State)); - if (state == NULL) - return NULL; + if (rt->state != RUNTIME_OUTPUT || rt->cur_output == rt->num_output) { - String code; - String data; + switch (rt->state) { - int ret = parse_program_header(p, &code, &data, err, errmax); - if (ret < 0) - return NULL; + case RUNTIME_BEGIN: + break; - *state = (WL_State) { - .code=code, - .data=data, - .off=0, - .trace=false, - .a=a, - .errbuf=err, - .errmax=errmax, - .errlen=0, - .num_frames=0, - .eval_depth=0, - .num_groups=0, - .num_prints=0, - .cur_print=0, - }; + case RUNTIME_DONE: + return (WL_EvalResult) { .type=WL_EVAL_DONE }; - state->frames[state->num_frames++] = (Frame) { 0, 0 }; + case RUNTIME_ERROR: + return (WL_EvalResult) { .type=WL_EVAL_ERROR }; - return state; -} + case RUNTIME_OUTPUT: + rt->stack -= rt->num_output; + break; -void WL_State_free(WL_State *state) -{ - state->num_frames--; + case RUNTIME_SYSVAR: + { + ASSERT(rt->stack >= rt->stack_before_user); - // TODO -} + int pushed_by_user = rt->stack - rt->stack_before_user; + if (pushed_by_user > 1) { + REPORT(&rt->err, "Invalid API usage"); + rt->state = RUNTIME_ERROR; + return (WL_EvalResult) { .type=WL_EVAL_ERROR }; + } -void WL_State_trace(WL_State *state, int trace) -{ - state->trace = (trace != 0); -} + if (rt->stack == rt->stack_before_user) { + // User didn't push anything on the stack + if (!rt_check_stack(rt, 1)) + return (WL_EvalResult) { .type=WL_EVAL_ERROR }; + rt->values[rt->stack++] = VALUE_NONE; + } -WL_Result WL_eval(WL_State *state) -{ - if (state->sysvar.len > 0) { - - if (state->syscall_error) - return (WL_Result) { WL_ERROR, (WL_String) { NULL, 0 } }; - - state->sysvar = S(""); - } - - if (state->syscall.len > 0) { - - if (state->syscall_error) - return (WL_Result) { WL_ERROR, (WL_String) { NULL, 0 } }; - - int group = state->groups[state->num_groups-1]; - - Value v = state->eval_stack[--state->eval_depth]; - if (type_of(v) != TYPE_INT) { - assert(0); // TODO - } - int64_t num_rets = get_int(v); - for (int i = 0; i < num_rets; i++) - state->eval_stack[group + i] = state->eval_stack[state->eval_depth - num_rets + i]; - - state->eval_depth = group + num_rets; - - state->syscall = S(""); - } - - while (state->num_prints == 0) { - - int ret = step(state); - if (ret < 0) return (WL_Result) { WL_ERROR, (WL_String) { NULL, 0 } }; - if (ret == 1) return (WL_Result) { WL_DONE, (WL_String) { NULL, 0 } }; - - if (state->sysvar.len > 0) - return (WL_Result) { WL_VAR, (WL_String) { state->sysvar.ptr, state->sysvar.len } }; - - if (state->syscall.len > 0) - return (WL_Result) { WL_CALL, (WL_String) { state->syscall.ptr, state->syscall.len } }; - } - - Value v = state->eval_stack[state->eval_depth - state->num_prints + state->cur_print]; - - state->cur_print++; - if (state->cur_print == state->num_prints) { - state->cur_print = 0; - state->num_prints = 0; - } - - WL_String str; - - if (type_of(v) == TYPE_STRING) { - String str2 = get_str(v); - str.ptr = str2.ptr; - str.len = str2.len; - } else { - int cap = 8; - char *dst = alloc(state->a, cap, 1); - int len = value_to_string(v, dst, cap); - if (len > cap) { - if (!grow_alloc(state->a, dst, len)) { - assert(0); // TODO + rt_pop_frame(rt); } - value_to_string(v, dst, len); - } - str.ptr = dst; - str.len = len; - } + break; - return (WL_Result) { WL_OUTPUT, str }; -} + case RUNTIME_SYSCALL: + ASSERT(rt->stack >= rt->stack_before_user); + rt_pop_frame(rt); + break; -static bool in_syscall(WL_State *state) -{ - return (state->syscall.len > 0 || state->sysvar.len > 0) && !state->syscall_error; -} - -int WL_peeknone(WL_State *state, int off) -{ - if (!in_syscall(state)) return 0; - - if (state->eval_depth + off < state->stack_base_for_user || off >= 0) - return 0; - - Value v = state->eval_stack[state->eval_depth + off]; - if (type_of(v) != TYPE_NONE) - return 0; - - return 1; -} - -int WL_peekint(WL_State *state, int off, long long *x) -{ - if (!in_syscall(state)) return 0; - - if (state->eval_depth + off < state->stack_base_for_user || off >= 0) - return 0; - - Value v = state->eval_stack[state->eval_depth + off]; - if (type_of(v) != TYPE_INT) - return 0; - - *x = get_int(v); - return 1; -} - -int WL_peekfloat(WL_State *state, int off, float *x) -{ - if (!in_syscall(state)) return 0; - - if (state->eval_depth + off < state->stack_base_for_user || off >= 0) - return 0; - - Value v = state->eval_stack[state->eval_depth + off]; - if (type_of(v) != TYPE_FLOAT) - return 0; - - *x = get_float(v); - return 1; -} - -int WL_peekstr(WL_State *state, int off, WL_String *str) -{ - if (!in_syscall(state)) return 0; - - if (state->eval_depth + off < state->stack_base_for_user || off >= 0) - return 0; - - Value v = state->eval_stack[state->eval_depth + off]; - if (type_of(v) != TYPE_STRING) - return 0; - - String s = get_str(v); - *str = (WL_String) { s.ptr, s.len }; - return 1; -} - -int WL_popnone(WL_State *state) -{ - if (!in_syscall(state)) return 0; - - if (state->eval_depth == state->stack_base_for_user) - return 0; - - Value v = state->eval_stack[state->eval_depth-1]; - if (type_of(v) != TYPE_NONE) - return 0; - - state->eval_depth--; - return 1; -} - -int WL_popint(WL_State *state, long long *x) -{ - if (!in_syscall(state)) return 0; - - if (state->eval_depth == state->stack_base_for_user) - return 0; - - Value v = state->eval_stack[state->eval_depth-1]; - if (type_of(v) != TYPE_INT) - return 0; - - *x = get_int(v); - - state->eval_depth--; - return 1; -} - -int WL_popfloat(WL_State *state, float *x) -{ - if (!in_syscall(state)) return 0; - - if (state->eval_depth == state->stack_base_for_user) - return 0; - - Value v = state->eval_stack[state->eval_depth-1]; - if (type_of(v) != TYPE_FLOAT) - return 0; - - *x = get_float(v); - - state->eval_depth--; - return 1; -} - -int WL_popstr(WL_State *state, WL_String *str) -{ - if (!in_syscall(state)) return 0; - - if (state->eval_depth == state->stack_base_for_user) - return 0; - - Value v = state->eval_stack[state->eval_depth-1]; - if (type_of(v) != TYPE_STRING) - return 0; - - String s = get_str(v); - *str = (WL_String) { s.ptr, s.len }; - - state->eval_depth--; - return 1; -} - -int WL_popany(WL_State *state) -{ - if (!in_syscall(state)) - return 0; - - if (state->eval_depth == state->stack_base_for_user) - return 0; - - state->eval_depth--; - return 1; -} - -void WL_select(WL_State *state) -{ - Value key = state->eval_stack[--state->eval_depth]; - Value set = state->eval_stack[state->eval_depth-1]; - Value val; - - Type set_type = type_of(set); - if (set_type == TYPE_ARRAY) { - - Type key_type = type_of(key); - if (key_type != TYPE_INT) { - assert(0); // TODO + default: + UNREACHABLE; } - int64_t idx = get_int(key); - Value *src = array_select(set, idx); - if (src == NULL) { - assert(0); // TODO - } - val = *src; + rt->state = RUNTIME_LOOP; - } else if (set_type == TYPE_MAP) { + do { - int ret = map_select(set, key, &val); - if (ret < 0) { - assert(0); // TODO + step(rt); + + if (rt->err.yes) + rt->state = RUNTIME_ERROR; + + } while (rt->state == RUNTIME_LOOP); + + } + + switch (rt->state) { + + case RUNTIME_LOOP: + UNREACHABLE; + + case RUNTIME_DONE: + break; + + case RUNTIME_ERROR: + return (WL_EvalResult) { .type=WL_EVAL_ERROR }; + + case RUNTIME_OUTPUT: + { + ASSERT(rt->cur_output < rt->num_output); + + Value v = rt->values[rt->stack - rt->num_output + rt->cur_output]; + Type type = value_type(v); + + String str; + if (type == TYPE_STRING) + str = value_to_str(v); + else { + int len = value_convert_to_str(v, rt->buf, SIZEOF(rt->buf)); + if (len > SIZEOF(rt->buf)) { + char *p = alloc(rt->arena, len, 1); + if (p == NULL) { + REPORT(&rt->err, "Out of memory"); + rt->state = RUNTIME_ERROR; + return (WL_EvalResult) { .type=WL_EVAL_ERROR }; + } + len = value_convert_to_str(v, p, len); + str = (String) { p, len }; + } else { + str = (String) { rt->buf, len }; + } + } + + rt->cur_output++; + return (WL_EvalResult) { .type=WL_EVAL_OUTPUT, .str={ str.ptr, str.len } }; } - } else { + case RUNTIME_SYSVAR: + return (WL_EvalResult) { .type=WL_EVAL_SYSVAR, .str=(WL_String) { rt->str_for_user.ptr, rt->str_for_user.len } }; - assert(0); // TODO + case RUNTIME_SYSCALL: + return (WL_EvalResult) { .type=WL_EVAL_SYSCALL, .str=(WL_String) { rt->str_for_user.ptr, rt->str_for_user.len } }; } - state->eval_stack[state->eval_depth++] = val; + return (WL_EvalResult) { .type=WL_EVAL_DONE }; } -void WL_pushnone(WL_State *state) -{ - if (!in_syscall(state)) return; - - state->eval_stack[state->eval_depth++] = VALUE_NONE; -} - -void WL_pushint(WL_State *state, long long x) -{ - if (!in_syscall(state)) return; - - Value v = make_int(state->a, x); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - state->syscall_error = true; - return; - } - - state->eval_stack[state->eval_depth++] = v; -} - -void WL_pushfloat(WL_State *state, float x) -{ - if (!in_syscall(state)) return; - - Value v = make_float(state->a, x); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - state->syscall_error = true; - return; - } - - state->eval_stack[state->eval_depth++] = v; -} - -void WL_pushstr(WL_State *state, WL_String str) -{ - if (!in_syscall(state)) return; - - Value v = make_str(state->a, (String) { str.ptr, str.len }); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - state->syscall_error = true; - return; - } - - state->eval_stack[state->eval_depth++] = v; -} - -void WL_pusharray(WL_State *state, int cap) -{ - if (!in_syscall(state)) return; - - (void) cap; - Value v = make_array(state->a); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - state->syscall_error = true; - return; - } - - state->eval_stack[state->eval_depth++] = v; -} - -void WL_pushmap(WL_State *state, int cap) -{ - if (!in_syscall(state)) return; - - (void) cap; - Value v = make_map(state->a); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - state->syscall_error = true; - return; - } - - state->eval_stack[state->eval_depth++] = v; -} - -void WL_insert(WL_State *state) -{ - Value key = state->eval_stack[--state->eval_depth]; - Value val = state->eval_stack[--state->eval_depth]; - Value set = state->eval_stack[state->eval_depth-1]; - - Type set_type = type_of(set); - if (set_type == TYPE_ARRAY) { - - Type key_type = type_of(key); - if (key_type != TYPE_INT) { - assert(0); // TODO - } - - int64_t idx = get_int(key); - Value *dst = array_select(set, idx); - if (dst == NULL) { - assert(0); // TODO - } - *dst = val; - - } else if (set_type == TYPE_MAP) { - - int ret = map_insert(state->a, set, key, val); - if (ret < 0) { - assert(0); // TODO - } - - } else { - - assert(0); // TODO - } -} - -void WL_append(WL_State *state) -{ - Value val = state->eval_stack[--state->eval_depth]; - Value set = state->eval_stack[state->eval_depth-1]; - - if (type_of(set) != TYPE_ARRAY) { - assert(0); // TODO - return; - } - - if (array_append(state->a, set, val) < 0) { - assert(0); // TODO - } -} - -//////////////////////////////////////////////////////////////////////////////////////// -// src/compile.c -//////////////////////////////////////////////////////////////////////////////////////// - - -#ifndef WL_AMALGAMATION -#include "eval.h" -#include "parse.h" -#include "assemble.h" -#include "compile.h" -#endif - -#define FILE_LIMIT 32 - -typedef struct { - String file; - Node* root; - Node* includes; -} CompiledFile; - -struct WL_Compiler { - WL_Arena* arena; - CompiledFile files[FILE_LIMIT]; - int num_files; - String waiting_file; -}; - -int WL_streq(WL_String a, char *b, int blen) +bool wl_streq(WL_String a, char *b, int blen) { if (b == NULL) b = ""; if (blen < 0) blen = strlen(b); - - if (a.len != blen) - return 0; - - for (int i = 0; i < a.len; i++) - if (a.ptr[i] != b[i]) - return 0; - - return 1; + return streq((String) { a.ptr, a.len }, (String) { b, blen }); } -WL_Compiler *WL_Compiler_init(WL_Arena *arena) +int wl_arg_count(WL_Runtime *rt) { - WL_Compiler *compiler = alloc(arena, (int) sizeof(WL_Compiler), _Alignof(WL_Compiler)); - if (compiler == NULL) - return NULL; - compiler->arena = arena; - compiler->num_files = 0; - compiler->waiting_file = (String) { NULL, 0 }; - return compiler; + if (rt->state != RUNTIME_SYSVAR && + rt->state != RUNTIME_SYSCALL) + return -1; + + ASSERT(rt->num_frames > 0); + return rt->frames[rt->num_frames-1].varbase - rt->vars; // TODO: is this right? } -void WL_Compiler_free(WL_Compiler *compiler) +static Value user_arg(WL_Runtime *rt, int idx, Type type) { - (void) compiler; - // TODO + if (rt->state != RUNTIME_SYSVAR && + rt->state != RUNTIME_SYSCALL) + return -1; + + int tot = wl_arg_count(rt); + if (idx < 0 || idx >= tot) + return false; + + Value v = *rt_variable(rt, tot - idx - 1); + if (value_type(v) != type) + return VALUE_ERROR; + + return v; } -WL_CompileResult WL_compile(WL_Compiler *compiler, WL_String file, WL_String content) +bool wl_arg_none(WL_Runtime *rt, int idx) { - if (compiler->waiting_file.len > 0) - file = (WL_String) { compiler->waiting_file.ptr, compiler->waiting_file.len }; - else { - // TODO: copy file path - // file = strdup(file, compiler->arena) + Value v = user_arg(rt, idx, TYPE_NONE); + if (v == VALUE_ERROR) + return false; + return true; +} + +bool wl_arg_bool(WL_Runtime *rt, int idx, bool *x) +{ + Value v = user_arg(rt, idx, TYPE_BOOL); + if (v == VALUE_ERROR) + return false; + *x = (v == VALUE_TRUE); + return true; +} + +bool wl_arg_s64(WL_Runtime *rt, int idx, int64_t *x) +{ + Value v = user_arg(rt, idx, TYPE_INT); + if (v == VALUE_ERROR) + return false; + *x = value_to_s64(v); + return true; +} + +bool wl_arg_f64(WL_Runtime *rt, int idx, double *x) +{ + Value v = user_arg(rt, idx, TYPE_FLOAT); + if (v == VALUE_ERROR) + return false; + *x = value_to_f64(v); + return true; +} + +bool wl_arg_str(WL_Runtime *rt, int idx, WL_String *x) +{ + Value v = user_arg(rt, idx, TYPE_STRING); + if (v == VALUE_ERROR) + return false; + String s = value_to_str(v); + *x = (WL_String) { s.ptr, s.len }; + return true; +} + +bool wl_arg_array(WL_Runtime *rt, int idx) +{ + Value v = user_arg(rt, idx, TYPE_ARRAY); + if (v == VALUE_ERROR) + return false; + return true; +} + +bool wl_arg_map(WL_Runtime *rt, int idx) +{ + Value v = user_arg(rt, idx, TYPE_MAP); + if (v == VALUE_ERROR) + return false; + return true; +} + +static Value user_peek(WL_Runtime *rt, int off, Type type) +{ + if (rt->state != RUNTIME_SYSVAR && + rt->state != RUNTIME_SYSCALL) + return VALUE_ERROR; + + if (rt->stack + off < rt->stack_before_user || off >= 0) + return VALUE_ERROR; + + Value v = rt->values[rt->stack + off]; + if (value_type(v) != type) + return VALUE_ERROR; + + return v; +} + +bool wl_peek_none(WL_Runtime *rt, int off) +{ + Value v = user_peek(rt, off, TYPE_NONE); + if (v == VALUE_ERROR) + return false; + return true; +} + +bool wl_peek_bool(WL_Runtime *rt, int off, bool *x) +{ + Value v = user_peek(rt, off, TYPE_BOOL); + if (v == VALUE_ERROR) + return false; + *x = (v == VALUE_TRUE); + return true; +} + +bool wl_peek_s64(WL_Runtime *rt, int off, int64_t *x) +{ + Value v = user_peek(rt, off, TYPE_INT); + if (v == VALUE_ERROR) + return false; + *x = value_to_s64(v); + return true; +} + +bool wl_peek_f64(WL_Runtime *rt, int off, double *x) +{ + Value v = user_peek(rt, off, TYPE_FLOAT); + if (v == VALUE_ERROR) + return false; + *x = value_to_f64(v); + return true; +} + +bool wl_peek_str(WL_Runtime *rt, int off, WL_String *x) +{ + Value v = user_peek(rt, off, TYPE_STRING); + if (v == VALUE_ERROR) + return false; + String s = value_to_str(v); + *x = (WL_String) { s.ptr, s.len }; + return true; +} + +bool wl_pop_any(WL_Runtime *rt) +{ + if (rt->state != RUNTIME_SYSVAR && + rt->state != RUNTIME_SYSCALL) + return VALUE_ERROR; + + if (rt->stack == rt->stack_before_user) + return false; + + ASSERT(rt->stack > 0); + rt->stack--; + return true; +} + +static Value user_pop(WL_Runtime *rt, Type type) +{ + if (rt->state != RUNTIME_SYSVAR && + rt->state != RUNTIME_SYSCALL) + return VALUE_ERROR; + + if (rt->stack == rt->stack_before_user) + return VALUE_ERROR; + + ASSERT(rt->stack > 0); + Value v = rt->values[rt->stack-1]; + if (value_type(v) != type) + return VALUE_ERROR; + + rt->stack--; + return v; +} + +bool wl_pop_none(WL_Runtime *rt) +{ + Value v = user_pop(rt, TYPE_NONE); + if (v == VALUE_ERROR) + return false; + return true; +} + +bool wl_pop_bool(WL_Runtime *rt, bool *x) +{ + Value v = user_pop(rt, TYPE_BOOL); + if (v == VALUE_ERROR) + return false; + *x = (v == VALUE_TRUE); + return true; +} + +bool wl_pop_s64(WL_Runtime *rt, int64_t *x) +{ + Value v = user_pop(rt, TYPE_INT); + if (v == VALUE_ERROR) + return false; + *x = value_to_s64(v); + return true; +} + +bool wl_pop_f64(WL_Runtime *rt, double *x) +{ + Value v = user_pop(rt, TYPE_FLOAT); + if (v == VALUE_ERROR) + return false; + *x = value_to_f64(v); + return true; +} + +bool wl_pop_str(WL_Runtime *rt, WL_String *x) +{ + Value v = user_pop(rt, TYPE_STRING); + if (v == VALUE_ERROR) + return false; + String s = value_to_str(v); + *x = (WL_String) { s.ptr, s.len }; + return true; +} + +void wl_push_none(WL_Runtime *rt) +{ + if (rt->state != RUNTIME_SYSVAR && + rt->state != RUNTIME_SYSCALL) + return; + + if (!rt_check_stack(rt, 1)) + return; + + rt->values[rt->stack++] = VALUE_NONE; +} + +void wl_push_true(WL_Runtime *rt) +{ + if (rt->state != RUNTIME_SYSVAR && + rt->state != RUNTIME_SYSCALL) + return; + + if (!rt_check_stack(rt, 1)) + return; + + rt->values[rt->stack++] = VALUE_TRUE; +} + +void wl_push_false(WL_Runtime *rt) +{ + if (rt->state != RUNTIME_SYSVAR && + rt->state != RUNTIME_SYSCALL) + return; + + if (!rt_check_stack(rt, 1)) + return; + + rt->values[rt->stack++] = VALUE_FALSE; +} + +void wl_push_s64(WL_Runtime *rt, int64_t x) +{ + if (rt->state != RUNTIME_SYSVAR && + rt->state != RUNTIME_SYSCALL) + return; + + if (!rt_check_stack(rt, 1)) + return; + + Value v = value_from_s64(x, rt->arena, &rt->err); + if (v == VALUE_ERROR) { + rt->state = RUNTIME_ERROR; + return; } - char err[1<<9]; - ParseResult pres = parse((String) { content.ptr, content.len }, compiler->arena, err, (int) sizeof(err)); - if (pres.node == NULL) { - printf("%s\n", err); // TODO - return (WL_CompileResult) { .type=WL_COMPILE_RESULT_ERROR }; + rt->values[rt->stack++] = v; +} + +void wl_push_f64(WL_Runtime *rt, double x) +{ + if (rt->state != RUNTIME_SYSVAR && + rt->state != RUNTIME_SYSCALL) + return; + + if (!rt_check_stack(rt, 1)) + return; + + Value v = value_from_f64(x, rt->arena, &rt->err); + if (v == VALUE_ERROR) { + rt->state = RUNTIME_ERROR; + return; } - CompiledFile compiled_file = { - .file = { file.ptr, file.len }, - .root = pres.node, - .includes = pres.includes, - }; - compiler->files[compiler->num_files++] = compiled_file; + rt->values[rt->stack++] = v; +} - for (int i = 0; i < compiler->num_files; i++) { +void wl_push_str(WL_Runtime *rt, WL_String x) +{ + if (rt->state != RUNTIME_SYSVAR && + rt->state != RUNTIME_SYSCALL) + return; - Node *include = compiler->files[i].includes; - while (include) { + if (!rt_check_stack(rt, 1)) + return; - assert(include->type == NODE_INCLUDE); + Value v = value_from_str((String) { x.ptr, x.len }, rt->arena, &rt->err); + if (v == VALUE_ERROR) { + rt->state = RUNTIME_ERROR; + return; + } - if (include->include_root == NULL) { - for (int j = 0; j < compiler->num_files; j++) { - if (streq(include->include_path, compiler->files[j].file)) { - include->include_root = compiler->files[j].root; - break; - } - } - } + rt->values[rt->stack++] = v; +} - if (include->include_root == NULL) { +void wl_push_array(WL_Runtime *rt, int cap) +{ + if (rt->state != RUNTIME_SYSVAR && + rt->state != RUNTIME_SYSCALL) + return; - if (compiler->num_files == FILE_LIMIT) { - assert(0); // TODO - } + if (!rt_check_stack(rt, 1)) + return; - // TODO: Make the path relative to the compiled file + Value v = value_empty_array(cap, rt->arena, &rt->err); + if (v == VALUE_ERROR) { + rt->state = RUNTIME_ERROR; + return; + } - compiler->waiting_file = include->include_path; - return (WL_CompileResult) { .type=WL_COMPILE_RESULT_FILE, .path={ include->include_path.ptr, include->include_path.len } }; - } + rt->values[rt->stack++] = v; +} - include = include->include_next; +void wl_push_map(WL_Runtime *rt, int cap) +{ + if (rt->state != RUNTIME_SYSVAR && + rt->state != RUNTIME_SYSCALL) + return; + + if (!rt_check_stack(rt, 1)) + return; + + Value v = value_empty_map(cap, rt->arena, &rt->err); + if (v == VALUE_ERROR) { + rt->state = RUNTIME_ERROR; + return; + } + + rt->values[rt->stack++] = v; +} + +void wl_push_arg(WL_Runtime *rt, int idx) +{ + if (rt->state != RUNTIME_SYSVAR && + rt->state != RUNTIME_SYSCALL) + return; + + if (!rt_check_stack(rt, 1)) + return; + + int tot = wl_arg_count(rt); + if (idx < 0 || idx >= tot) { + REPORT(&rt->err, "Invalid API usagge"); + rt->state = RUNTIME_ERROR; + return; + } + + rt->values[rt->stack++] = *rt_variable(rt, tot - idx - 1); +} + +void wl_insert(WL_Runtime *rt) +{ + if (rt->state != RUNTIME_SYSVAR && + rt->state != RUNTIME_SYSCALL) + return; + + if (rt->stack - rt->stack_before_user < 3) { + REPORT(&rt->err, "Invalid API usagge"); + rt->state = RUNTIME_ERROR; + return; + } + + Value key = rt->values[--rt->stack]; + Value val = rt->values[--rt->stack]; + Value set = rt->values[rt->stack-1]; + + if (!value_insert(set, key, val, rt->arena, &rt->err)) { + rt->state = RUNTIME_ERROR; + return; + } +} + +void wl_append(WL_Runtime *rt) +{ + if (rt->state != RUNTIME_SYSVAR && + rt->state != RUNTIME_SYSCALL) + return; + + if (rt->stack - rt->stack_before_user < 2) { + REPORT(&rt->err, "Invalid API usagge"); + rt->state = RUNTIME_ERROR; + return; + } + + Value val = rt->values[--rt->stack]; + Value set = rt->values[rt->stack-1]; + + if (!value_append(set, val, rt->arena, &rt->err)) { + rt->state = RUNTIME_ERROR; + return; + } +} + +void wl_runtime_dump(WL_Runtime *rt) +{ + for (int i = 0; i < rt->num_frames; i++) { + printf("=== frame %d ===\n", i); + + Frame *frame = &rt->frames[i]; + + int num_vars; + if (i+1 < rt->num_frames) + num_vars = frame->varbase - rt->frames[i+1].varbase; + else + num_vars = frame->varbase - rt->vars; + + for (int j = 0; j < num_vars; j++) { + printf(" %d = ", j); + value_print(rt->values[frame->varbase - j]); } } - - AssembleResult ares = assemble(compiler->files[0].root, compiler->arena, err, (int) sizeof(err)); - if (ares.errlen) { - printf("%s\n", err); // TODO - return (WL_CompileResult) { .type=WL_COMPILE_RESULT_ERROR }; - } - - return (WL_CompileResult) { .type=WL_COMPILE_RESULT_DONE, .program=ares.program }; -} - -void WL_dump_program(WL_Program program) -{ - print_program(program); + printf("===============\n"); } diff --git a/WL.h b/WL.h index f0118bb..d2657eb 100644 --- a/WL.h +++ b/WL.h @@ -1,20 +1,7 @@ -#ifndef WL_AMALGAMATION -#define WL_AMALGAMATION +#include -// This file was generated automatically. Do not modify directly! - -//////////////////////////////////////////////////////////////////////////////////////// -// src/compile.h -//////////////////////////////////////////////////////////////////////////////////////// - -#ifndef WL_PUBLIC_INCLUDED -#define WL_PUBLIC_INCLUDED - -#ifndef WL_AMALGAMATION -#include "includes.h" -#endif - -typedef struct WL_State WL_State; +typedef struct WL_Runtime WL_Runtime; +typedef struct WL_Compiler WL_Compiler; typedef struct { char *ptr; @@ -24,70 +11,79 @@ typedef struct { typedef struct { char *ptr; int len; -} WL_Program; - -typedef enum { - WL_DONE, - WL_ERROR, - WL_VAR, - WL_CALL, - WL_OUTPUT, -} WL_ResultType; - -typedef struct { - WL_ResultType type; - WL_String str; -} WL_Result; + int cur; +} WL_Arena; typedef struct { char *ptr; int len; - int cur; -} WL_Arena; - -typedef struct WL_Compiler WL_Compiler; +} WL_Program; typedef enum { - WL_COMPILE_RESULT_DONE, - WL_COMPILE_RESULT_FILE, - WL_COMPILE_RESULT_ERROR, -} WL_CompileResultType; + WL_ADD_ERROR, + WL_ADD_AGAIN, + WL_ADD_LINK, +} WL_AddResultType; typedef struct { - WL_CompileResultType type; - WL_Program program; - WL_String path; -} WL_CompileResult; + WL_AddResultType type; + WL_String path; +} WL_AddResult; -WL_Compiler* WL_Compiler_init (WL_Arena *arena); -void WL_Compiler_free (WL_Compiler *compiler); -WL_CompileResult WL_compile (WL_Compiler *compiler, WL_String file, WL_String content); -WL_State* WL_State_init (WL_Arena *a, WL_Program p, char *err, int errmax); -void WL_State_free (WL_State *state); -void WL_State_trace (WL_State *state, int trace); -WL_Result WL_eval (WL_State *state); +typedef enum { + WL_EVAL_NONE, + WL_EVAL_DONE, + WL_EVAL_ERROR, + WL_EVAL_OUTPUT, + WL_EVAL_SYSVAR, + WL_EVAL_SYSCALL, +} WL_EvalResultType; -void WL_dump_program(WL_Program program); +typedef struct { + WL_EvalResultType type; + WL_String str; +} WL_EvalResult; -int WL_streq (WL_String a, char *b, int blen); -int WL_peeknone (WL_State *state, int off); -int WL_peekint (WL_State *state, int off, long long *x); -int WL_peekfloat (WL_State *state, int off, float *x); -int WL_peekstr (WL_State *state, int off, WL_String *str); -int WL_popnone (WL_State *state); -int WL_popint (WL_State *state, long long *x); -int WL_popfloat (WL_State *state, float *x); -int WL_popstr (WL_State *state, WL_String *str); -int WL_popany (WL_State *state); -void WL_select (WL_State *state); -void WL_pushnone (WL_State *state); -void WL_pushint (WL_State *state, long long x); -void WL_pushfloat (WL_State *state, float x); -void WL_pushstr (WL_State *state, WL_String str); -void WL_pusharray (WL_State *state, int cap); -void WL_pushmap (WL_State *state, int cap); -void WL_insert (WL_State *state); -void WL_append (WL_State *state); +WL_Compiler* wl_compiler_init (WL_Arena *arena); +WL_AddResult wl_compiler_add (WL_Compiler *compiler, WL_String content); +int wl_compiler_link (WL_Compiler *compiler, WL_Program *program); +WL_String wl_compiler_error (WL_Compiler *compiler); +int wl_dump_ast (WL_Compiler *compiler, char *dst, int cap); +void wl_dump_program (WL_Program program); -#endif // WL_PUBLIC_INCLUDED -#endif // WL_AMALGAMATION +WL_Runtime* wl_runtime_init (WL_Arena *arena, WL_Program program); +WL_EvalResult wl_runtime_eval (WL_Runtime *rt); +WL_String wl_runtime_error (WL_Runtime *rt); +void wl_runtime_dump (WL_Runtime *rt); + +bool wl_streq (WL_String a, char *b, int blen); +int wl_arg_count (WL_Runtime *rt); +bool wl_arg_none (WL_Runtime *rt, int idx); +bool wl_arg_bool (WL_Runtime *rt, int idx, bool *x); +bool wl_arg_s64 (WL_Runtime *rt, int idx, int64_t *x); +bool wl_arg_f64 (WL_Runtime *rt, int idx, double *x); +bool wl_arg_str (WL_Runtime *rt, int idx, WL_String *x); +bool wl_arg_array (WL_Runtime *rt, int idx); +bool wl_arg_map (WL_Runtime *rt, int idx); +bool wl_peek_none (WL_Runtime *rt, int off); +bool wl_peek_bool (WL_Runtime *rt, int off, bool *x); +bool wl_peek_s64 (WL_Runtime *rt, int off, int64_t *x); +bool wl_peek_f64 (WL_Runtime *rt, int off, double *x); +bool wl_peek_str (WL_Runtime *rt, int off, WL_String *x); +bool wl_pop_any (WL_Runtime *rt); +bool wl_pop_none (WL_Runtime *rt); +bool wl_pop_bool (WL_Runtime *rt, bool *x); +bool wl_pop_s64 (WL_Runtime *rt, int64_t *x); +bool wl_pop_f64 (WL_Runtime *rt, double *x); +bool wl_pop_str (WL_Runtime *rt, WL_String *x); +void wl_push_none (WL_Runtime *rt); +void wl_push_true (WL_Runtime *rt); +void wl_push_false (WL_Runtime *rt); +void wl_push_s64 (WL_Runtime *rt, int64_t x); +void wl_push_f64 (WL_Runtime *rt, double x); +void wl_push_str (WL_Runtime *rt, WL_String x); +void wl_push_array (WL_Runtime *rt, int cap); +void wl_push_map (WL_Runtime *rt, int cap); +void wl_push_arg (WL_Runtime *rt, int idx); +void wl_insert (WL_Runtime *rt); +void wl_append (WL_Runtime *rt); diff --git a/amalg.py b/amalg.py deleted file mode 100644 index 8fc8426..0000000 --- a/amalg.py +++ /dev/null @@ -1,53 +0,0 @@ - -class Amalgamator: - def __init__(self): - self.out = "" - - def append_text(self, text): - self.out += text - - def append_file(self, file): - - self.out += "\n" - self.out += "////////////////////////////////////////////////////////////////////////////////////////\n" - self.out += "// " + file + "\n" - self.out += "////////////////////////////////////////////////////////////////////////////////////////\n" - self.out += "\n" - # self.out += "#line 1 \"" + file + "\"\n" - self.out += open(file).read() - - if len(self.out) > 0 and self.out[len(self.out)-1] != '\n': - self.out += "\n" - - def save(self, file): - open(file, 'w').write(self.out) - -desc = """ -// This file was generated automatically. Do not modify directly! -""" - -header = Amalgamator() -header.append_text("#ifndef WL_AMALGAMATION\n") -header.append_text("#define WL_AMALGAMATION\n") -header.append_text(desc) -header.append_file("src/compile.h") -header.append_text("#endif // WL_AMALGAMATION\n") -header.save("WL.h") - -source = Amalgamator() -source.append_text("#include \"WL.h\"\n") -source.append_file("src/includes.h") -source.append_file("src/basic.h") -source.append_file("src/basic.c") -source.append_file("src/file.h") -source.append_file("src/file.c") -source.append_file("src/parse.h") -source.append_file("src/parse.c") -source.append_file("src/assemble.h") -source.append_file("src/assemble.c") -source.append_file("src/value.h") -source.append_file("src/value.c") -source.append_file("src/eval.h") -source.append_file("src/eval.c") -source.append_file("src/compile.c") -source.save("WL.c") diff --git a/example.wl b/example.wl deleted file mode 100644 index 6efd7ce..0000000 --- a/example.wl +++ /dev/null @@ -1 +0,0 @@ -let ugu = 78 \ No newline at end of file diff --git a/examples/000_comment.wl b/examples/000_comment.wl deleted file mode 100644 index 279ca5e..0000000 --- a/examples/000_comment.wl +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/examples/001_basic_html.wl b/examples/001_basic_html.wl deleted file mode 100644 index 779b798..0000000 --- a/examples/001_basic_html.wl +++ /dev/null @@ -1,45 +0,0 @@ - - -I'm a link - - - -let name = "cozis" - -

Hello from \name

- - - -let valueA = "Some value" - -

- - - -let link = Click me - - - -link - -

You should click this link: \link

diff --git a/examples/002_ifelse_and_while.wl b/examples/002_ifelse_and_while.wl deleted file mode 100644 index 6c02faa..0000000 --- a/examples/002_ifelse_and_while.wl +++ /dev/null @@ -1,58 +0,0 @@ - - -let a = 2 - -if a == 1: { - The condition is true -} else { - It is false -} - - - -

- \if a == 1: { - The condition is true - } else { - It is false - } -

- - - -let i = 0 -while i < 3: { - I'm link number \i + 1 - i = i + 1 -} - - - -i = 0 -
    - \while i < 3: { -
  • I'm link number \i + 1
  • - i = i + 1 - } -
\ No newline at end of file diff --git a/examples/003_for_arrays_and_maps.wl b/examples/003_for_arrays_and_maps.wl deleted file mode 100644 index 3f5cc62..0000000 --- a/examples/003_for_arrays_and_maps.wl +++ /dev/null @@ -1,83 +0,0 @@ - - - -let my_var = [1, 2, 3] - -my_var - - - -let my_map = { 1: "First", 2: "Second", 3: "Third" } - -my_map[2] - - - -let person = { "name": "Cozis" } -let person_no_quotes = { name: "Cozis" } - - - -for key in { A: 1, B: 2, C: 3 }: { - key -} - -for key, i in { A: 1, B: 2, C: 3 }: { - -} - - - -for val in [5, 3, 7]: - val - -for val, i in [5, 3, 7]: - i - - - -let links = ["http://github.com", "http://reddit.com"] - - diff --git a/examples/004_functions.wl b/examples/004_functions.wl deleted file mode 100644 index f605abe..0000000 --- a/examples/004_functions.wl +++ /dev/null @@ -1,26 +0,0 @@ - - - -fun say_hello(name) { - print "Hello to " - print name -} - -say_hello("cozis") - - - -fun say_hello_2(name) - Hello, \name! - -say_hello_2("cozis") diff --git a/file.wl b/file.wl new file mode 100644 index 0000000..94ba872 --- /dev/null +++ b/file.wl @@ -0,0 +1,6 @@ + +let a = [] + +a << 1 << 2 + +a \ No newline at end of file diff --git a/main.c b/main.c index 79d5571..7c6182e 100644 --- a/main.c +++ b/main.c @@ -1,121 +1,189 @@ #include #include -#include #include +#include +#include #include +#include "wl.h" -#include "WL.h" +typedef struct FileData FileData; + +struct FileData { + FileData *next; + int size; + char data[]; +}; + +FileData *load_file(WL_String path) +{ + char buf[1<<10]; + if (path.len >= (int) sizeof(buf)) + return NULL; + memcpy(buf, path.ptr, path.len); + buf[path.len] = '\0'; + + FILE *f = fopen(buf, "rb"); + if (f == NULL) + return NULL; + + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + + FileData *data = malloc(sizeof(FileData) + size); + if (data == NULL) { + fclose(f); + return NULL; + } + data->size = size; + data->next = NULL; + + fread(data->data, 1, size, f); + fclose(f); + return data; +} int main(int argc, char **argv) { - if (argc < 2) { - printf("Missing file path\n"); + char *entry_file = NULL; + + bool bc = false; + bool ast = false; + bool run = true; + for (int i = 1; i < argc; i++) { + if (!strcmp(argv[i], "--bc")) + bc = true; + else if (!strcmp(argv[i], "--ast")) + ast = true; + else if (!strcmp(argv[i], "--no-run")) + run = false; + else + entry_file = argv[i]; + } + + if (entry_file == NULL) { + fprintf(stderr, "Usage: %s file.wl\n", argv[0]); return -1; } - char *file = argv[1]; - char err[1<<9]; - char *mem = malloc(1<<20); - WL_Arena a = { mem, 1<<20, 0 }; - - WL_Compiler *compiler = WL_Compiler_init(&a); - if (compiler == NULL) { - assert(0); // TODO + int cap = 1<<20; + char *mem = malloc(cap); + if (mem == NULL) { + fprintf(stderr, "Error: Allocation failure\n"); + return -1; } + WL_Arena arena = { mem, cap, 0 }; - int num_loaded_files = 0; - char *loaded_files[128]; - - WL_CompileResult result; - WL_String path = { file, strlen(file) }; - for (int i = 0;; i++) { - - char buf[1<<10]; - if (path.len >= (int) sizeof(buf)) { - assert(0); // TODO - } - memcpy(buf, path.ptr, path.len); - buf[path.len] = '\0'; - - FILE *f = fopen(buf, "rb"); - if (f == NULL) { - printf("File not found '%.*s'\n", path.len, path.ptr); + WL_Program program; + { + WL_Compiler *c = wl_compiler_init(&arena); + if (c == NULL) { + fprintf(stderr, "Error: Out of memory"); return -1; } - fseek(f, 0, SEEK_END); - long file_size = ftell(f); - fseek(f, 0, SEEK_SET); + FileData *file_head; + FileData **file_tail = &file_head; - char *file_data = malloc(file_size); + WL_String path = { entry_file, strlen(entry_file) }; + for (;;) { - fread(file_data, 1, file_size, f); - fclose(f); + FileData *file = load_file(path); + if (file == NULL) { + printf("Couldn't open '%.*s'\n", path.len, path.ptr); + return -1; + } + *file_tail = file; + file_tail = &file->next; - result = WL_compile(compiler, path, (WL_String) { file_data, file_size }); - - loaded_files[num_loaded_files++] = file_data; - - if (result.type == WL_COMPILE_RESULT_ERROR) { - printf("Compilation of '%.*s' failed\n", path.len, path.ptr); + WL_AddResult res = wl_compiler_add(c, (WL_String) { file->data, file->size }); + if (res.type == WL_ADD_ERROR) { + fprintf(stderr, "Error: %s\n", wl_compiler_error(c).ptr); + return -1; + } + if (res.type == WL_ADD_AGAIN) { + path = res.path; + continue; + } + assert(res.type == WL_ADD_LINK); break; } - if (result.type == WL_COMPILE_RESULT_DONE) - break; + *file_tail = NULL; - assert(result.type == WL_COMPILE_RESULT_FILE); - path = result.path; - } + if (ast) { + char buf[1<<10]; + int len = wl_dump_ast(c, buf, sizeof(buf)); + if (len > sizeof(buf)-1) + len = sizeof(buf)-1; + buf[len] = '\0'; - for (int i = 0; i < num_loaded_files; i++) - free(loaded_files[i]); + printf("%s\n", buf); + } - WL_Compiler_free(compiler); + int ret = wl_compiler_link(c, &program); + if (ret < 0) { + WL_String err = wl_compiler_error(c); + fprintf(stderr, "Error: %s\n", err.ptr); + return -1; + } - if (result.type == WL_COMPILE_RESULT_ERROR) { - printf("Compilation error\n"); - return -1; - } - WL_Program program = result.program; + if (bc) + wl_dump_program(program); - WL_State *state = WL_State_init(&a, program, err, (int) sizeof(err)); - - WL_State_trace(state, 0); - - for (bool done = false; !done; ) { - - WL_Result result = WL_eval(state); - switch (result.type) { - - case WL_DONE: - done = true; - break; - - case WL_ERROR: - done = true; - printf("%s\n", err); - break; - - case WL_VAR: - if (0) {} - else if (WL_streq(result.str, "varA", -1)) WL_pushint(state, 1); - else if (WL_streq(result.str, "varB", -1)) WL_pushint(state, 1); - else if (WL_streq(result.str, "varC", -1)) WL_pushint(state, 1); - break; - - case WL_CALL: - // TODO - printf("(called [%.*s])\n", result.str.len, result.str.ptr); - break; - - case WL_OUTPUT: - fwrite(result.str.ptr, 1, result.str.len, stdout); - break; + FileData *file = file_head; + while (file) { + FileData *next = file->next; + free(file); + file = next; + } + } + + if (run) { + WL_Runtime *rt = wl_runtime_init(&arena, program); + if (rt == NULL) { + fprintf(stderr, "Error: Invalid program or out of memory\n"); + return -1; + } + + FILE *output = stdout; + for (bool done = false; !done; ) { + WL_EvalResult res = wl_runtime_eval(rt); + + wl_runtime_dump(rt); + + switch (res.type) { + + case WL_EVAL_NONE: + break; + + case WL_EVAL_DONE: + done = true; + break; + + case WL_EVAL_ERROR: + printf("Error: %s\n", wl_runtime_error(rt).ptr); + return -1; + + case WL_EVAL_OUTPUT: + fwrite(res.str.ptr, 1, res.str.len, output); + break; + + case WL_EVAL_SYSVAR: + if (wl_streq(res.str, "varA", -1)) wl_push_s64(rt, 1); + if (wl_streq(res.str, "varB", -1)) wl_push_s64(rt, 7); + if (wl_streq(res.str, "varC", -1)) wl_push_s64(rt, 13); + break; + + case WL_EVAL_SYSCALL: + if (wl_streq(res.str, "testfn", -1)) { + for (int i = 0; i < wl_arg_count(rt); i++) + wl_push_arg(rt, i); + } + break; + } } } - WL_State_free(state); - free(mem); return 0; -} +} \ No newline at end of file diff --git a/main.wl b/main.wl deleted file mode 100644 index 10b3fd8..0000000 --- a/main.wl +++ /dev/null @@ -1,57 +0,0 @@ -include "example.wl" - -ugu - -let posts = [ - { - author: "UserA", - title: "I'm the first post", - content: "Sup everyone!", - date: "1 Jan 2025", - comments: [ - { - author: "UserB", - content: "Hello! This is a comment!", - comments: [ - { - author: "UserA", - content: "Hello to you!", - comments: [] - } - ] - } - ] - } -] - -fun comment(c) -
- \c.author -

\c.content

- \for subc in c.comments: - comment(subc) -
- - - - List of posts - - - \for post in posts: -
-

- \post.title -

- - Posted on \post.date by \post.author - -

- \post.content -

-
- \for c in post.comments: - comment(c) -
-
- - \ No newline at end of file diff --git a/manual.md b/manual.md new file mode 100644 index 0000000..2c6b5c6 --- /dev/null +++ b/manual.md @@ -0,0 +1,198 @@ +# Expressions + +A WL file is a sequence of statements. One type of statement is expressions: + +``` +1 + 2 +``` + +All expressions statements are evaluated and written to output. + +## Supported Types + +WL supports these type of values: + +1. None: A value which is only equal to itself +2. Booleans +3. Integers: Equivalent to `int64_t` in C +4. Floats: Equivalent to `double` in C +5. Strings: Sequence of bytes +6. Arrays: Etherogeneous sequences of values +7. Maps: Associations between arbitrary key-value paris + +This is how the literals are used: + +``` +none +true +false +"I'm a string" +'I'm a string too!' +[1, 2, 3] ++{ "I'm the first key": 1, "I'm the second key": 2 } +``` + +## Unary Operators + +The supported unary operators are + +1. `+`: Allowed on any type and returns the operand unchanged +2. `-`: Negates an integer or float value +3. `len`: Returns the number of items stored into an array or the number of key-value pairs in a map + +## Binary Operators + +The supported binary operators are + +1. `+`: Sums two numeric values. If a float value is involved, the result is a float too. +1. `-`: Subtracts two numeric values. If a float value is involved, the result is a float too. +1. `*`: Multiplies two numeric values. If a float value is involved, the result is a float too. +1. `/`: Divides two numeric values. If a float value is involved, the result is a float too. +1. `%`: Returns the division's remainder. The operands must be integers. +1. `==`: Returns `true` if the operands are the same, else returns `false`. +1. `!=`: Returns `true` if the operands are different, else returns `false` +1. `<`: Returns `true` if the first operand is lower than the second one, else returns `false`. The operands must be numeric. +1. `>`: Returns `true` if the first operand is greater than the second one, else returns `false`. The operands must be numeric. + +Note that there are no implicit conversions, so for instance the integer `1` is different from the floating point `1.0`. + +## Escaping Characters In String Literals + +String literals can only contain printable ASCII characters (codepoints 32 to 127). Any other byte value must be escaped. + +You can use `\n`, `\t`, `\r` to represent the line feed, horizontal tab, and carriage return characters. + +Since single `'` or double `"` quotes are used as string delimiters, you must escape any quote that's part of the value using a backslash: `\'`, `\"`. + +If a string contains a backslash, the backslash itself must be escaped `\\`. + +Any byte value can be encoded using the `\x` notation + +``` +"This byte \xFF is not valid ASCII" +``` + +It allows to encode any byte value with its uppercase or lowercase hexadecimal representation. There must always be two hex digits, even if the high bits are zero. + +## Variables & Scopes + +You can bind expression results to variables + +``` +let a = 1+2 +``` + +This will bind the result of the expression to the name "a". Variable names can contain digits, letters, and underscores, but the first character can't be a digit. When an expression is bound to a variable, it is not written to output. + +You can later reuse the bound value by its variable name + +``` +a + 3 +``` + +This will output `6`. + +You can't declare two variables with the same time. The following is invalid: + +``` +let a = 1 +let a = 2 +``` + +You can reuse the same variable name by declaring a new scope: + +``` +let a = 1 + +{ + let a = 2 + a +} +``` + +Grouping statements into scopes this way allows one to reuse variable names. Whenever a variable is referenced, the one in the nearest scope is used. So the previous example will output `2`. + +# If-else statements + +You can optionally run some code based on an expression result using if-else statements: + +``` +if 1 > 2: { + "First branch taken" +} else { + "Second branch taken" +} +``` + +As usual, you can omit the else branch + +``` +if 1 > 2: { + "Branch taken" +} +``` + +If the branch only contains one statement, you can omit the curly braces + +``` +if 1 > 2: + "First branch taken" +else + "Second branch taken" +``` + +A consequence of this is that you can chain if-else statements + +``` +let a = 4 +if a == 1: { + "a is 1" +} else if a == 2: { + "a is 2" +} else if a == 3: { + "a is 3" +} else { + "a is something else" +} +``` + +# While statements + +You can loop while a certain condition is true using a while statement + +``` +let i = 0 +while i < 3: { + "i=" + i + "\n" + i = i+1 +} +``` + +Which will print: + +``` +i=0 +i=1 +i=2 +``` + +# For statements + +You can iterate over the items of an array using the for statement + +``` +for item in ["A", "B", "C"]: { + item +} +``` + +This will print + +``` +ABC +``` + +By adding a second iteration variable, you will be able to read the current index + diff --git a/old.wl b/old.wl deleted file mode 100644 index ec80c30..0000000 --- a/old.wl +++ /dev/null @@ -1,28 +0,0 @@ ----3; - -hello; - -world; - -if 1 + 2: { - a; - b + 3 * 4; - c; -} - -while 1 + 2: - "This is a string"; - - - - aaa - Some Title - bbb - - -
    - $ while 1: -
  • $post.name; $post.date;
  • ; -
- -; diff --git a/old_2.wl b/old_2.wl deleted file mode 100644 index e69de29..0000000 diff --git a/src/assemble.c b/src/assemble.c deleted file mode 100644 index cc43d1d..0000000 --- a/src/assemble.c +++ /dev/null @@ -1,1637 +0,0 @@ - -#ifndef WL_AMALGAMATION -#include "includes.h" -#include "parse.h" -#include "assemble.h" -#endif - -#define MAX_SCOPES 32 -#define MAX_SYMBOLS 1024 -#define MAX_DEPTH 128 - -typedef struct FunctionCall FunctionCall; -struct FunctionCall { - FunctionCall *next; - String name; - int off; -}; - -typedef enum { - SYMBOL_VAR, - SYMBOL_FUNC, -} SymbolType; - -typedef struct { - SymbolType type; - String name; - int off; -} Symbol; - -typedef enum { - SCOPE_GLOBAL, - SCOPE_FUNC, - SCOPE_FOR, - SCOPE_WHILE, - SCOPE_IF, - SCOPE_ELSE, - SCOPE_BLOCK, - SCOPE_HTML, -} ScopeType; - -typedef struct { - ScopeType type; - int sym_base; - int max_vars; - FunctionCall* calls; -} Scope; - -typedef struct { - char *ptr; - int len; - int cap; - bool err; -} OutputBuffer; - -typedef struct { - - WL_Arena *a; - - OutputBuffer out; - - int num_syms; - Symbol syms[MAX_SYMBOLS]; - - int num_scopes; - Scope scopes[MAX_SCOPES]; - - int strings_len; - int strings_cap; - char *strings; - - char *errbuf; - int errmax; - int errlen; - -} Assembler; - -void assembler_report(Assembler *a, char *fmt, ...) -{ - if (a->errmax == 0 || a->errlen > 0) - return; - - int len = snprintf(a->errbuf, a->errmax, "Error: "); - if (len < 0) { - // TODO - } - - va_list args; - va_start(args, fmt); - int ret = vsnprintf(a->errbuf + len, a->errmax - len, fmt, args); - va_end(args); - if (ret < 0) { - // TODO - } - len += ret; - - a->errlen = len; -} - -int add_string_literal(Assembler *a, String str) -{ - if (a->strings_cap - a->strings_len < str.len) { - int c = MAX(2 * a->strings_cap, a->strings_len + str.len); - char *p = malloc(c); - if (p == NULL) { - assembler_report(a, "Out of memory"); - return -1; - } - if (a->strings_cap) { - memcpy(p, a->strings, a->strings_len); - free(a->strings); - } - - a->strings = p; - a->strings_cap = c; - } - - int off = a->strings_len; - memcpy(a->strings + a->strings_len, str.ptr, str.len); - a->strings_len += str.len; - - return off; -} - -void append_mem(OutputBuffer *out, void *ptr, int len) -{ - if (out->err) - return; - - if (out->cap - out->len < len) { - - int new_cap = MAX(out->len + len, 2 * out->cap); - char *new_ptr = malloc(new_cap); - if (new_ptr == NULL) { - out->err = true; - return; - } - - if (out->cap) { - memcpy(new_ptr, out->ptr, out->len); - free(out->ptr); - } - - out->ptr = new_ptr; - out->cap = new_cap; - } - - memcpy(out->ptr + out->len, ptr, len); - out->len += len; -} - -void patch_mem(OutputBuffer *out, int off, void *ptr, int len) -{ - if (out->err) - return; - - memcpy(out->ptr + off, ptr, len); -} - -int append_u8(OutputBuffer *out, uint8_t x) -{ - int off = out->len; - append_mem(out, &x, (int) sizeof(x)); - return off; -} - -int append_u32(OutputBuffer *out, uint32_t x) -{ - int off = out->len; - append_mem(out, &x, (int) sizeof(x)); - return off; -} - -int append_s64(OutputBuffer *out, int64_t x) -{ - int off = out->len; - append_mem(out, &x, (int) sizeof(x)); - return off; -} - -int append_f64(OutputBuffer *out, double x) -{ - int off = out->len; - append_mem(out, &x, (int) sizeof(x)); - return off; -} - -void patch_with_current_offset(OutputBuffer *out, int off) -{ - uint32_t x = out->len; - patch_mem(out, off, &x, (int) sizeof(x)); -} - -void patch_u32(OutputBuffer *out, int off, uint32_t x) -{ - patch_mem(out, off, &x, (int) sizeof(x)); -} - -int current_offset(OutputBuffer *out) -{ - return out->len; -} - -int count_nodes(Node *head) -{ - int n = 0; - Node *node = head; - while (node) { - n++; - node = node->next; - } - return n; -} - -Scope *parent_scope(Assembler *a) -{ - assert(a->num_scopes > 0); - - int parent = a->num_scopes-1; - while (a->scopes[parent].type != SCOPE_FUNC - && a->scopes[parent].type != SCOPE_GLOBAL) - parent--; - - Scope *scope = &a->scopes[parent]; - - assert(scope->type == SCOPE_GLOBAL - || scope->type == SCOPE_FUNC); - - return scope; -} - -bool global_scope(Assembler *a) -{ - return parent_scope(a)->type == SCOPE_GLOBAL; -} - -Symbol *find_symbol_in_local_scope(Assembler *a, String name) -{ - if (name.len == 0) - return NULL; - - Scope *scope = &a->scopes[a->num_scopes-1]; - for (int i = a->num_syms-1; i >= scope->sym_base; i--) - if (streq(a->syms[i].name, name)) - return &a->syms[i]; - return NULL; -} - -Symbol *find_symbol_in_function(Assembler *a, String name) -{ - if (name.len == 0) - return NULL; - - Scope *scope = parent_scope(a); - for (int i = a->num_syms-1; i >= scope->sym_base; i--) - if (streq(a->syms[i].name, name)) - return &a->syms[i]; - return NULL; -} - -int count_local_vars(Assembler *a) -{ - int n = 0; - Scope *scope = parent_scope(a); - for (int i = scope->sym_base; i < a->num_syms; i++) - if (a->syms[i].type == SYMBOL_VAR) - n++; - return n; -} - -int declare_variable(Assembler *a, String name) -{ - if (a->num_syms == MAX_SYMBOLS) { - assembler_report(a, "Symbol limit reached"); - return -1; - } - - if (find_symbol_in_local_scope(a, name)) { - assembler_report(a, "Symbol '%.*s' already declared in this scope", - name.len, name.ptr); - return -1; - } - - int off = count_local_vars(a); - a->syms[a->num_syms++] = (Symbol) { SYMBOL_VAR, name, off }; - - Scope *scope = parent_scope(a); - - if (scope->max_vars < off + 1) - scope->max_vars = off + 1; - - return off; -} - -int declare_function(Assembler *a, String name, int off) -{ - if (a->num_syms == MAX_SYMBOLS) { - assembler_report(a, "Symbol limit reached"); - return -1; - } - - if (find_symbol_in_local_scope(a, name)) { - assembler_report(a, "Symbol '%.*s' already declared in this scope", name.len, name.ptr); - return -1; - } - - a->syms[a->num_syms++] = (Symbol) { SYMBOL_FUNC, name, off }; - return 0; -} - -bool is_expr(Node *node) -{ - switch (node->type) { - - default: - break; - - case NODE_SELECT: - case NODE_NESTED: - case NODE_FUNC_CALL: - case NODE_OPER_LEN: - case NODE_OPER_POS: - case NODE_OPER_NEG: - case NODE_OPER_ASS: - case NODE_OPER_EQL: - case NODE_OPER_NQL: - case NODE_OPER_LSS: - case NODE_OPER_GRT: - case NODE_OPER_ADD: - case NODE_OPER_SUB: - case NODE_OPER_MUL: - case NODE_OPER_DIV: - case NODE_OPER_MOD: - case NODE_VALUE_INT: - case NODE_VALUE_FLOAT: - case NODE_VALUE_STR: - case NODE_VALUE_NONE: - case NODE_VALUE_TRUE: - case NODE_VALUE_FALSE: - case NODE_VALUE_VAR: - case NODE_VALUE_SYSVAR: - case NODE_VALUE_HTML: - case NODE_VALUE_ARRAY: - case NODE_VALUE_MAP: - return true; - } - - return false; -} - -int push_scope(Assembler *a, ScopeType type) -{ - if (a->num_scopes == MAX_SCOPES) { - assembler_report(a, "Scope limit reached"); - return -1; - } - Scope *scope = &a->scopes[a->num_scopes++]; - scope->type = type; - scope->sym_base = a->num_syms; - scope->max_vars = 0; - scope->calls = NULL; - return 0; -} - -int pop_scope(Assembler *a) -{ - Scope *scope = &a->scopes[a->num_scopes-1]; - - FunctionCall *call = scope->calls; - FunctionCall **prev = &scope->calls; - while (call) { - - Symbol *sym = find_symbol_in_local_scope(a, call->name); - - if (sym == NULL) { - prev = &call->next; - call = call->next; - continue; - } - - if (sym->type != SYMBOL_FUNC) { - assembler_report(a, "Symbol '%.*s' is not a function", call->name.len, call->name.ptr); - return -1; - } - - patch_u32(&a->out, call->off, sym->off); - - *prev = call->next; - call = call->next; - } - - if (scope->calls) { - - if (a->num_scopes == 1) { - assembler_report(a, "Undefined function '%.*s'", - scope->calls->name.len, - scope->calls->name.ptr); - return -1; - } - - Scope *parent_scope = &a->scopes[a->num_scopes-2]; - *prev = parent_scope->calls; - parent_scope->calls = scope->calls; - } - - a->num_syms = scope->sym_base; - a->num_scopes--; - return 0; -} - -void assemble_statement(Assembler *a, Node *node, bool pop_expr); - -typedef struct { - OutputBuffer tmp; -} HTMLAssembler; - -void write_buffered_html(Assembler *a, HTMLAssembler *ha) -{ - if (ha->tmp.len == 0) - return; - - int off = add_string_literal(a, (String) { ha->tmp.ptr, ha->tmp.len }); - append_u8(&a->out, OPCODE_PUSHS); - append_u32(&a->out, off); - append_u32(&a->out, ha->tmp.len); - - free(ha->tmp.ptr); - ha->tmp.ptr = NULL; - ha->tmp.len = 0; - ha->tmp.cap = 0; -} - -void assemble_html_2(Assembler *a, HTMLAssembler *ha, Node *node) -{ - append_u8(&ha->tmp, '<'); - append_mem(&ha->tmp, node->tagname.ptr, node->tagname.len); - - Node *attr = node->params; - while (attr) { - - String name = attr->attr_name; - Node *value = attr->attr_value; - - append_u8(&ha->tmp, ' '); - append_mem(&ha->tmp, name.ptr, name.len); - - if (value) { - append_u8(&ha->tmp, '='); - append_u8(&ha->tmp, '"'); - - if (value->type == NODE_VALUE_STR) { - append_mem(&ha->tmp, - value->sval.ptr, // TODO: escape - value->sval.len - ); - } else { - write_buffered_html(a, ha); - assemble_statement(a, value, false); - } - - append_u8(&ha->tmp, '"'); - } - attr = attr->next; - } - - if (node->no_body) { - append_u8(&ha->tmp, ' '); - append_u8(&ha->tmp, '/'); - append_u8(&ha->tmp, '>'); - } else { - - append_u8(&ha->tmp, '>'); - - Node *child = node->child; - while (child) { - if (child->type == NODE_VALUE_STR) - append_mem(&ha->tmp, child->sval.ptr, child->sval.len); - else if (child->type == NODE_VALUE_HTML) - assemble_html_2(a, ha, child); - else { - write_buffered_html(a, ha); - assemble_statement(a, child, false); - } - child = child->next; - } - - append_u8(&ha->tmp, '<'); - append_u8(&ha->tmp, '/'); - append_mem(&ha->tmp, node->tagname.ptr, node->tagname.len); - append_u8(&ha->tmp, '>'); - } -} - -void assemble_html(Assembler *a, Node *node) -{ - HTMLAssembler ha = { - .tmp={.ptr=NULL,.len=0,.cap=0,.err=false}, - }; - assemble_html_2(a, &ha, node); - write_buffered_html(a, &ha); -} - -void assemble_expr(Assembler *a, Node *node, int num_results) -{ - switch (node->type) { - - default: - assert(0); - break; - - case NODE_FUNC_CALL: - { - Node *func = node->left; - Node *args = node->right; - - append_u8(&a->out, OPCODE_GROUP); - - int arg_count = 0; - Node *arg = args; - while (arg) { - assemble_expr(a, arg, 1); - arg_count++; - arg = arg->next; - } - - if (func->type == NODE_VALUE_SYSVAR) { - - String name = func->sval; - int off = add_string_literal(a, name); - - append_u8(&a->out, OPCODE_SYSCALL); - append_u32(&a->out, off); - append_u32(&a->out, name.len); - - } else { - - assert(func->type == NODE_VALUE_VAR); - - append_u8(&a->out, OPCODE_CALL); - int p = append_u32(&a->out, 0); - - FunctionCall *call = alloc(a->a, sizeof(FunctionCall), _Alignof(FunctionCall)); - if (call == NULL) { - assembler_report(a, "Out of memory"); - return; - } - call->name = func->sval; - call->off = p; - - Scope *scope = &a->scopes[a->num_scopes-1]; - - call->next = scope->calls; - scope->calls = call; - } - - if (num_results == 0) - append_u8(&a->out, OPCODE_GPOP); - else if (num_results != -1) { - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results); - } - - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_LEN: - assemble_expr(a, node->left, 1); - append_u8(&a->out, OPCODE_LEN); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_POS: - assemble_expr(a, node->left, num_results); - break; - - case NODE_OPER_NEG: - assemble_expr(a, node->left, 1); - append_u8(&a->out, OPCODE_NEG); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_EQL: - assemble_expr(a, node->left, 1); - assemble_expr(a, node->right, 1); - append_u8(&a->out, OPCODE_EQL); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_NQL: - assemble_expr(a, node->left, 1); - assemble_expr(a, node->right, 1); - append_u8(&a->out, OPCODE_NQL); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_LSS: - assemble_expr(a, node->left, 1); - assemble_expr(a, node->right, 1); - append_u8(&a->out, OPCODE_LSS); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_GRT: - assemble_expr(a, node->left, 1); - assemble_expr(a, node->right, 1); - append_u8(&a->out, OPCODE_GRT); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_ADD: - assemble_expr(a, node->left, 1); - assemble_expr(a, node->right, 1); - append_u8(&a->out, OPCODE_ADD); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_SUB: - assemble_expr(a, node->left, 1); - assemble_expr(a, node->right, 1); - append_u8(&a->out, OPCODE_SUB); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_MUL: - assemble_expr(a, node->left, 1); - assemble_expr(a, node->right, 1); - append_u8(&a->out, OPCODE_MUL); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_DIV: - assemble_expr(a, node->left, 1); - assemble_expr(a, node->right, 1); - append_u8(&a->out, OPCODE_DIV); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_OPER_MOD: - assemble_expr(a, node->left, 1); - assemble_expr(a, node->right, 1); - append_u8(&a->out, OPCODE_MOD); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_VALUE_INT: - append_u8(&a->out, OPCODE_PUSHI); - append_s64(&a->out, node->ival); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_VALUE_FLOAT: - append_u8 (&a->out, OPCODE_PUSHF); - append_f64(&a->out, node->dval); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - break; - - case NODE_VALUE_STR: - { - int off = add_string_literal(a, node->sval); - append_u8(&a->out, OPCODE_PUSHS); - append_u32(&a->out, off); - append_u32(&a->out, node->sval.len); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - } - break; - - case NODE_VALUE_NONE: - { - append_u8(&a->out, OPCODE_PUSHN); - } - break; - - case NODE_VALUE_TRUE: - { - append_u8(&a->out, OPCODE_PUSHT); - } - break; - - case NODE_VALUE_FALSE: - { - append_u8(&a->out, OPCODE_PUSHFL); - } - break; - - case NODE_VALUE_VAR: - { - String name = node->sval; - Symbol *sym = find_symbol_in_function(a, name); - if (sym == NULL) { - assembler_report(a, "Reference to undefined variable '%.*s'", name.len, name.ptr); - return; - } - if (sym->type != SYMBOL_VAR) { - assembler_report(a, "Symbol '%.*s' is not a variable", sym->name.len, sym->name.ptr); - return; - } - append_u8(&a->out, OPCODE_PUSHV); - append_u8(&a->out, sym->off); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - } - break; - - case NODE_VALUE_SYSVAR: - { - String name = node->sval; - int off = add_string_literal(a, name); - - append_u8(&a->out, OPCODE_SYSVAR); - append_u32(&a->out, off); - append_u32(&a->out, name.len); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - } - break; - - case NODE_VALUE_HTML: - { - if (num_results != -1) - append_u8(&a->out, OPCODE_GROUP); - - assemble_html(a, node); - - if (num_results != -1) { - - append_u8(&a->out, OPCODE_GPACK); - - if (num_results > 1) { - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - } - } - break; - - case NODE_VALUE_ARRAY: - { - append_u8(&a->out, OPCODE_PUSHA); - append_u32(&a->out, count_nodes(node->child)); - - Node *child = node->child; - while (child) { - assemble_expr(a, child, 1); - append_u8(&a->out, OPCODE_APPEND); - child = child->next; - } - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - } - break; - - case NODE_VALUE_MAP: - { - append_u8(&a->out, OPCODE_PUSHM); - append_u32(&a->out, count_nodes(node->child)); - - Node *child = node->child; - while (child) { - assemble_expr(a, child, 1); - assemble_expr(a, child->key, 1); - append_u8(&a->out, OPCODE_INSERT1); - child = child->next; - } - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - } - break; - - case NODE_SELECT: - { - Node *set = node->left; - Node *key = node->right; - - assemble_expr(a, set, 1); - assemble_expr(a, key, 1); - append_u8(&a->out, OPCODE_SELECT); - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - } - break; - - case NODE_NESTED: - assemble_expr(a, node->left, num_results); - break; - - case NODE_OPER_ASS: - { - Node *dst = node->left; - Node *src = node->right; - - if (dst->type == NODE_VALUE_VAR) { - - String name = dst->sval; - - Symbol *sym = find_symbol_in_function(a, name); - if (sym == NULL) { - assembler_report(a, "Undeclared variable '%.*s'", name.len, name.ptr); - return; - } - - if (sym->type != SYMBOL_VAR) { - assembler_report(a, "Symbol '%.*s' can't be assigned to", name.len, name.ptr); - return; - } - - assemble_expr(a, src, 1); - append_u8(&a->out, OPCODE_SETV); - append_u8(&a->out, sym->off); - - } else if (dst->type == NODE_SELECT) { - - assemble_expr(a, src, 1); - assemble_expr(a, dst->left, 1); - assemble_expr(a, dst->right, 1); - append_u8(&a->out, OPCODE_INSERT2); - - } else { - - assembler_report(a, "Assignment left side can't be assigned to"); - return; - } - - if (num_results == 0) - append_u8(&a->out, OPCODE_POP); - else if (num_results != -1 && num_results != 1) { - append_u8(&a->out, OPCODE_GROUP); - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, num_results-1); - append_u8(&a->out, OPCODE_GCOALESCE); - } - } - break; - } -} - -void assemble_statement(Assembler *a, Node *node, bool pop_expr) -{ - switch (node->type) { - - case NODE_INCLUDE: - { - assert(node->include_root); - assemble_statement(a, node->include_root, pop_expr); - } - break; - - case NODE_PRINT: - { - append_u8(&a->out, OPCODE_GROUP); - assemble_expr(a, node->left, -1); - append_u8(&a->out, OPCODE_GPRINT); - append_u8(&a->out, OPCODE_GPOP); - } - break; - - case NODE_FUNC_DECL: - { - append_u8(&a->out, OPCODE_JUMP); - int p1 = append_u32(&a->out, 0); - - int ret = declare_function(a, node->func_name, current_offset(&a->out)); - if (ret < 0) return; - - ret = push_scope(a, SCOPE_FUNC); - if (ret < 0) return; - - int arg_count = count_nodes(node->func_args); - - append_u8(&a->out, OPCODE_GTRUNC); - append_u32(&a->out, arg_count); - - append_u8(&a->out, OPCODE_GTRUNC); - int p = append_u32(&a->out, 0); - - Node *arg = node->func_args; - int idx = 0; - while (arg) { - - int off = declare_variable(a, arg->sval); - if (off < 0) return; - - assert(off == idx); - - idx++; - arg = arg->next; - } - - append_u8(&a->out, OPCODE_GROUP); - - if (is_expr(node->func_body)) { - assemble_expr(a, node->func_body, -1); - } else { - assemble_statement(a, node->func_body, true); - append_u8(&a->out, OPCODE_PUSHN); - } - - append_u8(&a->out, OPCODE_GOVERWRITE); - append_u8(&a->out, OPCODE_RET); - - patch_u32(&a->out, p, a->scopes[a->num_scopes-1].max_vars); - - ret = pop_scope(a); - if (ret < 0) return; - - patch_with_current_offset(&a->out, p1); - } - break; - - case NODE_VAR_DECL: - { - int off = declare_variable(a, node->var_name); - if (off < 0) return; - - if (node->var_value) - assemble_expr(a, node->var_value, 1); - else - append_u8(&a->out, OPCODE_PUSHN); - - append_u8(&a->out, OPCODE_SETV); - append_u8(&a->out, off); - } - break; - - case NODE_BLOCK: - case NODE_GLOBAL_BLOCK: - { - if (node->type == NODE_BLOCK) { - int ret = push_scope(a, SCOPE_BLOCK); - if (ret < 0) return; - } - - Node *stmt = node->left; - while (stmt) { - assemble_statement(a, stmt, pop_expr); - stmt = stmt->next; - } - - if (node->type == NODE_BLOCK) { - int ret = pop_scope(a); - if (ret < 0) return; - } - } - break; - - case NODE_IFELSE: - { - // If there is no else branch: - // - // - // JIFP end - // - // end: - // ... - // - // If there is: - // - // - // JIFP else - // - // JUMP end - // else: - // - // end: - // ... - - if (node->right) { - - assemble_expr(a, node->cond, 1); - - append_u8(&a->out, OPCODE_JIFP); - int p1 = append_u32(&a->out, 0); - - int ret = push_scope(a, SCOPE_IF); - if (ret < 0) return; - - assemble_statement(a, node->left, pop_expr); - - ret = pop_scope(a); - if (ret < 0) return; - - append_u8(&a->out, OPCODE_JUMP); - int p2 = append_u32(&a->out, 0); - - patch_with_current_offset(&a->out, p1); - - ret = push_scope(a, SCOPE_ELSE); - if (ret < 0) return; - - assemble_statement(a, node->right, pop_expr); - - ret = pop_scope(a); - if (ret < 0) return; - - patch_with_current_offset(&a->out, p2); - - } else { - - assemble_expr(a, node->cond, 1); - - append_u8(&a->out, OPCODE_JIFP); - int p1 = append_u32(&a->out, 0); - - int ret = push_scope(a, SCOPE_IF); - if (ret < 0) return; - - assemble_statement(a, node->left, pop_expr); - - ret = pop_scope(a); - if (ret < 0) return; - - patch_with_current_offset(&a->out, p1); - } - } - break; - - case NODE_WHILE: - { - // start: - // - // JIFP end - // - // JUMP start - // end: - // ... - - int start = current_offset(&a->out); - - assemble_expr(a, node->cond, 1); - - append_u8(&a->out, OPCODE_JIFP); - int p = append_u32(&a->out, 0); - - int ret = push_scope(a, SCOPE_WHILE); - if (ret < 0) return; - - assemble_statement(a, node->left, pop_expr); - - ret = pop_scope(a); - if (ret < 0) return; - - append_u8(&a->out, OPCODE_JUMP); - append_u32(&a->out, start); - - patch_with_current_offset(&a->out, p); - } - break; - - case NODE_FOR: - { - int ret = push_scope(a, SCOPE_FOR); - if (ret < 0) return; - - int var_1 = declare_variable(a, node->for_var1); - int var_2 = declare_variable(a, node->for_var2); - int var_3 = declare_variable(a, (String) { NULL, 0 }); - - assemble_expr(a, node->for_set, 1); - append_u8(&a->out, OPCODE_SETV); - append_u8(&a->out, var_3); - - append_u8(&a->out, OPCODE_PUSHI); - append_s64(&a->out, 0); - append_u8(&a->out, OPCODE_SETV); - append_u8(&a->out, var_2); - - int start = append_u8(&a->out, OPCODE_FOR); - append_u8(&a->out, var_3); - append_u8(&a->out, var_1); - append_u8(&a->out, var_2); - int p = append_u32(&a->out, 0); - - assemble_statement(a, node->left, pop_expr); - - append_u8(&a->out, OPCODE_JUMP); - append_u32(&a->out, start); - - patch_with_current_offset(&a->out, p); - - ret = pop_scope(a); - if (ret < 0) return; - } - break; - - default: - assemble_expr(a, node, pop_expr ? 0 : -1); - break; - } -} - -typedef struct { - uint32_t magic; - uint32_t code_size; - uint32_t data_size; -} Header; - -AssembleResult assemble(Node *root, WL_Arena *arena, char *errbuf, int errmax) -{ - Assembler a = {0}; - a.errbuf = errbuf; - a.errmax = errmax; - a.a = arena; - - int ret = push_scope(&a, SCOPE_GLOBAL); - if (ret < 0) - return (AssembleResult) { (WL_Program) {0}, a.errlen }; - - append_u8(&a.out, OPCODE_GROUP); - - append_u8(&a.out, OPCODE_GTRUNC); - int p = append_u32(&a.out, 0); - - append_u8(&a.out, OPCODE_GROUP); - - assemble_statement(&a, root, false); - - append_u8(&a.out, OPCODE_GPRINT); - append_u8(&a.out, OPCODE_GPOP); - - append_u8(&a.out, OPCODE_GPOP); - append_u8(&a.out, OPCODE_EXIT); - - patch_u32(&a.out, p, a.scopes[a.num_scopes-1].max_vars); - - ret = pop_scope(&a); - if (ret < 0) - return (AssembleResult) { (WL_Program) {0}, a.errlen }; - - OutputBuffer out = {0}; - append_u32(&out, 0xFEEDBEEF); // magic - append_u32(&out, a.out.len); // code size - append_u32(&out, a.strings_len); // data size - append_mem(&out, a.out.ptr, a.out.len); - append_mem(&out, a.strings, a.strings_len); - - free(a.out.ptr); - return (AssembleResult) { (WL_Program) { out.ptr, out.len }, a.errlen }; -} - -int parse_program_header(WL_Program p, String *code, String *data, char *errbuf, int errmax) -{ - if ((uint32_t) p.len < 3 * sizeof(uint32_t)) { - snprintf(errbuf, errmax, "Invalid program"); - return -1; - } - - uint32_t magic; - uint32_t code_size; - uint32_t data_size; - memcpy(&magic, p.ptr + 0, sizeof(uint32_t)); - memcpy(&code_size, p.ptr + 4, sizeof(uint32_t)); - memcpy(&data_size, p.ptr + 8, sizeof(uint32_t)); - - if (magic != 0xFEEDBEEF) { - snprintf(errbuf, errmax, "Invalid program"); - return -1; - } - - if (code_size + data_size + 3 * sizeof(uint32_t) != (uint32_t) p.len) { - snprintf(errbuf, errmax, "Invalid program"); - return -1; - } - - *code = (String) { p.ptr + 3 * sizeof(uint32_t), code_size }; - *data = (String) { p.ptr + 3 * sizeof(uint32_t) + code_size, data_size }; - return 0; -} - -void print_program(WL_Program program) -{ - String code; - String data; - - char err[128]; - if (parse_program_header(program, &code, &data, err, COUNT(err)) < 0) { - printf("%s\n", err); - return; - } - - char *p = code.ptr; - for (;;) { - printf(" %-3d: ", (int) (p - code.ptr)); - p = print_instruction(p, data.ptr); - printf("\n"); - if (p == code.ptr + code.len) - break; - } -} - -char *print_instruction(char *p, char *data) -{ - switch (*(p++)) { - - default: - printf("(unknown opcode 0x%x)", *p); - break; - - case OPCODE_NOPE: - printf("NOPE"); - break; - - case OPCODE_EXIT: - printf("EXIT"); - break; - - case OPCODE_GROUP: - { - printf("GROUP"); - } - break; - - case OPCODE_GPOP: - { - printf("GPOP"); - } - break; - - case OPCODE_GPRINT: - { - printf("GPRINT"); - } - break; - - case OPCODE_GTRUNC: - { - uint32_t off; - memcpy(&off, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("GTRUNC %u", off); - } - break; - - case OPCODE_GCOALESCE: - { - printf("GCOALESCE"); - } - break; - - case OPCODE_GOVERWRITE: - { - printf("GOVERWRITE"); - } - break; - - case OPCODE_GPACK: - { - printf("GPACK"); - } - break; - - case OPCODE_PUSHI: - { - int64_t x; - memcpy(&x, p, sizeof(int64_t)); - p += sizeof(int64_t); - - printf("PUSHI %" LLU, x); - } - break; - - case OPCODE_PUSHF: - { - double x; - memcpy(&x, p, sizeof(double)); - p += sizeof(double); - - printf("PUSHF %lf", x); - } - break; - - case OPCODE_PUSHS: - { - uint32_t off; - memcpy(&off, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - uint32_t len; - memcpy(&len, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("PUSHS \"%.*s\"", (int) len, (char*) data + off); - } - break; - - case OPCODE_PUSHV: - { - uint8_t idx; - memcpy(&idx, p, sizeof(uint8_t)); - p += sizeof(uint8_t); - - printf("PUSHV %u", idx); - } - break; - - case OPCODE_PUSHA: - { - uint32_t cap; - memcpy(&cap, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("PUSHA %u", cap); - } - break; - - case OPCODE_PUSHM: - { - uint32_t cap; - memcpy(&cap, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("PUSHM %u", cap); - } - break; - - case OPCODE_PUSHN: - { - printf("PUSHN"); - } - break; - - case OPCODE_POP: - printf("POP"); - break; - - case OPCODE_NEG: - printf("NEG"); - break; - - case OPCODE_EQL: - printf("EQL"); - break; - - case OPCODE_NQL: - printf("NQL"); - break; - - case OPCODE_LSS: - printf("LSS"); - break; - - case OPCODE_GRT: - printf("GRT"); - break; - - case OPCODE_ADD: - printf("ADD"); - break; - - case OPCODE_SUB: - printf("SUB"); - break; - - case OPCODE_MUL: - printf("MUL"); - break; - - case OPCODE_DIV: - printf("DIV"); - break; - - case OPCODE_MOD: - printf("MOD"); - break; - - case OPCODE_SETV: - { - uint8_t idx; - memcpy(&idx, p, sizeof(uint8_t)); - p += sizeof(uint8_t); - - printf("SETV %u", idx); - } - break; - - case OPCODE_JUMP: - { - uint32_t off; - memcpy(&off, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("JUMP %u", off); - } - break; - - case OPCODE_JIFP: - { - uint32_t off; - memcpy(&off, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("JIFP %u", off); - } - break; - - case OPCODE_CALL: - { - uint32_t off; - memcpy(&off, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("CALL %u", off); - } - break; - - case OPCODE_RET: - printf("RET"); - break; - - case OPCODE_APPEND: - printf("APPEND"); - break; - - case OPCODE_INSERT1: - printf("INSERT1"); - break; - - case OPCODE_INSERT2: - printf("INSERT2"); - break; - - case OPCODE_SELECT: - printf("SELECT"); - break; - - case OPCODE_PRINT: - printf("PRINT"); - break; - - case OPCODE_SYSVAR: - { - uint32_t off; - memcpy(&off, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - uint32_t len; - memcpy(&len, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("SYSVAR \"%.*s\"", (int) len, (char*) data + off); - } - break; - - case OPCODE_SYSCALL: - { - uint32_t off; - memcpy(&off, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - uint32_t len; - memcpy(&len, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("SYSCALL \"%.*s\"", (int) len, (char*) data + off); - } - break; - - case OPCODE_PUSHT: - printf("PUSHT"); - break; - - case OPCODE_PUSHFL: - printf("PUSHFL"); - break; - - case OPCODE_FOR: - { - uint8_t a; - memcpy(&a, p, sizeof(uint8_t)); - p += sizeof(uint8_t); - - uint8_t b; - memcpy(&b, p, sizeof(uint8_t)); - p += sizeof(uint8_t); - - uint8_t c; - memcpy(&c, p, sizeof(uint8_t)); - p += sizeof(uint8_t); - - uint32_t d; - memcpy(&d, p, sizeof(uint32_t)); - p += sizeof(uint32_t); - - printf("FOR %u %u %u %u", a, b, c, d); - } - break; - } - - return p; -} \ No newline at end of file diff --git a/src/assemble.h b/src/assemble.h deleted file mode 100644 index c8c3a3c..0000000 --- a/src/assemble.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef WL_ASSEMBLE_INCLUDED -#define WL_ASSEMBLE_INCLUDED - -#ifndef WL_AMALGAMATION -#include "public.h" -#include "parse.h" -#endif - -enum { - OPCODE_NOPE = 0x00, - OPCODE_EXIT = 0x23, - OPCODE_GROUP = 0x25, - OPCODE_GPOP = 0x26, - OPCODE_GPRINT = 0x27, - OPCODE_GTRUNC = 0x28, - OPCODE_GCOALESCE = 0x29, - OPCODE_GOVERWRITE = 0x2A, - OPCODE_GPACK = 0x2B, - OPCODE_PUSHI = 0x01, - OPCODE_PUSHF = 0x02, - OPCODE_PUSHS = 0x03, - OPCODE_PUSHV = 0x04, - OPCODE_PUSHA = 0x05, - OPCODE_PUSHM = 0x06, - OPCODE_PUSHN = 0x21, - OPCODE_POP = 0x07, - OPCODE_NEG = 0x08, - OPCODE_EQL = 0x09, - OPCODE_NQL = 0x0A, - OPCODE_LSS = 0x0B, - OPCODE_GRT = 0x0C, - OPCODE_ADD = 0x0D, - OPCODE_SUB = 0x0E, - OPCODE_MUL = 0x0F, - OPCODE_DIV = 0x10, - OPCODE_MOD = 0x11, - OPCODE_SETV = 0x12, - OPCODE_JUMP = 0x13, - OPCODE_JIFP = 0x14, - OPCODE_CALL = 0x15, - OPCODE_RET = 0x16, - OPCODE_APPEND = 0x17, - OPCODE_INSERT1 = 0x18, - OPCODE_INSERT2 = 0x19, - OPCODE_SELECT = 0x20, - OPCODE_PRINT = 0x24, - OPCODE_SYSVAR = 0x2C, - OPCODE_SYSCALL = 0x2D, - OPCODE_FOR = 0x2E, - OPCODE_PUSHT = 0x2F, - OPCODE_PUSHFL = 0x30, - OPCODE_LEN = 0x31, -}; - -typedef struct { - WL_Program program; - int errlen; -} AssembleResult; - -int parse_program_header(WL_Program p, String *code, String *data, char *errbuf, int errmax); -void print_program(WL_Program program); -char *print_instruction(char *p, char *data); -AssembleResult assemble(Node *root, WL_Arena *arena, char *errbuf, int errmax); - -#endif // WL_ASSEMBLE_INCLUDED \ No newline at end of file diff --git a/src/basic.c b/src/basic.c deleted file mode 100644 index 5747774..0000000 --- a/src/basic.c +++ /dev/null @@ -1,98 +0,0 @@ - -#ifndef WL_AMALGAMATION -#include "includes.h" -#include "basic.h" -#include "public.h" -#endif - -bool is_space(char c) -{ - return c == ' ' || c == '\t' || c == '\r' || c == '\n'; -} - -bool is_digit(char c) -{ - return c >= '0' && c <= '9'; -} - -bool is_alpha(char c) -{ - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); -} - -bool is_printable(char c) -{ - return c >= ' ' && c <= '~'; -} - -bool is_hex_digit(char c) -{ - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); -} - -char to_lower(char c) -{ - if (c >= 'A' && c <= 'Z') - return c - 'A' + 10; - return c; -} - -int hex_digit_to_int(char c) -{ - if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - - if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - - return c - '0'; -} - - -bool streq(String a, String b) -{ - if (a.len != b.len) - return false; - for (int i = 0; i < a.len; i++) - if (a.ptr[i] != b.ptr[i]) - return false; - return true; -} - -bool streqcase(String a, String b) -{ - if (a.len != b.len) - return false; - for (int i = 0; i < a.len; i++) - if (to_lower(a.ptr[i]) != to_lower(b.ptr[i])) - return false; - return true; -} - -void *alloc(WL_Arena *a, int len, int align) -{ - int pad = -(intptr_t) (a->ptr + a->cur) & (align-1); - if (a->len - a->cur < len + pad) - return NULL; - void *ret = a->ptr + a->cur + pad; - a->cur += pad + len; - return ret; -} - -bool grow_alloc(WL_Arena *a, char *p, int new_len) -{ - int new_cur = (p - a->ptr) + new_len; - if (new_cur > a->len) - return false; - a->cur = new_cur; - return true; -} - -String copystr(String s, WL_Arena *a) -{ - char *p = alloc(a, s.len, 1); - if (p == NULL) - return (String) { NULL, 0 }; - memcpy(p, s.ptr, s.len); - return (String) { p, s.len }; -} \ No newline at end of file diff --git a/src/basic.h b/src/basic.h deleted file mode 100644 index d124327..0000000 --- a/src/basic.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef WL_BASIC_INCLUDED -#define WL_BASIC_INCLUDED - -#ifndef WL_AMALGAMATION -#include "public.h" -#endif - -typedef struct { - char *ptr; - int len; -} String; - -#ifdef _WIN32 -#define LLU "llu" -#define LLD "lld" -#else -#define LLU "lu" -#define LLD "ld" -#endif - -#define S(X) (String) { (X), (int) sizeof(X)-1 } - -#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) -#define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) - -#define COUNT(X) (int) (sizeof(X) / sizeof((X)[0])) - -bool is_space(char c); -bool is_digit(char c); -bool is_alpha(char c); -bool is_printable(char c); -char to_lower(char c); -bool is_hex_digit(char c); -int hex_digit_to_int(char c); - -bool streq(String a, String b); -bool streqcase(String a, String b); -String copystr(String s, WL_Arena *a); - -void *alloc(WL_Arena *a, int len, int align); -bool grow_alloc(WL_Arena *a, char *p, int new_len); - -#endif // WL_BASIC_INCLUDED \ No newline at end of file diff --git a/src/compile.c b/src/compile.c deleted file mode 100644 index 8f71e3a..0000000 --- a/src/compile.c +++ /dev/null @@ -1,123 +0,0 @@ - -#ifndef WL_AMALGAMATION -#include "eval.h" -#include "parse.h" -#include "assemble.h" -#include "compile.h" -#endif - -#define FILE_LIMIT 32 - -typedef struct { - String file; - Node* root; - Node* includes; -} CompiledFile; - -struct WL_Compiler { - WL_Arena* arena; - CompiledFile files[FILE_LIMIT]; - int num_files; - String waiting_file; -}; - -int WL_streq(WL_String a, char *b, int blen) -{ - if (b == NULL) b = ""; - if (blen < 0) blen = strlen(b); - - if (a.len != blen) - return 0; - - for (int i = 0; i < a.len; i++) - if (a.ptr[i] != b[i]) - return 0; - - return 1; -} - -WL_Compiler *WL_Compiler_init(WL_Arena *arena) -{ - WL_Compiler *compiler = alloc(arena, (int) sizeof(WL_Compiler), _Alignof(WL_Compiler)); - if (compiler == NULL) - return NULL; - compiler->arena = arena; - compiler->num_files = 0; - compiler->waiting_file = (String) { NULL, 0 }; - return compiler; -} - -void WL_Compiler_free(WL_Compiler *compiler) -{ - (void) compiler; - // TODO -} - -WL_CompileResult WL_compile(WL_Compiler *compiler, WL_String file, WL_String content) -{ - if (compiler->waiting_file.len > 0) - file = (WL_String) { compiler->waiting_file.ptr, compiler->waiting_file.len }; - else { - // TODO: copy file path - // file = strdup(file, compiler->arena) - } - - char err[1<<9]; - ParseResult pres = parse((String) { content.ptr, content.len }, compiler->arena, err, (int) sizeof(err)); - if (pres.node == NULL) { - printf("%s\n", err); // TODO - return (WL_CompileResult) { .type=WL_COMPILE_RESULT_ERROR }; - } - - CompiledFile compiled_file = { - .file = { file.ptr, file.len }, - .root = pres.node, - .includes = pres.includes, - }; - compiler->files[compiler->num_files++] = compiled_file; - - for (int i = 0; i < compiler->num_files; i++) { - - Node *include = compiler->files[i].includes; - while (include) { - - assert(include->type == NODE_INCLUDE); - - if (include->include_root == NULL) { - for (int j = 0; j < compiler->num_files; j++) { - if (streq(include->include_path, compiler->files[j].file)) { - include->include_root = compiler->files[j].root; - break; - } - } - } - - if (include->include_root == NULL) { - - if (compiler->num_files == FILE_LIMIT) { - assert(0); // TODO - } - - // TODO: Make the path relative to the compiled file - - compiler->waiting_file = include->include_path; - return (WL_CompileResult) { .type=WL_COMPILE_RESULT_FILE, .path={ include->include_path.ptr, include->include_path.len } }; - } - - include = include->include_next; - } - } - - AssembleResult ares = assemble(compiler->files[0].root, compiler->arena, err, (int) sizeof(err)); - if (ares.errlen) { - printf("%s\n", err); // TODO - return (WL_CompileResult) { .type=WL_COMPILE_RESULT_ERROR }; - } - - return (WL_CompileResult) { .type=WL_COMPILE_RESULT_DONE, .program=ares.program }; -} - -void WL_dump_program(WL_Program program) -{ - print_program(program); -} \ No newline at end of file diff --git a/src/compile.h b/src/compile.h deleted file mode 100644 index 0022ceb..0000000 --- a/src/compile.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef WL_PUBLIC_INCLUDED -#define WL_PUBLIC_INCLUDED - -#ifndef WL_AMALGAMATION -#include "includes.h" -#endif - -typedef struct WL_State WL_State; - -typedef struct { - char *ptr; - int len; -} WL_String; - -typedef struct { - char *ptr; - int len; -} WL_Program; - -typedef enum { - WL_DONE, - WL_ERROR, - WL_VAR, - WL_CALL, - WL_OUTPUT, -} WL_ResultType; - -typedef struct { - WL_ResultType type; - WL_String str; -} WL_Result; - -typedef struct { - char *ptr; - int len; - int cur; -} WL_Arena; - -typedef struct WL_Compiler WL_Compiler; - -typedef enum { - WL_COMPILE_RESULT_DONE, - WL_COMPILE_RESULT_FILE, - WL_COMPILE_RESULT_ERROR, -} WL_CompileResultType; - -typedef struct { - WL_CompileResultType type; - WL_Program program; - WL_String path; -} WL_CompileResult; - -WL_Compiler* WL_Compiler_init (WL_Arena *arena); -void WL_Compiler_free (WL_Compiler *compiler); -WL_CompileResult WL_compile (WL_Compiler *compiler, WL_String file, WL_String content); -WL_State* WL_State_init (WL_Arena *a, WL_Program p, char *err, int errmax); -void WL_State_free (WL_State *state); -void WL_State_trace (WL_State *state, int trace); -WL_Result WL_eval (WL_State *state); - -void WL_dump_program(WL_Program program); - -int WL_streq (WL_String a, char *b, int blen); -int WL_peeknone (WL_State *state, int off); -int WL_peekint (WL_State *state, int off, long long *x); -int WL_peekfloat (WL_State *state, int off, float *x); -int WL_peekstr (WL_State *state, int off, WL_String *str); -int WL_popnone (WL_State *state); -int WL_popint (WL_State *state, long long *x); -int WL_popfloat (WL_State *state, float *x); -int WL_popstr (WL_State *state, WL_String *str); -int WL_popany (WL_State *state); -void WL_select (WL_State *state); -void WL_pushnone (WL_State *state); -void WL_pushint (WL_State *state, long long x); -void WL_pushfloat (WL_State *state, float x); -void WL_pushstr (WL_State *state, WL_String str); -void WL_pusharray (WL_State *state, int cap); -void WL_pushmap (WL_State *state, int cap); -void WL_insert (WL_State *state); -void WL_append (WL_State *state); - -#endif // WL_PUBLIC_INCLUDED diff --git a/src/eval.c b/src/eval.c deleted file mode 100644 index 16e1ac3..0000000 --- a/src/eval.c +++ /dev/null @@ -1,1389 +0,0 @@ - -#ifndef WL_AMALGAMATION -#include "includes.h" -#include "value.h" -#include "eval.h" -#endif - -#define FRAME_LIMIT 128 -#define EVAL_STACK_LIMIT 128 -#define GROUP_LIMIT 128 - -typedef struct { - int group; - int return_addr; -} Frame; - -struct WL_State { - - String code; - String data; - int off; - - bool trace; - - WL_Arena *a; - - char *errbuf; - int errmax; - int errlen; - - int num_frames; - Frame frames[FRAME_LIMIT]; - - int eval_depth; - Value eval_stack[EVAL_STACK_LIMIT]; - - int num_groups; - int groups[GROUP_LIMIT]; - - int cur_print; - int num_prints; - - String sysvar; - String syscall; - bool syscall_error; - int stack_before_user; - int stack_base_for_user; -}; - -void eval_report(WL_State *state, char *fmt, ...) -{ - if (state->errmax == 0 || state->errlen > 0) - return; - - int len = snprintf(state->errbuf, state->errmax, "Error: "); - if (len < 0) { - // TODO - } - - va_list args; - va_start(args, fmt); - int ret = vsnprintf(state->errbuf + len, state->errmax - len, fmt, args); - va_end(args); - if (ret < 0) { - // TODO - } - len += ret; - - state->errlen = len; -} - -static uint8_t read_u8(WL_State *state) -{ - assert(state->off >= 0); - assert(state->off < state->code.len); - return state->code.ptr[state->off++]; -} - -static void read_mem(WL_State *state, void *dst, int len) -{ - memcpy(dst, (uint8_t*) state->code.ptr + state->off, len); - state->off += len; -} - -static uint32_t read_u32(WL_State *state) -{ - uint32_t x; - read_mem(state, &x, (int) sizeof(x)); - return x; -} - -static int64_t read_s64(WL_State *state) -{ - int64_t x; - read_mem(state, &x, (int) sizeof(x)); - return x; -} - -static double read_f64(WL_State *state) -{ - double x; - read_mem(state, &x, (int) sizeof(x)); - return x; -} - -int step(WL_State *state) -{ - uint8_t opcode = read_u8(state); - - if (state->trace) { - printf("%-3d: ", state->off-1); - print_instruction(state->code.ptr + state->off - 1, state->data.ptr); - printf("\n"); - } - - switch (opcode) { - - case OPCODE_NOPE: - { - // Do nothing - } - break; - - case OPCODE_EXIT: - { - return 1; - } - break; - - case OPCODE_GROUP: - { - state->groups[state->num_groups++] = state->eval_depth; - } - break; - - case OPCODE_GPOP: - { - int group = state->groups[--state->num_groups]; - state->eval_depth = group; - } - break; - - case OPCODE_GPRINT: - { - state->num_prints = state->eval_depth - state->groups[state->num_groups-1]; - } - break; - - case OPCODE_GCOALESCE: - { - state->num_groups--; - } - break; - - case OPCODE_GTRUNC: - { - uint32_t num = read_u32(state); - - int group_size = state->eval_depth - state->groups[state->num_groups-1]; - - if (group_size < (int) num) - for (int i = 0; i < (int) num - group_size; i++) - state->eval_stack[state->eval_depth + i] = VALUE_NONE; - - state->eval_depth = state->groups[state->num_groups-1] + num; - } - break; - - case OPCODE_GOVERWRITE: - { - int current = state->groups[state->num_groups-1]; - int parent = state->groups[state->num_groups-2]; - - int current_size = state->eval_depth - current; - - for (int i = 0; i < current_size; i++) - state->eval_stack[parent + i] = state->eval_stack[current + i]; - - state->num_groups--; - state->eval_depth = parent + current_size; - } - break; - - case OPCODE_GPACK: - { - Value array = make_array(state->a); - if (array == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - for (int i = state->groups[state->num_groups-1]; i < state->eval_depth; i++) { - int ret = array_append(state->a, array, state->eval_stack[i]); - if (ret < 0) { - eval_report(state, "Out of memory"); - return -1; - } - } - - state->eval_depth = state->groups[--state->num_groups]; - state->eval_stack[state->eval_depth++] = array; - } - break; - - case OPCODE_PUSHN: - { - state->eval_stack[state->eval_depth++] = VALUE_NONE; - } - break; - - case OPCODE_PUSHI: - { - int64_t x = read_s64(state); - - Value v = make_int(state->a, x); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = v; - } - break; - - case OPCODE_PUSHF: - { - double x = read_f64(state); - - Value v = make_float(state->a, x); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = v; - } - break; - - case OPCODE_PUSHS: - { - uint32_t off = read_u32(state); - uint32_t len = read_u32(state); - - Value v = make_str(state->a, (String) { state->data.ptr + off, len }); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = v; - } - break; - - case OPCODE_PUSHT: - { - state->eval_stack[state->eval_depth++] = VALUE_TRUE; - } - break; - - case OPCODE_PUSHFL: - { - state->eval_stack[state->eval_depth++] = VALUE_FALSE; - } - break; - - case OPCODE_PUSHV: - { - uint8_t idx = read_u8(state); - - int group = state->frames[state->num_frames-1].group; - Value v = state->eval_stack[state->groups[group] + idx]; - - state->eval_stack[state->eval_depth++] = v; - } - break; - - case OPCODE_PUSHA: - { - uint32_t cap = read_u32(state); - (void) cap; - - Value v = make_array(state->a); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = v; - } - break; - - case OPCODE_PUSHM: - { - uint32_t cap = read_u32(state); - (void) cap; - - Value v = make_map(state->a); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = v; - } - break; - - case OPCODE_POP: - { - assert(state->num_groups == 0 || state->eval_depth > state->groups[state->num_groups-1]); - state->eval_depth--; - } - break; - - case OPCODE_NEG: - { - Value a = state->eval_stack[--state->eval_depth]; - Type t = type_of(a); - - Value r; - if (0) {} - else if (t == TYPE_INT) r = make_int(state->a, -get_int(a)); - else if (t == TYPE_FLOAT) r = make_float(state->a, -get_float(a)); - else { - eval_report(state, "Invalid operation on non-numeric value"); - return -1; - } - - if (r == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_EQL: - { - Value a = state->eval_stack[state->eval_depth-2]; - Value b = state->eval_stack[state->eval_depth-1]; - state->eval_depth -= 2; - - Value r = valeq(a, b) ? VALUE_TRUE : VALUE_FALSE; - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_NQL: - { - Value a = state->eval_stack[state->eval_depth-2]; - Value b = state->eval_stack[state->eval_depth-1]; - state->eval_depth -= 2; - - Value r = valeq(a, b) ? VALUE_FALSE : VALUE_TRUE; - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_LSS: - { - Value a = state->eval_stack[state->eval_depth-2]; - Value b = state->eval_stack[state->eval_depth-1]; - state->eval_depth -= 2; - - if (type_of(a) != TYPE_INT || type_of(b) != TYPE_INT) { - eval_report(state, "Invalid operation on non-numeric value"); - return -1; - } - - Value r = valgrt(a, b) || valeq(a, b) ? VALUE_FALSE : VALUE_TRUE; - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_GRT: - { - Value a = state->eval_stack[state->eval_depth-2]; - Value b = state->eval_stack[state->eval_depth-1]; - state->eval_depth -= 2; - - if (type_of(a) != TYPE_INT || type_of(b) != TYPE_INT) { - eval_report(state, "Invalid operation on non-numeric value"); - return -1; - } - - Value r = valgrt(a, b) ? VALUE_TRUE : VALUE_FALSE; - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_ADD: - { - Value a = state->eval_stack[state->eval_depth-2]; - Value b = state->eval_stack[state->eval_depth-1]; - state->eval_depth -= 2; - - #define TYPE_PAIR(X, Y) (((uint16_t) (X) << 16) | (uint16_t) (Y)) - - Type t1 = type_of(a); - Type t2 = type_of(b); - - Value r; - switch (TYPE_PAIR(t1, t2)) { - - case TYPE_PAIR(TYPE_INT, TYPE_INT): - { - int64_t u = get_int(a); - int64_t v = get_int(b); - // TODO: check overflow and underflow - r = make_int(state->a, u + v); - } - break; - - case TYPE_PAIR(TYPE_INT, TYPE_FLOAT): - { - float u = (float) get_int(a); - float v = get_float(b); - r = make_float(state->a, u + v); - } - break; - - case TYPE_PAIR(TYPE_FLOAT, TYPE_INT): - { - float u = get_float(a); - float v = (float) get_int(b); - r = make_float(state->a, u + v); - } - break; - - case TYPE_PAIR(TYPE_FLOAT, TYPE_FLOAT): - { - float u = get_float(a); - float v = get_float(b); - // TODO: check overflow and underflow - r = make_float(state->a, u + v); - } - break; - - default: - eval_report(state, "Invalid operation on non-numeric value"); - return -1; - } - - if (r == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_SUB: - { - Value a = state->eval_stack[state->eval_depth-2]; - Value b = state->eval_stack[state->eval_depth-1]; - state->eval_depth -= 2; - - Type t1 = type_of(a); - Type t2 = type_of(b); - - Value r; - switch (TYPE_PAIR(t1, t2)) { - - case TYPE_PAIR(TYPE_INT, TYPE_INT): - { - int64_t u = get_int(a); - int64_t v = get_int(b); - // TODO: check overflow and underflow - r = make_int(state->a, u - v); - } - break; - - case TYPE_PAIR(TYPE_INT, TYPE_FLOAT): - { - float u = (float) get_int(a); - float v = get_float(b); - r = make_float(state->a, u - v); - } - break; - - case TYPE_PAIR(TYPE_FLOAT, TYPE_INT): - { - float u = get_float(a); - float v = (float) get_int(b); - r = make_float(state->a, u - v); - } - break; - - case TYPE_PAIR(TYPE_FLOAT, TYPE_FLOAT): - { - float u = get_float(a); - float v = get_float(b); - // TODO: check overflow and underflow - r = make_float(state->a, u - v); - } - break; - - default: - eval_report(state, "Invalid operation on non-numeric value"); - return -1; - } - - if (r == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_MUL: - { - Value a = state->eval_stack[state->eval_depth-2]; - Value b = state->eval_stack[state->eval_depth-1]; - state->eval_depth -= 2; - - Type t1 = type_of(a); - Type t2 = type_of(b); - - Value r; - switch (TYPE_PAIR(t1, t2)) { - - case TYPE_PAIR(TYPE_INT, TYPE_INT): - { - int64_t u = get_int(a); - int64_t v = get_int(b); - // TODO: check overflow and underflow - r = make_int(state->a, u * v); - } - break; - - case TYPE_PAIR(TYPE_INT, TYPE_FLOAT): - { - float u = (float) get_int(a); - float v = get_float(b); - r = make_float(state->a, u * v); - } - break; - - case TYPE_PAIR(TYPE_FLOAT, TYPE_INT): - { - float u = get_float(a); - float v = (float) get_int(b); - r = make_float(state->a, u * v); - } - break; - - case TYPE_PAIR(TYPE_FLOAT, TYPE_FLOAT): - { - float u = get_float(a); - float v = get_float(b); - // TODO: check overflow and underflow - r = make_float(state->a, u * v); - } - break; - - default: - eval_report(state, "Invalid operation on non-numeric value"); - return -1; - } - - if (r == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_DIV: - { - Value a = state->eval_stack[state->eval_depth-2]; - Value b = state->eval_stack[state->eval_depth-1]; - state->eval_depth -= 2; - - Type t1 = type_of(a); - Type t2 = type_of(b); - - Value r; - switch (TYPE_PAIR(t1, t2)) { - - case TYPE_PAIR(TYPE_INT, TYPE_INT): - { - // TODO: check division by 0 - - int64_t u = get_int(a); - int64_t v = get_int(b); - r = make_int(state->a, u / v); - } - break; - - case TYPE_PAIR(TYPE_INT, TYPE_FLOAT): - { - // TODO: check division by 0 - - float u = (float) get_int(a); - float v = get_float(b); - r = make_float(state->a, u / v); - } - break; - - case TYPE_PAIR(TYPE_FLOAT, TYPE_INT): - { - // TODO: check division by 0 - - float u = get_float(a); - float v = (float) get_int(b); - r = make_float(state->a, u / v); - } - break; - - case TYPE_PAIR(TYPE_FLOAT, TYPE_FLOAT): - { - float u = get_float(a); - float v = get_float(b); - r = make_float(state->a, u / v); - } - break; - - default: - eval_report(state, "Invalid operation on non-numeric value"); - return -1; - } - - if (r == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_MOD: - { - Value a = state->eval_stack[state->eval_depth-2]; - Value b = state->eval_stack[state->eval_depth-1]; - state->eval_depth -= 2; - - Type t1 = type_of(a); - Type t2 = type_of(b); - - if (t1 != TYPE_INT || t2 != TYPE_INT) { - eval_report(state, "Invalid modulo operation on non-integer value"); - return -1; - } - - int64_t u = get_int(a); - int64_t v = get_int(b); - Value r = make_int(state->a, u % v); - if (r == VALUE_ERROR) { - eval_report(state, "Out of memory"); - return -1; - } - - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_SETV: - { - uint8_t x = read_u8(state); - - Frame *f = &state->frames[state->num_frames-1]; - state->eval_stack[state->groups[f->group] + x] = state->eval_stack[--state->eval_depth]; - } - break; - - case OPCODE_JUMP: - { - uint32_t x = read_u32(state); - state->off = x; - } - break; - - case OPCODE_JIFP: - { - uint32_t x = read_u32(state); - Value a = state->eval_stack[--state->eval_depth]; - - if (a == VALUE_FALSE) - state->off = x; - else { - if (a != VALUE_TRUE) { - eval_report(state, "Invalid operation on non-boolean value"); - return -1; - } - } - } - break; - - case OPCODE_CALL: - { - uint32_t off = read_u32(state); - - if (state->num_frames == FRAME_LIMIT) { - eval_report(state, "Frame limit reached"); - return -1; - } - state->frames[state->num_frames++] = (Frame) {.return_addr=state->off, .group=state->num_groups-1}; - - state->off = off; - } - break; - - case OPCODE_RET: - { - state->off = state->frames[--state->num_frames].return_addr; - } - break; - - case OPCODE_APPEND: - { - Value val = state->eval_stack[state->eval_depth-1]; - Value set = state->eval_stack[state->eval_depth-2]; - state->eval_depth--; - - if (type_of(set) != TYPE_ARRAY) { - eval_report(state, "Invalid operation on non-array value"); - return -1; - } - - int ret = array_append(state->a, set, val); - if (ret < 0) { - eval_report(state, "Out of memory"); - return -1; - } - } - break; - - case OPCODE_INSERT1: - { - Value key = state->eval_stack[state->eval_depth-1]; - Value val = state->eval_stack[state->eval_depth-2]; - Value set = state->eval_stack[state->eval_depth-3]; - state->eval_depth -= 2; - - if (type_of(set) == TYPE_ARRAY) { - - if (type_of(key) != TYPE_INT) { - assert(0); // TODO - } - int64_t idx = get_int(key); - - Value *dst = array_select(set, idx); - if (dst == NULL) { - eval_report(state, "Index out of range"); - return -1; - } - *dst = val; - - } else if (type_of(set) == TYPE_MAP) { - - int ret = map_insert(state->a, set, key, val); - if (ret < 0) { - eval_report(state, "Out of memory"); - return -1; - } - - } else { - eval_report(state, "Invalid insertion on non-array and non-map value"); - return -1; - } - } - break; - - case OPCODE_INSERT2: - { - Value key = state->eval_stack[state->eval_depth-1]; - Value set = state->eval_stack[state->eval_depth-2]; - Value val = state->eval_stack[state->eval_depth-3]; - state->eval_depth -= 2; - - if (type_of(set) == TYPE_ARRAY) { - - if (type_of(key) != TYPE_INT) { - assert(0); // TODO - } - int64_t idx = get_int(key); - - Value *dst = array_select(set, idx); - if (dst == NULL) { - eval_report(state, "Index out of range"); - return -1; - } - *dst = val; - - } else if (type_of(set) == TYPE_MAP) { - - int ret = map_insert(state->a, set, key, val); - if (ret < 0) { - eval_report(state, "Out of memory"); - return -1; - } - - } else { - eval_report(state, "Invalid insertion on non-array and non-map value"); - return -1; - } - } - break; - - case OPCODE_SELECT: - { - Value key = state->eval_stack[state->eval_depth-1]; - Value set = state->eval_stack[state->eval_depth-2]; - state->eval_depth -= 2; - - Value r; - if (type_of(set) == TYPE_ARRAY) { - - if (type_of(key) != TYPE_INT) { - assert(0); // TODO - } - int64_t idx = get_int(key); - - Value *src = array_select(set, idx); - if (src == NULL) { - eval_report(state, "Index out of range"); - return -1; - } - r = *src; - - } else if (type_of(set) == TYPE_MAP) { - - int ret = map_select(set, key, &r); - if (ret < 0) { - eval_report(state, "Key not contained in map"); - return -1; - } - - } else { - eval_report(state, "Invalid selection from non-array and non-map value"); - return -1; - } - - state->eval_stack[state->eval_depth++] = r; - } - break; - - case OPCODE_PRINT: - { - state->num_prints = 1; - } - break; - - case OPCODE_SYSVAR: - { - uint32_t off = read_u32(state); - uint32_t len = read_u32(state); - String name = { state->data.ptr + off, len }; - - state->sysvar = name; - state->stack_before_user = state->eval_depth; - state->stack_base_for_user = state->groups[state->num_groups-1]; - } - break; - - case OPCODE_SYSCALL: - { - uint32_t off = read_u32(state); - uint32_t len = read_u32(state); - String name = { state->data.ptr + off, len }; - - int num_args = state->eval_depth - state->groups[state->num_groups-1]; - - Value v = make_int(state->a, num_args); - if (v == VALUE_ERROR) { - assert(0); // TODO - } - state->eval_stack[state->eval_depth++] = v; - - state->syscall = name; - state->stack_before_user = state->eval_depth; - state->stack_base_for_user = state->groups[state->num_groups-1]; - } - break; - - case OPCODE_FOR: - { - uint8_t var_3 = read_u8(state); - uint8_t var_1 = read_u8(state); - uint8_t var_2 = read_u8(state); - uint32_t end = read_u32(state); - - int base; - { - int group = state->frames[state->num_frames-1].group; - base = state->groups[group]; - } - - int64_t idx; - { - Value idx_val = state->eval_stack[base + var_2]; - if (type_of(idx_val) != TYPE_INT) { - assert(0); // TODO - } - idx = get_int(idx_val); - } - - Value set = state->eval_stack[base + var_3]; - - Type set_type = type_of(set); - if (set_type == TYPE_ARRAY) { - - if (value_length(set) == idx) { - state->off = end; - break; - } - - state->eval_stack[base + var_1] = *array_select(set, idx); - - } else if (set_type == TYPE_MAP) { - - if (value_length(set) == idx) { - state->off = end; - break; - } - - state->eval_stack[base + var_1] = *map_select_by_index(set, idx); - - } else { - assert(0); // TODO - } - - Value v = make_int(state->a, idx + 1); - if (v == VALUE_ERROR) { - assert(0); // TODO - } - state->eval_stack[base + var_2] = v; - } - break; - - case OPCODE_LEN: - { - Value set = state->eval_stack[state->eval_depth-1]; - - Type type = type_of(set); - if (type != TYPE_ARRAY && type != TYPE_MAP) { - assert(0); // TODO - } - - Value len = make_int(state->a, value_length(set)); - if (len == VALUE_ERROR) { - assert(0); // TODO - } - - state->eval_stack[state->eval_depth++] = len; - } - break; - - default: - eval_report(state, "Invalid opcode (offset %d)", state->off-1); - return -1; - } - - return 0; -} - -WL_State *WL_State_init(WL_Arena *a, WL_Program p, char *err, int errmax) -{ - WL_State *state = alloc(a, (int) sizeof(WL_State), _Alignof(WL_State)); - if (state == NULL) - return NULL; - - String code; - String data; - - int ret = parse_program_header(p, &code, &data, err, errmax); - if (ret < 0) - return NULL; - - *state = (WL_State) { - .code=code, - .data=data, - .off=0, - .trace=false, - .a=a, - .errbuf=err, - .errmax=errmax, - .errlen=0, - .num_frames=0, - .eval_depth=0, - .num_groups=0, - .num_prints=0, - .cur_print=0, - }; - - state->frames[state->num_frames++] = (Frame) { 0, 0 }; - - return state; -} - -void WL_State_free(WL_State *state) -{ - state->num_frames--; - - // TODO -} - -void WL_State_trace(WL_State *state, int trace) -{ - state->trace = (trace != 0); -} - -WL_Result WL_eval(WL_State *state) -{ - if (state->sysvar.len > 0) { - - if (state->syscall_error) - return (WL_Result) { WL_ERROR, (WL_String) { NULL, 0 } }; - - state->sysvar = S(""); - } - - if (state->syscall.len > 0) { - - if (state->syscall_error) - return (WL_Result) { WL_ERROR, (WL_String) { NULL, 0 } }; - - int group = state->groups[state->num_groups-1]; - - Value v = state->eval_stack[--state->eval_depth]; - if (type_of(v) != TYPE_INT) { - assert(0); // TODO - } - int64_t num_rets = get_int(v); - for (int i = 0; i < num_rets; i++) - state->eval_stack[group + i] = state->eval_stack[state->eval_depth - num_rets + i]; - - state->eval_depth = group + num_rets; - - state->syscall = S(""); - } - - while (state->num_prints == 0) { - - int ret = step(state); - if (ret < 0) return (WL_Result) { WL_ERROR, (WL_String) { NULL, 0 } }; - if (ret == 1) return (WL_Result) { WL_DONE, (WL_String) { NULL, 0 } }; - - if (state->sysvar.len > 0) - return (WL_Result) { WL_VAR, (WL_String) { state->sysvar.ptr, state->sysvar.len } }; - - if (state->syscall.len > 0) - return (WL_Result) { WL_CALL, (WL_String) { state->syscall.ptr, state->syscall.len } }; - } - - Value v = state->eval_stack[state->eval_depth - state->num_prints + state->cur_print]; - - state->cur_print++; - if (state->cur_print == state->num_prints) { - state->cur_print = 0; - state->num_prints = 0; - } - - WL_String str; - - if (type_of(v) == TYPE_STRING) { - String str2 = get_str(v); - str.ptr = str2.ptr; - str.len = str2.len; - } else { - int cap = 8; - char *dst = alloc(state->a, cap, 1); - int len = value_to_string(v, dst, cap); - if (len > cap) { - if (!grow_alloc(state->a, dst, len)) { - assert(0); // TODO - } - value_to_string(v, dst, len); - } - str.ptr = dst; - str.len = len; - } - - return (WL_Result) { WL_OUTPUT, str }; -} - -static bool in_syscall(WL_State *state) -{ - return (state->syscall.len > 0 || state->sysvar.len > 0) && !state->syscall_error; -} - -int WL_peeknone(WL_State *state, int off) -{ - if (!in_syscall(state)) return 0; - - if (state->eval_depth + off < state->stack_base_for_user || off >= 0) - return 0; - - Value v = state->eval_stack[state->eval_depth + off]; - if (type_of(v) != TYPE_NONE) - return 0; - - return 1; -} - -int WL_peekint(WL_State *state, int off, long long *x) -{ - if (!in_syscall(state)) return 0; - - if (state->eval_depth + off < state->stack_base_for_user || off >= 0) - return 0; - - Value v = state->eval_stack[state->eval_depth + off]; - if (type_of(v) != TYPE_INT) - return 0; - - *x = get_int(v); - return 1; -} - -int WL_peekfloat(WL_State *state, int off, float *x) -{ - if (!in_syscall(state)) return 0; - - if (state->eval_depth + off < state->stack_base_for_user || off >= 0) - return 0; - - Value v = state->eval_stack[state->eval_depth + off]; - if (type_of(v) != TYPE_FLOAT) - return 0; - - *x = get_float(v); - return 1; -} - -int WL_peekstr(WL_State *state, int off, WL_String *str) -{ - if (!in_syscall(state)) return 0; - - if (state->eval_depth + off < state->stack_base_for_user || off >= 0) - return 0; - - Value v = state->eval_stack[state->eval_depth + off]; - if (type_of(v) != TYPE_STRING) - return 0; - - String s = get_str(v); - *str = (WL_String) { s.ptr, s.len }; - return 1; -} - -int WL_popnone(WL_State *state) -{ - if (!in_syscall(state)) return 0; - - if (state->eval_depth == state->stack_base_for_user) - return 0; - - Value v = state->eval_stack[state->eval_depth-1]; - if (type_of(v) != TYPE_NONE) - return 0; - - state->eval_depth--; - return 1; -} - -int WL_popint(WL_State *state, long long *x) -{ - if (!in_syscall(state)) return 0; - - if (state->eval_depth == state->stack_base_for_user) - return 0; - - Value v = state->eval_stack[state->eval_depth-1]; - if (type_of(v) != TYPE_INT) - return 0; - - *x = get_int(v); - - state->eval_depth--; - return 1; -} - -int WL_popfloat(WL_State *state, float *x) -{ - if (!in_syscall(state)) return 0; - - if (state->eval_depth == state->stack_base_for_user) - return 0; - - Value v = state->eval_stack[state->eval_depth-1]; - if (type_of(v) != TYPE_FLOAT) - return 0; - - *x = get_float(v); - - state->eval_depth--; - return 1; -} - -int WL_popstr(WL_State *state, WL_String *str) -{ - if (!in_syscall(state)) return 0; - - if (state->eval_depth == state->stack_base_for_user) - return 0; - - Value v = state->eval_stack[state->eval_depth-1]; - if (type_of(v) != TYPE_STRING) - return 0; - - String s = get_str(v); - *str = (WL_String) { s.ptr, s.len }; - - state->eval_depth--; - return 1; -} - -int WL_popany(WL_State *state) -{ - if (!in_syscall(state)) - return 0; - - if (state->eval_depth == state->stack_base_for_user) - return 0; - - state->eval_depth--; - return 1; -} - -void WL_select(WL_State *state) -{ - Value key = state->eval_stack[--state->eval_depth]; - Value set = state->eval_stack[state->eval_depth-1]; - Value val; - - Type set_type = type_of(set); - if (set_type == TYPE_ARRAY) { - - Type key_type = type_of(key); - if (key_type != TYPE_INT) { - assert(0); // TODO - } - - int64_t idx = get_int(key); - Value *src = array_select(set, idx); - if (src == NULL) { - assert(0); // TODO - } - val = *src; - - } else if (set_type == TYPE_MAP) { - - int ret = map_select(set, key, &val); - if (ret < 0) { - assert(0); // TODO - } - - } else { - - assert(0); // TODO - } - - state->eval_stack[state->eval_depth++] = val; -} - -void WL_pushnone(WL_State *state) -{ - if (!in_syscall(state)) return; - - state->eval_stack[state->eval_depth++] = VALUE_NONE; -} - -void WL_pushint(WL_State *state, long long x) -{ - if (!in_syscall(state)) return; - - Value v = make_int(state->a, x); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - state->syscall_error = true; - return; - } - - state->eval_stack[state->eval_depth++] = v; -} - -void WL_pushfloat(WL_State *state, float x) -{ - if (!in_syscall(state)) return; - - Value v = make_float(state->a, x); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - state->syscall_error = true; - return; - } - - state->eval_stack[state->eval_depth++] = v; -} - -void WL_pushstr(WL_State *state, WL_String str) -{ - if (!in_syscall(state)) return; - - Value v = make_str(state->a, (String) { str.ptr, str.len }); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - state->syscall_error = true; - return; - } - - state->eval_stack[state->eval_depth++] = v; -} - -void WL_pusharray(WL_State *state, int cap) -{ - if (!in_syscall(state)) return; - - (void) cap; - Value v = make_array(state->a); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - state->syscall_error = true; - return; - } - - state->eval_stack[state->eval_depth++] = v; -} - -void WL_pushmap(WL_State *state, int cap) -{ - if (!in_syscall(state)) return; - - (void) cap; - Value v = make_map(state->a); - if (v == VALUE_ERROR) { - eval_report(state, "Out of memory"); - state->syscall_error = true; - return; - } - - state->eval_stack[state->eval_depth++] = v; -} - -void WL_insert(WL_State *state) -{ - Value key = state->eval_stack[--state->eval_depth]; - Value val = state->eval_stack[--state->eval_depth]; - Value set = state->eval_stack[state->eval_depth-1]; - - Type set_type = type_of(set); - if (set_type == TYPE_ARRAY) { - - Type key_type = type_of(key); - if (key_type != TYPE_INT) { - assert(0); // TODO - } - - int64_t idx = get_int(key); - Value *dst = array_select(set, idx); - if (dst == NULL) { - assert(0); // TODO - } - *dst = val; - - } else if (set_type == TYPE_MAP) { - - int ret = map_insert(state->a, set, key, val); - if (ret < 0) { - assert(0); // TODO - } - - } else { - - assert(0); // TODO - } -} - -void WL_append(WL_State *state) -{ - Value val = state->eval_stack[--state->eval_depth]; - Value set = state->eval_stack[state->eval_depth-1]; - - if (type_of(set) != TYPE_ARRAY) { - assert(0); // TODO - return; - } - - if (array_append(state->a, set, val) < 0) { - assert(0); // TODO - } -} diff --git a/src/eval.h b/src/eval.h deleted file mode 100644 index c3edd6d..0000000 --- a/src/eval.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef WL_EVAL_INCLUDED -#define WL_EVAL_INCLUDED - -#ifndef WL_AMALGAMATION -#include "assemble.h" -#endif - -// TODO: pretty sure this is unused -int eval(WL_Program p, WL_Arena *a, char *errbuf, int errmax); - -#endif // WL_EVAL_INCLUDED diff --git a/src/file.c b/src/file.c deleted file mode 100644 index 048f026..0000000 --- a/src/file.c +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef WL_AMALGAMATION -#include "includes.h" -#include "file.h" -#endif - -int file_open(String path, File *handle, int *size) -{ - char zt[1<<10]; - if (path.len >= COUNT(zt)) - return -1; - memcpy(zt, path.ptr, path.len); - zt[path.len] = '\0'; - -#ifdef _WIN32 - *handle = CreateFileA( - zt, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - if (*handle == INVALID_HANDLE_VALUE) { - DWORD error = GetLastError(); - if (error == ERROR_FILE_NOT_FOUND || - error == ERROR_ACCESS_DENIED) - return 1; - return -1; - } - LARGE_INTEGER fileSize; - if (!GetFileSizeEx(*handle, &fileSize)) { - CloseHandle(*handle); - return -1; - } - if (fileSize.QuadPart > INT_MAX) { - CloseHandle(*handle); - return -1; - } - *size = (int) fileSize.QuadPart; -#else - *handle = open(zt, O_RDONLY); - if (*handle < 0) { - if (errno == ENOENT) - return 1; - return -1; - } - struct stat info; - if (fstat(*handle, &info) < 0) { - close(*handle); - return -1; - } - if (S_ISDIR(info.st_mode)) { - close(*handle); - return 1; - } - if (info.st_size > INT_MAX) { - close(*handle); - return -1; - } - *size = (int) info.st_size; -#endif - return 0; -} - -void file_close(File file) -{ -#ifdef _WIN32 - CloseHandle(file); -#else - close(file); -#endif -} - -int file_read(File file, char *dst, int max) -{ -#ifdef _WIN32 - DWORD num; - BOOL ok = ReadFile(file, dst, max, &num, NULL); - if (!ok) - return -1; - return (int) num; -#else - return read(file, dst, max); -#endif -} - -int file_read_all(String path, String *dst) -{ - int len; - File handle; - if (file_open(path, &handle, &len) < 0) - return -1; - - char *ptr = malloc(len+1); - if (ptr == NULL) { - file_close(handle); - return -1; - } - - for (int copied = 0; copied < len; ) { - int ret = file_read(handle, ptr + copied, len - copied); - if (ret <= 0) { - free(ptr); - file_close(handle); - return -1; - } - copied += ret; - } - - *dst = (String) { ptr, len }; - file_close(handle); - return 0; -} diff --git a/src/file.h b/src/file.h deleted file mode 100644 index 3663dc5..0000000 --- a/src/file.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef WL_FILE_INCLUDED -#define WL_FILE_INCLUDED - -#ifndef WL_AMALGAMATION -#include "includes.h" -#include "basic.h" -#endif - -#ifdef _WIN32 -typedef HANDLE File; -#else -typedef int File; -#endif - -int file_open(String path, File *handle, int *size); -void file_close(File file); -int file_read(File file, char *dst, int max); -int file_read_all(String path, String *dst); - -#endif // WL_FILE_INCLUDED \ No newline at end of file diff --git a/src/includes.h b/src/includes.h deleted file mode 100644 index 21a41cf..0000000 --- a/src/includes.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef WL_INCLUDES_INCLUDED -#define WL_INCLUDES_INCLUDED - -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#endif - -#ifndef _WIN32 -#include -#include -#include -#include -#include -#endif - -#endif // WL_INCLUDES_INCLUDED \ No newline at end of file diff --git a/src/parse.c b/src/parse.c deleted file mode 100644 index b67260a..0000000 --- a/src/parse.c +++ /dev/null @@ -1,1958 +0,0 @@ - -#ifndef WL_AMALGAMATION -#include "parse.h" -#endif - -typedef struct { - char *src; - int len; - int cur; -} Scanner; - -typedef enum { - TOKEN_END, - TOKEN_ERROR, - TOKEN_IDENT, - TOKEN_KWORD_IF, - TOKEN_KWORD_ELSE, - TOKEN_KWORD_WHILE, - TOKEN_KWORD_FOR, - TOKEN_KWORD_IN, - TOKEN_KWORD_FUN, - TOKEN_KWORD_LET, - TOKEN_KWORD_PRINT, - TOKEN_KWORD_NONE, - TOKEN_KWORD_TRUE, - TOKEN_KWORD_FALSE, - TOKEN_KWORD_INCLUDE, - TOKEN_KWORD_LEN, - TOKEN_VALUE_FLOAT, - TOKEN_VALUE_INT, - TOKEN_VALUE_STR, - TOKEN_OPER_EQL, - TOKEN_OPER_NQL, - TOKEN_OPER_LSS, - TOKEN_OPER_GRT, - TOKEN_OPER_ADD, - TOKEN_OPER_SUB, - TOKEN_OPER_MUL, - TOKEN_OPER_DIV, - TOKEN_OPER_MOD, - TOKEN_OPER_ASS, - TOKEN_PAREN_OPEN, - TOKEN_PAREN_CLOSE, - TOKEN_BRACKET_OPEN, - TOKEN_BRACKET_CLOSE, - TOKEN_CURLY_OPEN, - TOKEN_CURLY_CLOSE, - TOKEN_DOT, - TOKEN_COMMA, - TOKEN_COLON, - TOKEN_DOLLAR, - TOKEN_NEWLINE, -} TokType; - -typedef struct { - TokType type; - union { - int64_t ival; - uint64_t uval; - double dval; - String sval; - }; -} Token; - -typedef struct { - Scanner s; - WL_Arena* a; - char* errbuf; - int errmax; - int errlen; - Node* include_head; - Node** include_tail; -} Parser; - -bool consume_str(Scanner *s, String x) -{ - if (x.len == 0) - return false; - - if (x.len > s->len - s->cur) - return false; - - for (int i = 0; i < x.len; i++) - if (s->src[s->cur+i] != x.ptr[i]) - return false; - - s->cur += x.len; - return true; -} - -String tok2str(Token token, char *buf, int max) -{ - switch (token.type) { - - case TOKEN_END: - return S("EOF"); - - case TOKEN_ERROR: - return S("ERROR"); - - case TOKEN_IDENT: - { - int len = snprintf(buf, max, "%.*s", token.sval.len, token.sval.ptr); - return (String) { buf, len }; - } - break; - - case TOKEN_KWORD_IF: return S("if"); - case TOKEN_KWORD_ELSE: return S("else"); - case TOKEN_KWORD_WHILE: return S("while"); - case TOKEN_KWORD_FOR: return S("for"); - case TOKEN_KWORD_IN: return S("in"); - case TOKEN_KWORD_FUN: return S("fun"); - case TOKEN_KWORD_LET: return S("let"); - case TOKEN_KWORD_PRINT: return S("print"); - case TOKEN_KWORD_NONE: return S("none"); - case TOKEN_KWORD_TRUE: return S("true"); - case TOKEN_KWORD_FALSE: return S("false"); - case TOKEN_KWORD_INCLUDE: return S("include"); - case TOKEN_KWORD_LEN: return S("len"); - - case TOKEN_VALUE_FLOAT: - { - int len = snprintf(buf, max, "%lf", token.dval); - return (String) { buf, len }; - } - break; - - case TOKEN_VALUE_INT: - { - int len = snprintf(buf, max, "%" LLU, token.uval); - return (String) { buf, len }; - } - break; - - case TOKEN_VALUE_STR: - { - int len = snprintf(buf, max, "\"%.*s\"", token.sval.len, token.sval.ptr); - return (String) { buf, len }; - } - break; - - case TOKEN_OPER_ASS: return S("=="); - case TOKEN_OPER_EQL: return S("=="); - case TOKEN_OPER_NQL: return S("!="); - case TOKEN_OPER_LSS: return S("<"); - case TOKEN_OPER_GRT: return S(">"); - case TOKEN_OPER_ADD: return S("+"); - case TOKEN_OPER_SUB: return S("-"); - case TOKEN_OPER_MUL: return S("*"); - case TOKEN_OPER_DIV: return S("/"); - case TOKEN_OPER_MOD: return S("%"); - - case TOKEN_PAREN_OPEN: return S("("); - case TOKEN_PAREN_CLOSE: return S(")"); - - case TOKEN_BRACKET_OPEN: return S("["); - case TOKEN_BRACKET_CLOSE: return S("]"); - - case TOKEN_CURLY_OPEN: return S("{"); - case TOKEN_CURLY_CLOSE: return S("}"); - - case TOKEN_DOT: return S("."); - case TOKEN_COMMA: return S(","); - case TOKEN_COLON: return S(":"); - case TOKEN_DOLLAR: return S("$"); - - case TOKEN_NEWLINE: return S("\\n"); - } - - return S("???"); -} - -void parser_report(Parser *p, char *fmt, ...) -{ - if (p->errmax == 0 || p->errlen > 0) - return; - - int line = 1; - int cur = 0; - while (cur < p->s.cur) { - if (p->s.src[cur] == '\n') - line++; - cur++; - } - - int len = snprintf(p->errbuf, p->errmax, "Error (line %d): ", line); - if (len < 0) { - // TODO - } - - va_list args; - va_start(args, fmt); - int ret = vsnprintf(p->errbuf + len, p->errmax - len, fmt, args); - va_end(args); - if (ret < 0) { - // TODO - } - len += ret; - - p->errlen = len; -} - -Node *alloc_node(Parser *p) -{ - Node *n = alloc(p->a, sizeof(Node), _Alignof(Node)); - if (n == NULL) { - parser_report(p, "Out of memory"); - return NULL; - } - - return n; -} - -Token next_token(Parser *p) -{ - for (;;) { - while (p->s.cur < p->s.len && is_space(p->s.src[p->s.cur])) - p->s.cur++; - - if (!consume_str(&p->s, S(""))) - break; - p->s.cur++; - } - } - - if (p->s.cur == p->s.len) - return (Token) { .type=TOKEN_END }; - char c = p->s.src[p->s.cur]; - - if (is_alpha(c) || c == '_') { - - int start = p->s.cur; - do - p->s.cur++; - while (p->s.cur < p->s.len && (is_alpha(p->s.src[p->s.cur]) || is_digit(p->s.src[p->s.cur]) || p->s.src[p->s.cur] == '_')); - - String kword = { - p->s.src + start, - p->s.cur - start - }; - - if (streq(kword, S("if"))) return (Token) { .type=TOKEN_KWORD_IF }; - if (streq(kword, S("else"))) return (Token) { .type=TOKEN_KWORD_ELSE }; - if (streq(kword, S("while"))) return (Token) { .type=TOKEN_KWORD_WHILE }; - if (streq(kword, S("for"))) return (Token) { .type=TOKEN_KWORD_FOR }; - if (streq(kword, S("in"))) return (Token) { .type=TOKEN_KWORD_IN }; - if (streq(kword, S("fun"))) return (Token) { .type=TOKEN_KWORD_FUN }; - if (streq(kword, S("let"))) return (Token) { .type=TOKEN_KWORD_LET }; - if (streq(kword, S("print"))) return (Token) { .type=TOKEN_KWORD_PRINT }; - if (streq(kword, S("none"))) return (Token) { .type=TOKEN_KWORD_NONE }; - if (streq(kword, S("true"))) return (Token) { .type=TOKEN_KWORD_TRUE }; - if (streq(kword, S("false"))) return (Token) { .type=TOKEN_KWORD_FALSE }; - if (streq(kword, S("include"))) return (Token) { .type=TOKEN_KWORD_INCLUDE }; - if (streq(kword, S("len"))) return (Token) { .type=TOKEN_KWORD_LEN }; - - return (Token) { .type=TOKEN_IDENT, .sval=kword }; - } - - if (is_digit(c)) { - - int peek = p->s.cur; - do - peek++; - while (peek < p->s.len && is_digit(p->s.src[peek])); - - if (p->s.len - peek > 1 && p->s.src[peek] == '.' && is_digit(p->s.src[peek+1])) { - - double buf = 0; - do { - int d = p->s.src[p->s.cur++] - '0'; - buf = buf * 10 + d; - } while (p->s.cur < p->s.len && p->s.src[p->s.cur] != '.'); - - p->s.cur++; - - double q = 1; - do { - int d = p->s.src[p->s.cur++] - '0'; - q /= 10; - buf += q * d; - } while (p->s.cur < p->s.len && is_digit(p->s.src[p->s.cur])); - - return (Token) { .type=TOKEN_VALUE_FLOAT, .dval=buf }; - - } else { - - uint64_t buf = 0; - do { - int d = p->s.src[p->s.cur++] - '0'; - if (buf > (UINT64_MAX - d) / 10) { - parser_report(p, "Integer literal overflow"); - return (Token) { .type=TOKEN_ERROR }; - } - buf = buf * 10 + d; - } while (p->s.cur < p->s.len && is_digit(p->s.src[p->s.cur])); - - return (Token) { .type=TOKEN_VALUE_INT, .uval=buf }; - } - } - - if (c == '\'' || c == '"') { - - char f = c; - p->s.cur++; - - char *buf = NULL; - int len = 0; - - for (;;) { - - int substr_off = p->s.cur; - - while (p->s.cur < p->s.len && is_printable(p->s.src[p->s.cur]) && p->s.src[p->s.cur] != f && p->s.src[p->s.cur] != '\\') - p->s.cur++; - - int substr_len = p->s.cur - substr_off; - - if (buf == NULL) - buf = alloc(p->a, substr_len+1, 1); - else - if (!grow_alloc(p->a, buf, len + substr_len+1)) - buf = NULL; - - if (buf == NULL) { - parser_report(p, "Out of memory"); - return (Token) { .type=TOKEN_ERROR }; - } - - if (substr_len > 0) { - memcpy( - buf + len, - p->s.src + substr_off, - p->s.cur - substr_off - ); - len += substr_len; - } - - if (p->s.cur == p->s.len) { - parser_report(p, "String literal wasn't closed"); - return (Token) { .type=TOKEN_ERROR }; - } - - if (!is_printable(p->s.src[p->s.cur])) { - parser_report(p, "Invalid byte in string literal"); - return (Token) { .type=TOKEN_ERROR }; - } - - if (p->s.src[p->s.cur] == f) - break; - - p->s.cur++; - if (p->s.cur == p->s.len) { - parser_report(p, "Missing special character after escape character \\"); - return (Token) { .type=TOKEN_ERROR }; - } - - switch (p->s.src[p->s.cur]) { - case 'n': buf[len++] = '\n'; break; - case 't': buf[len++] = '\t'; break; - case 'r': buf[len++] = '\r'; break; - case '"': buf[len++] = '"'; break; - case '\'': buf[len++] = '\''; break; - case '\\': buf[len++] = '\\'; break; - - case 'x': - { - if (p->s.len - p->s.cur < 3 - || !is_hex_digit(p->s.src[p->s.cur+1]) - || !is_hex_digit(p->s.src[p->s.cur+2])) - return (Token) { .type=TOKEN_ERROR }; - buf[len++] - = (hex_digit_to_int(p->s.src[p->s.cur+1]) << 4) - | (hex_digit_to_int(p->s.src[p->s.cur+2]) << 0); - p->s.cur += 2; - } - break; - - default: - parser_report(p, "Invalid character after escape character \\"); - return (Token) { .type=TOKEN_ERROR }; - } - - p->s.cur++; - } - - p->s.cur++; - return (Token) { .type=TOKEN_VALUE_STR, .sval=(String) { .ptr=buf, .len=len } }; - } - - if (consume_str(&p->s, S("=="))) return (Token) { .type=TOKEN_OPER_EQL }; - if (consume_str(&p->s, S("!="))) return (Token) { .type=TOKEN_OPER_NQL }; - if (consume_str(&p->s, S("<"))) return (Token) { .type=TOKEN_OPER_LSS }; - if (consume_str(&p->s, S(">"))) return (Token) { .type=TOKEN_OPER_GRT }; - if (consume_str(&p->s, S("+"))) return (Token) { .type=TOKEN_OPER_ADD }; - if (consume_str(&p->s, S("-"))) return (Token) { .type=TOKEN_OPER_SUB }; - if (consume_str(&p->s, S("*"))) return (Token) { .type=TOKEN_OPER_MUL }; - if (consume_str(&p->s, S("/"))) return (Token) { .type=TOKEN_OPER_DIV }; - if (consume_str(&p->s, S("%"))) return (Token) { .type=TOKEN_OPER_MOD }; - if (consume_str(&p->s, S("="))) return (Token) { .type=TOKEN_OPER_ASS }; - - if (consume_str(&p->s, S("("))) return (Token) { .type=TOKEN_PAREN_OPEN }; - if (consume_str(&p->s, S(")"))) return (Token) { .type=TOKEN_PAREN_CLOSE }; - if (consume_str(&p->s, S("["))) return (Token) { .type=TOKEN_BRACKET_OPEN }; - if (consume_str(&p->s, S("]"))) return (Token) { .type=TOKEN_BRACKET_CLOSE }; - if (consume_str(&p->s, S("{"))) return (Token) { .type=TOKEN_CURLY_OPEN }; - if (consume_str(&p->s, S("}"))) return (Token) { .type=TOKEN_CURLY_CLOSE }; - if (consume_str(&p->s, S("."))) return (Token) { .type=TOKEN_DOT }; - if (consume_str(&p->s, S(","))) return (Token) { .type=TOKEN_COMMA }; - if (consume_str(&p->s, S(":"))) return (Token) { .type=TOKEN_COLON }; - if (consume_str(&p->s, S("$"))) return (Token) { .type=TOKEN_DOLLAR }; - - parser_report(p, "Invalid character '%c'", c); - return (Token) { .type=TOKEN_ERROR }; -} - -Token next_token_or_newline(Parser *p) -{ - int peek = p->s.cur; - while (peek < p->s.len && is_space(p->s.src[peek]) && p->s.src[peek] != '\n') - peek++; - - if (peek < p->s.len && p->s.src[peek] == '\n') { - p->s.cur = peek+1; - return (Token) { .type=TOKEN_NEWLINE }; - } - - return next_token(p); -} - -enum { - IGNORE_GRT = 1 << 0, - IGNORE_LSS = 1 << 1, - IGNORE_DIV = 1 << 2, -}; - -Node *parse_stmt(Parser *p, int opflags); -Node *parse_expr(Parser *p, int opflags); - -Node *parse_html(Parser *p) -{ - // NOTE: The first < was already consumed - - Token t = next_token(p); - if (t.type != TOKEN_IDENT) { - char buf[1<<8]; - String ts = tok2str(t, buf, COUNT(buf)); - parser_report(p, "HTML tag doesn't start with a name (got '%.*s' instead)", ts.len, ts.ptr); - return NULL; - } - String tagname = t.sval; - - Node *param_head; - Node **param_tail = ¶m_head; - - bool no_body = false; - for (;;) { - - String attr_name; - Node *attr_value; - - t = next_token(p); - - if (t.type == TOKEN_OPER_GRT) - break; - - if (t.type == TOKEN_OPER_DIV) { - t = next_token(p); - if (t.type != TOKEN_OPER_GRT) { - parser_report(p, "Invalid token '/' inside an HTML tag"); - return NULL; - } - no_body = true; - break; - } - - if (t.type != TOKEN_IDENT) { - parser_report(p, "Invalid token inside HTML tag"); - return NULL; - } - attr_name = t.sval; - - Scanner saved = p->s; - t = next_token(p); - if (t.type == TOKEN_OPER_ASS) { - - attr_value = parse_expr(p, IGNORE_GRT | IGNORE_DIV); - if (attr_value == NULL) - return NULL; - - } else { - p->s = saved; - attr_value = NULL; - } - - Node *child = alloc_node(p); - if (child == NULL) - return NULL; - - child->type = NODE_HTML_PARAM; - child->attr_name = attr_name; - child->attr_value = attr_value; - - *param_tail = child; - param_tail = &child->next; - } - - *param_tail = NULL; - - Node *head; - Node **tail = &head; - - if (!no_body) for (;;) { - - for (;;) { - - int off = p->s.cur; - - for (;;) { - - while (p->s.cur < p->s.len && p->s.src[p->s.cur] != '<' && p->s.src[p->s.cur] != '\\') - p->s.cur++; - - if (!consume_str(&p->s, S(""))) - break; - p->s.cur++; - } - } - - if (p->s.cur > off) { - - Node *child = alloc_node(p); - if (child == NULL) - return NULL; - - child->type = NODE_VALUE_STR; - child->sval = (String) { p->s.src + off, p->s.cur - off }; - - *tail = child; - tail = &child->next; - } - - if (p->s.cur == p->s.len || p->s.src[p->s.cur] == '<') - break; - - p->s.cur++; // Consume "\" - - { - Node *child = parse_stmt(p, IGNORE_LSS); - if (child == NULL) - return NULL; - - *tail = child; - tail = &child->next; - } - } - - if (p->s.cur == p->s.len) { - parser_report(p, "Missing closing HTML tag"); - return NULL; - } - p->s.cur++; // Consume < - - Scanner saved = p->s; - t = next_token(p); - if (t.type == TOKEN_OPER_DIV) { - t = next_token(p); - if (t.type == TOKEN_IDENT && streqcase(t.sval, tagname)) { - t = next_token(p); - if (t.type != TOKEN_OPER_GRT) { - parser_report(p, "Unexpected token in closing HTML tag"); - return NULL; - } - break; - } - } - - p->s = saved; - - Node *child = parse_html(p); - if (child == NULL) - return NULL; - - *tail = child; - tail = &child->next; - } - - *tail = NULL; - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->type = NODE_VALUE_HTML; - parent->tagname = tagname; - parent->params = param_head; - parent->child = head; - parent->no_body = no_body; - - return parent; -} - -Node *parse_array(Parser *p) -{ - // Left bracket already consumed - - Node *head; - Node **tail = &head; - - Scanner saved = p->s; - Token t = next_token(p); - if (t.type != TOKEN_BRACKET_CLOSE) { - - p->s = saved; - - for (;;) { - - Node *child = parse_expr(p, 0); - if (child == NULL) - return NULL; - - *tail = child; - tail = &child->next; - - saved = p->s; - t = next_token(p); - if (t.type == TOKEN_COMMA) { - saved = p->s; - t = next_token(p); - } - - if (t.type == TOKEN_BRACKET_CLOSE) - break; - - p->s = saved; - } - } - - *tail = NULL; - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->type = NODE_VALUE_ARRAY; - parent->child = head; - - return parent; -} - -Node *parse_map(Parser *p) -{ - // Left bracket already consumed - - Node *head; - Node **tail = &head; - - Scanner saved = p->s; - Token t = next_token(p); - if (t.type != TOKEN_CURLY_CLOSE) { - - p->s = saved; - - for (;;) { - - Node *key; - - saved = p->s; - t = next_token(p); - if (t.type == TOKEN_IDENT) { - - key = alloc_node(p); - if (key == NULL) - return NULL; - - key->type = NODE_VALUE_STR; - key->sval = t.sval; - - } else { - - p->s = saved; - key = parse_expr(p, 0); - if (key == NULL) - return NULL; - } - - t = next_token(p); - if (t.type != TOKEN_COLON) { - parser_report(p, "Missing ':' after key inside map literal"); - return NULL; - } - - Node *child = parse_expr(p, 0); - if (child == NULL) - return NULL; - child->key = key; - - *tail = child; - tail = &child->next; - - saved = p->s; - t = next_token(p); - if (t.type == TOKEN_COMMA) { - saved = p->s; - t = next_token(p); - } - - if (t.type == TOKEN_CURLY_CLOSE) - break; - - p->s = saved; - } - } - - *tail = NULL; - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->type = NODE_VALUE_MAP; - parent->child = head; - - return parent; -} - -int precedence(Token t, int flags) -{ - switch (t.type) { - - case TOKEN_OPER_ASS: - return 1; - - case TOKEN_OPER_EQL: - case TOKEN_OPER_NQL: - return 2; - - case TOKEN_OPER_LSS: - if (flags & IGNORE_LSS) - return -1; - return 2; - - case TOKEN_OPER_GRT: - if (flags & IGNORE_GRT) - return -1; - return 2; - - case TOKEN_OPER_ADD: - case TOKEN_OPER_SUB: - return 3; - - case TOKEN_OPER_MUL: - case TOKEN_OPER_MOD: - return 4; - - case TOKEN_OPER_DIV: - if (flags & IGNORE_DIV) - return -1; - return 4; - - default: - break; - } - - return -1; -} - -bool right_associative(Token t) -{ - return t.type == TOKEN_OPER_ASS; -} - -Node *parse_atom(Parser *p) -{ - Token t = next_token(p); - - Node *ret; - switch (t.type) { - case TOKEN_OPER_ADD: - { - Node *child = parse_atom(p); - if (child == NULL) - return NULL; - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->type = NODE_OPER_POS; - parent->left = child; - - ret = parent; - } - break; - - case TOKEN_OPER_SUB: - { - Node *child = parse_atom(p); - if (child == NULL) - return NULL; - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->type = NODE_OPER_NEG; - parent->left = child; - - ret = parent; - } - break; - - case TOKEN_KWORD_LEN: - { - Node *child = parse_atom(p); - if (child == NULL) - return NULL; - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->type = NODE_OPER_LEN; - parent->left = child; - - ret = parent; - } - break; - - case TOKEN_IDENT: - { - Node *node = alloc_node(p); - if (node == NULL) - return NULL; - - node->type = NODE_VALUE_VAR; - node->sval = t.sval; - - ret = node; - } - break; - - case TOKEN_VALUE_INT: - { - Node *node = alloc_node(p); - if (node == NULL) - return NULL; - - node->type = NODE_VALUE_INT; - node->ival = t.uval; - - ret = node; - } - break; - - case TOKEN_VALUE_FLOAT: - { - Node *node = alloc_node(p); - if (node == NULL) - return NULL; - - node->type = NODE_VALUE_FLOAT; - node->dval = t.dval; - - ret = node; - } - break; - - case TOKEN_VALUE_STR: - { - Node *node = alloc_node(p); - if (node == NULL) - return NULL; - - node->type = NODE_VALUE_STR; - node->sval = t.sval; - - ret = node; - } - break; - - case TOKEN_KWORD_NONE: - { - Node *node = alloc_node(p); - if (node == NULL) - return NULL; - - node->type = NODE_VALUE_NONE; - node->sval = t.sval; - - ret = node; - } - break; - - case TOKEN_KWORD_TRUE: - { - Node *node = alloc_node(p); - if (node == NULL) - return NULL; - - node->type = NODE_VALUE_TRUE; - node->sval = t.sval; - - ret = node; - } - break; - case TOKEN_KWORD_FALSE: - { - Node *node = alloc_node(p); - if (node == NULL) - return NULL; - - node->type = NODE_VALUE_FALSE; - node->sval = t.sval; - - ret = node; - } - break; - - case TOKEN_OPER_LSS: - { - Node *node = parse_html(p); - if (node == NULL) - return NULL; - - ret = node; - } - break; - - case TOKEN_PAREN_OPEN: - { - Node *node = parse_expr(p, 0); - if (node == NULL) - return NULL; - - Token t = next_token(p); - if (t.type != TOKEN_PAREN_CLOSE) { - parser_report(p, "Missing ')' after expression"); - return NULL; - } - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->type = NODE_NESTED; - parent->left = node; - - ret = parent; - } - break; - - case TOKEN_BRACKET_OPEN: - { - Node *node = parse_array(p); - if (node == NULL) - return NULL; - - ret = node; - } - break; - - case TOKEN_CURLY_OPEN: - { - Node *node = parse_map(p); - if (node == NULL) - return NULL; - - ret = node; - } - break; - - case TOKEN_DOLLAR: - { - t = next_token(p); - if (t.type != TOKEN_IDENT) { - parser_report(p, "Missing identifier after '$'"); - return NULL; - } - - Node *node = alloc_node(p); - if (node == NULL) - return NULL; - - node->type = NODE_VALUE_SYSVAR; - node->sval = t.sval; - - ret = node; - } - break; - - default: - { - char buf[1<<8]; - String str = tok2str(t, buf, COUNT(buf)); - parser_report(p, "Invalid token \'%.*s\' inside expression", str.len, str.ptr); - } - return NULL; - } - - for (;;) { - Scanner saved = p->s; - t = next_token(p); - if (t.type == TOKEN_DOT) { - - t = next_token(p); - if (t.type != TOKEN_IDENT) { - parser_report(p, "Invalid token after '.' where an identifier was expected"); - return NULL; - } - - Node *child = alloc_node(p); - if (child == NULL) - return NULL; - - child->type = NODE_VALUE_STR; - child->sval = t.sval; - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->type = NODE_SELECT; - parent->left = ret; - parent->right = child; - - ret = parent; - - } else if (t.type == TOKEN_BRACKET_OPEN) { - - Node *child = parse_expr(p, 0); - if (child == NULL) - return NULL; - - t = next_token(p); - if (t.type != TOKEN_BRACKET_CLOSE) { - parser_report(p, "Missing token ']'"); - return NULL; - } - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->type = NODE_SELECT; - parent->left = ret; - parent->right = child; - - ret = parent; - - } else if (t.type == TOKEN_PAREN_OPEN && (ret->type == NODE_VALUE_VAR || ret->type == NODE_VALUE_SYSVAR)) { - - Node *arg_head; - Node **arg_tail = &arg_head; - - Scanner saved = p->s; - t = next_token(p); - if (t.type != TOKEN_PAREN_CLOSE) { - - p->s = saved; - - for (;;) { - - Node *argval = parse_expr(p, 0); - if (argval == NULL) - return NULL; - - *arg_tail = argval; - arg_tail = &argval->next; - - t = next_token(p); - if (t.type == TOKEN_PAREN_CLOSE) - break; - - if (t.type != TOKEN_COMMA) { - parser_report(p, "Expected ',' after argument in function call"); - return NULL; - } - } - } - - *arg_tail = NULL; - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->type = NODE_FUNC_CALL; - parent->left = ret; - parent->right = arg_head; - - ret = parent; - - } else { - p->s = saved; - break; - } - } - - return ret; -} - -Node *parse_expr_inner(Parser *p, Node *left, int min_prec, int flags) -{ - for (;;) { - - Scanner saved = p->s; - Token t1 = next_token_or_newline(p); - if (precedence(t1, flags) < min_prec) { - p->s = saved; - break; - } - - Node *right = parse_atom(p); - if (right == NULL) - return NULL; - - for (;;) { - - saved = p->s; - Token t2 = next_token_or_newline(p); - int p1 = precedence(t1, flags); - int p2 = precedence(t2, flags); - p->s = saved; - - if (p2 < 0) - break; - - if (p2 <= p1 && (p1 != p2 || !right_associative(t2))) - break; - - right = parse_expr_inner(p, right, p1 + (p2 > p1), flags); - if (right == NULL) - return NULL; - } - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->left = left; - parent->right = right; - - switch (t1.type) { - case TOKEN_OPER_ASS: parent->type = NODE_OPER_ASS; break; - case TOKEN_OPER_EQL: parent->type = NODE_OPER_EQL; break; - case TOKEN_OPER_NQL: parent->type = NODE_OPER_NQL; break; - case TOKEN_OPER_LSS: parent->type = NODE_OPER_LSS; break; - case TOKEN_OPER_GRT: parent->type = NODE_OPER_GRT; break; - case TOKEN_OPER_ADD: parent->type = NODE_OPER_ADD; break; - case TOKEN_OPER_SUB: parent->type = NODE_OPER_SUB; break; - case TOKEN_OPER_MUL: parent->type = NODE_OPER_MUL; break; - case TOKEN_OPER_DIV: parent->type = NODE_OPER_DIV; break; - case TOKEN_OPER_MOD: parent->type = NODE_OPER_MOD; break; - default: - parser_report(p, "Operator not implemented"); - return NULL; - } - - left = parent; - } - - return left; -} - -Node *parse_expr(Parser *p, int flags) -{ - Node *left = parse_atom(p); - if (left == NULL) - return NULL; - - return parse_expr_inner(p, left, 0, flags); -} - -Node *parse_expr_stmt(Parser *p, int opflags) -{ - Node *e = parse_expr(p, opflags); - if (e == NULL) - return NULL; - - return e; -} - -Node *parse_ifelse_stmt(Parser *p, int opflags) -{ - Token t = next_token(p); - if (t.type != TOKEN_KWORD_IF) { - parser_report(p, "Missing 'if' keyword before if statement"); - return NULL; - } - - Node *cond = parse_expr(p, 0); - if (cond == NULL) - return NULL; - - t = next_token(p); - if (t.type != TOKEN_COLON) { - parser_report(p, "Missing ':' after if condition"); - return NULL; - } - - Node *if_stmt = parse_stmt(p, opflags); - if (if_stmt == NULL) - return NULL; - - Scanner saved = p->s; - t = next_token(p); - - Node *else_stmt = NULL; - if (t.type == TOKEN_KWORD_ELSE) { - - else_stmt = parse_stmt(p, opflags); - if (else_stmt == NULL) - return NULL; - - } else { - p->s = saved; - } - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->type = NODE_IFELSE; - parent->left = if_stmt; - parent->right = else_stmt; - parent->cond = cond; - - return parent; -} - -Node *parse_for_stmt(Parser *p, int opflags) -{ - Token t = next_token(p); - if (t.type != TOKEN_KWORD_FOR) { - parser_report(p, "Missing 'for' keyword at the start of a for statement"); - return NULL; - } - - t = next_token(p); - if (t.type != TOKEN_IDENT) { - parser_report(p, "Missing iteraion variable name in for statement"); - return NULL; - } - String var1 = t.sval; - - t = next_token(p); - - String var2 = S(""); - if (t.type == TOKEN_COMMA) { - - t = next_token(p); - if (t.type != TOKEN_IDENT) { - parser_report(p, "Missing iteration variable name after ',' in for statement"); - return NULL; - } - var2 = t.sval; - - t = next_token(p); - } - - if (t.type != TOKEN_KWORD_IN) { - parser_report(p, "Missing 'in' keyword after iteration variable name in for statement"); - return NULL; - } - - Node *set = parse_expr(p, 0); - if (set == NULL) - return NULL; - - t = next_token(p); - if (t.type != TOKEN_COLON) { - parser_report(p, "Missing ':' after for statement set expression"); - return NULL; - } - - Node *body = parse_stmt(p, opflags); - if (body == NULL) - return NULL; - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->type = NODE_FOR; - parent->left = body; - parent->for_var1 = var1; - parent->for_var2 = var2; - parent->for_set = set; - - return parent; -} - -Node *parse_while_stmt(Parser *p, int opflags) -{ - Token t = next_token(p); - if (t.type != TOKEN_KWORD_WHILE) { - parser_report(p, "Missing keyword 'while' at the start of a while statement"); - return NULL; - } - - Node *cond = parse_expr(p, 0); - if (cond == NULL) - return NULL; - - t = next_token(p); - if (t.type != TOKEN_COLON) { - parser_report(p, "Missing token ':' after while statement condition"); - return NULL; - } - - Node *stmt = parse_stmt(p, opflags); - if (stmt == NULL) - return NULL; - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->type = NODE_WHILE; - parent->left = stmt; - parent->cond = cond; - - return parent; -} - -Node *parse_block_stmt(Parser *p, bool curly) -{ - if (curly) { - Token t = next_token(p); - if (t.type != TOKEN_CURLY_OPEN) { - parser_report(p, "Missing '{' at the start of a block statement"); - return NULL; - } - } - - Node *head; - Node **tail = &head; - - for (;;) { - - Scanner saved = p->s; - Token t = next_token(p); - if (curly) { - if (t.type == TOKEN_CURLY_CLOSE) - break; - } else { - if (t.type == TOKEN_END) - break; - } - p->s = saved; - - Node *node = parse_stmt(p, 0); - if (node == NULL) - return NULL; - - *tail = node; - tail = &node->next; - } - - *tail = NULL; - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->type = NODE_BLOCK; - parent->left = head; - - return parent; -} - -Node *parse_func_decl(Parser *p, int opflags) -{ - Token t = next_token(p); - if (t.type != TOKEN_KWORD_FUN) { - parser_report(p, "Missing keyword 'fun' at the start of a function declaration"); - return NULL; - } - - t = next_token(p); - if (t.type != TOKEN_IDENT) { - parser_report(p, "Missing function name after 'fun' keyword"); - return NULL; - } - String name = t.sval; - - t = next_token(p); - if (t.type != TOKEN_PAREN_OPEN) { - parser_report(p, "Missing '(' after function name in declaration"); - return NULL; - } - - Node *arg_head; - Node **arg_tail = &arg_head; - - Scanner saved = p->s; - t = next_token(p); - if (t.type != TOKEN_PAREN_CLOSE) { - p->s = saved; - - for (;;) { - - t = next_token(p); - if (t.type != TOKEN_IDENT) { - parser_report(p, "Missing argument name in function declaration"); - return NULL; - } - String argname = t.sval; - - Node *node = alloc_node(p); - if (node == NULL) - return NULL; - - node->type = NODE_FUNC_ARG; - node->sval = argname; - - *arg_tail = node; - arg_tail = &node->next; - - Scanner saved = p->s; - t = next_token(p); - if (t.type == TOKEN_COMMA) { - saved = p->s; - t = next_token(p); - } - - if (t.type == TOKEN_PAREN_CLOSE) - break; - p->s = saved; - } - } - - *arg_tail = NULL; - - Node *body = parse_stmt(p, opflags); - if (body == NULL) - return NULL; - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->type = NODE_FUNC_DECL; - parent->func_name = name; - parent->func_args = arg_head; - parent->func_body = body; - - return parent; -} - -Node *parse_var_decl(Parser *p, int opflags) -{ - Token t = next_token(p); - if (t.type != TOKEN_KWORD_LET) { - parser_report(p, "Missing keyword 'let' at the start of a variable declaration"); - return NULL; - } - - t = next_token(p); - if (t.type != TOKEN_IDENT) { - parser_report(p, "Missing variable name after 'let' keyword"); - return NULL; - } - String name = t.sval; - - Scanner saved = p->s; - t = next_token(p); - - Node *value; - if (t.type == TOKEN_OPER_ASS) { - - value = parse_expr(p, opflags); - if (value == NULL) - return NULL; - - } else { - p->s = saved; - value = NULL; - } - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->type = NODE_VAR_DECL; - parent->var_name = name; - parent->var_value = value; - - return parent; -} - -Node *parse_print_stmt(Parser *p, int opflags) -{ - Token t = next_token(p); - if (t.type != TOKEN_KWORD_PRINT) { - parser_report(p, "Missing keyword 'print' at the start of a print statement"); - return NULL; - } - - Node *arg = parse_expr(p, opflags); - if (arg == NULL) - return NULL; - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->type = NODE_PRINT; - parent->left = arg; - - return parent; -} - -Node *parse_include_stmt(Parser *p) -{ - Token t = next_token(p); - if (t.type != TOKEN_KWORD_INCLUDE) { - parser_report(p, "Missing keyword 'include' at the start of an include statement"); - return NULL; - } - - t = next_token(p); - if (t.type != TOKEN_VALUE_STR) { - parser_report(p, "Missing file path string after 'include' keyword"); - return NULL; - } - String path = t.sval; - - Node *parent = alloc_node(p); - if (parent == NULL) - return NULL; - - parent->type = NODE_INCLUDE; - parent->include_path = path; - parent->include_root = NULL; - - *p->include_tail = parent; - p->include_tail = &parent->include_next; - - return parent; -} - -Node *parse_stmt(Parser *p, int opflags) -{ - Scanner saved = p->s; - Token t = next_token(p); - p->s = saved; - - switch (t.type) { - - case TOKEN_KWORD_INCLUDE: - return parse_include_stmt(p); - - case TOKEN_KWORD_PRINT: - return parse_print_stmt(p, opflags); - - case TOKEN_KWORD_FUN: - return parse_func_decl(p, opflags); - - case TOKEN_KWORD_LET: - return parse_var_decl(p, opflags); - - case TOKEN_KWORD_IF: - return parse_ifelse_stmt(p, opflags); - - case TOKEN_KWORD_WHILE: - return parse_while_stmt(p, opflags); - - case TOKEN_KWORD_FOR: - return parse_for_stmt(p, opflags); - - case TOKEN_CURLY_OPEN: - return parse_block_stmt(p, true); - - default: - break; - } - - return parse_expr_stmt(p, opflags); -} - -void print_node(Node *node) -{ - switch (node->type) { - - case NODE_VALUE_NONE: - printf("none"); - break; - - case NODE_VALUE_TRUE: - printf("true"); - break; - - case NODE_VALUE_FALSE: - printf("false"); - break; - - case NODE_NESTED: - { - printf("("); - print_node(node->left); - printf(")"); - } - break; - - case NODE_PRINT: - { - printf("print "); - print_node(node->left); - } - break; - - case NODE_BLOCK: - { - printf("{"); - Node *cur = node->left; - while (cur) { - print_node(cur); - printf(";"); - cur = cur->next; - } - printf("}"); - } - break; - - case NODE_OPER_LEN: - printf("len("); - print_node(node->left); - printf(")"); - break; - - case NODE_OPER_POS: - printf("("); - printf("+"); - print_node(node->left); - printf(")"); - break; - - case NODE_OPER_NEG: - printf("("); - printf("-"); - print_node(node->left); - printf(")"); - break; - - case NODE_OPER_ASS: - printf("("); - print_node(node->left); - printf("="); - print_node(node->right); - printf(")"); - break; - - case NODE_OPER_EQL: - printf("("); - print_node(node->left); - printf("=="); - print_node(node->right); - printf(")"); - break; - - case NODE_OPER_NQL: - printf("("); - print_node(node->left); - printf("!="); - print_node(node->right); - printf(")"); - break; - - case NODE_OPER_LSS: - printf("("); - print_node(node->left); - printf("<"); - print_node(node->right); - printf(")"); - break; - - case NODE_OPER_GRT: - printf("("); - print_node(node->left); - printf(">"); - print_node(node->right); - printf(")"); - break; - - case NODE_OPER_ADD: - printf("("); - print_node(node->left); - printf("+"); - print_node(node->right); - printf(")"); - break; - - case NODE_OPER_SUB: - printf("("); - print_node(node->left); - printf("-"); - print_node(node->right); - printf(")"); - break; - - case NODE_OPER_MUL: - printf("("); - print_node(node->left); - printf("*"); - print_node(node->right); - printf(")"); - break; - - case NODE_OPER_DIV: - printf("("); - print_node(node->left); - printf("/"); - print_node(node->right); - printf(")"); - break; - - case NODE_OPER_MOD: - printf("("); - print_node(node->left); - printf("%%"); - print_node(node->right); - printf(")"); - break; - - case NODE_VALUE_INT: - printf("%" LLU, node->ival); - break; - - case NODE_VALUE_FLOAT: - printf("%f", node->dval); - break; - - case NODE_VALUE_STR: - printf("\"%.*s\"", node->sval.len, node->sval.ptr); - break; - - case NODE_VALUE_VAR: - printf("%.*s", node->sval.len, node->sval.ptr); - break; - - case NODE_VALUE_SYSVAR: - printf("$%.*s", node->sval.len, node->sval.ptr); - break; - - case NODE_IFELSE: - printf("if "); - print_node(node->cond); - printf(":"); - print_node(node->left); - if (node->right) { - printf(" else "); - print_node(node->right); - } - break; - - case NODE_WHILE: - printf("while "); - print_node(node->cond); - printf(":"); - print_node(node->left); - break; - - case NODE_VALUE_HTML: - { - printf("<%.*s", - node->tagname.len, - node->tagname.ptr - ); - - Node *param = node->params; - while (param) { - if (param->attr_value) { - printf(" %.*s=", - param->attr_name.len, - param->attr_name.ptr); - print_node(param->attr_value); - } else { - printf(" %.*s", - param->attr_name.len, - param->attr_name.ptr - ); - } - param = param->next; - } - printf(">"); - - Node *child = node->child; - while (child) { - print_node(child); - child = child->next; - } - - printf("", - node->tagname.len, - node->tagname.ptr - ); - } - break; - - case NODE_FOR: - { - printf("for %.*s", - node->for_var1.len, - node->for_var1.ptr - ); - if (node->for_var2.len > 0) { - printf(", %.*s", - node->for_var2.len, - node->for_var2.ptr - ); - } - printf(" in "); - print_node(node->for_set); - printf(": "); - print_node(node->left); - } - break; - - case NODE_SELECT: - { - print_node(node->left); - printf("["); - print_node(node->right); - printf("]"); - } - break; - - case NODE_VALUE_ARRAY: - { - printf("["); - Node *child = node->child; - while (child) { - print_node(child); - printf(", "); - child = child->next; - } - printf("]"); - } - break; - - case NODE_VALUE_MAP: - { - printf("{"); - Node *child = node->child; - while (child) { - print_node(child->key); - printf(": "); - print_node(child); - printf(", "); - child = child->next; - } - printf("}"); - } - break; - - case NODE_HTML_PARAM: - { - printf("???"); - } - break; - - case NODE_FUNC_DECL: - { - printf("fun %.*s(", - node->func_name.len, - node->func_name.ptr); - Node *arg = node->func_args; - while (arg) { - print_node(arg); - arg = arg->next; - if (arg) - printf(", "); - } - printf(")"); - print_node(node->func_body); - } - break; - - case NODE_FUNC_ARG: - { - printf("%.*s", node->sval.len, node->sval.ptr); - } - break; - - case NODE_FUNC_CALL: - { - print_node(node->left); - printf("("); - Node *arg = node->right; - while (arg) { - print_node(arg); - arg = arg->next; - if (arg) - printf(", "); - } - printf(")"); - } - break; - - case NODE_VAR_DECL: - { - printf("let %.*s", - node->var_name.len, - node->var_name.ptr); - if (node->var_value) { - printf(" = "); - print_node(node->var_value); - } - //printf(";"); - } - break; - - case NODE_INCLUDE: - { - printf("include \"%.*s\"", - node->include_path.len, - node->include_path.ptr); - } - break; - - default: - printf("(invalid node type %x)", node->type); - break; - } -} - -ParseResult parse(String src, WL_Arena *a, char *errbuf, int errmax) -{ - Parser p = { - .s={ src.ptr, src.len, 0 }, - .a=a, - .errbuf=errbuf, - .errmax=errmax, - .errlen=0, - }; - - p.include_tail = &p.include_head; - - Node *node = parse_block_stmt(&p, false); - if (node == NULL) - return (ParseResult) { .node=NULL, .includes=NULL, .errlen=p.errlen }; - - assert(node->type == NODE_BLOCK); - node->type = NODE_GLOBAL_BLOCK; - - *p.include_tail = NULL; - return (ParseResult) { .node=node, .includes=p.include_head, .errlen=-1 }; -} diff --git a/src/parse.h b/src/parse.h deleted file mode 100644 index 7bb80ce..0000000 --- a/src/parse.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef WL_PARSE_INCLUDED -#define WL_PARSE_INCLUDED - -#ifndef WL_AMALGAMATION -#include "includes.h" -#include "basic.h" -#endif - -typedef enum { - NODE_FUNC_DECL, - NODE_FUNC_ARG, - NODE_FUNC_CALL, - NODE_VAR_DECL, - NODE_PRINT, - NODE_BLOCK, - NODE_GLOBAL_BLOCK, - NODE_IFELSE, - NODE_FOR, - NODE_WHILE, - NODE_INCLUDE, - NODE_SELECT, - NODE_NESTED, - NODE_OPER_LEN, - NODE_OPER_POS, - NODE_OPER_NEG, - NODE_OPER_ASS, - NODE_OPER_EQL, - NODE_OPER_NQL, - NODE_OPER_LSS, - NODE_OPER_GRT, - NODE_OPER_ADD, - NODE_OPER_SUB, - NODE_OPER_MUL, - NODE_OPER_DIV, - NODE_OPER_MOD, - NODE_VALUE_INT, - NODE_VALUE_FLOAT, - NODE_VALUE_STR, - NODE_VALUE_NONE, - NODE_VALUE_TRUE, - NODE_VALUE_FALSE, - NODE_VALUE_VAR, - NODE_VALUE_SYSVAR, - NODE_VALUE_HTML, - NODE_VALUE_ARRAY, - NODE_VALUE_MAP, - NODE_HTML_PARAM, -} NodeType; - -typedef struct Node Node; -struct Node { - NodeType type; - Node *next; - - Node *key; - - Node *left; - Node *right; - - uint64_t ival; - double dval; - String sval; - - Node *params; - Node *child; - bool no_body; - - Node *cond; - - String tagname; - String attr_name; - Node *attr_value; - - String for_var1; - String for_var2; - Node *for_set; - - String func_name; - Node *func_args; - Node *func_body; - - String var_name; - Node *var_value; - - String include_path; - Node* include_next; - Node* include_root; -}; - -typedef struct { - Node *node; - Node *includes; - int errlen; -} ParseResult; - -void print_node(Node *node); -ParseResult parse(String src, WL_Arena *a, char *errbuf, int errmax); - -#endif // WL_PARSE_INCLUDED \ No newline at end of file diff --git a/src/value.c b/src/value.c deleted file mode 100644 index 5fbd922..0000000 --- a/src/value.c +++ /dev/null @@ -1,501 +0,0 @@ - -#ifndef WL_AMALGAMATION -#include "value.h" -#endif - -#define ITEMS_PER_MAP_BATCH 8 -#define ITEMS_PER_ARRAY_BATCH 16 - -typedef struct MapItems MapItems; -struct MapItems { - MapItems *next; - Value keys [ITEMS_PER_MAP_BATCH]; - Value items[ITEMS_PER_MAP_BATCH]; -}; - -typedef struct { - Type type; - int count; - int tail_count; - MapItems head; - MapItems *tail; -} MapValue; - -typedef struct ArrayItems ArrayItems; -struct ArrayItems { - ArrayItems *next; - Value items[ITEMS_PER_ARRAY_BATCH]; -}; - -typedef struct { - Type type; - int count; - int tail_count; - ArrayItems head; - ArrayItems *tail; -} ArrayValue; - -typedef struct { - Type type; - double raw; -} FloatValue; - -typedef struct { - Type type; - int64_t raw; -} IntValue; - -typedef struct { - Type type; - int len; - char data[]; -} StringValue; - -Type type_of(Value v) -{ - // 000 none - // 001 true - // 010 false - // 011 int - // 100 - // 101 - // 110 error - // 111 pointer - - switch (v & 7) { - case 0: return TYPE_NONE; - case 1: return TYPE_BOOL; - case 2: return TYPE_BOOL; - case 3: return TYPE_INT; - case 4: break; - case 5: break; - case 6: return TYPE_ERROR; - case 7: return *(Type*) ((uintptr_t) v & ~(uintptr_t) 7); - } - - return TYPE_ERROR; -} - -int64_t get_int(Value v) -{ - if ((v & 7) == 3) - return (int64_t) (v >> 3); - - IntValue *p = (IntValue*) v; - return p->raw; -} - -float get_float(Value v) -{ - FloatValue *p = (FloatValue*) v; - return p->raw; -} - -String get_str(Value v) -{ - StringValue *p = (StringValue*) (v & ~(uintptr_t) 7); - return (String) { p->data, p->len }; -} - -static MapValue *get_map(Value v) -{ - return (MapValue*) (v & ~(uintptr_t) 7); -} - -static ArrayValue *get_array(Value v) -{ - return (ArrayValue*) (v & ~(uintptr_t) 7); -} - -Value make_int(WL_Arena *a, int64_t x) -{ - if (x <= (int64_t) (1ULL << 60)-1 && x >= (int64_t) -(1ULL << 60)) - return ((Value) x << 3) | 3; - - IntValue *v = alloc(a, (int) sizeof(IntValue), _Alignof(IntValue)); - if (v == NULL) - return VALUE_ERROR; - - v->type = TYPE_INT; - v->raw = x; - - assert(((uintptr_t) v & 7) == 0); - return ((Value) v) | 7; -} - -Value make_float(WL_Arena *a, float x) -{ - FloatValue *v = alloc(a, (int) sizeof(FloatValue), _Alignof(FloatValue)); - if (v == NULL) - return VALUE_ERROR; - - v->type = TYPE_FLOAT; - v->raw = x; - - assert(((uintptr_t) v & 7) == 0); - return ((Value) v) | 7; -} - -Value make_str(WL_Arena *a, String x) // TODO: This should reuse the string contents when possible -{ - StringValue *v = alloc(a, (int) sizeof(StringValue) + x.len, 8); - if (v == NULL) - return VALUE_ERROR; - - v->type = TYPE_STRING; - v->len = x.len; - memcpy(v->data, x.ptr, x.len); - - assert(((uintptr_t) v & 7) == 0); - return ((Value) v) | 7; -} - -Value make_map(WL_Arena *a) -{ - MapValue *m = alloc(a, (int) sizeof(MapValue), _Alignof(MapValue)); - if (m == NULL) - return VALUE_ERROR; - - m->type = TYPE_MAP; - m->count = 0; - m->tail_count = 0; - m->tail = &m->head; - m->head.next = NULL; - - return (Value) m | 7; -} - -Value make_array(WL_Arena *a) -{ - ArrayValue *v = alloc(a, (int) sizeof(ArrayValue), _Alignof(ArrayValue)); - if (v == NULL) - return VALUE_ERROR; - - v->type = TYPE_ARRAY; - v->count = 0; - v->tail_count = 0; - v->tail = &v->head; - v->head.next = NULL; - - return (Value) v | 7; -} - -int map_select(Value map, Value key, Value *val) -{ - MapValue *p = get_map(map); - MapItems *batch = &p->head; - while (batch) { - - int num = ITEMS_PER_MAP_BATCH; - if (batch->next == NULL) - num = p->tail_count; - - for (int i = 0; i < num; i++) - if (valeq(batch->keys[i], key)) { - *val = batch->items[i]; - return 0; - } - - batch = batch->next; - } - - return -1; -} - -Value *map_select_by_index(Value map, int key) -{ - MapValue *p = get_map(map); - MapItems *batch = &p->head; - int cursor = 0; - while (batch) { - - int num = ITEMS_PER_MAP_BATCH; - if (batch->next == NULL) - num = p->tail_count; - - if (cursor <= key && key < cursor + num) - return &batch->keys[key - cursor]; - - batch = batch->next; - cursor += num; - } - - return NULL; -} - -int map_insert(WL_Arena *a, Value map, Value key, Value val) -{ - MapValue *p = get_map(map); - if (p->tail_count == ITEMS_PER_MAP_BATCH) { - - MapItems *batch = alloc(a, (int) sizeof(MapItems), _Alignof(MapItems)); - if (batch == NULL) - return -1; - - batch->next = NULL; - if (p->tail) - p->tail->next = batch; - p->tail = batch; - p->tail_count = 0; - } - - p->tail->keys[p->tail_count] = key; - p->tail->items[p->tail_count] = val; - p->tail_count++; - p->count++; - return 0; -} - -Value *array_select(Value array, int key) -{ - ArrayValue *p = get_array(array); - ArrayItems *batch = &p->head; - int cursor = 0; - while (batch) { - - int num = ITEMS_PER_ARRAY_BATCH; - if (batch->next == NULL) - num = p->tail_count; - - if (cursor <= key && key < cursor + num) - return &batch->items[key - cursor]; - - batch = batch->next; - cursor += num; - } - - return NULL; -} - -int array_append(WL_Arena *a, Value array, Value val) -{ - ArrayValue *p = get_array(array); - if (p->tail_count == ITEMS_PER_ARRAY_BATCH) { - - ArrayItems *batch = alloc(a, (int) sizeof(ArrayItems), _Alignof(ArrayItems)); - if (batch == NULL) - return -1; - - batch->next = NULL; - - if (p->tail) - p->tail->next = batch; - p->tail = batch; - p->tail_count = 0; - } - - p->tail->items[p->tail_count] = val; - p->tail_count++; - p->count++; - return 0; -} - -bool valeq(Value a, Value b) -{ - Type t1 = type_of(a); - Type t2 = type_of(b); - - if (t1 != t2) - return false; - - switch (t1) { - - case TYPE_NONE: - return VALUE_TRUE; - - case TYPE_BOOL: - return a == b; - - case TYPE_INT: - return get_int(a) == get_int(b); - - case TYPE_FLOAT: - return get_float(a) == get_float(b); - - case TYPE_MAP: - return false; // TODO - - case TYPE_ARRAY: - return false; // TODO - - case TYPE_STRING: - return streq(get_str(a), get_str(b)); - - case TYPE_ERROR: - return true; - } - - return false; -} - -bool valgrt(Value a, Value b) -{ - Type t1 = type_of(a); - Type t2 = type_of(b); - - if (t1 != t2) - return false; - - switch (t1) { - - case TYPE_NONE: - return VALUE_FALSE; - - case TYPE_BOOL: - return VALUE_FALSE; - - case TYPE_INT: - return get_int(a) > get_int(b); - - case TYPE_FLOAT: - return get_float(a) > get_float(b); - - case TYPE_MAP: - return false; - - case TYPE_ARRAY: - return false; - - case TYPE_STRING: - return false; - - case TYPE_ERROR: - return false; - } - - return false; -} - -int value_length(Value v) -{ - Type type = type_of(v); - - if (type == TYPE_ARRAY) - return get_array(v)->count; - - if (type == TYPE_MAP) - return get_map(v)->count; - - return -1; -} - -typedef struct { - char *dst; - int max; - int len; -} ToStringContext; - -static void tostr_appends(ToStringContext *tostr, String x) -{ - if (tostr->max > tostr->len) { - int cpy = tostr->max - tostr->len; - if (cpy > x.len) - cpy = x.len; - memcpy(tostr->dst + tostr->len, x.ptr, cpy); - } - tostr->len += x.len; -} - -static void tostr_appendi(ToStringContext *tostr, int64_t x) -{ - int len; - if (tostr->max >= tostr->len) - len = snprintf(tostr->dst + tostr->len, tostr->max - tostr->len, "%" LLD, x); - else - len = snprintf(NULL, 0, "%" LLD, x); - tostr->len += len; -} - -static void tostr_appendf(ToStringContext *tostr, double x) -{ - int len; - if (tostr->max >= tostr->len) - len = snprintf(tostr->dst + tostr->len, tostr->max - tostr->len, "%f", x); - else - len = snprintf(NULL, 0, "%f", x); - tostr->len += len; -} - -static void value_to_string_inner(Value v, ToStringContext *tostr) -{ - switch (type_of(v)) { - - case TYPE_NONE: - //tostr_appends(tostr, S("none")); - break; - - case TYPE_BOOL: - // TODO - //tostr_appends(tostr, get_bool(v) ? S("true") : S("false")); - break; - - case TYPE_INT: - tostr_appendi(tostr, get_int(v)); - break; - - case TYPE_FLOAT: - tostr_appendf(tostr, get_float(v)); - break; - - case TYPE_MAP: - { - tostr_appends(tostr, S("{ ")); - MapValue *m = get_map(v); - MapItems *batch = &m->head; - while (batch) { - - int num = ITEMS_PER_MAP_BATCH; - if (batch->next == NULL) - num = m->tail_count; - - for (int i = 0; i < num; i++) { - value_to_string_inner(batch->keys[i], tostr); - tostr_appends(tostr, S(": ")); - value_to_string_inner(batch->items[i], tostr); - - if (batch->next != NULL || i+1 < num) - tostr_appends(tostr, S(", ")); - } - - batch = batch->next; - } - tostr_appends(tostr, S(" }")); - } - break; - - case TYPE_ARRAY: - { - ArrayValue *a = get_array(v); - ArrayItems *batch = &a->head; - int cursor = 0; - while (batch) { - - int num = ITEMS_PER_ARRAY_BATCH; - if (batch->next == NULL) - num = a->tail_count; - - for (int i = 0; i < num; i++) - value_to_string_inner(batch->items[i], tostr); - - batch = batch->next; - cursor += num; - } - } - break; - - case TYPE_STRING: - tostr_appends(tostr, get_str(v)); - break; - - case TYPE_ERROR: - tostr_appends(tostr, S("error")); - break; - } -} - -int value_to_string(Value v, char *dst, int max) -{ - ToStringContext tostr = { dst, max, 0 }; - value_to_string_inner(v, &tostr); - return tostr.len; -} \ No newline at end of file diff --git a/src/value.h b/src/value.h deleted file mode 100644 index c547f3a..0000000 --- a/src/value.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef WL_VALUE_INCLUDED -#define WL_VALUE_INCLUDED - -#ifndef WL_AMALGAMATION -#include "basic.h" -#include "includes.h" -#endif - -#define VALUE_NONE ((Value) 0) -#define VALUE_TRUE ((Value) 1) -#define VALUE_FALSE ((Value) 2) -#define VALUE_ERROR ((Value) 6) - -typedef enum { - TYPE_NONE, - TYPE_BOOL, - TYPE_INT, - TYPE_FLOAT, - TYPE_MAP, - TYPE_ARRAY, - TYPE_STRING, - TYPE_ERROR, -} Type; - -typedef uint64_t Value; - -Type type_of (Value v); -int64_t get_int (Value v); -float get_float (Value v); -String get_str (Value v); -Value make_int (WL_Arena *a, int64_t x); -Value make_float (WL_Arena *a, float x); -Value make_str (WL_Arena *a, String x); -Value make_map (WL_Arena *a); -Value make_array (WL_Arena *a); -int map_select (Value map, Value key, Value *val); -Value* map_select_by_index(Value map, int key); -int map_insert (WL_Arena *a, Value map, Value key, Value val); -Value* array_select (Value array, int key); -int array_append (WL_Arena *a, Value array, Value val); -bool valeq (Value a, Value b); -bool valgrt (Value a, Value b); -int value_length (Value v); -int value_to_string(Value v, char *dst, int max); - -#endif // WL_VALUE_INCLUDED \ No newline at end of file diff --git a/test.c b/test.c new file mode 100644 index 0000000..420fd66 --- /dev/null +++ b/test.c @@ -0,0 +1,237 @@ +#include +#include +#include +#include +#include +#include "wl.h" + +#define COUNT(X) (int) (sizeof(X) / sizeof((X)[0])) + +struct { + int line; + char *in; + char *out; +} tests[] = { + {__LINE__, "", ""}, + {__LINE__, "1", "1"}, + {__LINE__, "none", ""}, + {__LINE__, "true", "true"}, + {__LINE__, "false", "false"}, + {__LINE__, "\"Hello, world!\"", "Hello, world!"}, + {__LINE__, "'Hello, world!'", "Hello, world!"}, + {__LINE__, "\"\\\\\"", "\\"}, + {__LINE__, "\"\\n\"", "\n"}, + {__LINE__, "\"\\t\"", "\t"}, + {__LINE__, "\"\\r\"", "\r"}, + {__LINE__, "\"\\\"\"", "\""}, + {__LINE__, "\"\\'\"", "'"}, + {__LINE__, "\"\\xFF\"", "\xFF"}, + {__LINE__, "\"\\x0F\"", "\x0F"}, + {__LINE__, "\"\\xF0\"", "\xF0"}, + {__LINE__, "[1, 2, 3]", "123"}, + {__LINE__, "+{}", "{}"}, + {__LINE__, "+{a:1}", "{a: 1}"}, + {__LINE__, "+{a:1,b:2,c:3}", "{a: 1, b: 2, c: 3}"}, + {__LINE__, "10-1-2", "7"}, + {__LINE__, "1+2*3", "7"}, + {__LINE__, "(1+2)*3", "9"}, + {__LINE__, "1+(2*3)", "7"}, + {__LINE__, "1+2", "3"}, + {__LINE__, "2-1", "1"}, + {__LINE__, "1-2", "-1"}, + {__LINE__, "2*3", "6"}, + {__LINE__, "2/3", "0"}, + {__LINE__, "4/2", "2"}, + {__LINE__, "0%2", "0"}, + {__LINE__, "3%2", "1"}, + {__LINE__, "1.0+2.0", "3.00"}, + {__LINE__, "2.0-1.0", "1.00"}, + {__LINE__, "1.0-2.0", "-1.00"}, + {__LINE__, "2.0*3.0", "6.00"}, + {__LINE__, "2.0/3.0", "0.67"}, + {__LINE__, "4.0/2.0", "2.00"}, + {__LINE__, "1+2.0", "3.00"}, + {__LINE__, "2-1.0", "1.00"}, + {__LINE__, "1-2.0", "-1.00"}, + {__LINE__, "2*3.0", "6.00"}, + {__LINE__, "2/3.0", "0.67"}, + {__LINE__, "4/2.0", "2.00"}, + {__LINE__, "1.0+2", "3.00"}, + {__LINE__, "2.0-1", "1.00"}, + {__LINE__, "1.0-2", "-1.00"}, + {__LINE__, "2.0*3", "6.00"}, + {__LINE__, "2.0/3", "0.67"}, + {__LINE__, "4.0/2", "2.00"}, + {__LINE__, "len []", "0"}, + {__LINE__, "len [1, 2, 3]", "3"}, + {__LINE__, "len {}", "0"}, + {__LINE__, "len {a:1}", "1"}, + {__LINE__, "len {a:1,b:2,c:3}", "3"}, + {__LINE__, "-12", "-12"}, + {__LINE__, "-12.34", "-12.34"}, + {__LINE__, "1<2", "true"}, + {__LINE__, "2<1", "false"}, + {__LINE__, "1>2", "false"}, + {__LINE__, "2>1", "true"}, + {__LINE__, "1==1", "true"}, + {__LINE__, "1==2", "false"}, + {__LINE__, "1!=1", "false"}, + {__LINE__, "1!=2", "true"}, + {__LINE__, "let a = 1\na", "1"}, + {__LINE__, "let a = 1\na = 2\na", "2"}, + {__LINE__, "[5, 6, 7][0]", "5"}, + {__LINE__, "[5, 6, 7][1]", "6"}, + {__LINE__, "[5, 6, 7][2]", "7"}, + {__LINE__, "+{a:5,b:6,c:7}.a", "5"}, + {__LINE__, "+{a:5,b:6,c:7}.b", "6"}, + {__LINE__, "+{a:5,b:6,c:7}.c", "7"}, + {__LINE__, "+{a:5,b:6,c:7}['a']", "5"}, + {__LINE__, "+{a:5,b:6,c:7}['b']", "6"}, + {__LINE__, "+{a:5,b:6,c:7}['c']", "7"}, + {__LINE__, "let x = 1\n{\nlet x = 2\nx\n}", "2"}, + {__LINE__, "let x = {a:5,b:6,c:7}\nx.a = true\nx", "{a: true, b: 6, c: 7}"}, + {__LINE__, "let x = {a:5,b:6,c:7}\nx.b = true\nx", "{a: 5, b: true, c: 7}"}, + {__LINE__, "let x = {a:5,b:6,c:7}\nx.c = true\nx", "{a: 5, b: 6, c: true}"}, + {__LINE__, "let x = {a:5,b:6,c:7}\nx['a'] = true\nx", "{a: true, b: 6, c: 7}"}, + {__LINE__, "let x = {a:5,b:6,c:7}\nx['b'] = true\nx", "{a: 5, b: true, c: 7}"}, + {__LINE__, "let x = {a:5,b:6,c:7}\nx['c'] = true\nx", "{a: 5, b: 6, c: true}"}, + {__LINE__, "let a\nlet b\nlet c\na = b = c = 5\n", ""}, + {__LINE__, "let a\nlet b\nlet c\na = b = c = 5\na\nb\nc", "555"}, + {__LINE__, "if 1 < 2: 1\n2", "12"}, + {__LINE__, "if 1 > 2: 1\n2", "2"}, + {__LINE__, "if 1 < 2: 1 else 2\n3", "13"}, + {__LINE__, "if 1 > 2: 1 else 2\n3", "23"}, + {__LINE__, "let i = 0\nwhile i < 3: {\ntrue\ni = i + 1\n}\n", "truetruetrue"}, + {__LINE__, "for a in ['A', 'B', 'C']: a", "ABC"}, + {__LINE__, "for a, b in ['A', 'B', 'C']: { a\n b }", "A0B1C2"}, + {__LINE__, "for a in {x:1,y:2,z:3}: a", "xyz"}, + {__LINE__, "for a, b in {x:1,y:2,z:3}: { a\n b }", "x0y1z2"}, + {__LINE__, "procedure P() 0", ""}, + {__LINE__, "procedure P(a) a", ""}, + {__LINE__, "procedure P(a, b, c) { a\nb\nc }", ""}, + {__LINE__, "procedure P() 0\nP()", "0"}, + {__LINE__, "procedure P(a, b, c) { a\nb\nc }\nP()", ""}, + {__LINE__, "procedure P(a, b, c) { a\nb\nc }\nP(1)", "1"}, + {__LINE__, "procedure P(a, b, c) { a\nb\nc }\nP(1, 2)", "12"}, + {__LINE__, "procedure P(a, b, c) { a\nb\nc }\nP(1, 2, 3)", "123"}, + {__LINE__, "procedure N(n) n\nN(1) + N(2)", "3"}, + {__LINE__, "P()\nprocedure P() 1", "1"}, + {__LINE__, "P()\nprocedure P() 1\n{\nprocedure P() 2\n}", "1"}, + {__LINE__, "procedure P() 1\nP()\n{\nprocedure P() 2\n}", "1"}, + {__LINE__, "procedure P() 1\n{\nP()\nprocedure P() 2\n}", "2"}, + {__LINE__, "procedure P() 1\n{\nprocedure P() 2\nP()\n}", "2"}, + {__LINE__, "procedure P() 1\n{\nprocedure P() 2\n}\nP()", "1"}, + {__LINE__, "Hello, world!", "Hello, world!"}, + {__LINE__, "
  • A
  • B
  • C
", "
  • A
  • B
  • C
"}, + {__LINE__, "let a =
  • A
  • B
  • C
", ""}, + {__LINE__, "let a =
  • A
  • B
  • C
\na", "
  • A
  • B
  • C
"}, +}; + +int run_test(char *in, char *out, void *mem, int cap, int test_line) +{ + WL_Arena arena = { mem, cap, 0 }; + WL_Program program; + + WL_Compiler *c = wl_compiler_init(&arena); + if (c == NULL) { + fprintf(stderr, "Error: Out of memory"); + return -1; + } + + WL_AddResult res = wl_compiler_add(c, (WL_String) { in, strlen(in) }); + if (res.type == WL_ADD_ERROR) { + fprintf(stderr, "Error: %s\n", wl_compiler_error(c).ptr); + return -1; + } + if (res.type != WL_ADD_LINK) { + fprintf(stderr, "Error: Unexpected compiler state\n"); + return -1; + } + + int ret = wl_compiler_link(c, &program); + if (ret < 0) { + WL_String err = wl_compiler_error(c); + fprintf(stderr, "Error: %s\n", err.ptr); + return -1; + } + + WL_Runtime *rt = wl_runtime_init(&arena, program); + if (rt == NULL) { + fprintf(stderr, "Error: Invalid program or out of memory\n"); + return -1; + } + + char output[1<<10]; + int outlen = 0; + + for (bool done = false; !done; ) { + WL_EvalResult res = wl_runtime_eval(rt); + switch (res.type) { + + case WL_EVAL_NONE: + break; + + case WL_EVAL_DONE: + done = true; + break; + + case WL_EVAL_ERROR: + printf("Error: %s (test at line %d)\n", wl_runtime_error(rt).ptr, test_line); + return 0; + + case WL_EVAL_OUTPUT: + if ((int) sizeof(output) - outlen < res.str.len) { + printf("Error: Output is too long\n"); + return -1; + } + memcpy(output + outlen, res.str.ptr, res.str.len); + outlen += res.str.len; + break; + + case WL_EVAL_SYSVAR: + break; + + case WL_EVAL_SYSCALL: + break; + } + } + + //printf("[%s] -> [%.*s]\n", in, outlen, output); + + if (outlen != strlen(out) || memcmp(out, output, outlen)) { + fprintf(stderr, "Error: Output mistmatch\n"); + fprintf(stderr, " Source : [%s]\n", in); + fprintf(stderr, " Output : [%.*s]\n", outlen, output); + fprintf(stderr, " Expected: [%s]\n", out); +#if 1 + fprintf(stderr, " Program:\n"); + char buf[1<<10]; + int len = wl_dump_program(program, buf, sizeof(buf)); + if (len < 0) + fprintf(stderr, " (Invalid program)\n"); + else if (len >= sizeof(buf)) { + fprintf(stderr, " (Program disassembly is too big)\n"); + } else { + buf[len] = '\0'; + fprintf(stderr, "%s", buf); + } +#endif + return 0; + } + + return 1; +} + +int main(void) +{ + int cap = 1<<20; + char *mem = malloc(cap); + if (mem == NULL) + return -1; + + for (int i = 0; i < COUNT(tests); i++) + run_test(tests[i].in, tests[i].out, mem, cap, tests[i].line); + + free(mem); + return 0; +}