From 2ce583502188aa1bdce1a2cbc1bd202c75f0d677 Mon Sep 17 00:00:00 2001 From: Francesco Cozzuto Date: Tue, 5 Aug 2025 14:43:27 +0200 Subject: [PATCH] Working on the public API --- Makefile | 7 +- WL.c | 5573 ++++++++++++++++++++++++++++++++++++++++++++++++ WL.h | 67 + amalg.py | 53 + main.c | 70 + main.wl | 10 +- src/assemble.c | 130 +- src/assemble.h | 18 +- src/basic.c | 22 +- src/basic.h | 30 +- src/eval.c | 1207 +++++------ src/eval.h | 5 +- src/file.c | 12 +- src/file.h | 5 +- src/includes.h | 25 + src/main.c | 48 - src/parse.c | 52 +- src/parse.h | 6 +- src/public.c | 41 + src/public.h | 56 + src/value.c | 458 ++++ src/value.h | 44 + 22 files changed, 7086 insertions(+), 853 deletions(-) create mode 100644 WL.c create mode 100644 WL.h create mode 100644 amalg.py create mode 100644 main.c create mode 100644 src/includes.h delete mode 100644 src/main.c create mode 100644 src/public.c create mode 100644 src/public.h create mode 100644 src/value.c create mode 100644 src/value.h diff --git a/Makefile b/Makefile index 18d6090..7302d81 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,5 @@ -all: - gcc src/basic.c src/file.c src/parse.c src/assemble.c src/eval.c src/main.c -o wl -Wall -Wextra -O0 -g3 +all: WL.c + gcc WL.c main.c -o wl -Wall -Wextra -O0 -g3 + +WL.c: $(wildcard src/*.c src/*.h) + py amalg.py \ No newline at end of file diff --git a/WL.c b/WL.c new file mode 100644 index 0000000..0e65889 --- /dev/null +++ b/WL.c @@ -0,0 +1,5573 @@ +#include "WL.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// src/includes.h +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "src/includes.h" +#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 + +//////////////////////////////////////////////////////////////////////////////////////// +// src/basic.h +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "src/basic.h" +#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); + +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 +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "src/basic.c" + +#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; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// src/file.h +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "src/file.h" +#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 + +//////////////////////////////////////////////////////////////////////////////////////// +// src/file.c +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "src/file.c" +#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; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// src/parse.h +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "src/parse.h" +#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_IFELSE, + NODE_FOR, + NODE_WHILE, + NODE_SELECT, + NODE_NESTED, + 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_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; + String text; + + Node *key; + + Node *left; + Node *right; + + uint64_t ival; + double dval; + String sval; + + Node *params; + Node *child; + + 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; +}; + +typedef struct { + Node *node; + 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 +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "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_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; +} 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_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 }; + 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 + + int node_offset = p->s.cur-1; + + 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 (;;) { + + int param_offset = p->s.cur; + + 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->text = (String) { p->s.src + param_offset, p->s.cur - param_offset }; + 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->text = (String) { p->s.src + off, p->s.cur - off }; + 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->text = (String) { p->s.src + node_offset, p->s.cur - node_offset }; + parent->tagname = tagname; + parent->params = param_head; + parent->child = head; + + return parent; +} + +Node *parse_array(Parser *p) +{ + // Left bracket already consumed + + int start = p->s.cur; + + 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->text = (String) { p->s.src + start, p->s.cur - start }; + parent->child = head; + + return parent; +} + +Node *parse_map(Parser *p) +{ + // Left bracket already consumed + + int start = p->s.cur; + + 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->text = t.sval; + 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->text = (String) { p->s.src + start, p->s.cur - start }; + 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) +{ + int start = p->s.cur; + + 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->text = (String) { p->s.src + start, p->s.cur - start }; + 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->text = (String) { p->s.src + start, p->s.cur - start }; + 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->text = (String) { p->s.src + start, p->s.cur - start }; + 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->text = (String) { p->s.src + start, p->s.cur - start }; + 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->text = (String) { p->s.src + start, p->s.cur - start }; + 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->text = (String) { p->s.src + start, p->s.cur - start }; + 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->text = (String) { p->s.src + start, p->s.cur - start }; + 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->text = (String) { p->s.src + start, p->s.cur - start }; + 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->text = t.sval; + child->sval = t.sval; + + Node *parent = alloc_node(p); + if (parent == NULL) + return NULL; + + parent->type = NODE_SELECT; + parent->text = (String) { p->s.src + start, p->s.cur - start }; + 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->text = (String) { p->s.src + start, p->s.cur - start }; + 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->text = (String) { p->s.src + start, p->s.cur - start }; + 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 start, int flags) +{ + for (;;) { + + Scanner saved = p->s; + Token t1 = next_token_or_newline(p); + if (precedence(t1, flags) < min_prec) { + p->s = saved; + break; + } + + int right_start = p->s.cur; + + 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), right_start, flags); + if (right == NULL) + return NULL; + } + + Node *parent = alloc_node(p); + if (parent == NULL) + return NULL; + + parent->text = (String) { p->s.src + start, p->s.cur - start }; + 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) +{ + int start = p->s.cur; + + Node *left = parse_atom(p); + if (left == NULL) + return NULL; + + return parse_expr_inner(p, left, 0, start, 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) +{ + int start = p->s.cur; + + 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->text = (String) { p->s.src + start, p->s.cur - start }; + parent->left = if_stmt; + parent->right = else_stmt; + parent->cond = cond; + + return parent; +} + +Node *parse_for_stmt(Parser *p, int opflags) +{ + int start = p->s.cur; + + 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->text = (String) { p->s.src + start, p->s.cur - start }; + 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) +{ + int start = p->s.cur; + + 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->text = (String) { p->s.src + start, p->s.cur - start }; + parent->left = stmt; + parent->cond = cond; + + return parent; +} + +Node *parse_block_stmt(Parser *p, bool curly) +{ + int start = p->s.cur; + + 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->text = (String) { p->s.src + start, p->s.cur - start }; + parent->left = head; + + return parent; +} + +Node *parse_func_decl(Parser *p, int opflags) +{ + int start = p->s.cur; + + 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 (;;) { + + int arg_start = p->s.cur; + + 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->text = (String) { p->s.src + arg_start, p->s.cur - arg_start }; + 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->text = (String) { p->s.src + start, p->s.cur - start }; + parent->func_name = name; + parent->func_args = arg_head; + parent->func_body = body; + + return parent; +} + +Node *parse_var_decl(Parser *p, int opflags) +{ + int start = p->s.cur; + + 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->text = (String) { p->s.src + start, p->s.cur - start }; + parent->var_name = name; + parent->var_value = value; + + return parent; +} + +Node *parse_print_stmt(Parser *p, int opflags) +{ + int start = p->s.cur; + + 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->text = (String) { p->s.src + start, p->s.cur - start }; + parent->left = arg; + + 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_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_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_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; + } +} + +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, + }; + + Node *node = parse_block_stmt(&p, false); + + if (node == NULL) + return (ParseResult) { NULL, p.errlen }; + + return (ParseResult) { node, -1 }; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// src/assemble.h +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "src/assemble.h" +#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, +}; + +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 +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "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 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) +{ + 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) +{ + 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_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_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, '='); + + if (value->type == NODE_VALUE_STR) { + append_u8(&ha->tmp, '"'); + append_mem(&ha->tmp, + value->sval.ptr, // TODO: escape + value->sval.len + ); + append_u8(&ha->tmp, '"'); + } else { + write_buffered_html(a, ha); + assemble_statement(a, value, false); + } + } + attr = attr->next; + } + 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_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_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_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); + + 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: + { + 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; + } + + 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: + { + assemble_expr(a, node->for_set, 1); + + // TODO + + int ret = push_scope(a, SCOPE_FOR); + if (ret < 0) return; + + declare_variable(a, node->for_var1); + declare_variable(a, node->for_var2); + + assemble_statement(a, node->left, pop_expr); + + 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; + } + + return p; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// src/value.h +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "src/value.h" +#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); +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_to_string(Value v, char *dst, int max); + +#endif // WL_VALUE_INCLUDED + +//////////////////////////////////////////////////////////////////////////////////////// +// src/value.c +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "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 { + 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; + + 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; + + 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; +} + +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; + 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_MAP_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_MAP_BATCH) { + + ArrayItems *batch = alloc(a, (int) sizeof(ArrayItems), _Alignof(ArrayItems)); + if (batch == NULL) + return -1; + + batch->next = NULL; + 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; +} + +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); + 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_MAP_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; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// src/eval.h +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "src/eval.h" +#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 + +//////////////////////////////////////////////////////////////////////////////////////// +// src/eval.c +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "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 + +typedef struct { + int group; + int return_addr; +} Frame; + +struct WL_State { + + String code; + String data; + int off; + + 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; +}; + +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) +{ + 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); +/* + printf("%-3d: ", e->off); + print_instruction(state->code.ptr + e->off, e->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_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) { + + Value *dst = array_select(set, key); + 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) { + + Value *dst = array_select(set, key); + 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) { + + Value *src = array_select(set, key); + if (src == NULL) { + assert(0); // TODO + } + r = *src; + + } else if (type_of(set) == TYPE_MAP) { + + int ret = map_select(set, key, &r); + if (ret < 0) { + assert(0); // TODO + } + + } 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; + } + break; + + case OPCODE_SYSCALL: + { + uint32_t off = read_u32(state); + uint32_t len = read_u32(state); + String name = { state->data.ptr + off, len }; + + state->syscall = name; + } + 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, + .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 +} + +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 } }; + + // TODO + state->sysvar = S(""); + } + + if (state->syscall.len > 0) { + + if (state->syscall_error) + return (WL_Result) { WL_ERROR, (WL_String) { NULL, 0 } }; + + 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_popint(WL_State *state, long long *x) +{ + if (!in_syscall(state)) return 0; + + if (state->eval_depth == 0) + 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 true; +} + +int WL_popfloat(WL_State *state, float *x) +{ + if (!in_syscall(state)) return 0; + + if (state->eval_depth == 0) + 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 true; +} + +int WL_popstr(WL_State *state, char **str, int *len) +{ + if (!in_syscall(state)) return 0; + + if (state->eval_depth == 0) + 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 = s.ptr; + *len = s.len; + + state->eval_depth--; + return true; +} + +int WL_popany(WL_State *state) +{ + if (!in_syscall(state)) + return 0; + + if (state->eval_depth == 0) + return 0; + + state->eval_depth--; + return 1; +} + +void WL_select(WL_State *state) +{ + // TODO +} + +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, char *str, int len) +{ + if (!in_syscall(state)) return; + + Value v = make_str(state->a, (String) { 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) +{ + // TODO +} + +//////////////////////////////////////////////////////////////////////////////////////// +// src/public.c +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "src/public.c" + +#ifndef WL_AMALGAMATION +#include "eval.h" +#include "parse.h" +#include "assemble.h" +#include "WL.h" +#endif + +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; +} + +int WL_compile(char *src, int len, WL_Arena arena, WL_Program *program, char *err, int errmax) +{ + if (src == NULL) src = ""; + if (len < 0) len = strlen(src); + + Node *root; + ParseResult pres = parse((String) {src, len}, &arena, err, errmax); + if (pres.node == NULL) + return -1; + root = pres.node; + + AssembleResult ares = assemble(root, &arena, err, errmax); + if (ares.errlen) + return -1; + *program = ares.program; + + return 0; +} diff --git a/WL.h b/WL.h new file mode 100644 index 0000000..5bee788 --- /dev/null +++ b/WL.h @@ -0,0 +1,67 @@ +#ifndef WL_AMALGAMATION +#define WL_AMALGAMATION + +// This file was generated automatically. Do not modify directly! + +//////////////////////////////////////////////////////////////////////////////////////// +// src/public.h +//////////////////////////////////////////////////////////////////////////////////////// + +#line 1 "src/public.h" +#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; + +int WL_compile (char *src, int len, WL_Arena arena, WL_Program *program, char *err, int errmax); +WL_State *WL_State_init (WL_Arena *a, WL_Program p, char *err, int errmax); +void WL_State_free (WL_State *state); +WL_Result WL_eval (WL_State *state); +int WL_streq (WL_String a, char *b, int blen); +int WL_popint (WL_State *state, long long *x); +int WL_popfloat (WL_State *state, float *x); +int WL_popstr (WL_State *state, char **str, int *len); +int WL_popany (WL_State *state); +void WL_select (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, char *str, int len); +void WL_pusharray (WL_State *state, int cap); +void WL_pushmap (WL_State *state, int cap); +void WL_insert (WL_State *state); + +#endif // WL_PUBLIC_INCLUDED +#endif // WL_AMALGAMATION diff --git a/amalg.py b/amalg.py new file mode 100644 index 0000000..14adebe --- /dev/null +++ b/amalg.py @@ -0,0 +1,53 @@ + +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/public.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/public.c") +source.save("WL.c") diff --git a/main.c b/main.c new file mode 100644 index 0000000..a0b45fd --- /dev/null +++ b/main.c @@ -0,0 +1,70 @@ +#include +#include +#include + +#include "WL.h" + +int main(void) +{ + FILE *f = fopen("main.wl", "rb"); + if (f == NULL) + return -1; + + fseek(f, 0, SEEK_END); + long file_size = ftell(f); + fseek(f, 0, SEEK_SET); + + char *file_data = malloc(file_size); + + fread(file_data, 1, file_size, f); + fclose(f); + + char err[1<<9]; + char *mem = malloc(1<<20); + WL_Arena a = { mem, 1<<20, 0 }; + + WL_Program program; + int ret = WL_compile(file_data, file_size, a, &program, err, (int) sizeof(err)); + if (ret < 0) { + printf("%s\n", err); + return -1; + } + + WL_State *state = WL_State_init(&a, program, err, (int) sizeof(err)); + + 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; + 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; + } + } + + WL_State_free(state); + free(file_data); + free(mem); + return 0; +} diff --git a/main.wl b/main.wl index be4f2e6..ca88e76 100644 --- a/main.wl +++ b/main.wl @@ -1,10 +1,2 @@ -fun comment(data) -
- \data.author -

\data.content

- \for sub_comment in data.sub_comments: - comment(sub_comment) -
- -comment() \ No newline at end of file +$do_something(1, 2, 3) \ No newline at end of file diff --git a/src/assemble.c b/src/assemble.c index 6a09787..746e1d2 100644 --- a/src/assemble.c +++ b/src/assemble.c @@ -1,10 +1,6 @@ -#include -#include -#include -#include -#include #ifndef WL_AMALGAMATION +#include "includes.h" #include "parse.h" #include "assemble.h" #endif @@ -53,12 +49,12 @@ typedef struct { char *ptr; int len; int cap; - b32 err; + bool err; } OutputBuffer; typedef struct { - Arena *a; + WL_Arena *a; OutputBuffer out; @@ -232,7 +228,7 @@ Scope *parent_scope(Assembler *a) return scope; } -b32 global_scope(Assembler *a) +bool global_scope(Assembler *a) { return parent_scope(a)->type == SCOPE_GLOBAL; } @@ -305,7 +301,7 @@ int declare_function(Assembler *a, String name, int off) return 0; } -b32 is_expr(Node *node) +bool is_expr(Node *node) { switch (node->type) { @@ -331,6 +327,7 @@ b32 is_expr(Node *node) case NODE_VALUE_FLOAT: case NODE_VALUE_STR: case NODE_VALUE_VAR: + case NODE_VALUE_SYSVAR: case NODE_VALUE_HTML: case NODE_VALUE_ARRAY: case NODE_VALUE_MAP: @@ -400,7 +397,7 @@ int pop_scope(Assembler *a) return 0; } -void assemble_statement(Assembler *a, Node *node, b32 pop_expr); +void assemble_statement(Assembler *a, Node *node, bool pop_expr); typedef struct { OutputBuffer tmp; @@ -506,10 +503,35 @@ void assemble_expr(Assembler *a, Node *node, int num_results) arg = arg->next; } - assert(func->type == NODE_VALUE_VAR); + if (func->type == NODE_VALUE_SYSVAR) { - append_u8(&a->out, OPCODE_CALL); - int p = append_u32(&a->out, 0); + 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); @@ -519,19 +541,6 @@ void assemble_expr(Assembler *a, Node *node, int num_results) } append_u8(&a->out, OPCODE_GCOALESCE); - - 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; } break; @@ -751,7 +760,27 @@ void assemble_expr(Assembler *a, Node *node, int num_results) if (num_results == 0) append_u8(&a->out, OPCODE_POP); - else if (num_results != -1) { + 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); @@ -901,7 +930,7 @@ void assemble_expr(Assembler *a, Node *node, int num_results) } } -void assemble_statement(Assembler *a, Node *node, b32 pop_expr) +void assemble_statement(Assembler *a, Node *node, bool pop_expr) { switch (node->type) { @@ -1131,13 +1160,16 @@ typedef struct { uint32_t data_size; } Header; -AssembleResult assemble(Node *root, Arena *arena, char *errbuf, int errmax) +AssembleResult assemble(Node *root, WL_Arena *arena, char *errbuf, int errmax) { - Assembler a = {0, .a=arena, .errbuf=errbuf, .errmax=errmax, .errlen=0}; + Assembler a = {0}; + a.errbuf = errbuf; + a.errmax = errmax; + a.a = arena; int ret = push_scope(&a, SCOPE_GLOBAL); if (ret < 0) - return (AssembleResult) { (Program) {0}, a.errlen }; + return (AssembleResult) { (WL_Program) {0}, a.errlen }; append_u8(&a.out, OPCODE_GROUP); @@ -1158,7 +1190,7 @@ AssembleResult assemble(Node *root, Arena *arena, char *errbuf, int errmax) ret = pop_scope(&a); if (ret < 0) - return (AssembleResult) { (Program) {0}, a.errlen }; + return (AssembleResult) { (WL_Program) {0}, a.errlen }; OutputBuffer out = {0}; append_u32(&out, 0xFEEDBEEF); // magic @@ -1168,10 +1200,10 @@ AssembleResult assemble(Node *root, Arena *arena, char *errbuf, int errmax) append_mem(&out, a.strings, a.strings_len); free(a.out.ptr); - return (AssembleResult) { (Program) { out.ptr, out.len }, a.errlen }; + return (AssembleResult) { (WL_Program) { out.ptr, out.len }, a.errlen }; } -int parse_program_header(Program p, String *code, String *data, char *errbuf, int errmax) +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"); @@ -1200,7 +1232,7 @@ int parse_program_header(Program p, String *code, String *data, char *errbuf, in return 0; } -void print_program(Program program) +void print_program(WL_Program program) { String code; String data; @@ -1460,6 +1492,34 @@ char *print_instruction(char *p, char *data) 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; } return p; diff --git a/src/assemble.h b/src/assemble.h index 5703d87..982ecec 100644 --- a/src/assemble.h +++ b/src/assemble.h @@ -1,7 +1,10 @@ #ifndef WL_ASSEMBLE_INCLUDED #define WL_ASSEMBLE_INCLUDED +#ifndef WL_AMALGAMATION +#include "public.h" #include "parse.h" +#endif enum { OPCODE_NOPE = 0x00, @@ -41,21 +44,18 @@ enum { OPCODE_INSERT2 = 0x19, OPCODE_SELECT = 0x20, OPCODE_PRINT = 0x24, + OPCODE_SYSVAR = 0x2C, + OPCODE_SYSCALL = 0x2D, }; typedef struct { - void *ptr; - int len; -} Program; - -typedef struct { - Program program; + WL_Program program; int errlen; } AssembleResult; -int parse_program_header(Program p, String *code, String *data, char *errbuf, int errmax); -void print_program(Program program); +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, Arena *arena, char *errbuf, int errmax); +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 index 5057ce8..af2be6c 100644 --- a/src/basic.c +++ b/src/basic.c @@ -1,31 +1,31 @@ -#include -#include #ifndef WL_AMALGAMATION +#include "includes.h" #include "basic.h" +#include "public.h" #endif -b32 is_space(char c) +bool is_space(char c) { return c == ' ' || c == '\t' || c == '\r' || c == '\n'; } -b32 is_digit(char c) +bool is_digit(char c) { return c >= '0' && c <= '9'; } -b32 is_alpha(char c) +bool is_alpha(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } -b32 is_printable(char c) +bool is_printable(char c) { return c >= ' ' && c <= '~'; } -b32 is_hex_digit(char c) +bool is_hex_digit(char c) { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } @@ -49,7 +49,7 @@ int hex_digit_to_int(char c) } -b32 streq(String a, String b) +bool streq(String a, String b) { if (a.len != b.len) return false; @@ -59,7 +59,7 @@ b32 streq(String a, String b) return true; } -b32 streqcase(String a, String b) +bool streqcase(String a, String b) { if (a.len != b.len) return false; @@ -69,7 +69,7 @@ b32 streqcase(String a, String b) return true; } -void *alloc(Arena *a, int len, int align) +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) @@ -79,7 +79,7 @@ void *alloc(Arena *a, int len, int align) return ret; } -b32 grow_alloc(Arena *a, char *p, int new_len) +bool grow_alloc(WL_Arena *a, char *p, int new_len) { int new_cur = (p - a->ptr) + new_len; if (new_cur > a->len) diff --git a/src/basic.h b/src/basic.h index 6bfa112..6e50563 100644 --- a/src/basic.h +++ b/src/basic.h @@ -1,21 +1,15 @@ #ifndef WL_BASIC_INCLUDED #define WL_BASIC_INCLUDED -typedef unsigned int b32; -#define true 1 -#define false 0 +#ifndef WL_AMALGAMATION +#include "public.h" +#endif typedef struct { char *ptr; int len; } String; -typedef struct { - char *ptr; - int len; - int cur; -} Arena; - #ifdef _WIN32 #define LLU "llu" #define LLD "lld" @@ -31,18 +25,18 @@ typedef struct { #define COUNT(X) (int) (sizeof(X) / sizeof((X)[0])) -b32 is_space(char c); -b32 is_digit(char c); -b32 is_alpha(char c); -b32 is_printable(char c); +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); -b32 is_hex_digit(char c); +bool is_hex_digit(char c); int hex_digit_to_int(char c); -b32 streq(String a, String b); -b32 streqcase(String a, String b); +bool streq(String a, String b); +bool streqcase(String a, String b); -void *alloc(Arena *a, int len, int align); -b32 grow_alloc(Arena *a, char *p, int new_len); +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/eval.c b/src/eval.c index 905e0a4..d857eff 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1,9 +1,7 @@ -#include -#include -#include -#include #ifndef WL_AMALGAMATION +#include "includes.h" +#include "value.h" #include "eval.h" #endif @@ -11,91 +9,18 @@ #define EVAL_STACK_LIMIT 128 #define GROUP_LIMIT 128 -#define HEAP_BASE 0xFEEDBEEFFEEDBEEF - -/* -int -float -array -map -html -bool -none -*/ - -typedef enum { - TYPE_NONE, - TYPE_BOOL, - TYPE_INT, - TYPE_FLOAT, - TYPE_MAP, - TYPE_ARRAY, - TYPE_STRING, - TYPE_ERROR, -} Type; - -typedef uint64_t Value; - -#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; - typedef struct { int group; int return_addr; } Frame; -typedef struct { +struct WL_State { String code; String data; int off; - Arena *a; + WL_Arena *a; char *errbuf; int errmax; @@ -110,439 +35,76 @@ typedef struct { int num_groups; int groups[GROUP_LIMIT]; -} Eval; + int cur_print; + int num_prints; -#define VALUE_NONE ((Value) 0) -#define VALUE_TRUE ((Value) 1) -#define VALUE_FALSE ((Value) 2) -#define VALUE_ERROR ((Value) 6) + String sysvar; + String syscall; + bool syscall_error; +}; -void eval_report(Eval *e, char *fmt, ...) +void eval_report(WL_State *state, char *fmt, ...) { - if (e->errmax == 0 || e->errlen > 0) + if (state->errmax == 0 || state->errlen > 0) return; - int len = snprintf(e->errbuf, e->errmax, "Error: "); + int len = snprintf(state->errbuf, state->errmax, "Error: "); if (len < 0) { // TODO } va_list args; va_start(args, fmt); - int ret = vsnprintf(e->errbuf + len, e->errmax - len, fmt, args); + int ret = vsnprintf(state->errbuf + len, state->errmax - len, fmt, args); va_end(args); if (ret < 0) { // TODO } len += ret; - e->errlen = len; + state->errlen = len; } -Type type_of(Value v) +static uint8_t read_u8(WL_State *state) { - // 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; + return state->code.ptr[state->off++]; } -int64_t get_int(Value v) +static void read_mem(WL_State *state, void *dst, int len) { - if ((v & 7) == 3) - return (int64_t) (v >> 3); - - IntValue *p = (IntValue*) v; - return p->raw; + memcpy(dst, (uint8_t*) state->code.ptr + state->off, len); + state->off += len; } -float get_float(Value v) +static uint32_t read_u32(WL_State *state) { - FloatValue *p = (FloatValue*) v; - return p->raw; + uint32_t x; + read_mem(state, &x, (int) sizeof(x)); + return x; } -String get_str(Value v) +static int64_t read_s64(WL_State *state) { - StringValue *p = (StringValue*) (v & ~(uintptr_t) 7); - return (String) { p->data, p->len }; + int64_t x; + read_mem(state, &x, (int) sizeof(x)); + return x; } -MapValue *get_map(Value v) +static double read_f64(WL_State *state) { - return (MapValue*) (v & ~(uintptr_t) 7); + double x; + read_mem(state, &x, (int) sizeof(x)); + return x; } -ArrayValue *get_array(Value v) +int step(WL_State *state) { - return (ArrayValue*) (v & ~(uintptr_t) 7); -} - -Value make_int(Eval *e, int64_t x) -{ - if (x <= (int64_t) (1ULL << 60)-1 && x >= (int64_t) -(1ULL << 60)) - return ((Value) x << 3) | 3; - - IntValue *v = alloc(e->a, (int) sizeof(IntValue), _Alignof(IntValue)); - if (v == NULL) { - eval_report(e, "Out of memory"); - return VALUE_ERROR; - } - - v->type = TYPE_INT; - v->raw = x; - - assert(((uintptr_t) v & 7) == 0); - return ((Value) v) | 7; -} - -Value make_float(Eval *e, float x) -{ - FloatValue *v = alloc(e->a, (int) sizeof(FloatValue), _Alignof(FloatValue)); - if (v == NULL) { - eval_report(e, "Out of memory"); - return VALUE_ERROR; - } - - v->type = TYPE_FLOAT; - v->raw = x; - - assert(((uintptr_t) v & 7) == 0); - return ((Value) v) | 7; -} - -Value make_str(Eval *e, String x) // TODO: This should reuse the string contents when possible -{ - StringValue *v = alloc(e->a, (int) sizeof(StringValue) + x.len, 8); - if (v == NULL) { - eval_report(e, "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; -} - -Value make_map(Eval *e) -{ - MapValue *m = alloc(e->a, (int) sizeof(MapValue), _Alignof(MapValue)); - if (m == NULL) { - eval_report(e, "Out of memory"); - return VALUE_ERROR; - } - - m->type = TYPE_MAP; - m->count = 0; - m->tail_count = 0; - m->tail = &m->head; - - return (Value) m | 7; -} - -Value make_array(Eval *e) -{ - ArrayValue *a = alloc(e->a, (int) sizeof(ArrayValue), _Alignof(ArrayValue)); - if (a == NULL) { - eval_report(e, "Out of memory"); - return VALUE_ERROR; - } - - a->type = TYPE_ARRAY; - a->count = 0; - a->tail_count = 0; - a->tail = &a->head; - - return (Value) a | 7; -} - -b32 valeq(Value a, Value b); - -int map_select(Eval *e, Value map, Value key, Value *val) -{ - (void) e; - - 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; -} - -int map_insert(Eval *e, Value map, Value key, Value val) -{ - MapValue *p = get_map(map); - if (p->tail_count == ITEMS_PER_MAP_BATCH) { - - MapItems *batch = alloc(e->a, (int) sizeof(MapItems), _Alignof(MapItems)); - if (batch == NULL) { - eval_report(e, "Out of memory"); - return -1; - } - - batch->next = NULL; - 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; -} - -void value_print(Value v); - -void map_print(Value v) -{ - printf("{ "); - - MapValue *p = get_map(v); - 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++) { - value_print(batch->keys[i]); - printf(": "); - value_print(batch->items[i]); - printf(", "); - } - - batch = batch->next; - } - - printf("}"); -} - -Value *array_select(Eval *e, Value array, int key) -{ - (void) e; - - ArrayValue *p = get_array(array); - ArrayItems *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->items[key - cursor]; - - batch = batch->next; - cursor += num; - } - - return NULL; -} - -int array_append(Eval *e, Value array, Value val) -{ - ArrayValue *p = get_array(array); - if (p->tail_count == ITEMS_PER_MAP_BATCH) { - - ArrayItems *batch = alloc(e->a, (int) sizeof(ArrayItems), _Alignof(ArrayItems)); - if (batch == NULL) { - eval_report(e, "Out of memory"); - return -1; - } - - batch->next = NULL; - p->tail = batch; - p->tail_count = 0; - } - - p->tail->items[p->tail_count] = val; - p->tail_count++; - p->count++; - return 0; -} - -void array_print(Value v) -{ - ArrayValue *p = get_array(v); - ArrayItems *batch = &p->head; - int cursor = 0; - while (batch) { - - int num = ITEMS_PER_MAP_BATCH; - if (batch->next == NULL) - num = p->tail_count; - - for (int i = 0; i < num; i++) - value_print(batch->items[i]); - - batch = batch->next; - cursor += num; - } -} - -b32 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; -} - -b32 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; -} - -void value_print(Value v) -{ - switch (type_of(v)) { - - case TYPE_NONE: - printf("none"); - break; - - case TYPE_BOOL: - printf(v == VALUE_TRUE ? "true" : "false"); - break; - - case TYPE_INT: - printf("%" LLD, get_int(v)); - break; - - case TYPE_FLOAT: - printf("%lf", get_float(v)); - break; - - case TYPE_MAP: - map_print(v); - break; - - case TYPE_ARRAY: - array_print(v); - break; - - case TYPE_STRING: - { - String s = get_str(v); - printf("%.*s", s.len, s.ptr); - } - break; - - case TYPE_ERROR: - printf("error"); - break; - } - fflush(stdout); -} - -int step(Eval *e) -{ - uint8_t opcode = e->code.ptr[e->off]; + uint8_t opcode = read_u8(state); /* printf("%-3d: ", e->off); - print_instruction(e->code.ptr + e->off, e->data.ptr); + print_instruction(state->code.ptr + e->off, e->data.ptr); printf("\n"); */ - e->off++; switch (opcode) { @@ -560,247 +122,258 @@ int step(Eval *e) case OPCODE_GROUP: { - e->groups[e->num_groups++] = e->eval_depth; + state->groups[state->num_groups++] = state->eval_depth; } break; case OPCODE_GPOP: { - int group = e->groups[--e->num_groups]; - e->eval_depth = group; + int group = state->groups[--state->num_groups]; + state->eval_depth = group; } break; case OPCODE_GPRINT: { - for (int i = e->groups[e->num_groups-1]; i < e->eval_depth; i++) - value_print(e->eval_stack[i]); + state->num_prints = state->eval_depth - state->groups[state->num_groups-1]; } break; case OPCODE_GCOALESCE: { - e->num_groups--; + state->num_groups--; } break; case OPCODE_GTRUNC: { - uint32_t num; - memcpy(&num, (uint8_t*) e->code.ptr + e->off, sizeof(uint32_t)); - e->off += (int) sizeof(uint32_t); + uint32_t num = read_u32(state); - int group_size = e->eval_depth - e->groups[e->num_groups-1]; + int group_size = state->eval_depth - state->groups[state->num_groups-1]; - if (group_size < (int) num) { + if (group_size < (int) num) for (int i = 0; i < (int) num - group_size; i++) - e->eval_stack[e->eval_depth + i] = VALUE_NONE; - } + state->eval_stack[state->eval_depth + i] = VALUE_NONE; - e->eval_depth = e->groups[e->num_groups-1] + num; + state->eval_depth = state->groups[state->num_groups-1] + num; } break; case OPCODE_GOVERWRITE: { - int current = e->groups[e->num_groups-1]; - int parent = e->groups[e->num_groups-2]; + int current = state->groups[state->num_groups-1]; + int parent = state->groups[state->num_groups-2]; - int current_size = e->eval_depth - current; + int current_size = state->eval_depth - current; for (int i = 0; i < current_size; i++) - e->eval_stack[parent + i] = e->eval_stack[current + i]; + state->eval_stack[parent + i] = state->eval_stack[current + i]; - e->num_groups--; - e->eval_depth = parent + current_size; + state->num_groups--; + state->eval_depth = parent + current_size; } break; case OPCODE_GPACK: { - Value array = make_array(e); - if (array == VALUE_ERROR) + Value array = make_array(state->a); + if (array == VALUE_ERROR) { + eval_report(state, "Out of memory"); return -1; - for (int i = e->groups[e->num_groups-1]; i < e->eval_depth; i++) - array_append(e, array, e->eval_stack[i]); + } - e->eval_depth = e->groups[--e->num_groups]; - e->eval_stack[e->eval_depth++] = array; + 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: { - e->eval_stack[e->eval_depth++] = VALUE_NONE; + state->eval_stack[state->eval_depth++] = VALUE_NONE; } break; case OPCODE_PUSHI: { - int64_t x; - memcpy(&x, (uint8_t*) e->code.ptr + e->off, sizeof(x)); - e->off += (int) sizeof(x); + int64_t x = read_s64(state); - Value v = make_int(e, x); - if (v == VALUE_ERROR) return -1; + Value v = make_int(state->a, x); + if (v == VALUE_ERROR) { + eval_report(state, "Out of memory"); + return -1; + } - e->eval_stack[e->eval_depth++] = v; + state->eval_stack[state->eval_depth++] = v; } break; case OPCODE_PUSHF: { - double x; - memcpy(&x, (uint8_t*) e->code.ptr + e->off, sizeof(x)); - e->off += (int) sizeof(x); + double x = read_f64(state); - Value v = make_float(e, x); - if (v == VALUE_ERROR) return -1; + Value v = make_float(state->a, x); + if (v == VALUE_ERROR) { + eval_report(state, "Out of memory"); + return -1; + } - e->eval_stack[e->eval_depth++] = v; + state->eval_stack[state->eval_depth++] = v; } break; case OPCODE_PUSHS: { - uint32_t off; - memcpy(&off, (uint8_t*) e->code.ptr + e->off, sizeof(uint32_t)); - e->off += (int) sizeof(uint32_t); + uint32_t off = read_u32(state); + uint32_t len = read_u32(state); - uint32_t len; - memcpy(&len, (uint8_t*) e->code.ptr + e->off, sizeof(uint32_t)); - e->off += (int) sizeof(uint32_t); + Value v = make_str(state->a, (String) { state->data.ptr + off, len }); + if (v == VALUE_ERROR) { + eval_report(state, "Out of memory"); + return -1; + } - Value v = make_str(e, (String) { e->data.ptr + off, len }); - if (v == VALUE_ERROR) return -1; - - e->eval_stack[e->eval_depth++] = v; + state->eval_stack[state->eval_depth++] = v; } break; case OPCODE_PUSHV: { - uint8_t idx; - memcpy(&idx, (uint8_t*) e->code.ptr + e->off, sizeof(uint8_t)); - e->off += sizeof(uint8_t); + uint8_t idx = read_u8(state); - int group = e->frames[e->num_frames-1].group; - Value v = e->eval_stack[e->groups[group] + idx]; + int group = state->frames[state->num_frames-1].group; + Value v = state->eval_stack[state->groups[group] + idx]; - e->eval_stack[e->eval_depth++] = v; + state->eval_stack[state->eval_depth++] = v; } break; case OPCODE_PUSHA: { - uint32_t cap; - memcpy(&cap, (uint8_t*) e->code.ptr + e->off, sizeof(uint32_t)); - e->off += sizeof(uint32_t); + uint32_t cap = read_u32(state); + (void) cap; - Value v = make_array(e); - if (v == VALUE_ERROR) return -1; + Value v = make_array(state->a); + if (v == VALUE_ERROR) { + eval_report(state, "Out of memory"); + return -1; + } - e->eval_stack[e->eval_depth++] = v; + state->eval_stack[state->eval_depth++] = v; } break; case OPCODE_PUSHM: { - uint32_t cap; - memcpy(&cap, (uint8_t*) e->code.ptr + e->off, sizeof(uint32_t)); - e->off += sizeof(uint32_t); + uint32_t cap = read_u32(state); + (void) cap; - Value v = make_map(e); - if (v == VALUE_ERROR) return -1; + Value v = make_map(state->a); + if (v == VALUE_ERROR) { + eval_report(state, "Out of memory"); + return -1; + } - e->eval_stack[e->eval_depth++] = v; + state->eval_stack[state->eval_depth++] = v; } break; case OPCODE_POP: { - assert(e->num_groups == 0 || e->eval_depth > e->groups[e->num_groups-1]); - e->eval_depth--; + assert(state->num_groups == 0 || state->eval_depth > state->groups[state->num_groups-1]); + state->eval_depth--; } break; case OPCODE_NEG: { - Value a = e->eval_stack[--e->eval_depth]; + 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(e, -get_int(a)); - else if (t == TYPE_FLOAT) r = make_float(e, -get_float(a)); + 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(e, "Invalid operation on non-numeric value"); + eval_report(state, "Invalid operation on non-numeric value"); return -1; } - e->eval_stack[e->eval_depth++] = r; + 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 = e->eval_stack[e->eval_depth-2]; - Value b = e->eval_stack[e->eval_depth-1]; - e->eval_depth -= 2; + 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; - e->eval_stack[e->eval_depth++] = r; + state->eval_stack[state->eval_depth++] = r; } break; case OPCODE_NQL: { - Value a = e->eval_stack[e->eval_depth-2]; - Value b = e->eval_stack[e->eval_depth-1]; - e->eval_depth -= 2; + 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; - e->eval_stack[e->eval_depth++] = r; + state->eval_stack[state->eval_depth++] = r; } break; case OPCODE_LSS: { - Value a = e->eval_stack[e->eval_depth-2]; - Value b = e->eval_stack[e->eval_depth-1]; - e->eval_depth -= 2; + 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(e, "Invalid operation on non-numeric value"); + eval_report(state, "Invalid operation on non-numeric value"); return -1; } Value r = valgrt(a, b) || valeq(a, b) ? VALUE_FALSE : VALUE_TRUE; - e->eval_stack[e->eval_depth++] = r; + state->eval_stack[state->eval_depth++] = r; } break; case OPCODE_GRT: { - Value a = e->eval_stack[e->eval_depth-2]; - Value b = e->eval_stack[e->eval_depth-1]; - e->eval_depth -= 2; + 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(e, "Invalid operation on non-numeric value"); + eval_report(state, "Invalid operation on non-numeric value"); return -1; } Value r = valgrt(a, b) ? VALUE_TRUE : VALUE_FALSE; - e->eval_stack[e->eval_depth++] = r; + state->eval_stack[state->eval_depth++] = r; } break; case OPCODE_ADD: { - Value a = e->eval_stack[e->eval_depth-2]; - Value b = e->eval_stack[e->eval_depth-1]; - e->eval_depth -= 2; + 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)) @@ -815,8 +388,7 @@ int step(Eval *e) int64_t u = get_int(a); int64_t v = get_int(b); // TODO: check overflow and underflow - r = make_int(e, u + v); - if (r == VALUE_ERROR) return -1; + r = make_int(state->a, u + v); } break; @@ -824,8 +396,7 @@ int step(Eval *e) { float u = (float) get_int(a); float v = get_float(b); - r = make_float(e, u + v); - if (r == VALUE_ERROR) return -1; + r = make_float(state->a, u + v); } break; @@ -833,8 +404,7 @@ int step(Eval *e) { float u = get_float(a); float v = (float) get_int(b); - r = make_float(e, u + v); - if (r == VALUE_ERROR) return -1; + r = make_float(state->a, u + v); } break; @@ -843,25 +413,29 @@ int step(Eval *e) float u = get_float(a); float v = get_float(b); // TODO: check overflow and underflow - r = make_float(e, u + v); - if (r == VALUE_ERROR) return -1; + r = make_float(state->a, u + v); } break; default: - eval_report(e, "Invalid operation on non-numeric value"); + eval_report(state, "Invalid operation on non-numeric value"); return -1; } - e->eval_stack[e->eval_depth++] = r; + 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 = e->eval_stack[e->eval_depth-2]; - Value b = e->eval_stack[e->eval_depth-1]; - e->eval_depth -= 2; + 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); @@ -874,8 +448,7 @@ int step(Eval *e) int64_t u = get_int(a); int64_t v = get_int(b); // TODO: check overflow and underflow - r = make_int(e, u - v); - if (r == VALUE_ERROR) return -1; + r = make_int(state->a, u - v); } break; @@ -883,8 +456,7 @@ int step(Eval *e) { float u = (float) get_int(a); float v = get_float(b); - r = make_float(e, u - v); - if (r == VALUE_ERROR) return -1; + r = make_float(state->a, u - v); } break; @@ -892,8 +464,7 @@ int step(Eval *e) { float u = get_float(a); float v = (float) get_int(b); - r = make_float(e, u - v); - if (r == VALUE_ERROR) return -1; + r = make_float(state->a, u - v); } break; @@ -902,25 +473,29 @@ int step(Eval *e) float u = get_float(a); float v = get_float(b); // TODO: check overflow and underflow - r = make_float(e, u - v); - if (r == VALUE_ERROR) return -1; + r = make_float(state->a, u - v); } break; default: - eval_report(e, "Invalid operation on non-numeric value"); + eval_report(state, "Invalid operation on non-numeric value"); return -1; } - e->eval_stack[e->eval_depth++] = r; + 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 = e->eval_stack[e->eval_depth-2]; - Value b = e->eval_stack[e->eval_depth-1]; - e->eval_depth -= 2; + 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); @@ -933,8 +508,7 @@ int step(Eval *e) int64_t u = get_int(a); int64_t v = get_int(b); // TODO: check overflow and underflow - r = make_int(e, u * v); - if (r == VALUE_ERROR) return -1; + r = make_int(state->a, u * v); } break; @@ -942,8 +516,7 @@ int step(Eval *e) { float u = (float) get_int(a); float v = get_float(b); - r = make_float(e, u * v); - if (r == VALUE_ERROR) return -1; + r = make_float(state->a, u * v); } break; @@ -951,8 +524,7 @@ int step(Eval *e) { float u = get_float(a); float v = (float) get_int(b); - r = make_float(e, u * v); - if (r == VALUE_ERROR) return -1; + r = make_float(state->a, u * v); } break; @@ -961,25 +533,29 @@ int step(Eval *e) float u = get_float(a); float v = get_float(b); // TODO: check overflow and underflow - r = make_float(e, u * v); - if (r == VALUE_ERROR) return -1; + r = make_float(state->a, u * v); } break; default: - eval_report(e, "Invalid operation on non-numeric value"); + eval_report(state, "Invalid operation on non-numeric value"); return -1; } - e->eval_stack[e->eval_depth++] = r; + 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 = e->eval_stack[e->eval_depth-2]; - Value b = e->eval_stack[e->eval_depth-1]; - e->eval_depth -= 2; + 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); @@ -993,8 +569,7 @@ int step(Eval *e) int64_t u = get_int(a); int64_t v = get_int(b); - r = make_int(e, u / v); - if (r == VALUE_ERROR) return -1; + r = make_int(state->a, u / v); } break; @@ -1004,8 +579,7 @@ int step(Eval *e) float u = (float) get_int(a); float v = get_float(b); - r = make_float(e, u / v); - if (r == VALUE_ERROR) return -1; + r = make_float(state->a, u / v); } break; @@ -1015,8 +589,7 @@ int step(Eval *e) float u = get_float(a); float v = (float) get_int(b); - r = make_float(e, u / v); - if (r == VALUE_ERROR) return -1; + r = make_float(state->a, u / v); } break; @@ -1024,75 +597,76 @@ int step(Eval *e) { float u = get_float(a); float v = get_float(b); - r = make_float(e, u / v); - if (r == VALUE_ERROR) return -1; + r = make_float(state->a, u / v); } break; default: - eval_report(e, "Invalid operation on non-numeric value"); + eval_report(state, "Invalid operation on non-numeric value"); return -1; } - e->eval_stack[e->eval_depth++] = r; + 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 = e->eval_stack[e->eval_depth-2]; - Value b = e->eval_stack[e->eval_depth-1]; - e->eval_depth -= 2; + 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(e, "Invalid modulo operation on non-integer value"); + 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(e, u % v); - if (r == VALUE_ERROR) return -1; + Value r = make_int(state->a, u % v); + if (r == VALUE_ERROR) { + eval_report(state, "Out of memory"); + return -1; + } - e->eval_stack[e->eval_depth++] = r; + state->eval_stack[state->eval_depth++] = r; } break; case OPCODE_SETV: { - uint8_t x; - memcpy(&x, (uint8_t*) e->code.ptr + e->off, (int) sizeof(x)); - e->off += (int) sizeof(x); + uint8_t x = read_u8(state); - Frame *f = &e->frames[e->num_frames-1]; - e->eval_stack[e->groups[f->group] + x] = e->eval_stack[--e->eval_depth]; + 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; - memcpy(&x, (uint8_t*) e->code.ptr + e->off, (int) sizeof(x)); - e->off = x; + uint32_t x = read_u32(state); + state->off = x; } break; case OPCODE_JIFP: { - uint32_t x; - memcpy(&x, (uint8_t*) e->code.ptr + e->off, (int) sizeof(x)); - e->off += (int) sizeof(x); - - Value a = e->eval_stack[--e->eval_depth]; + uint32_t x = read_u32(state); + Value a = state->eval_stack[--state->eval_depth]; if (a == VALUE_FALSE) - e->off = x; + state->off = x; else { if (a != VALUE_TRUE) { - eval_report(e, "Invalid operation on non-boolean value"); + eval_report(state, "Invalid operation on non-boolean value"); return -1; } } @@ -1101,65 +675,69 @@ int step(Eval *e) case OPCODE_CALL: { - uint32_t off; - memcpy(&off, (uint8_t*) e->code.ptr + e->off, sizeof(uint32_t)); - e->off += (int) sizeof(uint32_t); + uint32_t off = read_u32(state); - if (e->num_frames == FRAME_LIMIT) { - eval_report(e, "Frame limit reached"); + if (state->num_frames == FRAME_LIMIT) { + eval_report(state, "Frame limit reached"); return -1; } - e->frames[e->num_frames++] = (Frame) {.return_addr=e->off, .group=e->num_groups-1}; + state->frames[state->num_frames++] = (Frame) {.return_addr=state->off, .group=state->num_groups-1}; - e->off = off; + state->off = off; } break; case OPCODE_RET: { - e->off = e->frames[--e->num_frames].return_addr; + state->off = state->frames[--state->num_frames].return_addr; } break; case OPCODE_APPEND: { - Value val = e->eval_stack[e->eval_depth-1]; - Value set = e->eval_stack[e->eval_depth-2]; - e->eval_depth--; + 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(e, "Invalid operation on non-array value"); + eval_report(state, "Invalid operation on non-array value"); return -1; } - int ret = array_append(e, set, val); - if (ret < 0) 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 = e->eval_stack[e->eval_depth-1]; - Value val = e->eval_stack[e->eval_depth-2]; - Value set = e->eval_stack[e->eval_depth-3]; - e->eval_depth -= 2; + 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) { - Value *dst = array_select(e, set, key); + Value *dst = array_select(set, key); if (dst == NULL) { - eval_report(e, "Index out of range"); + eval_report(state, "Index out of range"); return -1; } *dst = val; } else if (type_of(set) == TYPE_MAP) { - int ret = map_insert(e, set, key, val); - if (ret < 0) return -1; + int ret = map_insert(state->a, set, key, val); + if (ret < 0) { + eval_report(state, "Out of memory"); + return -1; + } } else { - eval_report(e, "Invalid insertion on non-array and non-map value"); + eval_report(state, "Invalid insertion on non-array and non-map value"); return -1; } } @@ -1167,27 +745,30 @@ int step(Eval *e) case OPCODE_INSERT2: { - Value key = e->eval_stack[e->eval_depth-1]; - Value set = e->eval_stack[e->eval_depth-2]; - Value val = e->eval_stack[e->eval_depth-3]; - e->eval_depth -= 2; + 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) { - Value *dst = array_select(e, set, key); + Value *dst = array_select(set, key); if (dst == NULL) { - eval_report(e, "Index out of range"); + eval_report(state, "Index out of range"); return -1; } *dst = val; } else if (type_of(set) == TYPE_MAP) { - int ret = map_insert(e, set, key, val); - if (ret < 0) return -1; + int ret = map_insert(state->a, set, key, val); + if (ret < 0) { + eval_report(state, "Out of memory"); + return -1; + } } else { - eval_report(e, "Invalid insertion on non-array and non-map value"); + eval_report(state, "Invalid insertion on non-array and non-map value"); return -1; } } @@ -1195,14 +776,14 @@ int step(Eval *e) case OPCODE_SELECT: { - Value key = e->eval_stack[e->eval_depth-1]; - Value set = e->eval_stack[e->eval_depth-2]; - e->eval_depth -= 2; + 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) { - Value *src = array_select(e, set, key); + Value *src = array_select(set, key); if (src == NULL) { assert(0); // TODO } @@ -1210,65 +791,305 @@ int step(Eval *e) } else if (type_of(set) == TYPE_MAP) { - int ret = map_select(e, set, key, &r); + int ret = map_select(set, key, &r); if (ret < 0) { assert(0); // TODO } } else { - eval_report(e, "Invalid selection from non-array and non-map value"); + eval_report(state, "Invalid selection from non-array and non-map value"); return -1; } - e->eval_stack[e->eval_depth++] = r; + state->eval_stack[state->eval_depth++] = r; } break; case OPCODE_PRINT: { - Value v = e->eval_stack[e->eval_depth-1]; - value_print(v); + 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; + } + break; + + case OPCODE_SYSCALL: + { + uint32_t off = read_u32(state); + uint32_t len = read_u32(state); + String name = { state->data.ptr + off, len }; + + state->syscall = name; } break; default: - eval_report(e, "Invalid opcode (offset %d)", e->off-1); + eval_report(state, "Invalid opcode (offset %d)", state->off-1); return -1; } return 0; } -int eval(Program p, Arena *a, char *errbuf, int errmax) +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, errbuf, errmax); + int ret = parse_program_header(p, &code, &data, err, errmax); if (ret < 0) - return -1; + return NULL; - Eval e = { + *state = (WL_State) { .code=code, .data=data, .off=0, .a=a, - .errbuf=errbuf, + .errbuf=err, .errmax=errmax, .errlen=0, .num_frames=0, .eval_depth=0, .num_groups=0, + .num_prints=0, + .cur_print=0, }; - e.frames[e.num_frames++] = (Frame) { 0, 0 }; + state->frames[state->num_frames++] = (Frame) { 0, 0 }; - for (;;) { - int ret = step(&e); - if (ret < 0) return -1; - if (ret == 1) break; + return state; +} + +void WL_State_free(WL_State *state) +{ + state->num_frames--; + + // TODO +} + +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 } }; + + // TODO + state->sysvar = S(""); } - e.num_frames--; - return 0; + if (state->syscall.len > 0) { + + if (state->syscall_error) + return (WL_Result) { WL_ERROR, (WL_String) { NULL, 0 } }; + + 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_popint(WL_State *state, long long *x) +{ + if (!in_syscall(state)) return 0; + + if (state->eval_depth == 0) + 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 true; +} + +int WL_popfloat(WL_State *state, float *x) +{ + if (!in_syscall(state)) return 0; + + if (state->eval_depth == 0) + 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 true; +} + +int WL_popstr(WL_State *state, char **str, int *len) +{ + if (!in_syscall(state)) return 0; + + if (state->eval_depth == 0) + 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 = s.ptr; + *len = s.len; + + state->eval_depth--; + return true; +} + +int WL_popany(WL_State *state) +{ + if (!in_syscall(state)) + return 0; + + if (state->eval_depth == 0) + return 0; + + state->eval_depth--; + return 1; +} + +void WL_select(WL_State *state) +{ + // TODO +} + +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, char *str, int len) +{ + if (!in_syscall(state)) return; + + Value v = make_str(state->a, (String) { 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) +{ + // TODO } \ No newline at end of file diff --git a/src/eval.h b/src/eval.h index 5622851..c3edd6d 100644 --- a/src/eval.h +++ b/src/eval.h @@ -1,8 +1,11 @@ #ifndef WL_EVAL_INCLUDED #define WL_EVAL_INCLUDED +#ifndef WL_AMALGAMATION #include "assemble.h" +#endif -int eval(Program p, Arena *a, char *errbuf, int errmax); +// 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 index 402272f..048f026 100644 --- a/src/file.c +++ b/src/file.c @@ -1,15 +1,5 @@ -#include -#include - -#ifndef _WIN32 -#include -#include -#include -#include -#include -#endif - #ifndef WL_AMALGAMATION +#include "includes.h" #include "file.h" #endif diff --git a/src/file.h b/src/file.h index 4b93b96..3663dc5 100644 --- a/src/file.h +++ b/src/file.h @@ -2,12 +2,11 @@ #define WL_FILE_INCLUDED #ifndef WL_AMALGAMATION +#include "includes.h" #include "basic.h" #endif -#if _WIN32 -#define WIN32_LEAN_AND_MEAN -#include +#ifdef _WIN32 typedef HANDLE File; #else typedef int File; diff --git a/src/includes.h b/src/includes.h new file mode 100644 index 0000000..21a41cf --- /dev/null +++ b/src/includes.h @@ -0,0 +1,25 @@ +#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/main.c b/src/main.c deleted file mode 100644 index 177339f..0000000 --- a/src/main.c +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include - -#include "file.h" -#include "eval.h" -#include "parse.h" -#include "assemble.h" - -int main(void) -{ - String src; - int ret = file_read_all(S("main.wl"), &src); - if (ret < 0) { - printf("Error!\n"); - return -1; - } - - char err[1<<9]; - char *mem = malloc(1<<20); - Arena a = { mem, 1<<20, 0 }; - - ParseResult parse_result = parse(src, &a, err, COUNT(err)); - if (parse_result.node == NULL) { - printf("%.*s\n", parse_result.errlen, err); - return -1; - } - - print_node(parse_result.node); - printf("\n"); - - AssembleResult assemble_result = assemble(parse_result.node, &a, err, COUNT(err)); - if (assemble_result.errlen) { - printf("%.*s\n", parse_result.errlen, err); - return -1; - } - - print_program(assemble_result.program); - - ret = eval(assemble_result.program, &a, err, COUNT(err)); - if (ret < 0) { - printf("%s\n", err); - return -1; - } - - free(src.ptr); - free(mem); - return 0; -} diff --git a/src/parse.c b/src/parse.c index dc69a8e..8eaa54e 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1,6 +1,3 @@ -#include -#include -#include #ifndef WL_AMALGAMATION #include "parse.h" @@ -46,6 +43,7 @@ typedef enum { TOKEN_DOT, TOKEN_COMMA, TOKEN_COLON, + TOKEN_DOLLAR, TOKEN_NEWLINE, } TokType; @@ -61,13 +59,13 @@ typedef struct { typedef struct { Scanner s; - Arena *a; + WL_Arena *a; char *errbuf; int errmax; int errlen; } Parser; -b32 consume_str(Scanner *s, String x) +bool consume_str(Scanner *s, String x) { if (x.len == 0) return false; @@ -153,6 +151,7 @@ String tok2str(Token token, char *buf, int max) 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"); } @@ -395,6 +394,7 @@ Token next_token(Parser *p) 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 }; @@ -441,7 +441,7 @@ Node *parse_html(Parser *p) Node *param_head; Node **param_tail = ¶m_head; - b32 no_body = false; + bool no_body = false; for (;;) { int param_offset = p->s.cur; @@ -768,7 +768,7 @@ int precedence(Token t, int flags) return -1; } -b32 right_associative(Token t) +bool right_associative(Token t) { return t.type == TOKEN_OPER_ASS; } @@ -927,6 +927,26 @@ Node *parse_atom(Parser *p) } 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->text = (String) { p->s.src + start, p->s.cur - start }; + node->sval = t.sval; + + ret = node; + } + break; + default: { char buf[1<<8]; @@ -989,7 +1009,7 @@ Node *parse_atom(Parser *p) ret = parent; - } else if (t.type == TOKEN_PAREN_OPEN && ret->type == NODE_VALUE_VAR) { + } 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; @@ -1280,7 +1300,7 @@ Node *parse_while_stmt(Parser *p, int opflags) return parent; } -Node *parse_block_stmt(Parser *p, b32 curly) +Node *parse_block_stmt(Parser *p, bool curly) { int start = p->s.cur; @@ -1523,6 +1543,14 @@ void print_node(Node *node) { switch (node->type) { + case NODE_NESTED: + { + printf("("); + print_node(node->left); + printf(")"); + } + break; + case NODE_PRINT: { printf("print "); @@ -1653,6 +1681,10 @@ void print_node(Node *node) 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); @@ -1823,7 +1855,7 @@ void print_node(Node *node) } } -ParseResult parse(String src, Arena *a, char *errbuf, int errmax) +ParseResult parse(String src, WL_Arena *a, char *errbuf, int errmax) { Parser p = { .s={ src.ptr, src.len, 0 }, diff --git a/src/parse.h b/src/parse.h index dcf7991..73b8cb2 100644 --- a/src/parse.h +++ b/src/parse.h @@ -1,9 +1,8 @@ #ifndef WL_PARSE_INCLUDED #define WL_PARSE_INCLUDED -#include - #ifndef WL_AMALGAMATION +#include "includes.h" #include "basic.h" #endif @@ -35,6 +34,7 @@ typedef enum { NODE_VALUE_FLOAT, NODE_VALUE_STR, NODE_VALUE_VAR, + NODE_VALUE_SYSVAR, NODE_VALUE_HTML, NODE_VALUE_ARRAY, NODE_VALUE_MAP, @@ -83,6 +83,6 @@ typedef struct { } ParseResult; void print_node(Node *node); -ParseResult parse(String src, Arena *a, char *errbuf, int errmax); +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/public.c b/src/public.c new file mode 100644 index 0000000..d8c6f92 --- /dev/null +++ b/src/public.c @@ -0,0 +1,41 @@ + +#ifndef WL_AMALGAMATION +#include "eval.h" +#include "parse.h" +#include "assemble.h" +#include "WL.h" +#endif + +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; +} + +int WL_compile(char *src, int len, WL_Arena arena, WL_Program *program, char *err, int errmax) +{ + if (src == NULL) src = ""; + if (len < 0) len = strlen(src); + + Node *root; + ParseResult pres = parse((String) {src, len}, &arena, err, errmax); + if (pres.node == NULL) + return -1; + root = pres.node; + + AssembleResult ares = assemble(root, &arena, err, errmax); + if (ares.errlen) + return -1; + *program = ares.program; + + return 0; +} \ No newline at end of file diff --git a/src/public.h b/src/public.h new file mode 100644 index 0000000..6cd983e --- /dev/null +++ b/src/public.h @@ -0,0 +1,56 @@ +#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; + +int WL_compile (char *src, int len, WL_Arena arena, WL_Program *program, char *err, int errmax); +WL_State *WL_State_init (WL_Arena *a, WL_Program p, char *err, int errmax); +void WL_State_free (WL_State *state); +WL_Result WL_eval (WL_State *state); +int WL_streq (WL_String a, char *b, int blen); +int WL_popint (WL_State *state, long long *x); +int WL_popfloat (WL_State *state, float *x); +int WL_popstr (WL_State *state, char **str, int *len); +int WL_popany (WL_State *state); +void WL_select (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, char *str, int len); +void WL_pusharray (WL_State *state, int cap); +void WL_pushmap (WL_State *state, int cap); +void WL_insert (WL_State *state); + +#endif // WL_PUBLIC_INCLUDED diff --git a/src/value.c b/src/value.c new file mode 100644 index 0000000..20baeaf --- /dev/null +++ b/src/value.c @@ -0,0 +1,458 @@ + +#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; + + 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; + + 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; +} + +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; + 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_MAP_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_MAP_BATCH) { + + ArrayItems *batch = alloc(a, (int) sizeof(ArrayItems), _Alignof(ArrayItems)); + if (batch == NULL) + return -1; + + batch->next = NULL; + 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; +} + +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); + 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_MAP_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 new file mode 100644 index 0000000..3038b27 --- /dev/null +++ b/src/value.h @@ -0,0 +1,44 @@ +#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); +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_to_string(Value v, char *dst, int max); + +#endif // WL_VALUE_INCLUDED \ No newline at end of file