Skip to content

Commit 251d1c1

Browse files
committed
Parse and desugar tuple patterns with ...
1 parent cfb4e76 commit 251d1c1

File tree

7 files changed

+151
-9
lines changed

7 files changed

+151
-9
lines changed

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -410,9 +410,7 @@ class Desugarer(tl: TraceLogger, elaborator: Elaborator)
410410
// 2. A variable number of middle patterns indicated by `..`.
411411
// 3. A fixed number of trailing patterns.
412412
val (lead, rest) = args.foldLeft[(Ls[Tree], Opt[(Opt[Tree], Ls[Tree])])]((Nil, N)):
413-
case ((lead, N), Jux(Ident(".."), pat)) => (lead, S((S(pat), Nil)))
414-
case ((lead, N), App(Ident(".."), TyTup(tys))) => (lead, S((S(Tup(tys)), Nil)))
415-
case ((lead, N), Ident("..")) => (lead, S((N, Nil)))
413+
case ((lead, N), Spread(_, _, patOpt)) => (lead, S((patOpt, Nil)))
416414
case ((lead, N), pat) => (lead :+ pat, N)
417415
case ((lead, S((rest, last))), pat) => (lead, S((rest, last :+ pat)))
418416
// Some helper functions. TODO: deduplicate

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ object Keyword:
5757
val ascPrec = nextPrec // * `x => x : T` should parsed as `x => (x : T)`
5858
val `=` = Keyword("=", eqPrec, eqPrec)
5959
val `:` = Keyword(":", ascPrec, eqPrec)
60+
val `..` = Keyword("..", N, N)
61+
val `...` = Keyword("...", N, N)
6062
// val `;` = Keyword(";", ascPrec, eqPrec)
6163

6264
val `if` = Keyword("if", N, nextPrec)
@@ -121,6 +123,8 @@ object Keyword:
121123

122124
type Infix = `and`.type | `or`.type | `then`.type | `else`.type | `is`.type | `:`.type | `->`.type |
123125
`=>`.type | `extends`.type | `restricts`.type | `as`.type
126+
127+
type Ellipsis = `...`.type | `..`.type
124128

125129
type letLike = `let`.type | `set`.type
126130

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,8 @@ class Lexer(origin: Origin, dbg: Bool)(using raise: Raise):
194194
def loc(start: Int, end: Int): Loc = Loc(start, end, origin)
195195

196196
def mkSymIdent(nme: Str) = nme match
197-
case "..." => SUSPENSION
197+
case ".." => SUSPENSION(false)
198+
case "..." => SUSPENSION(true)
198199
case _ => IDENT(nme, true)
199200

200201
@tailrec final
@@ -369,9 +370,9 @@ class Lexer(origin: Origin, dbg: Bool)(using raise: Raise):
369370
import BracketKind._
370371
def go(toks: Ls[Token -> Loc], canStartAngles: Bool, stack: Ls[BracketKind -> Loc -> Ls[Stroken -> Loc]], acc: Ls[Stroken -> Loc]): Ls[Stroken -> Loc] =
371372
toks match
372-
case (SUSPENSION, l0) :: Nil =>
373+
case (SUSPENSION(true), l0) :: Nil =>
373374
go(OPEN_BRACKET(Indent) -> l0 :: LITVAL(Tree.UnitLit(true)) -> l0 :: Nil, false, stack, acc)
374-
case (SUSPENSION, l0) :: (NEWLINE, l1) :: rest =>
375+
case (SUSPENSION(true), l0) :: (NEWLINE, l1) :: rest =>
375376
go(OPEN_BRACKET(Indent) -> (l0 ++ l1) :: rest, false, stack, acc)
376377
case (QUOTE, l0) :: (IDENT("<", true), l1) :: rest =>
377378
go(rest, false, stack, (IDENT("<", true), l1) :: (QUOTE, l0) :: acc)
@@ -526,7 +527,8 @@ object Lexer:
526527
case (BRACKETS(k, contents), _) =>
527528
k.beg + printTokens(contents) + k.end
528529
case (COMMENT(text: String), _) => "/*" + text + "*/"
529-
case (SUSPENSION, _) => "..."
530+
case (SUSPENSION(true), _) => "..."
531+
case (SUSPENSION(false), _) => ".."
530532
def printTokens(ts: Ls[TokLoc]): Str =
531533
ts.iterator.map(printToken).mkString("|", "|", "|")
532534

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import scala.annotation.tailrec
1414
import Keyword.`let`
1515
import hkmc2.syntax.ParseRule.prefixRules
1616
import hkmc2.syntax.ParseRule.infixRules
17+
import hkmc2.syntax.Keyword.Ellipsis
1718

1819

1920
object Parser:
@@ -563,6 +564,12 @@ abstract class Parser(
563564
case (BRACKETS(Indent | Curly, _), loc) :: _ =>
564565
err((msg"Expected an expression; found block instead" -> lastLoc :: Nil))
565566
errExpr
567+
case (SUSPENSION(dotDotDot), loc) :: _ =>
568+
consume
569+
val bod = yeetSpaces match
570+
case Nil | (COMMA, _) :: _ => N
571+
case _ => S(simpleExprImpl(prec))
572+
Spread(if dotDotDot then Keyword.`...` else Keyword.`..`, S(loc), bod)
566573
case (tok, loc) :: _ =>
567574
TODO(tok)
568575
case Nil =>

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ sealed abstract class Token:
2727
case BRACKETS(BracketKind.Indent, contents) => s"indented block"
2828
case BRACKETS(k, contents) => s"${k.name} section"
2929
case COMMENT(text) => "comment"
30-
case SUSPENSION => "'...' suspension"
30+
case SUSPENSION(true) => "'...' ellipsis"
31+
case SUSPENSION(false) => "'..' ellipsis"
3132

3233
/** Type of 'Structured Tokens' aka 'Strokens',
3334
* which use a `BRACKETS` construct instead of `OPEN_BRACKET`/`CLOSE_BRACKET` and `INDENT`/`DEINDENT` */
@@ -49,7 +50,7 @@ final case class OPEN_BRACKET(k: BracketKind) extends Token
4950
final case class CLOSE_BRACKET(k: BracketKind) extends Token
5051
final case class BRACKETS(k: BracketKind, contents: Ls[Stroken -> Loc])(val innerLoc: Loc) extends Token with Stroken
5152
final case class COMMENT(text: String) extends Token with Stroken
52-
object SUSPENSION extends Token with Stroken
53+
final case class SUSPENSION(dotDotDot: Bool) extends Token with Stroken
5354

5455

5556
sealed abstract class BracketKind:

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ enum Tree extends AutoLocated:
7171
case Region(name: Tree, body: Tree)
7272
case RegRef(reg: Tree, value: Tree)
7373
case Effectful(eff: Tree, body: Tree)
74+
case Spread(kw: Keyword.Ellipsis, kwLoc: Opt[Loc], body: Opt[Tree])
7475

7576
def children: Ls[Tree] = this match
7677
case _: Empty | _: Error | _: Ident | _: Literal => Nil
@@ -102,6 +103,7 @@ enum Tree extends AutoLocated:
102103
case Sel(prefix, name) => prefix :: Nil
103104
case Open(bod) => bod :: Nil
104105
case Def(lhs, rhs) => lhs :: rhs :: Nil
106+
case Spread(_, _, body) => body.toList
105107

106108
def describe: Str = this match
107109
case Empty() => "empty"
@@ -135,6 +137,7 @@ enum Tree extends AutoLocated:
135137
case Effectful(eff, body) => "effectful"
136138
case Handle(_, _, _, _) => "handle"
137139
case Def(lhs, rhs) => "defining assignment"
140+
case Spread(_, _, _) => "spread"
138141

139142
def showDbg: Str = toString // TODO
140143

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
:parseOnly
2+
:pt
3+
4+
fun f(xs) = if xs is
5+
[..xs] then 0
6+
[...xs] then 1
7+
[..[]] then 2
8+
[...[]] then 3
9+
[..Cons(x, xs)] then 4
10+
[...Cons(x, xs)] then 5
11+
[..] then 6
12+
[...] then 7
13+
[.., x] then 8
14+
[..., x] then 9
15+
[... , x] then 10
16+
//│ Parsed tree:
17+
//│ TermDef:
18+
//│ k = Fun
19+
//│ head = App:
20+
//│ lhs = Ident of "f"
21+
//│ rhs = Tup of Ls of
22+
//│ Ident of "xs"
23+
//│ rhs = S of IfLike:
24+
//│ kw = keyword 'if'
25+
//│ split = InfixApp:
26+
//│ lhs = Ident of "xs"
27+
//│ kw = keyword 'is'
28+
//│ rhs = Block of Ls of
29+
//│ InfixApp:
30+
//│ lhs = Tup of Ls of
31+
//│ Spread:
32+
//│ kw = keyword '..'
33+
//│ kwLoc = S of Loc at :2:4-2:6
34+
//│ body = S of Ident of "xs"
35+
//│ kw = keyword 'then'
36+
//│ rhs = IntLit of 0
37+
//│ InfixApp:
38+
//│ lhs = Tup of Ls of
39+
//│ Spread:
40+
//│ kw = keyword '...'
41+
//│ kwLoc = S of Loc at :3:4-3:7
42+
//│ body = S of Ident of "xs"
43+
//│ kw = keyword 'then'
44+
//│ rhs = IntLit of 1
45+
//│ InfixApp:
46+
//│ lhs = Tup of Ls of
47+
//│ Spread:
48+
//│ kw = keyword '..'
49+
//│ kwLoc = S of Loc at :4:4-4:6
50+
//│ body = S of Tup of Nil
51+
//│ kw = keyword 'then'
52+
//│ rhs = IntLit of 2
53+
//│ InfixApp:
54+
//│ lhs = Tup of Ls of
55+
//│ Spread:
56+
//│ kw = keyword '...'
57+
//│ kwLoc = S of Loc at :5:4-5:7
58+
//│ body = S of Tup of Nil
59+
//│ kw = keyword 'then'
60+
//│ rhs = IntLit of 3
61+
//│ InfixApp:
62+
//│ lhs = Tup of Ls of
63+
//│ Spread:
64+
//│ kw = keyword '..'
65+
//│ kwLoc = S of Loc at :6:4-6:6
66+
//│ body = S of App:
67+
//│ lhs = Ident of "Cons"
68+
//│ rhs = Tup of Ls of
69+
//│ Ident of "x"
70+
//│ Ident of "xs"
71+
//│ kw = keyword 'then'
72+
//│ rhs = IntLit of 4
73+
//│ InfixApp:
74+
//│ lhs = Tup of Ls of
75+
//│ Spread:
76+
//│ kw = keyword '...'
77+
//│ kwLoc = S of Loc at :7:4-7:7
78+
//│ body = S of App:
79+
//│ lhs = Ident of "Cons"
80+
//│ rhs = Tup of Ls of
81+
//│ Ident of "x"
82+
//│ Ident of "xs"
83+
//│ kw = keyword 'then'
84+
//│ rhs = IntLit of 5
85+
//│ InfixApp:
86+
//│ lhs = Tup of Ls of
87+
//│ Spread:
88+
//│ kw = keyword '..'
89+
//│ kwLoc = S of Loc at :8:4-8:6
90+
//│ body = N
91+
//│ kw = keyword 'then'
92+
//│ rhs = IntLit of 6
93+
//│ InfixApp:
94+
//│ lhs = Tup of Ls of
95+
//│ Spread:
96+
//│ kw = keyword '...'
97+
//│ kwLoc = S of Loc at :9:4-9:7
98+
//│ body = N
99+
//│ kw = keyword 'then'
100+
//│ rhs = IntLit of 7
101+
//│ InfixApp:
102+
//│ lhs = Tup of Ls of
103+
//│ Spread:
104+
//│ kw = keyword '..'
105+
//│ kwLoc = S of Loc at :10:4-10:6
106+
//│ body = N
107+
//│ Ident of "x"
108+
//│ kw = keyword 'then'
109+
//│ rhs = IntLit of 8
110+
//│ InfixApp:
111+
//│ lhs = Tup of Ls of
112+
//│ Spread:
113+
//│ kw = keyword '...'
114+
//│ kwLoc = S of Loc at :11:4-11:7
115+
//│ body = N
116+
//│ Ident of "x"
117+
//│ kw = keyword 'then'
118+
//│ rhs = IntLit of 9
119+
//│ InfixApp:
120+
//│ lhs = Tup of Ls of
121+
//│ Spread:
122+
//│ kw = keyword '...'
123+
//│ kwLoc = S of Loc at :12:4-12:7
124+
//│ body = N
125+
//│ Ident of "x"
126+
//│ kw = keyword 'then'
127+
//│ rhs = IntLit of 10

0 commit comments

Comments
 (0)