From b1ce54f04844569d705269d8845a2cabc1841a83 Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 17 Jan 2026 17:52:23 +0800 Subject: [PATCH 1/3] printer improvements --- .../main/scala/hkmc2/codegen/Printer.scala | 60 ++++++++-- .../test/mlscript/codegen/BlockPrinter.mls | 113 ++++++++++++++++++ 2 files changed, 165 insertions(+), 8 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala index d36932b598..9a4c335401 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala @@ -9,6 +9,11 @@ import hkmc2.Message.MessageContext import hkmc2.document._ import hkmc2.semantics.Elaborator.State import hkmc2.utils.Scope +import hkmc2.semantics.* +import hkmc2.syntax.Cls +import hkmc2.syntax.Pat +import hkmc2.syntax.Obj +import hkmc2.syntax.Mod object Printer: def getVar(l: Local)(using Raise, Scope): String = l match @@ -60,11 +65,38 @@ object Printer: case End(msg) => doc"end ${msg}" case _ => TODO(blk) + def mkDocument(c: ClsLikeBody)(using Raise, Scope): Document = + mkDocument(c.privateFields, c.publicFields, c.methods, N, c.ctor) + + def mkDocument( + privateFields: List[TermSymbol], + publicFields: List[(BlockMemberSymbol, TermSymbol)], + methods: List[FunDefn], + preCtor: Opt[Block], + ctor: Block + )(using Raise, Scope): Document = + val privFields = privateFields.map(x => doc"let ${x.id.name} = ...").mkDocument(sep = doc" # ") + val pubFields = publicFields.map(x => doc"${x._1.nme}").mkDocument(sep = doc" # ") + val docPrivFlds = if privateFields.isEmpty then doc"" else doc" # ${privFields}" + val docPubFlds = if publicFields.isEmpty then doc"" else doc" # ${pubFields}" + val docPreCtor = preCtor match + case Some(End(_)) => doc"" + case Some(value) => doc" # preCtor { #{ # ${mkDocument(value)} #} # }" + case None => doc"" + val docCtor = ctor match + case End(_) => doc"" + case _ => doc" # ctor { #{ # ${mkDocument(ctor)} #} # }" + + val mtds = methods.map(mkDocument).mkDocument(sep = doc" # ") + val docMethods = if methods.isEmpty then doc"" else doc" # ${mtds}" + if publicFields.isEmpty && privateFields.isEmpty && methods.isEmpty then doc"" + else doc" { #{ ${docPrivFlds}${docPubFlds}${docPreCtor}${docCtor}${docMethods} #} # }" + def mkDocument(defn: Defn)(using Raise, Scope): Document = defn match case FunDefn(own, sym, dSym, params, body) => - val docParams = doc"${own.fold("")(_.toString+"::")}${params.map(_.params.map(x => summon[Scope].allocateName(x.sym)).mkDocument("(", ", ", ")")).mkDocument("")}" + val docParams = doc"${own.fold("")(_.toString + "::")}${sym.nme}${params.map(_.params.map(x => summon[Scope].allocateName(x.sym)).mkDocument("(", ", ", ")")).mkDocument("")}" val docBody = mkDocument(body) - doc"fun ${sym.nme}${docParams} { #{ # ${docBody} #} # }" + doc"fun ${docParams} { #{ # ${docBody} #} # }" case ValDefn(tsym, sym, rhs) => doc"val ${tsym.nme} = ${mkDocument(rhs)}" case ClsLikeDefn(own, isym, sym, ctorSym, k, paramsOpt, auxParams, parentSym, methods, @@ -77,14 +109,24 @@ object Printer: val clsParams = paramsOpt.fold(Nil)(_.paramSyms) val auxClsParams = auxParams.flatMap(_.paramSyms) val ctorParams = (clsParams ++ auxClsParams).map(p => summon[Scope].allocateName(p)) - val privFields = privateFields.map(x => doc"let ${x.id.name} = ...").mkDocument(sep = doc" # ") - val pubFields = publicFields.map(x => doc"${x._1.nme}").mkDocument(sep = doc" # ") - val docPrivFlds = if privateFields.isEmpty then doc"" else doc" # ${privFields}" - val docPubFlds = if publicFields.isEmpty then doc"" else doc" # ${pubFields}" - val docBody = if publicFields.isEmpty && privateFields.isEmpty then doc"" else doc" { #{ ${docPrivFlds}${docPubFlds} #} # }" val docCtorParams = if clsParams.isEmpty then doc"" else doc"(${ctorParams.mkDocument(", ")})" val docStaged = if isym.defn.forall(_.hasStagedModifier.isEmpty) then doc"" else doc"staged " - doc"${docStaged}class ${own.fold("")(_.toString+"::")}${sym.nme}${docCtorParams}${docBody}" + val docBody = mkDocument(privateFields, publicFields, methods, S(preCtor), ctor) + val docPreCtor = mkDocument(preCtor) + val docCtor = mkDocument(ctor) + val clsType = k match + case Cls => "class" + case Pat => "pattern" + case Obj => "object" + case Mod => "module" + val docCls = doc"${docStaged}${clsType} ${own.fold("")(_.toString+"::")}${sym.nme}${docCtorParams}${docBody}" + val docModule = mod match + case Some(mod) => + val docStaged = if mod.isym.defn.forall(_.hasStagedModifier.isEmpty) then doc"" else doc"staged " + val docBody = mkDocument(mod) + doc" in # ${docStaged}module ${own.fold("")(_.toString+"::")}${sym.nme}${docBody}" + case None => doc"" + doc"${docCls}${docModule}" def mkDocument(arg: Arg)(using Raise, Scope): Document = val doc = mkDocument(arg.value) @@ -118,6 +160,8 @@ object Printer: doc"${if mut then "mut " else ""}{ ${ args.map(x => x.idx.fold(doc"...")(p => mkDocument(p) :: ": ") :: mkDocument(x.value)).mkDocument(", ") } }" + case DynSelect(qual, fld, arrayIdx) => + doc"${mkDocument(qual)}${if arrayIdx then "." else "!"}${mkDocument(fld)}" case x: Path => mkDocument(x) def mkDocument(prog: Program)(using Raise, Scope): Document = summon[Scope].nest.givenIn: diff --git a/hkmc2/shared/src/test/mlscript/codegen/BlockPrinter.mls b/hkmc2/shared/src/test/mlscript/codegen/BlockPrinter.mls index aa178c1b44..b8bf7f052b 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BlockPrinter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BlockPrinter.mls @@ -35,3 +35,116 @@ let x = x + 1 //│ set block$res3 = undefined in //│ end //│ x = 1 + +:slot +module A with + val x = 2 + let y = 3 + class C(x) with + fun f(x) = 2 + fun g(x) = 3 +//│ Pretty Lowered: +//│ +//│ define class A in +//│ module A { +//│ let y = ... +//│ x +//│ ctor { +//│ define val x = 2 in +//│ set y = 3 in +//│ define class module:A::C(x4) { +//│ let x = ... +//│ ctor { +//│ set x5 = x4 in +//│ end +//│ } +//│ fun class:C::f(x6) { +//│ return 2 +//│ } +//│ } in +//│ end +//│ } +//│ fun module:A::g(x7) { +//│ return 3 +//│ } +//│ } in +//│ set block$res4 = undefined in +//│ end + +:slot +class A +class B(x) extends A with + val y = 2 +//│ Pretty Lowered: +//│ +//│ define class A in +//│ define class B(x4) { +//│ let x = ... +//│ y +//│ preCtor { +//│ return super() +//│ } +//│ ctor { +//│ set x5 = x4 in +//│ define val y = 2 in +//│ end +//│ } +//│ } in +//│ set block$res5 = undefined in +//│ end + +:slot +pattern P = B +//│ Pretty Lowered: +//│ +//│ define pattern P { +//│ fun unapply(input) { +//│ match input +//│ B => +//│ set output = input in +//│ set tmp1 = { } in +//│ return new runtime.MatchSuccess(output, tmp1) +//│ else +//│ return new runtime.MatchFailure(null) +//│ in +//│ end +//│ } +//│ fun unapplyStringPrefix(input1) { +//│ return new runtime.MatchFailure(null) +//│ } +//│ } in +//│ set block$res6 = undefined in +//│ end + +:slot +object A with + fun f = 42 +//│ Pretty Lowered: +//│ define object A { fun object:A::f { return 42 } } in set block$res7 = undefined in end + +:slot +let x = [1, 2, 3] +x.0 +//│ Pretty Lowered: +//│ +//│ set x4 = [1, 2, 3] in +//│ set selRes = x4.0 in +//│ set discarded = x4.0$__checkNotMethod in +//│ match selRes +//│ undefined => +//│ throw new globalThis.Error("Access to required field '0' yielded 'undefined'") +//│ else +//│ +//│ in +//│ set block$res8 = selRes in +//│ return undefined +//│ = 1 +//│ x = [1, 2, 3] + +:slot +let y = { x: 2 } +y!x +//│ Pretty Lowered: +//│ set x5 = 2 in set y = { "x": x5 } in set block$res9 = y!"x" in return undefined +//│ = 2 +//│ y = {x: 2} From 8d19d59e4267a6e6ed78b94b8b28457d155005be Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 17 Jan 2026 17:55:54 +0800 Subject: [PATCH 2/3] update imports --- hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala index 9a4c335401..7264006687 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala @@ -7,13 +7,10 @@ import mlscript.utils._, shorthands._ import hkmc2._ import hkmc2.Message.MessageContext import hkmc2.document._ +import hkmc2.semantics._ +import hkmc2.syntax._ import hkmc2.semantics.Elaborator.State import hkmc2.utils.Scope -import hkmc2.semantics.* -import hkmc2.syntax.Cls -import hkmc2.syntax.Pat -import hkmc2.syntax.Obj -import hkmc2.syntax.Mod object Printer: def getVar(l: Local)(using Raise, Scope): String = l match From 3c6a31fd22699b0c47260903ddd09764eb2aed1f Mon Sep 17 00:00:00 2001 From: CAG2Mark Date: Sat, 17 Jan 2026 18:02:01 +0800 Subject: [PATCH 3/3] improve module printing --- hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala | 2 +- hkmc2/shared/src/test/mlscript/codegen/BlockPrinter.mls | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala b/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala index 7264006687..43fca77fcc 100644 --- a/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala +++ b/hkmc2/shared/src/main/scala/hkmc2/codegen/Printer.scala @@ -121,7 +121,7 @@ object Printer: case Some(mod) => val docStaged = if mod.isym.defn.forall(_.hasStagedModifier.isEmpty) then doc"" else doc"staged " val docBody = mkDocument(mod) - doc" in # ${docStaged}module ${own.fold("")(_.toString+"::")}${sym.nme}${docBody}" + doc" with # ${docStaged}module ${own.fold("")(_.toString+"::")}${sym.nme}${docBody}" case None => doc"" doc"${docCls}${docModule}" diff --git a/hkmc2/shared/src/test/mlscript/codegen/BlockPrinter.mls b/hkmc2/shared/src/test/mlscript/codegen/BlockPrinter.mls index b8bf7f052b..565db48cca 100644 --- a/hkmc2/shared/src/test/mlscript/codegen/BlockPrinter.mls +++ b/hkmc2/shared/src/test/mlscript/codegen/BlockPrinter.mls @@ -45,7 +45,7 @@ module A with fun g(x) = 3 //│ Pretty Lowered: //│ -//│ define class A in +//│ define class A with //│ module A { //│ let y = ... //│ x