mirror of
https://github.com/apple/pkl.git
synced 2026-06-30 10:11:42 +02:00
Resolve variables at parse time (#1429)
This replaces `ResolveVariableNode` and `ResolveMethodNode` with their resolution. When we build the truffle node tree, we determine whether names resolve to: * lexical scope * base module * implicit this Then, we use this information to directly construct the underlying nodes (`ReadPropertyNode`, `ReadLocalPropertyNode`, etc). Additionally, `AstBuilder` determines whether the property access must be const or not. This introduces a `BaseModuleMembers` registry, which gets generated as part of Java compilation.
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.core.generator;
|
||||
|
||||
import com.palantir.javapoet.CodeBlock;
|
||||
import com.palantir.javapoet.JavaFile;
|
||||
import com.palantir.javapoet.MethodSpec;
|
||||
import com.palantir.javapoet.TypeName;
|
||||
import com.palantir.javapoet.TypeSpec;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import org.pkl.parser.Parser;
|
||||
import org.pkl.parser.syntax.Modifier.ModifierValue;
|
||||
|
||||
public final class BaseModuleMembersGenerator {
|
||||
record Members(Set<String> properties, Set<String> methods) {}
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length < 2) {
|
||||
throw new IllegalArgumentException(
|
||||
"Usage: BaseModuleMembersGenerator <path-to-base.pkl> <output-dir>");
|
||||
}
|
||||
var members = buildMembers(args[0]);
|
||||
generateJavaCode(members, args[1]);
|
||||
}
|
||||
|
||||
private static void generateJavaCode(Members members, String outputDir) {
|
||||
var privateConstructor = MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE).build();
|
||||
|
||||
var hasPropertyMethod =
|
||||
buildHasMethod("hasProperty", members.properties().stream().sorted().toList());
|
||||
var hasMethodMethod = buildHasMethod("hasMethod", members.methods().stream().sorted().toList());
|
||||
|
||||
var classSpec =
|
||||
TypeSpec.classBuilder("BaseModuleMembers")
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
|
||||
.addMethod(privateConstructor)
|
||||
.addMethod(hasPropertyMethod)
|
||||
.addMethod(hasMethodMethod)
|
||||
.build();
|
||||
|
||||
var javaFile =
|
||||
JavaFile.builder("org.pkl.core.runtime", classSpec)
|
||||
.addFileComment("DO NOT EDIT — generated by BaseModuleMembersGenerator")
|
||||
.build();
|
||||
|
||||
try {
|
||||
javaFile.writeTo(Path.of(outputDir));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodSpec buildHasMethod(String methodName, List<String> names) {
|
||||
var code = CodeBlock.builder();
|
||||
code.add("return switch (name) {\n");
|
||||
code.indent();
|
||||
code.add("case $S", names.get(0));
|
||||
if (names.size() == 1) {
|
||||
code.add(" -> true;\n");
|
||||
} else {
|
||||
code.add(",\n");
|
||||
code.indent();
|
||||
for (var i = 1; i < names.size() - 1; i++) {
|
||||
code.add("$S,\n", names.get(i));
|
||||
}
|
||||
code.add("$S -> true;\n", names.get(names.size() - 1));
|
||||
code.unindent();
|
||||
}
|
||||
code.add("default -> false;\n");
|
||||
code.unindent();
|
||||
code.add("};\n");
|
||||
|
||||
return MethodSpec.methodBuilder(methodName)
|
||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||
.returns(TypeName.BOOLEAN)
|
||||
.addParameter(String.class, "name")
|
||||
.addCode(code.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
private static String getBaseModuleText(String path) {
|
||||
try {
|
||||
return Files.readString(Path.of(path));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Members buildMembers(String basePklPath) {
|
||||
var text = getBaseModuleText(basePklPath);
|
||||
var parsed = new Parser().parseModule(text);
|
||||
var properties = new HashSet<String>();
|
||||
var methods = new HashSet<String>();
|
||||
for (var property : parsed.getProperties()) {
|
||||
if (isLocal(property.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
properties.add(property.getName().getValue());
|
||||
}
|
||||
for (var clazz : parsed.getClasses()) {
|
||||
if (isLocal(clazz.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
properties.add(clazz.getName().getValue());
|
||||
}
|
||||
for (var typealias : parsed.getTypeAliases()) {
|
||||
if (isLocal(typealias.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
properties.add(typealias.getName().getValue());
|
||||
}
|
||||
for (var method : parsed.getMethods()) {
|
||||
if (isLocal(method.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
methods.add(method.getName().getValue());
|
||||
}
|
||||
return new Members(properties, methods);
|
||||
}
|
||||
|
||||
private static boolean isLocal(List<org.pkl.parser.syntax.Modifier> modifiers) {
|
||||
return modifiers.stream().anyMatch((it) -> it.getValue() == ModifierValue.LOCAL);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user