Skip to content

Commit 2d3f76d

Browse files
committed
Add symbols to selections generated by pattern matching
1 parent 7158626 commit 2d3f76d

File tree

12 files changed

+213
-59
lines changed

12 files changed

+213
-59
lines changed

hkmc2/shared/src/main/scala/hkmc2/codegen/Block.scala

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ sealed abstract class Block extends Product with AutoLocated:
3333
case Match(scrut, arms, dflt, rst) =>
3434
arms.flatMap(_._2.definedVars).toSet ++ dflt.toList.flatMap(_.definedVars) ++ rst.definedVars
3535
case End(_) => Set.empty
36-
case Break(_, _) => Set.empty
36+
case Break(_) => Set.empty
37+
case Continue(_) => Set.empty
3738
case Define(defn, rst) => rst.definedVars
3839
case TryBlock(sub, fin, rst) => sub.definedVars ++ fin.definedVars ++ rst.definedVars
3940
case Label(lbl, bod, rst) => bod.definedVars ++ rst.definedVars
@@ -59,13 +60,15 @@ case class Match(
5960
) extends Block with ProductWithTail
6061

6162
// * `implct`: whether it's a JS implicit return, without the `return` keyword
63+
// * TODO could just remove this flag and add a flag in Scope instead
6264
case class Return(res: Result, implct: Bool) extends BlockTail
6365

6466
case class Throw(exc: Result) extends BlockTail
6567

6668
case class Label(label: Local, body: Block, rest: Block) extends BlockTail
6769

68-
case class Break(label: Local, toBeginning: Bool) extends BlockTail
70+
case class Break(label: Local) extends BlockTail
71+
case class Continue(label: Local) extends BlockTail
6972

7073
// TODO: remove this form?
7174
case class Begin(sub: Block, rest: Block) extends Block with ProductWithTail
@@ -75,7 +78,7 @@ case class TryBlock(sub: Block, finallyDo: Block, rest: Block) extends Block wit
7578
case class Assign(lhs: Local, rhs: Result, rest: Block) extends Block with ProductWithTail
7679
// case class Assign(lhs: Path, rhs: Result, rest: Block) extends Block with ProductWithTail
7780

78-
case class AssignField(lhs: Path, nme: Tree.Ident, rhs: Result, rest: Block) extends Block with ProductWithTail
81+
case class AssignField(lhs: Path, nme: Tree.Ident, rhs: Result, rest: Block)(symbol: Opt[FieldSymbol]) extends Block with ProductWithTail
7982

8083
case class Define(defn: Defn, rest: Block) extends Block with ProductWithTail
8184

@@ -99,7 +102,8 @@ final case class ClsLikeDefn(
99102
sym: MemberSymbol[? <: ClassLikeDef],
100103
k: syntax.ClsLikeKind,
101104
methods: Ls[FunDefn],
102-
fields: Ls[TermSymbol],
105+
privateFields: Ls[TermSymbol],
106+
publicFields: Ls[TermDefinition],
103107
ctor: Block,
104108
) extends Defn
105109

@@ -123,7 +127,8 @@ case class Instantiate(cls: Path, args: Ls[Path]) extends Result
123127

124128
sealed abstract class Path extends Result
125129

126-
case class Select(qual: Path, name: Tree.Ident) extends Path
130+
case class Select(qual: Path, name: Tree.Ident)(val symbol: Opt[FieldSymbol]) extends Path with ProductWithExtraInfo:
131+
def extraInfo: Str = symbol.mkString
127132

128133
enum Value extends Path:
129134
case Ref(l: Local)

hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala

Lines changed: 27 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -118,15 +118,19 @@ class Lowering(using TL, Raise, Elaborator.State):
118118
val (mtds, rest1) = bodBlk.stats.partitionMap:
119119
case td: TermDefinition if td.k is syntax.Fun => L(td)
120120
case s => R(s)
121-
val (flds, rest2) = rest1.partitionMap:
121+
val (privateFlds, rest2) = rest1.partitionMap:
122122
case LetDecl(sym: TermSymbol) => L(sym)
123123
case s => R(s)
124+
val publicFlds = rest2.collect:
125+
case td @ TermDefinition(k = (_: syntax.Val)) => td
124126
Define(ClsLikeDefn(cls.sym, syntax.Cls,
125127
mtds.flatMap: td =>
126128
td.body.map: bod =>
127129
FunDefn(td.sym, td.params, term(bod)(Ret))
128130
,
129-
flds, term(Blk(rest2, bodBlk.res))(ImplctRet).mapTail:
131+
privateFlds,
132+
publicFlds,
133+
term(Blk(rest2, bodBlk.res))(ImplctRet).mapTail:
130134
case Return(Value.Lit(syntax.Tree.UnitLit(true)), true) => End()
131135
case t => t
132136
),
@@ -144,14 +148,14 @@ class Lowering(using TL, Raise, Elaborator.State):
144148
case Ref(sym: LocalSymbol) =>
145149
subTerm(rhs): r =>
146150
Assign(sym, r, k(Value.Lit(syntax.Tree.UnitLit(true))))
147-
case SynthSel(prefix, nme) =>
151+
case sel @ SynthSel(prefix, nme) =>
148152
subTerm(prefix): p =>
149153
subTerm(rhs): r =>
150-
AssignField(p, nme, r, k(Value.Lit(syntax.Tree.UnitLit(true))))
151-
case Sel(prefix, nme) =>
154+
AssignField(p, nme, r, k(Value.Lit(syntax.Tree.UnitLit(true))))(sel.sym)
155+
case sel @ Sel(prefix, nme) =>
152156
subTerm(prefix): p =>
153157
subTerm(rhs): r =>
154-
AssignField(p, nme, r, k(Value.Lit(syntax.Tree.UnitLit(true))))
158+
AssignField(p, nme, r, k(Value.Lit(syntax.Tree.UnitLit(true))))(sel.sym)
155159

156160
case st.Blk((imp @ Import(sym, path)) :: stats, res) =>
157161
raise(ErrorReport(
@@ -237,39 +241,24 @@ class Lowering(using TL, Raise, Elaborator.State):
237241
case cls: ClassSymbol => cls.tree.clsParams
238242
case _: ModuleSymbol => Nil
239243
assert(args0.isEmpty || clsParams.length === args.length)
240-
def mkArgs(args: Ls[(LocalSymbol & NamedSymbol) -> BlockLocalSymbol])(using Subst): Case -> Block = args match
241-
// def mkArgs(args: Ls[Param -> BlockLocalSymbol])(using Subst): Block = args match
244+
def mkArgs(args: Ls[TermSymbol -> BlockLocalSymbol])(using Subst): Case -> Block = args match
242245
case Nil =>
243-
// mkMatch(Case.Cls(cls, st) -> go(tail, topLevel = false))
244246
Case.Cls(cls, st) -> go(tail, topLevel = false)
245247
case (param, arg) :: args =>
246-
// summon[Subst].+(arg -> Value.Ref(new TempSymbol(N)))
247-
// Assign(arg, Select(sr, Tree.Ident("head")), mkArgs(args))
248-
249248
val (cse, blk) = mkArgs(args)
250-
(cse, Assign(arg, Select(sr, param.id/*FIXME incorrect Ident?*/), blk))
251-
// mkMatch(cse -> Assign(arg, Select(sr, param.sym.id/*FIXME incorrect Ident?*/), blk))
252-
// Assign(arg, Select(sr, param.sym.id/*FIXME incorrect Ident?*/), mkArgs(args))
253-
254-
// val (cse, blk) =
255-
// mkMatch(cse -> blk)
249+
(cse, Assign(arg, Select(sr, param.id/*FIXME incorrect Ident?*/)(S(param)), blk))
256250
mkMatch(mkArgs(clsParams.zip(args)))
257251
case Pattern.Tuple(len, inf) => mkMatch(Case.Tup(len, inf) -> go(tail, topLevel = false))
258-
// Match(sr, cse :: Nil,
259-
// S(go(restSplit, topLevel = true)),
260-
// End()
261-
// )
262252
case Split.Else(els) =>
263253
if k.isInstanceOf[TailOp] && isIf then term(els)(k)
264254
else
265255
term(els): r =>
266256
Assign(l, r,
267-
if isWhile && !topLevel then Break(lbl, toBeginning = true)
268-
// if isWhile then Break(lbl, toBeginning = !topLevel)
257+
if isWhile && !topLevel then Continue(lbl)
269258
else End()
270259
)
271260
case Split.End =>
272-
Throw(Instantiate(Select(Value.Ref(State.globalThisSymbol), Tree.Ident("Error")),
261+
Throw(Instantiate(Select(Value.Ref(State.globalThisSymbol), Tree.Ident("Error"))(N),
273262
Value.Lit(syntax.Tree.StrLit("match error")) :: Nil)) // TODO add failed-match scrutinee info
274263

275264
if k.isInstanceOf[TailOp] && isIf then go(iftrm.normalized, topLevel = true)
@@ -283,12 +272,12 @@ class Lowering(using TL, Raise, Elaborator.State):
283272
else k(Value.Lit(syntax.Tree.UnitLit(true))) // * it seems this currently never happens
284273
)
285274

286-
case SynthSel(prefix, nme) =>
275+
case sel @ SynthSel(prefix, nme) =>
287276
subTerm(prefix): p =>
288-
k(Select(p, nme))
277+
k(Select(p, nme)(sel.sym))
289278

290-
case Sel(prefix, nme) =>
291-
setupSelection(prefix, nme)(k)
279+
case sel @ Sel(prefix, nme) =>
280+
setupSelection(prefix, nme, sel.sym)(k)
292281

293282

294283
case New(cls, as) =>
@@ -336,18 +325,18 @@ class Lowering(using TL, Raise, Elaborator.State):
336325
go(sym -> path.toString :: acc, st.Blk(stats, res))
337326
case _ => Program(acc.reverse, topLevel(trm))
338327
go(Nil, main)
339-
340-
341-
342-
def setupSelection(prefix: Term, nme: Tree.Ident)(k: Result => Block)(using Subst): Block =
328+
329+
def setupSelection(prefix: Term, nme: Tree.Ident, sym: Opt[FieldSymbol])(k: Result => Block)(using Subst): Block =
343330
subTerm(prefix): p =>
344-
k(Select(p, nme))
331+
k(Select(p, nme)(sym))
345332

333+
334+
346335
trait LoweringSelSanityChecks
347336
(instrument: Bool)(using TL, Raise, Elaborator.State)
348337
extends Lowering:
349338

350-
override def setupSelection(prefix: st, nme: Tree.Ident)(k: Result => Block)(using Subst): Block =
339+
override def setupSelection(prefix: st, nme: Tree.Ident, sym: Opt[FieldSymbol])(k: Result => Block)(using Subst): Block =
351340
if instrument then
352341
subTerm(prefix): p =>
353342
val selRes = TempSymbol(N, "selRes")
@@ -362,8 +351,9 @@ trait LoweringSelSanityChecks
362351
Split.Else(selRes.ref()))
363352
Assign(
364353
selRes,
365-
Select(p, nme),
354+
Select(p, nme)(sym),
366355
term(IfLike(syntax.Keyword.`if`, split)(split))(k))
367356
else
368-
super.setupSelection(prefix, nme)(k)
357+
super.setupSelection(prefix, nme, sym)(k)
358+
369359

hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,16 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder:
153153
Return(Lam(ps, block), false)
154154
val (params, bodyDoc) = setupFunction(some(sym.nme), ps, result)
155155
doc"function ${sym.nme}($params) { #{ # ${bodyDoc} #} # }"
156-
case ClsLikeDefn(sym, syntax.Cls, mtds, flds, ctor) =>
156+
case ClsLikeDefn(sym, syntax.Cls, mtds, privFlds, _pubFlds, ctor) =>
157+
// * Note: `_pubFlds` is not used because in JS, fields are not declared
157158
val clsDefn = sym.defn.getOrElse(die)
158159
val clsParams = clsDefn.paramsOpt.fold(Nil)(_.paramSyms)
159160
val ctorParams = clsParams.map(p => p -> scope.allocateName(p))
160161
val ctorCode = ctorParams.foldRight(body(ctor)):
161162
case ((sym, nme), acc) =>
162163
doc"this.${sym.name} = $nme; # ${acc}"
163164
val clsJS = doc"class ${sym.nme} { #{ ${
164-
flds.map(f => doc" # #${f.nme};").mkDocument(doc"")
165+
privFlds.map(f => doc" # #${f.nme};").mkDocument(doc"")
165166
} # constructor(${
166167
ctorParams.unzip._2.mkDocument(", ")
167168
}) { #{ # ${
@@ -283,10 +284,10 @@ class JSBuilder(using Elaborator.State, Elaborator.Ctx) extends CodeBuilder:
283284
case Throw(res) =>
284285
doc" # throw ${result(res)};"
285286

286-
case Break(lbl, false) =>
287+
case Break(lbl) =>
287288
doc" # break ${getVar(lbl)};"
288289

289-
case Break(lbl, true) =>
290+
case Continue(lbl) =>
290291
doc" # continue ${getVar(lbl)};"
291292

292293
case Label(lbl, bod, rst) =>

hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ object Elaborator:
8484
require(id.name == nme)
8585
Term.Ref(sym)(id, 666) // TODO 666
8686
def symbol = S(sym)
87-
final case class SelElem(val base: Elem, val nme: Str, val symOpt: Opt[Symbol]) extends Elem:
87+
final case class SelElem(val base: Elem, val nme: Str, val symOpt: Opt[FieldSymbol]) extends Elem:
8888
def ref(id: Tree.Ident): Term =
8989
// * Note: due to symbolic ops, we may have `id.name =/= nme`;
9090
// * e.g., we can have `id.name = "|>"` and `nme = "pipe"`.
@@ -136,7 +136,7 @@ extends Importer:
136136
def mkLetBinding(sym: LocalSymbol, rhs: Term): Ls[Statement] =
137137
LetDecl(sym) :: DefineVar(sym, rhs) :: Nil
138138

139-
def resolveField(srcTree: Tree, base: Opt[Symbol], nme: Ident): Opt[Symbol] =
139+
def resolveField(srcTree: Tree, base: Opt[Symbol], nme: Ident): Opt[FieldSymbol] =
140140
base match
141141
case S(psym: BlockMemberSymbol) =>
142142
psym.modTree match

hkmc2/shared/src/main/scala/hkmc2/semantics/Symbol.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ case class TupSymbol(arity: Opt[Int])(using State) extends CtorSymbol:
147147

148148
type TypeSymbol = ClassSymbol | TypeAliasSymbol
149149

150+
type FieldSymbol = TermSymbol | MemberSymbol[?]
151+
150152

151153
/** This is the symbol associated to specific definitions.
152154
* One overloaded `BlockMemberSymbol` may correspond to multiple `InnerSymbol`s

hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ enum Term extends Statement:
1515
case Ref(sym: Symbol)(val tree: Tree.Ident, val refNum: Int)
1616
case App(lhs: Term, rhs: Term)(val tree: Tree.App, val resSym: FlowSymbol)
1717
case TyApp(lhs: Term, targs: Ls[Term])
18-
case SynthSel(prefix: Term, nme: Tree.Ident)(val sym: Opt[Symbol])
19-
case Sel(prefix: Term, nme: Tree.Ident)(val sym: Opt[Symbol])
18+
case Sel(prefix: Term, nme: Tree.Ident)(val sym: Opt[FieldSymbol])
19+
case SynthSel(prefix: Term, nme: Tree.Ident)(val sym: Opt[FieldSymbol])
2020
case Tup(fields: Ls[Elem])(val tree: Tree.Tup)
2121
case IfLike(kw: Keyword.`if`.type | Keyword.`while`.type, desugared: Split)(val normalized: Split)
2222
case Lam(params: ParamList, body: Term)
@@ -76,7 +76,11 @@ end Term
7676

7777
import Term.*
7878

79-
sealed trait Statement extends AutoLocated:
79+
sealed trait Statement extends AutoLocated with ProductWithExtraInfo:
80+
81+
def extraInfo: Str = this match
82+
case trm @ (_: Sel | _: SynthSel) => trm.symbol.mkString
83+
case _ => ""
8084

8185
def subStatements: Ls[Statement] = this match
8286
case Blk(stats, res) => stats ::: res :: Nil
@@ -156,7 +160,8 @@ sealed trait Statement extends AutoLocated:
156160
case TyApp(lhs, targs) => s"${lhs.showDbg}[${targs.mkString(", ")}]"
157161
case Forall(tvs, body) => s"forall ${tvs.mkString(", ")}: ${body.toString}"
158162
case WildcardTy(in, out) => s"in ${in.map(_.toString).getOrElse("")} out ${out.map(_.toString).getOrElse("")}"
159-
case SynthSel(pre, nme) => s"${pre.showDbg}.${nme.name}"
163+
case Sel(pre, nme) => s"${pre.showDbg}.${nme.name}"
164+
case SynthSel(pre, nme) => s"${pre.showDbg}(.)${nme.name}"
160165
case IfLike(kw, body) => s"${kw.name} { ${body.showDbg} }"
161166
case Lam(params, body) => s"λ${params.showDbg}. ${body.showDbg}"
162167
case Blk(stats, res) =>

hkmc2/shared/src/main/scala/hkmc2/utils/utils.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ import mlscript.utils.StringOps
2727

2828
trait ProductWithTail extends Product
2929

30+
trait ProductWithExtraInfo extends Product:
31+
def extraInfo: Str
32+
3033
extension (t: Product)
3134
def showAsTree(using post: Product => String = Function.const("")): String =
3235
showAsTree(false)
@@ -55,7 +58,12 @@ extension (t: Product)
5558
case t: Product => t.showAsTree(inTailPos)
5659
case v => v.toString
5760
val postfix = post(t)
58-
val prefix = t.productPrefix + (if postfix.isEmpty then "" else s" ($postfix)")
61+
val midfix = t match
62+
case t: ProductWithExtraInfo => t.extraInfo match
63+
case "" => ""
64+
case str => "{" + str + "}"
65+
case _ => ""
66+
val prefix = t.productPrefix + midfix + (if postfix.isEmpty then "" else s" ($postfix)")
5967
t.productArity match
6068
case 0 => prefix
6169
case 1 => prefix + " of " + aux(t.productElement(0))

hkmc2/shared/src/test/mlscript/HkScratch.mls

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
:js
2+
:d
23
// :de
34
// :sjs
45
// :pt
56
// :elt
6-
:d
77

88
//│ Elab: { import member:Predef‹77› from /Users/parreaux/work/Research/code/mlscript-forks/hkmc2/shared/src/test/mlscript-compile/Predef.mjs; }
99

hkmc2/shared/src/test/mlscript/codegen/BasicTerms.mls

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ log("Hi")
4343
//│ main = Assign:
4444
//│ lhs = $tmp
4545
//│ rhs = Call:
46-
//│ fun = Select:
46+
//│ fun = Select{member:log}:
4747
//│ qual = Ref of globalThis:import#Prelude
4848
//│ name = Ident of "log"
4949
//│ args = Ls of

0 commit comments

Comments
 (0)