diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/FirstClassFunctionTransformer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/FirstClassFunctionTransformer.scala index aad8e08208..c07cb11474 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/FirstClassFunctionTransformer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/FirstClassFunctionTransformer.scala @@ -38,7 +38,7 @@ class FirstClassFunctionTransformer(using Elaborator.State, Elaborator.Ctx, Rais private def getParamList(ts: TermSymbol): Option[ParamList] = ts.defn.flatMap(_.params.headOption) override def applyPath(p: Path)(k: Path => Block): Block = p match - case ref @ Value.Ref(l: BlockMemberSymbol, disamb) => l.tsym match + case ref @ Value.Ref(l: BlockMemberSymbol, disamb) => disamb match case Some(s: TermSymbol) if s.k is syntax.Fun => val params = getParamList(l).getOrElse(lastWords(s"Cannot get ${l.nme}'s parameter list.")) val clsDef = generateFCFunctionClass(ref, params) @@ -46,18 +46,7 @@ class FirstClassFunctionTransformer(using Elaborator.State, Elaborator.Ctx, Rais val cls = Value.Ref(clsDef.sym, Some(clsDef.isym)) Scoped(Set(clsDef.sym, tmp), Define(clsDef, Assign(tmp, Instantiate(false, cls, Nil), k(Value.Ref(tmp, None))))) case Some(_) => k(p) - case None => disamb match - case Some(t: TermSymbol) if t.k is syntax.Fun => - val params = getParamList(l).getOrElse(lastWords(s"Cannot get ${t.nme}'s parameter list.")) - val clsDef = generateFCFunctionClass(ref, params) - val tmp = new TempSymbol(None) - val cls = Value.Ref(clsDef.sym, Some(clsDef.isym)) - Scoped(Set(clsDef.sym, tmp), Define(clsDef, Assign(tmp, Instantiate(false, cls, Nil), k(Value.Ref(tmp, None))))) - case Some(_) => k(p) - case _ => - raise(ErrorReport(msg"Cannot determine if ${l.nme} is a function." -> ref.toLoc :: Nil, - source = Diagnostic.Source.Compilation)) - k(p) + case None => lastWords(s"${l.nme}'s disamb cannot be empty.") case sel: Select => sel.symbol match case Some(s: TermSymbol) if (s.k is syntax.Fun) => val params = getParamList(s).getOrElse(lastWords(s"Cannot get ${s.nme}'s parameter list.")) @@ -72,6 +61,12 @@ class FirstClassFunctionTransformer(using Elaborator.State, Elaborator.Ctx, Rais k(p) case _ => k(p) + private def pathStartsWith(p: Path, symbol: Local): Bool = p match + case Value.Ref(l, _) => l is symbol + case Select(p, _) => pathStartsWith(p, symbol) + case DynSelect(p, _, _) => pathStartsWith(p, symbol) + case _ => false + override def applyResult(r: Result)(k: Result => Block): Block = r match case c @ Call(fun, args) => applyArgs(args): args2 => def call(f: Path) = Call(f, args2)(c.isMlsFun, c.mayRaiseEffects, c.explicitTailCall) @@ -85,7 +80,7 @@ class FirstClassFunctionTransformer(using Elaborator.State, Elaborator.Ctx, Rais else k(call(sel.selSN("call"))) case _ => raise(ErrorReport(msg"Cannot determine if ${sel.name.name} is a function object." -> fun.toLoc :: Nil, - source = Diagnostic.Source.Compilation)) + source = Diagnostic.Source.Compilation)) k(call(fun)) case s: DynSelect => raise(ErrorReport(msg"Cannot determine if the dynamic selection is a function object." -> s.toLoc :: Nil, diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala index b746859612..44527f8d5a 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala @@ -134,7 +134,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx): ) lazy val unreachableFn = - Select(Value.Ref(State.runtimeSymbol), Tree.Ident("unreachable"))(N) + Select(Value.Ref(State.runtimeSymbol), Tree.Ident("unreachable"))(S(State.unreachableSymbol)) def unit: Path = Select(Value.Ref(State.runtimeSymbol), Tree.Ident("Unit"))(S(State.unitSymbol)) diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala index 406f36b8a3..5e136ee2d9 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala @@ -158,6 +158,7 @@ object Elaborator: val Str = assumeBuiltinCls("Str") val BigInt = assumeBuiltinCls("BigInt") val Function = assumeBuiltinCls("Function") + val Error = assumeBuiltinCls("Error") val Bool = assumeBuiltinCls("Bool") val Object = assumeBuiltinCls("Object") val Array = assumeBuiltinCls("Array") @@ -257,6 +258,8 @@ object Elaborator: val globalThisSymbol = TopLevelSymbol("globalThis") val unitSymbol = ModuleOrObjectSymbol(DummyTypeDef(syntax.Obj), Ident("Unit")) val loopEndSymbol = ModuleOrObjectSymbol(DummyTypeDef(syntax.Obj), Ident("LoopEnd")) + val tupleSymbol = ModuleOrObjectSymbol(DummyTypeDef(syntax.Mod), Ident("Tuple")) + val strSymbol = ModuleOrObjectSymbol(DummyTypeDef(syntax.Mod), Ident("Str")) // In JavaScript, `import` can be used for getting current file path, as `import.meta` val importSymbol = new VarSymbol(Ident("import")) val noSymbol = NoSymbol() @@ -277,6 +280,14 @@ object Elaborator: val nonLocalRet = val id = new Ident("ret") BlockMemberSymbol(id.name, Nil, true) + val unreachableSymbol = TermSymbol(syntax.ImmutVal, N, new Ident("unreachable")) + val tupleGetSymbol = createFunSymbolInMod("get", "xs" :: "i" :: Nil, tupleSymbol) + val tupleSliceSymbol = createFunSymbolInMod("slice", "xs" :: "i" :: "j" :: Nil, tupleSymbol) + val tupleLazySliceSymbol = createFunSymbolInMod("lazySlice", "xs" :: "i" :: "j" :: Nil, tupleSymbol) + val strStartsWithSymbol = createFunSymbolInMod("startsWith", "string" :: "prefix" :: Nil, strSymbol) + val strGetSymbol = createFunSymbolInMod("get", "string" :: "i" :: Nil, strSymbol) + val strTakeSymbol = createFunSymbolInMod("take", "string" :: "n" :: Nil, strSymbol) + val strLeaveSymbol = createFunSymbolInMod("leave", "string" :: "n" :: Nil, strSymbol) val (matchSuccessClsSymbol, matchSuccessTrmSymbol) = val id = new Ident("MatchSuccess") val td = TypeDef(syntax.Cls, App(id, Tup(Ident("output") :: Ident("bindings") :: Nil)), N) @@ -323,6 +334,14 @@ object Elaborator: def dbgUid(uid: Uid[Symbol]): Str = if dbg then s"‹$uid›" else "" // ^ we do not display the uid by default to avoid polluting diff-test outputs + // Create a term symbol for a function defined in the given module + private def createFunSymbolInMod(name: Str, paramNames: List[Str], mod: ModuleOrObjectSymbol) = + val sym = TermSymbol(syntax.Fun, N, Ident(name)) + val bsym = BlockMemberSymbol(name, Nil, true) + val ps = PlainParamList(paramNames.map(s => Param.simple(VarSymbol(Ident(s))))) + sym.defn = S(TermDefinition(syntax.Fun, bsym, sym, ps :: Nil, N, N, N, + TermDefFlags(true), Modulefulness(S(mod))(false), Nil, N)) + sym transparent inline def State(using state: State): State = state end Elaborator diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/Normalization.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/Normalization.scala index 4b7327ca12..2fbb1f5b19 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/Normalization.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/Normalization.scala @@ -303,7 +303,7 @@ class Normalization(lowering: Lowering)(using tl: TL)(using Raise, Ctx, State) e * match failure in the future. */ private def throwMatchErrorBlock = - Throw(Instantiate(mut = false, Select(Value.Ref(State.globalThisSymbol), Tree.Ident("Error"))(N), + Throw(Instantiate(mut = false, Select(Value.Ref(State.globalThisSymbol), Tree.Ident("Error"))(S(ctx.builtins.Error)), Value.Lit(syntax.Tree.StrLit("match error")).asArg :: Nil)) // TODO add failed-match scrutinee info import syntax.Keyword.{`if`, `while`} diff --git a/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/TermSynthesizer.scala b/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/TermSynthesizer.scala index 79c29c4a99..e52d445a64 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/TermSynthesizer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/TermSynthesizer.scala @@ -56,13 +56,13 @@ trait TermSynthesizer(using State): val parameters = parametersOpt.map(_.map(_ -> N)) FlatPattern.ClassLike(matchFailureClass, State.matchFailureClsSymbol, parameters, false)(Tree.Dummy) - protected lazy val tupleSlice = sel(sel(runtimeRef, "Tuple"), "slice") - protected lazy val tupleLazySlice = sel(sel(runtimeRef, "Tuple"), "lazySlice") - protected lazy val tupleGet = sel(sel(runtimeRef, "Tuple"), "get") - protected lazy val stringStartsWith = sel(sel(runtimeRef, "Str"), "startsWith") - protected lazy val stringGet = sel(sel(runtimeRef, "Str"), "get") - protected lazy val stringTake = sel(sel(runtimeRef, "Str"), "take") - protected lazy val stringLeave = sel(sel(runtimeRef, "Str"), "leave") + protected lazy val tupleSlice = sel(sel(runtimeRef, "Tuple"), "slice", State.tupleSliceSymbol) + protected lazy val tupleLazySlice = sel(sel(runtimeRef, "Tuple"), "lazySlice", State.tupleLazySliceSymbol) + protected lazy val tupleGet = sel(sel(runtimeRef, "Tuple"), "get", State.tupleGetSymbol) + protected lazy val stringStartsWith = sel(sel(runtimeRef, "Str"), "startsWith", State.strStartsWithSymbol) + protected lazy val stringGet = sel(sel(runtimeRef, "Str"), "get", State.strGetSymbol) + protected lazy val stringTake = sel(sel(runtimeRef, "Str"), "take", State.strTakeSymbol) + protected lazy val stringLeave = sel(sel(runtimeRef, "Str"), "leave", State.strLeaveSymbol) /** Make a term that looks like `runtime.Tuple.get(t, i)`. */ protected final def callTupleGet(t: Term, i: Int, label: Str): Term = diff --git a/hkmc2/shared/src/test/mlscript/basics/LazySpreads.mls b/hkmc2/shared/src/test/mlscript/basics/LazySpreads.mls index c3b1d15ffd..72493744e8 100644 --- a/hkmc2/shared/src/test/mlscript/basics/LazySpreads.mls +++ b/hkmc2/shared/src/test/mlscript/basics/LazySpreads.mls @@ -57,7 +57,7 @@ fun sum2 = case //│ return 0 //│ } else if (runtime.Tuple.isArrayLike(caseScrut) && caseScrut.length >= 2) { //│ element0$ = runtime.Tuple.get(caseScrut, 0); -//│ middleElements3 = runtime.safeCall(runtime.Tuple.slice(caseScrut, 1, 1)); +//│ middleElements3 = runtime.Tuple.slice(caseScrut, 1, 1); //│ lastElement0$3 = runtime.Tuple.get(caseScrut, -1); //│ y = lastElement0$3; //│ xs = middleElements3; diff --git a/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls b/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls index 8b0b2ecff8..f70a95f52e 100644 --- a/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls +++ b/hkmc2/shared/src/test/mlscript/bbml/bbCodeGen.mls @@ -184,7 +184,7 @@ fun nott = case //│ return true; //│ break; //│ default: -//│ throw globalThis.Object.freeze(new globalThis.Error("match error")); +//│ throw globalThis.Object.freeze(new globalThis.Error.class("match error")); //│ break; //│ } //│ }); diff --git a/hkmc2/shared/src/test/mlscript/codegen/BlockPrinter.mls b/hkmc2/shared/src/test/mlscript/codegen/BlockPrinter.mls index c318b93203..818351b102 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BlockPrinter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BlockPrinter.mls @@ -65,13 +65,13 @@ case //│ Array(0) => //│ return Predef.print⁰("empty") //│ Array(1+) => -//│ set element0$ = runtime⁰.Tuple﹖.get﹖(caseScrut, 0); -//│ set middleElements = runtime⁰.Tuple﹖.slice﹖(caseScrut, 1, 0); +//│ set element0$ = runtime⁰.Tuple﹖.get⁰(caseScrut, 0); +//│ set middleElements = runtime⁰.Tuple﹖.slice⁰(caseScrut, 1, 0); //│ set xs = middleElements; //│ set x = element0$; //│ return Predef.print⁰("non-empty", x) //│ else -//│ throw new globalThis⁰.Error﹖("match error") +//│ throw new globalThis⁰.Error⁰("match error") //│ end //│ }; //│ lambda⁰ diff --git a/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls b/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls index cf359a6f01..5f639bcb4d 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/FieldSymbols.mls @@ -92,7 +92,7 @@ case //│ set a = arg$Foo$0$; //│ return a //│ else -//│ throw new globalThis⁰.Error﹖("match error") +//│ throw new globalThis⁰.Error⁰("match error") //│ end //│ }; //│ lambda⁰ diff --git a/hkmc2/shared/src/test/mlscript/codegen/FirstClassFunctionTransform.mls b/hkmc2/shared/src/test/mlscript/codegen/FirstClassFunctionTransform.mls index 62867ee8f2..136b6caaba 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/FirstClassFunctionTransform.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/FirstClassFunctionTransform.mls @@ -72,8 +72,8 @@ fun f(x, y, b) = //│ this.#b = b; //│ } //│ #Function$$cap; -//│ #b; //│ #y; +//│ #b; //│ call(z) { //│ return lambda1(this.#y, this.#b, z) //│ } @@ -1105,3 +1105,41 @@ foo(print) //│ tmp40 = globalThis.Object.freeze(new Function$77()); //│ foo14(tmp40) //│ > + + +if true is true then print("true") +//│ > true + + +if 0 is + Bool then 1 + Str then 1 + 0 then 0 + else 2 +//│ = 0 + + +class Foo(val x) + + +if Foo(1) is + Foo(x) then x + else 0 +//│ = 1 + + +if [1, 2, 3] is + [x, y] then x + y + [x, y, z] then x + y + z + [x, ...y] then x + else 0 +//│ = 6 + + +class Foo(val x) + +let foo = Foo(1) in foo.Foo#x +//│ = 1 + +let foo = new Foo(1) in foo.Foo#x +//│ = 1 diff --git a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls index 2389dfa158..1c124cf47e 100644 --- a/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls +++ b/hkmc2/shared/src/test/mlscript/lifter/ClassInFun.mls @@ -138,8 +138,8 @@ f().foo() //│ } //│ } //│ #Good$cap; -//│ #x; //│ #y; +//│ #x; //│ #scope0$cap; //│ foo() { //│ let tmp6, tmp7; diff --git a/hkmc2/shared/src/test/mlscript/std/FingerTreeListTest.mls b/hkmc2/shared/src/test/mlscript/std/FingerTreeListTest.mls index a467226b18..269f79e1f5 100644 --- a/hkmc2/shared/src/test/mlscript/std/FingerTreeListTest.mls +++ b/hkmc2/shared/src/test/mlscript/std/FingerTreeListTest.mls @@ -102,7 +102,7 @@ if xs is //│ let ls, a, middleElements, element0$; //│ if (runtime.Tuple.isArrayLike(xs1) && xs1.length >= 1) { //│ element0$ = runtime.Tuple.get(xs1, 0); -//│ middleElements = runtime.safeCall(runtime.Tuple.slice(xs1, 1, 0)); +//│ middleElements = runtime.Tuple.slice(xs1, 1, 0); //│ ls = middleElements; //│ a = element0$; //│ globalThis.Object.freeze([ diff --git a/hkmc2/shared/src/test/mlscript/ucs/normalization/ExcessiveDeduplication.mls b/hkmc2/shared/src/test/mlscript/ucs/normalization/ExcessiveDeduplication.mls index 9ab7d29834..82fd273d9b 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/normalization/ExcessiveDeduplication.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/normalization/ExcessiveDeduplication.mls @@ -63,14 +63,14 @@ if //│ > x is true and //│ > z is true then 1 //│ > y is []=1 and -//│ > let tmp:element0$ = ((tmp:runtime.)Tuple.)get(y, 0) +//│ > let tmp:element0$ = ((tmp:runtime.)Tuple.)get‹term:get›(y, 0) //│ > let b = tmp:element0$ //│ > else //│ > let x //│ > x = 1 //│ > builtin:+(x, b) //│ > y is []=1 and -//│ > let tmp:element0$ = ((tmp:runtime.)Tuple.)get(y, 0) +//│ > let tmp:element0$ = ((tmp:runtime.)Tuple.)get‹term:get›(y, 0) //│ > let b = tmp:element0$ //│ > else //│ > let x diff --git a/hkmc2/shared/src/test/mlscript/ucs/patterns/RestTuple.mls b/hkmc2/shared/src/test/mlscript/ucs/patterns/RestTuple.mls index 33339b6e3a..1e45b57dc4 100644 --- a/hkmc2/shared/src/test/mlscript/ucs/patterns/RestTuple.mls +++ b/hkmc2/shared/src/test/mlscript/ucs/patterns/RestTuple.mls @@ -10,7 +10,7 @@ fun nonsense(xs) = if xs is //│ nonsense = function nonsense(xs) { //│ let ys, middleElements; //│ if (runtime.Tuple.isArrayLike(xs) && xs.length >= 0) { -//│ middleElements = runtime.safeCall(runtime.Tuple.slice(xs, 0, 0)); +//│ middleElements = runtime.Tuple.slice(xs, 0, 0); //│ ys = middleElements; //│ return ys //│ } else { throw globalThis.Object.freeze(new globalThis.Error("match error")) } @@ -32,7 +32,7 @@ fun lead_and_last(xs) = if xs is //│ let x, ys, y, lastElement0$, middleElements, element0$; //│ if (runtime.Tuple.isArrayLike(xs) && xs.length >= 2) { //│ element0$ = runtime.Tuple.get(xs, 0); -//│ middleElements = runtime.safeCall(runtime.Tuple.slice(xs, 1, 1)); +//│ middleElements = runtime.Tuple.slice(xs, 1, 1); //│ lastElement0$ = runtime.Tuple.get(xs, -1); //│ y = lastElement0$; //│ ys = middleElements; @@ -68,7 +68,7 @@ fun nested_tuple_patterns(xs) = if xs is //│ split_default$: { //│ if (runtime.Tuple.isArrayLike(xs) && xs.length >= 2) { //│ element0$ = runtime.Tuple.get(xs, 0); -//│ middleElements = runtime.safeCall(runtime.Tuple.slice(xs, 1, 1)); +//│ middleElements = runtime.Tuple.slice(xs, 1, 1); //│ lastElement0$ = runtime.Tuple.get(xs, -1); //│ if (runtime.Tuple.isArrayLike(middleElements) && middleElements.length === 2) { //│ element0$1 = runtime.Tuple.get(middleElements, 0);