block pipe in situations where it is buggy for now. how to fix?
authorcassowarii <2374677+cassowarii@users.noreply.github.com>
Sat, 11 Jul 2026 05:19:29 +0000 (22:19 -0700)
committercassowarii <2374677+cassowarii@users.noreply.github.com>
Sat, 11 Jul 2026 05:19:29 +0000 (22:19 -0700)
sample/project_euler/pe06.sa
src/compile/analyze.c [new file with mode: 0644]
src/compile/analyze.h [new file with mode: 0644]
src/compile/ir.c
src/compile/ir.h

index 3587fe3..51aad0e 100644 (file)
@@ -15,11 +15,11 @@ def map f {
 }
 
 def sum_squares list {
-  list | (square | map) | sum
+  list.map(square) | sum
 }
 
 def square_sum list {
   list | sum | square
 }
 
-...[1, 101] | list::iota | (square_sum _) - (sum_squares _) | println
+list::iota 1, 101 | (square_sum _) - (sum_squares _) | println
diff --git a/src/compile/analyze.c b/src/compile/analyze.c
new file mode 100644 (file)
index 0000000..09a734f
--- /dev/null
@@ -0,0 +1,61 @@
+#include "compile/analyze.h"
+
+#include "parse/ast.h"
+#include "data/symbol.h"
+
+i32 sbAst_count_pipe_underscores(sbAst node) {
+  if (node == NULL) return 0;
+
+  switch (node->type) {
+    case AST_NULL:
+      /* like the end of a list or whatever */
+      return 0;
+    case AST_NODE_NAME:
+      if (sbstrncmp(sbSymbol_name(node->symb), "_", 1) == 0) {
+        return 1;
+      } else {
+        return 0;
+      }
+    case AST_NODE_SEQ:
+    case AST_NODE_NEXT:
+    case AST_NODE_MULTIVAL:
+    case AST_NODE_FUNCCALL:
+    case AST_VAL_FUNC:
+    case AST_VAL_IMFUNC:
+    case AST_VAL_OBJ:
+    case AST_NODE_HASHENTRY:
+      return sbAst_count_pipe_underscores(node->seq.left)
+           + sbAst_count_pipe_underscores(node->seq.right);
+    case AST_NODE_OP:
+      if (node->op.type == AST_OP_PIPE) {
+        /* for a pipe, underscores on the right side don't count
+         * towards this, because they refer to the result of whatever
+         * is on the left side. but underscores on the left side do
+         * refer to what we're binding, so they do count */
+        return sbAst_count_pipe_underscores(node->op.left);
+      } else {
+        return sbAst_count_pipe_underscores(node->op.left);
+             + sbAst_count_pipe_underscores(node->op.right);
+      }
+    case AST_NODE_ASSIGN:
+    case AST_NODE_LET:
+      /* for declarations and assignments, we only count the expressions
+       * on the right, not the things on the left (this might happen if we
+       * have a pipe into a function literal that contains statements etc) */
+      return sbAst_count_pipe_underscores(node->op.right);
+    case AST_VAL_LIST:
+    case AST_VAL_HASH:
+      /* check the things inside the whatever */
+      return sbAst_count_pipe_underscores(node->op.left);
+    case AST_VAL_NIL:
+    case AST_VAL_INT:
+    case AST_VAL_STRING:
+    case AST_VAL_FLOAT:
+    case AST_VAL_SYMBOL:
+    case AST_VAL_BOOLEAN:
+      /* these are not the name "_" */
+      return 0;
+    default:
+      PANIC("aah! (%lld)", (long long)node->type);
+  }
+}
diff --git a/src/compile/analyze.h b/src/compile/analyze.h
new file mode 100644 (file)
index 0000000..6978779
--- /dev/null
@@ -0,0 +1,4 @@
+#include "common.h"
+
+struct sbAstNode;
+i32 sbAst_count_pipe_underscores(struct sbAstNode *node);
index 6ee0b51..31e96a8 100644 (file)
@@ -1,5 +1,6 @@
 #include "compile/ir.h"
 
+#include "compile/analyze.h"
 #include "parse/ast.h"
 #include "data/symbol.h"
 #include "data/integer.h"
@@ -1032,15 +1033,22 @@ static sbIrExpr *compile_ast_expr(hIrChunk ck, sbAst node, flag list_context) {
     } else if (node->op.type == AST_OP_PIPE) {
       /* assign LHS to our temporary secret pipe variable,
        * then use RHS as our value */
-      if (node->op.right->has_us) {
+      i32 num_bindings = sbAst_count_pipe_underscores(node->op.right);
+      if (num_bindings > 0) {
         /* TODO: I realized this doesn't really make sense if we nest | within ().
          * like, a | (b | c _) _ won't work properly. so really we need a whole
          * stack of these temporary variables potentially... maybe we need to save
          * the current pipe var here and restore it again after? also, whither
          * something like a(b | c, d | e)? oh god... */
+        if (ck->pipe_var_in_use || list_context) {
+          PANIC("Pipe cannot currently be used in this context. I will fix it");
+        }
         sbIrExpr *left = compile_ast_expr(ck, node->op.left, FALSE);
         put_assign(ck, pipe_var(ck), left);
-        return compile_ast_expr(ck, node->op.right, FALSE);
+        ck->pipe_var_in_use = TRUE;
+        sbIrExpr *right = compile_ast_expr(ck, node->op.right, FALSE);
+        ck->pipe_var_in_use = FALSE;
+        return right;
       } else {
         /* if there is no _ to the right of the "|", assume it is a function
          * call that we are passing the left side to as a singular argument. this
@@ -1048,8 +1056,11 @@ static sbIrExpr *compile_ast_expr(hIrChunk ck, sbAst node, flag list_context) {
         return expr_call(ck,
             /* thing to call */
             compile_ast_expr(ck, node->op.right, FALSE),
-            /* parameter list (weirdly i guess the left thing could be a splat) */
-            expr_list(ck, compile_ast_expr(ck, node->op.left, TRUE)));
+            /* parameter list (true = the left thing can be a splat, because it does work,
+             * but you can't have commas. but you can do ...[x, y, z] | stuff) */
+            //expr_list(ck, compile_ast_expr(ck, node->op.left, TRUE)));
+            // ok currently you can't. but we will
+            expr_list(ck, compile_ast_expr(ck, node->op.left, FALSE)));
       }
     } else {
       sbIrExpr *left = NULL, *right = NULL;
index d1a378c..21a3cde 100644 (file)
@@ -142,6 +142,7 @@ struct sbIrProgram;
 
 typedef struct sbIrChunk {
   struct sbIrProgram *program;
+  flag pipe_var_in_use : 1;
   i32 id;
   i16 num_args;
   i16 num_upvalues;