diff --git a/.DS_Store b/.DS_Store index d8f2cf3..12b0a34 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/README.md b/README.md index f2fce41..c681c9b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@
- + diff --git a/bin/Splice b/bin/Splice index b486ed3..42d4a1f 100755 Binary files a/bin/Splice and b/bin/Splice differ diff --git a/bin/spbuild b/bin/spbuild index 08880d9..ad769dc 100755 Binary files a/bin/spbuild and b/bin/spbuild differ diff --git a/build.sh b/build.sh index c5d4635..4ae474b 100755 --- a/build.sh +++ b/build.sh @@ -20,16 +20,16 @@ echo "Proceeding with build on $OS/$ARCH..." echo "Building Splice runtime and native module..." # Compile Splice runtime (with SDK globals) -gcc -DSDK_IMPLEMENTATION -Isrc -Wall -Wextra -c src/splice.c -o "$BIN_DIR/Splice.o" +gcc -DSDK_IMPLEMENTATION -Isrc -Wall -Wextra -c -DNDEBUG -O3 -flto src/splice.c -o "$BIN_DIR/Splice.o" # Compile native module without SDK_IMPLEMENTATION -gcc -Isrc -Wall -Wextra -c src/module_stubs.c -o "$BIN_DIR/module_stubs.o" +gcc -Isrc -Wall -Wextra -c -DNDEBUG -O3 -flto src/module_stubs.c -o "$BIN_DIR/module_stubs.o" # Link executable (local binary: Splice) gcc "$BIN_DIR/Splice.o" "$BIN_DIR/module_stubs.o" -o "$BIN_DIR/Splice" echo "Building spbuild (bytecode compiler)..." -gcc -Isrc -Wall -Wextra src/build.c -o "$BIN_DIR/spbuild" +gcc -Isrc -Wall -Wextra -DNDEBUG -O3 -flto src/build.c -o "$BIN_DIR/spbuild" # --- Install section --- INSTALL_DIR="" diff --git a/docs.md b/docs.md index 8bbc9ef..965e566 100644 --- a/docs.md +++ b/docs.md @@ -1,11 +1,266 @@ -# How to code in Splice +# How to Code in Splice -Splice is a very basic language and some of the syntax is derived from Lua, Python and a bit of it's own syntax. +Splice is a lightweight and embeddable programming language. +Its syntax is inspired by Lua and Python, with additional custom syntax +designed to keep the language simple and predictable. -## Print +Splice programs are executed from top to bottom. -Printing in Splice is written using +--- -``` Splice +## Program Structure + +A basic Splice program consists of statements separated by semicolons. + +```splice +print("Hello, Splice"); +``` + +--- + +## Comments + +Splice supports single-line comments using `//`. + +```splice +// This is a comment +print("This line will execute"); +``` + +--- + +## Printing Output + +Printing output is done using the `print` keyword. + +```splice print("Hi"); ``` + +### Printing Variables + +```splice +let x = 10; +print(x); +``` + +--- + +## Variables + +Variables are declared using the `let` keyword. + +```splice +let number = 42; +let name = "Splice"; +let pi = 3.14; +``` + +Splice is dynamically typed, meaning the type is determined at runtime. + +--- + +## Arithmetic Operations + +Splice supports basic arithmetic operations. + +```splice +let a = 10; +let b = 5; + +print(a + b); +print(a - b); +print(a * b); +print(a / b); +``` + +--- + +## Comparison Operators + +Splice supports standard comparison operators. + +```splice +let x = 10; + +print(x == 10); +print(x != 5); +print(x > 5); +print(x < 20); +``` + +--- + +## If Statements + +Conditional execution is done using `if`. + +```splice +let age = 18; + +if (age >= 18) { + print("You are an adult"); +} +``` + +--- + +## If-Else Statements + +```splice +let score = 40; + +if (score >= 50) { + print("Pass"); +} else { + print("Fail"); +} +``` + +--- + +## While Loops + +The `while` loop executes as long as the condition is true. + +```splice +let i = 0; + +while (i < 5) { + print(i); + i = i + 1; +} +``` + +--- + +## For Loops + +Splice supports `for` loops with a counter variable. + +```splice +for (let i = 0; i < 5; i = i + 1) { + print(i); +} +``` + +--- + +## Functions + +Functions are declared using the `func` keyword. + +```splice +func add(a, b) { + return a + b; +} +``` + +### Calling Functions + +```splice +let result = add(10, 20); +print(result); +``` + +--- + +## Return Statement + +Functions return values using `return`. + +```splice +func square(x) { + return x * x; +} + +print(square(5)); +``` + +--- + +## User Input + +Splice supports input using the `input` keyword. + +```splice +let name = input("Enter your name: "); +print(name); +``` + +--- + +## Error Handling + +Splice provides basic error reporting at runtime. +Errors include: + +- Undefined variables +- Invalid operations +- Syntax errors + +--- + +## Execution Model (High Level) + +Splice executes programs by walking an Abstract Syntax Tree (AST). +Each node represents a language construct such as variables, function calls, +loops, or expressions. + +This design allows: + +- Easy embedding in C programs +- Simpler debugging +- Low memory overhead + +--- + +## Embedding Splice in C + +Splice is designed to be embedded in C applications. + +```c +#include + +#define ARDUINO +#define SPLICE_EMBED 1 + +#include "splice.h" + +/* +Splice source (compiled beforehand): + +print("Hello from Splice"); +*/ + +const unsigned char program[] = { + 'S','P','C',0x00, // magic + 0x01, // version + + AST_PRINT, + AST_STRING, + 0x00, 0x11, + 'H','e','l','l','o',' ', + 'f','r','o','m',' ', + 'S','p','l','i','c','e' +}; + +void setup() { + Serial.begin(115200); + while (!Serial); + + ASTNode *root = read_ast_from_spc_mem( + program, + sizeof(program) + ); + + interpret(root); +} + +void loop() {} + +``` + +--- + + diff --git a/examples/.DS_Store b/examples/.DS_Store new file mode 100644 index 0000000..d0c089b Binary files /dev/null and b/examples/.DS_Store differ diff --git a/examples/array/array.spc b/examples/array/array.spc deleted file mode 100644 index 0cc0ccb..0000000 Binary files a/examples/array/array.spc and /dev/null differ diff --git a/examples/calculator/calc_test.spl b/examples/calculator/calc_test.spl index 1676f88..fa43b30 100644 --- a/examples/calculator/calc_test.spl +++ b/examples/calculator/calc_test.spl @@ -1,10 +1,11 @@ func calculator(num1, num2, op) { if (op == "+") { - print(num1 + num2) - } else { - print("Invalid operator: " + op) + print(num1 + num2); + } + if (op == "-") { + print(num1 - num2); } } -calculator(5, 3, "+") // prints 8 -calculator(5, 3, "%") // prints "Invalid operator: %" +calculator(5, 3, "+"); +calculator(5, 3, "-"); diff --git a/examples/calculator/hello.spc b/examples/calculator/hello.spc new file mode 100644 index 0000000..2416d73 Binary files /dev/null and b/examples/calculator/hello.spc differ diff --git a/examples/forloops/for.spc b/examples/forloops/for.spc new file mode 100644 index 0000000..36e3e3f Binary files /dev/null and b/examples/forloops/for.spc differ diff --git a/examples/helloworld/hello.spc b/examples/helloworld/hello.spc index c0b4d3d..1a85573 100644 Binary files a/examples/helloworld/hello.spc and b/examples/helloworld/hello.spc differ diff --git a/examples/helloworld/helloworld.spl b/examples/helloworld/helloworld.spl index 44159b3..7baa068 100644 --- a/examples/helloworld/helloworld.spl +++ b/examples/helloworld/helloworld.spl @@ -1 +1 @@ -print("Hello world") +print("Hello world"); diff --git a/release.md b/release.md index 8f9260a..097300f 100644 --- a/release.md +++ b/release.md @@ -1,7 +1,8 @@ # Release notes -Current Version: 1.0 +Current Version: 1.1 Beta ## Overview -This Version of Splice Made it so now the ast Dce and all of the building is done in spbuild so vm can be use in a esp32 \ No newline at end of file +* Major bug fixes +* Added MAX_FUNCS **(Set 16 for embedded and 32 for Normal PC as functions excedding MAX_FUNCS cause Segmentation fault)** \ No newline at end of file diff --git a/splib/math.spl b/splib/math.spl index dc0245e..a64767b 100644 --- a/splib/math.spl +++ b/splib/math.spl @@ -36,122 +36,3 @@ func round(x) { return int(x - 0.5); }; -// ---- powers & roots (native) ---- -func pow(x, y) { - native pow(x, y); -}; - -func sqrt(x) { - native sqrt(x); -}; - -func exp(x) { - native exp(x); -}; - -func log(x) { - native log(x); -}; - -func log10(x) { - native log10(x); -}; - -func log2(x) { - native log2(x); -}; - -// ---- trigonometry (native) ---- -func sin(x) { - native sin(x); -}; - -func cos(x) { - native cos(x); -}; - -func tan(x) { - native tan(x); -}; - -func asin(x) { - native asin(x); -}; - -func acos(x) { - native acos(x); -}; - -func atan(x) { - native atan(x); -}; - -func atan2(y, x) { - native atan2(y, x); -}; - -// ---- angle conversion ---- -func radians(deg) { - return deg * (pi / 180); -}; - -func degrees(rad) { - return rad * (180 / pi); -}; - -// ---- rounding helpers (native) ---- -func floor(x) { - native floor(x); -}; - -func ceil(x) { - native ceil(x); -}; - -func trunc(x) { - native trunc(x); -}; - -// ---- number theory ---- -func gcd(a, b) { - a = abs(a); - b = abs(b); - - if (a == 0) { - return b; - }; - if (b == 0) { - return a; - }; - - while (a != b) { - if (a > b) { - a = a - b; - } else { - b = b - a; - }; - }; - - return a; -}; - -func lcm(a, b) { - return abs(a * b) / gcd(a, b); -}; - -func factorial(n) { - if (n < 0) { - raise ("factorial: negative value"); - }; - - let r = 1; - while (n > 1) { - r = r * n; - n = n - 1; - }; - - return r; -}; - - - diff --git a/src/build.c b/src/build.c index b2813d5..3181211 100644 --- a/src/build.c +++ b/src/build.c @@ -5,6 +5,13 @@ #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; +} static inline int write_ast_to_spc(const char *out_file, const ASTNode *root) { FILE *f = fopen(out_file, "wb"); @@ -306,10 +313,39 @@ static ASTNode *parse_primary(void) { } if (match(TK_LPAREN)) { - ASTNode *e = parse_expression(); + 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, "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, "Expected ')'"); - return e; + return first; } + if (match(TK_READ)) { consume(TK_LPAREN, "Expected '(' after read"); ASTNode *e = parse_expression(); diff --git a/src/module_stubs.c b/src/module_stubs.c index e5aba20..592055d 100644 --- a/src/module_stubs.c +++ b/src/module_stubs.c @@ -1,11 +1,3 @@ -/* Minimal module stubs for Splice runtime - * This file is intentionally small: it registers a tiny example native - * function and ensures module initializers are invoked at program - * startup. Real native modules can be added by providing additional - * init functions that call Splice_register_native() or by providing - * Splice_register_module_ symbols which are looked up by the - * import mechanism in `splice.h`. - */ #include "splice.h" #include diff --git a/src/splice.h b/src/splice.h index 5b6ab56..a07bb81 100644 --- a/src/splice.h +++ b/src/splice.h @@ -93,7 +93,11 @@ typedef struct Value { void *object; } Value; -typedef enum { OBJ_ARRAY } ObjectType; +typedef enum { + OBJ_ARRAY, + OBJ_TUPLE +} ObjectType; + typedef struct { ObjectType type; int count; @@ -107,6 +111,7 @@ typedef struct { /* ========================= AST ========================= */ + typedef enum { AST_NUMBER = 0, AST_STRING, @@ -123,6 +128,7 @@ typedef enum { AST_INFO, AST_WHILE, AST_IF, + AST_TUPLE, AST_STATEMENTS, AST_FUNC_DEF, AST_FUNCTION_CALL, @@ -147,7 +153,10 @@ struct ASTNode { ASTNode *left; ASTNode *right; /* may be NULL for unary */ } binop; - + struct { + ASTNode** items; + int count; + } tuple; struct { char *varname; ASTNode *value; @@ -216,7 +225,10 @@ struct ASTNode { } indexassign; }; }; - +typedef struct { + ASTNode** items; + int count; +} ASTTuple; /* ========================= Env (vars + funcs) ========================= */ @@ -232,21 +244,59 @@ typedef struct { void *obj; } Var; -static Var vars[32]; -static int var_count = 0; +#define VAR_TABLE_SIZE 256 /* power of 2 = fast modulo */ -static inline Var *get_var(const char *name) { +typedef struct { + char *name; /* interned or strdup */ + VarType type; + double value; + char *str; + void *obj; + int used; +} VarSlot; + +static VarSlot var_table[VAR_TABLE_SIZE]; +static inline unsigned hash_str(const char *s) { + unsigned h = 2166136261u; + 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; - for (int j = 0; j < var_count; ++j) { - if (!vars[j].name) continue; - if (strcmp(vars[j].name, name) == 0) - return &vars[j]; + 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; @@ -260,68 +310,75 @@ static inline void free_object(void *obj) { } static inline void set_var_object(const char *name, void *obj) { - if (!name) - error(0, "set_var_object called with NULL name"); - - for (int j = 0; j < var_count; ++j) { - if (!vars[j].name) continue; - - if (strcmp(vars[j].name, name) == 0) { - if (vars[j].type == VAR_OBJECT) - free_object(vars[j].obj); - vars[j].type = VAR_OBJECT; - vars[j].obj = obj; - free(vars[j].str); - vars[j].str = NULL; - vars[j].value = 0; + if (!name) error(0, "set_var_object: NULL name"); + + unsigned h = hash_str(name); + unsigned idx = h & (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; } - } - vars[var_count].name = strdup(name); - vars[var_count].type = VAR_OBJECT; - vars[var_count].obj = obj; - vars[var_count].value = 0; - vars[var_count].str = NULL; - var_count++; + 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); + } } + static inline void set_var( const char *name, VarType type, double value, const char *str ) { - if (!name) { - error(0, "set_var: NULL variable name"); - } + if (!name) error(0, "set_var: NULL name"); - for (int j = 0; j < var_count; ++j) { - if (!vars[j].name) continue; + unsigned h = hash_str(name); + unsigned idx = h & (VAR_TABLE_SIZE - 1); - if (strcmp(vars[j].name, name) == 0) { - vars[j].type = type; - if (type == VAR_STRING) { - free(vars[j].str); - vars[j].str = strdup(str ? str : ""); - } else { - vars[j].value = value; - free(vars[j].str); - vars[j].str = NULL; - } + 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; } - } - vars[var_count].name = strdup(name); - vars[var_count].type = type; - vars[var_count].value = (type == VAR_NUMBER) ? value : 0; - vars[var_count].str = (type == VAR_STRING) ? strdup(str ? str : "") : NULL; - var_count++; + 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; + return; + } + + idx = (idx + 1) & (VAR_TABLE_SIZE - 1); + } } + /* ========================= Forward declarations ========================= */ @@ -536,6 +593,11 @@ static inline void free_ast(ASTNode *node) { 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_BINARY_OP: free(node->binop.op); @@ -699,6 +761,11 @@ static void write_ast_node(FILE *f, const ASTNode *n) { 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); @@ -804,6 +871,15 @@ static ASTNode *read_ast_node(FILE *f) { 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_STRING: case AST_IDENTIFIER: @@ -934,6 +1010,16 @@ static inline ASTNode *read_ast_from_spc(const char *filename) { /* ========================= 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) { @@ -988,6 +1074,24 @@ static inline Value eval(ASTNode *node) { tmp.number = node->number; return tmp; } + case AST_TUPLE: { + ObjArray *oa = (ObjArray*)calloc(1, sizeof(ObjArray)); + if (!oa) error(0, "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; @@ -997,32 +1101,34 @@ static inline Value eval(ASTNode *node) { } case AST_IDENTIFIER: { - Var *v = get_var(node->string); - if (!v) { - Value tmp; - tmp.type = VAL_NUMBER; - tmp.number = 0; + VarSlot *slot = get_var(node->string); + + if (!slot) { + Value tmp = { .type = VAL_NUMBER, .number = 0 }; return tmp; } - if (v->type == VAR_STRING) { + if (slot->type == VAR_STRING) { Value tmp; tmp.type = VAL_STRING; - tmp.string = strdup(v->str ? v->str : ""); + tmp.string = strdup(slot->str ? slot->str : ""); return tmp; } - if (v->type == VAR_OBJECT) { + + if (slot->type == VAR_OBJECT) { Value tmp; tmp.type = VAL_OBJECT; - tmp.object = v->obj; + tmp.object = slot->obj; return tmp; } + Value tmp; tmp.type = VAL_NUMBER; - tmp.number = v->value; + tmp.number = slot->value; return tmp; } + case AST_ARRAY_LITERAL: { ObjArray *oa = (ObjArray*)calloc(1, sizeof(ObjArray)); if (!oa) error(0, "OOM array"); @@ -1089,10 +1195,18 @@ static inline Value eval(ASTNode *node) { if (!node->indexassign.target || node->indexassign.target->type != AST_IDENTIFIER) error(0, "index assign: target must be identifier"); - Var *v = get_var(node->indexassign.target->string); - if (!v || v->type != VAR_OBJECT) error(0, "index assign: variable is not array"); + VarSlot *slot = get_var(node->indexassign.target->string); + double d = slot ? slot->value : 0.0; + + if (!slot || slot->type != VAR_OBJECT) + error(0, "index assign: variable is not array"); + + + ObjArray *oa = (ObjArray*)slot->obj; + if (oa->type == OBJ_TUPLE) { + error(0, "cannot assign to tuple (immutable)"); + } - ObjArray *oa = (ObjArray*)v->obj; int idx = (int)eval(node->indexassign.index).number; Value val = eval(node->indexassign.value); @@ -1210,7 +1324,12 @@ static inline Value eval(ASTNode *node) { Value v = eval(node->funccall.args[1]); if (a.type != VAL_OBJECT) error(0, "append: first arg must be array"); ObjArray *oa = (ObjArray*)a.object; - if (!oa || oa->type != OBJ_ARRAY) error(0, "append: not an array"); + if (!oa) error(0, "append: invalid object"); + if (oa->type == OBJ_TUPLE) + error(0, "append: cannot modify tuple (immutable)"); + if (oa->type != OBJ_ARRAY) + error(0, "append: not an array"); + if (oa->count >= oa->capacity) { int newcap = oa->capacity ? oa->capacity * 2 : 4; @@ -1243,7 +1362,6 @@ static inline Value eval(ASTNode *node) { ASTNode *func = get_func(node->funccall.funcname); if (!func) error(0, "Undefined function: %s", node->funccall.funcname); - int saved = var_count; for (int j = 0; j < func->funcdef.param_count; ++j) { Value av; if (j < node->funccall.arg_count) { @@ -1278,7 +1396,7 @@ static inline Value eval(ASTNode *node) { } else { result = return_value; } - var_count = saved; + return result; } @@ -1291,14 +1409,81 @@ static inline Value eval(ASTNode *node) { } } +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, "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) return v.string; + + 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, "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) { @@ -1309,7 +1494,8 @@ static inline void interpret(ASTNode *node) { 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, clone_ast(s)); + add_func(s->funcdef.funcname, s); + } for (int j = 0; j < node->statements.count; ++j) { @@ -1342,10 +1528,10 @@ static inline void interpret(ASTNode *node) { } case AST_PRINT: { - char *s = eval_to_string(node->print.expr); - printf("%s\n", s); - free(s); - break; + Value v = eval(node->print.expr); + print_value(v); + if (v.type == VAL_STRING) free(v.string); + } diff --git a/test/main.spc b/test/main.spc new file mode 100644 index 0000000..425dea9 Binary files /dev/null and b/test/main.spc differ