Skip to content

Commit f129656

Browse files
committed
Merge branch 'topic/lkql_jit/add_streams' into 'master'
Implement "Streams" in the LKQL JIT engine See merge request eng/libadalang/langkit-query-language!591
2 parents a2d84c9 + 85cf31a commit f129656

File tree

101 files changed

+930
-140
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+930
-140
lines changed

lkql_jit/cli/src/main/java/com/adacore/lkql_jit/cli/BaseLKQLChecker.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ public abstract class BaseLKQLChecker extends AbstractLanguageLauncher {
3232
public static final String checkerSource =
3333
"""
3434
val analysis_units = specified_units()
35-
map(analysis_units, (unit) => node_checker(unit.root))
36-
map(analysis_units, (unit) => unit_checker(unit))
35+
analysis_units.map((unit) => node_checker(unit.root)).to_list
36+
analysis_units.map((unit) => unit_checker(unit)).to_list
3737
""";
3838

3939
// ----- Constructors -----

lkql_jit/cli/src/main/java/com/adacore/lkql_jit/cli/GNATCheckWorker.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ public LKQLRuleFileError(String message) {
641641
val analysis_units = specified_units()
642642
val roots = [unit.root for unit in analysis_units]
643643
644-
map(roots, (root) => node_checker(root))
645-
map(analysis_units, (unit) => unit_checker(unit))
644+
roots.map((root) => node_checker(root)).to_list
645+
analysis_units.map((unit) => unit_checker(unit)).to_list
646646
""";
647647
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
import com.adacore.lkql_jit.runtime.values.*;
1111
import com.adacore.lkql_jit.runtime.values.interfaces.Iterable;
1212
import com.adacore.lkql_jit.runtime.values.interfaces.*;
13+
import com.adacore.lkql_jit.runtime.values.lists.BaseLKQLLazyList;
1314
import com.adacore.lkql_jit.runtime.values.lists.BaseLKQLList;
14-
import com.adacore.lkql_jit.runtime.values.lists.LKQLLazyList;
1515
import com.adacore.lkql_jit.runtime.values.lists.LKQLList;
16+
import com.adacore.lkql_jit.runtime.values.lists.LKQLStream;
1617
import com.oracle.truffle.api.CompilerDirectives;
1718
import com.oracle.truffle.api.dsl.ImplicitCast;
1819
import com.oracle.truffle.api.dsl.TypeCast;
@@ -38,7 +39,8 @@
3839
LKQLProperty.class,
3940
LKQLTuple.class,
4041
LKQLList.class,
41-
LKQLLazyList.class,
42+
LKQLStream.class,
43+
BaseLKQLLazyList.class,
4244
BaseLKQLList.class,
4345
Indexable.class,
4446
Iterable.class,

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

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -432,43 +432,6 @@ public boolean executeRepeating(VirtualFrame frame) {
432432
}
433433
}
434434

435-
@BuiltInFunction(name = "map", doc = "Given a collection, a mapping function")
436-
abstract static class MapExpr extends BuiltInBody {
437-
438-
@Specialization(
439-
limit = Constants.SPECIALIZED_LIB_LIMIT,
440-
guards = "function.parameterNames.length == 1"
441-
)
442-
protected LKQLList onValidArgs(
443-
Iterable iterable,
444-
LKQLFunction function,
445-
@CachedLibrary("function") InteropLibrary functionLibrary
446-
) {
447-
Object[] res = new Object[(int) iterable.size()];
448-
int i = 0;
449-
Iterator iterator = iterable.iterator();
450-
451-
while (iterator.hasNext()) {
452-
try {
453-
res[i] = functionLibrary.execute(
454-
function,
455-
function.closure.getContent(),
456-
iterator.next()
457-
);
458-
} catch (
459-
ArityException | UnsupportedTypeException | UnsupportedMessageException e
460-
) {
461-
// TODO: Implement runtime checks in the LKQLFunction class and base computing
462-
// on them (#138)
463-
throw LKQLRuntimeException.fromJavaException(e, this);
464-
}
465-
i++;
466-
}
467-
468-
return new LKQLList(res);
469-
}
470-
}
471-
472435
@BuiltInFunction(
473436
name = "profile",
474437
doc = "Given any object, if it is a callable, return its profile as text"

lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/methods/IterableMethods.java

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,19 @@
88
import com.adacore.lkql_jit.annotations.BuiltInMethod;
99
import com.adacore.lkql_jit.annotations.BuiltinMethodContainer;
1010
import com.adacore.lkql_jit.built_ins.BuiltInBody;
11+
import com.adacore.lkql_jit.runtime.values.LKQLFunction;
1112
import com.adacore.lkql_jit.runtime.values.LKQLTuple;
1213
import com.adacore.lkql_jit.runtime.values.interfaces.Iterable;
1314
import com.adacore.lkql_jit.runtime.values.interfaces.Iterator;
15+
import com.adacore.lkql_jit.runtime.values.lists.BaseLKQLLazyList;
16+
import com.adacore.lkql_jit.runtime.values.lists.LKQLFlattenResult;
1417
import com.adacore.lkql_jit.runtime.values.lists.LKQLList;
18+
import com.adacore.lkql_jit.runtime.values.lists.LKQLMapResult;
19+
import com.adacore.lkql_jit.utils.Constants;
1520
import com.adacore.lkql_jit.utils.LKQLTypesHelper;
1621
import com.oracle.truffle.api.dsl.Specialization;
17-
import java.util.LinkedList;
18-
import java.util.List;
22+
import com.oracle.truffle.api.interop.InteropLibrary;
23+
import com.oracle.truffle.api.library.CachedLibrary;
1924

2025
/** This class contains all built-in methods for the iterable type in the LKQL language. */
2126
@BuiltinMethodContainer(
@@ -60,14 +65,14 @@ abstract static class ToListExpr extends BuiltInBody {
6065
@Specialization
6166
public LKQLList doGeneric(Iterable self) {
6267
// Create a new list from the iterable
63-
List<Object> resList = new LinkedList<>();
68+
var res = new Object[(int) self.size()];
6469
Iterator iterator = self.iterator();
65-
while (iterator.hasNext()) {
66-
resList.add(iterator.next());
70+
for (int i = 0; i < res.length; i++) {
71+
res[i] = iterator.next();
6772
}
6873

6974
// Return the new list value
70-
return new LKQLList(resList.toArray(new Object[0]));
75+
return new LKQLList(res);
7176
}
7277
}
7378

@@ -79,4 +84,63 @@ public long doGeneric(Iterable self) {
7984
return self.size();
8085
}
8186
}
87+
88+
@BuiltInMethod(
89+
name = "map",
90+
doc = """
91+
Given an iterable and a function that takes one argument and return a value, return \
92+
a new iterable, result of the application of the function on all iterable elements.
93+
The returned iterable value is lazy."""
94+
)
95+
abstract static class MapExpr extends BuiltInBody {
96+
97+
@Specialization(
98+
limit = Constants.SPECIALIZED_LIB_LIMIT,
99+
guards = "function.parameterNames.length == 1"
100+
)
101+
protected BaseLKQLLazyList onIterable(
102+
Iterable iterable,
103+
LKQLFunction function,
104+
@CachedLibrary("function") InteropLibrary functionLibrary
105+
) {
106+
return new LKQLMapResult(iterable, function, functionLibrary);
107+
}
108+
}
109+
110+
@BuiltInMethod(
111+
name = "flatten",
112+
doc = """
113+
Given an iterable of iterables, flatten all of them in a resulting iterable value. The \
114+
returned value is lazy.""",
115+
isProperty = true
116+
)
117+
abstract static class FlattenExpr extends BuiltInBody {
118+
119+
@Specialization
120+
protected BaseLKQLLazyList onIterable(Iterable iterable) {
121+
return new LKQLFlattenResult(iterable);
122+
}
123+
}
124+
125+
@BuiltInMethod(
126+
name = "flat_map",
127+
doc = """
128+
Given an iterable and a function that takes one argument and return another iterable \
129+
value, return a new iterable, result of the function application on all elements, flatten \
130+
in a sole iterable value. The returned iterable value is lazy."""
131+
)
132+
abstract static class FlatMapExpr extends BuiltInBody {
133+
134+
@Specialization(
135+
limit = Constants.SPECIALIZED_LIB_LIMIT,
136+
guards = "function.parameterNames.length == 1"
137+
)
138+
protected BaseLKQLLazyList onIterable(
139+
Iterable iterable,
140+
LKQLFunction function,
141+
@CachedLibrary("function") InteropLibrary functionLibrary
142+
) {
143+
return new LKQLFlattenResult(new LKQLMapResult(iterable, function, functionLibrary));
144+
}
145+
}
82146
}

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

Lines changed: 104 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@
2020
import com.adacore.lkql_jit.nodes.arguments.NamedArg;
2121
import com.adacore.lkql_jit.nodes.declarations.*;
2222
import com.adacore.lkql_jit.nodes.expressions.Expr;
23-
import com.adacore.lkql_jit.nodes.expressions.FunCallNodeGen;
24-
import com.adacore.lkql_jit.nodes.expressions.FunExpr;
23+
import com.adacore.lkql_jit.nodes.expressions.*;
2524
import com.adacore.lkql_jit.nodes.expressions.block_expression.BlockBody;
2625
import com.adacore.lkql_jit.nodes.expressions.block_expression.BlockBodyDecl;
2726
import com.adacore.lkql_jit.nodes.expressions.block_expression.BlockExpr;
@@ -68,9 +67,27 @@ public enum FrameKind {
6867
None,
6968
}
7069

70+
/**
71+
* Return whether the provided Lkt node is an expression used as a right hand operand in
72+
* a stream related binary operation.
73+
*/
74+
private static boolean isStreamOpRhs(LktNode node) {
75+
return (
76+
node instanceof Liblktlang.Expr &&
77+
node.parent() instanceof Liblktlang.BinOp binOp &&
78+
(binOp.fOp() instanceof OpStreamCons || binOp.fOp() instanceof OpStreamConcat) &&
79+
binOp.fRight().equals(node)
80+
);
81+
}
82+
7183
/** Whether "node" needs a frame to be introduced to contain inner bindings. */
7284
private static FrameKind needsFrame(LktNode node) {
73-
if ((node instanceof FunDecl) || (node instanceof Liblktlang.LangkitRoot)) {
85+
if (
86+
node instanceof FunDecl ||
87+
node instanceof Liblktlang.LambdaExpr ||
88+
node instanceof Liblktlang.LangkitRoot ||
89+
isStreamOpRhs(node)
90+
) {
7491
return FrameKind.Concrete;
7592
} else if (
7693
node instanceof Liblktlang.BlockExpr ||
@@ -106,6 +123,8 @@ private static void recurseBuildFrames(LktNode node, ScriptFramesBuilder builder
106123

107124
if (node instanceof FunParamDecl funParamDecl) {
108125
builder.addParameter(funParamDecl.fSynName().getText());
126+
} else if (node instanceof LambdaParamDecl lambdaParamDecl) {
127+
builder.addParameter(lambdaParamDecl.fSynName().getText());
109128
}
110129

111130
var frameKind = needsFrame(node);
@@ -193,11 +212,23 @@ private FunExpr createFunExpr(
193212
var defaultVals = new Expr[params.getChildrenCount()];
194213

195214
for (int i = 0; i < params.getChildrenCount(); i++) {
196-
var paramDecl = (FunParamDecl) params.getChild(i);
197-
paramNames[i] = paramDecl.fSynName().getText();
198-
defaultVals[i] = paramDecl.fDefaultVal().isNone()
199-
? null
200-
: buildExpr(paramDecl.fDefaultVal());
215+
String paramName;
216+
Liblktlang.Expr defaultValue;
217+
switch (params.getChild(i)) {
218+
case FunParamDecl funParamDecl -> {
219+
paramName = funParamDecl.fSynName().getText();
220+
defaultValue = funParamDecl.fDefaultVal();
221+
}
222+
case LambdaParamDecl lambdaParamDecl -> {
223+
paramName = lambdaParamDecl.fSynName().getText();
224+
defaultValue = lambdaParamDecl.fDefaultVal();
225+
}
226+
default -> throw LKQLRuntimeException.create(
227+
"Cannot handle " + params.getChild(i) + " parameter declaration"
228+
);
229+
}
230+
paramNames[i] = paramName;
231+
defaultVals[i] = defaultValue.isNone() ? null : buildExpr(defaultValue);
201232
}
202233
final var body = buildExpr(functionBody);
203234

@@ -207,7 +238,7 @@ private FunExpr createFunExpr(
207238
this.frames.getClosureDescriptor(),
208239
paramNames,
209240
defaultVals,
210-
doc.isNone() ? "" : doc.pDenotedValue().value,
241+
doc == null || doc.isNone() ? "" : doc.pDenotedValue().value,
211242
body,
212243
functionName
213244
);
@@ -329,7 +360,18 @@ private Expr buildExpr(Liblktlang.Expr expr) {
329360
}
330361
} else if (expr instanceof NullLit nullLit) {
331362
return new NullLiteral(loc(nullLit));
363+
} else if (expr instanceof Liblktlang.ParenExpr parenExpr) {
364+
return buildExpr(parenExpr.fExpr());
332365
} else if (expr instanceof CallExpr callExpr) {
366+
final Liblktlang.Expr calleeNode = callExpr.fName();
367+
368+
// Special case for the "Unit()" call. This is the constructor for the "Unit" type
369+
// in the Lkt language.
370+
if (calleeNode.getText().equals("Unit")) {
371+
return new UnitLiteral(loc(callExpr));
372+
}
373+
374+
// In all other cases, translate the call expression to a function call
333375
final Expr callee = buildExpr(callExpr.fName());
334376
final ArgList arguments = buildArgs(
335377
Arrays.stream(callExpr.fArgs().children())
@@ -384,6 +426,44 @@ private Expr buildExpr(Liblktlang.Expr expr) {
384426
return IsClauseNodeGen.create(loc(isA), pattern, nodeExpr);
385427
} else if (expr instanceof StringLit stringLit) {
386428
return new StringLiteral(loc(stringLit), parseStringLiteral(stringLit));
429+
} else if (expr instanceof Liblktlang.ArrayLiteral arrayLiteral) {
430+
return new ListLiteral(
431+
loc(arrayLiteral),
432+
Arrays.stream(arrayLiteral.fExprs().children())
433+
.map(e -> buildExpr((Liblktlang.Expr) e))
434+
.toList()
435+
.toArray(new Expr[0])
436+
);
437+
} else if (
438+
expr instanceof Liblktlang.BinOp binOp &&
439+
(binOp.fOp() instanceof OpStreamCons || binOp.fOp() instanceof OpStreamConcat)
440+
) {
441+
// Special case for stream constructors since the right operand is lazy
442+
final var head = buildExpr(binOp.fLeft());
443+
this.frames.enterFrame(binOp.fRight());
444+
final var tail = buildExpr(binOp.fRight());
445+
446+
final var res =
447+
switch (binOp.fOp().getKind()) {
448+
case OP_STREAM_CONS -> StreamConsNodeGen.create(
449+
loc(binOp),
450+
tail,
451+
this.frames.getFrameDescriptor(),
452+
this.frames.getClosureDescriptor(),
453+
head
454+
);
455+
case OP_STREAM_CONCAT -> StreamConcatNodeGen.create(
456+
loc(binOp),
457+
tail,
458+
this.frames.getFrameDescriptor(),
459+
this.frames.getClosureDescriptor(),
460+
head
461+
);
462+
default -> null;
463+
};
464+
465+
this.frames.exitFrame();
466+
return res;
387467
} else if (expr instanceof Liblktlang.BinOp binOp) {
388468
final var left = buildExpr(binOp.fLeft());
389469
final var right = buildExpr(binOp.fRight());
@@ -412,6 +492,13 @@ private Expr buildExpr(Liblktlang.Expr expr) {
412492
};
413493
} else if (expr instanceof NotExpr notExpr) {
414494
return UnNotNodeGen.create(loc(notExpr), buildExpr(notExpr.fExpr()));
495+
} else if (expr instanceof SubscriptExpr subscriptExpr) {
496+
return IndexingNodeGen.create(
497+
loc(subscriptExpr),
498+
subscriptExpr.fNullCond() instanceof NullCondQualifierPresent,
499+
buildExpr(subscriptExpr.fPrefix()),
500+
buildExpr(subscriptExpr.fIndex())
501+
);
415502
} else if (expr instanceof DotExpr dotExpr) {
416503
final var memberIdentifier = dotExpr.fSuffix();
417504
return DotAccessWrapperNodeGen.create(
@@ -436,6 +523,14 @@ private Expr buildExpr(Liblktlang.Expr expr) {
436523
.toList()
437524
.toArray(new MatchArm[0]);
438525
return new Match(loc(matchExpr), matchVal, arms);
526+
} else if (expr instanceof Liblktlang.LambdaExpr lambdaExpr) {
527+
return createFunExpr(
528+
lambdaExpr,
529+
"<anonymous>",
530+
null,
531+
lambdaExpr.fBody(),
532+
lambdaExpr.fParams()
533+
);
439534
} else {
440535
throw LKQLRuntimeException.create(
441536
"Translation for " + expr.getKind() + " not implemented"

0 commit comments

Comments
 (0)