mirror of
https://github.com/apple/pkl.git
synced 2026-05-03 13:44:26 +02:00
Overhaul implementation of for-generators (#844)
Motivation:
* fix known bugs and limitations of for-generators
* improve code health by removing complex workarounds
Changes:
* simplify AstBuilder code related to for-generators
* track for-generators via `SymbolTable.enterForGenerator()`
* add `RestoreForBindingsNode` during initial AST construction
instead of calling `MemberNode.replaceBody()` later on
* simplify some unnecessarily complex code
* remove workarounds and band-aids such as:
* `isInIterable`
* `executeAndSetEagerly`
* adding dummy slots in `AmendFunctionNode`
* overhaul implementation of for-generators
* store keys and values of for-generator iterations in regular instead of auxiliary frame slots
* set them via `TypeNode.executeAndSet()`
* `ResolveVariableNode` no longer needs to search auxiliary slots
* `Read(Enclosing)AuxiliarySlot` is no longer needed
* at the start of each for-generator iteration, create a new `VirtualFrame`
that is a copy of the current frame (arguments + slots)
and stores the iteration key and value in additional slots.
* execute for-generator iteration with the newly created frame
* `childNode.execute(newFrame)`
* Pkl objects created during the iteration will materialize this frame
* store newly created frames in `owner.extraStorage`
if their for-generator slots may be accessed when a generated member is executed
* resolving variable names to for-generator variables at parse time would make this analysis more precise
* when a generated member is executed,
* retrieve the corresponding frame stored in `owner.extraStorage`
* copy the retrieved frame's for-generator slots into slots of the current frame
Result:
* for-generators are implemented in a correct, reasonably simple, and reasonably efficient way
* complexity is fully contained within package `generator` and `AstBuilder`
* for-generator keys and values can be accessed from all nested scopes:
* key and value expressions of generated members
* condition expressions of nested when-generators
* iterable expressions of nested for-generators
* for-generator keys and values can be accessed from within objects created by the expressions listed above
* sibling for-generators can use the same key/value variable names
* parent/child for-generators can use the same key/value variable names
* fixes https://github.com/apple/pkl/issues/741
This commit is contained in:
@@ -1,11 +0,0 @@
|
||||
a = List(1, 2, 3, 4)
|
||||
|
||||
b = List("a", "b", "c", "d")
|
||||
|
||||
foo {
|
||||
for (_dup, i in a) {
|
||||
for (_dup, j in b) {
|
||||
i + j
|
||||
}
|
||||
}
|
||||
}
|
||||
18
pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorInFunctionBody.pkl
vendored
Normal file
18
pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorInFunctionBody.pkl
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
function myMethod(arg) = new {
|
||||
arg = "property" // same name as method arg
|
||||
for (key, value in List("one", "two", arg)) { // `arg` resolves to method arg
|
||||
[Pair(arg, key)] = // `arg` resolves to method arg
|
||||
Pair(arg, value) // `arg` resolves to object property
|
||||
}
|
||||
}
|
||||
|
||||
local myLambda = (arg) -> new Dynamic {
|
||||
arg = "property" // same name as lambda arg
|
||||
for (key, value in List("one", "two", arg)) { // `arg` resolves to lambda arg
|
||||
[Pair(arg, key)] = // `arg` resolves to lambda arg
|
||||
Pair(arg, value) // `arg` resolves to object property
|
||||
}
|
||||
}
|
||||
|
||||
res1 = myMethod("three")
|
||||
res2 = myLambda.apply("three")
|
||||
25
pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorNestedReference2.pkl
vendored
Normal file
25
pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorNestedReference2.pkl
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// https://github.com/apple/pkl/issues/741
|
||||
|
||||
bar = new {}
|
||||
|
||||
res1 {
|
||||
for (i in List(1)) {
|
||||
...(bar) {
|
||||
baz {
|
||||
new { i }
|
||||
}
|
||||
}.baz
|
||||
}
|
||||
}
|
||||
|
||||
res2 {
|
||||
for (i in List(1)) {
|
||||
for (elem in (bar) {
|
||||
baz {
|
||||
new { i }
|
||||
}
|
||||
}.baz) {
|
||||
elem
|
||||
}
|
||||
}
|
||||
}
|
||||
33
pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorVariableShadowing.pkl
vendored
Normal file
33
pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorVariableShadowing.pkl
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
examples {
|
||||
local a = List("1", "2", "3", "4")
|
||||
local b = List("a", "b", "c", "d")
|
||||
|
||||
["shadow key variable"] {
|
||||
new {
|
||||
for (key, outerValue in a) {
|
||||
for (key, innerValue in b) {
|
||||
List(outerValue, key, innerValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
["shadow value variable"] {
|
||||
new {
|
||||
for (outerKey, value in a) {
|
||||
for (innerKey, value in b) {
|
||||
List(outerKey, value, innerKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
["sibling for-generators can use same variable names"] {
|
||||
new {
|
||||
for (key, value in a) {
|
||||
List(key, value)
|
||||
}
|
||||
for (key, value in b) {
|
||||
List(key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
–– Pkl Error ––
|
||||
Duplicate definition of member `_dup`.
|
||||
|
||||
x | for (_dup, j in b) {
|
||||
^^^^
|
||||
at forGeneratorDuplicateParams2#foo (file:///$snippetsDir/input/errors/forGeneratorDuplicateParams2.pkl)
|
||||
12
pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorInFunctionBody.pcf
vendored
Normal file
12
pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorInFunctionBody.pcf
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
res1 {
|
||||
arg = "property"
|
||||
[Pair("three", 0)] = Pair("property", "one")
|
||||
[Pair("three", 1)] = Pair("property", "two")
|
||||
[Pair("three", 2)] = Pair("property", "three")
|
||||
}
|
||||
res2 {
|
||||
arg = "property"
|
||||
[Pair("three", 0)] = Pair("property", "one")
|
||||
[Pair("three", 1)] = Pair("property", "two")
|
||||
[Pair("three", 2)] = Pair("property", "three")
|
||||
}
|
||||
11
pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorNestedReference2.pcf
vendored
Normal file
11
pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorNestedReference2.pcf
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
bar {}
|
||||
res1 {
|
||||
new {
|
||||
1
|
||||
}
|
||||
}
|
||||
res2 {
|
||||
new {
|
||||
1
|
||||
}
|
||||
}
|
||||
54
pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorVariableShadowing.pcf
vendored
Normal file
54
pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorVariableShadowing.pcf
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
examples {
|
||||
["shadow key variable"] {
|
||||
new {
|
||||
List("1", 0, "a")
|
||||
List("1", 1, "b")
|
||||
List("1", 2, "c")
|
||||
List("1", 3, "d")
|
||||
List("2", 0, "a")
|
||||
List("2", 1, "b")
|
||||
List("2", 2, "c")
|
||||
List("2", 3, "d")
|
||||
List("3", 0, "a")
|
||||
List("3", 1, "b")
|
||||
List("3", 2, "c")
|
||||
List("3", 3, "d")
|
||||
List("4", 0, "a")
|
||||
List("4", 1, "b")
|
||||
List("4", 2, "c")
|
||||
List("4", 3, "d")
|
||||
}
|
||||
}
|
||||
["shadow value variable"] {
|
||||
new {
|
||||
List(0, "a", 0)
|
||||
List(0, "b", 1)
|
||||
List(0, "c", 2)
|
||||
List(0, "d", 3)
|
||||
List(1, "a", 0)
|
||||
List(1, "b", 1)
|
||||
List(1, "c", 2)
|
||||
List(1, "d", 3)
|
||||
List(2, "a", 0)
|
||||
List(2, "b", 1)
|
||||
List(2, "c", 2)
|
||||
List(2, "d", 3)
|
||||
List(3, "a", 0)
|
||||
List(3, "b", 1)
|
||||
List(3, "c", 2)
|
||||
List(3, "d", 3)
|
||||
}
|
||||
}
|
||||
["sibling for-generators can use same variable names"] {
|
||||
new {
|
||||
List(0, "1")
|
||||
List(1, "2")
|
||||
List(2, "3")
|
||||
List(3, "4")
|
||||
List(0, "a")
|
||||
List(1, "b")
|
||||
List(2, "c")
|
||||
List(3, "d")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user