346 lines
7.7 KiB
Plaintext
346 lines
7.7 KiB
Plaintext
# Milestones 20 / 20.5: `yield`, value blocks, and value if/loops.
|
|
#
|
|
# A `{ ... }`, an `if`, or a labeled `for`/`while` on the right of a declaration
|
|
# or assignment is a *value source*: `yield <expr>` supplies the value (the block
|
|
# analogue of `return`). Every path must yield. Each section returns a distinct
|
|
# code on failure; success falls through to 42.
|
|
|
|
# --- value blocks (milestone 20) ---------------------------------------------
|
|
|
|
# Untyped `::`: the local's type is the yield's natural type.
|
|
basic func() i32 {
|
|
x :: {
|
|
a :: 20
|
|
b :: 22
|
|
yield a + b
|
|
}
|
|
return x
|
|
}
|
|
|
|
# Typed `T =`: the yield coerces to the annotation.
|
|
typed func() i64 {
|
|
x i64 := {
|
|
yield 100
|
|
}
|
|
return x
|
|
}
|
|
|
|
# The yielded value is captured before defers run: the defer mutates a block
|
|
# local, but the captured value is unchanged.
|
|
spill func() i32 {
|
|
v :: {
|
|
n i32 := 5
|
|
defer n = 999
|
|
yield n
|
|
}
|
|
return v # 5, not 999
|
|
}
|
|
|
|
# Reassignment into an existing mutable local.
|
|
reassign func() i32 {
|
|
r i32 := 0
|
|
r = {
|
|
yield 7
|
|
}
|
|
return r
|
|
}
|
|
|
|
# --- value if-statements (milestone 20.5) ------------------------------------
|
|
|
|
# Untyped `::` over an `else if` chain; the first branch fixes the type.
|
|
vif_untyped func(sel i32) i32 {
|
|
r :: if (sel == 0) {
|
|
yield 10
|
|
} else if (sel == 1) {
|
|
yield 20
|
|
} else {
|
|
yield 30
|
|
}
|
|
return r
|
|
}
|
|
|
|
# Typed `T =`: every branch coerces to the annotation.
|
|
vif_typed func(sel i32) i32 {
|
|
r i32 := if (sel == 0) { yield 100 } else { yield 200 }
|
|
return r
|
|
}
|
|
|
|
# Assigned into an existing local.
|
|
vif_reassign func(sel i32) i32 {
|
|
r i32 := 0
|
|
r = if (sel == 0) { yield 7 } else { yield 9 }
|
|
return r
|
|
}
|
|
|
|
# A branch is a full value block: leading statements + a defer captured before
|
|
# the yield.
|
|
vif_defer func() i32 {
|
|
r :: if (true) {
|
|
n i32 := 5
|
|
defer n = 999
|
|
yield n
|
|
} else {
|
|
yield 0
|
|
}
|
|
return r # 5
|
|
}
|
|
|
|
vif_expression func(old_entries []u8) usize {
|
|
new_size :: if (old_entries.len > 0) old_entries.len * 2 else 8
|
|
return new_size
|
|
}
|
|
|
|
# --- value loops (milestone 20.5) --------------------------------------------
|
|
|
|
# Labeled `for` used as a value: `yield :blk i` exits early with a value, the
|
|
# trailing `yield null` supplies the value when the loop completes. The `{i,
|
|
# null}` yields resolve the result to an optional.
|
|
loop_search func() i32 {
|
|
# first i in 0..10 whose square exceeds 40 (6*6=36 no, 7*7=49 yes -> 7).
|
|
idx :: for 0..10 |i| blk: {
|
|
if (i * i > 40) yield :blk i
|
|
yield null
|
|
}
|
|
if idx |found| {
|
|
if (found == 7) return 0
|
|
return 1
|
|
}
|
|
return 2
|
|
}
|
|
|
|
# Same loop, but nothing matches -> the fall-through `yield null` is the result.
|
|
loop_none func() i32 {
|
|
idx :: for 0..10 |i| blk: {
|
|
if (i > 100) yield :blk i
|
|
yield null
|
|
}
|
|
if idx |found| {
|
|
_ = found
|
|
return 1 # should be unreachable: no match
|
|
}
|
|
return 0
|
|
}
|
|
|
|
# Labeled `while` value loop (label follows the `: update` clause).
|
|
loop_while func() i32 {
|
|
n i32 := 0
|
|
found :: while n < 100 : n += 1 blk: {
|
|
if (n == 8) yield :blk n
|
|
yield null
|
|
}
|
|
if found |v| {
|
|
if (v == 8) return 0
|
|
return 1
|
|
}
|
|
return 2
|
|
}
|
|
|
|
# --- value if/loop follow-ups (milestone 20.6) -------------------------------
|
|
|
|
# A branch may exit on every path (here `return`) instead of yielding; the slot
|
|
# read after the `if` is only reached on the yielding path.
|
|
vif_return func(sel i32) i32 {
|
|
r :: if (sel == 0) {
|
|
yield 10
|
|
} else {
|
|
return 55 # exits the function instead of yielding
|
|
}
|
|
return r + 1 # only when sel == 0: 10 + 1 = 11
|
|
}
|
|
|
|
# unwrap-`if` as a value source: present -> transform, absent -> default.
|
|
vif_unwrap func(opt ?i32) i32 {
|
|
r :: if opt |v| {
|
|
yield v * 2
|
|
} else {
|
|
yield 99
|
|
}
|
|
return r
|
|
}
|
|
|
|
# The simple "unwrap or fallback" case is just `orelse` (already a plain expression).
|
|
orelse_value func(opt ?i32) i32 {
|
|
r :: opt orelse 7
|
|
return r
|
|
}
|
|
|
|
# Untyped value loop where `null` is yielded (in a labeled yield) before any
|
|
# concrete value: the element type still resolves to ?<i> from `yield :blk i`.
|
|
loop_none_first func() i32 {
|
|
r :: for 0..10 |i| blk: {
|
|
if (i > 100) yield :blk null
|
|
if (i * i > 40) yield :blk i # first concrete yield: i == 7
|
|
yield null
|
|
}
|
|
if r |found| {
|
|
if (found == 7) return 0
|
|
return 1
|
|
}
|
|
return 2
|
|
}
|
|
|
|
# --- labels: value blocks + outer-loop yield/break (milestone 20.7) -----------
|
|
|
|
# A labeled value block: `yield :blk` exits the block (past a nested `if`) with a
|
|
# value. Every path must yield.
|
|
lblock func(sel i32) i32 {
|
|
r :: blk: {
|
|
base :: 10
|
|
if (sel == 0) {
|
|
yield :blk base
|
|
} else {
|
|
yield :blk base * 2
|
|
}
|
|
}
|
|
return r
|
|
}
|
|
|
|
# An early `yield :blk` skips the rest of the block; the value is captured before
|
|
# the block's defer runs.
|
|
lblock_defer func() i32 {
|
|
r :: blk: {
|
|
n i32 := 5
|
|
defer n = 999
|
|
if (true) yield :blk n
|
|
yield :blk 0
|
|
}
|
|
return r # 5, not 999
|
|
}
|
|
|
|
# A labeled block whose `{T, null}` yields resolve the result to an optional.
|
|
lblock_optional func(present i32) i32 {
|
|
r :: blk: {
|
|
if (present == 0) yield :blk null
|
|
yield :blk 8
|
|
}
|
|
if r |v| {
|
|
return v
|
|
}
|
|
return -1
|
|
}
|
|
|
|
# `yield :outer v` exits an OUTER value loop from inside an inner loop.
|
|
yield_outer func(target i32) i32 {
|
|
found :: for 0..3 |row| outer: {
|
|
for 0..3 |col| {
|
|
if (row * 3 + col == target) yield :outer (row * 10 + col)
|
|
}
|
|
yield null
|
|
}
|
|
if found |v| {
|
|
return v
|
|
}
|
|
return -1
|
|
}
|
|
|
|
# Plain `break :outer` exits an outer loop from an inner loop.
|
|
break_outer func() i32 {
|
|
count i32 := 0
|
|
for 0..3 |a| outer: {
|
|
for 0..3 |b| {
|
|
count += 1
|
|
if (a == 1 and b == 1) break :outer
|
|
}
|
|
}
|
|
return count # 5
|
|
}
|
|
|
|
# A labeled block *statement* (not a value source): `break :blk` exits it early.
|
|
stmt_block func(early i32) i32 {
|
|
x i32 := 0
|
|
blk: {
|
|
x = 1
|
|
if (early == 1) break :blk
|
|
x = 2
|
|
}
|
|
return x # early == 1 -> 1, else 2
|
|
}
|
|
|
|
# `break :search` escapes a nested loop and the block in one jump; the block's
|
|
# defer still runs on the way out.
|
|
stmt_block_escape func() i32 {
|
|
hits i32 := 0
|
|
search: {
|
|
defer hits += 1000
|
|
for 0..10 |i| {
|
|
hits += 1
|
|
if (i == 3) break :search
|
|
}
|
|
hits += 100 # skipped by break :search
|
|
}
|
|
return hits # 4 + 1000 (defer) = 1004
|
|
}
|
|
|
|
# A labeled block can also be exited through an ordinary nested block.
|
|
stmt_block_nested func() i32 {
|
|
hits i32 := 0
|
|
outer: {
|
|
{
|
|
hits = 1
|
|
break :outer
|
|
}
|
|
hits = 100 # skipped by break :outer
|
|
}
|
|
return hits
|
|
}
|
|
|
|
# Item B: a `null` yielded before a concrete `yield :blk` that references a block local.
|
|
lblock_local func() i32 {
|
|
r :: blk: {
|
|
val :: 9
|
|
if (false) yield :blk null
|
|
yield :blk val
|
|
}
|
|
if r |v| {
|
|
return v
|
|
}
|
|
return -1
|
|
}
|
|
|
|
main func() i32 {
|
|
if (basic() != 42) return 101
|
|
if (typed() != 100) return 102
|
|
if (spill() != 5) return 103
|
|
if (reassign() != 7) return 104
|
|
|
|
if (vif_untyped(0) != 10) return 105
|
|
if (vif_untyped(1) != 20) return 106
|
|
if (vif_untyped(2) != 30) return 107
|
|
if (vif_typed(0) != 100) return 108
|
|
if (vif_typed(1) != 200) return 109
|
|
if (vif_reassign(0) != 7) return 110
|
|
if (vif_reassign(9) != 9) return 111
|
|
if (vif_defer() != 5) return 112
|
|
if (vif_expression("") != 8) return 136
|
|
if (vif_expression("abc") != 6) return 137
|
|
|
|
if (loop_search() != 0) return 113
|
|
if (loop_none() != 0) return 114
|
|
if (loop_while() != 0) return 115
|
|
|
|
if (vif_return(0) != 11) return 116
|
|
if (vif_return(1) != 55) return 117
|
|
if (vif_unwrap(21) != 42) return 118
|
|
if (vif_unwrap(null) != 99) return 119
|
|
if (orelse_value(5) != 5) return 120
|
|
if (orelse_value(null) != 7) return 121
|
|
if (loop_none_first() != 0) return 122
|
|
|
|
if (lblock(0) != 10) return 123
|
|
if (lblock(1) != 20) return 124
|
|
if (lblock_defer() != 5) return 125
|
|
if (lblock_optional(0) != -1) return 126
|
|
if (lblock_optional(1) != 8) return 127
|
|
if (yield_outer(4) != 11) return 128
|
|
if (yield_outer(99) != -1) return 129
|
|
if (break_outer() != 5) return 130
|
|
|
|
if (stmt_block(1) != 1) return 131
|
|
if (stmt_block(0) != 2) return 132
|
|
if (stmt_block_escape() != 1004) return 133
|
|
if (lblock_local() != 9) return 134
|
|
if (stmt_block_nested() != 1) return 135
|
|
|
|
return 42
|
|
}
|