Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of delete (SPICE-0008) #658

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
40 changes: 40 additions & 0 deletions docs/modules/language-reference/pages/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3178,6 +3178,7 @@ pkl: TRACE: num1 * num2 = 672 (at file:///some/module.pkl, line 42)
This section discusses language features that are generally more relevant to template and library authors than template consumers.

<<meaning-of-new,Meaning of `new`>> +
<<member-deletion,Deleting members with `delete`>> +
<<let-expressions,Let Expressions>> +
<<type-tests,Type Tests>> +
<<type-casts,Type Casts>> +
Expand Down Expand Up @@ -3357,6 +3358,45 @@ swiftHatchlings = typedProperty.listHatchlings(new { "Poppy"; "Chirpy" }) // <8>
<7> Admending the property default value `new Listing { new Bird { name = "Osprey" } }`; the result contains both birds.
<8> Error: Cannot tell which parent to amend.

[[member-deletions]]
=== Deleting members with `delete`

Members can be deleted when amending the object containing them.
This is done using the `delete` keyword.
By itself, `delete` is not an expression; it can be used only in specific places, namely in a member
assignment.
These are all the positions where `delete` can be used:

[source,pkl]
----
foo {
bar = delete // <1>
["baz"] = delete // <2>
[42] = delete // <3>
[[qux == 1337]] = delete // <4>
}
----
<1> Property deletion.
<2> Entry deletion.
<3> Element deletion.
<4> Member predicate deletion (can include both entries and elements).

Properties can only be deleted from `Dynamic`s, because a `Typed` must have precisely the properties defined in its class.
Since elements are contiguously indices, deleting elements effectively renames other elements.
This means that the indices used in the definition of an object can differ from those used in subscripting.
In the following, `result = true`:

[source,pkl]
----
result = (new Listing {
/* [0] */ "foo"
/* [1] */ "bar"
/* [2] */ "baz"
}) {
[1] = delete
}[1] == "baz"
----

[[let-expressions]]
=== Let Expressions

Expand Down
17 changes: 8 additions & 9 deletions pkl-core/src/main/antlr/PklParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,14 @@ objectBody
;

objectMember
: modifier* Identifier (typeAnnotation? '=' expr | objectBody+) # objectProperty
| methodHeader '=' expr # objectMethod
| t='[[' k=expr err1=']'? err2=']'? ('=' v=expr | objectBody+) # memberPredicate
| t='[' k=expr err1=']'? err2=']'? ('=' v=expr | objectBody+) # objectEntry
| expr # objectElement
| ('...' | '...?') expr # objectSpread
| 'when' '(' e=expr err=')'? (b1=objectBody ('else' b2=objectBody)?) # whenGenerator
| 'for' '(' t1=parameter (',' t2=parameter)? 'in' e=expr err=')'? objectBody # forGenerator
: modifier* Identifier (typeAnnotation? '=' (v=expr | d='delete') | objectBody+) # objectProperty
| methodHeader '=' expr # objectMethod
| t='[[' k=expr err1=']'? err2=']'? ('=' (v=expr | d='delete') | objectBody+) # memberPredicate
| t='[' k=expr err1=']'? err2=']'? ('=' (v=expr | d='delete') | objectBody+) # objectEntry
| expr # objectElement
| ('...' | '...?') expr # objectSpread
| 'when' '(' e=expr err=')'? (b1=objectBody ('else' b2=objectBody)?) # whenGenerator
| 'for' '(' t1=parameter (',' t2=parameter)? 'in' e=expr err=')'? objectBody # forGenerator
;

stringConstant
Expand All @@ -247,7 +247,6 @@ reservedKeyword
: 'protected'
| 'override'
| 'record'
| 'delete'
| 'case'
| 'switch'
| 'vararg'
Expand Down
12 changes: 9 additions & 3 deletions pkl-core/src/main/java/org/pkl/core/ast/VmModifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ private VmModifier() {}

public static final int GLOB = 0x1000;

public static final int DELETE = 0x2000;

// modifier sets

public static final int NONE = 0;
Expand Down Expand Up @@ -134,16 +136,20 @@ public static boolean isEntry(int modifiers) {
return (modifiers & ENTRY) != 0;
}

public static boolean isDelete(int modifiers) {
return (modifiers & DELETE) != 0;
}

public static boolean isType(int modifiers) {
return (modifiers & (CLASS | TYPE_ALIAS | IMPORT)) != 0 && (modifiers & GLOB) == 0;
return (modifiers & (CLASS | TYPE_ALIAS | IMPORT)) != 0 && (modifiers & (GLOB | DELETE)) == 0;
}

public static boolean isLocalOrExternalOrHidden(int modifiers) {
return (modifiers & (LOCAL | EXTERNAL | HIDDEN)) != 0;
}

public static boolean isLocalOrExternalOrAbstract(int modifiers) {
return (modifiers & (LOCAL | EXTERNAL | ABSTRACT)) != 0;
public static boolean isLocalOrExternalOrAbstractOrDelete(int modifiers) {
return (modifiers & (LOCAL | EXTERNAL | ABSTRACT | DELETE)) != 0;
}

public static boolean isConstOrFixed(int modifiers) {
Expand Down
33 changes: 28 additions & 5 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 @@ -651,7 +651,13 @@ private VmException missingLocalPropertyValue(TypeAnnotationContext typeAnnCtx)

private ObjectMember doVisitObjectProperty(ObjectPropertyContext ctx) {
return doVisitObjectProperty(
ctx, ctx.modifier(), ctx.Identifier(), ctx.typeAnnotation(), ctx.expr(), ctx.objectBody());
ctx,
ctx.modifier(),
ctx.Identifier(),
ctx.typeAnnotation(),
ctx.v,
ctx.d,
ctx.objectBody());
}

private ObjectMember doVisitObjectMethod(ObjectMethodContext ctx) {
Expand Down Expand Up @@ -720,6 +726,7 @@ private ObjectMember doVisitObjectProperty(
TerminalNode propertyName,
@Nullable TypeAnnotationContext typeAnnCtx,
@Nullable ExprContext exprCtx,
@Nullable Token deleteToken,
@Nullable List<? extends ObjectBodyContext> bodyCtx) {

return doVisitObjectProperty(
Expand All @@ -730,18 +737,21 @@ private ObjectMember doVisitObjectProperty(
propertyName.getText(),
typeAnnCtx,
exprCtx,
deleteToken,
bodyCtx);
}

private ObjectMember doVisitObjectProperty(
SourceSection sourceSection,
SourceSection headerSection,
int modifiers,
int propertyModifiers,
String propertyName,
@Nullable TypeAnnotationContext typeAnnCtx,
@Nullable ExprContext exprCtx,
@Nullable Token deleteToken,
@Nullable List<? extends ObjectBodyContext> bodyCtx) {

var modifiers = propertyModifiers | (deleteToken == null ? 0 : VmModifier.DELETE);
var isLocal = VmModifier.isLocal(modifiers);
var identifier = Identifier.property(propertyName, isLocal);

Expand Down Expand Up @@ -783,6 +793,15 @@ private ObjectMember doVisitObjectProperty(
// 2. if in a const scope (i.e. `const bar = new { foo { ... } }`),
// `super.foo` does not reference something outside the scope.
false));
} else if (deleteToken != null) {
var deleteSourceSection = createSourceSection(deleteToken);
if (isLocal) {
throw exceptionBuilder()
.evalError("cannotDeleteLocalProperty")
.withSourceSection(deleteSourceSection)
.build();
}
bodyNode = VmUtils.DELETE_MARKER;
} else { // foo = ...
assert exprCtx != null;
bodyNode = visitExpr(exprCtx);
Expand Down Expand Up @@ -1218,7 +1237,8 @@ private Pair<ExpressionNode, ObjectMember> doVisitMemberPredicate(MemberPredicat
var keyNode = symbolTable.enterCustomThisScope(scope -> visitExpr(ctx.k));

return symbolTable.enterEntry(
keyNode, objectMemberInserter(createSourceSection(ctx), keyNode, ctx.v, ctx.objectBody()));
keyNode,
objectMemberInserter(createSourceSection(ctx), keyNode, ctx.v, ctx.d, ctx.objectBody()));
}

private Pair<ExpressionNode, ObjectMember> doVisitObjectEntry(ObjectEntryContext ctx) {
Expand All @@ -1232,20 +1252,22 @@ private Pair<ExpressionNode, ObjectMember> doVisitObjectEntry(ObjectEntryContext
var keyNode = visitExpr(ctx.k);

return symbolTable.enterEntry(
keyNode, objectMemberInserter(createSourceSection(ctx), keyNode, ctx.v, ctx.objectBody()));
keyNode,
objectMemberInserter(createSourceSection(ctx), keyNode, ctx.v, ctx.d, ctx.objectBody()));
}

private Function<EntryScope, Pair<ExpressionNode, ObjectMember>> objectMemberInserter(
SourceSection sourceSection,
ExpressionNode keyNode,
@Nullable ExprContext valueCtx,
@Nullable Token deleteToken,
List<? extends ObjectBodyContext> objectBodyCtxs) {
return scope -> {
var member =
new ObjectMember(
sourceSection,
keyNode.getSourceSection(),
VmModifier.ENTRY,
VmModifier.ENTRY | (deleteToken == null ? 0 : VmModifier.DELETE),
null,
scope.getQualifiedName());

Expand Down Expand Up @@ -2610,6 +2632,7 @@ private EconomicMap<Object, ObjectMember> doVisitModuleProperties(
ctx.Identifier(),
ctx.typeAnnotation(),
ctx.expr(),
null,
ctx.objectBody());

if (moduleInfo.isAmend() && !member.isLocal() && ctx.typeAnnotation() != null) {
Expand Down
8 changes: 6 additions & 2 deletions pkl-core/src/main/java/org/pkl/core/ast/member/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ public final boolean isType() {
return VmModifier.isType(modifiers);
}

public final boolean isDelete() {
return VmModifier.isDelete(modifiers);
}

public final boolean isLocalOrExternalOrHidden() {
return VmModifier.isLocalOrExternalOrHidden(modifiers);
}
Expand All @@ -127,7 +131,7 @@ public final boolean isConstOrFixed() {
return VmModifier.isConstOrFixed(modifiers);
}

public final boolean isLocalOrExternalOrAbstract() {
return VmModifier.isLocalOrExternalOrAbstract(modifiers);
public final boolean isLocalOrExternalOrAbstractOrDelete() {
return VmModifier.isLocalOrExternalOrAbstractOrDelete(modifiers);
}
}
5 changes: 3 additions & 2 deletions pkl-core/src/main/java/org/pkl/core/runtime/TestRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -247,12 +247,13 @@ private void doRunAndValidateExamples(
return true;
}
if (examples.getCachedValue(groupKey) == null) {
var key = String.valueOf(groupKey);
allGroupsSucceeded.set(false);
results
.newResult(String.valueOf(groupKey))
.newResult(key)
.addFailure(
Failure.buildExamplePropertyMismatchFailure(
getDisplayUri(groupMember), String.valueOf(groupKey), false));
getDisplayUri(groupMember), key, false));
}
return true;
});
Expand Down
2 changes: 1 addition & 1 deletion pkl-core/src/main/java/org/pkl/core/runtime/VmDynamic.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public int getLength() {
return length;
}

/** Tells whether this object has any elements. */
@Override
public boolean hasElements() {
return length != 0;
}
Expand Down
3 changes: 1 addition & 2 deletions pkl-core/src/main/java/org/pkl/core/runtime/VmFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.frame.MaterializedFrame;
import java.util.function.BiFunction;
import org.graalvm.collections.UnmodifiableEconomicMap;
import org.pkl.core.ast.PklRootNode;
import org.pkl.core.ast.member.ObjectMember;
Expand Down Expand Up @@ -142,7 +141,7 @@ public boolean iterateAlreadyForcedMemberValues(ForcedMemberValueConsumer consum
}

@Override
public boolean iterateMembers(BiFunction<Object, ObjectMember, Boolean> consumer) {
public boolean iterateMembers(MemberConsumer consumer) {
return true;
}

Expand Down
5 changes: 5 additions & 0 deletions pkl-core/src/main/java/org/pkl/core/runtime/VmListing.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ public int getLength() {
return length;
}

@Override
public boolean hasElements() {
return !isEmpty();
}

public boolean isEmpty() {
return length == 0;
}
Expand Down
5 changes: 5 additions & 0 deletions pkl-core/src/main/java/org/pkl/core/runtime/VmMapping.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ public VmClass getVmClass() {
return BaseModule.getMappingClass();
}

@Override
public boolean hasElements() {
return false;
}

@TruffleBoundary
public VmSet getAllKeys() {
synchronized (this) {
Expand Down
Loading