Skip to content

Commit 92ddab6

Browse files
dvdvgtb-studios
andauthored
Parse capture annotation syntax for (extern) function definitions (#1109)
Resolves #1106 ``` extern? <CAPTURES> def ... : ... = ... ``` becomes ``` extern? def ... at <CAPTURES> : ... = ... ``` or to say it in terms of a regex ``` /(^( *extern) *(\w+|\{ *((\w+ *, *)| *\w+ *)* *\}) *)(?=def )(.*)(: *.*)$/gm $2 $6 at $3$7 ``` --------- Co-authored-by: Jonathan Brachthäuser <[email protected]>
1 parent 6205934 commit 92ddab6

File tree

92 files changed

+479
-402
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+479
-402
lines changed

effekt/js/src/test/scala/effekt/WebTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ object WebTests extends TestSuite {
4343
*/
4444
def evaluate[A](includes: List[String], content: String): A =
4545
run(includes,
46-
s"""|extern io def withResult[A](value: A): Unit = js"(function() { module.exports.result = $${value} })()"
46+
s"""|extern def withResult[A](value: A) at io: Unit = js"(function() { module.exports.result = $${value} })()"
4747
|def main() = { withResult(${content}) }
4848
|""".stripMargin)
4949
js.Dynamic.global.globalThis.module.`exports`.result.asInstanceOf[A]

effekt/jvm/src/main/scala/effekt/Repl.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ class Repl(driver: Driver) extends REPL[Tree, EffektConfig, EffektError] {
321321
val fakeSpan = Span(source, 0, 0, origin = Origin.Synthesized)
322322
val fullSpan = Span(source, 0, source.content.length, origin = Origin.Synthesized)
323323
ModuleDecl("interactive", includes,
324-
definitions :+ FunDef(IdDef(defName, fakeSpan), Many.empty(fakeSpan), Many.empty(fakeSpan), Many.empty(fakeSpan), Maybe.None(fakeSpan),
324+
definitions :+ FunDef(IdDef(defName, fakeSpan), Many.empty(fakeSpan), Many.empty(fakeSpan), Many.empty(fakeSpan), Maybe.None(fakeSpan), Maybe.None(fakeSpan),
325325
body, Info.empty(fakeSpan), fullSpan), None, fullSpan)
326326
}
327327

effekt/jvm/src/test/scala/effekt/LSPTests.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,10 @@ class LSPTests extends FunSuite {
13001300
| None(),
13011301
| Span(StringSource(def main() = <>, file://test.effekt), 10, 10, Real())
13021302
| ),
1303+
| Maybe(
1304+
| None(),
1305+
| Span(StringSource(def main() = <>, file://test.effekt), 10, 10, Real())
1306+
| ),
13031307
| Return(
13041308
| Hole(
13051309
| IdDef(
@@ -1325,8 +1329,7 @@ class LSPTests extends FunSuite {
13251329
| Maybe(
13261330
| None(),
13271331
| Span(StringSource(def main() = <>, file://test.effekt), 0, 0, Real())
1328-
| ),
1329-
| None()
1332+
| )
13301333
| ),
13311334
| Span(StringSource(def main() = <>, file://test.effekt), 0, 15, Real())
13321335
| )

effekt/jvm/src/test/scala/effekt/ParserTests.scala

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ class ParserTests extends munit.FunSuite {
145145
parse(input, _.externDef())
146146

147147
def parseInfo(input: String)(using munit.Location): Info =
148-
parse(input, _.info(parseCaptures = true))
148+
parse(input, _.info())
149149

150150
// Custom asserts
151151
//
@@ -545,6 +545,7 @@ class ParserTests extends munit.FunSuite {
545545
parseStmts("val (left, right) = list; return left")
546546

547547
parseStmts("val g: () => Unit / Exc at {exc} = fun() { closure() }; ()")
548+
parseStmts("val g: () => Unit / Exc at exc = fun() { closure() }; ()")
548549
}
549550

550551
test("Pattern-matching val parses with correct span") {
@@ -982,6 +983,7 @@ class ParserTests extends munit.FunSuite {
982983
DefDef(
983984
IdDef("foo", Span(source, pos(0), pos(1))),
984985
Maybe.None(Span(source, pos(1), pos(1))),
986+
Maybe.None(Span(source, pos(1), pos(1))),
985987
Var(IdRef(Nil, "f", Span(source, pos(2), pos(3))), Span(source, pos(2), pos(3))),
986988
Info.empty(Span(source, 0, 0)),
987989
Span(source, 0, pos.last)))
@@ -1013,6 +1015,12 @@ class ParserTests extends munit.FunSuite {
10131015
)
10141016
}
10151017

1018+
test("Function definition with capture set") {
1019+
parseDefinition("def foo(v: Int) at {}: Unit = <>")
1020+
parseDefinition("def foo(v: Int) at io: Unit = <>")
1021+
parseDefinition("def foo(v: Int) at {async, io}: Unit = <>")
1022+
}
1023+
10161024
test("Function definition"){
10171025
val (source, pos) =
10181026
raw"""def foo[T1, T2](x: T1, y: T2){b: => Unit}: Unit = <>
@@ -1022,7 +1030,7 @@ class ParserTests extends munit.FunSuite {
10221030
val definition = parseDefinition(source.content)
10231031

10241032
val funDef = definition match {
1025-
case fd@FunDef(id, tparams, vparams, bparams, ret, body, doc, span) => fd
1033+
case fd@FunDef(id, tparams, vparams, bparams, captures, ret, body, doc, span) => fd
10261034
case other =>
10271035
throw new IllegalArgumentException(s"Expected FunDef but got ${other.getClass.getSimpleName}")
10281036
}
@@ -1041,7 +1049,7 @@ class ParserTests extends munit.FunSuite {
10411049
val definition = parseDefinition(source.content)
10421050

10431051
val funDef = definition match {
1044-
case fd@FunDef(id, tparams, vparams, bparams, ret, body, doc, span) => fd
1052+
case fd@FunDef(id, tparams, vparams, bparams, captures, ret, body, doc, span) => fd
10451053
case other =>
10461054
throw new IllegalArgumentException(s"Expected FunDef but got ${other.getClass.getSimpleName}")
10471055
}
@@ -1058,7 +1066,7 @@ class ParserTests extends munit.FunSuite {
10581066
val definition = parseDefinition(source.content)
10591067

10601068
val funDef = definition match {
1061-
case fd@FunDef(id, tparams, vparams, bparams, ret, body, doc, span) => fd
1069+
case fd@FunDef(id, tparams, vparams, bparams, captures, ret, body, doc, span) => fd
10621070
case other =>
10631071
throw new IllegalArgumentException(s"Expected FunDef but got ${other.getClass.getSimpleName}")
10641072
}
@@ -1086,7 +1094,7 @@ class ParserTests extends munit.FunSuite {
10861094
val definition = parseDefinition(source.content)
10871095

10881096
val funDef = definition match {
1089-
case fd@FunDef(id, tparams, vparams, bparams, ret, body, doc, span) => fd
1097+
case fd@FunDef(id, tparams, vparams, bparams, captures, ret, body, doc, span) => fd
10901098
case other =>
10911099
throw new IllegalArgumentException(s"Expected FunDef but got ${other.getClass.getSimpleName}")
10921100
}
@@ -1130,45 +1138,41 @@ class ParserTests extends munit.FunSuite {
11301138
assertEquals(valDef.span, span)
11311139
}
11321140

1133-
test("Declaration info with capture set") {
1141+
test("Declaration info with comment and extern") {
11341142
val (source, pos) =
11351143
raw"""/// Some doc comment
1136-
|private extern {a, b, c}
1137-
|↑ ↑↑ ↑↑ ↑
1144+
|private extern
1145+
|↑ ↑↑ ↑
11381146
|""".sourceAndPositions
11391147

11401148
parseInfo(source.content) match {
11411149
case Info(
11421150
Some(doc),
11431151
isPrivate,
11441152
isExtern,
1145-
Some(CaptureSet(captures, Span(_, captFrom, captTo, _)))) =>
1153+
) =>
11461154

11471155
assertEquals(doc, " Some doc comment")
11481156
assertEquals(isPrivate, Maybe(Some(()), Span(source, pos(0), pos(1))))
11491157
assertEquals(isExtern, Maybe(Some(()), Span(source, pos(2), pos(3))))
1150-
assertEquals(captures.map(_.name), List("a", "b", "c"))
1151-
assertEquals(captFrom, pos(4))
1152-
assertEquals(captTo, pos(5))
11531158

11541159
case info => fail(s"Wrong info: ${info}")
11551160
}
11561161
}
11571162

1158-
test("Only private") {
1163+
test("Only comment and private") {
11591164
val (source, pos) =
11601165
raw"""/// Some doc comment
11611166
|private
11621167
|↑ ↑
11631168
|""".sourceAndPositions
11641169

11651170
parseInfo(source.content) match {
1166-
case Info(doc, isPrivate, isExtern, externCapture) =>
1171+
case Info(doc, isPrivate, isExtern) =>
11671172

11681173
assertEquals(doc, Some(" Some doc comment"))
11691174
assertEquals(isPrivate, Maybe(Some(()), Span(source, pos(0), pos(1))))
11701175
assertEquals(isExtern, Maybe(None, Span(source, pos(1), pos(1))))
1171-
assertEquals(externCapture, None)
11721176
}
11731177
}
11741178

@@ -1179,11 +1183,10 @@ class ParserTests extends munit.FunSuite {
11791183
|""".sourceAndPositions
11801184

11811185
parseInfo(source.content) match {
1182-
case Info(doc, isPrivate, isExtern, externCapture) =>
1186+
case Info(doc, isPrivate, isExtern) =>
11831187
assertEquals(doc, Some(" Some doc comment"))
11841188
assertEquals(isPrivate, Maybe(None, Span(source, pos(0), pos(0))))
11851189
assertEquals(isExtern, Maybe(None, Span(source, pos(0), pos(0))))
1186-
assertEquals(externCapture, None)
11871190
}
11881191
}
11891192

@@ -1587,7 +1590,7 @@ class ParserTests extends munit.FunSuite {
15871590
}
15881591

15891592
test("Extern definition") {
1590-
parseExternDef("extern {io} def read(s: String): Int = default { 42 } js { 1 + 1 } chez { 42 }")
1593+
parseExternDef("extern def read(s: String) at {io}: Int = default { 42 } js { 1 + 1 } chez { 42 }")
15911594
parseExternDef("extern \"console.log(42)\"")
15921595
parseExternDef("extern \"\"\"console.log(42)\"\"\"")
15931596
parseExternDef("extern type Complex")
@@ -1596,7 +1599,7 @@ class ParserTests extends munit.FunSuite {
15961599
parseExternDef("extern resource withFile: [A](String) { () => A } => A")
15971600
parseExternDef("extern include \"path/to/file\"")
15981601
parseExternDef("extern js \"\"\"console.log(42)\"\"\"")
1599-
parseExternDef("extern pure def read(s: String): String = default { s }")
1602+
parseExternDef("extern def read(s: String) at {}: String = default { s }")
16001603
parseExternDef("extern def read(s: String): String = \"${s}\"")
16011604
parseProgram(
16021605
"extern def println(value: String): Unit =" +

effekt/shared/src/main/scala/effekt/Intelligence.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ trait Intelligence {
127127
p.productElementNames.zip(p.productIterator)
128128
.collectFirst {
129129
case ("doc", Some(s: String)) => s
130-
case ("info", source.Info(Some(s: String), _, _, _)) => s
130+
case ("info", source.Info(Some(s: String), _, _)) => s
131131
}
132132
case _ => None
133133
}

effekt/shared/src/main/scala/effekt/Lexer.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,6 @@ enum TokenKind {
141141
case `and`
142142
case `is`
143143
case `namespace`
144-
case `pure`
145144

146145
case `module`
147146
case `import`
@@ -180,7 +179,7 @@ object TokenKind {
180179
`let`, `true`, `false`, `val`, `var`, `if`, `else`, `while`, `type`, `effect`, `interface`,
181180
`try`, `with`, `case`, `do`, `fun`, `match`, `def`, `module`, `import`, `export`, `extern`,
182181
`include`, `record`, `box`, `unbox`, `return`, `region`, `resource`, `new`, `and`, `is`,
183-
`namespace`, `pure`, `private`
182+
`namespace`, `private`
184183
)
185184

186185
val keywordMap: immutable.HashMap[String, TokenKind] =

effekt/shared/src/main/scala/effekt/Namer.scala

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -135,17 +135,18 @@ object Namer extends Phase[Parsed, NameResolved] {
135135
}
136136

137137
// allow recursive definitions of objects
138-
case d @ source.DefDef(id, annot, source.New(source.Implementation(interface, clauses, _), _), doc, span) =>
138+
case d @ source.DefDef(id, captures, annot, source.New(source.Implementation(interface, clauses, _), _), doc, span) =>
139139
val tpe = Context.at(interface) { resolveBlockRef(interface) }
140-
val sym = Binder.DefBinder(Context.nameFor(id), Some(tpe), d)
140+
val cpts = captures.unspan.map { resolve }
141+
val sym = Binder.DefBinder(Context.nameFor(id), cpts, Some(tpe), d)
141142
Context.define(id, sym)
142143

143-
case d @ source.DefDef(id, annot, block, doc, span) =>
144+
case d @ source.DefDef(id, captures, annot, block, doc, span) =>
144145
()
145146

146-
case f @ source.FunDef(id, tparams, vparams, bparams, annot, body, doc, span) =>
147+
case f @ source.FunDef(id, tparams, vparams, bparams, captures, annot, body, doc, span) =>
147148
val uniqueId = Context.nameFor(id)
148-
149+
val cpts = captures.map { resolve }.unspan
149150
// we create a new scope, since resolving type params introduces them in this scope
150151
val sym = Context scoped {
151152
val tps = tparams map resolve
@@ -158,7 +159,7 @@ object Namer extends Phase[Parsed, NameResolved] {
158159
Context.bindBlocks(bps)
159160
annot map resolve
160161
}
161-
UserFunction(uniqueId, tps.unspan, vps.unspan, bps.unspan, ret.unspan.map { _._1 }, ret.unspan.map { _._2 }, f)
162+
UserFunction(uniqueId, tps.unspan, vps.unspan, bps.unspan, cpts, ret.unspan.map { _._1 }, ret.unspan.map { _._2 }, f)
162163
}
163164
Context.define(id, sym)
164165

@@ -219,9 +220,9 @@ object Namer extends Phase[Parsed, NameResolved] {
219220
ExternInterface(Context.nameFor(id), tps, decl)
220221
})
221222

222-
case d @ source.ExternDef(capture, id, tparams, vparams, bparams, ret, bodies, doc, span) => {
223+
case d @ source.ExternDef(id, tparams, vparams, bparams, captures, ret, bodies, doc, span) => {
223224
val name = Context.nameFor(id)
224-
val capt = resolve(capture)
225+
val capt = resolve(captures)
225226
Context.define(id, Context scoped {
226227
val tps = tparams map resolve
227228
val vps = vparams map resolve
@@ -314,16 +315,17 @@ object Namer extends Phase[Parsed, NameResolved] {
314315
Context.define(id, sym)
315316

316317
// already has been preresolved (to enable recursive definitions)
317-
case d @ source.DefDef(id, annot, source.New(impl, _), doc, span) =>
318+
case d @ source.DefDef(id, captures, annot, source.New(impl, _), doc, span) =>
318319
resolve(impl)
319320

320-
case d @ source.DefDef(id, annot, binding, doc, span) =>
321+
case d @ source.DefDef(id, captures, annot, binding, doc, span) =>
321322
val tpe = annot.map(resolveBlockType)
322323
resolve(binding)
323-
Context.define(id, DefBinder(Context.nameFor(id), tpe.unspan, d))
324+
val cpts = captures.unspan.map { resolve }
325+
Context.define(id, DefBinder(Context.nameFor(id), cpts, tpe.unspan, d))
324326

325327
// FunDef and InterfaceDef have already been resolved as part of the module declaration
326-
case f @ source.FunDef(id, tparams, vparams, bparams, ret, body, doc, span) =>
328+
case f @ source.FunDef(id, tparams, vparams, bparams, captures, ret, body, doc, span) =>
327329
val sym = f.symbol
328330
Context.scopedWithName(id.name) {
329331
sym.tparams.foreach { p => Context.bind(p) }
@@ -333,7 +335,7 @@ object Namer extends Phase[Parsed, NameResolved] {
333335
resolve(body)
334336
}
335337

336-
case f @ source.ExternDef(capture, id, tparams, vparams, bparams, ret, bodies, doc, span) =>
338+
case f @ source.ExternDef(id, tparams, vparams, bparams, captures, ret, bodies, doc, span) =>
337339
val sym = f.symbol
338340
Context.scopedWithName(id.name) {
339341
sym.tparams.foreach { p => Context.bind(p) }

0 commit comments

Comments
 (0)