Skip to content

Commit 55e4008

Browse files
LPTKchengluyu
andauthored
Reimplement operator apps, operator splits, and "placeholder syntax" for lambdas (#302)
Co-authored-by: Luyu Cheng <[email protected]>
1 parent c88fde9 commit 55e4008

File tree

89 files changed

+3401
-1460
lines changed

Some content is hidden

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

89 files changed

+3401
-1460
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ class BBTyper(using elState: Elaborator.State, tl: TL):
339339
(lhs, rhs) match
340340
case (Term.Lam(PlainParamList(params), body), ft @ PolyFunType(args, ret, eff)) => // * annoted functions
341341
if params.length != args.length then
342-
(error(msg"Cannot type function ${lhs.toString} as ${rhs.show}" -> lhs.toLoc :: Nil), Bot)
342+
(error(msg"Cannot type function ${lhs.toString} as ${rhs.show}" -> lhs.toLoc :: Nil), Bot)
343343
else
344344
val nestCtx = ctx.nest
345345
val argsTy = params.zip(args).map:

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ object Printer:
1919
// ts.trmTree
2020
case ts: semantics.InnerSymbol => ts.nme
2121
case ts: semantics.BuiltinSymbol => ts.nme
22-
case _ => summon[Scope].lookup_!(l)
22+
case _ => summon[Scope].lookup(l) match
23+
case S(str) => str
24+
case N => s"‹not in scope: ${l}"
2325

2426
def mkDocument(blk: Block)(using Raise, Scope): Document = blk match
2527
case Match(scrut, arms, dflt, rest) =>
@@ -95,6 +97,10 @@ object Printer:
9597
case Value.Arr(elems) =>
9698
val docElems = elems.map(x => mkDocument(x)).mkString(", ")
9799
doc"[${docElems}]"
100+
case Value.Rcd(args) =>
101+
doc"{ ${
102+
args.map(x => x.idx.fold(doc"...")(p => mkDocument(p) :: ": ") :: mkDocument(x.value)).mkString(", ")
103+
} }"
98104

99105
def mkDocument(path: Path)(using Raise, Scope): Document = path match
100106
case Select(qual, name) =>

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

Lines changed: 131 additions & 106 deletions
Large diffs are not rendered by default.

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,12 +252,18 @@ sealed trait Statement extends AutoLocated with ProductWithExtraInfo:
252252
case Neg(e) => e :: Nil
253253
case Annotated(ann, target) => ann.subTerms ::: target :: Nil
254254

255+
// private def treeOrSubterms(t: Tree, t: Term): Ls[Located] = t match
256+
private def treeOrSubterms(t: Tree): Ls[Located] = t match
257+
case Tree.DummyApp | Tree.DummyTup => subTerms
258+
case _ => t :: Nil
259+
255260
protected def children: Ls[Located] = this match
256261
case t: Lit => t.lit.asTree :: Nil
257-
case t: Ref => t.tree :: Nil
258-
case t: Tup => t.tree :: Nil
262+
case t: Ref => treeOrSubterms(t.tree)
263+
case t: Tup => treeOrSubterms(t.tree)
259264
case l: Lam => l.params.paramSyms.map(_.id) ::: l.body :: Nil
260-
case t: App => t.tree :: Nil
265+
case t: App => treeOrSubterms(t.tree)
266+
case IfLike(kw, desug) => desug :: Nil
261267
case SynthSel(pre, nme) => pre :: nme :: Nil
262268
case Sel(pre, nme) => pre :: nme :: Nil
263269
case SelProj(prefix, cls, proj) => prefix :: cls :: proj :: Nil

hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/DeBrujinSplit.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ object DeBrujinSplit:
2121
/** Resolve the constructor in the elaborator context. */
2222
def resolve(ctor: Ident | Sel, params: Ls[Tree]): Opt[F] =
2323
val term = scoped("ucs:mute"):
24-
elaborator.cls(ctor, inAppPrefix = false)
24+
elaborator.cls(elaborator.term(ctor), inAppPrefix = false)
2525
term.symbol.flatMap(_.asClsLike).map:
2626
case symbol: (ClassSymbol | ModuleSymbol) =>
2727
val pattern = ClassLike(ConstructorLike.Symbol(symbol))
@@ -70,9 +70,6 @@ object DeBrujinSplit:
7070
* not found.
7171
*/
7272
def dealWithCtor(ctor: Ident | Sel, params: Ls[Tree]): F = ctor match
73-
case Ident("~") => (scrutinee, innermost, alternative) =>
74-
Branch(scrutinee, ClassLike(ConstructorLike.StringJoin), innermost.increment(2), alternative)
75-
alternative
7673
case Ident(ctorName) => patternParams.find(_.sym.name == ctorName) match
7774
case S(Param(sym = symbol)) => (scrutinee, innermost, alternative) =>
7875
log(s"found an input pattern: ${symbol.name}")
@@ -93,6 +90,9 @@ object DeBrujinSplit:
9390
val buildRight = go(rhs)
9491
val latter = buildRight(scrutinee, consequence, alternative)
9592
buildLeft(scrutinee, consequence, latter)
93+
case lhs ~ rhs => (scrutinee, innermost, alternative) =>
94+
Branch(scrutinee, ClassLike(ConstructorLike.StringJoin), innermost.increment(2), alternative)
95+
alternative
9696
case Under() => (_, consequence, _) => consequence
9797
case ctor: (Ident | Sel) => dealWithCtor(ctor, Nil)
9898
case App(Ident("-"), Tup(IntLit(n) :: Nil)) =>
@@ -110,6 +110,7 @@ object DeBrujinSplit:
110110
(_, _, alternative) => alternative
111111
// END TODO: Support range patterns
112112
case App(ctor: (Ident | Sel), Tup(params)) => dealWithCtor(ctor, params)
113+
case OpApp(lhs, op: Ident, rhs :: Nil) => dealWithCtor(op, Ls(lhs, rhs))
113114
case literal: syntax.Literal => Branch(_, Literal(literal), _, _)
114115
scoped("ucs:rp:elaborate"):
115116
log(s"tree: ${tree.showDbg}")

hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/Desugarer.scala

Lines changed: 64 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import utils.TraceLogger
99
import syntax.Literal
1010
import Keyword.{as, and, `do`, `else`, is, let, `then`, where}
1111
import collection.mutable.{HashMap, SortedSet}
12-
import Elaborator.{ctx, Ctxl}
12+
import Elaborator.{ctx, Ctxl, UnderCtx}
1313
import scala.annotation.targetName
1414
import hkmc2.semantics.ClassDef.Parameterized
1515

@@ -26,11 +26,11 @@ object Desugarer:
2626
val tupleLast: HashMap[Int, BlockLocalSymbol] = HashMap.empty
2727
end Desugarer
2828

29-
class Desugarer(val elaborator: Elaborator)
29+
class Desugarer(val elaborator: Elaborator)(using UnderCtx)
3030
(using raise: Raise, state: Elaborator.State, c: Elaborator.Ctx) extends DesugaringBase:
3131
import Desugarer.*
3232
import Elaborator.Ctx
33-
import elaborator.term, elaborator.tl.*
33+
import elaborator.term, elaborator.subterm, elaborator.tl.*
3434

3535
given Ordering[Loc] = Ordering.by: loc =>
3636
(loc.spanStart, loc.spanEnd)
@@ -126,7 +126,7 @@ class Desugarer(val elaborator: Elaborator)
126126
raise(ErrorReport(msg"only one branch is supported in shorthands" -> tree.toLoc :: Nil))
127127
termSplitShorthands(branch, finish)(fallback)(ctx)
128128
case coda is rhs => fallback => ctx =>
129-
nominate(ctx, finish(term(coda)(using ctx))):
129+
nominate(ctx, finish(subterm(coda)(using ctx))):
130130
patternSplitShorthands(rhs, _)(fallback)
131131
case matches => fallback =>
132132
// There are N > 0 conjunct matches. We use `::[T]` instead of `List[T]`.
@@ -177,6 +177,35 @@ class Desugarer(val elaborator: Elaborator)
177177
nominate(ctx, term(coda)(using ctx)):
178178
expandMatch(_, pat, sequel)(fallback)
179179
expandMatch(scrutSymbol, headPattern, tailSplit)(fallback)
180+
181+
def termSplit(trees: Ls[Tree], finish: Term => Term): Split => Sequel =
182+
trees.foldRight(default): (t, elabFallback) =>
183+
t match
184+
case LetLike(`let`, ident @ Ident(_), N, N) => ???
185+
case LetLike(`let`, ident @ Ident(_), S(termTree), N) => fallback => ctx => trace(
186+
pre = s"termSplit: let ${ident.name} = $termTree",
187+
post = (res: Split) => s"termSplit: let >>> $res"
188+
):
189+
val sym = VarSymbol(ident)
190+
val fallbackCtx = ctx + (ident.name -> sym)
191+
Split.Let(sym, term(termTree)(using ctx), elabFallback(fallback)(fallbackCtx)).withLocOf(t)
192+
case Modified(Keyword.`do`, doLoc, computation) => fallback => ctx => trace(
193+
pre = s"termSplit: do $computation",
194+
post = (res: Split) => s"termSplit: else >>> $res"
195+
):
196+
val sym = TempSymbol(N, "doTemp")
197+
Split.Let(sym, term(computation)(using ctx), elabFallback(fallback)(ctx)).withLocOf(t)
198+
case Modified(Keyword.`else`, elsLoc, default) => fallback => ctx => trace(
199+
pre = s"termSplit: else $default",
200+
post = (res: Split) => s"termSplit: else >>> $res"
201+
):
202+
// TODO: report `rest` as unreachable
203+
Split.default(term(default)(using ctx)).withLocOf(t)
204+
case branch => fallback => ctx => trace(
205+
pre = s"termSplit: $branch",
206+
post = (res: Split) => s"termSplit >>> $res"
207+
):
208+
termSplit(branch, finish)(elabFallback(fallback)(ctx))(ctx)
180209

181210
/** Desugar a _term split_ (TS) into a _split_ of core abstract syntax.
182211
* @param tree the tree representing the term split.
@@ -186,35 +215,9 @@ class Desugarer(val elaborator: Elaborator)
186215
* matches and splits
187216
*/
188217
def termSplit(tree: Tree, finish: Term => Term): Split => Sequel =
218+
log(s"termSplit: $tree")
189219
tree match
190-
case blk: Block =>
191-
blk.desugStmts.foldRight(default): (t, elabFallback) =>
192-
t match
193-
case LetLike(`let`, ident @ Ident(_), N, N) => ???
194-
case LetLike(`let`, ident @ Ident(_), S(termTree), N) => fallback => ctx => trace(
195-
pre = s"termSplit: let ${ident.name} = $termTree",
196-
post = (res: Split) => s"termSplit: let >>> $res"
197-
):
198-
val sym = VarSymbol(ident)
199-
val fallbackCtx = ctx + (ident.name -> sym)
200-
Split.Let(sym, term(termTree)(using ctx), elabFallback(fallback)(fallbackCtx)).withLocOf(t)
201-
case Modified(Keyword.`do`, doLoc, computation) => fallback => ctx => trace(
202-
pre = s"termSplit: do $computation",
203-
post = (res: Split) => s"termSplit: else >>> $res"
204-
):
205-
val sym = TempSymbol(N, "doTemp")
206-
Split.Let(sym, term(computation)(using ctx), elabFallback(fallback)(ctx)).withLocOf(t)
207-
case Modified(Keyword.`else`, elsLoc, default) => fallback => ctx => trace(
208-
pre = s"termSplit: else $default",
209-
post = (res: Split) => s"termSplit: else >>> $res"
210-
):
211-
// TODO: report `rest` as unreachable
212-
Split.default(term(default)(using ctx)).withLocOf(t)
213-
case branch => fallback => ctx => trace(
214-
pre = s"termSplit: $branch",
215-
post = (res: Split) => s"termSplit >>> $res"
216-
):
217-
termSplit(branch, finish)(elabFallback(fallback)(ctx))(ctx).withLocOf(t)
220+
case blk: Block => termSplit(blk.desugStmts, finish)
218221
case coda is rhs => fallback => ctx =>
219222
nominate(ctx, finish(term(coda)(using ctx))):
220223
patternSplit(rhs, _)(fallback)
@@ -246,7 +249,8 @@ class Desugarer(val elaborator: Elaborator)
246249
):
247250
nominate(ctx, finish(term(headCoda)(using ctx))):
248251
expandMatch(_, headPattern, tailSplit)(fallback)
249-
case tree @ App(opIdent @ Ident(opName), rawTup @ Tup(lhs :: rhs :: Nil)) => fallback => ctx => trace(
252+
// Handle binary operators.
253+
case tree @ OpApp(lhs, opIdent @ Ident(opName), rhss) => fallback => ctx => trace(
250254
pre = s"termSplit: after op <<< $opName",
251255
post = (res: Split) => s"termSplit: after op >>> $res"
252256
):
@@ -258,22 +262,16 @@ class Desugarer(val elaborator: Elaborator)
258262
val finishInner = (rhsTerm: Term) =>
259263
val first = Fld(FldFlags.empty, lhsSymbol.ref(/* FIXME ident? */), N)
260264
val second = Fld(FldFlags.empty, rhsTerm, N)
261-
val arguments = Term.Tup(first :: second :: Nil)(rawTup)
265+
val arguments = Term.Tup(first :: second :: Nil)(Tree.DummyTup)
262266
val joint = FlowSymbol("‹applied-result›")
263-
Term.App(opRef, arguments)(tree, N, joint)
264-
termSplit(rhs, finishInner)(fallback)
265-
case tree @ App(lhs, blk @ OpBlock(opRhsApps)) => fallback => ctx =>
267+
Term.App(opRef, arguments)(Tree.DummyApp, N, joint)
268+
termSplit(rhss, finishInner)(fallback)
269+
// Handle operator splits.
270+
case tree @ OpSplit(lhs, rhss) => fallback => ctx =>
266271
nominate(ctx, finish(term(lhs)(using ctx))): vs =>
267-
val mkInnerFinish = (op: Term) => (rhsTerm: Term) =>
268-
val first = Fld(FldFlags.empty, vs.ref(/* FIXME ident? */), N)
269-
val second = Fld(FldFlags.empty, rhsTerm, N)
270-
val rawTup = Tup(lhs :: Nil): Tup // <-- loc might be wrong
271-
val arguments = Term.Tup(first :: second :: Nil)(rawTup)
272-
val joint = FlowSymbol("‹applied-result›")
273-
Term.App(op, arguments)(tree, N, joint)
274-
opRhsApps.foldRight(Function.const(fallback): Sequel): (tt, elabFallback) =>
275-
tt match
276-
case (Tree.Empty(), LetLike(`let`, pat, termTree, N)) => ctx =>
272+
rhss.foldRight(Function.const(fallback): Sequel): (branch, elabFallback) =>
273+
branch match
274+
case LetLike(`let`, pat, termTree, N) => ctx =>
277275
val ident = pat match // TODO handle patterns and rm special cases
278276
case ident: Ident => ident
279277
case und: Under => new Ident("_").withLocOf(und)
@@ -283,22 +281,20 @@ class Desugarer(val elaborator: Elaborator)
283281
val sym = VarSymbol(ident)
284282
val fallbackCtx = ctx + (ident.name -> sym)
285283
Split.Let(sym, term(termTree)(using ctx), elabFallback(fallbackCtx))
286-
case (Tree.Empty(), Modified(Keyword.`do`, doLoc, computation)) => ctx => trace(
284+
case Modified(Keyword.`do`, doLoc, computation) => ctx => trace(
287285
pre = s"termSplit: do $computation",
288-
post = (res: Split) => s"termSplit: else >>> $res"
286+
post = (res: Split) => s"termSplit: do >>> $res"
289287
):
290288
val sym = TempSymbol(N, "doTemp")
291289
Split.Let(sym, term(computation)(using ctx), elabFallback(ctx))
292-
case (Tree.Empty(), Modified(Keyword.`else`, elsLoc, default)) => ctx =>
290+
case Modified(Keyword.`else`, elsLoc, default) => ctx =>
293291
// TODO: report `rest` as unreachable
294292
Split.default(term(default)(using ctx))
295-
case ((rawOp @ Ident(_)), rhs) => ctx =>
296-
val op = term(rawOp)(using ctx)
297-
val innerFinish = mkInnerFinish(op)
298-
termSplit(rhs, innerFinish)(elabFallback(ctx))(ctx)
299-
case (op, rhs) => ctx =>
300-
raise(ErrorReport(msg"Unrecognized operator branch." -> op.toLoc :: Nil))
301-
elabFallback(ctx)
293+
case rawRhs => ctx =>
294+
log(s"rawRhs: $rawRhs")
295+
val rhs = rawRhs.splitOn(Trm(vs.ref(/* FIXME ident? */)))
296+
log(s"rhs: $rhs")
297+
termSplit(rhs, identity)(elabFallback(ctx))(ctx)
302298
case _ => fallback => _ =>
303299
raise(ErrorReport(msg"Unrecognized term split (${tree.describe})." -> tree.toLoc :: Nil))
304300
fallback.withoutLoc // Hacky... a loc is always added for the result
@@ -308,15 +304,14 @@ class Desugarer(val elaborator: Elaborator)
308304
* @param scrutinee the elaborated scrutinee
309305
* @param cont the continuation that needs the symbol and the context
310306
*/
311-
def nominate(baseCtx: Ctx, scrutinee: Term)
307+
def nominate(baseCtx: Ctx, scrutinee: Term, nameHint: Str = "scrut")
312308
(cont: BlockLocalSymbol => Sequel): Split = scrutinee match
313309
case ref @ Term.Ref(symbol: VarSymbol) =>
314310
val innerCtx = baseCtx + (ref.tree.name -> symbol)
315311
cont(symbol)(innerCtx)
316312
case _ =>
317-
val name = "scrut"
318-
val symbol = TempSymbol(N, name)
319-
val innerCtx = baseCtx + (name -> symbol)
313+
val symbol = TempSymbol(N, nameHint)
314+
val innerCtx = baseCtx + (nameHint -> symbol)
320315
Split.Let(symbol, scrutinee, cont(symbol)(innerCtx))
321316

322317
/** Decompose a `Tree` of conjunct matches. The tree is from the same line in
@@ -435,7 +430,7 @@ class Desugarer(val elaborator: Elaborator)
435430
pre = s"patternBranch <<< $patternAndMatches -> ${consequent.fold(_.showDbg, _.showDbg)}",
436431
post = (res: Split) => s"patternBranch >>> ${res.showDbg}")
437432
case _ =>
438-
raise(ErrorReport(msg"Unrecognized pattern split." -> tree.toLoc :: Nil))
433+
raise(ErrorReport(msg"Unrecognized pattern split (${tree.describe})." -> tree.toLoc :: Nil))
439434
_ => _ => Split.default(Term.Error)
440435

441436
/** Elaborate a single match (a scrutinee and a pattern) and forms a split
@@ -448,7 +443,7 @@ class Desugarer(val elaborator: Elaborator)
448443
def expandMatch(scrutSymbol: BlockLocalSymbol, pattern: Tree, sequel: Sequel): Split => Sequel =
449444
def ref = scrutSymbol.ref(/* FIXME ident? */)
450445
def dealWithCtorCase(ctor: Ctor, compile: Bool)(fallback: Split): Sequel = ctx =>
451-
val clsTrm = elaborator.cls(ctor, inAppPrefix = false)
446+
val clsTrm = elaborator.cls(term(ctor), inAppPrefix = false)
452447
clsTrm.symbol.flatMap(_.asClsLike) match
453448
case S(cls: ClassSymbol) =>
454449
if compile then warn(msg"Cannot compile the class `${cls.name}`" -> ctor.toLoc)
@@ -475,7 +470,7 @@ class Desugarer(val elaborator: Elaborator)
475470
pre = s"expandMatch <<< ${ctor}(${args.iterator.map(_.showDbg).mkString(", ")})",
476471
post = (r: Split) => s"expandMatch >>> ${r.showDbg}"
477472
):
478-
val clsTrm = elaborator.cls(ctor, inAppPrefix = false)
473+
val clsTrm = elaborator.cls(term(ctor), inAppPrefix = false)
479474
clsTrm.symbol.flatMap(_.asClsLike) match
480475
case S(cls: ClassSymbol) =>
481476
val paramSymbols = cls.defn match
@@ -584,10 +579,10 @@ class Desugarer(val elaborator: Elaborator)
584579
Branch(ref, Pattern.Lit(IntLit(-value)), sequel(ctx)) ~: fallback
585580
case App(Ident("-"), Tup(DecLit(value) :: Nil)) => fallback => ctx =>
586581
Branch(ref, Pattern.Lit(DecLit(-value)), sequel(ctx)) ~: fallback
587-
case App(Ident("&"), Tree.Tup(lhs :: rhs :: Nil)) => fallback => ctx =>
582+
case OpApp(lhs, Ident("&"), rhs :: Nil) => fallback => ctx =>
588583
val newSequel = expandMatch(scrutSymbol, rhs, sequel)(fallback)
589584
expandMatch(scrutSymbol, lhs, newSequel)(fallback)(ctx)
590-
case App(Ident("|"), Tree.Tup(lhs :: rhs :: Nil)) => fallback => ctx =>
585+
case OpApp(lhs, Ident("|"), rhs :: Nil) => fallback => ctx =>
591586
val newFallback = expandMatch(scrutSymbol, rhs, sequel)(fallback)(ctx)
592587
expandMatch(scrutSymbol, lhs, sequel)(newFallback)(ctx)
593588
// A single constructor pattern.
@@ -598,6 +593,9 @@ class Desugarer(val elaborator: Elaborator)
598593
dealWithAppCtorCase(app, ctor, args, false)
599594
case app @ App(ctor: Ctor, Tup(args)) =>
600595
dealWithAppCtorCase(app, ctor, args, false)
596+
case app @ OpApp(lhs, ctor: Ctor, rhss) =>
597+
// TODO improve (eventually remove DummyApp)
598+
dealWithAppCtorCase(Tree.DummyApp, ctor, lhs :: rhss, false)
601599
// A single literal pattern
602600
case literal: Literal => fallback => ctx => trace(
603601
pre = s"expandMatch: literal <<< $literal",

hkmc2/shared/src/main/scala/hkmc2/semantics/ucs/HelperExtractors.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ object HelperExtractors:
1010
/** A helper extractor for matching the tree of `x | y`. */
1111
object or:
1212
infix def unapply(tree: Tree): Opt[(Tree, Tree)] = tree match
13-
case App(Ident("|"), Tup(lhs :: rhs :: Nil)) => S(lhs, rhs)
13+
case OpApp(lhs, Ident("|"), rhs :: Nil) => S(lhs, rhs)
1414
case _ => N
1515

1616
/** A helper extractor for matching the tree of `x a y`.*/
@@ -24,11 +24,11 @@ object HelperExtractors:
2424
*/
2525
object to:
2626
infix def unapply(tree: Tree): Opt[(Tree, (Bool, Tree))] = tree match
27-
case App(Ident("..="), Tup(lhs :: rhs :: Nil)) => S(lhs, (true, rhs))
28-
case App(Ident("..<"), Tup(lhs :: rhs :: Nil)) => S(lhs, (false, rhs))
27+
case OpApp(lhs, Ident("..="), rhs :: Nil) => S(lhs, (true, rhs))
28+
case OpApp(lhs, Ident("..<"), rhs :: Nil) => S(lhs, (false, rhs))
2929
case _ => N
3030

3131
object `~`:
3232
infix def unapply(tree: Tree): Opt[(Tree, Tree)] = tree match
33-
case App(Ident("~"), Tup(lhs :: rhs :: Nil)) => S(lhs, rhs)
33+
case OpApp(lhs, Ident("~"), rhs :: Nil) => S(lhs, rhs)
3434
case _ => N

0 commit comments

Comments
 (0)