Skip to content

Commit 5664756

Browse files
committed
Add basic record syntax, which does not require curly braces
1 parent 8363f1c commit 5664756

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1070
-380
lines changed

hkmc2/shared/src/main/scala/hkmc2/bbml/bbML.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,8 @@ class BBTyper(using elState: Elaborator.State, tl: TL):
247247
val cr = freshVar(new TempSymbol(S(unq), "ctx"))
248248
constrain(tryMkMono(ty, body), BbCtx.codeTy(tv, cr))
249249
(tv, cr, eff)
250-
case blk @ Term.Blk(LetDecl(sym, _) :: DefineVar(sym2, rhs) :: Nil, body) if sym2 is sym => // TODO: more than one!!
250+
case blk @ Term.Blk(LetDecl(sym, _) :: DefineVar(sym2, rhs) :: Nil, body)
251+
if sym2 is sym => // TODO: more than one!!
251252
val (rhsTy, rhsCtx, rhsEff) = typeCode(rhs)(using ctx)
252253
val nestCtx = ctx.nextLevel
253254
given BbCtx = nestCtx

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ case class Program(
2222

2323
sealed abstract class Block extends Product with AutoLocated:
2424

25+
def ~(that: Block): Block = Begin(this, that)
26+
2527
protected def children: Ls[Located] = ??? // Maybe extending AutoLocated is unnecessary
2628

2729
lazy val definedVars: Set[Local] = this match
@@ -381,12 +383,14 @@ enum Case:
381383
case Lit(_) => Set.empty
382384
case Cls(_, path) => path.freeVars
383385
case Tup(_, _) => Set.empty
384-
386+
385387
lazy val freeVarsLLIR: Set[Local] = this match
386388
case Lit(_) => Set.empty
387389
case Cls(_, path) => path.freeVarsLLIR
388390
case Tup(_, _) => Set.empty
389391

392+
sealed trait TrivialResult extends Result
393+
390394
sealed abstract class Result:
391395

392396
// TODO rm Lam from values and thus the need for this method
@@ -431,7 +435,7 @@ case class Call(fun: Path, args: Ls[Arg])(val isMlsFun: Bool, val mayRaiseEffect
431435

432436
case class Instantiate(cls: Path, args: Ls[Path]) extends Result
433437

434-
sealed abstract class Path extends Result:
438+
sealed abstract class Path extends TrivialResult:
435439
def selN(id: Tree.Ident): Path = Select(this, id)(N)
436440
def sel(id: Tree.Ident, sym: FieldSymbol): Path = Select(this, id)(S(sym))
437441
def selSN(id: Str): Path = selN(new Tree.Ident(id))
@@ -448,9 +452,15 @@ enum Value extends Path:
448452
case Lit(lit: Literal)
449453
case Lam(params: ParamList, body: Block)
450454
case Arr(elems: Ls[Arg])
455+
case Rcd(elems: Ls[RcdArg])
451456

452457
case class Arg(spread: Bool, value: Path)
453458

459+
// * `IndxdArg(S(idx), value)` represents a key-value pair in a record `(idx): value`
460+
// * `IndxdArg(N, value)` represents a spread element in a record `...value`
461+
case class RcdArg(idx: Opt[Path], value: Path):
462+
def spread: Bool = idx.isEmpty
463+
454464
extension (k: Block => Block)
455465

456466
def chain(other: Block => Block): Block => Block = b => k(other(b))

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@ class BlockTransformer(subst: SymbolSubst):
132132
case Value.Arr(elems) =>
133133
val elems2 = elems.mapConserve(applyArg)
134134
if (elems2 is elems) then v else Value.Arr(elems2)
135+
case Value.Rcd(fields) =>
136+
val fields2 = fields.mapConserve:
137+
case arg @ RcdArg(idx, v) =>
138+
val v2 = applyPath(v)
139+
if v2 is v then arg else RcdArg(idx, v2)
140+
if fields2 is fields then v else Value.Rcd(fields2)
135141

136142
def applyLocal(sym: Local): Local = sym.subst
137143

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

Lines changed: 105 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,113 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
6363
def unit: Path =
6464
Select(Value.Ref(State.runtimeSymbol), Tree.Ident("Unit"))(S(summon[Ctx].builtins.Unit))
6565

66+
67+
type Rcd = List[RcdArg]
68+
6669
def returnedTerm(t: st)(using Subst): Block = term(t)(Ret)
6770

6871
// * Used to work around Scala's @tailrec annotation for those few calls that are not in tail position.
6972
final def term_nonTail(t: st, inStmtPos: Bool = false)(k: Result => Block)(using Subst): Block =
7073
term(t: st, inStmtPos: Bool)(k)
7174

75+
def block(stats: Ls[Statement], res: Rcd \/ Term)(k: Result => Block)(using Subst): Block =
76+
stats match
77+
case (t: sem.Term) :: stats =>
78+
subTerm(t, inStmtPos = true): r =>
79+
block(stats, res)(r => k(r))
80+
case Nil =>
81+
res match
82+
case R(res) => term(res)(k)
83+
case L(flds) =>
84+
k(Value.Rcd(flds.reverse))
85+
case RcdField(lhs, rhs) :: stats =>
86+
res match
87+
case R(_) => wat("RcdField in non-Rcd context", res)
88+
case L(flds) =>
89+
subTerm(lhs): l =>
90+
subTerm_nonTail(rhs): r =>
91+
block(stats, L(RcdArg(S(l), r) :: flds))(k)
92+
case (decl @ LetDecl(sym, annotations)) :: (stats @ ((_: DefineVar) :: _)) =>
93+
reportAnnotations(decl, annotations)
94+
block(stats, res)(k)
95+
case (decl @ LetDecl(sym, annotations)) :: stats =>
96+
reportAnnotations(decl, annotations)
97+
block(DefineVar(sym, Term.Lit(Tree.UnitLit(false))) :: stats, res)(k)
98+
case DefineVar(sym, rhs) :: stats =>
99+
subTerm(rhs): r =>
100+
Assign(sym, r, block(stats, res)(k))
101+
case (imp @ Import(sym, path)) :: stats =>
102+
raise(ErrorReport(
103+
msg"Imports must be at the top level" ->
104+
imp.toLoc :: Nil,
105+
source = Diagnostic.Source.Compilation))
106+
block(stats, res)(k)
107+
case (d: Declaration) :: stats =>
108+
d match
109+
case td: TermDefinition =>
110+
reportAnnotations(td, td.extraAnnotations)
111+
td.body match
112+
case N => // abstract declarations have no lowering
113+
block(stats, res)(k)
114+
case S(bod) =>
115+
td.k match
116+
case knd: syntax.Val =>
117+
assert(td.params.isEmpty)
118+
subTerm_nonTail(bod)(r =>
119+
// Assign(td.sym, r,
120+
// term(st.Blk(stats, res))(k)))
121+
Define(ValDefn(td.owner, knd, td.sym, r),
122+
block(stats, res)(k)))
123+
case syntax.Fun =>
124+
val (paramLists, bodyBlock) = setupFunctionOrByNameDef(td.params, bod, S(td.sym.nme))
125+
Define(FunDefn(td.owner, td.sym, paramLists, bodyBlock),
126+
block(stats, res)(k))
127+
case syntax.Ins =>
128+
// Implicit instances are not parameterized for now.
129+
assert(td.params.isEmpty)
130+
subTerm(bod)(r =>
131+
Define(ValDefn(td.owner, syntax.ImmutVal, td.sym, r),
132+
block(stats, res)(k)))
133+
case cls: ClassLikeDef if cls.sym.defn.exists(_.isDeclare.isDefined) =>
134+
// * Declarations have no lowering
135+
block(stats, res)(k)
136+
case cls: ClassLikeDef =>
137+
reportAnnotations(cls, cls.extraAnnotations)
138+
val (mtds, publicFlds, privateFlds, ctor) = gatherMembers(cls.body)
139+
cls.ext match
140+
case N =>
141+
Define(ClsLikeDefn(cls.owner, cls.sym, cls.bsym, cls.kind, cls.paramsOpt, Nil, N,
142+
mtds,
143+
privateFlds,
144+
publicFlds,
145+
End(),
146+
ctor
147+
),
148+
block(stats, res)(k))
149+
case S(ext) =>
150+
assert(k isnt syntax.Mod) // modules can't extend things and can't have super calls
151+
subTerm(ext.cls): clsp =>
152+
val pctor = // TODO dedup with New case
153+
args(ext.args): args =>
154+
Return(Call(Value.Ref(State.builtinOpsMap("super")), args)(true, true), implct = true)
155+
Define(
156+
ClsLikeDefn(
157+
cls.owner, cls.sym, cls.bsym, cls.kind, cls.paramsOpt, Nil, S(clsp),
158+
mtds, privateFlds, publicFlds, pctor, ctor
159+
),
160+
block(stats, res)(k)
161+
)
162+
case td: TypeDef => // * Type definitions are erased
163+
block(stats, res)(k)
164+
72165
@tailrec
73166
final def term(t: st, inStmtPos: Bool = false)(k: Result => Block)(using Subst): Block =
74167
tl.log(s"Lowering.term ${t.showDbg.truncate(100, "[...]")}${
75168
if inStmtPos then " (in stmt)" else ""}${
76169
t.symbol.fold("")(" – symbol " + _)}")
77170
def warnStmt = if inStmtPos then
78-
raise(WarningReport:
79-
msg"Pure expression in statement position" -> t.toLoc :: Nil)
171+
raise:
172+
WarningReport(msg"Pure expression in statement position" -> t.toLoc :: Nil, S(t))
80173
t match
81174
case st.UnitVal() => k(unit)
82175
case st.Lit(lit) =>
@@ -234,8 +327,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
234327
subTerm(prefix): p =>
235328
conclude(Select(p, nme)(sel.sym))
236329
case _ => subTerm(f)(conclude)
237-
238-
case st.Blk((h @ Handle(lhs, rhs, as, cls, defs)) :: stmts, res) =>
330+
case h @ Handle(lhs, rhs, as, cls, defs, bod) =>
239331
if !lowerHandlers then
240332
raise(ErrorReport(
241333
msg"Effect handlers are not enabled" ->
@@ -255,83 +347,9 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
255347
subTerm(rhs): par =>
256348
subTerms(as): asr =>
257349
HandleBlock(lhs, resSym, par, asr, cls, handlers,
258-
term_nonTail(st.Blk(stmts, res))(HandleBlockReturn(_)),
350+
term_nonTail(bod)(HandleBlockReturn(_)),
259351
k(Value.Ref(resSym)))
260-
261-
case st.TyApp(lhs, _) => term(lhs)(k)
262-
case st.Blk(Nil, res) => term(res)(k)
263-
case st.Blk((t: sem.Term) :: stats, res) =>
264-
subTerm(t, inStmtPos = true)(r => term_nonTail(st.Blk(stats, res))(k))
265-
case st.Blk((d: Declaration) :: stats, res) =>
266-
d match
267-
case td: TermDefinition =>
268-
reportAnnotations(td, td.extraAnnotations)
269-
td.body match
270-
case N => // abstract declarations have no lowering
271-
term(st.Blk(stats, res))(k)
272-
case S(bod) =>
273-
td.k match
274-
case knd: syntax.Val =>
275-
assert(td.params.isEmpty)
276-
subTerm_nonTail(bod)(r =>
277-
// Assign(td.sym, r,
278-
// term(st.Blk(stats, res))(k)))
279-
Define(ValDefn(td.owner, knd, td.sym, r),
280-
term_nonTail(st.Blk(stats, res))(k)))
281-
case syntax.Fun =>
282-
val (paramLists, bodyBlock) = setupFunctionOrByNameDef(td.params, bod, S(td.sym.nme))
283-
Define(FunDefn(td.owner, td.sym, paramLists, bodyBlock),
284-
term_nonTail(st.Blk(stats, res))(k))
285-
case syntax.Ins =>
286-
// Implicit instances are not parameterized for now.
287-
assert(td.params.isEmpty)
288-
subTerm(bod)(r =>
289-
Define(ValDefn(td.owner, syntax.ImmutVal, td.sym, r),
290-
term_nonTail(st.Blk(stats, res))(k)))
291-
case cls: ClassLikeDef if cls.sym.defn.exists(_.isDeclare.isDefined) =>
292-
// * Declarations have no lowering
293-
term(st.Blk(stats, res))(k)
294-
case cls: ClassLikeDef =>
295-
reportAnnotations(cls, cls.extraAnnotations)
296-
val (mtds, publicFlds, privateFlds, ctor) = gatherMembers(cls.body)
297-
cls.ext match
298-
case N =>
299-
Define(
300-
ClsLikeDefn(cls.owner, cls.sym, cls.bsym, cls.kind, cls.paramsOpt, Nil, N,
301-
mtds, privateFlds, publicFlds, End(), ctor),
302-
term_nonTail(st.Blk(stats, res))(k)
303-
)
304-
case S(ext) =>
305-
assert(k isnt syntax.Mod) // modules can't extend things and can't have super calls
306-
subTerm(ext.cls): clsp =>
307-
val pctor = // TODO dedup with `New` case
308-
args(ext.args): args =>
309-
Return(Call(Value.Ref(State.builtinOpsMap("super")), args)(true, true), implct = true)
310-
Define(
311-
ClsLikeDefn(cls.owner, cls.sym, cls.bsym, cls.kind, cls.paramsOpt, Nil, S(clsp),
312-
mtds, privateFlds, publicFlds, pctor, ctor),
313-
term_nonTail(st.Blk(stats, res))(k)
314-
)
315-
case _ =>
316-
// TODO handle
317-
term(st.Blk(stats, res))(k)
318-
case st.Blk((decl @ LetDecl(sym, annotations)) :: (stats @ ((_: DefineVar) :: _)), res) =>
319-
reportAnnotations(decl, annotations)
320-
term(st.Blk(stats, res))(k)
321-
case st.Blk((decl @ LetDecl(sym, annotations)) :: stats, res) =>
322-
reportAnnotations(decl, annotations)
323-
term(st.Blk(DefineVar(sym, Term.Lit(Tree.UnitLit(false))) :: stats, res))(k)
324-
case st.Blk(DefineVar(sym, rhs) :: stats, res) =>
325-
val oldSubst = subst
326-
rhs match
327-
// * This is currently wrong because if `r` is mutable, we can't substitute `sym` with `r`
328-
// * TODO: Distinguish mutable from immutable local variables
329-
// case Ref(r) =>
330-
// given Subst = oldSubst + (sym -> Value.Ref(r))
331-
// term(st.Blk(stats, res))(k)
332-
case _ =>
333-
subTerm(rhs): r =>
334-
Assign(sym, r, term_nonTail(st.Blk(stats, res))(k))
352+
case st.Blk(sts, res) => block(sts, R(res))(k)
335353
case Assgn(lhs, rhs) =>
336354
lhs match
337355
case Ref(sym) =>
@@ -493,13 +511,13 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
493511
subTerm(prefix): p =>
494512
subTerm_nonTail(fld): f =>
495513
k(DynSelect(p, f, ai))
496-
514+
497515

498516
case New(cls, as, N) =>
499517
subTerm(cls): sr =>
500518
subTerms(as): asr =>
501519
k(Instantiate(sr, asr))
502-
520+
503521
case New(cls, as, S((isym, rft))) =>
504522
subTerm(cls): clsp =>
505523
val sym = new BlockMemberSymbol(isym.name, Nil)
@@ -510,15 +528,15 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
510528
val clsDef = ClsLikeDefn(N, isym, sym, syntax.Cls, N, Nil, S(clsp),
511529
mtds, privateFlds, publicFlds, pctor, ctor)
512530
Define(clsDef, term_nonTail(New(sym.ref(), Nil, N))(k))
513-
531+
514532
case Try(sub, finallyDo) =>
515533
val l = new TempSymbol(S(sub))
516534
TryBlock(
517535
subTerm_nonTail(sub)(p => Assign(l, p, End())),
518536
subTerm_nonTail(finallyDo)(_ => End()),
519537
k(Value.Ref(l))
520538
)
521-
539+
522540
// * BbML-specific cases: t.Cls#field and mutable operations
523541
case sp @ SelProj(prefix, _, proj) =>
524542
setupSelection(prefix, proj, sp.sym)(k)
@@ -536,6 +554,9 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
536554
subTerm_nonTail(rhs): value =>
537555
AssignField(ref, Tree.Ident("value"), value, k(value))(N)
538556

557+
case Rcd(stats) =>
558+
block(stats, L(Nil))(k)
559+
539560
case Neg(_) =>
540561
raise(ErrorReport(
541562
msg"Unexpected type annotations ${t.show}" ->

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,14 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
144144
case Value.Arr(es) if es.isEmpty => doc"[]"
145145
case Value.Arr(es) =>
146146
doc"[ #{ # ${es.map(argument).mkDocument(doc", # ")} #} # ]"
147+
case Value.Rcd(flds) =>
148+
doc"{ # #{ ${
149+
flds.map:
150+
case RcdArg(S(idx), v) =>
151+
doc"${result(idx)}: ${result(v)}"
152+
case RcdArg(N, v) => ???
153+
.mkDocument(", ")
154+
} #} # }"
147155

148156
def returningTerm(t: Block, endSemi: Bool)(using Raise, Scope): Document =
149157
def mkSemi = if endSemi then ";" else ""

0 commit comments

Comments
 (0)