Skip to content

The Ultimate Pattern Syntax #316

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 66 commits into from
Aug 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
5a5d806
wip new Pattern enum
Chronarak Jun 10, 2025
d5c78ca
wip pattern compiler
Chronarak Jun 10, 2025
7dd1a61
Rename flat patterns
chengluyu Jun 11, 2025
a13f880
Reintroduce patterns
chengluyu Jun 15, 2025
7ac28c7
Fix pattern examples
chengluyu Jun 17, 2025
abc5926
Support top-level pattern bindings
chengluyu Jun 18, 2025
c9711ff
Elaborate chain patterns
chengluyu Jun 18, 2025
d00e7c9
Renovate `Translator`
chengluyu Jun 19, 2025
11a2195
Major update on translation and add many tests
chengluyu Jun 24, 2025
6ca78ca
Implement pattern compilation
chengluyu Jun 29, 2025
b19fe31
Revise the wording
chengluyu Jun 30, 2025
e5c2438
Add a blank line at the end of the file
chengluyu Jun 30, 2025
89269e3
Delete the comment that was accidentally committed
chengluyu Jun 30, 2025
b392c4a
Improve `pluralize`
chengluyu Jun 30, 2025
e3d0fdd
Remove a `:todo` flag
chengluyu Jun 30, 2025
bcac1bd
Add examples of DNF and CNF
chengluyu Jun 30, 2025
575bdcf
Further improve `pluralize`
chengluyu Jun 30, 2025
78ea61c
Delete a self-talking comment
chengluyu Jun 30, 2025
1dfe444
Merge branch 'origin/hkmc2'
chengluyu Aug 3, 2025
163f091
Remove useless classes from `Term.mls`
chengluyu Aug 3, 2025
837994c
Rename `Translator` as `NaiveCompiler`
chengluyu Aug 3, 2025
d6137a6
Fix desugaring derp
LPTK Aug 4, 2025
b22f737
Merge branch 'hkmc2'
chengluyu Aug 6, 2025
e569640
Very minor cleanups
chengluyu Aug 6, 2025
e027628
Remove old naive pattern compilation completely
chengluyu Aug 8, 2025
f209f8f
Call `NaiveCompiler` from `Lowering`
chengluyu Aug 8, 2025
16cce58
Move `NaiveCompiler` into the `ups` package
chengluyu Aug 8, 2025
e530991
Merge the immutability update on branch 'hkmc2'
chengluyu Aug 8, 2025
22f70b9
Group tests about string patterns
chengluyu Aug 8, 2025
e1b66ae
Fix string patterns and add tests
chengluyu Aug 8, 2025
bdbad7c
Merge branch 'hkmc2' into ups
LPTK Aug 8, 2025
b873b2c
Clean up the pattern compiler
chengluyu Aug 8, 2025
6cee1c3
Fix Unicode characters
chengluyu Aug 8, 2025
850d874
Add spaces on both sides of the `::` operator
chengluyu Aug 8, 2025
e8c6c44
Correct the wording in an error message
chengluyu Aug 8, 2025
406637b
Correct the grammar in a comment
chengluyu Aug 8, 2025
4a7639c
Amend test changes
chengluyu Aug 8, 2025
20467e5
Remove a use of default arguments
chengluyu Aug 8, 2025
0196de5
Stop compiling pattern arguments in the `Desugarer`
chengluyu Aug 8, 2025
ab8596b
Do no generate objects for direct use of patterns
chengluyu Aug 8, 2025
39d8046
Pattern definitions do not need parameter lists
chengluyu Aug 8, 2025
41f42c1
Correct the use of top and bottom
chengluyu Aug 8, 2025
513b1ea
Correct the grammar in a comment
chengluyu Aug 8, 2025
6c61134
Add a space between two imported names
chengluyu Aug 9, 2025
3346a6d
Correct the grammar in a comment
chengluyu Aug 10, 2025
41ca4cd
Replace the Unicode escapes with actual characters
chengluyu Aug 10, 2025
125b128
Encapsulate pretty printing of splits better
chengluyu Aug 10, 2025
52775c5
Revert an outdated change
chengluyu Aug 10, 2025
7ae33a7
Revert the error message for unrecognized patterns
chengluyu Aug 10, 2025
d708760
Remove an outdated test command
chengluyu Aug 10, 2025
5d14750
Fix the separation of test blocks
chengluyu Aug 10, 2025
7a52f7b
Do no use `showDbg` in error messages
chengluyu Aug 11, 2025
6f6abda
Break the rendering output into multiple lines
chengluyu Aug 11, 2025
9f4e6d9
Better document the function `tuple` in `Elaborator.pattern`
chengluyu Aug 11, 2025
dc2e9de
Amend test changes caused by previous changes
chengluyu Aug 11, 2025
08036b9
Improve backlog tests
chengluyu Aug 11, 2025
883f40b
Do not make terms with implicit args before resolution
chengluyu Aug 11, 2025
d7c9b89
Revert newlines
chengluyu Aug 11, 2025
bc0f600
Improve the check for `isMlsFun`
chengluyu Aug 11, 2025
df53e8e
Hide a long-winded test output
chengluyu Aug 12, 2025
62ca882
Improve an error message
chengluyu Aug 12, 2025
2b722c9
Document a test which should be reported
chengluyu Aug 12, 2025
4143501
Remove an outdated comment
chengluyu Aug 12, 2025
cc7cb36
Add a test to show the difference of two rendering modes
chengluyu Aug 12, 2025
e0e6d1f
Rename the test folder rp to ups
chengluyu Aug 12, 2025
7390402
Revert needless change
LPTK Aug 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions hkmc2/shared/src/main/scala/hkmc2/Message.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ object Message:
sealed abstract class Bit
final case class Text(str: Str) extends Bit
final case class Code(ty: TypeLike) extends Bit
implicit def fromType(ty: TypeLike): Message = Message(Code(ty)::Nil)
implicit def fromStr(str: Str): Message = Message(Text(str)::Nil)
implicit def fromType(ty: TypeLike): Message = Message(Code(ty) :: Nil)
implicit def fromStr(str: Str): Message = Message(Text(str) :: Nil)
implicit def fromInt(int: Int): Message = Message(Text(int.toString) :: Nil)

implicit class MessageContext(private val ctx: StringContext):
def msg(inserted: Message*): Message =
Expand Down
8 changes: 4 additions & 4 deletions hkmc2/shared/src/main/scala/hkmc2/bbml/bbML.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import mlscript.utils.*, shorthands.*
import utils.*

import Message.MessageContext
import semantics.*, semantics.Term.*
import semantics.*, Term.*, ucs.FlatPattern
import Elaborator.Ctx
import syntax.*
import Tree.*
Expand Down Expand Up @@ -258,7 +258,7 @@ class BBTyper(using elState: Elaborator.State, tl: TL):
val res = freshVar(new TempSymbol(S(blk), "ctx"))(using ctx)
constrain(bodyCtx, sk | res)
(bodyTy, rhsCtx | res, rhsEff | bodyEff)
case Term.IfLike(Keyword.`if`, Split.Let(_, cond, Split.Cons(Branch(_, Pattern.Lit(BoolLit(true)), Split.Else(cons)), Split.Else(alts)))) =>
case Term.IfLike(Keyword.`if`, Split.Let(_, cond, Split.Cons(Branch(_, FlatPattern.Lit(BoolLit(true)), Split.Else(cons)), Split.Else(alts)))) =>
val (condTy, condCtx, condEff) = typeCode(cond)
val (consTy, consCtx, consEff) = typeCode(cons)
val (altsTy, altsCtx, altsEff) = typeCode(alts)
Expand Down Expand Up @@ -295,7 +295,7 @@ class BBTyper(using elState: Elaborator.State, tl: TL):
val nestCtx1 = ctx.nest
val nestCtx2 = ctx.nest
val patTy = pattern match
case pat: Pattern.ClassLike =>
case pat: FlatPattern.ClassLike =>
pat.constructor.symbol.flatMap(_.asCls) match
case S(sym) =>
val (clsTy, tv, emptyTy) = sym.defn.map(sym -> _) match
Expand All @@ -313,7 +313,7 @@ class BBTyper(using elState: Elaborator.State, tl: TL):
case N =>
error(msg"Not a valid class: ${pat.constructor.describe}" -> pat.constructor.toLoc :: Nil)
Bot
case Pattern.Lit(lit) => lit match
case FlatPattern.Lit(lit) => lit match
case _: Tree.BoolLit => BbCtx.boolTy
case _: Tree.IntLit => BbCtx.intTy
case _: Tree.DecLit => BbCtx.numTy
Expand Down
39 changes: 29 additions & 10 deletions hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import utils.*

import hkmc2.Message.MessageContext

import semantics.*
import semantics.*, ucs.FlatPattern
import hkmc2.{semantics => sem}
import semantics.{Term => st}
import semantics.Term.{Throw => _, *}
Expand Down Expand Up @@ -184,7 +184,9 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
blockImpl(stats, res)(k)
case cls: ClassLikeDef =>
reportAnnotations(cls, cls.extraAnnotations)
val (mtds, publicFlds, privateFlds, ctor) = gatherMembers(cls.body)
val (mtds, publicFlds, privateFlds, ctor) = cls match
case pd: PatternDef => compilePatternMethods(pd)
case _ => gatherMembers(cls.body)
cls.ext match
case N =>
Define(ClsLikeDefn(cls.owner, cls.sym, cls.bsym, cls.kind, cls.paramsOpt, cls.auxParams, N,
Expand Down Expand Up @@ -341,7 +343,9 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
case _: sem.BuiltinSymbol => true
case sym: sem.BlockMemberSymbol =>
sym.trmImplTree.fold(sym.clsTree.isDefined)(_.k is syntax.Fun)
case _ => false
// Do not perform safety check on `MatchResult` and `MatchFailure`.
case sym => (sym is State.matchResultClsSymbol) ||
(sym is State.matchFailureClsSymbol)
def conclude(fr: Path) =
arg match
case Tup(fs) =>
Expand Down Expand Up @@ -506,8 +510,8 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
End()
)
pat match
case Pattern.Lit(lit) => mkMatch(Case.Lit(lit) -> go(tail, topLevel = false))
case Pattern.ClassLike(ctor, argsOpt, _mode, _refined) =>
case FlatPattern.Lit(lit) => mkMatch(Case.Lit(lit) -> go(tail, topLevel = false))
case FlatPattern.ClassLike(ctor, argsOpt, _mode, _refined) =>
/** Make a continuation that creates the match. */
def k(ctorSym: ClassLikeSymbol, clsParams: Ls[TermSymbol])(st: Path): Block =
val args = argsOpt.map(_.map(_.scrutinee)).getOrElse(Nil)
Expand Down Expand Up @@ -537,8 +541,8 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
// resolves to a class or module. Branches with unresolved
// constructors should have been removed.
lastWords("Pattern.ClassLike: constructor is neither a class nor a module")
case Pattern.Tuple(len, inf) => mkMatch(Case.Tup(len, inf) -> go(tail, topLevel = false))
case Pattern.Record(entries) =>
case FlatPattern.Tuple(len, inf) => mkMatch(Case.Tup(len, inf) -> go(tail, topLevel = false))
case FlatPattern.Record(entries) =>
val objectSym = ctx.builtins.Object
mkMatch( // checking that we have an object
Case.Cls(objectSym, Value.Ref(BuiltinSymbol(objectSym.nme, false, false, true, false))),
Expand All @@ -565,7 +569,7 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
val normalized = tl.scoped("ucs:normalize"):
normalize(iftrm.desugared)
tl.scoped("ucs:normalized"):
tl.log(s"Normalized:\n${Split.display(normalized)}")
tl.log(s"Normalized:\n${normalized.prettyPrint}")

if k.isInstanceOf[TailOp] && isIf then go(normalized, topLevel = true)
else
Expand Down Expand Up @@ -683,8 +687,8 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
def setupSymbol(symbol: Local)(k: Result => Block)(using Subst): Block =
k(Instantiate(mut = false, Value.Ref(State.termSymbol).selSN("Symbol"), Value.Lit(Tree.StrLit(symbol.nme)) :: Nil))

def quotePattern(p: Pattern)(k: Result => Block)(using Subst): Block = p match
case Pattern.Lit(lit) => setupTerm("LitPattern", Value.Lit(lit) :: Nil)(k)
def quotePattern(p: FlatPattern)(k: Result => Block)(using Subst): Block = p match
case FlatPattern.Lit(lit) => setupTerm("LitPattern", Value.Lit(lit) :: Nil)(k)
case _ => // TODO
fail:
ErrorReport(
Expand Down Expand Up @@ -819,6 +823,21 @@ class Lowering()(using Config, TL, Raise, State, Ctx):
case t => t
(mtds, publicFlds, privateFlds, ctor)

/** Compile the pattern definition into `unapply` and `unapplyStringPrefix`
* methods using the `NaiveCompiler`, which transliterate the pattern into
* UCS splits that backtrack without any optimizations. */
def compilePatternMethods(defn: PatternDef)(using Subst):
// The return type is intended to be consistent with `gatherMembers`
(Ls[FunDefn], Ls[BlockMemberSymbol -> TermSymbol], Ls[TermSymbol], Block) =
val compiler = new ups.NaiveCompiler
val methods = compiler.compilePattern(defn)
val mtds = methods
.flatMap: td =>
td.body.map: bod =>
val (paramLists, bodyBlock) = setupFunctionDef(td.params, bod, S(td.sym.nme))
FunDefn(td.owner, td.sym, paramLists, bodyBlock)
(mtds, Nil, Nil, End())

def args(elems: Ls[Elem])(k: Ls[Arg] => Block)(using Subst): Block =
val as = elems.map:
case sem.Fld(sem.FldFlags.benign(), value, N) => R(N -> value)
Expand Down
2 changes: 2 additions & 0 deletions hkmc2/shared/src/main/scala/hkmc2/codegen/js/JSBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ class JSBuilder(using TL, State, Ctx) extends CodeBuilder:
then doc"$runtimeVar.Tuple.lazyConcat(${es.map(argument).mkDocument(doc", ")})"
else doc"[ #{ # ${es.map(argument).mkDocument(doc", # ")} #} # ]"
if mut then inner else s"$freeze(${inner})"
case Value.Rcd(mut, Nil) =>
if mut then "{}" else s"$freeze({})"
case Value.Rcd(mut, flds) =>
val inner = doc"{ # #{ ${
flds.map:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ trait BlockImpl(using Elaborator.State):
val desugStmts =
def desug(stmts: Ls[Tree]): Ls[Tree] =
stmts match
case PossiblyAnnotated(anns, syntax.Desugared(td: TypeDef)) :: stmts =>
case syntax.Desugared(PossiblyAnnotated(anns, td: TypeDef)) :: stmts =>
val ctors = td.withPart.toList.flatMap:
case Block(sts) => sts.flatMap:
case Constructor(Block(ctors)) => ctors
Expand Down
Loading
Loading