Skip to content
Draft
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
114 changes: 84 additions & 30 deletions pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -1325,11 +1325,22 @@ public PklRootNode visitModule(Module mod) {

var extendsOrAmendsClause = moduleDecl != null ? moduleDecl.getExtendsOrAmendsDecl() : null;

var supermoduleNode =
extendsOrAmendsClause == null
? resolveBaseModuleClass(
org.pkl.core.runtime.Identifier.MODULE, BaseModule::getModuleClass)
: doVisitImport(false, extendsOrAmendsClause, extendsOrAmendsClause.getUrl());
ExpressionNode supermoduleNode;
if (extendsOrAmendsClause == null) {
supermoduleNode =
resolveBaseModuleClass(
org.pkl.core.runtime.Identifier.MODULE, BaseModule::getModuleClass);
} else {
supermoduleNode = doVisitImport(false, extendsOrAmendsClause, extendsOrAmendsClause.getUrl());
if (extendsOrAmendsClause.getParentTypeName() != null) {
supermoduleNode =
ReadPropertyNodeGen.create(
createSourceSection(extendsOrAmendsClause),
org.pkl.core.runtime.Identifier.get(
extendsOrAmendsClause.getParentTypeName().getValue()),
supermoduleNode);
}
}

var propertyNames =
CollectionUtils.<String>newHashSet(
Expand Down Expand Up @@ -1421,9 +1432,12 @@ private EconomicMap<Object, ObjectMember> doVisitModuleProperties(
var result = EconomicMaps.<Object, ObjectMember>create(totalSize);

for (var _import : imports) {
var member = visitImportClause(_import);
checkDuplicateMember(member.getName(), member.getHeaderSection(), propertyNames);
EconomicMaps.put(result, member.getName(), member);
visitImportClause(_import)
.forEach(
(member) -> {
checkDuplicateMember(member.getName(), member.getHeaderSection(), propertyNames);
EconomicMaps.put(result, member.getName(), member);
});
}

for (var clazz : classes) {
Expand Down Expand Up @@ -1479,36 +1493,76 @@ private EconomicMap<Object, ObjectMember> doVisitModuleProperties(
}

@Override
public ObjectMember visitImportClause(ImportClause imp) {
public List<ObjectMember> visitImportClause(ImportClause imp) {
var importNode = doVisitImport(imp.isGlob(), imp, imp.getImportStr());
var moduleKey = moduleResolver.resolve(importNode.getImportUri());
var importName =
org.pkl.core.runtime.Identifier.property(
imp.getAlias() != null ? imp.getAlias().getValue() : IoUtils.inferModuleName(moduleKey),
true);

return symbolTable.enterProperty(
importName,
ConstLevel.NONE,
scope -> {
var modifiers = VmModifier.IMPORT | VmModifier.LOCAL | VmModifier.CONST;
if (imp.isGlob()) {
modifiers = modifiers | VmModifier.GLOB;
}
var result =
new ObjectMember(
importNode.getSourceSection(),
importNode.getSourceSection(),
modifiers,
scope.getName(),
scope.getQualifiedName());

result.initMemberNode(
new UntypedObjectMemberNode(
language, scope.buildFrameDescriptor(), result, importNode));
var imports =
new ArrayList<ObjectMember>(
1
+ (imp.getDeconstructions() != null
? imp.getDeconstructions().getDeconstructions().size()
: 0));
imports.add(
symbolTable.enterProperty(
importName,
ConstLevel.NONE,
scope -> handleImportClauseItem(imp, importNode, null, scope)));

if (imp.getDeconstructions() == null) return imports;
assert !imp.isGlob();
for (var deconstruction : imp.getDeconstructions().getDeconstructions()) {
var deconstructionName =
org.pkl.core.runtime.Identifier.property(
deconstruction.getAlias() != null
? deconstruction.getAlias().getValue()
: deconstruction.getName().getValue(),
true);
imports.add(
symbolTable.enterProperty(
deconstructionName,
ConstLevel.NONE,
scope ->
handleImportClauseItem(
imp,
importNode,
org.pkl.core.runtime.Identifier.get(deconstruction.getName().getValue()),
scope)));
}
return imports;
}

private ObjectMember handleImportClauseItem(
ImportClause imp,
AbstractImportNode importNode,
@Nullable org.pkl.core.runtime.Identifier memberName,
SymbolTable.PropertyScope scope) {
var modifiers = VmModifier.IMPORT | VmModifier.LOCAL | VmModifier.CONST;
if (imp.isGlob()) {
modifiers = modifiers | VmModifier.GLOB;
}
var result =
new ObjectMember(
importNode.getSourceSection(),
importNode.getSourceSection(),
modifiers,
scope.getName(),
scope.getQualifiedName());

result.initMemberNode(
new UntypedObjectMemberNode(
language,
scope.buildFrameDescriptor(),
result,
memberName == null
? importNode
: ReadPropertyNodeGen.create(createSourceSection(imp), memberName, importNode)));

return result;
});
return result;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.pkl.core.ast.member.ObjectMember;
import org.pkl.core.ast.type.UnresolvedTypeNode;
import org.pkl.core.runtime.ModuleInfo;
import org.pkl.core.runtime.VmClass;
import org.pkl.core.runtime.VmLanguage;
import org.pkl.core.runtime.VmTyped;
import org.pkl.core.runtime.VmUtils;
Expand Down Expand Up @@ -78,4 +79,10 @@ protected VmTyped eval(VirtualFrame frame, VmTyped supermodule) {

return module;
}

// when using `amends Foo in "bar.pkl"`
@Specialization
protected VmTyped eval(VirtualFrame frame, VmClass superclass) {
return eval(frame, superclass.getPrototype());
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-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.
Expand Down Expand Up @@ -191,13 +191,17 @@ public TypeNode execute(VirtualFrame frame) {
return new TypeAliasTypeNode(sourceSection, alias, new TypeNode[0]);
}

var module = (VmTyped) type;
assert module.isModuleObject();
var clazz = module.getVmClass();
if (!module.isPrototype()) {
throw exceptionBuilder().evalError("notAModuleType", clazz.getModuleName()).build();
var moduleOrClass = (VmTyped) type;
var clazz = moduleOrClass.getVmClass();
if (!moduleOrClass.isPrototype()) {
throw exceptionBuilder()
.evalError(
"notAModuleOrClassType",
moduleOrClass.isModuleObject() ? "Module" : "Class",
clazz.getDisplayName())
.build();
}
return TypeNode.forClass(sourceSection, module.getVmClass());
return TypeNode.forClass(sourceSection, clazz);
}
}

Expand Down
2 changes: 1 addition & 1 deletion pkl-core/src/main/java/org/pkl/core/repl/ReplServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ private List<Object> evaluate(
var exprNode = builder.visitExpr(expr);
evaluateExpr(replState, exprNode, forceResults, results);
} else if (tree instanceof ImportClause importClause) {
addStaticModuleProperty(builder.visitImportClause(importClause));
builder.visitImportClause(importClause).forEach(this::addStaticModuleProperty);
} else if (tree instanceof ClassProperty classProperty) {
var propertyNode = builder.visitClassProperty(classProperty);
var property = addModuleProperty(propertyNode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ Cannot invoke abstract property `{0}`.
cannotInvokeSupermethodFromHere=\
Cannot invoke a supermethod from here.

notAModuleType=\
Module `{0}` cannot be extended or used as type because it amends another module.
notAModuleOrClassType=\
{0} `{1}` cannot be extended or used as type because it amends another module or class.

invalidSupertype=\
`{0}` is not a valid supertype.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
amends Widget in ".../input/basic/imported.pkl"

qux = 5
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
amends baz in ".../input/basic/imported.pkl"

qux = 5
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
extends Widget in ".../input/basic/imported.pkl"

corge = quux * qux
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
extends baz in ".../input/basic/imported.pkl"
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import "imported.pkl" as _imported, { Widget as _Widget, baz }
import "imported2.pkl" as imported2, { baz as qux, bax as quux }
import "pkl:test"

a = _imported
b: _imported = new {
foo = 7
}
c = baz
d: _Widget = new {
qux = 5
}
e = test.catch(() -> new baz {}) // TODO provide a better error here than an assertion failure
f = test.catch(() -> new qux {}) // TODO provide a better error here than an assertion failure
g = test.catch(() -> new imported2 {}) // TODO provide a better error here than an assertion failure
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import "imported.pkl" as { Widget as _Widget }

class _Widget {}

a = new _Widget {}
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
foo = bar * 2
bar = 3

open class Widget {
qux: Int
quux: Int = qux * 3
}

baz: Widget = new {
qux = 4
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
amends "imported.pkl"
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// these don't extend Module so they have no `output` property
// thus cannot be rendered directly
amendModuleClass = import(".../input-helper/modules/amendModuleClass.pkl")
amendModuleValue = import(".../input-helper/modules/amendModuleValue.pkl")
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import "pkl:test"

// these don't extend Module so they have no `output` property
// thus cannot be rendered directly
extendModuleClass = (import(".../input-helper/modules/extendModuleClass.pkl")) {
qux = 2
}
extendModuleValue = test.catch(() -> import(".../input-helper/modules/extendModuleValue.pkl"))
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
a {
foo = 6
bar = 3
baz {
qux = 4
quux = 12
}
}
b {
foo = 7
bar = 3
baz {
qux = 4
quux = 12
}
}
c {
qux = 4
quux = 12
}
d {
qux = 5
quux = 15
}
e = "Class `imported` cannot be extended or used as type because it amends another module or class."
f = "Class `imported` cannot be extended or used as type because it amends another module or class."
g = "Module `imported` cannot be extended or used as type because it amends another module or class."
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
–– Pkl Error ––
Duplicate definition of member `_Widget`.

x | class _Widget {}
^^^^^^^^^^^^^
at import5 (file:///$snippetsDir/input/basic/import5.pkl)
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
foo = 6
bar = 3
baz {
qux = 4
quux = 12
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
foo = 6
bar = 3
baz {
qux = 4
quux = 12
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
amendModuleClass {
qux = 5
quux = 15
}
amendModuleValue {
qux = 5
quux = 15
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
–– Pkl Error ––
Cannot find property `output` in module `amendModuleClass`.

Available properties in module `amendModuleClass`:
quux
qux
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
–– Pkl Error ––
Cannot find property `output` in module `amendModuleValue`.

Available properties in module `amendModuleValue`:
quux
qux
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
extendModuleClass {
qux = 2
quux = 6
corge = 12
}
extendModuleValue = "Class `imported#Widget` cannot be extended or used as type because it amends another module or class."
Loading
Loading