diff --git a/.DS_Store b/.DS_Store index 12b0a34..0a83c1e 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..44a86ab --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +// A launch configuration that launches the extension inside a new window +// Use IntelliSense to learn about possible attributes. +// Hover to view descriptions of existing attributes. +// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Extension", + "type": "extensionHost", + "request": "launch", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}" + ] + } + ] +} diff --git a/COMMENTS.md b/COMMENTS.md new file mode 100644 index 0000000..fd1a955 --- /dev/null +++ b/COMMENTS.md @@ -0,0 +1,46 @@ +# Collected comments from `src/splice.h` + +This file lists the comments that were present in `src/splice.h` before removal, along with their approximate line numbers (based on the header at the time of extraction). The header was stripped of comments to reduce size for embedded builds; this file preserves the removed comments for reference. + +Comments in `src/splice.h` (by approximate line number): + +- Line 3: `/* Splice runtime header */` +- Line 67: `/* Diagnostics */` +- Line 90: `/* Values / Objects */` +- Line 113: `/* SDK include */` +- Line 116: `/* AST */` +- Line 157: `/* "+", "-", "*", "/", "==", "&&", "!" ... */` (inline on `char *op`) +- Line 159: `/* may be NULL for unary */` (inline on `ASTNode *right`) +- Line 185: `/* may be NULL */` (inline on `else_branch`) +- Line 238: `/* Env (vars + funcs) */` +- Line 251: `/* power of 2 = fast modulo */` (inline on `#define VAR_TABLE_SIZE`) +- Line 254: `/* interned or strdup */` (inline on `char *name`) +- Line 292: `/* empty slot = not found */` (inline on `return NULL`) +- Line 297: `/* linear probe */` (inline on `idx = (idx + 1) & ...`) +- Line 370: `/* Forward declarations */` +- Line 455: `/* AST alloc/free */` +- Line 465: `/* AST serialization helpers */` +- Line 481: `/* !SPLICE_EMBED */` (in `#endif /* !SPLICE_EMBED */`) +- Line 587: `/* =========================\n Runtime eval/interpret\n ========================= */` (decorative) +- Line 864: `/* string concat on + */` +- Line 939: `/* ---------------- recursion safety ---------------- */` +- Line 945: `/* ============================================================\n NATIVE C FUNCTIONS\n ============================================================ */` +- Line 967: `/* ============================================================\n USER-DEFINED FUNCTION\n ============================================================ */` +- Line 975: `/* ============================================================\n SAVE OLD PARAM VALUES (GLOBAL TABLE HACK)\n ============================================================ */` +- Line 987: `/* shallow copy is enough */` (inline on `saved[i] = *v;`) +- Line 994: `/* ============================================================\n BIND PARAMETERS\n ============================================================ */` +- Line 1017: `/* ============================================================` (decorative) +- Line 1027: `/* normal execution */` +- Line 1029: `/* no return → default 0 */` +- Line 1031: `/* returned via AST_RETURN */` +- Line 1037: `/* ============================================================` (decorative) +- Line 1048: `/* param did not exist before → reset to 0 */` +- Line 1081: `/* take ownership of string returned by eval */` +- Line 1327: `/* =========================` (decorative) +- Line 1337: `/* Splice_H */` (in `#endif /* Splice_H */`) + +Notes: +- Many comments were decorative section dividers or short inline hints. All have been removed from `src/splice.h` to reduce header size for embedded builds. +- If you want to retain a subset, I can reintroduce selected comments behind `#if !SPLICE_EMBED` so they remain only in desktop builds. + +If you want an exact before/after diff with line numbers, I can produce that next. \ No newline at end of file diff --git a/Docs/doc/docs.md b/Docs/doc/docs.md index 9d54f44..e6acbad 100644 --- a/Docs/doc/docs.md +++ b/Docs/doc/docs.md @@ -215,7 +215,9 @@ This design allows: --- -## Embedding Splice in C +## SpliceCSDK + +### Embedding Splice in C Splice is designed to be embedded in C applications. @@ -261,9 +263,38 @@ void loop() {} ``` ---- +### Splice functions + +Splice has functions that let you configure the VM. These Function are great to configure the VM for specfic tasks. + +#### ``splice_set_call_depth`` +This function is designed to set a recursion max depth. This is great for limiting how many times a function can recurse. Below is an example of showing this happening. + +``` C +#include +#include +#include +#include "splice.h" +// This code will tell splice that after 100 recursions to stop. +splice_set_call_depth(100); +ASTNode *root = read_ast_from_spc(arg); +interpret(root); +free_ast(root); +``` +#### ``splice_disable_tick_limit`` +This function is designed to disable Splice's default **Tick Limit** (Currently set to ``1000000``) +This function works cleanly with the [``splice_set_tick``](#splice_set_tick) + +#### ``splice_set_tick`` + + +> This function **will only** work when ``splice_disable_tick_limit`` is in the script + +This function is desinged to set how many instructions will run before exiting + +--- © Copyright 2026 OpenSplice and the Sinha Group -Licensed under the MIT License \ No newline at end of file +Licensed under the MIT License diff --git a/bin/Splice b/bin/Splice index 3fa83d3..c98b0b5 100755 Binary files a/bin/Splice and b/bin/Splice differ diff --git a/bin/spbuild b/bin/spbuild index a618b4f..be68b35 100755 Binary files a/bin/spbuild and b/bin/spbuild differ diff --git a/examples/array/array.spc b/examples/array/array.spc new file mode 100644 index 0000000..50a8f00 Binary files /dev/null and b/examples/array/array.spc differ diff --git a/examples/array/array.spl b/examples/array/array.spl index af129f8..6227e89 100644 --- a/examples/array/array.spl +++ b/examples/array/array.spl @@ -1,4 +1,4 @@ -let nums = [1,2,3]; +nums = [1,2,3]; print(len(nums)); append(nums, 42); print(len(nums)); diff --git a/examples/calculator/hello.spc b/examples/calculator/hello.spc index 6c6a4b8..06ea798 100644 Binary files a/examples/calculator/hello.spc and b/examples/calculator/hello.spc differ diff --git a/examples/forloops/for.spc b/examples/forloops/for.spc index 36e3e3f..75bb5b6 100644 Binary files a/examples/forloops/for.spc and b/examples/forloops/for.spc differ diff --git a/recurse.spc b/recurse.spc deleted file mode 100644 index e62abf3..0000000 Binary files a/recurse.spc and /dev/null differ diff --git a/recurse.spl b/recurse.spl deleted file mode 100644 index 7e26be8..0000000 --- a/recurse.spl +++ /dev/null @@ -1,9 +0,0 @@ -let i = 0 - -while (i < 5) { - i = i + 1 - if (i == 3) { - continue - } - print(i) -} diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/src/.DS_Store differ diff --git a/src/build.c b/src/build.c index ab8c270..6965069 100644 --- a/src/build.c +++ b/src/build.c @@ -1,103 +1,262 @@ #include #include #include +#include #include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#endif + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif -#include "splice.h" - /* uses AST types + write_ast_to_spc */ -ASTNode* ast_tuple(ASTNode** items, int count) { - ASTNode* node = malloc(sizeof(ASTNode)); - node->type = AST_TUPLE; - node->tuple.items = items; - node->tuple.count = count; - return node; +#ifdef _WIN32 +#define splice_getcwd _getcwd +#else +#define splice_getcwd getcwd +#endif + +/* ========================================================= + This builder emits SPC compatible with the simplified VM + header you posted: + - u8 node type + - strings: u32 len + bytes + - numbers: 8 bytes raw (double) + - STATEMENTS: u32 count + nodes + - LET/ASSIGN: str name + node + - BINARY_OP: str op + left + right + - PRINT: node + - WHILE: cond + body + - IF: cond + then + else (else can be NULL node) + - FOR: var + start + end + body + - FUNC_DEF: name + body + - FUNCTION_CALL: name + - RETURN: expr (can be NULL node) + - BREAK/CONTINUE: no payload + ========================================================= */ + +/* ===== SPC header ===== */ +#define SPC_MAGIC "SPC\0" +#define SPC_VERSION 1 + +/* ===== AST types (MUST MATCH YOUR VM) ===== */ +typedef enum { + AST_NUMBER = 0, + AST_STRING, + AST_IDENTIFIER, + AST_BINARY_OP, + AST_LET, + AST_ASSIGN, + AST_BREAK, + AST_PRINT, + AST_CONTINUE, + AST_WHILE, + AST_IF, + AST_STATEMENTS, + AST_FUNC_DEF, + AST_FUNCTION_CALL, + AST_RETURN, + AST_FOR +} ASTNodeType; + +typedef struct ASTNode ASTNode; + +struct ASTNode { + ASTNodeType type; + union { + double number; + char *string; + + struct { char *op; ASTNode *left; ASTNode *right; } binop; + struct { char *name; ASTNode *value; } var; + struct { ASTNode *expr; } print; + struct { ASTNode *cond; ASTNode *body; } whilestmt; + struct { ASTNode *cond; ASTNode *then_b; ASTNode *else_b; } ifstmt; + struct { ASTNode **stmts; int count; } statements; + struct { char *name; ASTNode *body; } funcdef; + struct { char *name; } funccall; + struct { ASTNode *expr; } retstmt; + struct { char *var; ASTNode *start; ASTNode *end; ASTNode *body; } forstmt; + }; +}; + +/* ===== utils ===== */ +static void die(const char *msg) { fprintf(stderr, "%s\n", msg); exit(1); } + +static void *xmalloc(size_t n) { + void *p = malloc(n); + if (!p) die("spbuild: OOM"); + return p; +} +static void *xrealloc(void *p, size_t n) { + void *q = realloc(p, n); + if (!q) die("spbuild: OOM"); + return q; +} +static char *xstrdup(const char *s) { + if (!s) s = ""; + size_t n = strlen(s); + char *p = (char*)xmalloc(n + 1); + memcpy(p, s, n + 1); + return p; } -static inline int write_ast_to_spc(const char *out_file, const ASTNode *root) { - FILE *f = fopen(out_file, "wb"); - if (!f) return 0; +/* ===== AST constructors ===== */ +static ASTNode *ast_new(ASTNodeType t) { + ASTNode *n = (ASTNode*)xmalloc(sizeof(ASTNode)); + memset(n, 0, sizeof(*n)); + n->type = t; + return n; +} - fwrite(SPC_MAGIC, 1, 4, f); - w_u8(f, (unsigned char)SPC_VERSION); +static ASTNode *ast_number(double v) { ASTNode *n=ast_new(AST_NUMBER); n->number=v; return n; } +static ASTNode *ast_string(const char *s) { ASTNode *n=ast_new(AST_STRING); n->string=xstrdup(s); return n; } +static ASTNode *ast_ident(const char *s) { ASTNode *n=ast_new(AST_IDENTIFIER); n->string=xstrdup(s); return n; } - write_ast_node(f, root); - fclose(f); - return 1; +static ASTNode *ast_binop(const char *op, ASTNode *l, ASTNode *r) { + ASTNode *n = ast_new(AST_BINARY_OP); + n->binop.op = xstrdup(op); + n->binop.left = l; + n->binop.right = r; + return n; } -static const char *resolve_builtin_import(const char *name) { -#ifdef _WIN32 - const char *prefix = "C:\\Program Files\\Splice\\"; -#else - const char *prefix = "/usr/local/bin/"; -#endif - if (strcmp(name, "math") == 0) { - static char path[256]; - snprintf(path, sizeof(path), "%ssplib/math.spc", prefix); - return path; - } +static ASTNode *ast_print(ASTNode *e) { ASTNode *n=ast_new(AST_PRINT); n->print.expr=e; return n; } - if (strcmp(name, "io") == 0) { - static char path[256]; - snprintf(path, sizeof(path), "%ssplib/io.spc", prefix); - return path; - } +static ASTNode *ast_var(ASTNodeType t, const char *name, ASTNode *val) { + ASTNode *n = ast_new(t); + n->var.name = xstrdup(name); + n->var.value = val; + return n; +} - return name; /* fallback: user-provided path */ +static ASTNode *ast_statements(ASTNode **stmts, int count) { + ASTNode *n=ast_new(AST_STATEMENTS); + n->statements.stmts=stmts; + n->statements.count=count; + return n; } +static ASTNode *ast_while(ASTNode *c, ASTNode *b) { + ASTNode *n=ast_new(AST_WHILE); + n->whilestmt.cond=c; n->whilestmt.body=b; + return n; +} -/* ========================= - Read whole file - ========================= */ -static char *read_text_file(const char *path) { - FILE *f = fopen(path, "rb"); - if (!f) return NULL; - fseek(f, 0, SEEK_END); - long n = ftell(f); - rewind(f); - char *buf = (char*)malloc((size_t)n + 1); - if (!buf) { fclose(f); return NULL; } - if (fread(buf, 1, (size_t)n, f) != (size_t)n) { fclose(f); free(buf); return NULL; } - buf[n] = 0; - fclose(f); - return buf; +static ASTNode *ast_if(ASTNode *c, ASTNode *t, ASTNode *e) { + ASTNode *n=ast_new(AST_IF); + n->ifstmt.cond=c; n->ifstmt.then_b=t; n->ifstmt.else_b=e; + return n; } -/* ========================= - Tokenizer - ========================= */ -typedef enum { - TK_EOF=0, - TK_IDENT, - TK_NUMBER, - TK_STRING, +static ASTNode *ast_for(const char *v, ASTNode *s, ASTNode *e, ASTNode *b) { + ASTNode *n=ast_new(AST_FOR); + n->forstmt.var=xstrdup(v); + n->forstmt.start=s; n->forstmt.end=e; n->forstmt.body=b; + return n; +} - TK_LET, TK_FUNC, TK_RETURN, TK_PRINT, TK_INPUT, - TK_READ, TK_WRITE, - TK_IF, TK_ELSE, TK_WHILE, TK_FOR, TK_IN, - TK_RAISE, - TK_TRUE, TK_FALSE, - TK_AND, TK_OR, TK_NOT,TK_DOTDOT,TK_BREAK,TK_CONTINUE, +static ASTNode *ast_funcdef(const char *name, ASTNode *body) { + ASTNode *n=ast_new(AST_FUNC_DEF); + n->funcdef.name=xstrdup(name); + n->funcdef.body=body; + return n; +} + +static ASTNode *ast_call0(const char *name) { + ASTNode *n=ast_new(AST_FUNCTION_CALL); + n->funccall.name=xstrdup(name); + return n; +} +static ASTNode *ast_return(ASTNode *e) { ASTNode *n=ast_new(AST_RETURN); n->retstmt.expr=e; return n; } + +/* ===== free AST (builder side) ===== */ +static void free_ast(ASTNode *n) { + if (!n) return; + switch (n->type) { + case AST_STRING: + case AST_IDENTIFIER: + free(n->string); + break; + case AST_BINARY_OP: + free(n->binop.op); + free_ast(n->binop.left); + free_ast(n->binop.right); + break; + case AST_PRINT: + free_ast(n->print.expr); + break; + case AST_LET: + case AST_ASSIGN: + free(n->var.name); + free_ast(n->var.value); + break; + case AST_STATEMENTS: + for (int i=0;istatements.count;i++) free_ast(n->statements.stmts[i]); + free(n->statements.stmts); + break; + case AST_WHILE: + free_ast(n->whilestmt.cond); + free_ast(n->whilestmt.body); + break; + case AST_IF: + free_ast(n->ifstmt.cond); + free_ast(n->ifstmt.then_b); + free_ast(n->ifstmt.else_b); + break; + case AST_FOR: + free(n->forstmt.var); + free_ast(n->forstmt.start); + free_ast(n->forstmt.end); + free_ast(n->forstmt.body); + break; + case AST_FUNC_DEF: + free(n->funcdef.name); + free_ast(n->funcdef.body); + break; + case AST_FUNCTION_CALL: + free(n->funccall.name); + break; + case AST_RETURN: + free_ast(n->retstmt.expr); + break; + default: + break; + } + free(n); +} +/* ========================================================= + LEXER + ========================================================= */ - TK_LPAREN, TK_RPAREN, - TK_LBRACE, TK_RBRACE, - TK_LBRACKET, TK_RBRACKET, - TK_COMMA, TK_SEMI, - TK_DOT,TK_QUIT, +typedef enum { + TK_EOF=0, + TK_IDENT, TK_NUMBER, TK_STRING, + + TK_LET, TK_PRINT, TK_IF, TK_ELSE, TK_WHILE, TK_FOR, TK_IN, + TK_FUNC, TK_RETURN, TK_BREAK, TK_CONTINUE, - TK_ASSIGN, /* = */ - TK_PLUS, TK_MINUS, TK_STAR, TK_SLASH, TK_IMPORT, + TK_LPAREN, TK_RPAREN, TK_LBRACE, TK_RBRACE, + TK_COMMA, TK_SEMI, + TK_ASSIGN, - TK_LT, TK_GT, TK_LE, TK_GE, TK_EQ, TK_NEQ + TK_PLUS, TK_MINUS, TK_STAR, TK_SLASH, TK_MOD, + TK_LT, TK_GT, TK_LE, TK_GE, TK_EQ, TK_NEQ, + TK_AND, TK_OR, TK_NOT, + TK_DOTDOT } TokType; typedef struct { TokType t; - char *lex; /* for IDENT/STRING */ - double num; /* for NUMBER */ + char *lex; + double num; int line; } Tok; @@ -107,106 +266,85 @@ typedef struct { int cap; } TokVec; -static void tv_init(TokVec *v) { v->data=NULL; v->count=0; v->cap=0; } -static void tv_push(TokVec *v, Tok x) { +static void tv_push(TokVec *v, Tok t) { if (v->count >= v->cap) { v->cap = v->cap ? v->cap*2 : 256; - Tok *nd = (Tok*)realloc(v->data, sizeof(Tok) * (size_t)v->cap); - if (!nd) error(0, "Splice/SystemError oom realloc tokens"); - v->data = nd; + v->data = (Tok*)xrealloc(v->data, sizeof(Tok)*(size_t)v->cap); } - v->data[v->count++] = x; -} -static void tv_free(TokVec *v) { - for (int i=0;icount;i++) free(v->data[i].lex); - free(v->data); + v->data[v->count++] = t; } -static int is_boundary(char c) { return !(isalnum((unsigned char)c) || c=='_'); } +static TokType kw_type(const char *id) { + if (!strcmp(id,"let")) return TK_LET; + if (!strcmp(id,"print")) return TK_PRINT; + if (!strcmp(id,"if")) return TK_IF; + if (!strcmp(id,"else")) return TK_ELSE; + if (!strcmp(id,"while")) return TK_WHILE; + if (!strcmp(id,"for")) return TK_FOR; + if (!strcmp(id,"in")) return TK_IN; + if (!strcmp(id,"func")) return TK_FUNC; + if (!strcmp(id,"return")) return TK_RETURN; + if (!strcmp(id,"break")) return TK_BREAK; + if (!strcmp(id,"continue")) return TK_CONTINUE; + return TK_IDENT; +} -static void tokenize(const char *src, TokVec *out) { +static void lex(const char *src, TokVec *out) { int line = 1; const char *p = src; while (*p) { - if (*p=='\n') { line++; p++; continue; } + if (*p == '\n') { line++; p++; tv_push(out,(Tok){.t=TK_SEMI,.line=line}); continue; } if (isspace((unsigned char)*p)) { p++; continue; } - /* comments // ... */ if (p[0]=='/' && p[1]=='/') { while (*p && *p!='\n') p++; continue; } - /* string "..." */ if (*p=='"') { p++; const char *s = p; - while (*p && *p!='"') p++; + while (*p && *p!='"') { + if (*p=='\\' && p[1]) p++; /* skip escaped char */ + p++; + } size_t n = (size_t)(p - s); - char *str = (char*)malloc(n+1); - if (!str) error(line, "Splice/SystemErroroom string"); - memcpy(str, s, n); - str[n]=0; - Tok t = { .t=TK_STRING, .lex=str, .line=line }; - tv_push(out, t); + char *buf = (char*)xmalloc(n+1); + /* simple unescape for \" and \\ and \n */ + size_t j=0; + for (size_t i=0;i= sizeof(tmp)) error(line, "Splice/OverflowError number too long"); - memcpy(tmp, s, n); - tmp[n]=0; - Tok t = { .t=TK_NUMBER, .num=strtod(tmp,NULL), .line=line }; - tv_push(out, t); + if (n >= sizeof(tmp)) die("number too long"); + memcpy(tmp,s,n); tmp[n]=0; + tv_push(out,(Tok){.t=TK_NUMBER,.num=strtod(tmp,NULL),.line=line}); continue; } - /* identifiers/keywords */ if (isalpha((unsigned char)*p) || *p=='_') { - const char *s = p; + const char *s=p; while (isalnum((unsigned char)*p) || *p=='_') p++; - size_t n = (size_t)(p - s); - char *id = (char*)malloc(n+1); - if (!id) error(line, "Splice/SystemErroroom ident"); - memcpy(id, s, n); - id[n]=0; - - TokType kw = TK_IDENT; - if (!strcmp(id,"let")) kw=TK_LET; - else if (!strcmp(id,"func")) kw=TK_FUNC; - else if (!strcmp(id,"return")) kw=TK_RETURN; - else if (!strcmp(id,"print")) kw=TK_PRINT; - else if (!strcmp(id,"raise")) kw=TK_RAISE; - else if (!strcmp(id,"if")) kw=TK_IF; - else if (!strcmp(id,"else")) kw=TK_ELSE; - else if (!strcmp(id,"while")) kw=TK_WHILE; - else if (!strcmp(id,"for")) kw=TK_FOR; - else if (!strcmp(id,"in")) kw=TK_IN; - else if (!strcmp(id,"read")) kw = TK_READ; - else if (!strcmp(id,"write")) kw = TK_WRITE; - else if (!strcmp(id,"true")) kw=TK_TRUE; - else if (!strcmp(id,"break")) kw = TK_BREAK; - else if (!strcmp(id, "continue")) kw = TK_CONTINUE; - else if (!strcmp(id,"false")) kw=TK_FALSE; - else if (!strcmp(id,"and")) kw=TK_AND; - else if (!strcmp(id,"or")) kw=TK_OR; - else if (!strcmp(id,"not")) kw=TK_NOT; - else if (!strcmp(id,"input")) kw=TK_INPUT; - else if (!strcmp(id,"import")) kw=TK_IMPORT; - else if (!strcmp(id,"quit")) kw=TK_QUIT; - - Tok t = { .t=kw, .lex=(kw==TK_IDENT? id : NULL), .line=line }; - if (kw!=TK_IDENT) free(id); - tv_push(out, t); + size_t n=(size_t)(p-s); + char *id=(char*)xmalloc(n+1); + memcpy(id,s,n); id[n]=0; + TokType t = kw_type(id); + if (t==TK_IDENT) tv_push(out,(Tok){.t=t,.lex=id,.line=line}); + else { free(id); tv_push(out,(Tok){.t=t,.line=line}); } continue; } @@ -215,540 +353,572 @@ static void tokenize(const char *src, TokVec *out) { if (p[0]=='!' && p[1]=='=') { tv_push(out,(Tok){.t=TK_NEQ,.line=line}); p+=2; continue; } if (p[0]=='<' && p[1]=='=') { tv_push(out,(Tok){.t=TK_LE,.line=line}); p+=2; continue; } if (p[0]=='>' && p[1]=='=') { tv_push(out,(Tok){.t=TK_GE,.line=line}); p+=2; continue; } - if (p[0]=='.' && p[1]=='.') { - tv_push(out, (Tok){ .t = TK_DOTDOT, .line = line }); - p += 2; - continue; - } - /* single-char */ + if (p[0]=='&' && p[1]=='&') { tv_push(out,(Tok){.t=TK_AND,.line=line}); p+=2; continue; } + if (p[0]=='|' && p[1]=='|') { tv_push(out,(Tok){.t=TK_OR,.line=line}); p+=2; continue; } + if (p[0]=='.' && p[1]=='.') { tv_push(out,(Tok){.t=TK_DOTDOT,.line=line}); p+=2; continue; } + + /* single char */ switch (*p) { - case '.': tv_push(out,(Tok){.t=TK_DOT,.line=line}); p++; break; case '(': tv_push(out,(Tok){.t=TK_LPAREN,.line=line}); p++; break; case ')': tv_push(out,(Tok){.t=TK_RPAREN,.line=line}); p++; break; case '{': tv_push(out,(Tok){.t=TK_LBRACE,.line=line}); p++; break; case '}': tv_push(out,(Tok){.t=TK_RBRACE,.line=line}); p++; break; - case '[': tv_push(out,(Tok){.t=TK_LBRACKET,.line=line}); p++; break; - case ']': tv_push(out,(Tok){.t=TK_RBRACKET,.line=line}); p++; break; case ',': tv_push(out,(Tok){.t=TK_COMMA,.line=line}); p++; break; case ';': tv_push(out,(Tok){.t=TK_SEMI,.line=line}); p++; break; - - - - - - case '=': tv_push(out,(Tok){.t=TK_ASSIGN,.line=line}); p++; break; case '+': tv_push(out,(Tok){.t=TK_PLUS,.line=line}); p++; break; case '-': tv_push(out,(Tok){.t=TK_MINUS,.line=line}); p++; break; case '*': tv_push(out,(Tok){.t=TK_STAR,.line=line}); p++; break; case '/': tv_push(out,(Tok){.t=TK_SLASH,.line=line}); p++; break; - + case '%': tv_push(out,(Tok){.t=TK_MOD,.line=line}); p++; break; case '<': tv_push(out,(Tok){.t=TK_LT,.line=line}); p++; break; case '>': tv_push(out,(Tok){.t=TK_GT,.line=line}); p++; break; - + case '!': tv_push(out,(Tok){.t=TK_NOT,.line=line}); p++; break; default: - error(line, "Splice/SyntaxError Unknown char: '%c'", *p); + fprintf(stderr,"lexer: unknown char '%c' at line %d\n", *p, line); + exit(1); } } - tv_push(out, (Tok){ .t=TK_EOF, .line=line }); + tv_push(out,(Tok){.t=TK_EOF,.line=line}); } -/* ========================= - Parser (recursive descent) - ========================= */ +static void tv_free(TokVec *v) { + for (int i=0;icount;i++) free(v->data[i].lex); + free(v->data); +} + +/* ========================================================= + PARSER (recursive descent) + ========================================================= */ + static TokVec *G; static int P; static Tok *peek(void) { return &G->data[P]; } -static Tok *prev(void) { return &G->data[P-1]; } static int at(TokType t) { return peek()->t == t; } -static Tok *consume(TokType t, const char *msg) { - if (!at(t)) error(peek()->line, "%s", msg); - return &G->data[P++]; -} +static Tok *advance(void) { return &G->data[P++]; } static int match(TokType t) { if (at(t)) { P++; return 1; } return 0; } -static ASTNode *parse_expression(void); -static ASTNode *parse_statement(void); -static ASTNode *parse_statements_until(TokType end); +static void expect(TokType t, const char *msg) { + if (!at(t)) { + fprintf(stderr,"parse error line %d: %s\n", peek()->line, msg); + exit(1); + } + P++; +} + +static ASTNode *parse_expr(void); +static ASTNode *parse_stmt(void); +static ASTNode *parse_block(void); +/* primary: number|string|ident|call| (expr) */ static ASTNode *parse_primary(void) { if (match(TK_NUMBER)) { - ASTNode *n = ast_new(AST_NUMBER); - n->number = prev()->num; - return n; + return ast_number(G->data[P-1].num); } if (match(TK_STRING)) { - ASTNode *n = ast_new(AST_STRING); - n->string = strdup(prev()->lex ? prev()->lex : ""); - return n; + return ast_string(G->data[P-1].lex ? G->data[P-1].lex : ""); } - if (match(TK_TRUE)) { ASTNode *n=ast_new(AST_NUMBER); n->number=1; return n; } - if (match(TK_FALSE)) { ASTNode *n=ast_new(AST_NUMBER); n->number=0; return n; } - if (match(TK_IDENT)) { - char *name = strdup(prev()->lex); - - /* call: ident(...) */ + const char *name = G->data[P-1].lex; + /* call: ident() only (no args in your VM struct) */ if (match(TK_LPAREN)) { - ASTNode **args = NULL; - int ac=0, cap=0; - - if (!at(TK_RPAREN)) { - for (;;) { - ASTNode *e = parse_expression(); - if (ac>=cap) { cap = cap?cap*2:8; args=(ASTNode**)realloc(args,sizeof(ASTNode*)*(size_t)cap); } - args[ac++] = e; - if (!match(TK_COMMA)) break; - } - } - consume(TK_RPAREN, "Splice/SyntaxError Expected ')' after call args"); - - ASTNode *c = ast_new(AST_FUNCTION_CALL); - c->funccall.funcname = name; - c->funccall.args = args; - c->funccall.arg_count = ac; - return c; + expect(TK_RPAREN, "Expected ')' after call"); + return ast_call0(name); } - - /* ident[index] */ - if (match(TK_LBRACKET)) { - ASTNode *idx = parse_expression(); - consume(TK_RBRACKET, "Splice/SyntaxError Expected ']' after index"); - - ASTNode *id = ast_new(AST_IDENTIFIER); - id->string = name; - - ASTNode *ix = ast_new(AST_INDEX_EXPR); - ix->indexexpr.target = id; - ix->indexexpr.index = idx; - return ix; - } - - ASTNode *id = ast_new(AST_IDENTIFIER); - id->string = name; - return id; + return ast_ident(name); } - if (match(TK_LPAREN)) { - ASTNode *first = parse_expression(); - - /* tuple if comma appears */ - if (match(TK_COMMA)) { - ASTNode **items = NULL; - int count = 0, cap = 0; - - /* first element */ - cap = 4; - items = (ASTNode**)malloc(sizeof(ASTNode*) * cap); - items[count++] = first; - - do { - if (count >= cap) { - cap *= 2; - items = (ASTNode**)realloc(items, sizeof(ASTNode*) * cap); - } - items[count++] = parse_expression(); - } while (match(TK_COMMA)); - - consume(TK_RPAREN, "Splice/SyntaxError Expected ')' after tuple"); - - ASTNode *t = ast_new(AST_TUPLE); - t->tuple.items = items; - t->tuple.count = count; - return t; - } - - /* otherwise normal grouping */ - consume(TK_RPAREN, "Splice/SyntaxError Expected ')'"); - return first; - } - - if (match(TK_READ)) { - consume(TK_LPAREN, "Splice/SyntaxError Expected '(' after read"); - ASTNode *e = parse_expression(); - consume(TK_RPAREN, "Splice/SyntaxError Expected ')' after read"); - - ASTNode *n = ast_new(AST_READ); - n->read.expr = e; - return n; - } - if (match(TK_INPUT)) { - consume(TK_LPAREN, "Splice/SyntaxError Expected '(' after input"); - ASTNode *e = parse_expression(); - consume(TK_RPAREN, "Splice/SyntaxError Expected ')' after input prompt"); - ASTNode *n = ast_new(AST_INPUT); - n->input.prompt = e; - return n; - } - if (match(TK_LBRACKET)) { - ASTNode **els=NULL; - int ec=0, cap=0; - - if (!at(TK_RBRACKET)) { - for (;;) { - ASTNode *e = parse_expression(); - if (ec>=cap) { cap = cap?cap*2:8; els=(ASTNode**)realloc(els,sizeof(ASTNode*)*(size_t)cap); } - els[ec++] = e; - if (!match(TK_COMMA)) break; - } - } - consume(TK_RBRACKET, "Splice/SyntaxError Expected ']' after array literal"); - - ASTNode *a = ast_new(AST_ARRAY_LITERAL); - a->arraylit.elements = els; - a->arraylit.count = ec; - return a; + ASTNode *e = parse_expr(); + expect(TK_RPAREN, "Expected ')'"); + return e; } - - error(peek()->line, "Splice/SyntaxError Expected expression"); - return NULL; + fprintf(stderr,"parse error line %d: expected primary\n", peek()->line); + exit(1); } +/* unary: -x or !x */ static ASTNode *parse_unary(void) { - if (match(TK_NOT)) { - ASTNode *n = ast_new(AST_BINARY_OP); - n->binop.op = strdup("!"); - n->binop.left = parse_unary(); - n->binop.right = NULL; - return n; - } if (match(TK_MINUS)) { - /* -(x) => (-1 * x) */ - ASTNode *mul = ast_new(AST_BINARY_OP); - mul->binop.op = strdup("*"); - ASTNode *m1 = ast_new(AST_NUMBER); - m1->number = -1.0; - mul->binop.left = m1; - mul->binop.right = parse_unary(); - return mul; + /* -(x) -> (-1 * x) */ + return ast_binop("*", ast_number(-1.0), parse_unary()); + } + if (match(TK_NOT)) { + /* !x -> (0 == x) is not correct; but your VM has no boolean type. + We'll implement !x as (x == 0) using binary op "!"? No. + We'll encode op "!" and expect VM to support it if you added it. + If VM doesn't support "!", remove this feature. + */ + return ast_binop("!", parse_unary(), NULL); } return parse_primary(); } +/* mul: * / % */ static ASTNode *parse_mul(void) { - ASTNode *left = parse_unary(); - while (at(TK_STAR) || at(TK_SLASH)) { - TokType op = peek()->t; P++; - ASTNode *n = ast_new(AST_BINARY_OP); - n->binop.op = strdup(op==TK_STAR ? "*" : "/"); - n->binop.left = left; - n->binop.right = parse_unary(); - left = n; - } - return left; + ASTNode *l = parse_unary(); + while (at(TK_STAR) || at(TK_SLASH) || at(TK_MOD)) { + TokType op = advance()->t; + const char *s = (op==TK_STAR)?"*":(op==TK_SLASH)?"/":"%"; + ASTNode *r = parse_unary(); + l = ast_binop(s, l, r); + } + return l; } +/* add: + - */ static ASTNode *parse_add(void) { - ASTNode *left = parse_mul(); + ASTNode *l = parse_mul(); while (at(TK_PLUS) || at(TK_MINUS)) { - TokType op = peek()->t; P++; - ASTNode *n = ast_new(AST_BINARY_OP); - n->binop.op = strdup(op==TK_PLUS ? "+" : "-"); - n->binop.left = left; - n->binop.right = parse_mul(); - left = n; + TokType op = advance()->t; + const char *s = (op==TK_PLUS)?"+":"-"; + ASTNode *r = parse_mul(); + l = ast_binop(s, l, r); } - return left; + return l; } +/* cmp: < > <= >= == != */ static ASTNode *parse_cmp(void) { - ASTNode *left = parse_add(); + ASTNode *l = parse_add(); while (at(TK_LT)||at(TK_GT)||at(TK_LE)||at(TK_GE)||at(TK_EQ)||at(TK_NEQ)) { - TokType op = peek()->t; P++; + TokType op = advance()->t; const char *s = (op==TK_LT)?"<":(op==TK_GT)?">":(op==TK_LE)?"<=":(op==TK_GE)?">=":(op==TK_EQ)?"==":"!="; - ASTNode *n = ast_new(AST_BINARY_OP); - n->binop.op = strdup(s); - n->binop.left = left; - n->binop.right = parse_add(); - left = n; + ASTNode *r = parse_add(); + l = ast_binop(s, l, r); } - return left; + return l; } +/* logic: && || (encoded as "&&" "||") */ static ASTNode *parse_logic(void) { - ASTNode *left = parse_cmp(); + ASTNode *l = parse_cmp(); while (at(TK_AND) || at(TK_OR)) { - TokType op = peek()->t; P++; - ASTNode *n = ast_new(AST_BINARY_OP); - n->binop.op = strdup(op==TK_AND ? "&&" : "||"); - n->binop.left = left; - n->binop.right = parse_cmp(); - left = n; + TokType op = advance()->t; + const char *s = (op==TK_AND)?"&&":"||"; + ASTNode *r = parse_cmp(); + l = ast_binop(s, l, r); } - return left; + return l; } -static ASTNode *parse_expression(void) { return parse_logic(); } - -static ASTNode *parse_statements_until(TokType end) { - ASTNode **stmts=NULL; - int c=0, cap=0; - - while (!at(end) && !at(TK_EOF)) { - ASTNode *s = parse_statement(); - if (!s) { /* allow empty */ } - else { - if (c>=cap) { cap=cap?cap*2:16; stmts=(ASTNode**)realloc(stmts,sizeof(ASTNode*)*(size_t)cap); } - stmts[c++] = s; - } - match(TK_SEMI); - } +static ASTNode *parse_expr(void) { return parse_logic(); } - ASTNode *blk = ast_new(AST_STATEMENTS); - blk->statements.stmts = stmts; - blk->statements.count = c; - return blk; +/* statement separators: many semis/newlines are ok */ +static void eat_separators(void) { + while (match(TK_SEMI)) {} } -static ASTNode *parse_statement(void) { - if (match(TK_LET)) { - Tok *id = consume(TK_IDENT, "Splice/SyntaxError Expected identifier after let"); - consume(TK_ASSIGN, "Splice/SyntaxError Expected '=' after let name"); - ASTNode *rhs = parse_expression(); +static ASTNode *parse_stmt(void) { + eat_separators(); - ASTNode *n = ast_new(AST_LET); - n->var.varname = strdup(id->lex); - n->var.value = rhs; - return n; - } - if (match(TK_CONTINUE)) { - return ast_new(AST_CONTINUE); + if (match(TK_LET)) { + expect(TK_IDENT, "Expected identifier after let"); + const char *name = G->data[P-1].lex; + expect(TK_ASSIGN, "Expected '=' after let name"); + ASTNode *rhs = parse_expr(); + return ast_var(AST_LET, name, rhs); } - if (match(TK_BREAK)) { - return ast_new(AST_BREAK); + + if (match(TK_PRINT)) { + expect(TK_LPAREN, "Expected '(' after print"); + ASTNode *e = parse_expr(); + expect(TK_RPAREN, "Expected ')' after print"); + return ast_print(e); } + if (match(TK_BREAK)) return ast_new(AST_BREAK); + if (match(TK_CONTINUE)) return ast_new(AST_CONTINUE); - if (match(TK_PRINT)) { - consume(TK_LPAREN, "Splice/SyntaxError Expected '(' after print"); - ASTNode *e = parse_expression(); - consume(TK_RPAREN, "Splice/SyntaxError Expected ')' after print expr"); - ASTNode *n = ast_new(AST_PRINT); - n->print.expr = e; - return n; + if (match(TK_RETURN)) { + /* return expr; (expr optional) */ + if (at(TK_SEMI) || at(TK_RBRACE) || at(TK_EOF)) return ast_return(NULL); + return ast_return(parse_expr()); } - if (match(TK_QUIT)) { - ASTNode *zero = ast_new(AST_NUMBER); - zero->number = 0; - ASTNode *ret = ast_new(AST_RETURN); - ret->retstmt.expr = zero; + if (match(TK_FUNC)) { + expect(TK_IDENT, "Expected function name"); + const char *name = G->data[P-1].lex; + expect(TK_LPAREN, "Expected '(' after func name"); + expect(TK_RPAREN, "Expected ')' (no args supported)"); + ASTNode *body = parse_block(); + return ast_funcdef(name, body); + } - return ret; + if (match(TK_IF)) { + expect(TK_LPAREN, "Expected '(' after if"); + ASTNode *cond = parse_expr(); + expect(TK_RPAREN, "Expected ')'"); + ASTNode *thenb = parse_block(); + ASTNode *elseb = NULL; + if (match(TK_ELSE)) { + /* else { ... } */ + elseb = parse_block(); + } + return ast_if(cond, thenb, elseb); } + if (match(TK_WHILE)) { + expect(TK_LPAREN, "Expected '(' after while"); + ASTNode *cond = parse_expr(); + expect(TK_RPAREN, "Expected ')'"); + ASTNode *body = parse_block(); + return ast_while(cond, body); + } - if (match(TK_IMPORT)) { - Tok *f = consume(TK_STRING, "Splice/SyntaxError Expected string filename after import"); + if (match(TK_FOR)) { + expect(TK_IDENT, "Expected for variable name"); + const char *var = G->data[P-1].lex; + expect(TK_IN, "Expected 'in' after for var"); + ASTNode *start = parse_expr(); + expect(TK_DOTDOT, "Expected '..' in for range"); + ASTNode *end = parse_expr(); + ASTNode *body = parse_block(); + return ast_for(var, start, end, body); + } + + /* assignment or expression statement */ + if (at(TK_IDENT) && G->data[P+1].t == TK_ASSIGN) { + const char *name = advance()->lex; + advance(); /* '=' */ + ASTNode *rhs = parse_expr(); + return ast_var(AST_ASSIGN, name, rhs); + } + + /* expression stmt (incl call) */ + return parse_expr(); +} - ASTNode *n = ast_new(AST_IMPORT); +static ASTNode *parse_block(void) { + expect(TK_LBRACE, "Expected '{'"); - const char *raw = f->lex ? f->lex : ""; - const char *resolved = resolve_builtin_import(raw); + ASTNode **stmts = NULL; + int count = 0, cap = 0; - n->importstmt.filename = strdup(resolved); - return n; + while (!at(TK_RBRACE) && !at(TK_EOF)) { + ASTNode *s = parse_stmt(); + if (s) { + if (count >= cap) { + cap = cap ? cap*2 : 16; + stmts = (ASTNode**)xrealloc(stmts, sizeof(ASTNode*)*(size_t)cap); + } + stmts[count++] = s; + } + eat_separators(); } - if (match(TK_WRITE)) { - consume(TK_LPAREN, "Splice/SyntaxError Expected '(' after write"); - ASTNode *path = parse_expression(); - consume(TK_COMMA, "Splice/SyntaxError Expected ',' after write path"); - ASTNode *val = parse_expression(); - consume(TK_RPAREN, "Splice/SyntaxError Expected ')' after write"); - ASTNode *n = ast_new(AST_WRITE); - n->write.path = path; - n->write.value = val; - return n; - } + expect(TK_RBRACE, "Expected '}'"); + return ast_statements(stmts, count); +} - if (match(TK_RAISE)) { - consume(TK_LPAREN, "Splice/SyntaxError Expected '(' after raise"); - ASTNode *e = parse_expression(); - consume(TK_RPAREN, "Splice/SyntaxError Expected ')' after raise expr"); - ASTNode *n = ast_new(AST_RAISE); - n->raise.expr = e; - return n; - } +static ASTNode *parse_program(TokVec *v) { + G = v; P = 0; - if (match(TK_RETURN)) { - ASTNode *e = NULL; - if (!at(TK_SEMI) && !at(TK_RBRACE)) e = parse_expression(); - ASTNode *n = ast_new(AST_RETURN); - n->retstmt.expr = e; - return n; - } + ASTNode **stmts = NULL; + int count=0, cap=0; - if (match(TK_FUNC)) { - Tok *id = consume(TK_IDENT, "Splice/SyntaxError Expected function name"); - consume(TK_LPAREN, "Splice/SyntaxError Expected '(' after func name"); - - char **params=NULL; - int pc=0, cap=0; - - if (!at(TK_RPAREN)) { - for (;;) { - Tok *p = consume(TK_IDENT, "Splice/SyntaxError Expected param name"); - if (pc>=cap) { cap=cap?cap*2:8; params=(char**)realloc(params,sizeof(char*)*(size_t)cap); } - params[pc++] = strdup(p->lex); - if (!match(TK_COMMA)) break; + while (!at(TK_EOF)) { + ASTNode *s = parse_stmt(); + if (s) { + if (count >= cap) { + cap = cap ? cap*2 : 16; + stmts = (ASTNode**)xrealloc(stmts, sizeof(ASTNode*)*(size_t)cap); } + stmts[count++] = s; } - consume(TK_RPAREN, "Splice/SyntaxError Expected ')' after params"); - - consume(TK_LBRACE, "Splice/SyntaxError Expected '{' before func body"); - ASTNode *body = parse_statements_until(TK_RBRACE); - consume(TK_RBRACE, "Splice/SyntaxError Expected '}' after func body"); - ASTNode *n = ast_new(AST_FUNC_DEF); - n->funcdef.funcname = strdup(id->lex); - n->funcdef.params = params; - n->funcdef.param_count = pc; - n->funcdef.body = body; - return n; + eat_separators(); } - if (match(TK_IF)) { - consume(TK_LPAREN, "Splice/SyntaxError Expected '(' after if"); - ASTNode *cond = parse_expression(); - consume(TK_RPAREN, "Splice/SyntaxError Expected ')' after if cond"); + return ast_statements(stmts, count); +} - consume(TK_LBRACE, "Splice/SyntaxError Expected '{' after if"); - ASTNode *thenb = parse_statements_until(TK_RBRACE); - consume(TK_RBRACE, "Splice/SyntaxError Expected '}' after if body"); +/* ========================================================= + SPC WRITER (matches your VM) + ========================================================= */ - ASTNode *elseb = NULL; +static void wr_u8(FILE *f, uint8_t v) { fwrite(&v,1,1,f); } - if (match(TK_ELSE)) { - /* else if (...) { ... } */ - if (at(TK_IF)) { - elseb = parse_statement(); // recurse → AST_IF - } - /* else { ... } */ - else { - consume(TK_LBRACE, "Splice/SyntaxError Expected '{' after else"); - elseb = parse_statements_until(TK_RBRACE); - consume(TK_RBRACE, "Splice/SyntaxError Expected '}' after else body"); - } - } +static void wr_u32(FILE *f, uint32_t v) { + /* little-endian */ + uint8_t b[4]; + b[0] = (uint8_t)(v & 0xFF); + b[1] = (uint8_t)((v >> 8) & 0xFF); + b[2] = (uint8_t)((v >> 16) & 0xFF); + b[3] = (uint8_t)((v >> 24) & 0xFF); + fwrite(b,1,4,f); +} + +static void wr_double(FILE *f, double v) { + fwrite(&v, 1, 8, f); +} + +static void wr_str(FILE *f, const char *s) { + if (!s) s = ""; + uint32_t len = (uint32_t)strlen(s); + wr_u32(f, len); + if (len) fwrite(s,1,len,f); +} + +static void write_node(FILE *f, ASTNode *n); + +static void write_node(FILE *f, ASTNode *n) { + if (!n) { + /* NULL: write a dummy node of type AST_NUMBER with value 0 + because your VM read_node() doesn't have a NULL sentinel. + This keeps structure valid when else/return expr is missing. + */ + wr_u8(f, (uint8_t)AST_NUMBER); + wr_double(f, 0.0); + return; + } + + wr_u8(f, (uint8_t)n->type); + + switch (n->type) { + case AST_NUMBER: wr_double(f, n->number); break; + case AST_STRING: + case AST_IDENTIFIER: wr_str(f, n->string); break; + + case AST_BINARY_OP: + wr_str(f, n->binop.op); + write_node(f, n->binop.left); + /* unary '!' encoded with right=NULL: emit 0 on right */ + write_node(f, n->binop.right); + break; + + case AST_PRINT: + write_node(f, n->print.expr); + break; + + case AST_LET: + case AST_ASSIGN: + wr_str(f, n->var.name); + write_node(f, n->var.value); + break; + + case AST_STATEMENTS: + wr_u32(f, (uint32_t)n->statements.count); + for (int i=0;istatements.count;i++) write_node(f, n->statements.stmts[i]); + break; + + case AST_WHILE: + write_node(f, n->whilestmt.cond); + write_node(f, n->whilestmt.body); + break; + + case AST_IF: + write_node(f, n->ifstmt.cond); + write_node(f, n->ifstmt.then_b); + write_node(f, n->ifstmt.else_b); + break; + + case AST_FOR: + wr_str(f, n->forstmt.var); + write_node(f, n->forstmt.start); + write_node(f, n->forstmt.end); + write_node(f, n->forstmt.body); + break; + + case AST_FUNC_DEF: + wr_str(f, n->funcdef.name); + write_node(f, n->funcdef.body); + break; + + case AST_FUNCTION_CALL: + wr_str(f, n->funccall.name); + break; + + case AST_RETURN: + write_node(f, n->retstmt.expr); + break; + + case AST_BREAK: + case AST_CONTINUE: + break; + + default: + die("spbuild: unsupported node in writer"); + } +} - ASTNode *n = ast_new(AST_IF); - n->ifstmt.condition = cond; - n->ifstmt.then_branch = thenb; - n->ifstmt.else_branch = elseb; - return n; +static int write_spc(const char *out_path, ASTNode *root) { + int fd = open(out_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd < 0) return 0; + FILE *f = fdopen(fd, "wb"); + if (!f) { + close(fd); + return 0; } + fwrite(SPC_MAGIC, 1, 4, f); + wr_u8(f, (uint8_t)SPC_VERSION); + write_node(f, root); + fclose(f); + return 1; +} +/* ========================================================= + MAIN + ========================================================= */ - if (match(TK_WHILE)) { - consume(TK_LPAREN, "Splice/SyntaxError Expected '(' after while"); - ASTNode *cond = parse_expression(); - consume(TK_RPAREN, "Splice/SyntaxError Expected ')' after while cond"); - consume(TK_LBRACE, "Splice/SyntaxError Expected '{' after while"); - ASTNode *body = parse_statements_until(TK_RBRACE); - consume(TK_RBRACE, "Splice/SyntaxError Expected '}' after while body"); +static char *read_file(const char *path) { + FILE *f = fopen(path, "rb"); + if (!f) return NULL; + fseek(f, 0, SEEK_END); + long sz = ftell(f); + rewind(f); + if (sz < 0) { fclose(f); return NULL; } + char *buf = (char*)xmalloc((size_t)sz + 1); + size_t rd = fread(buf, 1, (size_t)sz, f); + buf[rd] = 0; + fclose(f); + return buf; +} - ASTNode *n = ast_new(AST_WHILE); - n->whilestmt.cond = cond; - n->whilestmt.body = body; - return n; +/* Basic validation to reduce path traversal risk. + Allows only non-empty relative paths and rejects any ".." component. */ +static int is_safe_relative_path(const char *arg) { + if (arg == NULL || *arg == '\0') { + return 0; } - if (match(TK_FOR)) { - Tok *id = consume(TK_IDENT, "Splice/SyntaxError Expected for variable"); - consume(TK_IN, "Splice/SyntaxError Expected 'in' after for var"); - ASTNode *start = parse_expression(); - consume(TK_DOTDOT, "Splice/SyntaxError Expected '.' in for range"); - ASTNode *end = parse_expression(); - consume(TK_LBRACE, "Splice/SyntaxError Expected '{' after for range"); - ASTNode *body = parse_statements_until(TK_RBRACE); - consume(TK_RBRACE, "Splice/SyntaxError Expected '}' after for body"); - - ASTNode *n = ast_new(AST_FOR); - n->forstmt.for_var = strdup(id->lex); - n->forstmt.for_start = start; - n->forstmt.for_end = end; - n->forstmt.for_body = body; - return n; - } - - /* assignment or expr statement */ - if (at(TK_IDENT)) { - Tok *id = peek(); P++; - - /* ident = expr */ - if (match(TK_ASSIGN)) { - ASTNode *rhs = parse_expression(); - ASTNode *n = ast_new(AST_ASSIGN); - n->var.varname = strdup(id->lex); - n->var.value = rhs; - return n; - } + /* Reject absolute POSIX-style paths. */ + if (arg[0] == '/') { + return 0; + } - /* ident[expr] = expr */ - if (match(TK_LBRACKET)) { - ASTNode *idx = parse_expression(); - consume(TK_RBRACKET, "Splice/SyntaxError Expected ']' after index"); - consume(TK_ASSIGN, "Splice/SyntaxError Expected '=' after index"); - ASTNode *rhs = parse_expression(); - - ASTNode *target = ast_new(AST_IDENTIFIER); - target->string = strdup(id->lex); - - ASTNode *n = ast_new(AST_INDEX_ASSIGN); - n->indexassign.target = target; - n->indexassign.index = idx; - n->indexassign.value = rhs; - return n; + /* Rudimentary check against Windows drive letters like "C:" or "C:\". */ + if (((arg[0] >= 'A' && arg[0] <= 'Z') || (arg[0] >= 'a' && arg[0] <= 'z')) && + arg[1] == ':') { + return 0; + } + + /* Scan components separated by '/' or '\\' and reject any that are exactly "..". */ + const char *p = arg; + while (*p) { + while (*p == '/' || *p == '\\') { + p++; + } + if (!*p) { + break; } + const char *start = p; + while (*p && *p != '/' && *p != '\\') { + p++; + } + size_t len = (size_t)(p - start); + if (len == 2 && start[0] == '.' && start[1] == '.') { + return 0; + } + } + + return 1; +} - /* fall back: treat as expression starting with identifier */ - P--; /* rewind */ +static int path_within_base(const char *path, const char *base) { + size_t base_len = strlen(base); + if (strncmp(path, base, base_len) != 0) { + return 0; } + return path[base_len] == '\0' || path[base_len] == '/'; +} - /* expression statement */ - return parse_expression(); +static int fullpath_buf(const char *path, char *out, size_t out_sz) { +#ifdef _WIN32 + return _fullpath(out, path, out_sz) != NULL; +#else + (void)out_sz; + return realpath(path, out) != NULL; +#endif } -/* Parse full program */ -static ASTNode *parse_program(TokVec *v) { - G = v; P = 0; - ASTNode *root = parse_statements_until(TK_EOF); - return root; +static int resolve_input_path(const char *arg, char *dst, size_t dst_len) { + if (!is_safe_relative_path(arg)) { + return 0; + } + + char cwd[PATH_MAX]; + char resolved[PATH_MAX]; + if (!splice_getcwd(cwd, sizeof(cwd))) { + return 0; + } + if (!fullpath_buf(arg, resolved, sizeof(resolved))) { + return 0; + } + if (!path_within_base(resolved, cwd)) { + return 0; + } + if (snprintf(dst, dst_len, "%s", resolved) >= (int)dst_len) { + return 0; + } + return 1; +} + +static int is_safe_filename(const char *name) { + if (name == NULL || *name == '\0') { + return 0; + } + for (const char *p = name; *p; p++) { + unsigned char c = (unsigned char)*p; + if (!(c == '.' || c == '_' || c == '-' || + (c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z'))) { + return 0; + } + } + if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { + return 0; + } + return 1; } -/* ========================= - Main (spbuild) - ========================= */ int main(int argc, char **argv) { - if (argc < 3) { + if (argc != 3) { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } - char *src = read_text_file(argv[1]); + const char *in_arg = argv[1]; + const char *out_arg = argv[2]; + char in_path[PATH_MAX]; + char out_path[PATH_MAX]; + + if (!resolve_input_path(in_arg, in_path, sizeof(in_path))) { + fprintf(stderr, "spbuild: unsafe input path '%s'\n", in_arg); + return 1; + } + + if (!is_safe_filename(out_arg)) { + fprintf(stderr, "spbuild: unsafe output filename '%s'\n", out_arg); + return 1; + } + + if (snprintf(out_path, sizeof(out_path), "./%s", out_arg) >= (int)sizeof(out_path)) { + fprintf(stderr, "spbuild: output path too long\n"); + return 1; + } + + char *src = read_file(in_path); if (!src) { - fprintf(stderr, "Could not read: %s\n", argv[1]); + fprintf(stderr, "spbuild: cannot read %s\n", in_arg); return 1; } - TokVec tv; tv_init(&tv); - tokenize(src, &tv); + TokVec tv = {0}; + lex(src, &tv); ASTNode *root = parse_program(&tv); - if (!write_ast_to_spc(argv[2], root)) { - fprintf(stderr, "Could not write: %s\n", argv[2]); + if (!write_spc(out_path, root)) { + fprintf(stderr, "spbuild: failed to write %s\n", out_arg); free_ast(root); tv_free(&tv); free(src); return 1; } - success(0, "Wrote AST SPC: %s", argv[2]); - free_ast(root); tv_free(&tv); free(src); diff --git a/src/module_stubs.c b/src/module_stubs.c index 592055d..4818db6 100644 --- a/src/module_stubs.c +++ b/src/module_stubs.c @@ -1,22 +1,101 @@ - #include "splice.h" +#include "sdk.h" #include #include +#include +#include +/* ===== VM OBJECT MODEL (LOCAL MIRROR) ===== */ +/* MUST match VM layout EXACTLY */ +static void error(int ln, const char *fmt, ...) { + (void)ln; + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "[ERROR] "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(1); +} +typedef enum { + OBJ_ARRAY, + OBJ_TUPLE +} ObjectType; + +typedef struct { + ObjectType type; + int count; + int capacity; + Value *items; +} ObjArray; + +/* ===== natives ===== */ -/* Example native: noop() -> 0 */ static Value native_noop(int argc, Value *argv) { (void)argc; (void)argv; - Value tmp; - tmp.type = VAL_NUMBER; - tmp.number = 0; - return tmp; + Value v = { .type = VAL_NUMBER, .number = 0 }; + return v; +} + +static Value native_len(int argc, Value *argv) { + Value out = { .type = VAL_NUMBER, .number = 0 }; + if (argc < 1) return out; + + Value v = argv[0]; + + if (v.type == VAL_STRING) { + out.number = (double)strlen(v.string ? v.string : ""); + return out; + } + + if (v.type == VAL_OBJECT && v.object) { + ObjArray *oa = (ObjArray*)v.object; + if (oa->type == OBJ_ARRAY || oa->type == OBJ_TUPLE) { + out.number = (double)oa->count; + } + } + return out; +} + +static Value native_append(int argc, Value *argv) { + Value out = { .type = VAL_NUMBER, .number = 0 }; + if (argc < 2) return out; + + Value target = argv[0]; + Value val = argv[1]; + + if (target.type != VAL_OBJECT) + error(0, "Splice/TypeError append: target must be array"); + + ObjArray *oa = (ObjArray*)target.object; + if (!oa || oa->type != OBJ_ARRAY) + error(0, "Splice/TypeError append: target not array"); + + if (oa->count >= oa->capacity) { + int newcap = oa->capacity ? oa->capacity * 2 : 4; + Value *ni = (Value*)realloc(oa->items, sizeof(Value) * newcap); + if (!ni) error(0, "Splice/SystemError OOM append"); + oa->items = ni; + oa->capacity = newcap; + } + + if (val.type == VAL_STRING) { + Value copy = val; + copy.string = strdup(val.string ? val.string : ""); + oa->items[oa->count++] = copy; + } else { + oa->items[oa->count++] = val; + } + + out.number = (double)oa->count; + return out; } -/* Constructor: register builtins and initialize any registered modules. */ -__attribute__((constructor)) static void Splice_module_stubs_init(void) { - /* register a tiny example native so tests/examples can call it */ - Splice_register_native("noop", native_noop); +/* ===== registration ===== */ - /* initialize any modules that have been registered */ +__attribute__((constructor)) +static void Splice_module_stubs_init(void) { + Splice_register_native("noop", native_noop); + Splice_register_native("len", native_len); + Splice_register_native("append", native_append); Splice_init_all_modules(); } diff --git a/src/sdk.h b/src/sdk.h index e3cf007..521a936 100644 --- a/src/sdk.h +++ b/src/sdk.h @@ -10,11 +10,12 @@ #define SPLICE_EMBED 0 #endif + /* ============================================================ Value forward declaration (from Splice.h) ============================================================ */ struct Value; -typedef struct Value Value; + /* ============================================================ Native function pointer type diff --git a/src/splice.c b/src/splice.c index df8f4a1..9a6b476 100644 --- a/src/splice.c +++ b/src/splice.c @@ -1,7 +1,85 @@ +#define SPLICE_NO_INLINE_MEMREADER + #include -#include #include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#endif + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +#ifdef _WIN32 +#define splice_getcwd _getcwd +#else +#define splice_getcwd getcwd +#endif + #include "splice.h" +#include "sdk.h" + +/* ========================= + Logging helpers + ========================= */ + +static void success(int ln, const char *fmt, ...) { + (void)ln; + va_list ap; + va_start(ap, fmt); + fprintf(stdout, "[OK] "); + vfprintf(stdout, fmt, ap); + fprintf(stdout, "\n"); + va_end(ap); +} + +/* ========================= + CLI VM entry + ========================= */ + +/* Basic validation to reduce path traversal risk. + Allows only non-empty relative paths and rejects any ".." component. */ +static int is_safe_relative_path(const char *arg) { + if (arg == NULL || *arg == '\0') { + return 0; + } + + /* Reject absolute POSIX-style paths. */ + if (arg[0] == '/') { + return 0; + } + + /* Rudimentary check against Windows drive letters like "C:" or "C:\". */ + if (((arg[0] >= 'A' && arg[0] <= 'Z') || (arg[0] >= 'a' && arg[0] <= 'z')) && + arg[1] == ':') { + return 0; + } + + /* Scan components separated by '/' or '\\' and reject any that are exactly "..". */ + const char *p = arg; + while (*p) { + while (*p == '/' || *p == '\\') { + p++; + } + if (!*p) { + break; + } + const char *start = p; + while (*p && *p != '/' && *p != '\\') { + p++; + } + size_t len = (size_t)(p - start); + if (len == 2 && start[0] == '.' && start[1] == '.') { + return 0; + } + } + + return 1; +} int main(int argc, char **argv) { if (argc < 2) { @@ -14,15 +92,86 @@ int main(int argc, char **argv) { return 0; } - const char *arg = argv[1]; - const char *ext = strrchr(arg, '.'); + if (!is_safe_relative_path(argv[1])) { + fprintf(stderr, "[ERROR] invalid SPC path\n"); + return 1; + } + const char *path = argv[1]; + const char *ext = strrchr(path, '.'); if (!ext || strcmp(ext, ".spc") != 0) { fprintf(stderr, "[ERROR] only .spc supported by VM now\n"); return 1; } - ASTNode *root = read_ast_from_spc(arg); + /* ========================= + Read SPC into memory + ========================= */ + + FILE *f = fopen(path, "rb"); + if (!f) { + perror("fopen"); + return 1; + } + + fseek(f, 0, SEEK_END); + long size = ftell(f); + rewind(f); + + if (size <= 0) { + fprintf(stderr, "[ERROR] empty SPC file\n"); + fclose(f); + return 1; + } + + unsigned char *buf = (unsigned char *)malloc((size_t)size); + if (!buf) { + fprintf(stderr, "[ERROR] OOM reading SPC\n"); + fclose(f); + return 1; + } + + if (fread(buf, 1, (size_t)size, f) != (size_t)size) { + fprintf(stderr, "[ERROR] failed to read SPC\n"); + free(buf); + fclose(f); + return 1; + } + + fclose(f); + + /* ========================= + Reset VM state (important!) + ========================= */ + + splice_reset_vm(); + + /* ========================= + Load AST from memory + ========================= */ + arena_init(64 * 1024); + ASTNode *root = read_ast_from_spc_mem(buf, (size_t)size); + if (!root) { + fprintf(stderr, "[ERROR] failed to parse SPC\n"); + free(buf); + return 1; + } + + /* ========================= + Execute program + ========================= */ + interpret(root); - free_ast(root); + + /* ========================= + Cleanup + ========================= */ + + /* + DO NOT free AST here unless you have a free_ast(). + VM owns AST lifetime. + */ + free(buf); + + return 0; } diff --git a/src/splice.h b/src/splice.h index d436d23..6820107 100644 --- a/src/splice.h +++ b/src/splice.h @@ -1,116 +1,73 @@ -#ifndef Splice_H -#define Splice_H -/* Header-only Splice runtime + AST serialization. - - VM loads .spc => AST => interpret - - Builder writes AST directly into .spc -*/ +#ifndef SPLICE_H +#define SPLICE_H -#include #include #include -#include -#include -#include - -#if defined(_WIN32) - #include -#elif !defined(ARDUINO) - #include -#endif -#ifdef ARDUINO - #define SPLICE_HAS_STDIO 0 -#else - #define SPLICE_HAS_STDIO 1 -#endif -#if SPLICE_HAS_STDIO - #include -#endif +#include +#include -#ifdef ARDUINO -#define SPLICE_EMBED 1 +#if defined(SPLICE_PLATFORM_ARDUINO) + #include + #define SPLICE_PRINTLN(s) Serial.println(s) + #define SPLICE_FAIL(msg) do { Serial.println(msg); while (1) delay(1000); } while (0) #else -#define SPLICE_EMBED 0 + #define SPLICE_PRINTLN(s) puts(s) + #define SPLICE_FAIL(msg) do { fprintf(stderr, "%s\n", msg); exit(1); } while (0) #endif +/* ================= ARENA (RUNTIME ALLOCATED) ================= */ -#define MAX_IMPORTS 32 - -static char *imported_files[MAX_IMPORTS]; -static int imported_count = 0; - +static unsigned char *splice_arena = NULL; +static size_t splice_arena_size = 0; +static size_t splice_arena_pos = 0; - - - - - - -/* ========================= - Diagnostics - ========================= */ -static inline void error(int ln, const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - fprintf(stderr, "[ERROR] line %d: ", ln); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - va_end(ap); - exit(1); +static void arena_init(size_t size) { + splice_arena = (unsigned char *)malloc(size); + if (!splice_arena) SPLICE_FAIL("ARENA_ALLOC_FAIL"); + splice_arena_size = size; + splice_arena_pos = 0; + memset(splice_arena, 0, size); } -static inline void warn(int ln, const char *fmt, ...) { - va_list ap; va_start(ap, fmt); - fprintf(stderr, "[WARN] line %d: ", ln); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - va_end(ap); -} -static inline void info(int ln, const char *fmt, ...) { - (void)ln; - va_list ap; va_start(ap, fmt); - fprintf(stdout, "[INFO] "); - vfprintf(stdout, fmt, ap); - fprintf(stdout, "\n"); - va_end(ap); + +static void arena_free(void) { + free(splice_arena); + splice_arena = NULL; + splice_arena_size = splice_arena_pos = 0; } -static inline void success(int ln, const char *fmt, ...) { - (void)ln; - va_list ap; va_start(ap, fmt); - fprintf(stdout, "[SUCCESS] "); - vfprintf(stdout, fmt, ap); - fprintf(stdout, "\n"); - va_end(ap); + +static void *arena_alloc(size_t n) { + n = (n + 7) & ~7; + if (splice_arena_pos + n > splice_arena_size) + SPLICE_FAIL("ARENA_OOM"); + void *p = splice_arena + splice_arena_pos; + splice_arena_pos += n; + return p; } -/* ========================= - Values / Objects - ========================= */ +/* ================= VALUES ================= */ + typedef enum { VAL_NUMBER, VAL_STRING, VAL_OBJECT } ValueType; -typedef struct Value { +typedef struct { ValueType type; double number; - char *string; + const char *string; void *object; } Value; -typedef enum { - OBJ_ARRAY, - OBJ_TUPLE -} ObjectType; +/* ================= EXEC ================= */ -typedef struct { - ObjectType type; - int count; - int capacity; - Value *items; -} ObjArray; +typedef enum { + EXEC_OK, + EXEC_BREAK, + EXEC_CONTINUE, + EXEC_RETURN, + EXEC_ERROR +} ExecResult; -/* If you have sdk.h natives, keep it. If not, stub it. */ -#include "sdk.h" +static Value splice_return_value; -/* ========================= - AST - ========================= */ +/* ================= AST ================= */ typedef enum { AST_NUMBER = 0, @@ -121,26 +78,14 @@ typedef enum { AST_ASSIGN, AST_BREAK, AST_PRINT, - AST_READ, - AST_WRITE, AST_CONTINUE, - - AST_RAISE, - AST_WARN, - AST_INPUT, - AST_INFO, AST_WHILE, AST_IF, - AST_TUPLE, AST_STATEMENTS, AST_FUNC_DEF, AST_FUNCTION_CALL, AST_RETURN, - AST_IMPORT, - AST_FOR, - AST_ARRAY_LITERAL, - AST_INDEX_EXPR, - AST_INDEX_ASSIGN + AST_FOR } ASTNodeType; typedef struct ASTNode ASTNode; @@ -149,1745 +94,363 @@ struct ASTNode { ASTNodeType type; union { double number; - char *string; - - struct { - char *op; /* "+", "-", "*", "/", "==", "&&", "!" ... */ - ASTNode *left; - ASTNode *right; /* may be NULL for unary */ - } binop; - struct { - ASTNode** items; - int count; - } tuple; - struct { - char *varname; - ASTNode *value; - } var; + const char *string; + struct { const char *op; ASTNode *left; ASTNode *right; } binop; + struct { const char *name; ASTNode *value; } var; struct { ASTNode *expr; } print; - struct { ASTNode *prompt; } input; - struct { ASTNode *expr; } raise; - struct { ASTNode *expr; } warn; - struct { ASTNode *expr; } info; - struct { ASTNode *expr; } read; - struct { ASTNode *path; ASTNode *value; } write; - - struct { ASTNode *cond; ASTNode *body; } whilestmt; - - struct { - ASTNode *condition; - ASTNode *then_branch; - ASTNode *else_branch; /* may be NULL */ - } ifstmt; - - struct { - ASTNode **stmts; - int count; - } statements; - - struct { - char *funcname; - char **params; - int param_count; - ASTNode *body; - } funcdef; - - struct { - char *funcname; - ASTNode **args; - int arg_count; - } funccall; - + struct { ASTNode *cond; ASTNode *then_b; ASTNode *else_b; } ifstmt; + struct { ASTNode **stmts; int count; } statements; + struct { const char *name; ASTNode *body; } funcdef; + struct { const char *name; } funccall; struct { ASTNode *expr; } retstmt; - - struct { char *filename; } importstmt; - - struct { - char *for_var; - ASTNode *for_start; - ASTNode *for_end; - ASTNode *for_body; - } forstmt; - - struct { - ASTNode **elements; - int count; - } arraylit; - - struct { - ASTNode *target; - ASTNode *index; - } indexexpr; - - struct { - ASTNode *target; - ASTNode *index; - ASTNode *value; - } indexassign; + struct { const char *var; ASTNode *start; ASTNode *end; ASTNode *body; } forstmt; }; }; -typedef struct { - ASTNode** items; - int count; -} ASTTuple; -/* ========================= - Env (vars + funcs) - ========================= */ -static jmp_buf return_buf; -static Value return_value; -static jmp_buf *break_buf = NULL; -static jmp_buf *continue_buf = NULL; -typedef enum { VAR_NUMBER, VAR_STRING, VAR_OBJECT } VarType; -typedef struct { - char *name; - VarType type; - double value; - char *str; - void *obj; -} Var; -#define VAR_TABLE_SIZE 256 /* power of 2 = fast modulo */ +/* ================= VARIABLES ================= */ + +#define VAR_TABLE_SIZE 64 typedef struct { - char *name; /* interned or strdup */ - VarType type; - double value; - char *str; - void *obj; - int used; + const char *name; + Value value; + int used; } VarSlot; static VarSlot var_table[VAR_TABLE_SIZE]; -static inline unsigned hash_str(const char *s) { + +static unsigned hash_str(const char *s) { unsigned h = 2166136261u; - while (*s) { - h ^= (unsigned char)*s++; - h *= 16777619u; - } + while (*s) { h ^= (unsigned char)*s++; h *= 16777619u; } return h; } -static inline Var* var_number(double d) { - Var *v = malloc(sizeof(Var)); - v->name = NULL; - v->type = VAR_NUMBER; - v->value = d; - v->str = NULL; - v->obj = NULL; - return v; -} - - -static inline VarSlot *get_var(const char *name) { - if (!name) return NULL; - - unsigned h = hash_str(name); - unsigned idx = h & (VAR_TABLE_SIZE - 1); - - for (unsigned i = 0; i < VAR_TABLE_SIZE; i++) { - VarSlot *v = &var_table[idx]; - - if (!v->used) - return NULL; /* empty slot = not found */ - - if (strcmp(v->name, name) == 0) - return v; - - idx = (idx + 1) & (VAR_TABLE_SIZE - 1); /* linear probe */ - } - return NULL; -} - - - -static inline void free_object(void *obj) { - if (!obj) return; - ObjArray *oa = (ObjArray*)obj; - if (oa->type == OBJ_ARRAY) { - for (int j = 0; j < oa->count; ++j) { - if (oa->items[j].type == VAL_STRING) free(oa->items[j].string); - } - free(oa->items); - } - free(oa); -} - -static inline void set_var_object(const char *name, void *obj) { - if (!name) error(0, "Splice/NullError set_var_object: NULL name"); - - unsigned h = hash_str(name); - unsigned idx = h & (VAR_TABLE_SIZE - 1); +static VarSlot *get_var(const char *name) { + unsigned i = hash_str(name) & (VAR_TABLE_SIZE - 1); for (;;) { - VarSlot *v = &var_table[idx]; - - if (!v->used) { - v->used = 1; - v->name = strdup(name); - v->type = VAR_OBJECT; - v->obj = obj; - v->str = NULL; - v->value = 0; - return; - } - - if (strcmp(v->name, name) == 0) { - if (v->type == VAR_STRING) free(v->str); - v->type = VAR_OBJECT; - v->obj = obj; - return; - } - - idx = (idx + 1) & (VAR_TABLE_SIZE - 1); + VarSlot *v = &var_table[i]; + if (!v->used) return NULL; + if (strcmp(v->name, name) == 0) return v; + i = (i + 1) & (VAR_TABLE_SIZE - 1); } } - - -static inline void set_var( - const char *name, - VarType type, - double value, - const char *str -) { - if (!name) error(0, "Splice/NullError set_var: NULL name"); - - unsigned h = hash_str(name); - unsigned idx = h & (VAR_TABLE_SIZE - 1); - +static void set_var(const char *name, Value v) { + unsigned i = hash_str(name) & (VAR_TABLE_SIZE - 1); for (;;) { - VarSlot *v = &var_table[idx]; - - if (!v->used) { - v->used = 1; - v->name = strdup(name); - v->type = type; - v->value = value; - v->str = (type == VAR_STRING) ? strdup(str ? str : "") : NULL; - v->obj = NULL; - return; - } - - if (strcmp(v->name, name) == 0) { - if (v->type == VAR_STRING) free(v->str); - v->type = type; - v->value = value; - v->str = (type == VAR_STRING) ? strdup(str ? str : "") : NULL; + VarSlot *s = &var_table[i]; + if (!s->used || strcmp(s->name, name) == 0) { + s->used = 1; + s->name = name; + s->value = v; return; } - - idx = (idx + 1) & (VAR_TABLE_SIZE - 1); + i = (i + 1) & (VAR_TABLE_SIZE - 1); } } +/* ================= FUNCTIONS ================= */ - -/* ========================= - Forward declarations - ========================= */ - -static inline ASTNode *ast_new(ASTNodeType t); -static inline void free_ast(ASTNode *node); -static ASTNode *clone_ast(const ASTNode *n); - -static inline void interpret(ASTNode *node); -#define MAX_FUNCS 32 -typedef struct { char *name; ASTNode *def; } Func; -#define FUNC_TABLE_SIZE 128 +#define FUNC_TABLE_SIZE 32 typedef struct { - char *name; - ASTNode *def; + const char *name; + ASTNode *body; int used; } FuncSlot; static FuncSlot func_table[FUNC_TABLE_SIZE]; - -static inline void add_func(const char *name, ASTNode *def) { - if (!name) error(0, "Splice/NullError add_func: NULL name"); - - unsigned idx = hash_str(name) & (FUNC_TABLE_SIZE - 1); - +static void add_func(const char *name, ASTNode *body) { + unsigned i = hash_str(name) & (FUNC_TABLE_SIZE - 1); for (;;) { - FuncSlot *f = &func_table[idx]; - - if (!f->used) { + FuncSlot *f = &func_table[i]; + if (!f->used || strcmp(f->name, name) == 0) { f->used = 1; - f->name = strdup(name); - f->def = def; - return; - } - - if (strcmp(f->name, name) == 0) { - f->def = def; // overwrite allowed + f->name = name; + f->body = body; return; } - - idx = (idx + 1) & (FUNC_TABLE_SIZE - 1); + i = (i + 1) & (FUNC_TABLE_SIZE - 1); } } +static ASTNode *get_func(const char *name) { + unsigned i = hash_str(name) & (FUNC_TABLE_SIZE - 1); + for (;;) { + FuncSlot *f = &func_table[i]; + if (!f->used) return NULL; + if (strcmp(f->name, name) == 0) return f->body; + i = (i + 1) & (FUNC_TABLE_SIZE - 1); + } +} +/* ================= SPC LOADER ================= */ -static inline ASTNode *get_func(const char *name) { - if (!name) return NULL; - - unsigned idx = hash_str(name) & (FUNC_TABLE_SIZE - 1); - - for (unsigned i = 0; i < FUNC_TABLE_SIZE; i++) { - FuncSlot *f = &func_table[idx]; - - if (!f->used) - return NULL; - - if (strcmp(f->name, name) == 0) - return f->def; +#define SPC_MAGIC "SPC\0" +#define SPC_VERSION 1 +#define SPLICE_MAX_STRING_LEN (64 * 1024 - 1) - idx = (idx + 1) & (FUNC_TABLE_SIZE - 1); - } +typedef struct { + const unsigned char *data; + size_t size; + size_t pos; +} Reader; - return NULL; +static unsigned char rd_u8(Reader *r) { + if (r->pos >= r->size) SPLICE_FAIL("SPC_EOF"); + return r->data[r->pos++]; } +static uint32_t rd_u32(Reader *r) { + uint32_t v = 0; + v |= rd_u8(r); + v |= rd_u8(r) << 8; + v |= rd_u8(r) << 16; + v |= rd_u8(r) << 24; + return v; +} +static const char *rd_str(Reader *r) { + uint32_t len = rd_u32(r); + if (len > r->size - r->pos) SPLICE_FAIL("SPC_STR"); + if (len > SPLICE_MAX_STRING_LEN) SPLICE_FAIL("SPC_STR_LEN"); + char *s = (char *)arena_alloc((size_t)len + 1); + memcpy(s, r->data + r->pos, len); + s[len] = 0; + r->pos += len; + return s; +} +static ASTNode *read_node(Reader *r); -static ASTNode *clone_ast(const ASTNode *n) { - if (!n) return NULL; - - ASTNode *c = ast_new(n->type); +static ASTNode *read_node(Reader *r) { + ASTNode *n = (ASTNode *)arena_alloc(sizeof(ASTNode)); + n->type = (ASTNodeType)rd_u8(r); switch (n->type) { - - case AST_NUMBER: - c->number = n->number; + case AST_NUMBER: { + double tmp; + memcpy(&tmp, r->data + r->pos, 8); + r->pos += 8; + n->number = tmp; break; - + } case AST_STRING: case AST_IDENTIFIER: - c->string = n->string ? strdup(n->string) : NULL; + n->string = rd_str(r); break; - - case AST_BINARY_OP: - c->binop.op = strdup(n->binop.op); - c->binop.left = clone_ast(n->binop.left); - c->binop.right = clone_ast(n->binop.right); + case AST_PRINT: + n->print.expr = read_node(r); break; - case AST_LET: case AST_ASSIGN: - c->var.varname = strdup(n->var.varname); - c->var.value = clone_ast(n->var.value); - break; - - case AST_PRINT: - c->print.expr = clone_ast(n->print.expr); - break; - - case AST_INPUT: - c->input.prompt = clone_ast(n->input.prompt); + n->var.name = rd_str(r); + n->var.value = read_node(r); break; - - case AST_RAISE: - c->raise.expr = clone_ast(n->raise.expr); + case AST_BINARY_OP: + n->binop.op = rd_str(r); + n->binop.left = read_node(r); + n->binop.right = read_node(r); break; - - case AST_WARN: - c->warn.expr = clone_ast(n->warn.expr); + case AST_STATEMENTS: { + int c = (int)rd_u32(r); + n->statements.count = c; + n->statements.stmts = (ASTNode **)arena_alloc(sizeof(ASTNode *) * c); + for (int i = 0; i < c; i++) n->statements.stmts[i] = read_node(r); break; - - case AST_INFO: - c->info.expr = clone_ast(n->info.expr); + } + case AST_FUNC_DEF: + n->funcdef.name = rd_str(r); + n->funcdef.body = read_node(r); break; - - case AST_READ: - c->read.expr = clone_ast(n->read.expr); + case AST_FUNCTION_CALL: + n->funccall.name = rd_str(r); break; - - case AST_WRITE: - c->write.path = clone_ast(n->write.path); - c->write.value = clone_ast(n->write.value); + case AST_RETURN: + n->retstmt.expr = read_node(r); break; - case AST_WHILE: - c->whilestmt.cond = clone_ast(n->whilestmt.cond); - c->whilestmt.body = clone_ast(n->whilestmt.body); + n->whilestmt.cond = read_node(r); + n->whilestmt.body = read_node(r); break; - case AST_IF: - c->ifstmt.condition = clone_ast(n->ifstmt.condition); - c->ifstmt.then_branch = clone_ast(n->ifstmt.then_branch); - c->ifstmt.else_branch = clone_ast(n->ifstmt.else_branch); + n->ifstmt.cond = read_node(r); + n->ifstmt.then_b = read_node(r); + n->ifstmt.else_b = read_node(r); break; - - case AST_STATEMENTS: - c->statements.count = n->statements.count; - c->statements.stmts = - (ASTNode**)calloc(c->statements.count, sizeof(ASTNode*)); - for (int i = 0; i < c->statements.count; i++) - c->statements.stmts[i] = - clone_ast(n->statements.stmts[i]); + case AST_FOR: + n->forstmt.var = rd_str(r); + n->forstmt.start = read_node(r); + n->forstmt.end = read_node(r); + n->forstmt.body = read_node(r); break; - - case AST_FUNC_DEF: - c->funcdef.funcname = strdup(n->funcdef.funcname); - c->funcdef.param_count = n->funcdef.param_count; - c->funcdef.params = - (char**)calloc(c->funcdef.param_count, sizeof(char*)); - for (int i = 0; i < c->funcdef.param_count; i++) - c->funcdef.params[i] = - strdup(n->funcdef.params[i]); - c->funcdef.body = clone_ast(n->funcdef.body); + default: break; + } + return n; +} - case AST_FUNCTION_CALL: - c->funccall.funcname = strdup(n->funccall.funcname); - c->funccall.arg_count = n->funccall.arg_count; - c->funccall.args = - (ASTNode**)calloc(c->funccall.arg_count, sizeof(ASTNode*)); - for (int i = 0; i < c->funccall.arg_count; i++) - c->funccall.args[i] = - clone_ast(n->funccall.args[i]); - break; +static ASTNode *read_ast_from_spc_mem(const unsigned char *data, size_t size) { + if (size < 5) SPLICE_FAIL("SPC_SHORT"); + if (memcmp(data, SPC_MAGIC, 4) != 0) SPLICE_FAIL("SPC_MAGIC"); + if (data[4] != SPC_VERSION) SPLICE_FAIL("SPC_VERSION"); + Reader r = { data, size, 5 }; + return read_node(&r); +} - case AST_RETURN: - c->retstmt.expr = clone_ast(n->retstmt.expr); - break; +/* ================= EVAL ================= */ - case AST_IMPORT: - c->importstmt.filename = strdup(n->importstmt.filename); - break; +static Value eval(ASTNode *n) { + switch (n->type) { + case AST_NUMBER: return (Value){ VAL_NUMBER, n->number, NULL, NULL }; + case AST_STRING: return (Value){ VAL_STRING, 0, n->string, NULL }; + case AST_IDENTIFIER: { + VarSlot *v = get_var(n->string); + return v ? v->value : (Value){ VAL_NUMBER, 0, NULL, NULL }; + } + case AST_BINARY_OP: { + Value a = eval(n->binop.left); + Value b = eval(n->binop.right); + if (!strcmp(n->binop.op, "+")) { + if (a.type == VAL_STRING && b.type == VAL_STRING) { + size_t la = strlen(a.string); + size_t lb = strlen(b.string); + char *s = arena_alloc(la + lb + 1); + memcpy(s, a.string, la); + memcpy(s + la, b.string, lb); + s[la + lb] = 0; + return (Value){ VAL_STRING, 0, s, NULL }; + } + // fallback numeric + + return (Value){ VAL_NUMBER, a.number + b.number, NULL, NULL }; + } + + if (!strcmp(n->binop.op, "+")) return (Value){ VAL_NUMBER, a.number + b.number, NULL, NULL }; + if (!strcmp(n->binop.op, "-")) return (Value){ VAL_NUMBER, a.number - b.number, NULL, NULL }; + if (!strcmp(n->binop.op, "*")) return (Value){ VAL_NUMBER, a.number * b.number, NULL, NULL }; + if (!strcmp(n->binop.op, "/")) return (Value){ VAL_NUMBER, a.number / b.number, NULL, NULL }; + return (Value){ VAL_NUMBER, 0, NULL, NULL }; + } + default: + return (Value){ VAL_NUMBER, 0, NULL, NULL }; + } +} +static void splice_print_value(Value v) { + char buf[32]; - case AST_FOR: - c->forstmt.for_var = strdup(n->forstmt.for_var); - c->forstmt.for_start = clone_ast(n->forstmt.for_start); - c->forstmt.for_end = clone_ast(n->forstmt.for_end); - c->forstmt.for_body = clone_ast(n->forstmt.for_body); - break; + switch (v.type) { - case AST_ARRAY_LITERAL: - c->arraylit.count = n->arraylit.count; - c->arraylit.elements = - (ASTNode**)calloc(c->arraylit.count, sizeof(ASTNode*)); - for (int i = 0; i < c->arraylit.count; i++) - c->arraylit.elements[i] = - clone_ast(n->arraylit.elements[i]); - break; + case VAL_STRING: + if (v.string) { + SPLICE_PRINTLN(v.string); + } else { + SPLICE_PRINTLN("(null)"); + } + break; - case AST_INDEX_EXPR: - c->indexexpr.target = clone_ast(n->indexexpr.target); - c->indexexpr.index = clone_ast(n->indexexpr.index); - break; + case VAL_NUMBER: + // portable, works everywhere + snprintf(buf, sizeof(buf), "%g", v.number); + SPLICE_PRINTLN(buf); + break; - case AST_INDEX_ASSIGN: - c->indexassign.target = clone_ast(n->indexassign.target); - c->indexassign.index = clone_ast(n->indexassign.index); - c->indexassign.value = clone_ast(n->indexassign.value); - break; + case VAL_OBJECT: + // placeholder until objects exist + SPLICE_PRINTLN(""); + break; - default: - error(0, "Splice/VMError clone_ast: unsupported AST node %d", n->type); + default: + SPLICE_PRINTLN(""); + break; } - - return c; -} -static int strcmp_ptr(const void *a, const void *b) { - return strcmp(*(char**)a, *(char**)b); } -static int already_imported(const char *path) { - return bsearch( - &path, - imported_files, - imported_count, - sizeof(char*), - strcmp_ptr - ) != NULL; -} +/* ================= INTERPRET ================= */ -/* ========================= - AST alloc/free - ========================= */ -static inline ASTNode *ast_new(ASTNodeType t) { - ASTNode *n = (ASTNode*)calloc(1, sizeof(ASTNode)); - if (!n) error(0, "Splice/SystemError OOM allocating ASTNode"); - n->type = t; - return n; -} +static ExecResult interpret(ASTNode *n) { + switch (n->type) { -static inline void free_ast(ASTNode *node) { - if (!node) return; - switch (node->type) { - case AST_STRING: - case AST_IDENTIFIER: - free(node->string); - break; - case AST_TUPLE: - for (int i = 0; i < node->tuple.count; i++) - free_ast(node->tuple.items[i]); - free(node->tuple.items); - break; + case AST_STATEMENTS: + for (int i = 0; i < n->statements.count; i++) { + ExecResult r = interpret(n->statements.stmts[i]); + if (r != EXEC_OK) return r; + } + return EXEC_OK; - case AST_BINARY_OP: - free(node->binop.op); - free_ast(node->binop.left); - free_ast(node->binop.right); - break; + case AST_PRINT: { + Value v = eval(n->print.expr); + splice_print_value(v); + return EXEC_OK; + } + case AST_LET: case AST_ASSIGN: - free(node->var.varname); - free_ast(node->var.value); - break; - - case AST_PRINT: free_ast(node->print.expr); break; - case AST_INPUT: free_ast(node->input.prompt); break; - case AST_RAISE: free_ast(node->raise.expr); break; - case AST_WARN: free_ast(node->warn.expr); break; - case AST_INFO: free_ast(node->info.expr); break; - - case AST_WHILE: - free_ast(node->whilestmt.cond); - free_ast(node->whilestmt.body); - break; + set_var(n->var.name, eval(n->var.value)); + return EXEC_OK; case AST_IF: - free_ast(node->ifstmt.condition); - free_ast(node->ifstmt.then_branch); - free_ast(node->ifstmt.else_branch); - break; + return eval(n->ifstmt.cond).number + ? interpret(n->ifstmt.then_b) + : interpret(n->ifstmt.else_b); - case AST_STATEMENTS: - for (int j = 0; j < node->statements.count; ++j) - free_ast(node->statements.stmts[j]); - free(node->statements.stmts); - break; + case AST_WHILE: + while (eval(n->whilestmt.cond).number) { + ExecResult r = interpret(n->whilestmt.body); + if (r == EXEC_BREAK) break; + if (r == EXEC_CONTINUE) continue; + if (r != EXEC_OK) return r; + } + return EXEC_OK; - case AST_FUNC_DEF: - free(node->funcdef.funcname); - for (int j = 0; j < node->funcdef.param_count; ++j) free(node->funcdef.params[j]); - free(node->funcdef.params); - free_ast(node->funcdef.body); - break; + case AST_FOR: { + int s = (int)eval(n->forstmt.start).number; + int e = (int)eval(n->forstmt.end).number; + for (int i = s; i <= e; i++) { + set_var(n->forstmt.var, (Value){ VAL_NUMBER, i, NULL, NULL }); + ExecResult r = interpret(n->forstmt.body); + if (r == EXEC_BREAK) break; + if (r == EXEC_CONTINUE) continue; + if (r != EXEC_OK) return r; + } + return EXEC_OK; + } - case AST_FUNCTION_CALL: - free(node->funccall.funcname); - for (int j = 0; j < node->funccall.arg_count; ++j) free_ast(node->funccall.args[j]); - free(node->funccall.args); - break; + case AST_BREAK: return EXEC_BREAK; + case AST_CONTINUE: return EXEC_CONTINUE; case AST_RETURN: - free_ast(node->retstmt.expr); - break; - - case AST_IMPORT: - free(node->importstmt.filename); - break; - - case AST_FOR: - free(node->forstmt.for_var); - free_ast(node->forstmt.for_start); - free_ast(node->forstmt.for_end); - free_ast(node->forstmt.for_body); - break; - - case AST_ARRAY_LITERAL: - for (int j = 0; j < node->arraylit.count; ++j) free_ast(node->arraylit.elements[j]); - free(node->arraylit.elements); - break; + splice_return_value = eval(n->retstmt.expr); + return EXEC_RETURN; - case AST_INDEX_EXPR: - free_ast(node->indexexpr.target); - free_ast(node->indexexpr.index); - break; + case AST_FUNC_DEF: + add_func(n->funcdef.name, n->funcdef.body); + return EXEC_OK; - case AST_INDEX_ASSIGN: - free_ast(node->indexassign.target); - free_ast(node->indexassign.index); - free_ast(node->indexassign.value); - break; + case AST_FUNCTION_CALL: { + ASTNode *body = get_func(n->funccall.name); + if (!body) SPLICE_FAIL("UNDEF_FUNC"); + ExecResult r = interpret(body); + if (r == EXEC_RETURN) return EXEC_OK; + return r; + } default: - break; + eval(n); + return EXEC_OK; } - free(node); } -/* ========================= - AST serialization helpers - ========================= */ -#define SPC_MAGIC "SPC" -#define SPC_VERSION 1 -#define AST_NULL_SENTINEL 0xFF - -static inline void w_u8(FILE *f, unsigned char v) { - if (fputc(v, f) == EOF) error(0, "Splice/SystemError write u8 failed"); -} -static inline void w_u32(FILE *f, unsigned int v) { - if (fwrite(&v, 4, 1, f) != 1) error(0, "Splice/SystemError write u32 failed"); -} -static inline void w_u16(FILE *f, unsigned short v) { - if (fwrite(&v, 2, 1, f) != 1) error(0, "Splice/SystemError write u16 failed"); -} -static inline void w_double(FILE *f, double d) { - if (fwrite(&d, sizeof(double), 1, f) != 1) error(0, "Splice/SystemError write double failed"); -} -static inline void w_str(FILE *f, const char *s) { - if (!s) s = ""; - unsigned short len = (unsigned short)strlen(s); - w_u16(f, len); - if (len && fwrite(s, 1, len, f) != len) error(0, "Splice/SystemError write string failed"); -} +/* ================= RESET ================= */ -static inline unsigned char r_u8(FILE *f) { - int c = fgetc(f); - if (c == EOF) error(0, "Splice/SyntaxError Unexpected EOF (u8)"); - return (unsigned char)c; -} -static inline unsigned int r_u32(FILE *f) { - unsigned int v; - if (fread(&v, 4, 1, f) != 1) error(0, "Splice/SyntaxError Unexpected EOF (u32)"); - return v; -} -static inline unsigned short r_u16(FILE *f) { - unsigned short v; - if (fread(&v, 2, 1, f) != 1) error(0, "Splice/SyntaxError Unexpected EOF (u16)"); - return v; -} -static inline double r_double(FILE *f) { - double d; - if (fread(&d, sizeof(double), 1, f) != 1) error(0, "Splice/SyntaxError Unexpected EOF (double)"); - return d; -} -static inline char *r_str(FILE *f) { - unsigned short len = r_u16(f); - char *s = (char*)malloc((size_t)len + 1); - if (!s) error(0, "OOM reading string"); - if (len && fread(s, 1, len, f) != len) error(0, "Splice/SyntaxError Unexpected EOF (string)"); - s[len] = 0; - return s; +static void splice_reset_vm(void) { + memset(var_table, 0, sizeof(var_table)); + memset(func_table, 0, sizeof(func_table)); + splice_return_value = (Value){ VAL_NUMBER, 0, NULL, NULL }; } -static void write_ast_node(FILE *f, const ASTNode *n); - -static void write_ast_node(FILE *f, const ASTNode *n) { - if (!n) { w_u8(f, AST_NULL_SENTINEL); return; } - w_u8(f, (unsigned char)n->type); - - switch (n->type) { - case AST_NUMBER: w_double(f, n->number); break; - - case AST_STRING: - case AST_IDENTIFIER: - w_str(f, n->string); - break; - case AST_BREAK: - /* nothing to write */ - break; - case AST_CONTINUE: - break; - - case AST_BINARY_OP: - w_str(f, n->binop.op); - write_ast_node(f, n->binop.left); - write_ast_node(f, n->binop.right); - break; - case AST_READ: - write_ast_node(f, n->read.expr); - break; - case AST_TUPLE: - w_u32(f, (unsigned int)n->tuple.count); - for (int i = 0; i < n->tuple.count; i++) - write_ast_node(f, n->tuple.items[i]); - break; - - case AST_WRITE: - write_ast_node(f, n->write.path); - write_ast_node(f, n->write.value); - break; - - case AST_PRINT: write_ast_node(f, n->print.expr); break; - case AST_INPUT: write_ast_node(f, n->input.prompt); break; - case AST_RAISE: write_ast_node(f, n->raise.expr); break; - case AST_WARN: write_ast_node(f, n->warn.expr); break; - case AST_INFO: write_ast_node(f, n->info.expr); break; - - case AST_LET: - case AST_ASSIGN: - w_str(f, n->var.varname); - write_ast_node(f, n->var.value); - break; - - case AST_RETURN: - write_ast_node(f, n->retstmt.expr); - break; - - case AST_WHILE: - write_ast_node(f, n->whilestmt.cond); - write_ast_node(f, n->whilestmt.body); - break; - - case AST_IF: - write_ast_node(f, n->ifstmt.condition); - write_ast_node(f, n->ifstmt.then_branch); - write_ast_node(f, n->ifstmt.else_branch); - break; - - case AST_FOR: - w_str(f, n->forstmt.for_var); - write_ast_node(f, n->forstmt.for_start); - write_ast_node(f, n->forstmt.for_end); - write_ast_node(f, n->forstmt.for_body); - break; - - case AST_ARRAY_LITERAL: - w_u32(f, (unsigned int)n->arraylit.count); - for (int i = 0; i < n->arraylit.count; ++i) write_ast_node(f, n->arraylit.elements[i]); - break; - - case AST_INDEX_EXPR: - write_ast_node(f, n->indexexpr.target); - write_ast_node(f, n->indexexpr.index); - break; - - case AST_INDEX_ASSIGN: - write_ast_node(f, n->indexassign.target); - write_ast_node(f, n->indexassign.index); - write_ast_node(f, n->indexassign.value); - break; - - case AST_FUNC_DEF: - w_str(f, n->funcdef.funcname); - w_u32(f, (unsigned int)n->funcdef.param_count); - for (int i = 0; i < n->funcdef.param_count; ++i) w_str(f, n->funcdef.params[i]); - write_ast_node(f, n->funcdef.body); - break; - - case AST_FUNCTION_CALL: - w_str(f, n->funccall.funcname); - w_u32(f, (unsigned int)n->funccall.arg_count); - for (int i = 0; i < n->funccall.arg_count; ++i) write_ast_node(f, n->funccall.args[i]); - break; - - case AST_STATEMENTS: - w_u32(f, (unsigned int)n->statements.count); - for (int i = 0; i < n->statements.count; ++i) write_ast_node(f, n->statements.stmts[i]); - break; - - case AST_IMPORT: - w_str(f, n->importstmt.filename); - break; - - default: - error(0, "Splice/SystemError write_ast_node: unsupported type %d", (int)n->type); - } -} - -static ASTNode *read_ast_node(FILE *f); - -static ASTNode *read_ast_node(FILE *f) { - unsigned char t = r_u8(f); - if (t == AST_NULL_SENTINEL) return NULL; - - ASTNodeType type = (ASTNodeType)t; - ASTNode *n = ast_new(type); - - switch (type) { - case AST_READ: - n->read.expr = read_ast_node(f); - break; - case AST_BREAK: - /* nothing to read */ - break; - - case AST_WRITE: - n->write.path = read_ast_node(f); - n->write.value = read_ast_node(f); - break; - - case AST_NUMBER: - n->number = r_double(f); - break; - case AST_TUPLE: { - unsigned int c = r_u32(f); - n->tuple.count = (int)c; - n->tuple.items = - (ASTNode**)calloc(c ? c : 1, sizeof(ASTNode*)); - for (unsigned int i = 0; i < c; i++) - n->tuple.items[i] = read_ast_node(f); - break; - } - case AST_CONTINUE: - break; - - case AST_STRING: - case AST_IDENTIFIER: - n->string = r_str(f); - break; - - case AST_BINARY_OP: - n->binop.op = r_str(f); - n->binop.left = read_ast_node(f); - n->binop.right = read_ast_node(f); - break; - - case AST_PRINT: n->print.expr = read_ast_node(f); break; - case AST_INPUT: n->input.prompt = read_ast_node(f); break; - case AST_RAISE: n->raise.expr = read_ast_node(f); break; - case AST_WARN: n->warn.expr = read_ast_node(f); break; - case AST_INFO: n->info.expr = read_ast_node(f); break; - - case AST_LET: - case AST_ASSIGN: - n->var.varname = r_str(f); - n->var.value = read_ast_node(f); - break; - - case AST_RETURN: - n->retstmt.expr = read_ast_node(f); - break; - - case AST_WHILE: - n->whilestmt.cond = read_ast_node(f); - n->whilestmt.body = read_ast_node(f); - break; - - case AST_IF: - n->ifstmt.condition = read_ast_node(f); - n->ifstmt.then_branch = read_ast_node(f); - n->ifstmt.else_branch = read_ast_node(f); - break; - - case AST_FOR: - n->forstmt.for_var = r_str(f); - n->forstmt.for_start = read_ast_node(f); - n->forstmt.for_end = read_ast_node(f); - n->forstmt.for_body = read_ast_node(f); - break; - - case AST_ARRAY_LITERAL: { - unsigned int count = r_u32(f); - n->arraylit.count = (int)count; - n->arraylit.elements = (ASTNode**)calloc(count ? count : 1, sizeof(ASTNode*)); - if (!n->arraylit.elements) error(0, "Splice/SystemError OOM arraylit elements"); - for (unsigned int i = 0; i < count; ++i) n->arraylit.elements[i] = read_ast_node(f); - break; - } - - case AST_INDEX_EXPR: - n->indexexpr.target = read_ast_node(f); - n->indexexpr.index = read_ast_node(f); - break; - - case AST_INDEX_ASSIGN: - n->indexassign.target = read_ast_node(f); - n->indexassign.index = read_ast_node(f); - n->indexassign.value = read_ast_node(f); - break; - - case AST_FUNC_DEF: { - n->funcdef.funcname = r_str(f); - unsigned int pc = r_u32(f); - n->funcdef.param_count = (int)pc; - n->funcdef.params = (char**)calloc(pc ? pc : 1, sizeof(char*)); - if (!n->funcdef.params) error(0, "Splice/SystemError OOM func params"); - for (unsigned int i = 0; i < pc; ++i) n->funcdef.params[i] = r_str(f); - n->funcdef.body = read_ast_node(f); - break; - } - - case AST_FUNCTION_CALL: { - n->funccall.funcname = r_str(f); - unsigned int ac = r_u32(f); - n->funccall.arg_count = (int)ac; - n->funccall.args = (ASTNode**)calloc(ac ? ac : 1, sizeof(ASTNode*)); - if (!n->funccall.args) error(0, "Splice/SystemError OOM funccall args"); - for (unsigned int i = 0; i < ac; ++i) n->funccall.args[i] = read_ast_node(f); - break; - } - - case AST_STATEMENTS: { - unsigned int c = r_u32(f); - n->statements.count = (int)c; - n->statements.stmts = (ASTNode**)calloc(c ? c : 1, sizeof(ASTNode*)); - if (!n->statements.stmts) error(0, "Splice/SystemError OOM statements"); - for (unsigned int i = 0; i < c; ++i) n->statements.stmts[i] = read_ast_node(f); - break; - } - - case AST_IMPORT: - n->importstmt.filename = r_str(f); - break; - - default: - error(0, "Splice/SystemError read_ast_node: unknown type %d", (int)type); - } - return n; -} - - - -/* Public: VM helper */ -#if !SPLICE_EMBED -static inline ASTNode *read_ast_from_spc(const char *filename) { - FILE *f = fopen(filename, "rb"); - if (!f) { error(0, "Splice/SPCError Could not open bytecode file: %s", filename); return NULL; } - - char magic[5] = {0}; - if (fread(magic, 1, 4, f) != 4) error(0, "Splice/SPCError Invalid SPC (short)"); - if (memcmp(magic, SPC_MAGIC, 4) != 0) error(0, "Splice/SPCError Invalid SPC magic"); - - unsigned char ver = r_u8(f); - if (ver != SPC_VERSION) error(0, "Splice/SPCError Unsupported SPC version: %u", ver); - ASTNode *root = read_ast_node(f); - fclose(f); - return root; -} -#endif - -/* ========================= - Runtime eval/interpret - ========================= */ -static inline void print_value(Value v) { - if (v.type == VAL_STRING) { - printf("%s\n", v.string ? v.string : ""); - } else if (v.type == VAL_NUMBER) { - printf("%g\n", v.number); - } else { - printf("\n"); - } -} - -static inline char *eval_to_string(ASTNode *node); - -static inline Value eval(ASTNode *node) { - if (!node) { - Value tmp; - tmp.type = VAL_NUMBER; - tmp.number = 0; - return tmp; - } - - switch (node->type) { - case AST_READ: { - char *path = eval_to_string(node->read.expr); -#ifndef SPLICE_EMBED - FILE *f = fopen(path, "rb"); - if (!f) { - free(path); - Value tmp; - tmp.type = VAL_STRING; - tmp.string = strdup(""); - return tmp; - } - - fseek(f, 0, SEEK_END); - long size = ftell(f); - rewind(f); - - char *buf = (char*)malloc(size + 1); - if (!buf) error(0, "Splice/SystemError OOM in read()"); - fread(buf, 1, size, f); - buf[size] = 0; - - fclose(f); - free(path); - - Value tmp; - tmp.type = VAL_STRING; - tmp.string = buf; - return tmp; -#else - free(path); - Value tmp; - tmp.type = VAL_STRING; - tmp.string = strdup(""); - return tmp; #endif - } - - case AST_NUMBER: { - Value tmp; - tmp.type = VAL_NUMBER; - tmp.number = node->number; - return tmp; - } - case AST_TUPLE: { - ObjArray *oa = (ObjArray*)calloc(1, sizeof(ObjArray)); - if (!oa) error(0, "Splice/SystemError OOM tuple"); - - oa->type = OBJ_TUPLE; - oa->count = node->tuple.count; - oa->capacity = node->tuple.count; - oa->items = - (Value*)calloc((size_t)(oa->capacity ? oa->capacity : 1), sizeof(Value)); - - for (int i = 0; i < node->tuple.count; i++) - oa->items[i] = eval(node->tuple.items[i]); - - Value v; - v.type = VAL_OBJECT; - v.object = oa; - return v; - } - - case AST_STRING: { - Value tmp; - tmp.type = VAL_STRING; - tmp.string = strdup(node->string ? node->string : ""); - return tmp; - } - - case AST_IDENTIFIER: { - VarSlot *slot = get_var(node->string); - - if (!slot) { - Value tmp = { .type = VAL_NUMBER, .number = 0 }; - return tmp; - } - - if (slot->type == VAR_STRING) { - Value tmp; - tmp.type = VAL_STRING; - tmp.string = strdup(slot->str ? slot->str : ""); - return tmp; - } - - if (slot->type == VAR_OBJECT) { - Value tmp; - tmp.type = VAL_OBJECT; - tmp.object = slot->obj; - return tmp; - } - - Value tmp; - tmp.type = VAL_NUMBER; - tmp.number = slot->value; - return tmp; - } - - - case AST_ARRAY_LITERAL: { - ObjArray *oa = (ObjArray*)calloc(1, sizeof(ObjArray)); - if (!oa) error(0, "Splice/SystemError OOM array"); - oa->type = OBJ_ARRAY; - oa->count = node->arraylit.count; - oa->capacity = node->arraylit.count; - oa->items = (Value*)calloc((size_t)(oa->capacity ? oa->capacity : 1), sizeof(Value)); - if (!oa->items) error(0, "Splice/SystemError OOM array items"); - for (int j = 0; j < node->arraylit.count; ++j) oa->items[j] = eval(node->arraylit.elements[j]); - Value tmp; - tmp.type = VAL_OBJECT; - tmp.object = oa; - return tmp; - } - - case AST_INDEX_EXPR: { - Value target = eval(node->indexexpr.target); - Value idxv = eval(node->indexexpr.index); - int idx = (int)idxv.number; - - if (target.type != VAL_OBJECT) error(0, "Splice/IndexError index: target is not array"); - ObjArray *oa = (ObjArray*)target.object; - if (!oa || oa->type != OBJ_ARRAY) error(0, "Splice/IndexError index: not an array"); - - if (idx < 0 || idx >= oa->count) { - Value tmp; - tmp.type = VAL_NUMBER; - tmp.number = 0; - return tmp; - } - return oa->items[idx]; - } - case AST_INPUT: { - char *prompt = eval_to_string(node->input.prompt); - - #if SPLICE_HAS_STDIO - printf("%s", prompt); - fflush(stdout); - - char buf[1024]; - if (fgets(buf, sizeof(buf), stdin)) { - size_t len = strlen(buf); - if (len && buf[len - 1] == '\n') buf[len - 1] = 0; - } else { - buf[0] = 0; - } - - free(prompt); - - Value tmp; - tmp.type = VAL_STRING; - tmp.string = strdup(buf); - return tmp; - #else - free(prompt); - Value tmp; - tmp.type = VAL_STRING; - tmp.string = strdup(""); - return tmp; - #endif - } - - case AST_INDEX_ASSIGN: { - if (!node->indexassign.target || node->indexassign.target->type != AST_IDENTIFIER) - error(0, "Splice/IndexError index assign: target must be identifier"); - - VarSlot *slot = get_var(node->indexassign.target->string); - double d = slot ? slot->value : 0.0; - - if (!slot || slot->type != VAR_OBJECT) - error(0, "Splice/IndexError index assign: variable is not array"); - - - ObjArray *oa = (ObjArray*)slot->obj; - if (oa->type == OBJ_TUPLE) { - error(0, "Splice/TypeError cannot assign to tuple (immutable)"); - } - - int idx = (int)eval(node->indexassign.index).number; - Value val = eval(node->indexassign.value); - - if (idx < 0) error(0, "Splice/IndexError index assign: negative index"); - if (idx >= oa->count) { - while (idx >= oa->capacity) { - int newcap = oa->capacity ? oa->capacity * 2 : 4; - Value *ni = (Value*)realloc(oa->items, sizeof(Value) * (size_t)newcap); - if (!ni) error(0, "Splice/SystemError OOM realloc array"); - oa->items = ni; - oa->capacity = newcap; - } - for (int k = oa->count; k <= idx; ++k) { - oa->items[k].type = VAL_NUMBER; - oa->items[k].number = 0; - } - oa->count = idx + 1; - } - if (oa->items[idx].type == VAL_STRING) free(oa->items[idx].string); - oa->items[idx] = val; - Value tmp; - tmp.type = VAL_NUMBER; - tmp.number = 1; - return tmp; - } - - case AST_BINARY_OP: { - Value left = eval(node->binop.left); - Value right; - if (node->binop.right) { - right = eval(node->binop.right); - } else { - right.type = VAL_NUMBER; - right.number = 0; - right.string = NULL; - right.object = NULL; - } - - /* string concat on + */ - if (node->binop.op && strcmp(node->binop.op, "+") == 0 && - (left.type == VAL_STRING || right.type == VAL_STRING)) { - - char lb[64], rb[64]; - const char *ls = (left.type == VAL_STRING) ? left.string : (snprintf(lb, sizeof(lb), "%g", left.number), lb); - const char *rs = (right.type == VAL_STRING) ? right.string : (snprintf(rb, sizeof(rb), "%g", right.number), rb); - - char *out = (char*)malloc(strlen(ls) + strlen(rs) + 1); - if (!out) error(0, "Splice/SystemError OOM concat"); - strcpy(out, ls); - strcat(out, rs); - - if (left.type == VAL_STRING) free(left.string); - if (right.type == VAL_STRING) free(right.string); - - Value tmp; - tmp.type = VAL_STRING; - tmp.string = out; - return tmp; - } - - double lnum = (left.type == VAL_NUMBER) ? left.number : strtod(left.string ? left.string : "0", NULL); - double rnum = (right.type == VAL_NUMBER) ? right.number : strtod(right.string ? right.string : "0", NULL); - - double result = 0; - const char *op = node->binop.op ? node->binop.op : ""; - - if (strcmp(op, "==") == 0) { - if (left.type == VAL_STRING || right.type == VAL_STRING) { - const char *ls = (left.type == VAL_STRING) ? left.string : ""; - const char *rs = (right.type == VAL_STRING) ? right.string : ""; - result = (strcmp(ls, rs) == 0); - } else { - result = (lnum == rnum); - } - } - else if (strcmp(op, "!=") == 0) { - if (left.type == VAL_STRING || right.type == VAL_STRING) { - const char *ls = (left.type == VAL_STRING) ? left.string : ""; - const char *rs = (right.type == VAL_STRING) ? right.string : ""; - result = (strcmp(ls, rs) != 0); - } else { - result = (lnum != rnum); - } - } - else if (strcmp(op, "+") == 0) result = lnum + rnum; - else if (strcmp(op, "-") == 0) result = lnum - rnum; - else if (strcmp(op, "*") == 0) result = lnum * rnum; - else if (strcmp(op, "/") == 0) result = lnum / rnum; - else if (strcmp(op, "<") == 0) result = (lnum < rnum); - else if (strcmp(op, ">") == 0) result = (lnum > rnum); - else if (strcmp(op, "<=") == 0) result = (lnum <= rnum); - else if (strcmp(op, ">=") == 0) result = (lnum >= rnum); - else if (strcmp(op, "&&") == 0) result = ((lnum != 0) && (rnum != 0)); - else if (strcmp(op, "||") == 0) result = ((lnum != 0) || (rnum != 0)); - else if (strcmp(op, "!") == 0) result = (lnum == 0); - - if (left.type == VAL_STRING) free(left.string); - if (right.type == VAL_STRING) free(right.string); - - Value tmp; - tmp.type = VAL_NUMBER; - tmp.number = result; - return tmp; - } - - case AST_FUNCTION_CALL: { - /* builtins */ - if (strcmp(node->funccall.funcname, "len") == 0 && node->funccall.arg_count == 1) { - Value a = eval(node->funccall.args[0]); - if (a.type != VAL_OBJECT) { - Value tmp; - tmp.type = VAL_NUMBER; - tmp.number = 0; - return tmp; - } - ObjArray *oa = (ObjArray*)a.object; - if (!oa || oa->type != OBJ_ARRAY) { - Value tmp; - tmp.type = VAL_NUMBER; - tmp.number = 0; - return tmp; - } - Value tmp; - tmp.type = VAL_NUMBER; - tmp.number = (double)oa->count; - return tmp; - } - - if (strcmp(node->funccall.funcname, "append") == 0 && node->funccall.arg_count == 2) { - Value a = eval(node->funccall.args[0]); - Value v = eval(node->funccall.args[1]); - if (a.type != VAL_OBJECT) error(0, "Splice/ArrayError append: first arg must be array"); - ObjArray *oa = (ObjArray*)a.object; - if (!oa) error(0, "Splice/ArrayError append: invalid object"); - if (oa->type == OBJ_TUPLE) - error(0, "Splice/ArrayError append: cannot modify tuple (immutable)"); - if (oa->type != OBJ_ARRAY) - error(0, "Splice/ArrayError append: not an array"); - - if (oa->count >= oa->capacity) { - int newcap = oa->capacity ? oa->capacity * 2 : 4; - Value *ni = (Value*)realloc(oa->items, sizeof(Value) * (size_t)newcap); - if (!ni) error(0, "Splice/SystemError OOM append realloc"); - oa->items = ni; - oa->capacity = newcap; - } - oa->items[oa->count++] = v; - Value tmp; - tmp.type = VAL_NUMBER; - tmp.number = 1; - return tmp; - } - - /* native */ - SpliceCFunc native = Splice_get_native(node->funccall.funcname); - if (native) { - Value *args = (Value*)malloc(sizeof(Value) * (size_t)node->funccall.arg_count); - if (!args) error(0, "Splice/SystemError OOM native args"); - for (int j = 0; j < node->funccall.arg_count; ++j) args[j] = eval(node->funccall.args[j]); - Value r = native(node->funccall.arg_count, args); - for (int j = 0; j < node->funccall.arg_count; ++j) - if (args[j].type == VAL_STRING) free(args[j].string); - free(args); - return r; - } - - /* user-defined */ - ASTNode *func = get_func(node->funccall.funcname); - if (!func) error(0, "Splice/SyntaxError Undefined function: %s", node->funccall.funcname); - - for (int j = 0; j < func->funcdef.param_count; ++j) { - Value av; - if (j < node->funccall.arg_count) { - av = eval(node->funccall.args[j]); - } else { - av.type = VAL_NUMBER; - av.number = 0; - av.string = NULL; - av.object = NULL; - } - - if (av.type == VAL_STRING) { - - - - set_var(func->funcdef.params[j], VAR_STRING, 0, av.string); - free(av.string); - } else if (av.type == VAL_OBJECT) { - set_var_object(func->funcdef.params[j], av.object); - } else { - - - set_var(func->funcdef.params[j], VAR_NUMBER, av.number, NULL); - } - } - - Value result; - result.type = VAL_NUMBER; - result.number = 0; - if (setjmp(return_buf) == 0) { - interpret(func->funcdef.body); - } else { - result = return_value; - } - - return result; - } - - default: { - Value tmp; - tmp.type = VAL_NUMBER; - tmp.number = 0; - return tmp; - } - } -} - -static inline void sb_ensure(char **buf, size_t *cap, size_t need) { - if (need <= *cap) return; - while (*cap < need) *cap *= 2; - *buf = (char*)realloc(*buf, *cap); - if (!*buf) error(0, "Splice/SystemError OOM stringify"); -} - -static inline char *value_item_to_tmp(Value v, char tmp[128]) { - if (v.type == VAL_STRING) { - /* quoted strings */ - snprintf(tmp, 128, "\"%s\"", v.string ? v.string : ""); - return tmp; - } - if (v.type == VAL_NUMBER) { - snprintf(tmp, 128, "%g", v.number); - return tmp; - } - return ""; -} - -static inline char *eval_to_string(ASTNode *node) { - Value v = eval(node); - - if (v.type == VAL_STRING) { - /* caller owns it */ - return v.string; - } - - if (v.type == VAL_OBJECT) { - ObjArray *oa = (ObjArray*)v.object; - if (!oa) return strdup(""); - - if (oa->type == OBJ_ARRAY || oa->type == OBJ_TUPLE) { - /* build string */ - size_t cap = 128; - size_t len = 0; - char *out = (char*)malloc(cap); - if (!out) error(0, "Splice/SystemError OOM stringify"); - - char open = (oa->type == OBJ_TUPLE) ? '(' : '['; - char close = (oa->type == OBJ_TUPLE) ? ')' : ']'; - - out[len++] = open; - - for (int i = 0; i < oa->count; i++) { - char tmp[128]; - const char *s = value_item_to_tmp(oa->items[i], tmp); - - size_t sl = strlen(s); - sb_ensure(&out, &cap, len + sl + 4); - memcpy(out + len, s, sl); - len += sl; - - if (i + 1 < oa->count) { - out[len++] = ','; - out[len++] = ' '; - } - } - - out[len++] = close; - out[len] = 0; - return out; - } - - return strdup(""); - } - - /* number */ - char buf[64]; - snprintf(buf, sizeof(buf), "%g", v.number); - return strdup(buf); -} - - - -static inline void interpret(ASTNode *node); - -static inline void interpret(ASTNode *node) { - if (!node) return; - - switch (node->type) { - case AST_STATEMENTS: { - for (int j = 0; j < node->statements.count; ++j) { - ASTNode *s = node->statements.stmts[j]; - if (s && s->type == AST_FUNC_DEF) - add_func(s->funcdef.funcname, s); - - - } - for (int j = 0; j < node->statements.count; ++j) { - ASTNode *s = node->statements.stmts[j]; - if (!s || s->type == AST_FUNC_DEF) continue; - interpret(s); - } - break; - } - case AST_CONTINUE: - longjmp(*continue_buf, 1); - break; - - case AST_BREAK: - if (!break_buf) - error(0, "Splice/SyntaxError 'break' outside loop"); - longjmp(*break_buf, 1); - break; - - case AST_IMPORT: { - #if !SPLICE_EMBED - const char *path = node->importstmt.filename; - - if (already_imported(path)) { - break; // already loaded - } - - ASTNode *mod = read_ast_from_spc(path); - if (!mod) error(0, "Splice/ImportError import failed: %s", path); - - imported_files[imported_count++] = strdup(path); - - interpret(mod); // executes + registers funcs - free_ast(mod); - #else - error(0, "Splice/ImportError import not supported on embedded builds"); - #endif - break; - } - - case AST_PRINT: { - Value v = eval(node->print.expr); - print_value(v); - if (v.type == VAL_STRING) free(v.string); - - } - - - case AST_FUNC_DEF: - add_func(node->funcdef.funcname, clone_ast(node)); - - break; - - case AST_RETURN: - return_value = eval(node->retstmt.expr); - longjmp(return_buf, 1); - break; - - case AST_LET: - case AST_ASSIGN: { - Value val = eval(node->var.value); - if (val.type == VAL_STRING) { - - - set_var(node->var.varname, VAR_STRING, 0, val.string); - free(val.string); - } else if (val.type == VAL_OBJECT) { - set_var_object(node->var.varname, val.object); - } else { - - - set_var(node->var.varname, VAR_NUMBER, val.number, NULL); - } - break; - } - - case AST_IF: - if (eval(node->ifstmt.condition).number) - interpret(node->ifstmt.then_branch); - else - interpret(node->ifstmt.else_branch); - break; - case AST_WRITE: { - Value path = eval(node->write.path); - Value val = eval(node->write.value); - - if (path.type != VAL_STRING) { - error(0, "Splice/IOError write(): path must be string"); - } - - char *out; - if (val.type == VAL_STRING) { - out = val.string; - } else { - char buf[64]; - snprintf(buf, sizeof(buf), "%g", val.number); - out = strdup(buf); - } - -#ifndef SPLICE_EMBED - FILE *f = fopen(path.string, "wb"); - if (!f) { - free(path.string); - if (val.type == VAL_STRING) free(val.string); - error(0, "Splice/IOError write(): cannot open file"); - } - - fwrite(out, 1, strlen(out), f); - fclose(f); -#endif - - free(path.string); - if (val.type == VAL_STRING) free(val.string); - else free(out); - - break; - } - - case AST_WHILE: { - jmp_buf jb, jc; - - jmp_buf *prev_break = break_buf; - jmp_buf *prev_cont = continue_buf; - - break_buf = &jb; - continue_buf = &jc; - - if (setjmp(jb) == 0) { - while (eval(node->whilestmt.cond).number) { - if (setjmp(jc) == 0) { - interpret(node->whilestmt.body); - } - // continue lands here - } - } - - // restore outer loop context - break_buf = prev_break; - continue_buf = prev_cont; - - break; - } - - - - case AST_FOR: { - if (!node->forstmt.for_var) { - error(0, "Splice/NULLError for-loop variable name is NULL"); - } - - int start = (int)eval(node->forstmt.for_start).number; - int end = (int)eval(node->forstmt.for_end).number; - - jmp_buf jb, jc; - - jmp_buf *prev_break = break_buf; - jmp_buf *prev_cont = continue_buf; - - break_buf = &jb; - continue_buf = &jc; - - if (setjmp(jb) == 0) { - for (int k = start; k <= end; ++k) { - - set_var(node->forstmt.for_var, VAR_NUMBER, k, NULL); - - if (setjmp(jc) == 0) { - interpret(node->forstmt.for_body); - } - // continue jumps land here - } - } - - // restore outer context - break_buf = prev_break; - continue_buf = prev_cont; - - - - - break; - } - - - case AST_FUNCTION_CALL: - case AST_ARRAY_LITERAL: - case AST_INDEX_EXPR: - case AST_INDEX_ASSIGN: - (void)eval(node); - break; - - case AST_RAISE: { - char *msg = eval_to_string(node->raise.expr); - error(0, "%s", msg); - break; - } - - - case AST_WARN: { - char *msg = eval_to_string(node->warn.expr); - warn(0, "%s", msg); - free(msg); - break; - } - case AST_INFO: { - char *msg = eval_to_string(node->info.expr); - info(0, "%s", msg); - free(msg); - break; - } - - default: - break; - } -} -typedef struct { - const unsigned char *data; - size_t size; - size_t pos; -} SpcMemReader; - -static inline unsigned char m_u8(SpcMemReader *r) { - if (r->pos >= r->size) error(0, "Splice/IOError Unexpected EOF (mem u8)"); - return r->data[r->pos++]; -} - -static inline unsigned short m_u16(SpcMemReader *r) { - if (r->pos + 2 > r->size) error(0, "Splice/IOError Unexpected EOF (mem u16)"); - unsigned short v; - memcpy(&v, r->data + r->pos, 2); - r->pos += 2; - return v; -} - -static inline unsigned int m_u32(SpcMemReader *r) { - if (r->pos + 4 > r->size) error(0, "Splice/IOError Unexpected EOF (mem u32)"); - unsigned int v; - memcpy(&v, r->data + r->pos, 4); - r->pos += 4; - return v; -} - -static inline double m_double(SpcMemReader *r) { - if (r->pos + sizeof(double) > r->size) - error(0, "Splice/IOError Unexpected EOF (mem double)"); - double d; - memcpy(&d, r->data + r->pos, sizeof(double)); - r->pos += sizeof(double); - return d; -} - -static inline char *m_str(SpcMemReader *r) { - unsigned short len = m_u16(r); - if (r->pos + len > r->size) error(0, "Splice/IOError Unexpected EOF (mem string)"); - char *s = (char*)malloc(len + 1); - memcpy(s, r->data + r->pos, len); - r->pos += len; - s[len] = 0; - return s; -} -static ASTNode *read_ast_node_mem(SpcMemReader *r) { - unsigned char tag = m_u8(r); - - ASTNodeType type = (ASTNodeType)tag; - ASTNode *n = ast_new(type); - - - switch (n->type) { - case AST_NUMBER: - n->number = m_double(r); - break; - - case AST_STRING: - case AST_IDENTIFIER: - n->string = m_str(r); - break; - - case AST_BINARY_OP: - n->binop.op = m_str(r); - n->binop.left = read_ast_node_mem(r); - n->binop.right = read_ast_node_mem(r); - break; - - case AST_PRINT: - n->print.expr = read_ast_node_mem(r); - break; - - case AST_LET: - case AST_ASSIGN: - n->var.varname = m_str(r); - n->var.value = read_ast_node_mem(r); - break; - - case AST_STATEMENTS: { - unsigned int c = m_u32(r); - n->statements.count = (int)c; - n->statements.stmts = (ASTNode**)calloc(c ? c : 1, sizeof(ASTNode*)); - for (unsigned int i = 0; i < c; i++) - n->statements.stmts[i] = read_ast_node_mem(r); - break; - } - - case AST_FUNCTION_CALL: { - n->funccall.funcname = m_str(r); - unsigned int ac = m_u32(r); - n->funccall.arg_count = (int)ac; - n->funccall.args = (ASTNode**)calloc(ac ? ac : 1, sizeof(ASTNode*)); - for (unsigned int i = 0; i < ac; i++) - n->funccall.args[i] = read_ast_node_mem(r); - break; - } - - default: - error(0, "Splice/IOError Unsupported AST type in mem reader: %d", n->type); - } - - return n; -} -static inline ASTNode *read_ast_from_spc_mem( - const unsigned char *data, - size_t size -) { - if (size < 5) error(0, "Splice/SPCrror Invalid SPC (too small)"); - if (memcmp(data, SPC_MAGIC, 4) != 0) - error(0, "Splice/SPCrror Invalid SPC magic"); - - if (data[4] != SPC_VERSION) - error(0, "Splice/SPCrror Unsupported SPC version"); - - SpcMemReader r; - r.data = data; - r.size = size; - r.pos = 5; - - return read_ast_node_mem(&r); -} - - -#endif /* Splice_H */ diff --git a/test/main.spc b/test/main.spc index 425dea9..041024a 100644 Binary files a/test/main.spc and b/test/main.spc differ diff --git a/test/main.spl b/test/main.spl index f4fcad9..ff8421c 100644 --- a/test/main.spl +++ b/test/main.spl @@ -1,3 +1,2 @@ -// This code is ment for the CI/CD platform on Github to test versions -let vmver = "1.0.0"; -print("If this is running, SpliceVM version: " + vmver + " is working correctly"); +let vmver = "1.0.0" +print("If this is running, SpliceVM version: " + vmver + " is working correctly") diff --git a/test/out/main.spc b/test/out/main.spc new file mode 100644 index 0000000..041024a Binary files /dev/null and b/test/out/main.spc differ