plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


plum-examples/closures.plum
b7071c9 1
import std/Str
73b5e55 2
import std/Bool
ca5fd6f 3
import std/Number
b7071c9 4
0fe3528 5
fun each(cb: fn(Int) -> Int) -> Int =
6341c74 6
  cb(5)
6341c74 7
0fe3528 8
fun useCapturingClosure() -> Int =
a271f34 9
  offset := 100
a271f34 10
  cb := |v|
6341c74 11
    v + offset
6341c74 12
  cb(5)
6341c74 13
0fe3528 14
fun main() -> Int =
6341c74 15
  each(|v| v * 3) + useCapturingClosure()
a271f34 16
a43f3af 17
fun useSnapshotClosure() -> Int =
a43f3af 18
  x = 10
a43f3af 19
  snapshotCb = |v|
a43f3af 20
    x + v
a43f3af 21
  x = 999
a43f3af 22
  each(snapshotCb)
a43f3af 23
a43f3af 24
fun eachF(cb: fn(Float) -> Float) -> Float =
a43f3af 25
  cb(0.0)
a43f3af 26
a43f3af 27
fun useFloatClosure() -> Float =
a43f3af 28
  offset = 2.5
a43f3af 29
  floatCb = |v|
a43f3af 30
    offset + v
a43f3af 31
  eachF(floatCb)
a43f3af 32
5f2f962 33
enum ClosureCat =
5f2f962 34
  | ClosureCat(age: Int)
a43f3af 35
a43f3af 36
fun eachCat(cb: fn(ClosureCat) -> Int) -> Int =
a43f3af 37
  cb(ClosureCat(age: 7))
a43f3af 38
a43f3af 39
fun useClassClosure() -> Int =
a43f3af 40
  classCb = |c|
a43f3af 41
    c.age
a43f3af 42
  eachCat(classCb)
a43f3af 43
a43f3af 44
fun identity(value: T) -> T =
a43f3af 45
  value
a43f3af 46
a43f3af 47
fun eachViaGeneric(cb: fn(Int) -> Int) -> Int =
a43f3af 48
  cb(identity(7))
a43f3af 49
a43f3af 50
# `inner` (nested inside `outer`'s body) needs `offset` — a name from
a43f3af 51
# `useNestedClosure`'s scope, two levels up from `inner` itself, and not
a43f3af 52
# referenced by `outer` directly — exercising the multi-level capture chain:
a43f3af 53
# `outer` must itself capture `offset` purely because `inner` needs it.
a43f3af 54
fun useNestedClosure() -> Int =
a43f3af 55
  offset = 100
a43f3af 56
  outer = |v|
a43f3af 57
    inner = |w|
a43f3af 58
      w + offset
a43f3af 59
    inner(v)
a43f3af 60
  each(outer)
a43f3af 61
8de13fd 62
fun doubleForClosureTest(x: Int) -> Int =
8de13fd 63
  x * 2
8de13fd 64
8de13fd 65
fun classifySign(n: Int) -> Str =
8de13fd 66
  cb = |v| v > 0 ? "pos" : "nonpos"
8de13fd 67
  cb(n)
8de13fd 68
8de13fd 69
test "closures example computes correctly"
8de13fd 70
  assert main() == 120
8de13fd 71
8de13fd 72
# ---- closure regression tests ----
8de13fd 73
8de13fd 74
test "non capturing closure passed and called runs correctly"
8de13fd 75
  assert each(|v| v) == 5
8de13fd 76
8de13fd 77
# Regression test: the external scanner used to never emit a dedent for a
8de13fd 78
# multi-line closure body immediately followed by `)` on the same line as
8de13fd 79
# the body's last statement, so this shape didn't parse at all before.
8de13fd 80
test "multi line closure call argument with closing paren on last statement line runs correctly"
8de13fd 81
  multiLineResult = each(|v|
8de13fd 82
    bumped = v + 1
8de13fd 83
    bumped * 2)
8de13fd 84
  assert multiLineResult == 12
8de13fd 85
8de13fd 86
test "capturing closure snapshots value at creation time runs correctly"
8de13fd 87
  # each calls its callback with 5, so a correct snapshot of x==10 (its value
8de13fd 88
  # when the closure was created, not 999 — its value when `each(cb)` is
8de13fd 89
  # actually called) gives 10 + 5 = 15.
8de13fd 90
  assert useSnapshotClosure() == 15
8de13fd 91
8de13fd 92
test "closure assigned then called at float type runs correctly"
8de13fd 93
  assert useFloatClosure() == 2.5
8de13fd 94
8de13fd 95
test "closure assigned then called at class type runs correctly"
8de13fd 96
  assert useClassClosure() == 7
8de13fd 97
8de13fd 98
test "closure passed through already generic higher order function runs correctly"
8de13fd 99
  assert eachViaGeneric(|v| v * 2) == 14
8de13fd 100
a43f3af 101
test "nested closure literal runs correctly"
a9a0147 102
  assert useNestedClosure() == 105
a43f3af 103
a43f3af 104
test "nested closure literal passed directly as call argument runs correctly"
a9a0147 105
  assert each(|v|
a43f3af 106
    each(|w| w + v)) == 10
a43f3af 107
a43f3af 108
test "named function used as a closure typed value runs correctly"
a9a0147 109
  assert each(doubleForClosureTest) == 10
a43f3af 110
a43f3af 111
test "named function used as a value assigned then called runs correctly"
a43f3af 112
  f = doubleForClosureTest
a9a0147 113
  assert f(21) == 42
a43f3af 114
a43f3af 115
test "named function used as a value alongside a closure at the same call type runs correctly"
a43f3af 116
  # Proves the trampoline shares the same call_indirect type as an ordinary
a43f3af 117
  # closure of the same signature (both must resolve to the same wasm
a43f3af 118
  # function type, since both flow through the exact same `cb(...)` call site).
a9a0147 119
  assert each(doubleForClosureTest) + each(|v| v + 1) == 16
04d2270 120
04d2270 121
test "closure with an inline ternary body runs correctly"
04d2270 122
  # A ternary as a closure's own inline body used to parse as an OUTER
04d2270 123
  # ternary wrapping the whole closure instead (`(|v| v > 0) ? "pos" :
04d2270 124
  # "nonpos"`), a grammar precedence bug — see the closure/PREC.conditional
04d2270 125
  # fix in tooling/tree-sitter-plum/grammar.js.
a9a0147 126
  assert classifySign(5) == "pos"
a9a0147 127
  assert classifySign(-3) == "nonpos"