diff options
| author | Laurenz <laurmaedje@gmail.com> | 2024-04-13 10:39:45 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-04-13 08:39:45 +0000 |
| commit | 020294fca9a7065d4b9cf4e677f606ebaaa29b00 (patch) | |
| tree | c0027ad22046e2726c22298461327823d6b88d53 /tests/suite/scripting/closure.typ | |
| parent | 72dd79210602ecc799726fc096b078afbb47f299 (diff) | |
Better test runner (#3922)
Diffstat (limited to 'tests/suite/scripting/closure.typ')
| -rw-r--r-- | tests/suite/scripting/closure.typ | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/tests/suite/scripting/closure.typ b/tests/suite/scripting/closure.typ new file mode 100644 index 00000000..e3677d33 --- /dev/null +++ b/tests/suite/scripting/closure.typ @@ -0,0 +1,223 @@ +// Test closures. + +--- closure-without-params-non-atomic --- +// Don't parse closure directly in content. + +#let x = "x" + +// Should output `x => y`. +#x => y + +--- closure-without-captures --- +// Basic closure without captures. +#{ + let adder = (x, y) => x + y + test(adder(2, 3), 5) +} + +--- closure-as-arg --- +// Pass closure as argument and return closure. +// Also uses shorthand syntax for a single argument. +#{ + let chain = (f, g) => (x) => f(g(x)) + let f = x => x + 1 + let g = x => 2 * x + let h = chain(f, g) + test(h(2), 5) +} + +--- closure-capture-from-popped-stack-frame --- +// Capture environment. +#{ + let mark = "!" + let greet = { + let hi = "Hi" + name => { + hi + ", " + name + mark + } + } + + test(greet("Typst"), "Hi, Typst!") + + // Changing the captured variable after the closure definition has no effect. + mark = "?" + test(greet("Typst"), "Hi, Typst!") +} + +--- closure-shadows-outer-var --- +// Redefined variable. +#{ + let x = 1 + let f() = { + let x = x + 2 + x + } + test(f(), 3) +} + +--- closure-shadows-outer-var-import --- +// Import bindings. +#{ + let b = "module.typ" + let f() = { + import b: b + b + } + test(f(), 1) +} + +--- closure-shadows-outer-var-for-loop --- +// For loop bindings. +#{ + let v = (1, 2, 3) + let f() = { + let s = 0 + for v in v { s += v } + s + } + test(f(), 6) +} + +--- closure-let-basic --- +// Let + closure bindings. +#{ + let g = "hi" + let f() = { + let g() = "bye" + g() + } + test(f(), "bye") +} + +--- closure-let-args --- +// Parameter bindings. +#{ + let x = 5 + let g() = { + let f(x, y: x) = x + y + f + } + + test(g()(8), 13) +} + +--- closure-bad-capture --- +// Don't leak environment. +#{ + // Error: 16-17 unknown variable: x + let func() = x + let x = "hi" + func() +} + +--- closure-missing-arg-positional --- +// Too few arguments. +#{ + let types(x, y) = "[" + str(type(x)) + ", " + str(type(y)) + "]" + test(types(14%, 12pt), "[ratio, length]") + + // Error: 8-21 missing argument: y + test(types("nope"), "[string, none]") +} + +--- closure-too-many-args-positional --- +// Too many arguments. +#{ + let f(x) = x + 1 + + // Error: 8-13 unexpected argument + f(1, "two", () => x) +} + +--- closure-capture-in-lvalue --- +// Mutable method with capture in argument. +#let x = "b" +#let f() = { + let a = (b: 5) + a.at(x) = 10 + a +} +#f() + +--- closure-capture-mutate --- +#let x = () +#let f() = { + // Error: 3-4 variables from outside the function are read-only and cannot be modified + x.at(1) = 2 +} +#f() + +--- closure-named-args-basic --- +// Named arguments. +#{ + let greet(name, birthday: false) = { + if birthday { "Happy Birthday, " } else { "Hey, " } + name + "!" + } + + test(greet("Typst"), "Hey, Typst!") + test(greet("Typst", birthday: true), "Happy Birthday, Typst!") + + // Error: 23-35 unexpected argument: whatever + test(greet("Typst", whatever: 10)) +} + +--- closure-args-sink --- +// Parameter unpacking. +#let f((a, b), ..c) = (a, b, c) +#test(f((1, 2), 3, 4), (1, 2, (3, 4))) + +#let f((k: a, b), c: 3, (d,)) = (a, b, c, d) +#test(f((k: 1, b: 2), (4,)), (1, 2, 3, 4)) + +// Error: 8-14 expected identifier, found destructuring pattern +#let f((a, b): 0) = none + +// Error: 10-19 expected pattern, found array +#let f(..(a, b: c)) = none + +// Error: 10-16 expected pattern, found array +#let f(..(a, b)) = none + +--- closure-param-duplicate-positional --- +// Error: 11-12 duplicate parameter: x +#let f(x, x) = none + +--- closure-body-multiple-expressions --- +// Error: 21 expected comma +// Error: 22-23 expected pattern, found integer +// Error: 24-25 unexpected plus +// Error: 26-27 expected pattern, found integer +#let f = (x: () => 1 2 + 3) => 4 + +--- closure-param-duplicate-mixed --- +// Error: 14-15 duplicate parameter: a +// Error: 23-24 duplicate parameter: b +// Error: 35-36 duplicate parameter: b +#let f(a, b, a: none, b: none, c, b) = none + +--- closure-param-duplicate-spread --- +// Error: 13-14 duplicate parameter: a +#let f(a, ..a) = none + +--- closure-pattern-bad-string --- +// Error: 7-14 expected pattern, found string +#((a, "named": b) => none) + +--- closure-let-pattern-bad-string --- +// Error: 10-15 expected pattern, found string +#let foo("key": b) = key + +--- closure-param-keyword --- +// Error: 10-14 expected pattern, found `none` +// Hint: 10-14 keyword `none` is not allowed as an identifier; try `none_` instead +#let foo(none: b) = key + +--- closure-param-named-underscore --- +// Error: 10-11 expected identifier, found underscore +#let foo(_: 3) = none + +--- issue-non-atomic-closure --- +// Ensure that we can't have non-atomic closures. +#let x = 1 +#let c = [#(x) => (1, 2)] +#test(c.children.last(), [(1, 2)])) |
