Skip to content

Commit 8d19148

Browse files
committed
Implement UCS while expressions
1 parent b379b98 commit 8d19148

File tree

26 files changed

+876
-764
lines changed

26 files changed

+876
-764
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ class BBTyper(using elState: Elaborator.State, tl: TL):
287287
val res = freshVar(using ctx)
288288
constrain(bodyCtx, sk | res)
289289
(bodyTy, rhsCtx | res, rhsEff | bodyEff)
290-
case Term.If(Split.Cons(Branch(cond, Pattern.LitPat(BoolLit(true)), Split.Else(cons)), Split.Else(alts))) =>
290+
case Term.IfLike(Keyword.`if`, Split.Cons(Branch(cond, Pattern.LitPat(BoolLit(true)), Split.Else(cons)), Split.Else(alts))) =>
291291
val (condTy, condCtx, condEff) = typeCode(cond)
292292
val (consTy, consCtx, consEff) = typeCode(cons)
293293
val (altsTy, altsCtx, altsEff) = typeCode(alts)
@@ -373,7 +373,7 @@ class BBTyper(using elState: Elaborator.State, tl: TL):
373373
given Ctx = nextCtx
374374
constrain(ascribe(term, skolemize(pt))._2, Bot) // * never generalize terms with effects
375375
(pt, Bot)
376-
case (Term.If(branches), ty) => // * propagate
376+
case (Term.IfLike(Keyword.`if`, branches), ty) => // * propagate
377377
typeSplit(branches, S(ty))
378378
case (Term.Asc(term, ty), rhs) =>
379379
ascribe(term, typeType(ty))
@@ -536,7 +536,7 @@ class BBTyper(using elState: Elaborator.State, tl: TL):
536536
case Term.Asc(term, ty) =>
537537
val res = typeType(ty)(using ctx)
538538
ascribe(term, res)
539-
case Term.If(branches) => typeSplit(branches, N)
539+
case Term.IfLike(Keyword.`if`, branches) => typeSplit(branches, N)
540540
case Term.Region(sym, body) =>
541541
val nestCtx = ctx.nextLevel
542542
given Ctx = nestCtx

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ sealed abstract class Block extends Product with AutoLocated:
3232
case Match(scrut, arms, dflt, rst) =>
3333
arms.flatMap(_._2.definedVars).toSet ++ dflt.toList.flatMap(_.definedVars) ++ rst.definedVars
3434
case End(_) => Set.empty
35+
case Break(_, _) => Set.empty
3536
case Define(defn, rst) => rst.definedVars
3637
case TryBlock(sub, fin, rst) => sub.definedVars ++ fin.definedVars ++ rst.definedVars
38+
case Label(lbl, bod, rst) => bod.definedVars ++ rst.definedVars
3739

3840
// TODO conserve if no changes
3941
def mapTail(f: BlockTail => BlockTail): Block = this match
@@ -60,6 +62,11 @@ case class Return(res: Result, implct: Bool) extends BlockTail
6062

6163
case class Throw(exc: Result) extends BlockTail
6264

65+
case class Label(label: Local, body: Block, rest: Block) extends BlockTail
66+
67+
case class Break(label: Local, toBeginning: Bool) extends BlockTail
68+
69+
// TODO: remove this form?
6370
case class Begin(sub: Block, rest: Block) extends Block with ProductWithTail
6471

6572
case class TryBlock(sub: Block, finallyDo: Block, rest: Block) extends Block with ProductWithTail

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

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -204,52 +204,69 @@ class Lowering(using TL, Raise, Elaborator.State):
204204
)
205205
*/
206206

207-
case iftrm: st.If =>
207+
case iftrm: st.IfLike =>
208208

209-
tl.log(s"If $iftrm")
209+
tl.log(s"${iftrm.kw} $iftrm")
210+
211+
val isIf = iftrm.kw match
212+
case syntax.Keyword.`if` => true
213+
case syntax.Keyword.`while` => false
214+
val isWhile = !isIf
210215

211216
var usesResTmp = false
212217
lazy val l =
213218
usesResTmp = true
214219
new TempSymbol(summon[Elaborator.State].nextUid, S(t))
215220

216-
def go(split: Split)(using Subst): Block = split match
221+
lazy val lbl =
222+
new TempSymbol(summon[Elaborator.State].nextUid, S(t))
223+
224+
def go(split: Split, topLevel: Bool)(using Subst): Block = split match
217225
case Split.Let(sym, trm, tl) =>
218226
term(trm): r =>
219-
Assign(sym, r, go(tl))
227+
Assign(sym, r, go(tl, topLevel))
220228
case Split.Cons(Branch(scrut, pat, tail), restSplit) =>
221229
subTerm(scrut): sr =>
222230
tl.log(s"Binding scrut $scrut to $sr ${summon[Subst].map}")
223231
val cse = pat match
224-
case Pattern.LitPat(lit) => Case.Lit(lit) -> go(tail)
232+
case Pattern.LitPat(lit) => Case.Lit(lit) -> go(tail, topLevel = false)
225233
case Pattern.Class(cls, args0, _refined) =>
226234
val args = args0.getOrElse(Nil)
227235
val clsDefn = cls.defn.getOrElse(die)
228236
val clsParams = clsDefn.paramsOpt.getOrElse(Nil)
229237
assert(args0.isEmpty || clsParams.length === args.length)
230238
def mkArgs(args: Ls[Param -> BlockLocalSymbol])(using Subst): Case -> Block = args match
231-
case Nil => Case.Cls(cls) -> go(tail)
239+
case Nil => Case.Cls(cls) -> go(tail, topLevel = false)
232240
case (param, arg) :: args =>
233241
// summon[Subst].+(arg -> Value.Ref(new TempSymbol(summon[Elaborator.State].nextUid, N)))
234242
// Assign(arg, Select(sr, Tree.Ident("head")), mkArgs(args))
235243
val (cse, blk) = mkArgs(args)
236244
(cse, Assign(arg, Select(sr, param.sym.id/*FIXME incorrect Ident?*/), blk))
237245
mkArgs(clsParams.zip(args))
238246
Match(sr, cse :: Nil,
239-
// elseBranch,
240-
S(go(restSplit)),
247+
S(go(restSplit, topLevel = true)),
241248
End()
242249
)
243250
case Split.Else(els) =>
244-
if k.isInstanceOf[Ret] then term(els)(k)
245-
else term(els)(r => Assign(l, r, End()))
251+
if k.isInstanceOf[Ret] && isIf then term(els)(k)
252+
else
253+
term(els): r =>
254+
Assign(l, r,
255+
if isWhile && !topLevel then Break(lbl, toBeginning = true)
256+
// if isWhile then Break(lbl, toBeginning = !topLevel)
257+
else End()
258+
)
246259
case Split.Nil =>
247260
Throw(Instantiate(Value.Ref(Elaborator.Ctx.errorSymbol),
248261
Value.Lit(syntax.Tree.StrLit("match error")) :: Nil)) // TODO add failed-match scrutinee info
249262

250-
if k.isInstanceOf[Ret] then go(iftrm.normalized)
251-
else Begin(
252-
go(iftrm.normalized),
263+
if k.isInstanceOf[Ret] && isIf then go(iftrm.normalized, topLevel = true)
264+
else
265+
val body = if isWhile
266+
then Label(lbl, go(iftrm.normalized, topLevel = true), End())
267+
else go(iftrm.normalized, topLevel = true)
268+
Begin(
269+
body,
253270
if usesResTmp then k(Value.Ref(l))
254271
else k(Value.Lit(syntax.Tree.UnitLit(true))) // * it seems this currently never happens
255272
)

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,18 @@ class JSBuilder extends CodeBuilder:
203203

204204
case Throw(res) =>
205205
doc" # throw ${result(res)}"
206+
207+
case Break(lbl, false) =>
208+
doc" # break ${getVar(lbl)}"
209+
210+
case Break(lbl, true) =>
211+
doc" # continue ${getVar(lbl)}"
212+
213+
case Label(lbl, bod, rst) =>
214+
scope.allocateName(lbl)
215+
doc" # ${getVar(lbl)}: while (true) { #{ ${
216+
returningTerm(bod)
217+
} # break; #} # }${returningTerm(rst)}"
206218

207219
case TryBlock(sub, fin, rst) =>
208220
doc" # try { #{ ${returningTerm(sub)

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ extends Importer:
144144
case InfixApp(lhs, Keyword.`is` | Keyword.`and`, rhs) =>
145145
val des = new Desugarer(tl, this).shorthands(tree)(ctx)
146146
val nor = new ucs.Normalization(tl)(des)
147-
Term.If(des)(nor)
147+
Term.IfLike(Keyword.`if`, des)(nor)
148148
case App(Ident("|"), Tree.Tup(lhs :: rhs :: Nil)) =>
149149
Term.CompType(term(lhs), term(rhs), true)
150150
case App(Ident("&"), Tree.Tup(lhs :: rhs :: Nil)) =>
@@ -174,14 +174,14 @@ extends Importer:
174174
Term.New(term(cls), Nil).withLocOf(tree)
175175
// case _ =>
176176
// raise(ErrorReport(msg"Illegal new expression." -> tree.toLoc :: Nil))
177-
case Tree.If(split) =>
177+
case Tree.IfLike(kw, split) =>
178178
val desugared = new Desugarer(tl, this).termSplit(split, identity)(Split.Nil)(ctx)
179179
scoped("ucs:desugared"):
180180
log(s"Desugared:\n${Split.display(desugared)}")
181181
val normalized = new ucs.Normalization(tl)(desugared)
182182
scoped("ucs:normalized"):
183183
log(s"Normalized:\n${Split.display(normalized)}")
184-
Term.If(desugared)(normalized)
184+
Term.IfLike(kw, desugared)(normalized)
185185
case IfElse(InfixApp(InfixApp(scrutinee, Keyword.`is`, ctor @ Ident(cls)), Keyword.`then`, cons), alts) =>
186186
ctx.get(cls) match
187187
case S(sym: ClassSymbol) =>
@@ -192,7 +192,7 @@ extends Importer:
192192
Pattern.Class(sym, N, true)(ctor),
193193
Split.default(term(cons))
194194
) :: Split.default(term(alts)))
195-
Term.If(body)(body)
195+
Term.IfLike(Keyword.`if`, body)(body)
196196
case _ =>
197197
raise(ErrorReport(msg"Illegal pattern $cls." -> tree.toLoc :: Nil))
198198
Term.Error
@@ -203,7 +203,7 @@ extends Importer:
203203
scrutVar.ref(),
204204
Split.default(term(cons))
205205
) :: Split.default(term(alts)))
206-
Term.If(body)(body)
206+
Term.IfLike(Keyword.`if`, body)(body)
207207
case Tree.Quoted(body) => Term.Quoted(term(body))
208208
case Tree.Unquoted(body) => Term.Unquoted(term(body))
209209
case Tree.Case(branches) =>
@@ -215,7 +215,7 @@ extends Importer:
215215
val nor = new ucs.Normalization(tl)(des)
216216
scoped("ucs:normalized"):
217217
log(s"Normalized:\n${Split.display(nor)}")
218-
Term.Lam(Param(FldFlags.empty, scrut, N) :: Nil, Term.If(des)(nor))
218+
Term.Lam(Param(FldFlags.empty, scrut, N) :: Nil, Term.IfLike(Keyword.`if`, des)(nor))
219219
case Modified(Keyword.`return`, kwLoc, body) =>
220220
Term.Ret(term(body))
221221
case Tree.Region(id: Tree.Ident, body) =>

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ enum Term extends Statement:
1616
case TyApp(lhs: Term, targs: Ls[Term])
1717
case Sel(prefix: Term, nme: Tree.Ident)
1818
case Tup(fields: Ls[Fld])(val tree: Tree.Tup)
19-
case If(desugared: Split)(val normalized: Split)
19+
case IfLike(kw: Keyword.`if`.type | Keyword.`while`.type, desugared: Split)(val normalized: Split)
2020
case Lam(params: Ls[Param], body: Term)
2121
case FunTy(lhs: Term, rhs: Term, eff: Opt[Term])
2222
case Forall(tvs: Ls[QuantVar], body: Term)
@@ -63,7 +63,8 @@ enum Term extends Statement:
6363
case TyApp(lhs, targs) => "type application"
6464
case Sel(pre, nme) => "selection"
6565
case Tup(fields) => "tuple literal"
66-
case If(body) => "`if` expression"
66+
case IfLike(Keyword.`if`, body) => "`if` expression"
67+
case IfLike(Keyword.`while`, body) => "`while` expression"
6768
case Lam(params, body) => "function literal"
6869
case FunTy(lhs, rhs, eff) => "function type"
6970
case Forall(tvs, body) => "universal quantification"
@@ -95,7 +96,7 @@ sealed trait Statement extends AutoLocated:
9596
case TyApp(pre, tarsg) => pre :: tarsg
9697
case Sel(pre, _) => pre :: Nil
9798
case Tup(fields) => fields.map(_.value)
98-
case If(body) => Nil // TODO
99+
case IfLike(_, body) => Nil // TODO
99100
case Lam(params, body) => body :: Nil
100101
case Blk(stats, res) => stats.flatMap(_.subTerms) ::: res :: Nil
101102
case Quoted(term) => term :: Nil
@@ -158,7 +159,7 @@ sealed trait Statement extends AutoLocated:
158159
case Forall(tvs, body) => s"forall ${tvs.mkString(", ")}: ${body.toString}"
159160
case WildcardTy(in, out) => s"in ${in.map(_.toString).getOrElse("")} out ${out.map(_.toString).getOrElse("")}"
160161
case Sel(pre, nme) => s"${pre.showDbg}.${nme.name}"
161-
case If(body) => s"if { ${body.showDbg} }"
162+
case IfLike(kw, body) => s"${kw.name} { ${body.showDbg} }"
162163
case Lam(params, body) => s"λ${params.map(_.showDbg).mkString(", ")}. ${body.showDbg}"
163164
case Blk(stats, res) =>
164165
(stats.map(_.showDbg + "; ") :+ (res match { case Lit(Tree.UnitLit(true)) => "" case x => x.showDbg + " " }))

hkmc2/shared/src/main/scala/hkmc2/syntax/Keyword.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ object Keyword:
5959
// val `;` = Keyword(";", ascPrec, eqPrec)
6060

6161
val `if` = Keyword("if", N, nextPrec)
62+
val `while` = Keyword("while", N, curPrec)
6263
val `then` = Keyword("then", nextPrec, curPrec)
6364
val `else` = Keyword("else", nextPrec, curPrec)
6465
val `case` = Keyword("case", N, N)
@@ -75,9 +76,8 @@ object Keyword:
7576
val `rec` = Keyword("rec", N, N)
7677
val `in` = Keyword("in", curPrec, curPrec)
7778
val `out` = Keyword("out", N, curPrec)
78-
val `set` = Keyword("set", N, N)
79+
val `set` = Keyword("set", N, curPrec)
7980
val `do` = Keyword("do", N, N)
80-
val `while` = Keyword("while", N, N)
8181
val `declare` = Keyword("declare", N, N)
8282
val `trait` = Keyword("trait", N, N)
8383
val `mixin` = Keyword("mixin", N, N)

hkmc2/shared/src/main/scala/hkmc2/syntax/ParseRule.scala

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -159,29 +159,11 @@ object ParseRule:
159159
// ) { case (lhs, body) => Let(lhs, lhs, body) }
160160
)
161161

162-
val prefixRules: ParseRule[Tree] = ParseRule("start of statement", omitAltsStr = true)(
163-
letLike(`let`),
164-
letLike(`set`),
165-
Kw(`new`):
166-
ParseRule("`new` keyword"):
167-
Expr(ParseRule("`new` expression")(End(())))((body, _: Unit) => New(body))
168-
,
169-
Kw(`in`):
170-
ParseRule("modifier keyword `in`"):
171-
Expr(
172-
ParseRule("`in` expression")(
173-
Kw(`out`)(ParseRule(s"modifier keyword `out`")(standaloneExpr)).map(s => S(Tree.Modified(`out`, N/* TODO */, s))),
174-
End(N),
175-
)
176-
) {
177-
case (lhs, N) => Tree.Modified(`in`, N/* TODO */, lhs)
178-
case (lhs, S(rhs)) => Tup(Tree.Modified(`in`, N/* TODO */, lhs) :: rhs :: Nil)
179-
}
180-
,
181-
Kw(`if`):
182-
ParseRule("`if` keyword")(
162+
def ifLike(kw: `if`.type | `while`.type): Alt[Tree] =
163+
Kw(kw):
164+
ParseRule(s"'${kw.name}' keyword")(
183165
Expr(
184-
ParseRule("`if` expression")(
166+
ParseRule(s"'${kw.name}' expression")(
185167
End(N),
186168
Kw(`else`):
187169
ParseRule(s"`else` keyword")(
@@ -196,14 +178,35 @@ object ParseRule:
196178
val items = split match
197179
case Block(stmts) => stmts.appended(clause)
198180
case _ => split :: clause :: Nil
199-
If(Block(items))
200-
case (split, N) => If(split)
181+
IfLike(kw, Block(items))
182+
case (split, N) => IfLike(kw, split)
201183
,
202184
Blk(
203-
ParseRule("`if` block")(End(()))
204-
) { case (body, _) => If(body) }
185+
ParseRule(s"'${kw.name}' block")(End(()))
186+
) { case (body, _) => IfLike(kw, body) }
205187
)
188+
189+
val prefixRules: ParseRule[Tree] = ParseRule("start of statement", omitAltsStr = true)(
190+
letLike(`let`),
191+
letLike(`set`),
192+
Kw(`new`):
193+
ParseRule("`new` keyword"):
194+
Expr(ParseRule("`new` expression")(End(())))((body, _: Unit) => New(body))
195+
,
196+
Kw(`in`):
197+
ParseRule("modifier keyword `in`"):
198+
Expr(
199+
ParseRule("`in` expression")(
200+
Kw(`out`)(ParseRule(s"modifier keyword `out`")(standaloneExpr)).map(s => S(Tree.Modified(`out`, N/* TODO */, s))),
201+
End(N),
202+
)
203+
) {
204+
case (lhs, N) => Tree.Modified(`in`, N/* TODO */, lhs)
205+
case (lhs, S(rhs)) => Tup(Tree.Modified(`in`, N/* TODO */, lhs) :: rhs :: Nil)
206+
}
206207
,
208+
ifLike(`if`),
209+
ifLike(`while`),
207210
Kw(`else`):
208211
ParseRule("`else` clause")(
209212
Expr(ParseRule("`else` expression")(End(()))):

hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ enum Tree extends AutoLocated:
6363
case Sel(prefix: Tree, name: Ident)
6464
case InfixApp(lhs: Tree, kw: Keyword.Infix, rhs: Tree)
6565
case New(body: Tree)
66-
case If(split: Tree)
66+
case IfLike(kw: Keyword.`if`.type | Keyword.`while`.type, split: Tree)
6767
@deprecated("Use If instead", "hkmc2-ucs")
6868
case IfElse(cond: Tree, alt: Tree)
6969
case Case(branches: Tree)
@@ -88,7 +88,7 @@ enum Tree extends AutoLocated:
8888
case InfixApp(lhs, _, rhs) => Ls(lhs, rhs)
8989
case TermDef(k, head, rhs) => head :: rhs.toList
9090
case New(body) => body :: Nil
91-
case If(split) => split :: Nil
91+
case IfLike(_, split) => split :: Nil
9292
case IfElse(cond, alt) => cond :: alt :: Nil
9393
case Case(bs) => Ls(bs)
9494
case Region(name, body) => name :: body :: Nil
@@ -122,7 +122,8 @@ enum Tree extends AutoLocated:
122122
case Sel(prefix, name) => "selection"
123123
case InfixApp(lhs, kw, rhs) => "infix application"
124124
case New(body) => "new"
125-
case If(split) => "if expression"
125+
case IfLike(Keyword.`if`, split) => "if expression"
126+
case IfLike(Keyword.`while`, split) => "while expression"
126127
case IfElse(cond, alt) => "if-then-else"
127128
case Case(branches) => "case"
128129
case Region(name, body) => "region"
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const Stack = new class Stack {
2+
constructor() {
3+
this.Cons = class Cons {
4+
constructor(head, tail) {
5+
this.head = head;
6+
this.tail = tail;
7+
8+
}
9+
toString() { return "Cons(" + this.head + ", " + this.tail + ")"; }
10+
};
11+
this.Nil = new class Nil {
12+
constructor() {
13+
14+
}
15+
toString() { return "Nil"; }
16+
};
17+
}
18+
isEmpty(xs) {
19+
if (xs === this.Nil) {
20+
return true
21+
} else {
22+
return false
23+
}
24+
}
25+
toString() { return "Stack"; }
26+
};
27+
undefined
28+
export default Stack;

0 commit comments

Comments
 (0)