project euler #0 (which exposed a bug in my bigint promotion code!)
authorcassowarii <cassowary@cassowary.me>
Sun, 12 Jul 2026 23:24:19 +0000 (16:24 -0700)
committercassowarii <cassowary@cassowary.me>
Sun, 12 Jul 2026 23:24:19 +0000 (16:24 -0700)
sample/project_euler/pe00.sa [new file with mode: 0644]
src/compile/emit.c
src/data/integer.c
src/lib/module/list.c
src/parse/lexer.c

diff --git a/sample/project_euler/pe00.sa b/sample/project_euler/pe00.sa
new file mode 100644 (file)
index 0000000..a383a86
--- /dev/null
@@ -0,0 +1,5 @@
+list::iota1 130_000
+  | _.filter => n { not (n %% 2) }
+  | _.map => n { n * n }
+  | _.reduce 0, => a, b { a + b }
+  | println
index f7ee340..ccd02e4 100644 (file)
@@ -431,6 +431,7 @@ void compile_op(sbVmCompiler *cm, sbAstOp op) {
     case AST_OP_GT: EMIT(BC_OP_LE, BC_OP_NOT); break;
     case AST_OP_LE: EMIT(BC_OP_LE); break;
     case AST_OP_GE: EMIT(BC_OP_LT, BC_OP_NOT); break;
+    case AST_OP_NOT: EMIT(BC_OP_NOT); break;
     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;
index bee7706..b06ed74 100644 (file)
@@ -66,8 +66,8 @@ hInteger sbInteger_new(i64 value) {
       i->sign_bit = 1;
       value *= -1;
     }
-    i->buf.data[0] = (value % BIGINT_PIECE_MAX);
-    i->buf.data[1] = (value / BIGINT_PIECE_MAX);
+    ((piece*)(i->buf.data))[0] = (value % BIGINT_PIECE_MAX);
+    ((piece*)(i->buf.data))[1] = (value / BIGINT_PIECE_MAX);
 
     return i->handle;
   }
index 5845041..770fc0d 100644 (file)
 
 sbLibTable g_list_module;
 
-static void iota(hVm vm, usize argc) {
+static void do_iota(hVm vm, usize argc, i32 offset) {
   if (argc != 1 && argc != 2) {
-    PANIC("list::iota takes 1 or 2 arguments");
+    if (offset == 1) {
+      PANIC("list::iota1 takes 1 or 2 arguments");
+    } else {
+      PANIC("list::iota takes 1 or 2 arguments");
+    }
   }
 
   hV *first = sbVm_pop(vm);
@@ -20,17 +24,25 @@ static void iota(hVm vm, usize argc) {
   hInteger min, max;
   if (argc == 1) {
     if (first->type != IT_INTEGER) {
-      PANIC("list::iota's argument must be an integer!");
+      if (offset == 1) {
+        PANIC("list::iota1's argument must be an integer!");
+      } else {
+        PANIC("list::iota's argument must be an integer!");
+      }
     }
-    min = 0;
-    max = first->integer;
+    min = offset;
+    max = first->integer + offset;
   } else {
     hV *second = sbVm_pop(vm);
     if (first->type != IT_INTEGER || second->type != IT_INTEGER) {
-      PANIC("list::iota's arguments must be integers!");
+      if (offset == 1) {
+        PANIC("list::iota1's arguments must be integers!");
+      } else {
+        PANIC("list::iota's arguments must be integers!");
+      }
     }
-    min = first->integer;
-    max = second->integer;
+    min = first->integer + offset;
+    max = second->integer + offset;
   }
 
   hList result = sbList_new((usize)sbInteger_diff(max, min));
@@ -43,8 +55,17 @@ static void iota(hVm vm, usize argc) {
   sbVm_push_immediate(vm, &HVLIST(result));
 }
 
+static void iota(hVm vm, usize argc) {
+  do_iota(vm, argc, 0);
+}
+
+static void iota1(hVm vm, usize argc) {
+  do_iota(vm, argc, 1);
+}
+
 void sbLib_loadmodule_list() {
   sbLibTable_initialize(&g_list_module, 16, FALSE);
   REGISTER_VALUE(&g_list_module, "iota", &HVBUILTIN(iota));
+  REGISTER_VALUE(&g_list_module, "iota1", &HVBUILTIN(iota1));
   REGISTER_VALUE(&g_list_module, "convert", &HVSYM(S_OP_TO_LIST));
 }
index db364ae..7ec339a 100644 (file)
@@ -198,8 +198,6 @@ static flag is_closing_bracket(sbTokenType type) {
 /* header keywords that introduce a block and go until a { */
 static flag begins_brace_terminated_state(sbTokenType type) {
     return type == T_rDEF
-        || type == T_rIF
-        || type == T_rUNLESS
         || type == T_rWHILE
         || type == T_rUNTIL
         || type == T_rDO
@@ -211,6 +209,12 @@ static flag begins_brace_terminated_state(sbTokenType type) {
         || type == T_SQUIGARROW; /* squiggle arrow ~> a, b { ... } introduces block header also */
 }
 
+/* only starts BTS at start of line, not in middle of line */
+static flag begins_brace_terminated_state_at_line_start(sbTokenType type) {
+    return type == T_rIF
+        || type == T_rUNLESS;
+}
+
 /* things we can potentially insert a ( after because they may be a call */
 static flag can_open_paren_after(sbTokenType type) {
     return type == T_IDENTIFIER
@@ -232,19 +236,11 @@ static flag block_header_can_end_after(sbTokenType type) {
         || type == T_rDO;
 }
 
-/* these are operators that look weird if they get captured inside
- * of parentheses, and also if and unless in postfix form */
+/* close invisible parentheses before we see this token */
 static flag close_invisible_parens_before(sbTokenType type) {
-    return type == T_rAND
-        || type == T_rOR
+    return type == T_SEMICOLON
         || type == T_rIF
         || type == T_rUNLESS
-        || type == T_DOUBLEEQUALS
-        || type == T_NOTEQUALS
-        || type == T_LESS
-        || type == T_GREATER
-        || type == T_LESSEQUALS
-        || type == T_GREATEREQUALS
         || type == T_PIPE;
 }
 
@@ -394,7 +390,8 @@ static void compute_next_token(hLexer lx) {
         unstack_all_invisible_parentheses(lx);
     }
 
-    if (begins_brace_terminated_state(token.type)) {
+    if (begins_brace_terminated_state(token.type)
+        || (begins_brace_terminated_state_at_line_start(token.type) && lx->last_token_seen.type == ';')) {
         /* don't start brace-terminated state if we are directly after a '}' (otherwise it gets
          * confused by things like repeat..until */
         if (lx->last_token_seen.type != '}') {