Skip to content

Commit 89d0048

Browse files
author
Killian Perlin
committed
Merge branch 'topic/query_comprehension' into 'master'
Add Query Comprehensions to LKQL_V2 Closes #207 See merge request eng/libadalang/langkit-query-language!600
2 parents 75a3ecc + 1a42f99 commit 89d0048

File tree

36 files changed

+642
-84
lines changed

36 files changed

+642
-84
lines changed

lkql_checker/share/lkql/kp/kp_20229.lkql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ fun kp_20229(node) =
5050
if output then
5151
stdlib.any([
5252
from r.ref through parent select first ReturnStmt
53-
for r in output.p_find_all_references(units())
53+
for r in output.p_find_all_references(units().to_list)
5454
])
5555
else false
5656
}

lkql_jit/language/src/main/java/com/adacore/lkql_jit/LKQLLanguage.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ public final class LKQLLanguage extends TruffleLanguage<LKQLContext> {
8080
|" Yields all the previous siblings of the given node
8181
| AdaNode => rec(this.previous_sibling())
8282
| * => ()
83+
84+
val all_nodes = units().flat_map((unit) => children(unit.root))
8385
""";
8486

8587
// ----- Static variables -----

lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/BuiltInFunctions.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import com.adacore.lkql_jit.runtime.values.interfaces.Indexable;
1818
import com.adacore.lkql_jit.runtime.values.interfaces.Iterable;
1919
import com.adacore.lkql_jit.runtime.values.interfaces.Iterator;
20+
import com.adacore.lkql_jit.runtime.values.lists.BaseLKQLList;
21+
import com.adacore.lkql_jit.runtime.values.lists.LKQLLazyListStreamWrapper;
2022
import com.adacore.lkql_jit.runtime.values.lists.LKQLList;
2123
import com.adacore.lkql_jit.utils.Constants;
2224
import com.adacore.lkql_jit.utils.LKQLTypesHelper;
@@ -556,12 +558,8 @@ abstract static class UnitsExpr extends BuiltInBody {
556558

557559
@Specialization
558560
@CompilerDirectives.TruffleBoundary
559-
protected LKQLList alwaysTrue() {
560-
return new LKQLList(
561-
LKQLLanguage.getContext(this)
562-
.getAllUnits()
563-
.toArray(LangkitSupport.AnalysisUnit[]::new)
564-
);
561+
protected BaseLKQLList alwaysTrue() {
562+
return new LKQLLazyListStreamWrapper(LKQLLanguage.getContext(this).getAllUnits());
565563
}
566564
}
567565

lkql_jit/language/src/main/java/com/adacore/lkql_jit/langkit_translator/passes/FramingPass.java

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -325,24 +325,14 @@ public Void visit(Liblkqllang.NamedFunction namedFunction) {
325325
*/
326326
@Override
327327
public Void visit(Liblkqllang.ListComprehension listComprehension) {
328-
// Get the list comprehension generator list
329-
final Liblkqllang.ListCompAssocList generators = listComprehension.fGenerators();
330-
final int generatorsCount = generators.getChildrenCount();
331-
332328
// Visit all generator expressions
333-
for (int i = 0; i < generatorsCount; i++) {
334-
final Liblkqllang.ListCompAssoc assoc = (Liblkqllang.ListCompAssoc) generators.getChild(
335-
i
336-
);
329+
for (var assoc : listComprehension.fGenerators()) {
337330
assoc.fCollExpr().accept(this);
338331
}
339332

340333
// Open the frame and visit the list comprehension expressions
341334
this.scriptFramesBuilder.openFrame(listComprehension);
342-
for (int i = 0; i < generatorsCount; i++) {
343-
final Liblkqllang.ListCompAssoc assoc = (Liblkqllang.ListCompAssoc) generators.getChild(
344-
i
345-
);
335+
for (var assoc : listComprehension.fGenerators()) {
346336
final String symbol = assoc.fBindingName().getText();
347337
checkDuplicateParameters(symbol, assoc.fBindingName());
348338
this.scriptFramesBuilder.addParameter(symbol);

lkql_jit/language/src/main/java/com/adacore/lkql_jit/langkit_translator/passes/LktPasses.java

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@
2424
import com.adacore.lkql_jit.nodes.expressions.block_expression.BlockBody;
2525
import com.adacore.lkql_jit.nodes.expressions.block_expression.BlockBodyDecl;
2626
import com.adacore.lkql_jit.nodes.expressions.block_expression.BlockExpr;
27+
import com.adacore.lkql_jit.nodes.expressions.dot.BaseDotAccess;
2728
import com.adacore.lkql_jit.nodes.expressions.dot.DotAccessNodeGen;
2829
import com.adacore.lkql_jit.nodes.expressions.dot.DotAccessWrapperNodeGen;
30+
import com.adacore.lkql_jit.nodes.expressions.dot.SafeDotAccessNodeGen;
2931
import com.adacore.lkql_jit.nodes.expressions.literals.*;
3032
import com.adacore.lkql_jit.nodes.expressions.match.Match;
3133
import com.adacore.lkql_jit.nodes.expressions.match.MatchArm;
@@ -80,7 +82,11 @@ private static boolean isStreamOpRhs(LktNode node) {
8082
);
8183
}
8284

83-
/** Whether "node" needs a frame to be introduced to contain inner bindings. */
85+
/**
86+
* Whether "node" needs a frame to be introduced to contain inner bindings.
87+
* NB: this does not include the Query node (which needs a concrete frame)
88+
* because its handled in its own separate function
89+
*/
8490
private static FrameKind needsFrame(LktNode node) {
8591
if (
8692
node instanceof FunDecl ||
@@ -116,6 +122,12 @@ private static String getBindingName(LktNode node) {
116122
* nodes and building the frame tree.
117123
*/
118124
private static void recurseBuildFrames(LktNode node, ScriptFramesBuilder builder) {
125+
// Queries need special handling
126+
if (node instanceof Liblktlang.Query query) {
127+
handleQuery(query, builder);
128+
return;
129+
}
130+
119131
var bindingName = getBindingName(node);
120132
if (bindingName != null) {
121133
builder.addBinding(bindingName);
@@ -152,6 +164,19 @@ public static ScriptFramesBuilder buildFrames(LktNode root) {
152164
recurseBuildFrames(root, builder);
153165
return builder;
154166
}
167+
168+
/**
169+
* Special handling for query comprehensions
170+
* since the source part is not included in the concrete frame.
171+
*/
172+
public static void handleQuery(Liblktlang.Query query, ScriptFramesBuilder builder) {
173+
recurseBuildFrames(query.fSource(), builder);
174+
builder.openFrame(query);
175+
recurseBuildFrames(query.fPattern(), builder);
176+
if (!query.fGuard().isNone()) recurseBuildFrames(query.fGuard(), builder);
177+
if (!query.fMapping().isNone()) recurseBuildFrames(query.fMapping(), builder);
178+
builder.closeFrame();
179+
}
155180
}
156181

157182
private static class TranslationPass extends BaseTranslationPass {
@@ -501,14 +526,14 @@ private Expr buildExpr(Liblktlang.Expr expr) {
501526
);
502527
} else if (expr instanceof DotExpr dotExpr) {
503528
final var memberIdentifier = dotExpr.fSuffix();
504-
return DotAccessWrapperNodeGen.create(
505-
loc(dotExpr),
506-
DotAccessNodeGen.create(
507-
loc(dotExpr),
508-
new Identifier(loc(memberIdentifier), memberIdentifier.getText()),
509-
buildExpr(dotExpr.fPrefix())
510-
)
511-
);
529+
final var loc = loc(dotExpr);
530+
final var id = new Identifier(loc(memberIdentifier), memberIdentifier.getText());
531+
final var prefix = buildExpr(dotExpr.fPrefix());
532+
final BaseDotAccess dotAccess = (dotExpr.fNullCond() instanceof
533+
Liblktlang.NullCondQualifierPresent)
534+
? SafeDotAccessNodeGen.create(loc, id, prefix)
535+
: DotAccessNodeGen.create(loc, id, prefix);
536+
return DotAccessWrapperNodeGen.create(dotAccess.getSourceSection(), dotAccess);
512537
} else if (expr instanceof MatchExpr matchExpr) {
513538
Expr matchVal = buildExpr(matchExpr.fMatchExpr());
514539
var arms = Arrays.stream(matchExpr.fBranches().children())
@@ -531,6 +556,29 @@ private Expr buildExpr(Liblktlang.Expr expr) {
531556
lambdaExpr.fBody(),
532557
lambdaExpr.fParams()
533558
);
559+
} else if (expr instanceof Liblktlang.Query query) {
560+
var source = buildExpr(query.fSource());
561+
562+
this.frames.enterFrame(query);
563+
564+
var pattern = buildPattern(query.fPattern());
565+
var guard = query.fGuard().isNone() ? null : buildExpr(query.fGuard());
566+
var result = query.fMapping().isNone() ? null : buildExpr(query.fMapping());
567+
568+
var frameDescriptor = this.frames.getFrameDescriptor();
569+
var closureDescriptor = this.frames.getClosureDescriptor();
570+
571+
this.frames.exitFrame();
572+
573+
return QueryComprehensionNodeGen.create(
574+
loc(query),
575+
frameDescriptor,
576+
closureDescriptor,
577+
pattern,
578+
guard,
579+
result,
580+
source
581+
);
534582
} else {
535583
throw LKQLRuntimeException.create(
536584
"Translation for " + expr.getKind() + " not implemented"
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//
2+
// Copyright (C) 2005-2025, AdaCore
3+
// SPDX-License-Identifier: GPL-3.0-or-later
4+
//
5+
6+
package com.adacore.lkql_jit.nodes.expressions;
7+
8+
import com.adacore.lkql_jit.LKQLLanguage;
9+
import com.adacore.lkql_jit.exception.LKQLRuntimeException;
10+
import com.adacore.lkql_jit.nodes.patterns.Pattern;
11+
import com.adacore.lkql_jit.nodes.root_nodes.QueryComprehensionRootNode;
12+
import com.adacore.lkql_jit.nodes.utils.CreateClosureNode;
13+
import com.adacore.lkql_jit.runtime.values.interfaces.Iterable;
14+
import com.adacore.lkql_jit.runtime.values.lists.LKQLQueryComprehension;
15+
import com.adacore.lkql_jit.utils.ClosureDescriptor;
16+
import com.adacore.lkql_jit.utils.LKQLTypesHelper;
17+
import com.oracle.truffle.api.dsl.Fallback;
18+
import com.oracle.truffle.api.dsl.NodeChild;
19+
import com.oracle.truffle.api.dsl.Specialization;
20+
import com.oracle.truffle.api.frame.FrameDescriptor;
21+
import com.oracle.truffle.api.frame.VirtualFrame;
22+
import com.oracle.truffle.api.source.SourceSection;
23+
24+
@NodeChild(value = "source", type = Expr.class)
25+
public abstract class QueryComprehension extends Expr {
26+
27+
// ----- Attributes -----
28+
29+
private final QueryComprehensionRootNode rootNode;
30+
31+
// ----- Children -----
32+
33+
@Child
34+
@SuppressWarnings("FieldMayBeFinal")
35+
private CreateClosureNode createClosureNode;
36+
37+
// ----- Constructors -----
38+
39+
@SuppressWarnings("this-escape")
40+
protected QueryComprehension(
41+
final SourceSection location,
42+
final FrameDescriptor frameDescriptor,
43+
final ClosureDescriptor closureDescriptor,
44+
final Pattern pattern,
45+
final Expr guard,
46+
final Expr result
47+
) {
48+
super(location);
49+
this.rootNode = new QueryComprehensionRootNode(
50+
LKQLLanguage.getLanguage(this),
51+
frameDescriptor,
52+
pattern,
53+
guard,
54+
result
55+
);
56+
this.createClosureNode = new CreateClosureNode(closureDescriptor);
57+
}
58+
59+
// ----- Execution methods -----
60+
61+
@Specialization
62+
protected LKQLQueryComprehension onIterable(VirtualFrame frame, Iterable source) {
63+
return new LKQLQueryComprehension(this.rootNode, createClosureNode.execute(frame), source);
64+
}
65+
66+
@Fallback
67+
protected void fallback(VirtualFrame frame, Object notIterable) {
68+
throw LKQLRuntimeException.wrongType(
69+
LKQLTypesHelper.LKQL_ITERABLE,
70+
LKQLTypesHelper.fromJava(notIterable),
71+
this.getSource()
72+
);
73+
}
74+
75+
// ----- Class methods -----
76+
77+
abstract Expr getSource();
78+
79+
// ----- Override methods -----
80+
81+
/**
82+
* @see com.adacore.lkql_jit.nodes.LKQLNode#toString(int)
83+
*/
84+
@Override
85+
public String toString(int indentLevel) {
86+
return this.nodeRepresentation(indentLevel);
87+
}
88+
}

lkql_jit/language/src/main/java/com/adacore/lkql_jit/nodes/expressions/list_comprehension/ListComprehension.java

Lines changed: 6 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -30,32 +30,13 @@ public final class ListComprehension extends Expr {
3030

3131
// ----- Attributes -----
3232

33-
/** Slots for the list comprehension parameters. */
34-
private final int[] slots;
35-
36-
/** Descriptor of the frame to create. */
37-
private final FrameDescriptor frameDescriptor;
38-
39-
/** Descriptor for the values to close. */
40-
private final ClosureDescriptor closureDescriptor;
41-
4233
// ----- Children -----
4334

4435
/** Generators of the list comprehension. */
4536
@Child
4637
@SuppressWarnings("FieldMayBeFinal")
4738
private ComprehensionAssocList generators;
4839

49-
/** Result expression of the list comprehension. */
50-
@Child
51-
@SuppressWarnings("FieldMayBeFinal")
52-
private Expr expr;
53-
54-
/** Guard expression of the list comprehension. */
55-
@Child
56-
@SuppressWarnings("FieldMayBeFinal")
57-
private Expr guard;
58-
5940
@Child
6041
@SuppressWarnings("FieldMayBeFinal")
6142
private CreateClosureNode createClosureNode;
@@ -84,16 +65,13 @@ public ListComprehension(
8465
final Expr guard
8566
) {
8667
super(location);
87-
this.frameDescriptor = frameDescriptor;
88-
this.closureDescriptor = closureDescriptor;
8968
this.generators = generators;
90-
this.slots = new int[generators.getCompAssocs().length];
91-
for (int i = 0; i < this.slots.length; i++) {
92-
this.slots[i] = generators.getCompAssocs()[i].getSlot();
93-
}
94-
this.expr = expr;
95-
this.guard = guard;
96-
this.rootNode = createRootNode();
69+
this.rootNode = new ListComprehensionRootNode(
70+
LKQLLanguage.getLanguage(this),
71+
frameDescriptor,
72+
guard,
73+
expr
74+
);
9775
this.createClosureNode = new CreateClosureNode(closureDescriptor);
9876
}
9977

@@ -170,20 +148,6 @@ private boolean increaseIndexes(Iterator[] iterators, Object[] valueBuffer) {
170148
return false;
171149
}
172150

173-
/**
174-
* Create a root node for the list comprehension.
175-
*
176-
* @return The root node for the list comprehension execution.
177-
*/
178-
private ListComprehensionRootNode createRootNode() {
179-
return new ListComprehensionRootNode(
180-
LKQLLanguage.getLanguage(this),
181-
this.frameDescriptor,
182-
this.guard,
183-
this.expr
184-
);
185-
}
186-
187151
// ----- Override methods -----
188152

189153
/**

0 commit comments

Comments
 (0)