diff --git a/Docs/doc/Corebenifits.pdf b/Docs/doc/Corebenifits.pdf new file mode 100644 index 0000000..12c4715 Binary files /dev/null and b/Docs/doc/Corebenifits.pdf differ diff --git a/docs.md b/Docs/doc/docs.md similarity index 97% rename from docs.md rename to Docs/doc/docs.md index 965e566..9d54f44 100644 --- a/docs.md +++ b/Docs/doc/docs.md @@ -139,7 +139,7 @@ while (i < 5) { Splice supports `for` loops with a counter variable. ```splice -for (let i = 0; i < 5; i = i + 1) { +for i in 1 . 10 { print(i); } ``` @@ -264,3 +264,6 @@ void loop() {} --- + +© Copyright 2026 OpenSplice and the Sinha Group +Licensed under the MIT License \ No newline at end of file diff --git a/README.md b/README.md index c681c9b..6281002 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,11 @@ [![CI](https://img.shields.io/github/actions/workflow/status/Open-Splice/Splice/main.yml?branch=main&label=Splice%20CI%2FCD&logo=github&style=plastic)](https://github.com/Open-Splice/Splice/actions/workflows/main.yml) + + + + [![Live on GitBook](https://img.shields.io/badge/Live%20on-Gitbook-black.svg?style=plastic&logo=gitbook&logoColor=white)](https://sinha.gitbook.io/splice) [![Reddit](https://img.shields.io/badge/On-Reddit-%23FF4500.svg?style=plastic&logo=Reddit&logoColor=white)](https://www.reddit.com/r/OpenSplice/) diff --git a/bin/Splice b/bin/Splice new file mode 100755 index 0000000..3fa83d3 Binary files /dev/null and b/bin/Splice differ diff --git a/recurse b/bin/spbuild similarity index 60% rename from recurse rename to bin/spbuild index fb173dd..a618b4f 100755 Binary files a/recurse and b/bin/spbuild differ diff --git a/examples/forloops/forloops.spl b/examples/forloops/forloops.spl index 8495157..ed62b93 100644 --- a/examples/forloops/forloops.spl +++ b/examples/forloops/forloops.spl @@ -1,4 +1,4 @@ print("For loops in Splice"); -for i in 1 . 10 { +for i in 1 .. 10 { print(i); } diff --git a/recurse.c b/recurse.c deleted file mode 100644 index 00388d3..0000000 --- a/recurse.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "stdio.h" -#include "stdlib.h" -#include "string.h" -#include "ctype.h" - -void recurse(int a) { - printf("%d\n", a); - recurse(a + 1); -} -int main() { - recurse(1); - return 0; -} \ No newline at end of file diff --git a/recurse.spc b/recurse.spc index 8f00a2d..e62abf3 100644 Binary files a/recurse.spc and b/recurse.spc differ diff --git a/recurse.spl b/recurse.spl index f1af01a..7e26be8 100644 --- a/recurse.spl +++ b/recurse.spl @@ -1,5 +1,9 @@ -func hi(a) { - print(a); - hi(a + 1); +let i = 0 + +while (i < 5) { + i = i + 1 + if (i == 3) { + continue + } + print(i) } -hi(1); \ No newline at end of file diff --git a/src/build.c b/src/build.c index 8dd2934..ab8c270 100644 --- a/src/build.c +++ b/src/build.c @@ -78,13 +78,15 @@ typedef enum { TK_IF, TK_ELSE, TK_WHILE, TK_FOR, TK_IN, TK_RAISE, TK_TRUE, TK_FALSE, - TK_AND, TK_OR, TK_NOT, + TK_AND, TK_OR, TK_NOT,TK_DOTDOT,TK_BREAK,TK_CONTINUE, + + TK_LPAREN, TK_RPAREN, TK_LBRACE, TK_RBRACE, TK_LBRACKET, TK_RBRACKET, TK_COMMA, TK_SEMI, - TK_DOT, + TK_DOT,TK_QUIT, TK_ASSIGN, /* = */ TK_PLUS, TK_MINUS, TK_STAR, TK_SLASH, TK_IMPORT, @@ -152,7 +154,12 @@ static void tokenize(const char *src, TokVec *out) { /* number */ if (isdigit((unsigned char)*p)) { const char *s = p; - while (isdigit((unsigned char)*p) || *p=='.') p++; + while (isdigit((unsigned char)*p)) p++; + if (*p=='.' && isdigit((unsigned char)p[1])) { + p++; + while (isdigit((unsigned char)*p)) p++; + } + char tmp[128]; size_t n = (size_t)(p - s); if (n >= sizeof(tmp)) error(line, "Splice/OverflowError number too long"); @@ -187,12 +194,15 @@ static void tokenize(const char *src, TokVec *out) { 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); @@ -205,9 +215,14 @@ 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 */ 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; @@ -216,8 +231,12 @@ static void tokenize(const char *src, TokVec *out) { 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_DOT,.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; @@ -495,6 +514,13 @@ static ASTNode *parse_statement(void) { n->var.value = rhs; return n; } + if (match(TK_CONTINUE)) { + return ast_new(AST_CONTINUE); + } + if (match(TK_BREAK)) { + return ast_new(AST_BREAK); + } + if (match(TK_PRINT)) { consume(TK_LPAREN, "Splice/SyntaxError Expected '(' after print"); @@ -504,6 +530,17 @@ static ASTNode *parse_statement(void) { n->print.expr = e; return n; } + if (match(TK_QUIT)) { + ASTNode *zero = ast_new(AST_NUMBER); + zero->number = 0; + + ASTNode *ret = ast_new(AST_RETURN); + ret->retstmt.expr = zero; + + return ret; + } + + if (match(TK_IMPORT)) { Tok *f = consume(TK_STRING, "Splice/SyntaxError Expected string filename after import"); @@ -577,24 +614,34 @@ static ASTNode *parse_statement(void) { consume(TK_LPAREN, "Splice/SyntaxError Expected '(' after if"); ASTNode *cond = parse_expression(); consume(TK_RPAREN, "Splice/SyntaxError Expected ')' after if cond"); + consume(TK_LBRACE, "Splice/SyntaxError Expected '{' after if"); ASTNode *thenb = parse_statements_until(TK_RBRACE); consume(TK_RBRACE, "Splice/SyntaxError Expected '}' after if body"); ASTNode *elseb = NULL; + if (match(TK_ELSE)) { - consume(TK_LBRACE, "Splice/SyntaxError Expected '{' after else"); - elseb = parse_statements_until(TK_RBRACE); - consume(TK_RBRACE, "Splice/SyntaxError Expected '}' after else body"); + /* 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"); + } } ASTNode *n = ast_new(AST_IF); - n->ifstmt.condition = cond; + n->ifstmt.condition = cond; n->ifstmt.then_branch = thenb; n->ifstmt.else_branch = elseb; return n; } + if (match(TK_WHILE)) { consume(TK_LPAREN, "Splice/SyntaxError Expected '(' after while"); ASTNode *cond = parse_expression(); @@ -613,7 +660,7 @@ static ASTNode *parse_statement(void) { 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_DOT, "Splice/SyntaxError Expected '.' in for range"); + 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); diff --git a/src/splice.c b/src/splice.c index a17453a..df8f4a1 100644 --- a/src/splice.c +++ b/src/splice.c @@ -22,10 +22,7 @@ int main(int argc, char **argv) { } ASTNode *root = read_ast_from_spc(arg); - info(0, "Loaded AST from %s", arg); interpret(root); free_ast(root); - - success(0, "Code ran successfully"); return 0; } diff --git a/src/splice.h b/src/splice.h index d46a78d..d436d23 100644 --- a/src/splice.h +++ b/src/splice.h @@ -119,9 +119,12 @@ typedef enum { AST_BINARY_OP, AST_LET, AST_ASSIGN, + AST_BREAK, AST_PRINT, AST_READ, AST_WRITE, + AST_CONTINUE, + AST_RAISE, AST_WARN, AST_INPUT, @@ -234,7 +237,8 @@ typedef struct { ========================= */ 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; @@ -780,6 +784,11 @@ static void write_ast_node(FILE *f, const ASTNode *n) { 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); @@ -890,6 +899,9 @@ static ASTNode *read_ast_node(FILE *f) { 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); @@ -908,6 +920,8 @@ static ASTNode *read_ast_node(FILE *f) { n->tuple.items[i] = read_ast_node(f); break; } + case AST_CONTINUE: + break; case AST_STRING: case AST_IDENTIFIER: @@ -1300,8 +1314,24 @@ static inline Value eval(ASTNode *node) { double result = 0; const char *op = node->binop.op ? node->binop.op : ""; - if (strcmp(op, "==") == 0) result = (lnum == rnum); - else if (strcmp(op, "!=") == 0) result = (lnum != rnum); + 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; @@ -1531,6 +1561,15 @@ static inline void interpret(ASTNode *node) { } 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 @@ -1631,10 +1670,32 @@ static inline void interpret(ASTNode *node) { break; } - case AST_WHILE: - while (eval(node->whilestmt.cond).number) - interpret(node->whilestmt.body); + 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) { @@ -1644,10 +1705,33 @@ static inline void interpret(ASTNode *node) { int start = (int)eval(node->forstmt.for_start).number; int end = (int)eval(node->forstmt.for_end).number; - for (int k = start; k <= end; ++k) { - set_var(node->forstmt.for_var, VAR_NUMBER, k, NULL); - interpret(node->forstmt.for_body); + 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; }