Skip to content

Commit

Permalink
Parse and desugar tuple patterns with ...
Browse files Browse the repository at this point in the history
  • Loading branch information
chengluyu committed Nov 13, 2024
1 parent cfb4e76 commit 251d1c1
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 9 deletions.
4 changes: 1 addition & 3 deletions hkmc2/shared/src/main/scala/hkmc2/semantics/Desugarer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -410,9 +410,7 @@ class Desugarer(tl: TraceLogger, elaborator: Elaborator)
// 2. A variable number of middle patterns indicated by `..`.
// 3. A fixed number of trailing patterns.
val (lead, rest) = args.foldLeft[(Ls[Tree], Opt[(Opt[Tree], Ls[Tree])])]((Nil, N)):
case ((lead, N), Jux(Ident(".."), pat)) => (lead, S((S(pat), Nil)))
case ((lead, N), App(Ident(".."), TyTup(tys))) => (lead, S((S(Tup(tys)), Nil)))
case ((lead, N), Ident("..")) => (lead, S((N, Nil)))
case ((lead, N), Spread(_, _, patOpt)) => (lead, S((patOpt, Nil)))
case ((lead, N), pat) => (lead :+ pat, N)
case ((lead, S((rest, last))), pat) => (lead, S((rest, last :+ pat)))
// Some helper functions. TODO: deduplicate
Expand Down
4 changes: 4 additions & 0 deletions hkmc2/shared/src/main/scala/hkmc2/syntax/Keyword.scala
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ object Keyword:
val ascPrec = nextPrec // * `x => x : T` should parsed as `x => (x : T)`
val `=` = Keyword("=", eqPrec, eqPrec)
val `:` = Keyword(":", ascPrec, eqPrec)
val `..` = Keyword("..", N, N)
val `...` = Keyword("...", N, N)
// val `;` = Keyword(";", ascPrec, eqPrec)

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

type Infix = `and`.type | `or`.type | `then`.type | `else`.type | `is`.type | `:`.type | `->`.type |
`=>`.type | `extends`.type | `restricts`.type | `as`.type

type Ellipsis = `...`.type | `..`.type

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

Expand Down
10 changes: 6 additions & 4 deletions hkmc2/shared/src/main/scala/hkmc2/syntax/Lexer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,8 @@ class Lexer(origin: Origin, dbg: Bool)(using raise: Raise):
def loc(start: Int, end: Int): Loc = Loc(start, end, origin)

def mkSymIdent(nme: Str) = nme match
case "..." => SUSPENSION
case ".." => SUSPENSION(false)
case "..." => SUSPENSION(true)
case _ => IDENT(nme, true)

@tailrec final
Expand Down Expand Up @@ -369,9 +370,9 @@ class Lexer(origin: Origin, dbg: Bool)(using raise: Raise):
import BracketKind._
def go(toks: Ls[Token -> Loc], canStartAngles: Bool, stack: Ls[BracketKind -> Loc -> Ls[Stroken -> Loc]], acc: Ls[Stroken -> Loc]): Ls[Stroken -> Loc] =
toks match
case (SUSPENSION, l0) :: Nil =>
case (SUSPENSION(true), l0) :: Nil =>
go(OPEN_BRACKET(Indent) -> l0 :: LITVAL(Tree.UnitLit(true)) -> l0 :: Nil, false, stack, acc)
case (SUSPENSION, l0) :: (NEWLINE, l1) :: rest =>
case (SUSPENSION(true), l0) :: (NEWLINE, l1) :: rest =>
go(OPEN_BRACKET(Indent) -> (l0 ++ l1) :: rest, false, stack, acc)
case (QUOTE, l0) :: (IDENT("<", true), l1) :: rest =>
go(rest, false, stack, (IDENT("<", true), l1) :: (QUOTE, l0) :: acc)
Expand Down Expand Up @@ -526,7 +527,8 @@ object Lexer:
case (BRACKETS(k, contents), _) =>
k.beg + printTokens(contents) + k.end
case (COMMENT(text: String), _) => "/*" + text + "*/"
case (SUSPENSION, _) => "..."
case (SUSPENSION(true), _) => "..."
case (SUSPENSION(false), _) => ".."
def printTokens(ts: Ls[TokLoc]): Str =
ts.iterator.map(printToken).mkString("|", "|", "|")

Expand Down
7 changes: 7 additions & 0 deletions hkmc2/shared/src/main/scala/hkmc2/syntax/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import scala.annotation.tailrec
import Keyword.`let`
import hkmc2.syntax.ParseRule.prefixRules
import hkmc2.syntax.ParseRule.infixRules
import hkmc2.syntax.Keyword.Ellipsis


object Parser:
Expand Down Expand Up @@ -563,6 +564,12 @@ abstract class Parser(
case (BRACKETS(Indent | Curly, _), loc) :: _ =>
err((msg"Expected an expression; found block instead" -> lastLoc :: Nil))
errExpr
case (SUSPENSION(dotDotDot), loc) :: _ =>
consume
val bod = yeetSpaces match
case Nil | (COMMA, _) :: _ => N
case _ => S(simpleExprImpl(prec))
Spread(if dotDotDot then Keyword.`...` else Keyword.`..`, S(loc), bod)
case (tok, loc) :: _ =>
TODO(tok)
case Nil =>
Expand Down
5 changes: 3 additions & 2 deletions hkmc2/shared/src/main/scala/hkmc2/syntax/Token.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ sealed abstract class Token:
case BRACKETS(BracketKind.Indent, contents) => s"indented block"
case BRACKETS(k, contents) => s"${k.name} section"
case COMMENT(text) => "comment"
case SUSPENSION => "'...' suspension"
case SUSPENSION(true) => "'...' ellipsis"
case SUSPENSION(false) => "'..' ellipsis"

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


sealed abstract class BracketKind:
Expand Down
3 changes: 3 additions & 0 deletions hkmc2/shared/src/main/scala/hkmc2/syntax/Tree.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ enum Tree extends AutoLocated:
case Region(name: Tree, body: Tree)
case RegRef(reg: Tree, value: Tree)
case Effectful(eff: Tree, body: Tree)
case Spread(kw: Keyword.Ellipsis, kwLoc: Opt[Loc], body: Opt[Tree])

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

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

def showDbg: Str = toString // TODO

Expand Down
127 changes: 127 additions & 0 deletions hkmc2/shared/src/test/mlscript/ucs/syntax/TupleRest.mls
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
:parseOnly
:pt

fun f(xs) = if xs is
[..xs] then 0
[...xs] then 1
[..[]] then 2
[...[]] then 3
[..Cons(x, xs)] then 4
[...Cons(x, xs)] then 5
[..] then 6
[...] then 7
[.., x] then 8
[..., x] then 9
[... , x] then 10
//│ Parsed tree:
//│ TermDef:
//│ k = Fun
//│ head = App:
//│ lhs = Ident of "f"
//│ rhs = Tup of Ls of
//│ Ident of "xs"
//│ rhs = S of IfLike:
//│ kw = keyword 'if'
//│ split = InfixApp:
//│ lhs = Ident of "xs"
//│ kw = keyword 'is'
//│ rhs = Block of Ls of
//│ InfixApp:
//│ lhs = Tup of Ls of
//│ Spread:
//│ kw = keyword '..'
//│ kwLoc = S of Loc at :2:4-2:6
//│ body = S of Ident of "xs"
//│ kw = keyword 'then'
//│ rhs = IntLit of 0
//│ InfixApp:
//│ lhs = Tup of Ls of
//│ Spread:
//│ kw = keyword '...'
//│ kwLoc = S of Loc at :3:4-3:7
//│ body = S of Ident of "xs"
//│ kw = keyword 'then'
//│ rhs = IntLit of 1
//│ InfixApp:
//│ lhs = Tup of Ls of
//│ Spread:
//│ kw = keyword '..'
//│ kwLoc = S of Loc at :4:4-4:6
//│ body = S of Tup of Nil
//│ kw = keyword 'then'
//│ rhs = IntLit of 2
//│ InfixApp:
//│ lhs = Tup of Ls of
//│ Spread:
//│ kw = keyword '...'
//│ kwLoc = S of Loc at :5:4-5:7
//│ body = S of Tup of Nil
//│ kw = keyword 'then'
//│ rhs = IntLit of 3
//│ InfixApp:
//│ lhs = Tup of Ls of
//│ Spread:
//│ kw = keyword '..'
//│ kwLoc = S of Loc at :6:4-6:6
//│ body = S of App:
//│ lhs = Ident of "Cons"
//│ rhs = Tup of Ls of
//│ Ident of "x"
//│ Ident of "xs"
//│ kw = keyword 'then'
//│ rhs = IntLit of 4
//│ InfixApp:
//│ lhs = Tup of Ls of
//│ Spread:
//│ kw = keyword '...'
//│ kwLoc = S of Loc at :7:4-7:7
//│ body = S of App:
//│ lhs = Ident of "Cons"
//│ rhs = Tup of Ls of
//│ Ident of "x"
//│ Ident of "xs"
//│ kw = keyword 'then'
//│ rhs = IntLit of 5
//│ InfixApp:
//│ lhs = Tup of Ls of
//│ Spread:
//│ kw = keyword '..'
//│ kwLoc = S of Loc at :8:4-8:6
//│ body = N
//│ kw = keyword 'then'
//│ rhs = IntLit of 6
//│ InfixApp:
//│ lhs = Tup of Ls of
//│ Spread:
//│ kw = keyword '...'
//│ kwLoc = S of Loc at :9:4-9:7
//│ body = N
//│ kw = keyword 'then'
//│ rhs = IntLit of 7
//│ InfixApp:
//│ lhs = Tup of Ls of
//│ Spread:
//│ kw = keyword '..'
//│ kwLoc = S of Loc at :10:4-10:6
//│ body = N
//│ Ident of "x"
//│ kw = keyword 'then'
//│ rhs = IntLit of 8
//│ InfixApp:
//│ lhs = Tup of Ls of
//│ Spread:
//│ kw = keyword '...'
//│ kwLoc = S of Loc at :11:4-11:7
//│ body = N
//│ Ident of "x"
//│ kw = keyword 'then'
//│ rhs = IntLit of 9
//│ InfixApp:
//│ lhs = Tup of Ls of
//│ Spread:
//│ kw = keyword '...'
//│ kwLoc = S of Loc at :12:4-12:7
//│ body = N
//│ Ident of "x"
//│ kw = keyword 'then'
//│ rhs = IntLit of 10

0 comments on commit 251d1c1

Please sign in to comment.