Skip to content
Open
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
96 changes: 74 additions & 22 deletions effekt/shared/src/main/scala/effekt/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -270,40 +270,92 @@ class Parser(tokens: Seq[Token], source: Source) {
else ExprStmt(e, stmts(inBraces), span())
}) labelled "statements"

// ATTENTION: here the grammar changed (we added `with val` to disambiguate)
// with val <ID> (: <TYPE>)? = <EXPR>; <STMTS>
// with val (<ID> (: <TYPE>)?...) = <EXPR>
// with val <PATTERN> (, <PATTERN>)* = <EXPR>; <STMTS>
// with def <BLOCKPARAM> = <EXPR>; <STMTS>
// with <EXPR>; <STMTS>
def withStmt(inBraces: Boolean): Stmt = `with` ~> peek.kind match {
case `val` =>
val params = (`val` ~> peek.kind match {
case `(` => valueParamsOpt()
case _ => List(valueParamOpt()) // TODO copy position
})
desugarWith(params, Nil, `=` ~> expr(), semi() ~> stmts(inBraces), span())
consume(`val`)
val patterns = some(matchPattern, `,`)
val call = `=` ~> expr()
val body = semi() ~> stmts(inBraces)
desugarWithPatterns(patterns, call, body, span())

case `def` =>
val params = (`def` ~> peek.kind match {
case `{` => blockParamsOpt()
case _ => List(blockParamOpt()) // TODO copy position
})
desugarWith(Nil, params, `=` ~> expr(), semi() ~> stmts(inBraces), span())
val call = `=` ~> expr()
val body = semi() ~> stmts(inBraces)
val blockLit: BlockLiteral = BlockLiteral(Nil, Nil, params, body, body.span.synthesized)
desugarWith(call, blockLit, span())

case _ =>
val call = expr()
val body = semi() ~> stmts(inBraces)
val blockLit: BlockLiteral = BlockLiteral(Nil, Nil, Nil, body, body.span.synthesized)
desugarWith(call, blockLit, span())
}

// Desugar `with val` with pattern(s)
def desugarWithPatterns(patterns: Many[MatchPattern], call: Term, body: Stmt, withSpan: Span): Stmt = {
// Check if all patterns are simple variable bindings or ignored
val allSimpleVars = patterns.unspan.forall {
case AnyPattern(_, _) => true
case IgnorePattern(_) => true
case _ => false
}

val blockLit: BlockLiteral = if (allSimpleVars) {
// Simple case: all patterns are just variable names (or ignored)
// Desugar to: call { (x, y, _, ...) => body }
val vparams: List[ValueParam] = patterns.unspan.map {
case AnyPattern(id, span) => ValueParam(id, None, span)
case IgnorePattern(span) => ValueParam(IdDef(s"__ignored", span.synthesized), None, span)
case _ => sys.error("impossible: checked above")
}
BlockLiteral(Nil, vparams, Nil, body, body.span.synthesized)
} else {
// Complex case: at least one pattern needs matching
// Desugar to: call { case pat1, pat2, ... => body }
// This requires one argument per pattern, matching against multiple scrutinees
val patternList = patterns.unspan
val names = List.tabulate(patternList.length) { n => s"__withArg${n}" }
val argSpans = patternList.map(_.span)

val vparams: List[ValueParam] = names.zip(argSpans).map { (name, span) =>
ValueParam(IdDef(name, span.synthesized), None, span.synthesized)
}
val scrutinees = names.zip(argSpans).map { (name, span) =>
Var(IdRef(Nil, name, span.synthesized), span.synthesized)
}

val pattern: MatchPattern = patterns match {
case Many(List(single), _) => single
case Many(ps, span) => MultiPattern(ps, span)
}

val clause = MatchClause(pattern, Nil, body, Span(source, pattern.span.from, body.span.to, Synthesized))
val matchExpr = Match(scrutinees, List(clause), None, withSpan.synthesized)
val matchBody = Return(matchExpr, withSpan.synthesized)
BlockLiteral(Nil, vparams, Nil, matchBody, withSpan.synthesized)
}

case _ => desugarWith(Nil, Nil, expr(), semi() ~> stmts(inBraces), span())
desugarWith(call, blockLit, withSpan)
}

def desugarWith(vparams: List[ValueParam], bparams: List[BlockParam], call: Term, body: Stmt, withSpan: Span): Stmt = call match {
case m@MethodCall(receiver, id, tps, vargs, bargs, callSpan) =>
Return(MethodCall(receiver, id, tps, vargs, bargs :+ (BlockLiteral(Nil, vparams, bparams, body, body.span.synthesized)), callSpan), withSpan.synthesized)
case c@Call(callee, tps, vargs, bargs, callSpan) =>
Return(Call(callee, tps, vargs, bargs :+ (BlockLiteral(Nil, vparams, bparams, body, body.span.synthesized)), callSpan), withSpan.synthesized)
case Var(id, varSpan) =>
val tgt = IdTarget(id)
Return(Call(tgt, Nil, Nil, (BlockLiteral(Nil, vparams, bparams, body, body.span.synthesized)) :: Nil, varSpan), withSpan.synthesized)
case Do(id, targs, vargs, bargs, doSpan) =>
Return(Do(id, targs, vargs, bargs :+ BlockLiteral(Nil, vparams, bparams, body, body.span.synthesized), doSpan), withSpan.synthesized)
case term =>
Return(Call(ExprTarget(term), Nil, Nil, (BlockLiteral(Nil, vparams, bparams, body, body.span.synthesized)) :: Nil, term.span.synthesized), withSpan.synthesized)
def desugarWith(call: Term, blockLit: BlockLiteral, withSpan: Span): Stmt = call match {
case MethodCall(receiver, id, tps, vargs, bargs, callSpan) =>
Return(MethodCall(receiver, id, tps, vargs, bargs :+ blockLit, callSpan), withSpan.synthesized)
case Call(callee, tps, vargs, bargs, callSpan) =>
Return(Call(callee, tps, vargs, bargs :+ blockLit, callSpan), withSpan.synthesized)
case Var(id, varSpan) =>
Return(Call(IdTarget(id), Nil, Nil, blockLit :: Nil, varSpan), withSpan.synthesized)
case Do(id, targs, vargs, bargs, doSpan) =>
Return(Do(id, targs, vargs, bargs :+ blockLit, doSpan), withSpan.synthesized)
case term =>
Return(Call(ExprTarget(term), Nil, Nil, blockLit :: Nil, term.span.synthesized), withSpan.synthesized)
}

def maybeSemi(): Unit = if isSemi then semi()
Expand Down
2 changes: 1 addition & 1 deletion effekt/shared/src/main/scala/effekt/source/Tree.scala
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ enum MatchPattern extends Tree {
*
* case a, b => ...
*
* Currently should *only* occur in lambda-cases during Parsing
* Currently should *only* occur in lambda-cases & `with` statements during parsing
*/
case MultiPattern(patterns: List[MatchPattern], span: Span) extends MatchPattern
}
Expand Down
1 change: 1 addition & 0 deletions examples/pos/with_val_many_args.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
42, 100; a
9 changes: 9 additions & 0 deletions examples/pos/with_val_many_args.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module with_val_many_args

def callback { f: (Int, Int, String, Char) => Unit }: Unit =
f(42, 100, "hello", 'a')

def main() = {
with val i, j, _, c = callback;
println(show(i) ++ ", " ++ show(j) ++ "; " ++ show(c))
}
4 changes: 4 additions & 0 deletions examples/pos/with_val_many_ignored_args.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
before
called!
called!
after
13 changes: 13 additions & 0 deletions examples/pos/with_val_many_ignored_args.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module with_val_many_ignored_args

def callback { f: (Int, Int, String, Char) => Unit }: Unit = {
println("before")
f(42, 100, "hello", 'a')
f(0, 0, "", '0')
println("after")
}

def main() = {
with val _, _, _, _ = callback;
println("called!")
}
4 changes: 4 additions & 0 deletions examples/pos/with_val_pair.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
0: a
1: b
2: c
3: d
6 changes: 6 additions & 0 deletions examples/pos/with_val_pair.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module with_val_pair

def main() = {
with val (i, x) = [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')].foreach;
println(show(i) ++ ": " ++ show(x))
}
3 changes: 3 additions & 0 deletions examples/pos/with_val_pair_complex.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
1=1: a
2=2: b
3=3: c
7 changes: 7 additions & 0 deletions examples/pos/with_val_pair_complex.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module with_val_pair_complex

record Pos(i: Int, x: Char)
def main() = {
with val (i, Pos(j, x)) = [(1, Pos(1, 'a')), (2, Pos(2, 'b')), (3, Pos(3, 'c'))].foreach;
println(show(i) ++ "=" ++ show(j) ++ ": " ++ show(x))
}
4 changes: 4 additions & 0 deletions examples/pos/with_val_pair_explicit.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
0: a
1: b
2: c
3: d
6 changes: 6 additions & 0 deletions examples/pos/with_val_pair_explicit.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module with_val_pair_explicit

def main() = {
with val Tuple2(i, x) = [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')].foreach;
println(show(i) ++ ": " ++ show(x))
}
4 changes: 4 additions & 0 deletions examples/pos/with_val_two_args.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
0: a
1: b
2: c
3: d
6 changes: 6 additions & 0 deletions examples/pos/with_val_two_args.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module with_val_two_args

def main() = {
with val i, x = ['a', 'b', 'c', 'd'].foreachIndex;
println(show(i) ++ ": " ++ show(x))
}
3 changes: 3 additions & 0 deletions examples/pos/with_val_two_args_complex.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
0: a
1: b
2: c
7 changes: 7 additions & 0 deletions examples/pos/with_val_two_args_complex.effekt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module with_val_two_args_complex

record Pos(i: Int, x: Char)
def main() = {
with val i, Pos(j, x) = [Pos(1, 'a'), Pos(2, 'b'), Pos(3, 'c')].foreachIndex;
println(show(i) ++ ": " ++ show(x))
}
2 changes: 1 addition & 1 deletion examples/pos/withdefstatement.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def fresh[T](init: Int) { prog: {Cell} => T }: T = {
}

def main() = {
with def target = fresh(0)
with def {target} = fresh(0)
with def c1 = fresh(0)
with def c2 = fresh(0)
c1.set(1);
Expand Down
4 changes: 2 additions & 2 deletions examples/pos/withstatement.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ def bar { f: (Int, String) => Unit / {}}: Unit =

def user(): Unit = {
with printer;
with val x: Int = foreach([1,2,3]);
with val (a, b) = bar;
with val x = foreach([1,2,3]);
with val a, b = bar;
Comment on lines -14 to +15
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change ^ as type annotations are no longer supported and multiple arguments have to be written without the parenthesis.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to support else clauses for exhaustive pattern matching?

Like:

with val Some(v) = foo() else do raise()
...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a reason why not :)
In an ideal case, I'd like to share as much parser code as possible with destructuring ala val (x, Some(y)) = ... (and with lambda-case)

println(show(x) ++ b);
val z = {
val x = 2;
Expand Down