Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions effekt/shared/src/main/scala/effekt/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,15 @@ object Namer extends Phase[Parsed, NameResolved] {
patterns.flatMap { resolve }
case source.MultiPattern(patterns, _) =>
patterns.flatMap { resolve }
case source.OrPattern(patterns, _) =>
patterns.foreach { p =>
val bindings = resolve(p)
if (bindings.nonEmpty) {
val bs = bindings.map { b => pretty"`${b.name}`" }.mkString(", ")
Context.error(pretty"Pattern ${p} binds ${bs}, but or-patterns should not bind values.")
}
}
Nil
}

def resolve(p: source.MatchGuard)(using Context): Unit = p match {
Expand Down
22 changes: 18 additions & 4 deletions effekt/shared/src/main/scala/effekt/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -910,7 +910,7 @@ class Parser(positions: Positions, tokens: Seq[Token], source: Source) {

def matchClause(): MatchClause =
nonterminal:
val patterns = `case` ~> some(matchPattern, `,`)
val patterns = `case` ~> some(orPattern, `,`)
val pattern: MatchPattern = patterns match {
case Many(List(pat), _) => pat
case pats => MultiPattern(pats.unspan, pats.span)
Expand All @@ -930,25 +930,39 @@ class Parser(positions: Positions, tokens: Seq[Token], source: Source) {

def matchGuard(): MatchGuard =
nonterminal:
expr() ~ when(`is`) { Some(matchPattern()) } { None } match {
expr() ~ when(`is`) { Some(orPattern()) } { None } match {
case e ~ Some(p) => MatchGuard.PatternGuard(e, p, span())
case e ~ None => MatchGuard.BooleanGuard(e, span())
}

// TODO check positions and potentially change syntax
def orPattern(): MatchPattern =
nonterminal:
val subpatterns = ListBuffer.empty[MatchPattern]
subpatterns.addOne(matchPattern())
while (peek.kind == TokenKind.`|`) {
next()
subpatterns.addOne(matchPattern())
}
subpatterns.toList match {
case p :: Nil => p
case ps => OrPattern(ps, span())
}

def matchPattern(): MatchPattern =
nonterminal:
peek.kind match {
case `__` => skip(); IgnorePattern(span())
case _ if isVariable =>
idRef() match {
case id if peek(`(`) => TagPattern(id, many(matchPattern, `(`, `,`, `)`).unspan, span())
case id if peek(`(`) => TagPattern(id, many(orPattern, `(`, `,`, `)`).unspan, span())
case IdRef(Nil, name, span) => AnyPattern(IdDef(name, span), span)
case IdRef(_, name, _) => fail("Cannot use qualified names to bind a pattern variable")
}
case _ if isVariable =>
AnyPattern(idDef(), span())
case _ if isLiteral => LiteralPattern(literal(), span())
case `(` => some(matchPattern, `(`, `,`, `)`) match {
case `(` => some(orPattern, `(`, `,`, `)`) match {
case Many(p :: Nil , _) => fail("Pattern matching on tuples requires more than one element")
case Many(ps, _) => TagPattern(IdRef(List("effekt"), s"Tuple${ps.size}", span().synthesized), ps, span())
}
Expand Down
5 changes: 5 additions & 0 deletions effekt/shared/src/main/scala/effekt/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -609,8 +609,13 @@ object Typer extends Phase[NameResolved, Typechecked] {
}

bindings

case source.MultiPattern(patterns, _) =>
Context.panic("Multi-pattern should have been split at the match and not occur nested.")

case source.OrPattern(patterns, _) =>
patterns.foreach { p => checkPattern(sc, p) }
Map.empty
} match { case res => Context.annotateInferredType(pattern, sc); res }

def checkGuard(guard: MatchGuard)(using Context, Captures): Result[Map[Symbol, ValueType]] = guard match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,17 @@ object PatternMatchingCompiler {
core.Match(scrutinee, branches, default)
}

// (3c) Split or-patterns
def splitOnOr(ps: List[Pattern]) =
val Clause(Condition.Patterns(patterns) :: rest, target, targs, args) = headClause : @unchecked
compile(ps.map { p =>
Clause(Condition.Patterns(patterns + (scrutinee -> p)) :: rest, target, targs, args)
} ++ remainingClauses)

patterns(scrutinee) match {
case Pattern.Literal(lit, equals) => splitOnLiteral(lit, equals)
case p: Pattern.Tag => splitOnTag()
case Pattern.Or(ps) => splitOnOr(ps)
case _ => ???
}
}
Expand Down Expand Up @@ -278,9 +286,11 @@ object PatternMatchingCompiler {
case Condition.Patterns(other) :: rest =>
val substituted = other.map(substitute)
val additionalSubst = substituted.collect { case (sc, Pattern.Any(id)) => id -> sc }
val filtered = substituted.collect {
case (sc, p: Pattern.Tag) => sc -> p
case (sc, p: Pattern.Literal) => sc -> p
val filtered = substituted.flatMap {
case (sc, p: Pattern.Tag) => Some(sc -> p)
case (sc, p: Pattern.Literal) => Some(sc -> p)
case (sc, p: Pattern.Or) => Some(sc -> p)
case (sc, Pattern.Ignore() | Pattern.Any(_)) => None
}
normalize(patterns ++ filtered, rest, substitution ++ additionalSubst)

Expand Down
4 changes: 4 additions & 0 deletions effekt/shared/src/main/scala/effekt/core/Transformer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,7 @@ object Transformer extends Phase[Typechecked, CoreTransformed] {
case source.TagPattern(id, patterns, _) => patterns.flatMap(boundInPattern)
case _: source.LiteralPattern | _: source.IgnorePattern => Nil
case source.MultiPattern(patterns, _) => patterns.flatMap(boundInPattern)
case source.OrPattern(patterns, _) => Nil
}
def boundInGuard(g: source.MatchGuard): List[core.ValueParam] = g match {
case MatchGuard.BooleanGuard(condition, _) => Nil
Expand All @@ -680,6 +681,7 @@ object Transformer extends Phase[Typechecked, CoreTransformed] {
case p @ source.TagPattern(id, patterns, _) => Context.annotation(Annotations.TypeParameters, p) ++ patterns.flatMap(boundTypesInPattern)
case _: source.LiteralPattern | _: source.IgnorePattern => Nil
case source.MultiPattern(patterns, _) => patterns.flatMap(boundTypesInPattern)
case source.OrPattern(patterns, _) => Nil
}
def boundTypesInGuard(g: source.MatchGuard): List[Id] = g match {
case MatchGuard.BooleanGuard(condition, _) => Nil
Expand Down Expand Up @@ -718,6 +720,8 @@ object Transformer extends Phase[Typechecked, CoreTransformed] {
Pattern.Literal(Literal(value, transform(tpe)), equalsFor(tpe))
case source.MultiPattern(patterns, _) =>
Context.panic("Multi-pattern should have been split on toplevel / nested MultiPattern")
case source.OrPattern(pattern, _) =>
Pattern.Or(pattern.map(transformPattern))
}

def transformGuard(p: source.MatchGuard): List[Condition] =
Expand Down
9 changes: 8 additions & 1 deletion effekt/shared/src/main/scala/effekt/source/Tree.scala
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,14 @@ enum MatchPattern extends Tree {
*
* Currently should *only* occur in lambda-cases during Parsing
*/
case MultiPattern(patterns: List[MatchPattern], span: Span) extends MatchPattern
case MultiPattern(patterns: List[MatchPattern], span: Span)

/**
* A pattern with different alternatives that share the right and side
*
* case "a" | "b" => ...
*/
case OrPattern(patterns: List[MatchPattern], span: Span)
}
export MatchPattern.*

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ object ExhaustivityChecker {
case LiteralPattern(lit, _) => Pattern.Literal(lit.value, lit.tpe)
case MultiPattern(patterns, _) =>
Context.panic("Multi-pattern should have been split in preprocess already / nested MultiPattern")
case OrPattern(patterns, _) => Pattern.Any() // TODO
}
def preprocessGuard(g: source.MatchGuard)(using Context): Condition = g match {
case MatchGuard.BooleanGuard(condition, _) =>
Expand Down