From 29013be34209d3588224a92c793d259e0b613910 Mon Sep 17 00:00:00 2001 From: cassowarii <2374677+cassowarii@users.noreply.github.com> Date: Sun, 12 Jul 2026 21:42:26 -0700 Subject: [PATCH] range indexing a[b..c] (i reserve the right to change this syntax) --- src/compile/emit.c | 3 +++ src/compile/ir.c | 15 +++++++++++++-- src/data/integer.c | 8 ++++++++ src/data/integer.h | 2 ++ src/data/list.c | 12 ++++++++++++ src/data/list.h | 2 ++ src/data/value.c | 4 ++++ src/data/value.h | 1 + src/parse/ast.h | 1 + src/parse/parser.c | 25 +++++++++++++------------ src/parse/scanner.c | 12 ------------ src/vm/bytecode.h | 1 + src/vm/exec.c | 8 ++++++++ src/vm/operations.c | 37 +++++++++++++++++++++++++++++++++++++ src/vm/operations.h | 2 ++ 15 files changed, 107 insertions(+), 26 deletions(-) diff --git a/src/compile/emit.c b/src/compile/emit.c index ccd02e4..37638ba 100644 --- a/src/compile/emit.c +++ b/src/compile/emit.c @@ -435,6 +435,9 @@ void compile_op(sbVmCompiler *cm, sbAstOp op) { case AST_OP_AND: EMIT(BC_OP_AND); break; case AST_OP_OR: EMIT(BC_OP_OR); break; case AST_OP_INDEX: EMIT(BC_OP_INDEX); break; + case AST_OP_RANGEINDEX: EMIT(BC_OP_RANGEINDEX); break; + /* op range is currently only used in rangeindex; just pass them to it directly */ + case AST_OP_RANGE: break; case AST_OP_DIVBY: EMIT(BC_OP_MOD, BC_LD_IMM); EARG(0); EMIT(BC_OP_EQ); break; default: PANIC("unknown operation! (%lld / %c)", (long long)op, op); diff --git a/src/compile/ir.c b/src/compile/ir.c index 88807fe..c625403 100644 --- a/src/compile/ir.c +++ b/src/compile/ir.c @@ -1039,9 +1039,20 @@ static sbIrExpr *compile_ast_expr(hIrChunk ck, sbAst node, flag list_context) { sbIrExpr *hash = compile_ast_hash(ck, node->seq.left); return hash; } else if (node->type == AST_NODE_OP) { - if (node->op.type == AST_OP_SPLAT) { + if (node->op.type == AST_OP_RANGE) { + chunk_error(ck, "'..' operator not permitted in this context\n"); + return NULL; + } else if (node->op.type == AST_OP_RANGEINDEX) { + return expr_op(ck, AST_OP_RANGEINDEX, + compile_ast_expr(ck, node->op.left, FALSE), + expr_op(ck, AST_OP_RANGE, + compile_ast_expr(ck, node->op.right->op.left, FALSE), + compile_ast_expr(ck, node->op.right->op.right, FALSE) + ) + ); + } else if (node->op.type == AST_OP_SPLAT) { if (!list_context) { - chunk_error(ck, "'...' not allowed outside a list"); + chunk_error(ck, "'...' not allowed outside a list\n"); return NULL; } else { return expr_op(ck, AST_OP_SPLAT, compile_ast_expr(ck, node->op.left, FALSE), NULL); diff --git a/src/data/integer.c b/src/data/integer.c index b06ed74..bd12d06 100644 --- a/src/data/integer.c +++ b/src/data/integer.c @@ -87,6 +87,14 @@ void sbInteger_release(hInteger a) { } } +i64 sbInteger_get_value(hInteger a) { + if (!is_bigint(a)) { + return a; + } else { + PANIC("Integer value out of range"); + } +} + hInteger sbInteger_sum(hInteger a, hInteger b) { if (!is_bigint(a) && !is_bigint(b)) { return sbInteger_new(a + b); diff --git a/src/data/integer.h b/src/data/integer.h index b7beed3..ce092ef 100644 --- a/src/data/integer.h +++ b/src/data/integer.h @@ -11,6 +11,8 @@ void sbInteger_sys_deinit(); hInteger sbInteger_new(i64 value); +i64 sbInteger_get_value(hInteger a); + void sbInteger_retain(hInteger a); void sbInteger_release(hInteger a); diff --git a/src/data/list.c b/src/data/list.c index 467e9ea..cd4b438 100644 --- a/src/data/list.c +++ b/src/data/list.c @@ -2,6 +2,7 @@ #include "gc/gcinfo.h" #include "vm/exec.h" +#include "mem/debug.h" #define LIST_PER_BLOCK 256 #define METHOD_IS(name) (!sbstrncmp(method_name, name, sizeof(name))) @@ -33,6 +34,17 @@ hList sbList_new(usize capacity) { return index; } +hList sbList_of(usize length, hV *items) { + usize index; + usize capacity = length; + sbList *l = sbPool_alloc(&g_list_pool, &index); + if (capacity < 4) capacity = 4; + sbBuffer_initialize(&l->items, capacity * sizeof(hV)); + sbBuffer_set_size(&l->items, length * sizeof(hV)); + memcpy(l->items.data, items, length * sizeof(hV)); + return index; +} + void sbList_append(hList list, hV *item) { sbList *l = get_list_by_handle(list); sbV_retain(item); diff --git a/src/data/list.h b/src/data/list.h index 36b32a0..14432d6 100644 --- a/src/data/list.h +++ b/src/data/list.h @@ -6,6 +6,8 @@ void sbList_sys_deinit(); hList sbList_new(usize capacity); +hList sbList_of(usize length, hV *items); + void sbList_append(hList list, hV *item); hV *sbList_get_value(hList list, usize *length); diff --git a/src/data/value.c b/src/data/value.c index 68517cd..97b491f 100644 --- a/src/data/value.c +++ b/src/data/value.c @@ -67,6 +67,10 @@ hV sbV_empty_hash(usize capacity) { }; } +hV sbV_empty_string() { + return sbV_string((hString)0); +} + flag sbV_c_eq(const hV *a, const hV *b) { if (a->type != b->type) return FALSE; if (a->type == ITX_TOMBSTONE || b->type == ITX_TOMBSTONE) return FALSE; diff --git a/src/data/value.h b/src/data/value.h index 6aa6a63..1c28d80 100644 --- a/src/data/value.h +++ b/src/data/value.h @@ -75,6 +75,7 @@ hV sbV_int(hInteger i); hV sbV_boolean(flag b); hV sbV_empty_list(usize capacity); hV sbV_empty_hash(usize capacity); +hV sbV_empty_string(); flag sbV_c_eq(const hV *a, const hV *b); flag sbV_c_falsy(const hV *a); diff --git a/src/parse/ast.h b/src/parse/ast.h index c870e7a..dc317dc 100644 --- a/src/parse/ast.h +++ b/src/parse/ast.h @@ -66,6 +66,7 @@ typedef enum sbAstOp { AST_OP_RANGE, AST_OP_DIVBY, AST_OP_INDEX, + AST_OP_RANGEINDEX, AST_OP_SCOPE, AST_OP_UNPLUS, AST_OP_UNMINUS, diff --git a/src/parse/parser.c b/src/parse/parser.c index 639fec7..897d6a3 100644 --- a/src/parse/parser.c +++ b/src/parse/parser.c @@ -118,8 +118,6 @@ static tokenspelling token_spellings[] = { { T_BACKSLASH, "'\\'" }, { T_ARROW, "'->'" }, { T_FATARROW, "'=>'" }, - { T_SQUIGARROW, "'~>'" }, - { T_BACKSQUIGARROW, "'<~'" }, { T_COLONBRACE, "':{'" }, { T_PAAMAYIM_NEKUDOTAYIM, "'::'" }, { T_DOUBLEEQUALS, "'=='" }, @@ -146,7 +144,6 @@ static const char *const TOKEN_SPELLING_UNKNOWN = ""; static binop binops[] = { { T_PIPE, 6, 7, AST_OP_PIPE }, - { T_BACKSQUIGARROW, 8, 9, AST_OP_SEND }, { T_rOR, 15, 16, AST_OP_OR }, { T_rAND, 20, 21, AST_OP_AND }, { T_rIN, 25, 26, AST_OP_IN }, @@ -164,7 +161,6 @@ static binop binops[] = { { T_PERCENT, 60, 61, AST_OP_MOD }, { T_DOUBLESLASH, 60, 61, AST_OP_FLDIV }, { T_DOUBLEASTERISK, 71, 70, AST_OP_POW }, - { T_TWODOT, 80, 81, AST_OP_RANGE }, { T_DOT, 90, 91, AST_OP_NULL }, { T_ARROW, 90, 91, AST_OP_NULL }, { T_LPAREN, 90, 91, AST_OP_NULL }, @@ -531,13 +527,6 @@ static sbAst parse_expr(hParser pr, u8 min_precedence) { if (!expect(pr, T_RPAREN)) return syntax_error(pr); sbAst body = parse_block(pr); lhs = seq_node(pr, AST_VAL_FUNC, params, body); - } else if (t.type == T_SQUIGARROW) { - next_token(pr); - if (!expect(pr, T_LPAREN)) return syntax_error(pr); - sbAst params = parse_comma_exprs(pr, NULL); - if (!expect(pr, T_RPAREN)) return syntax_error(pr); - sbAst body = parse_block(pr); - lhs = seq_node(pr, AST_VAL_OBJ, params, body); } else if (t.type == T_COLONBRACE) { next_token(pr); sbAst body = parse_stmtseq(pr); @@ -600,6 +589,8 @@ static sbAst parse_expr(hParser pr, u8 min_precedence) { break; } + sbAstOp ast_op = infix->ast_op; + next_token(pr); /* consume operator token */ sbAst rhs; @@ -611,6 +602,16 @@ static sbAst parse_expr(hParser pr, u8 min_precedence) { } else if (op.type == T_LBRACKET) { /* indexing */ rhs = parse_expr(pr, 0); + if (rhs == NO_NODE) return syntax_error(pr); + + if (expect(pr, T_TWODOT)) { + /* Range indexing */ + ast_op = AST_OP_RANGEINDEX; + sbAst end_range = parse_expr(pr, 0); + if (end_range == NO_NODE) return syntax_error(pr); + rhs = binop_node(pr, AST_OP_RANGE, rhs, end_range); + } + if (!expect(pr, T_RBRACKET)) return syntax_error(pr); } else if (op.type == T_DOT || op.type == T_ARROW) { /* . and -> parse an identifier to their right as a symbol that will @@ -641,7 +642,7 @@ static sbAst parse_expr(hParser pr, u8 min_precedence) { } if (ast_type == AST_NODE_OP) { - lhs = binop_node(pr, infix->ast_op, lhs, rhs); + lhs = binop_node(pr, ast_op, lhs, rhs); } else { lhs = seq_node(pr, ast_type, lhs, rhs); } diff --git a/src/parse/scanner.c b/src/parse/scanner.c index 5808532..0e210ba 100644 --- a/src/parse/scanner.c +++ b/src/parse/scanner.c @@ -305,15 +305,6 @@ static sbLexToken compute_next_token(hScanner sc) { /* ! on its own is not an operator */ new_token.type = T_ERROR; } - } else if (ch == '~') { - ch = NEXT; - if (ch == '>') { - new_token.type = T_SQUIGARROW; - NEXT; - } else { - /* ~ on its own is not an operator */ - new_token.type = T_ERROR; - } } else if (ch == '>') { ch = NEXT; if (ch == '>') { @@ -346,9 +337,6 @@ static sbLexToken compute_next_token(hScanner sc) { } else if (ch == '=') { new_token.type = T_LESSEQUALS; NEXT; - } else if (ch == '~') { - new_token.type = T_BACKSQUIGARROW; - NEXT; } else { new_token.type = T_LESS; } diff --git a/src/vm/bytecode.h b/src/vm/bytecode.h index 54368eb..b994d95 100644 --- a/src/vm/bytecode.h +++ b/src/vm/bytecode.h @@ -51,6 +51,7 @@ typedef enum sbOpcode { BC_OP_AND, // logical and BC_OP_OR, // logical or BC_OP_INDEX, // index [] or :: + BC_OP_RANGEINDEX, // index [a..b] BC_ALLOC_VARS, // create space in rstack for local variables BC_CLOSURE, // create closure from top elements of stack BC_LIST_GATHER, // create list from count + list of values on stack diff --git a/src/vm/exec.c b/src/vm/exec.c index 2d2ee7c..24be18b 100644 --- a/src/vm/exec.c +++ b/src/vm/exec.c @@ -651,6 +651,14 @@ void execute_instruction(hVm vm) { npop_stack(vm, 2); push_stack(vm, x); break; + case BC_OP_RANGEINDEX: + v = peek_stack(vm, 2); + w = peek_stack(vm, 1); + x = peek_stack(vm, 0); + res = sbV_rangeindex(v, w, x); + npop_stack(vm, 3); + push_stack_immediate(vm, &res); + break; case BC_OP_DEREF: PANIC("todo"); case BC_ALLOC_VARS: diff --git a/src/vm/operations.c b/src/vm/operations.c index 8eb5973..bd406ae 100644 --- a/src/vm/operations.c +++ b/src/vm/operations.c @@ -142,6 +142,43 @@ hV *sbV_index(hV *a, hV *b) { } } +hV sbV_rangeindex(hV *a, hV *b, hV *c) { + if (b->type == IT_INTEGER && c->type == IT_INTEGER) { + usize length; + i64 min = sbInteger_get_value(b->integer); + i64 max = sbInteger_get_value(c->integer); + if (a->type == IT_LIST) { + hV *elements = sbList_get_value(a->list, &length); + if (min >= max || min >= length || max < 0) { + /* backwards or out of range */ + return sbV_empty_list(0); + } else { + /* now we know 0 <= max, min < max, min < length. clip min and max to bounds */ + if (max >= length) max = length; + if (min < 0) min = 0; + return HVLIST(sbList_of(max - min, &elements[min])); + } + } else if (a->type == IT_STRING) { + /* substring operation */ + char scratch[8]; + const char *strdata = sbString_get_value(a->string, scratch, &length); + if (min >= max || min >= length || max < 0) { + /* backwards or out of range */ + return sbV_empty_string(0); + } else { + /* now we know 0 <= max, min < max, min < length. clip min and max to bounds */ + if (max >= length) max = length; + if (min < 0) min = 0; + return HVSTR(sbString_new(&strdata[min], max - min)); + } + } else { + PANIC("todo %lld %lld %lld", (long long)a->type, (long long)b->type, (long long)c->type); + } + } else { + PANIC("Only integers are supported for range indexing!"); + } +} + void sbV_index_set(hV *obj, hV *key, hV *value) { if (obj->type == IT_LIST) { PANIC("todo"); diff --git a/src/vm/operations.h b/src/vm/operations.h index cc40d61..5683aa4 100644 --- a/src/vm/operations.h +++ b/src/vm/operations.h @@ -26,4 +26,6 @@ hV sbV_append(hV *a, hV *b); hV *sbV_index(hV *a, hV *b); +hV sbV_rangeindex(hV *a, hV *b, hV *c); + void sbV_index_set(hV *obj, hV *key, hV *value); -- 1.8.3.1