Skip to content

Commit c5941de

Browse files
committed
Resolve variables entirely within AstBuilder
1 parent 89d6bef commit c5941de

39 files changed

Lines changed: 1406 additions & 1814 deletions

docs/modules/style-guide/pages/index.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ Too many lines separate `foo` and `baz`.
193193
Properties that override an existing property shouldn't have doc comments nor type annotations,
194194
unless the type is intentionally overridden via `extends`.
195195

196-
[source%tested,{pkl}]
196+
[source%parsed,{pkl}]
197197
----
198198
amends "myOtherModule.pkl"
199199

pkl-core/pkl-core.gradle.kts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ dependencies {
5757

5858
add("generatorImplementation", libs.javaPoet)
5959
add("generatorImplementation", libs.truffleApi)
60+
add("generatorImplementation", projects.pklParser)
6061

6162
javaExecutableConfiguration(project(":pkl-cli", "javaExecutable"))
6263
}
@@ -124,6 +125,34 @@ tasks.test {
124125
maxHeapSize = "1g"
125126
}
126127

128+
val generateBaseModuleMemberRegistry by
129+
tasks.registering(JavaExec::class) {
130+
val outputDir = layout.buildDirectory.dir("generated/sources/baseModuleMembers")
131+
132+
val basePklFile = layout.projectDirectory.file("../stdlib/base.pkl")
133+
134+
inputs
135+
.file(basePklFile)
136+
.withPropertyName("basePkl")
137+
.withPathSensitivity(PathSensitivity.RELATIVE)
138+
139+
outputs.dir(outputDir)
140+
141+
classpath =
142+
generatorSourceSet.get().runtimeClasspath + tasks.processResources.get().outputs.files
143+
mainClass = "org.pkl.core.generator.BaseModuleMemberRegistryGenerator"
144+
145+
argumentProviders.add(
146+
CommandLineArgumentProvider {
147+
listOf(basePklFile.asFile.absolutePath, outputDir.get().asFile.absolutePath)
148+
}
149+
)
150+
}
151+
152+
sourceSets.main { java.srcDir(layout.buildDirectory.dir("generated/sources/baseModuleMembers")) }
153+
154+
tasks.compileJava { dependsOn(generateBaseModuleMemberRegistry) }
155+
127156
val testJavaExecutable by
128157
tasks.registering(Test::class) {
129158
configureExecutableTest("LanguageSnippetTestsEngine")
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.pkl.core.generator;
17+
18+
import com.palantir.javapoet.CodeBlock;
19+
import com.palantir.javapoet.JavaFile;
20+
import com.palantir.javapoet.MethodSpec;
21+
import com.palantir.javapoet.TypeName;
22+
import com.palantir.javapoet.TypeSpec;
23+
import java.io.IOException;
24+
import java.io.UncheckedIOException;
25+
import java.nio.file.Files;
26+
import java.nio.file.Path;
27+
import java.util.HashSet;
28+
import java.util.List;
29+
import java.util.Set;
30+
import javax.lang.model.element.Modifier;
31+
import org.pkl.parser.Parser;
32+
import org.pkl.parser.syntax.Modifier.ModifierValue;
33+
34+
public final class BaseModuleMemberRegistryGenerator {
35+
record Members(Set<String> properties, Set<String> methods) {}
36+
37+
public static void main(String[] args) {
38+
if (args.length < 2) {
39+
throw new IllegalArgumentException(
40+
"Usage: BaseModuleMemberRegistryGenerator <path-to-base.pkl> <output-dir>");
41+
}
42+
var members = buildMembers(args[0]);
43+
generateJavaCode(members, args[1]);
44+
}
45+
46+
private static void generateJavaCode(Members members, String outputDir) {
47+
var privateConstructor = MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE).build();
48+
49+
var hasPropertyMethod =
50+
buildHasMethod("hasProperty", members.properties().stream().sorted().toList());
51+
var hasMethodMethod = buildHasMethod("hasMethod", members.methods().stream().sorted().toList());
52+
53+
var classSpec =
54+
TypeSpec.classBuilder("BaseModuleMembers")
55+
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
56+
.addMethod(privateConstructor)
57+
.addMethod(hasPropertyMethod)
58+
.addMethod(hasMethodMethod)
59+
.build();
60+
61+
var javaFile =
62+
JavaFile.builder("org.pkl.core.runtime", classSpec)
63+
.addFileComment("DO NOT EDIT — generated by BaseModuleMemberRegistryGenerator")
64+
.build();
65+
66+
try {
67+
javaFile.writeTo(Path.of(outputDir));
68+
} catch (IOException e) {
69+
throw new UncheckedIOException(e);
70+
}
71+
}
72+
73+
private static MethodSpec buildHasMethod(String methodName, List<String> names) {
74+
var code = CodeBlock.builder();
75+
code.add("return switch (name) {\n");
76+
code.indent();
77+
code.add("case $S", names.get(0));
78+
if (names.size() == 1) {
79+
code.add(" -> true;\n");
80+
} else {
81+
code.add(",\n");
82+
code.indent();
83+
for (var i = 1; i < names.size() - 1; i++) {
84+
code.add("$S,\n", names.get(i));
85+
}
86+
code.add("$S -> true;\n", names.get(names.size() - 1));
87+
code.unindent();
88+
}
89+
code.add("default -> false;\n");
90+
code.unindent();
91+
code.add("};\n");
92+
93+
return MethodSpec.methodBuilder(methodName)
94+
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
95+
.returns(TypeName.BOOLEAN)
96+
.addParameter(String.class, "name")
97+
.addCode(code.build())
98+
.build();
99+
}
100+
101+
private static String getBaseModuleText(String path) {
102+
try {
103+
return Files.readString(Path.of(path));
104+
} catch (IOException e) {
105+
throw new UncheckedIOException(e);
106+
}
107+
}
108+
109+
private static Members buildMembers(String basePklPath) {
110+
var text = getBaseModuleText(basePklPath);
111+
var parsed = new Parser().parseModule(text);
112+
var properties = new HashSet<String>();
113+
var methods = new HashSet<String>();
114+
for (var property : parsed.getProperties()) {
115+
if (isLocal(property.getModifiers())) {
116+
continue;
117+
}
118+
properties.add(property.getName().getValue());
119+
}
120+
for (var clazz : parsed.getClasses()) {
121+
if (isLocal(clazz.getModifiers())) {
122+
continue;
123+
}
124+
properties.add(clazz.getName().getValue());
125+
}
126+
for (var typealias : parsed.getTypeAliases()) {
127+
if (isLocal(typealias.getModifiers())) {
128+
continue;
129+
}
130+
properties.add(typealias.getName().getValue());
131+
}
132+
for (var method : parsed.getMethods()) {
133+
if (isLocal(method.getModifiers())) {
134+
continue;
135+
}
136+
methods.add(method.getName().getValue());
137+
}
138+
return new Members(properties, methods);
139+
}
140+
141+
private static boolean isLocal(List<org.pkl.parser.syntax.Modifier> modifiers) {
142+
return modifiers.stream().anyMatch((it) -> it.getValue() == ModifierValue.LOCAL);
143+
}
144+
}

0 commit comments

Comments
 (0)