Skip to content

Commit d5be117

Browse files
committed
Merge branch 'topic/lkql_jit/add_merge_method' into 'master'
Replace object concatenation by a new "combine" method Closes #480 See merge request eng/libadalang/langkit-query-language!457
2 parents e847254 + 8103662 commit d5be117

File tree

29 files changed

+210
-105
lines changed

29 files changed

+210
-105
lines changed

lkql_checker/doc/gnatcheck_rm/using_gnatcheck.rst

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -592,8 +592,8 @@ the following configuration will raise an error:
592592

593593
You cannot provide more than **one** LKQL rule file when running GNATcheck. In
594594
order to compose a rule file with another you have to use the
595-
:ref:`LKQL importation mechanism<module_importation>` and concatenate rule
596-
objects. Here is an example of LKQL rule file composition:
595+
:ref:`LKQL importation mechanism<module_importation>` and combine rule objects.
596+
Here is an example of LKQL rule file composition:
597597

598598
.. code-block:: lkql
599599
@@ -609,9 +609,9 @@ objects. Here is an example of LKQL rule file composition:
609609
610610
import common_rules
611611
612-
val rules = common_rules.rules & @{
613-
Redundant_Null_Statements
614-
}
612+
val rules = common_rules.rules.combine(
613+
@{ Redundant_Null_Statements }
614+
)
615615
616616
Then you can run GNATcheck with the ``specific_rules.lkql`` file as coding
617617
standard to perform rules defined in ``common_rules.lkql`` combined to the ones
@@ -641,9 +641,12 @@ invalid:
641641
642642
import common_rules
643643
644-
val rules = common_rules.rules & @{
645-
Forbidden_Attributes: {Forbidden: ["Last"], instance_name: "Forbid_Attr"}
646-
}
644+
val rules = common_rules.rules.combine(@{
645+
Forbidden_Attributes: {
646+
Forbidden: ["Last"],
647+
instance_name: "Forbid_Attr"
648+
}
649+
})
647650
# error: This rule configuration defines two instances with the same name: "Forbid_Attr"
648651
649652

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import com.adacore.lkql_jit.annotations.*;
1111
import com.adacore.lkql_jit.exception.LKQLRuntimeException;
1212
import com.adacore.lkql_jit.nodes.utils.ConcatenationNode;
13+
import com.adacore.lkql_jit.nodes.utils.ValueCombiner;
1314
import com.adacore.lkql_jit.runtime.values.*;
1415
import com.adacore.lkql_jit.runtime.values.bases.BasicLKQLValue;
1516
import com.adacore.lkql_jit.runtime.values.interfaces.Indexable;
@@ -295,6 +296,26 @@ protected LKQLList onEmptyList(@SuppressWarnings("unused") LKQLList list) {
295296
}
296297
}
297298

299+
@BuiltInMethod(
300+
name = "combine",
301+
doc = "Combine two LKQL values if possible and return the result, recursively if required",
302+
targetTypes = {
303+
LKQLTypesHelper.LKQL_OBJECT, LKQLTypesHelper.LKQL_LIST, LKQLTypesHelper.LKQL_STRING,
304+
}
305+
)
306+
abstract static class CombineExpr extends BuiltInBody {
307+
308+
@Specialization
309+
protected Object onAll(
310+
Object left,
311+
Object right,
312+
@DefaultVal("true") boolean recursive,
313+
@Cached ValueCombiner combiner
314+
) {
315+
return combiner.execute(left, right, recursive, this.callNode);
316+
}
317+
}
318+
298319
@BuiltInFunction(name = "map", doc = "Given a collection, a mapping function")
299320
abstract static class MapExpr extends BuiltInBody {
300321

lkql_jit/language/src/main/java/com/adacore/lkql_jit/exception/LKQLRuntimeException.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import com.adacore.lkql_jit.utils.source_location.SourceLocation;
1212
import com.adacore.lkql_jit.utils.source_location.SourceSectionWrapper;
1313
import com.oracle.truffle.api.CompilerDirectives;
14+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
1415
import com.oracle.truffle.api.exception.AbstractTruffleException;
1516
import com.oracle.truffle.api.nodes.Node;
1617
import com.oracle.truffle.api.source.Source;
@@ -386,6 +387,18 @@ public static LKQLRuntimeException nullReceiver(Node location) {
386387
return LKQLRuntimeException.fromMessage("Null receiver in dot access", location);
387388
}
388389

390+
/** Create an exception when there is a collision during an object combination. */
391+
@TruffleBoundary
392+
public static LKQLRuntimeException objectCombiningCollision(
393+
String collidingMember,
394+
Node location
395+
) {
396+
return LKQLRuntimeException.fromMessage(
397+
"Cannot combine objects, both define the \"" + collidingMember + "\" member",
398+
location
399+
);
400+
}
401+
389402
// --- Argument exception
390403

391404
/**

lkql_jit/language/src/main/java/com/adacore/lkql_jit/nodes/utils/ConcatenationNode.java

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,12 @@
77

88
import com.adacore.lkql_jit.exception.LKQLRuntimeException;
99
import com.adacore.lkql_jit.nodes.LKQLNode;
10-
import com.adacore.lkql_jit.runtime.values.LKQLObject;
1110
import com.adacore.lkql_jit.runtime.values.lists.LKQLList;
12-
import com.adacore.lkql_jit.utils.Constants;
1311
import com.adacore.lkql_jit.utils.LKQLTypesHelper;
1412
import com.adacore.lkql_jit.utils.functions.StringUtils;
15-
import com.oracle.truffle.api.dsl.Cached;
1613
import com.oracle.truffle.api.dsl.Fallback;
1714
import com.oracle.truffle.api.dsl.Specialization;
18-
import com.oracle.truffle.api.library.CachedLibrary;
1915
import com.oracle.truffle.api.nodes.Node;
20-
import com.oracle.truffle.api.object.DynamicObjectLibrary;
2116

2217
public abstract class ConcatenationNode extends Node {
2318

@@ -43,48 +38,6 @@ protected LKQLList doLists(LKQLList left, LKQLList right, LKQLNode caller) {
4338
return new LKQLList(resContent);
4439
}
4540

46-
@Specialization(limit = Constants.SPECIALIZED_LIB_LIMIT)
47-
protected LKQLObject doObjects(
48-
LKQLObject left,
49-
LKQLObject right,
50-
LKQLNode caller,
51-
@CachedLibrary("left") DynamicObjectLibrary leftLib,
52-
@CachedLibrary("right") DynamicObjectLibrary rightLib,
53-
@CachedLibrary(limit = Constants.DISPATCHED_LIB_LIMIT) DynamicObjectLibrary resLib,
54-
@Cached ConcatenationNode innerConcat
55-
) {
56-
// Create the result object
57-
LKQLObject res = new LKQLObject(LKQLObject.emptyShape());
58-
59-
// Insert all keys of the left object in the result, resolving conflicts by merging
60-
// values.
61-
for (var key : leftLib.getKeyArray(left)) {
62-
if (!rightLib.containsKey(right, key)) {
63-
resLib.put(res, key, leftLib.getOrDefault(left, key, null));
64-
} else {
65-
resLib.put(
66-
res,
67-
key,
68-
innerConcat.execute(
69-
leftLib.getOrDefault(left, key, null),
70-
rightLib.getOrDefault(right, key, null),
71-
caller
72-
)
73-
);
74-
}
75-
}
76-
77-
// Insert keys from the right object that aren't in the resulting object
78-
for (var key : rightLib.getKeyArray(right)) {
79-
if (!resLib.containsKey(res, key)) {
80-
resLib.put(res, key, rightLib.getOrDefault(right, key, null));
81-
}
82-
}
83-
84-
// Return the resulting object
85-
return res;
86-
}
87-
8841
@Fallback
8942
protected void nonConcatenable(Object left, Object right, LKQLNode caller) {
9043
throw LKQLRuntimeException.unsupportedOperation(
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
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.utils;
7+
8+
import com.adacore.lkql_jit.exception.LKQLRuntimeException;
9+
import com.adacore.lkql_jit.nodes.LKQLNode;
10+
import com.adacore.lkql_jit.runtime.values.LKQLObject;
11+
import com.adacore.lkql_jit.utils.Constants;
12+
import com.oracle.truffle.api.dsl.Cached;
13+
import com.oracle.truffle.api.dsl.Fallback;
14+
import com.oracle.truffle.api.dsl.Specialization;
15+
import com.oracle.truffle.api.library.CachedLibrary;
16+
import com.oracle.truffle.api.nodes.Node;
17+
import com.oracle.truffle.api.object.DynamicObjectLibrary;
18+
19+
public abstract class ValueCombiner extends Node {
20+
21+
// ----- Execution methods -----
22+
23+
/** Combine two values. */
24+
public abstract Object execute(Object left, Object right, boolean recursive, LKQLNode caller);
25+
26+
// ----- Specializations -----
27+
28+
@Specialization(limit = Constants.SPECIALIZED_LIB_LIMIT)
29+
protected LKQLObject onObjects(
30+
LKQLObject left,
31+
LKQLObject right,
32+
boolean recursive,
33+
LKQLNode caller,
34+
@CachedLibrary("left") DynamicObjectLibrary leftLib,
35+
@CachedLibrary("right") DynamicObjectLibrary rightLib,
36+
@CachedLibrary(limit = Constants.DISPATCHED_LIB_LIMIT) DynamicObjectLibrary resLib,
37+
@Cached ValueCombiner recursiveCombiner
38+
) {
39+
// Create the result object
40+
LKQLObject res = new LKQLObject(LKQLObject.emptyShape());
41+
42+
// Insert all keys of the left object in the result, resolving conflicts by combining
43+
// values.
44+
for (var key : leftLib.getKeyArray(left)) {
45+
if (!rightLib.containsKey(right, key)) {
46+
resLib.put(res, key, leftLib.getOrDefault(left, key, null));
47+
} else if (recursive) {
48+
resLib.put(
49+
res,
50+
key,
51+
recursiveCombiner.execute(
52+
leftLib.getOrDefault(left, key, null),
53+
rightLib.getOrDefault(right, key, null),
54+
recursive,
55+
caller
56+
)
57+
);
58+
} else {
59+
throw LKQLRuntimeException.objectCombiningCollision((String) key, caller);
60+
}
61+
}
62+
63+
// Insert keys from the right object that aren't in the resulting object
64+
for (var key : rightLib.getKeyArray(right)) {
65+
if (!resLib.containsKey(res, key)) {
66+
resLib.put(res, key, rightLib.getOrDefault(right, key, null));
67+
}
68+
}
69+
70+
// Return the resulting object
71+
return res;
72+
}
73+
74+
@Fallback
75+
protected Object onOthers(
76+
Object left,
77+
Object right,
78+
boolean recursive,
79+
LKQLNode caller,
80+
@Cached ConcatenationNode concatNode
81+
) {
82+
return concatNode.execute(left, right, caller);
83+
}
84+
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import null_stmt
22

3-
val rules = null_stmt.rules & @{
4-
Goto_Statements
5-
}
3+
val rules = null_stmt.rules.combine(
4+
@{ Goto_Statements }
5+
)
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import part_one
22

3-
val rules = part_one.rules & @{
4-
Goto_Statements
5-
}
3+
val rules = part_one.rules.combine(
4+
@{ Goto_Statements }
5+
)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
import null_stmt
22

3-
val rules = null_stmt.rules @{}
3+
val rules = null_stmt.rules.combine(@{})
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
val rules = @{
2+
Redundant_Null_Statements
3+
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import part_two
22

3-
val rules = part_two.rules & @{
4-
Redundant_Null_Statements
5-
}
3+
val rules = part_two.rules.combine(
4+
@{ Redundant_Null_Statements }
5+
)

0 commit comments

Comments
 (0)