feat: pattern bindings, empty closures, match-as-expr, string interp

Sessions 10–12 from QUALITY_PLAN:

- Pattern payload bindings (Some(value) => value) in bootstrap and selfhost
- Empty-param closures via || (tkPipePipe) with loop/return bodies
- Expression-form match: let x = match …; newline before arms
- f"…" string interpolation desugared to String_Concat + conversions
- Lexer preserves \{ \} for literal braces in f-strings
- Bootstrap fix: f"plain" strips the f prefix after escape processing
- Examples: pattern_matching, closure_control, match_let, string_interp

Selfhost-loop remains binary-identical; all examples and error goldens pass.
This commit is contained in:
2026-07-18 00:58:44 +03:00
parent f619316470
commit db41ba4d84
18 changed files with 1192 additions and 67 deletions
+63
View File
@@ -0,0 +1,63 @@
// Closures with control flow: empty `||` params, while/break, early return
import Std::Io::{PrintLine, PrintInt};
import Std::Test::{Test_AssertEqInt, Test_Pass};
// Zero-param capturing closure written as `||` (not `| |`)
func MakeCounter(limit: int) -> func() -> int {
return || -> int {
var sum: int = 0;
var i: int = 0;
while i < limit {
sum = sum + i;
i = i + 1;
if i == 3 {
break;
}
}
return sum;
};
}
func MakeClamp(n: int) -> func(int) -> int {
return |x: int| -> int {
if x < 0 {
return 0;
}
if x > n {
return n;
}
return x;
};
}
// return from inside a loop inside a closure
func MakeLoopReturn(n: int) -> func() -> int {
return || -> int {
var i: int = 0;
while i < n {
if i == 2 {
return 99;
}
i = i + 1;
}
return i;
};
}
func Main() -> int {
let c: func() -> int = MakeCounter(10);
Test_AssertEqInt(c(), 3); // 0+1+2
let f: func(int) -> int = MakeClamp(5);
Test_AssertEqInt(f(-1), 0);
Test_AssertEqInt(f(3), 3);
Test_AssertEqInt(f(100), 5);
let g: func() -> int = MakeLoopReturn(10);
Test_AssertEqInt(g(), 99);
PrintInt(c());
PrintLine("");
Test_Pass("closure_control");
return 0;
}
+43
View File
@@ -0,0 +1,43 @@
// Match as expression: let-init, inline arms, combined with operators
import Std::Io::{PrintLine, PrintInt};
import Std::Test::{Test_AssertEqInt, Test_Pass};
enum Option {
Some(int),
None
}
func Main() -> int {
let opt: Option = Option { tag: Option_Some };
opt.data.Some_0 = 21;
let x: int = match opt {
Option::Some(v) => v + v,
Option::None => 0
};
Test_AssertEqInt(x, 42);
let n: int = 3;
let y: int = match n {
0 => 100,
1..5 => 200,
_ => -1
};
Test_AssertEqInt(y, 200);
let z: int = match n { 3 => 1, _ => 0 } + match n { 3 => 2, _ => 0 };
Test_AssertEqInt(z, 3);
let none: Option = Option { tag: Option_None };
// Same binding name as above — one alloca, reassigned per arm
let w: int = match none {
Option::Some(v) => v,
Option::None => -7
};
Test_AssertEqInt(w, -7);
PrintInt(x);
PrintLine("");
Test_Pass("match_let");
return 0;
}
+35 -3
View File
@@ -1,19 +1,41 @@
// Pattern Matching — enum tags, literals, ranges, wildcard
// Pattern Matching — enum tags, payload bindings, literals, ranges, wildcard
import Std::Io::{PrintLine, PrintInt};
import Std::Test::{Test_AssertEqInt, Test_Pass};
enum Option {
Some(int),
None
}
enum Msg {
Quit,
Move(int),
Write(String)
}
func GetValue(opt: Option) -> int {
// Pattern binding: value is bound from Option::Some payload
match opt {
Option::Some(value) => opt.data.Some_0,
Option::Some(value) => value,
Option::None => 0
}
}
func DoubleSome(opt: Option) -> int {
match opt {
Option::Some(n) => n + n,
Option::None => -1
}
}
func MsgCode(m: Msg) -> int {
match m {
Msg::Quit => 0,
Msg::Move(x) => x,
Msg::Write(s) => 1
}
}
func Classify(n: int) -> int {
// literal arms + exclusive range + inclusive range + wildcard
match n {
@@ -40,6 +62,15 @@ func Main() -> int {
let opt2: Option = Option { tag: Option_None };
Test_AssertEqInt(GetValue(opt1), 42);
Test_AssertEqInt(GetValue(opt2), 0);
Test_AssertEqInt(DoubleSome(opt1), 84);
Test_AssertEqInt(DoubleSome(opt2), -1);
let m: Msg = Msg { tag: Msg_Move };
m.data.Move_0 = 7;
Test_AssertEqInt(MsgCode(m), 7);
PrintLine("opt1 value: ");
PrintInt(GetValue(opt1));
PrintLine("");
@@ -61,5 +92,6 @@ func Main() -> int {
PrintLine("color:");
PrintLine(ColorName(2));
Test_Pass("pattern_matching");
return 0;
}
+23
View File
@@ -0,0 +1,23 @@
import Std::Io::{PrintLine};
import Std::Test::{Test_AssertEqString, Test_Pass};
func Main() -> int {
let name: String = "Bux";
let n: int = 42;
let msg: String = f"Hello, {name}! count={n}";
PrintLine(msg);
Test_AssertEqString(msg, "Hello, Bux! count=42");
let flag: bool = true;
let bmsg: String = f"flag={flag}";
Test_AssertEqString(bmsg, "flag=true");
let empty: String = f"plain";
Test_AssertEqString(empty, "plain");
let raw: String = f"use \{braces\} literally";
Test_AssertEqString(raw, "use {braces} literally");
Test_Pass("string_interp");
return 0;
}