Skip to content

Commit da53bca

Browse files
committed
Change '@' object semantics to ensure values are valid lists
@-objects default value is now a list with an empty object as sole element. This is way more convenient for LKQL rule files.
1 parent 10f0797 commit da53bca

File tree

9 files changed

+101
-88
lines changed

9 files changed

+101
-88
lines changed

lkql_checker/doc/gnatcheck_rm/using_gnatcheck.rst

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -456,9 +456,9 @@ it was provided by the --rule-file option.
456456
An LKQL rule file can be any valid LKQL file, the only requirement is that it
457457
must export a ``rules`` top-level symbol. This symbol defines an object value
458458
containing rules configuration; keys are GNATcheck rules to enable; and values
459-
are objects containing the rule parameters. A rule parameter value can be of
460-
the boolean, the integer, the string, or the list of strings type, as shown in
461-
the simple example below:
459+
are list of objects, each one representing an instance of the rule with its
460+
parameters. A rule parameter value can be of the boolean, the integer, the
461+
string, or the list of strings type, as shown in the simple example below:
462462

463463
::
464464

@@ -467,6 +467,9 @@ the simple example below:
467467
Forbidden_Attributes: {Forbidden: ["GNAT"], Allowed: ["First", "Last"]}
468468
}
469469

470+
Using the "@" object notation is strongly advised to make your configuration
471+
file way more understandable:
472+
470473
Please read the :ref:`Predefined_Rules` documentation to view examples on how
471474
to provide parameters to rules through LKQL rule files.
472475

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

Lines changed: 35 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -399,50 +399,19 @@ private static void processInstancesObject(
399399

400400
// Check that the value associated to the rule name is an array like value
401401
if (args.hasArrayElements()) {
402-
// If there is no element in the argument list, just create an instance with no
403-
// argument and no alias.
404-
if (args.getArraySize() == 0) {
405-
if (toPopulate.containsKey(lowerRuleName)) {
406-
errorInLKQLRuleFile(
407-
lkqlRuleFile,
408-
"Multiple instances with the same name: " + lowerRuleName
409-
);
410-
} else {
411-
toPopulate.put(
412-
lowerRuleName,
413-
new RuleInstance(
414-
lowerRuleName,
415-
Optional.empty(),
416-
sourceMode,
417-
new HashMap<>()
418-
)
419-
);
420-
}
421-
}
422402
// Else iterate over each argument object and create one instance for each
423-
else {
424-
for (long i = 0; i < args.getArraySize(); i++) {
425-
processArgsObject(
426-
lkqlRuleFile,
427-
args.getArrayElement(i),
428-
sourceMode,
429-
lowerRuleName,
430-
toPopulate
431-
);
403+
for (long i = 0; i < args.getArraySize(); i++) {
404+
var arg = args.getArrayElement(i);
405+
if (arg.hasMembers()) {
406+
processArgsObject(lkqlRuleFile, arg, sourceMode, lowerRuleName, toPopulate);
407+
} else if (acceptSoleArgs(ruleName) && arg.isString()) {
408+
processSoleArg(arg, sourceMode, ruleName, toPopulate);
409+
} else {
410+
errorInLKQLRuleFile(lkqlRuleFile, "Arguments should be in an object value");
432411
}
433412
}
434-
} else if (args.hasMembers()) {
435-
processArgsObject(lkqlRuleFile, args, sourceMode, lowerRuleName, toPopulate);
436413
} else {
437-
// Allow sole arguments for some rules
438-
if (acceptSoleArgs(lowerRuleName) && args.isString()) {
439-
processSoleArg(args, sourceMode, lowerRuleName, toPopulate);
440-
} else {
441-
errorInLKQLRuleFile(
442-
lkqlRuleFile,
443-
"Rule arguments must be an object or indexable value"
444-
);
445-
}
414+
errorInLKQLRuleFile(lkqlRuleFile, "The value associated to a rule must be a list");
446415
}
447416
}
448417
}
@@ -455,43 +424,36 @@ private static void processArgsObject(
455424
final String ruleName,
456425
final Map<String, RuleInstance> toPopulate
457426
) throws LKQLRuleFileError {
458-
// Ensure that the given value has members (is an object)
459-
if (!argsObject.hasMembers()) {
460-
errorInLKQLRuleFile(lkqlRuleFile, "Arguments should be in an object value");
461-
}
462-
// If this is a valid value, process arguments in it
463-
else {
464-
// Compute the instance arguments and optional instance name
465-
String instanceId = ruleName;
466-
Optional<String> instanceName = Optional.empty();
467-
Map<String, String> arguments = new HashMap<>();
468-
for (String argName : argsObject.getMemberKeys()) {
469-
if (argName.equals("instance_name")) {
470-
String aliasName = argsObject.getMember("instance_name").asString();
471-
instanceId = aliasName.toLowerCase();
472-
instanceName = Optional.of(aliasName);
473-
} else {
474-
Value argValue = argsObject.getMember(argName);
475-
arguments.put(
476-
argName,
477-
argValue.isString() ? "\"" + argValue + "\"" : argValue.toString()
478-
);
479-
}
480-
}
481-
482-
// Add an instance in the instance map if it is not present
483-
if (toPopulate.containsKey(instanceId)) {
484-
errorInLKQLRuleFile(
485-
lkqlRuleFile,
486-
"Multiple instances with the same name: " + instanceId
487-
);
427+
// Compute the instance arguments and optional instance name
428+
String instanceId = ruleName;
429+
Optional<String> instanceName = Optional.empty();
430+
Map<String, String> arguments = new HashMap<>();
431+
for (String argName : argsObject.getMemberKeys()) {
432+
if (argName.equals("instance_name")) {
433+
String aliasName = argsObject.getMember("instance_name").asString();
434+
instanceId = aliasName.toLowerCase();
435+
instanceName = Optional.of(aliasName);
488436
} else {
489-
toPopulate.put(
490-
instanceId,
491-
new RuleInstance(ruleName, instanceName, sourceMode, arguments)
437+
Value argValue = argsObject.getMember(argName);
438+
arguments.put(
439+
argName,
440+
argValue.isString() ? "\"" + argValue + "\"" : argValue.toString()
492441
);
493442
}
494443
}
444+
445+
// Add an instance in the instance map if it is not present
446+
if (toPopulate.containsKey(instanceId)) {
447+
errorInLKQLRuleFile(
448+
lkqlRuleFile,
449+
"Multiple instances with the same name: " + instanceId
450+
);
451+
} else {
452+
toPopulate.put(
453+
instanceId,
454+
new RuleInstance(ruleName, instanceName, sourceMode, arguments)
455+
);
456+
}
495457
}
496458

497459
/** Internal function to process a sole string argument for a compiler-based rule. */

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.adacore.lkql_jit.nodes.expressions.list_comprehension.ComprehensionAssoc;
3232
import com.adacore.lkql_jit.nodes.expressions.list_comprehension.ComprehensionAssocList;
3333
import com.adacore.lkql_jit.nodes.expressions.list_comprehension.ListComprehension;
34+
import com.adacore.lkql_jit.nodes.expressions.literals.ObjectLiteralFactory.AtObjectValueWrapperNodeGen;
3435
import com.adacore.lkql_jit.nodes.expressions.literals.*;
3536
import com.adacore.lkql_jit.nodes.expressions.match.Match;
3637
import com.adacore.lkql_jit.nodes.expressions.match.MatchArm;
@@ -1336,8 +1337,11 @@ public LKQLNode visit(Liblkqllang.AtObjectLiteral atObjectLiteral) {
13361337
keys.add(key);
13371338
final Liblkqllang.Expr exprBase = assoc.fExpr();
13381339
values[i] = exprBase.isNone()
1339-
? new ListLiteral(loc(assoc.fName()), new Expr[0])
1340-
: (Expr) exprBase.accept(this);
1340+
? new ListLiteral(
1341+
loc(assoc.fName()),
1342+
new Expr[] { new ObjectLiteral(loc(assoc.fName()), new String[0], new Expr[0]) }
1343+
)
1344+
: AtObjectValueWrapperNodeGen.create(loc(exprBase), (Expr) exprBase.accept(this));
13411345
}
13421346

13431347
// Return the new object literal node because at object is juste syntactic sugar

lkql_jit/language/src/main/java/com/adacore/lkql_jit/nodes/expressions/literals/ObjectLiteral.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
import com.adacore.lkql_jit.nodes.expressions.Expr;
99
import com.adacore.lkql_jit.runtime.values.LKQLObject;
10+
import com.adacore.lkql_jit.runtime.values.lists.LKQLList;
11+
import com.oracle.truffle.api.dsl.Fallback;
12+
import com.oracle.truffle.api.dsl.NodeChild;
13+
import com.oracle.truffle.api.dsl.Specialization;
1014
import com.oracle.truffle.api.frame.VirtualFrame;
1115
import com.oracle.truffle.api.nodes.ExplodeLoop;
1216
import com.oracle.truffle.api.object.DynamicObjectLibrary;
@@ -94,4 +98,36 @@ public LKQLObject executeObject(final VirtualFrame frame) {
9498
public String toString(final int indentLevel) {
9599
return this.nodeRepresentation(indentLevel);
96100
}
101+
102+
// ----- Inner classes -----
103+
104+
/** Wrapper node ensuring values of "@-objects" are lists. */
105+
@NodeChild(value = "wrappedExpr", type = Expr.class)
106+
public abstract static class AtObjectValueWrapper extends Expr {
107+
108+
// ----- Constructors -----
109+
110+
public AtObjectValueWrapper(SourceSection location) {
111+
super(location);
112+
}
113+
114+
// ----- Specializations -----
115+
116+
@Specialization
117+
protected LKQLList onList(LKQLList list) {
118+
return list;
119+
}
120+
121+
@Fallback
122+
protected LKQLList onOthers(Object obj) {
123+
return new LKQLList(new Object[] { obj });
124+
}
125+
126+
// ----- Override methods -----
127+
128+
@Override
129+
public String toString(int indentLevel) {
130+
return this.nodeRepresentation(indentLevel);
131+
}
132+
}
97133
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
val rules = @{
1+
val rules = {
22
identifier_suffixes: "huho"
33
}

testsuite/tests/gnatcheck_errors/invalid_lkql_rules_config/test.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ try "gnatcheck --help" for more information.
4343
Not valid arguments
4444
===================
4545

46-
gnatcheck: error: Rule arguments must be an object or indexable value (not_valid_args.lkql)
46+
gnatcheck: error: The value associated to a rule must be a list (not_valid_args.lkql)
4747
gnatcheck: error: no rule to check specified
4848
try "gnatcheck --help" for more information.
4949
>>>program returned status code 2

testsuite/tests/interpreter/at_object_literal/script.lkql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ print(x.key1.length)
99
print(x.key2)
1010
print(x.key2.length)
1111
print(x.key3)
12+
13+
print(x)
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
[]
2-
0
1+
[{}]
2+
1
33
[1, 2, 3]
44
3
5-
Coucou
5+
["Coucou"]
6+
{"key1": [{}], "key2": [1, 2, 3], "key3": ["Coucou"]}

user_manual/source/language_reference.rst

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -503,16 +503,21 @@ An object literal is a literal representation of an object value:
503503
.. raw:: html
504504
:file: ../../lkql/build/railroad-diagrams/at_object_lit.svg
505505

506-
"@" preceded object literals are similar to standard object literal with an
507-
empty list as default value for any key:
506+
"@" object literals are quite the same as standard objects literals, but each
507+
associated value is wrapped in a list (if not already one). You are also
508+
allowed to omit the associated expression when adding a key in the object. The
509+
default associated value is a list with only one element: an empty object.
508510

509511
.. code-block:: lkql
510512
511-
# At object literal
512-
@{a: 1, b, c: null, d}
513+
# @-object literal
514+
@{a: "Hello", b, c: 42, d}
513515
514516
# Is similar to
515-
{a: 1, b: [], c: null, d: []}
517+
{a: ["Hello"], b: [{}], c: [42], d: [{}]}
518+
519+
This "@" object notation are mainly used to express coding standards in LKQL
520+
rule configuration files, however, you can use it in any context.
516521

517522
Object keys may contain upper-case characters at declaration, but the LKQL
518523
engine will lower them. This means that object keys are case-insensitive:

0 commit comments

Comments
 (0)