diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Compiler.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Compiler.scala index fc01cde54a..9e767fd48b 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Compiler.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ups/Compiler.scala @@ -24,40 +24,45 @@ class Compiler(using Context)(using tl: TL)(using Ctx, State, Raise) extends Ter import Pattern.* /** A previously-computed matcher result for one field of the current - * multi-matcher. The runtime representation is shape-dependent: - * singleton-label matchers return the label's value directly, while - * multi-label matchers return a record keyed by label field names. + * multi-matcher. In full mode the value also carries the original field + * input, which is needed when a successful field pattern preserves its + * scrutinee. Match-only mode never consumes that input, so it stores the + * submatcher result directly. */ private final case class MatcherResult(symbol: VarSymbol, labels: Set[Label]): - private def result: Term = sel(symbol.safeRef, "result") - def input: Term = sel(symbol.safeRef, "input") + private def result(using ResultMode): Term = + if isMatchOnly then symbol.safeRef else sel(symbol.safeRef, "result") + def input(using ResultMode): Term = + softAssert(!isMatchOnly, + "Match-only field matcher results should not expose their input.") + sel(symbol.safeRef, "input") /** Read the result for one label from this matcher result, abstracting over * the singleton direct-return optimization. */ - def select(label: Label): Term = + def select(label: Label)(using ResultMode): Term = if labels.size is 1 then result else sel(result, label.asFieldName) /** Produce the default failure value for this matcher result with the same * shape that a successful submatcher call would have produced. */ - def default(using ResultMode): Term = - val result = labels.toList match - case label :: Nil => emptyMatchResult("empty") - case labels => - Rcd(false, labels.map: label => - RcdField(str(label.asFieldName), emptyMatchResult("empty"))) - rcd( - RcdField(str("input"), `null`), - RcdField(str("result"), result) - ) - + def default(using ResultMode): Term = matcherResult(`null`, labels.toList match + case label :: Nil => emptyMatchResult("empty") + case labels => + Rcd(false, labels.map: label => + RcdField(str(label.asFieldName), emptyMatchResult("empty")))) + + /** Make a match result record containing `input` and `result` fields. */ + private def matcherResult(input: => Term, result: => Term)(using ResultMode): Term = + if isMatchOnly then result + else rcd(RcdField(str("input"), input), RcdField(str("result"), result)) + private def bool(value: Bool): Term = Term.Lit(BoolLit(value)) - + private def isMatchOnly(using mode: ResultMode): Bool = mode is ResultMode.MatchOnly - + private def emptyMatchResult(reason: Str)(using mode: ResultMode): Term = if isMatchOnly then bool(false) else makeMatchFailure(str(reason)) - + private def nullifyEmptyBindings(bindings: Term): Term = bindings match case Rcd(false, Nil) => `null` case bindings => bindings @@ -81,11 +86,11 @@ class Compiler(using Context)(using tl: TL)(using Ctx, State, Raise) extends Ter extension (head: Head) /** Create a flat pattern that can be used in the UCS expressions. */ - def toFlatPattern: FlatPattern = head match + def toFlatPattern(arguments: Opt[Ls[(LocalVarSymbol, Opt[Loc])]]): FlatPattern = head match case lit: syntax.Literal => FlatPattern.Lit(lit) case sym: (ClassSymbol | ModuleOrObjectSymbol) => val constructor = reference(sym, head.toLoc).getOrElse(Term.Error().withLocOf(head)) - FlatPattern.ClassLike(constructor, sym, N, false)(Tree.Dummy) + FlatPattern.ClassLike(constructor, sym, arguments, false)(Tree.Dummy) def showDbg: Str = head match case lit: syntax.Literal => lit.idStr case sym: ClassLikeSymbol => sym.nme @@ -150,15 +155,25 @@ class Compiler(using Context)(using tl: TL)(using Ctx, State, Raise) extends Ter val scrutinee = VarSymbol(Ident("input")) // Assemble branches for constructors and literals. val branches = heads.map: head => - // Weird. Removing type annotations caused type errors. val specialized = expandedPatterns.specializeSet(S(head)) - val consequent = Split.Else(multiMatcherBranch(specialized, scrutinee)) - Branch(scrutinee.safeRef, head.toFlatPattern, consequent) + lazy val empty = (N: Opt[Ls[(LocalVarSymbol, Option[Loc])]], Map.empty[Ident | Int, LocalVarSymbol]) + val (classFieldArguments, classFields) = head match + case symbol: ClassSymbol => symbol.defn.getOrElse(lastWords(s"Missing definition for symbol `${symbol.nme}`.")).paramsOpt match + case N => empty + case S(params) => + val empty: (Ls[(LocalVarSymbol, Opt[Loc])], Map[Ident | Int, LocalVarSymbol]) = (Nil, Map.empty) + val (arguments, fields) = params.params.foldLeft(empty): (acc, param) => + val (argAcc, fieldAcc) = acc + val fieldSymbol = TempSymbol(N, param.sym.nme) + (argAcc :+ (fieldSymbol, param.toLoc), fieldAcc + ((param.sym.id: Ident | Int) -> fieldSymbol)) + (S(arguments), fields) + case _: (syntax.Literal | ModuleOrObjectSymbol) => empty + val consequent = Split.Else(multiMatcherBranch(specialized, scrutinee, classFields)) + Branch(scrutinee.safeRef, head.toFlatPattern(classFieldArguments), consequent) // Assemble the default branch. val default = - // Weird. Removing type annotations caused type errors. val specialized = expandedPatterns.specializeSet(N) - Split.Else(multiMatcherBranch(specialized, scrutinee)) + Split.Else(multiMatcherBranch(specialized, scrutinee, Map.empty)) // Make a split that tries all branches in order. val topmostSplit = branches.foldRight(default)(_ ~: _) val bodyTerm = SynthIf(topmostSplit) @@ -167,7 +182,8 @@ class Compiler(using Context)(using tl: TL)(using Ctx, State, Raise) extends Ter def multiMatcherBranch( patterns: Set[(Label, SpPat)], - scrutinee: LocalVarSymbol + scrutinee: LocalVarSymbol, + knownFields: Map[Ident | Int, LocalVarSymbol] )(using ResultMode): Blk = trace( pre = s"multiMatcherBranch: scrutinee = ${scrutinee} | patterns = ${ patterns.iterator.map: (label, pattern) => @@ -186,20 +202,21 @@ class Compiler(using Context)(using tl: TL)(using Ctx, State, Raise) extends Ter log(s"subPattern for field ${field.showDbg}: ${ subPatterns.iterator.map(_.showDbg).mkString("{", ", ", "}")}") val subMatcherSymbol = buildMultiMatcher(subPatterns) - val conditional = - // Check the presence of the field, and call the matcher if it exists. - val fieldIdent: Ident = field.asIdent - val fieldSymbol = TempSymbol(N, fieldIdent.name) - val fieldTest = FlatPattern.Record((fieldIdent -> fieldSymbol) :: Nil) - val consequent = Split.Else: - val resultTerm = app(subMatcherSymbol.safeRef, tup(fld(fieldSymbol.safeRef)), "result") - rcd( - RcdField(str("input"), fieldSymbol.safeRef), - RcdField(str("result"), resultTerm) - ) - val branch = Branch(scrutinee.safeRef, fieldTest, consequent) - SynthIf(branch ~: Split.Else(subScrutinee.default)) - LetDecl(subScrutinee.symbol, Nil) :: DefineVar(subScrutinee.symbol, conditional) :: Nil + /** Shorthands for the result of the matcher branch. */ + def makeResult(fieldSymbol: LocalVarSymbol) = matcherResult( + fieldSymbol.safeRef, + app(subMatcherSymbol.safeRef, tup(fld(fieldSymbol.safeRef)), "result")) + val result = knownFields.get(field) match + case S(fieldSymbol) => makeResult(fieldSymbol) + case N => + // Check the presence of the field, and call the matcher if it exists. + val fieldIdent: Ident = field.asIdent + val fieldSymbol = TempSymbol(N, fieldIdent.name) + val fieldTest = FlatPattern.Record((fieldIdent -> fieldSymbol) :: Nil) + val consequent = Split.Else(makeResult(fieldSymbol)) + val branch = Branch(scrutinee.safeRef, fieldTest, consequent) + SynthIf(branch ~: Split.Else(subScrutinee.default)) + LetDecl(subScrutinee.symbol, Nil) :: DefineVar(subScrutinee.symbol, result) :: Nil .toList // For each pattern, we compile a split and bind the result to a variable. // The variable will be a field of the output record. diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/CompiledClassPatterns.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/CompiledClassPatterns.mls new file mode 100644 index 0000000000..a22702f95c --- /dev/null +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/CompiledClassPatterns.mls @@ -0,0 +1,161 @@ +:js + +open annotations + +data class Add(val lhs, val rhs) + +:sir +case Add(0, 0) then 1 +//│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— +//│ let lambda; +//│ @private +//│ define lambda as fun lambda⁰(caseScrut) { +//│ let arg$Add$0$, arg$Add$1$; +//│ match caseScrut +//│ Add⁰ => +//│ set arg$Add$0$ = caseScrut.lhs⁰; +//│ set arg$Add$1$ = caseScrut.rhs⁰; +//│ match arg$Add$0$ +//│ 0 => +//│ match arg$Add$1$ +//│ 0 => +//│ return 1 +//│ else +//│ throw new globalThis⁰.Error⁰("match error") +//│ end +//│ else +//│ throw new globalThis⁰.Error⁰("match error") +//│ end +//│ else +//│ throw new globalThis⁰.Error⁰("match error") +//│ end +//│ }; +//│ return lambda⁰ +//│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— +//│ = fun + +:sir +case @compile Add(0, 0) then 1 +//│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— +//│ let lambda; +//│ @private +//│ define lambda as fun lambda³(caseScrut) { +//│ let matcher__Addʿ0ꓹ0ʾ$, matcher__0$, matchSuccess, lambda1, lambda2; +//│ define lambda1 as fun lambda¹(input) { +//│ let p_1$, p_1$1, tmp, tmp1; +//│ match input +//│ 0 => +//│ set tmp = true; +//│ set p_1$ = tmp; +//│ return p_1$ +//│ else +//│ set tmp1 = false; +//│ set p_1$1 = tmp1; +//│ return p_1$1 +//│ end +//│ }; +//│ set matcher__0$ = lambda¹; +//│ define lambda2 as fun lambda²(input) { +//│ let lhs, rhs, lhs1, rhs1, p_0$, result1$, result1$1, p_0$1, tmp, tmp1; +//│ match input +//│ Add⁰ => +//│ set lhs = input.lhs⁰; +//│ set rhs = input.rhs⁰; +//│ set lhs1 = matcher__0$(lhs); +//│ set rhs1 = matcher__0$(rhs); +//│ set result1$1 = lhs1; +//│ match result1$1 +//│ true => +//│ set result1$ = rhs1; +//│ match result1$ +//│ true => +//│ set tmp = true; +//│ end +//│ else +//│ set tmp = false; +//│ end +//│ end +//│ else +//│ set tmp = false; +//│ end +//│ set p_0$ = tmp; +//│ return p_0$ +//│ else +//│ set tmp1 = false; +//│ set p_0$1 = tmp1; +//│ return p_0$1 +//│ end +//│ }; +//│ set matcher__Addʿ0ꓹ0ʾ$ = lambda²; +//│ set matchSuccess = matcher__Addʿ0ꓹ0ʾ$(caseScrut); +//│ match matchSuccess +//│ true => +//│ return 1 +//│ else +//│ throw new globalThis⁰.Error⁰("match error") +//│ end +//│ }; +//│ return lambda³ +//│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— +//│ = fun + +data class Box(val value) + +// * TODO: We should find a way of optimizing away the local intermediate records in cases like this. +:soir +case @compile (Box(0) | Box(1)) then 1 +//│ ——————————————| Optimized IR |—————————————————————————————————————————————————————————————————————— +//│ let lambda; +//│ @private +//│ define lambda as fun lambda⁴(caseScrut) { +//│ let value, result1$, result2$; +//│ match caseScrut +//│ Box⁰ => +//│ let inlinedVal; +//│ set value = caseScrut.value⁰; +//│ match value +//│ 0 => +//│ set inlinedVal = { "p_1": true, "p_2": false }; +//│ end +//│ 1 => +//│ set inlinedVal = { "p_1": false, "p_2": true }; +//│ end +//│ else +//│ set inlinedVal = { "p_1": false, "p_2": false }; +//│ end +//│ set result1$ = inlinedVal.p_1﹖; +//│ match result1$ +//│ true => +//│ return 1 +//│ else +//│ set result2$ = inlinedVal.p_2﹖; +//│ match result2$ +//│ true => +//│ return 1 +//│ else +//│ throw new globalThis⁰.Error⁰("match error") +//│ end +//│ end +//│ else +//│ throw new globalThis⁰.Error⁰("match error") +//│ end +//│ }; +//│ return lambda⁴ +//│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— +//│ = fun + + +case @compile (Box(0) & Box(0 | 1)) then 1 +//│ = fun + +class Add2(val x, val y, z) + +:e +case @compile (Add2(0, _, _) | Add2(_, 1, _)) then 0 +//│ ╔══[COMPILATION ERROR] Parameter `z` is not accessible. +//│ ║ l.151: class Add2(val x, val y, z) +//│ ╙── ^ +//│ ╔══[COMPILATION ERROR] Parameter `z` is not accessible. +//│ ║ l.151: class Add2(val x, val y, z) +//│ ╙── ^ +//│ = fun diff --git a/hkmc2/shared/src/test/mlscript/ups/SimpleTransform.mls b/hkmc2/shared/src/test/mlscript/ups/SimpleTransform.mls index 401bcece78..765cf8bdc5 100644 --- a/hkmc2/shared/src/test/mlscript/ups/SimpleTransform.mls +++ b/hkmc2/shared/src/test/mlscript/ups/SimpleTransform.mls @@ -187,7 +187,7 @@ Infix("**", Literal(1), Literal(2)) is EvaluatedTerm fun evaluate(term) = if term is (@compile EvaluatedTerm) as v then Some(v) else None -//│ Lines of IR: 3624 +//│ Lines of IR: 3136 evaluate of Literal(1) diff --git a/hkmc2/shared/src/test/mlscript/ups/fixpoint/SimpleExample.mls b/hkmc2/shared/src/test/mlscript/ups/fixpoint/SimpleExample.mls index f09d1d4a6d..22ae1d422d 100644 --- a/hkmc2/shared/src/test/mlscript/ups/fixpoint/SimpleExample.mls +++ b/hkmc2/shared/src/test/mlscript/ups/fixpoint/SimpleExample.mls @@ -21,62 +21,34 @@ pattern Steps = @compile (Step as Steps | _) //│ method unapply⁰ = fun unapply¹(input) { //│ let output, inlinedVal, matcher0$, matcher1$, matcher2$, matcher3$, matcher4$, matcher__Lit$, mode, focus, stack, finalResult, lambda, lambda1, lambda2, lambda3, lambda4, lambda5; //│ define lambda as fun lambda⁰(input1) { -//│ let lhs, rhs, result2$, result1$, bindings, bindings1, tmp, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8; +//│ let lhs, rhs, lhs1, rhs1, result2$, result1$, bindings, bindings1, tmp, tmp1, tmp2; //│ match input1 //│ Add⁰ => -//│ match input1 -//│ Object⁰ => -//│ match input1 -//│ { lhs } => -//│ set lhs = input1.lhs﹖; -//│ set tmp = matcher1$(lhs); -//│ set tmp1 = { "input": lhs, "result": tmp }; -//│ end -//│ else -//│ set tmp2 = new runtime⁰.MatchFailure⁰("empty"); -//│ set tmp1 = { "input": null, "result": tmp2 }; -//│ end -//│ end -//│ else -//│ set tmp3 = new runtime⁰.MatchFailure⁰("empty"); -//│ set tmp1 = { "input": null, "result": tmp3 }; -//│ end -//│ match input1 -//│ Object⁰ => -//│ match input1 -//│ { rhs } => -//│ set rhs = input1.rhs﹖; -//│ set tmp4 = matcher2$(rhs); -//│ set tmp5 = { "input": rhs, "result": tmp4 }; -//│ end -//│ else -//│ set tmp6 = new runtime⁰.MatchFailure⁰("empty"); -//│ set tmp5 = { "input": null, "result": tmp6 }; -//│ end -//│ end -//│ else -//│ set tmp7 = new runtime⁰.MatchFailure⁰("empty"); -//│ set tmp5 = { "input": null, "result": tmp7 }; -//│ end -//│ set result1$ = tmp1.result﹖; +//│ set lhs = input1.lhs⁰; +//│ set rhs = input1.rhs⁰; +//│ set tmp = matcher1$(lhs); +//│ set lhs1 = { "input": lhs, "result": tmp }; +//│ set tmp1 = matcher2$(rhs); +//│ set rhs1 = { "input": rhs, "result": tmp1 }; +//│ set result1$ = lhs1.result﹖; //│ match result1$ //│ MatchSuccess⁰ => //│ set bindings = result1$.bindings⁰; -//│ set result2$ = tmp5.result﹖; +//│ set result2$ = rhs1.result﹖; //│ match result2$ //│ MatchSuccess⁰ => -//│ let inlinedVal1, a, b, c, tmp9, tmp10, tmp11, tmp12, tmp13; +//│ let inlinedVal1, a, b, c, tmp3, tmp4, tmp5, tmp6, tmp7; //│ set bindings1 = result2$.bindings⁰; -//│ set tmp8 = { ...bindings, ...bindings1 }; -//│ set a = tmp8.a﹖; -//│ set b = tmp8.b﹖; +//│ set tmp2 = { ...bindings, ...bindings1 }; +//│ set a = tmp2.a﹖; +//│ set b = tmp2.b﹖; //│ set c = +⁰(a, b); -//│ set tmp9 = +⁰("reduce ", a); -//│ set tmp10 = +⁰(tmp9, " + "); -//│ set tmp11 = +⁰(tmp10, b); -//│ set tmp12 = +⁰(tmp11, " = "); -//│ set tmp13 = +⁰(tmp12, c); -//│ do Predef⁰.print⁰(tmp13); +//│ set tmp3 = +⁰("reduce ", a); +//│ set tmp4 = +⁰(tmp3, " + "); +//│ set tmp5 = +⁰(tmp4, b); +//│ set tmp6 = +⁰(tmp5, " = "); +//│ set tmp7 = +⁰(tmp6, c); +//│ do Predef⁰.print⁰(tmp7); //│ set inlinedVal1 = Lit⁰(c); //│ return new runtime⁰.MatchSuccess⁰(inlinedVal1, null) //│ else @@ -105,34 +77,20 @@ pattern Steps = @compile (Step as Steps | _) //│ }; //│ set matcher3$ = lambda²; //│ define lambda1 as fun lambda³(input1) { -//│ let value, result3$, output3$, fieldBindings, bindings, tmp, tmp1, tmp2, tmp3, tmp4; +//│ let value, value1, result3$, output3$, fieldBindings, bindings, tmp, tmp1; //│ match input1 //│ Lit¹ => -//│ match input1 -//│ Object⁰ => -//│ match input1 -//│ { value } => -//│ set value = input1.value﹖; -//│ set tmp = matcher3$(value); -//│ set tmp1 = { "input": value, "result": tmp }; -//│ end -//│ else -//│ set tmp2 = new runtime⁰.MatchFailure⁰("empty"); -//│ set tmp1 = { "input": null, "result": tmp2 }; -//│ end -//│ end -//│ else -//│ set tmp3 = new runtime⁰.MatchFailure⁰("empty"); -//│ set tmp1 = { "input": null, "result": tmp3 }; -//│ end -//│ set result3$ = tmp1.result﹖; +//│ set value = input1.value⁰; +//│ set tmp = matcher3$(value); +//│ set value1 = { "input": value, "result": tmp }; +//│ set result3$ = value1.result﹖; //│ match result3$ //│ MatchSuccess⁰ => //│ set output3$ = result3$.output⁰; //│ set bindings = result3$.bindings⁰; //│ set fieldBindings = { "a": output3$ }; -//│ set tmp4 = { ...fieldBindings, ...bindings }; -//│ return new runtime⁰.MatchSuccess⁰(input1, tmp4) +//│ set tmp1 = { ...fieldBindings, ...bindings }; +//│ return new runtime⁰.MatchSuccess⁰(input1, tmp1) //│ else //│ return new runtime⁰.MatchFailure⁰("topmost") //│ end @@ -142,34 +100,20 @@ pattern Steps = @compile (Step as Steps | _) //│ }; //│ set matcher1$ = lambda³; //│ define lambda2 as fun lambda⁴(input1) { -//│ let value, result4$, output4$, fieldBindings, bindings, tmp, tmp1, tmp2, tmp3, tmp4; +//│ let value, value1, result4$, output4$, fieldBindings, bindings, tmp, tmp1; //│ match input1 //│ Lit¹ => -//│ match input1 -//│ Object⁰ => -//│ match input1 -//│ { value } => -//│ set value = input1.value﹖; -//│ set tmp = matcher4$(value); -//│ set tmp1 = { "input": value, "result": tmp }; -//│ end -//│ else -//│ set tmp2 = new runtime⁰.MatchFailure⁰("empty"); -//│ set tmp1 = { "input": null, "result": tmp2 }; -//│ end -//│ end -//│ else -//│ set tmp3 = new runtime⁰.MatchFailure⁰("empty"); -//│ set tmp1 = { "input": null, "result": tmp3 }; -//│ end -//│ set result4$ = tmp1.result﹖; +//│ set value = input1.value⁰; +//│ set tmp = matcher4$(value); +//│ set value1 = { "input": value, "result": tmp }; +//│ set result4$ = value1.result﹖; //│ match result4$ //│ MatchSuccess⁰ => //│ set output4$ = result4$.output⁰; //│ set bindings = result4$.bindings⁰; //│ set fieldBindings = { "b": output4$ }; -//│ set tmp4 = { ...fieldBindings, ...bindings }; -//│ return new runtime⁰.MatchSuccess⁰(input1, tmp4) +//│ set tmp1 = { ...fieldBindings, ...bindings }; +//│ return new runtime⁰.MatchSuccess⁰(input1, tmp1) //│ else //│ return new runtime⁰.MatchFailure⁰("topmost") //│ end diff --git a/hkmc2/shared/src/test/mlscript/ups/recursion/NatBox.mls b/hkmc2/shared/src/test/mlscript/ups/recursion/NatBox.mls index 63da25d47d..25a2c59916 100644 --- a/hkmc2/shared/src/test/mlscript/ups/recursion/NatBox.mls +++ b/hkmc2/shared/src/test/mlscript/ups/recursion/NatBox.mls @@ -18,62 +18,8 @@ fun int(n) = if n is int(nat(42)) //│ = 42 -// FIXME: why are we checking `Object` and `{ value }` patterns? -:sir :expect true nat(0) is @compile NatBox -//│ ———————————————| Lowered IR |——————————————————————————————————————————————————————————————————————— -//│ let scrut, matcher__NatBox$, matchSuccess, lambda; -//│ set scrut = nat⁰(0); -//│ define lambda as fun lambda⁰(input) { -//│ let p_0$, value, value1, p_0$1, result0$, p_0$2, tmp, tmp1, tmp2, tmp3, tmp4; -//│ match input -//│ null => -//│ set tmp = true; -//│ set p_0$ = tmp; -//│ return p_0$ -//│ Box⁰ => -//│ match input -//│ Object⁰ => -//│ match input -//│ { value } => -//│ set value1 = input.value﹖; -//│ set tmp1 = matcher__NatBox$(value1); -//│ set tmp2 = { "input": value1, "result": tmp1 }; -//│ end -//│ else -//│ set tmp2 = { "input": null, "result": false }; -//│ end -//│ end -//│ else -//│ set tmp2 = { "input": null, "result": false }; -//│ end -//│ set value = tmp2; -//│ set result0$ = value.result﹖; -//│ match result0$ -//│ true => -//│ set tmp3 = true; -//│ end -//│ else -//│ set tmp3 = false; -//│ end -//│ set p_0$1 = tmp3; -//│ return p_0$1 -//│ else -//│ set tmp4 = false; -//│ set p_0$2 = tmp4; -//│ return p_0$2 -//│ end -//│ }; -//│ set matcher__NatBox$ = lambda⁰; -//│ set matchSuccess = matcher__NatBox$(scrut); -//│ match matchSuccess -//│ true => -//│ return true -//│ else -//│ return false -//│ end -//│ —————————————————| Output |————————————————————————————————————————————————————————————————————————— //│ = true :expect true