plain 'return' + postfix 'if'/'unless'
authorcassowarii <cassowary@cassowary.me>
Tue, 7 Jul 2026 07:20:08 +0000 (00:20 -0700)
committercassowarii <cassowary@cassowary.me>
Tue, 7 Jul 2026 07:20:08 +0000 (00:20 -0700)
src/compile/ir.c
src/parse/lexer.c
src/parse/parser.c

index 42e37d5..a0387e8 100644 (file)
@@ -668,7 +668,8 @@ static void compile_ast_stmt(hIrChunk ck, sbAst node, flag implicit_return) {
        * OK, this one's pretty simple.
        */
       if (node->seq.left == NO_NODE) {
-        E1 = NULL;
+        /* plain 'return' with no qualifier means 'return nil' */
+        E1 = NIL_EXPR;
       } else {
         /* TODO handle multival here */
         E1 = compile_ast_expr(ck, node->seq.left->seq.left, FALSE);
index faa18a2..268042c 100644 (file)
@@ -233,10 +233,12 @@ static flag block_header_can_end_after(sbTokenType type) {
 }
 
 /* these are operators that look weird if they get captured inside
- * of parentheses. */
+ * of parentheses, and also if and unless in postfix form */
 static flag close_invisible_parens_before(sbTokenType type) {
     return type == T_rAND
         || type == T_rOR
+        || type == T_rIF
+        || type == T_rUNLESS
         || type == T_DOUBLEEQUALS
         || type == T_NOTEQUALS
         || type == T_LESS
@@ -399,6 +401,10 @@ static void compute_next_token(hLexer lx) {
         brackets_stack_pop(lx); /* remove 'B' state from bracket stack */
     }
 
+    if (close_invisible_parens_before(token.type)) {
+        unstack_all_invisible_parentheses(lx);
+    }
+
     if (begins_brace_terminated_state(token.type)) {
         /* don't start brace-terminated state if we are directly after a '}' (otherwise it gets
          * confused by things like repeat..until */
@@ -407,10 +413,6 @@ static void compute_next_token(hLexer lx) {
         }
     }
 
-    if (close_invisible_parens_before(token.type)) {
-        unstack_all_invisible_parentheses(lx);
-    }
-
     /* --- HERE IS WHERE THE TOKEN ACTUALLY GETS OUTPUT TO THE STREAM --- */
     /* Everything before this gets put into the stream ahead of this token. */
     /* Everything after this comes after the token. */
@@ -536,6 +538,13 @@ static void compute_next_token(hLexer lx) {
                 };
                 enqueue_output_token(lx, invisible_semicolon);
                 lx->last_token_seen = invisible_semicolon;
+
+                if (brackets_stack_top(lx) == 'B') {
+                    /* if we're in brace-terminated state but encounter an end of line, it's
+                     * ok to leave brace-terminated state. this applies to postfix 'if' and
+                     * 'unless' in particular */
+                    brackets_stack_pop(lx);
+                }
             }
         }
     }
index 30e8c10..077bee8 100644 (file)
@@ -684,10 +684,10 @@ static sbAst with_trailing_conditional(hParser pr, sbAst stmt) {
 
   if (expect(pr, T_rIF)) {
     sbAst condition = parse_expr(pr, 0);
-    result = tri_node(pr, AST_NODE_IF, condition, stmt, NO_NODE);
+    result = tri_node(pr, AST_NODE_IF, condition, seq_node(pr, AST_NODE_SEQ, stmt, NO_NODE), NO_NODE);
   } else if (expect(pr, T_rUNLESS)) {
     sbAst condition = parse_expr(pr, 0);
-    result = tri_node(pr, AST_NODE_IF, unop_node(pr, AST_OP_NOT, condition), stmt, NO_NODE);
+    result = tri_node(pr, AST_NODE_IF, unop_node(pr, AST_OP_NOT, condition), seq_node(pr, AST_NODE_SEQ, stmt, NO_NODE), NO_NODE);
   }
 
   return result;