@abaplint/core 2.115.28 → 2.116.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/src/cds/cds_lexer.js +10 -2
- package/build/src/cds/expressions/cds_aggregate.js +5 -2
- package/build/src/cds/expressions/cds_annotation.js +2 -2
- package/build/src/cds/expressions/cds_annotation_simple.js +4 -1
- package/build/src/cds/expressions/cds_arithmetics.js +3 -2
- package/build/src/cds/expressions/cds_as.js +3 -1
- package/build/src/cds/expressions/cds_association.js +7 -1
- package/build/src/cds/expressions/cds_cardinality.js +1 -1
- package/build/src/cds/expressions/cds_composition.js +6 -1
- package/build/src/cds/expressions/cds_condition.js +1 -1
- package/build/src/cds/expressions/cds_define_hierarchy.js +1 -1
- package/build/src/cds/expressions/cds_define_projection.js +1 -1
- package/build/src/cds/expressions/cds_define_table_function.js +1 -1
- package/build/src/cds/expressions/cds_define_view.js +1 -1
- package/build/src/cds/expressions/cds_function.js +5 -1
- package/build/src/cds/expressions/cds_join.js +1 -1
- package/build/src/cds/expressions/cds_select.js +2 -1
- package/build/src/cds/expressions/cds_source.js +3 -2
- package/build/src/cds/expressions/cds_string.js +3 -2
- package/build/src/cds/expressions/cds_with_parameters.js +1 -1
- package/build/src/registry.js +1 -1
- package/build/src/rules/aff_and_xml.js +57 -0
- package/build/src/rules/definitions_top.js +2 -2
- package/build/src/rules/index.js +1 -0
- package/package.json +3 -3
|
@@ -56,7 +56,7 @@ class CDSLexer {
|
|
|
56
56
|
let row = 1;
|
|
57
57
|
let col = 1;
|
|
58
58
|
let build = "";
|
|
59
|
-
const stream = new Stream(file.getRaw().replace(/\r/g, "").replace(/\u00a0/g, " "));
|
|
59
|
+
const stream = new Stream(file.getRaw().replace(/\r/g, "").replace(/\u00a0/g, " ").replace(/\u000b/g, " "));
|
|
60
60
|
let next = "";
|
|
61
61
|
while (stream.length() > 0) {
|
|
62
62
|
const prev = next;
|
|
@@ -67,7 +67,12 @@ class CDSLexer {
|
|
|
67
67
|
if (mode === Mode.String) {
|
|
68
68
|
build += next;
|
|
69
69
|
if (next === "'" && nextNext === "'") {
|
|
70
|
-
// escaped single quote, continue string
|
|
70
|
+
// escaped single quote (doubled), continue string
|
|
71
|
+
build += stream.takeNext();
|
|
72
|
+
col++;
|
|
73
|
+
}
|
|
74
|
+
else if (next === "\\" && nextNext === "'") {
|
|
75
|
+
// backslash-escaped single quote, continue string
|
|
71
76
|
build += stream.takeNext();
|
|
72
77
|
col++;
|
|
73
78
|
}
|
|
@@ -113,6 +118,8 @@ class CDSLexer {
|
|
|
113
118
|
else if (mode === Mode.Default && next === "/" && nextNext === "*") {
|
|
114
119
|
mode = Mode.MultiLineComment;
|
|
115
120
|
build = result.add(build, row, col, mode);
|
|
121
|
+
stream.takeNext(); // consume the '*' so it doesn't become prev for '*/' detection
|
|
122
|
+
col++;
|
|
116
123
|
continue;
|
|
117
124
|
}
|
|
118
125
|
switch (next) {
|
|
@@ -140,6 +147,7 @@ class CDSLexer {
|
|
|
140
147
|
case ")":
|
|
141
148
|
case "[":
|
|
142
149
|
case "]":
|
|
150
|
+
case "!":
|
|
143
151
|
case "=":
|
|
144
152
|
case "<":
|
|
145
153
|
case ">":
|
|
@@ -5,8 +5,11 @@ const _1 = require(".");
|
|
|
5
5
|
const combi_1 = require("../../abap/2_statements/combi");
|
|
6
6
|
class CDSAggregate extends combi_1.Expression {
|
|
7
7
|
getRunnable() {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
// CDSPrefixedName handles dotted paths with path filters e.g. a._Assoc[filter].Field
|
|
9
|
+
// CDSArithmetics handles expressions like sum(A + B), max(A * 100)
|
|
10
|
+
// fieldAsType handles avg(field AS type) / sum(field AS type) — SAP inline type coercion
|
|
11
|
+
const fieldAsType = (0, combi_1.seq)(_1.CDSPrefixedName, "AS", _1.CDSType);
|
|
12
|
+
const value = (0, combi_1.altPrio)(_1.CDSArithmetics, _1.CDSCast, _1.CDSCase, _1.CDSFunction, fieldAsType, _1.CDSPrefixedName, _1.CDSString, "*");
|
|
10
13
|
return (0, combi_1.seq)((0, combi_1.altPrio)("MAX", "MIN", "SUM", "AVG", "COUNT"), "(", (0, combi_1.opt)("DISTINCT"), value, ")");
|
|
11
14
|
}
|
|
12
15
|
}
|
|
@@ -7,8 +7,8 @@ const cds_annotation_array_1 = require("./cds_annotation_array");
|
|
|
7
7
|
class CDSAnnotation extends combi_1.Expression {
|
|
8
8
|
getRunnable() {
|
|
9
9
|
const nameWithSlash = (0, combi_1.seq)((0, combi_1.regex)(/^\w+$/), (0, combi_1.star)((0, combi_1.seq)("/", (0, combi_1.regex)(/^\w+$/))));
|
|
10
|
-
// Support both "@Name" (single token)
|
|
11
|
-
const annotationStart = (0, combi_1.alt)((0, combi_1.regex)(/^@\w+$/), (0, combi_1.seq)("@", (0, combi_1.regex)(/^\w+$/)));
|
|
10
|
+
// Support both "@Name" (single token), "@ Name" (two tokens), and "@< Name" (inline backward annotation)
|
|
11
|
+
const annotationStart = (0, combi_1.alt)((0, combi_1.regex)(/^@\w+$/), (0, combi_1.seq)("@", (0, combi_1.regex)(/^\w+$/)), (0, combi_1.seq)("@", "<", (0, combi_1.regex)(/^\w+$/)));
|
|
12
12
|
return (0, combi_1.seq)(annotationStart, (0, combi_1.star)((0, combi_1.seq)(".", nameWithSlash)), (0, combi_1.opt)((0, combi_1.seq)(":", (0, combi_1.alt)(cds_annotation_array_1.CDSAnnotationArray, _1.CDSAnnotationObject, _1.CDSAnnotationSimple))));
|
|
13
13
|
}
|
|
14
14
|
}
|
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CDSAnnotationSimple = void 0;
|
|
4
4
|
const _1 = require(".");
|
|
5
|
+
const cds_prefixed_name_1 = require("./cds_prefixed_name");
|
|
5
6
|
const combi_1 = require("../../abap/2_statements/combi");
|
|
6
7
|
class CDSAnnotationSimple extends combi_1.Expression {
|
|
7
8
|
getRunnable() {
|
|
8
|
-
const value = (0, combi_1.alt)(_1.CDSString, "true", "false", "null", (0, combi_1.seq)("-", (0, combi_1.regex)(/^\d+$/)), (0, combi_1.regex)(/^\d+$/), (0, combi_1.seq)((0, combi_1.regex)(/^\d+$/), ".", (0, combi_1.regex)(/^\d+$/)), (0, combi_1.seq)("#", "(", _1.CDSString, ")"),
|
|
9
|
+
const value = (0, combi_1.alt)(_1.CDSString, "true", "false", "null", (0, combi_1.seq)("-", (0, combi_1.regex)(/^\d+$/)), (0, combi_1.regex)(/^\d+$/), (0, combi_1.seq)((0, combi_1.regex)(/^\d+$/), ".", (0, combi_1.regex)(/^\d+$/)), (0, combi_1.seq)("#", "(", _1.CDSString, ")"),
|
|
10
|
+
// #(_Header.Field) — dotted path inside #(...)
|
|
11
|
+
(0, combi_1.seq)("#", "(", cds_prefixed_name_1.CDSPrefixedName, ")"), (0, combi_1.seq)("#", "(", (0, combi_1.regex)(/^[\w_]+$/), ")"), (0, combi_1.regex)(/^#[\w_]+$/));
|
|
9
12
|
return value;
|
|
10
13
|
}
|
|
11
14
|
}
|
|
@@ -28,10 +28,11 @@ class CDSArithmetics extends combi_1.Expression {
|
|
|
28
28
|
// Unary operator prefix, e.g. -field, +field
|
|
29
29
|
const unary = (0, combi_1.altPrio)("-", "+");
|
|
30
30
|
const unaryExpression = (0, combi_1.seq)(unary, val);
|
|
31
|
-
// An operand is
|
|
31
|
+
// An operand is a paren, unary-prefixed value, or bare value.
|
|
32
|
+
// Including unaryExpression allows "A + + B" and "A + -B" patterns.
|
|
32
33
|
// CDSArithParen = "(" altPrio(CDSArithmetics, CDSArithParen, val) ")" — separate singleton that
|
|
33
34
|
// can recursively contain itself, enabling deeply nested parentheses without infinite recursion.
|
|
34
|
-
const operand = (0, combi_1.altPrio)(_1.CDSArithParen, val);
|
|
35
|
+
const operand = (0, combi_1.altPrio)(_1.CDSArithParen, unaryExpression, val);
|
|
35
36
|
const operatorValue = (0, combi_1.seq)(operator, operand);
|
|
36
37
|
// Main form: operand op operand op ... (leading term may itself be a paren)
|
|
37
38
|
const withOperators = (0, combi_1.seq)(operand, (0, combi_1.plusPrio)(operatorValue));
|
|
@@ -5,7 +5,9 @@ const _1 = require(".");
|
|
|
5
5
|
const combi_1 = require("../../abap/2_statements/combi");
|
|
6
6
|
class CDSAs extends combi_1.Expression {
|
|
7
7
|
getRunnable() {
|
|
8
|
-
|
|
8
|
+
// Greedy opt (not optPrio) — avoids exponential backtracking in CDSElement
|
|
9
|
+
const colonType = (0, combi_1.seq)(":", (0, combi_1.alt)(_1.CDSType, _1.CDSName, "LOCALIZED"));
|
|
10
|
+
return (0, combi_1.seq)("AS", _1.CDSName, (0, combi_1.opt)(colonType));
|
|
9
11
|
}
|
|
10
12
|
}
|
|
11
13
|
exports.CDSAs = CDSAs;
|
|
@@ -6,7 +6,13 @@ const combi_1 = require("../../abap/2_statements/combi");
|
|
|
6
6
|
const cds_cardinality_1 = require("./cds_cardinality");
|
|
7
7
|
class CDSAssociation extends combi_1.Expression {
|
|
8
8
|
getRunnable() {
|
|
9
|
-
|
|
9
|
+
// Text cardinality: "to exact one", "to one", "to many" — no brackets
|
|
10
|
+
const textCardinality = (0, combi_1.seq)((0, combi_1.opt)("EXACT"), (0, combi_1.altPrio)("ONE", "MANY"));
|
|
11
|
+
// "association of one to many Target as _Alias on condition" — OF + cardinality form
|
|
12
|
+
const ofForm = (0, combi_1.seq)("ASSOCIATION", "OF", cds_cardinality_1.CDSCardinality, "TO", _1.CDSRelation, "ON", _1.CDSCondition);
|
|
13
|
+
// "association [0..1] to Target as _Alias on condition" — standard form
|
|
14
|
+
const standardForm = (0, combi_1.seq)("ASSOCIATION", (0, combi_1.opt)(cds_cardinality_1.CDSCardinality), "TO", (0, combi_1.opt)((0, combi_1.altPrio)(textCardinality, "PARENT")), _1.CDSRelation, "ON", _1.CDSCondition, (0, combi_1.opt)((0, combi_1.seq)("WITH", "DEFAULT", "FILTER", _1.CDSCondition)));
|
|
15
|
+
return (0, combi_1.altPrio)(ofForm, standardForm);
|
|
10
16
|
}
|
|
11
17
|
}
|
|
12
18
|
exports.CDSAssociation = CDSAssociation;
|
|
@@ -6,7 +6,7 @@ class CDSCardinality extends combi_1.Expression {
|
|
|
6
6
|
getRunnable() {
|
|
7
7
|
const numeric = (0, combi_1.seq)("[", (0, combi_1.alt)("0", "1", "*"), (0, combi_1.opt)((0, combi_1.seq)(".", ".", (0, combi_1.alt)("0", "1", "*"))), "]");
|
|
8
8
|
const num = (0, combi_1.alt)("ONE", "MANY");
|
|
9
|
-
const text = (0, combi_1.seq)(
|
|
9
|
+
const text = (0, combi_1.seq)(num, "TO", num);
|
|
10
10
|
return (0, combi_1.alt)(numeric, text);
|
|
11
11
|
}
|
|
12
12
|
}
|
|
@@ -6,7 +6,12 @@ const combi_1 = require("../../abap/2_statements/combi");
|
|
|
6
6
|
const cds_cardinality_1 = require("./cds_cardinality");
|
|
7
7
|
class CDSComposition extends combi_1.Expression {
|
|
8
8
|
getRunnable() {
|
|
9
|
-
|
|
9
|
+
const num = (0, combi_1.altPrio)("ONE", "MANY");
|
|
10
|
+
// Text cardinality after OF: "of exact one to many", "of one to many", or bare "of many" / "of one"
|
|
11
|
+
const textCardinality = (0, combi_1.altPrio)((0, combi_1.seq)((0, combi_1.opt)("EXACT"), num, "TO", num), (0, combi_1.seq)((0, combi_1.opt)("EXACT"), num));
|
|
12
|
+
// Only numeric cardinality [0..*] may appear before OF; text cardinality goes after OF.
|
|
13
|
+
const numericCardinality = (0, combi_1.seq)("[", (0, combi_1.altPrio)("0", "1", "*"), (0, combi_1.opt)((0, combi_1.seq)(".", ".", (0, combi_1.altPrio)("0", "1", "*"))), "]");
|
|
14
|
+
return (0, combi_1.seq)("COMPOSITION", (0, combi_1.opt)(numericCardinality), "OF", (0, combi_1.opt)((0, combi_1.altPrio)(cds_cardinality_1.CDSCardinality, textCardinality)), _1.CDSRelation);
|
|
10
15
|
}
|
|
11
16
|
}
|
|
12
17
|
exports.CDSComposition = CDSComposition;
|
|
@@ -6,7 +6,7 @@ const combi_1 = require("../../abap/2_statements/combi");
|
|
|
6
6
|
const cds_integer_1 = require("./cds_integer");
|
|
7
7
|
class CDSCondition extends combi_1.Expression {
|
|
8
8
|
getRunnable() {
|
|
9
|
-
const left = (0, combi_1.altPrio)(_1.CDSString, _1.CDSFunction, _1.CDSAggregate, _1.CDSPrefixedName);
|
|
9
|
+
const left = (0, combi_1.altPrio)(_1.CDSString, _1.CDSCast, _1.CDSFunction, _1.CDSAggregate, _1.CDSPrefixedName);
|
|
10
10
|
const nonLikeOperators = (0, combi_1.altPrio)("=", (0, combi_1.seq)("!", "="), (0, combi_1.seq)("<", ">"), (0, combi_1.seq)(">", "="), (0, combi_1.seq)("<", "="), "<", ">");
|
|
11
11
|
const likeOperators = (0, combi_1.altPrio)("LIKE", "NOT LIKE");
|
|
12
12
|
// Right side of comparison: simple values first, then parenthesized, then full arithmetic last.
|
|
@@ -10,7 +10,7 @@ class CDSDefineHierarchy extends combi_1.Expression {
|
|
|
10
10
|
const siblingsOrderField = (0, combi_1.seq)(_1.CDSPrefixedName, (0, combi_1.opt)(sortDirection));
|
|
11
11
|
const siblingsOrder = (0, combi_1.seq)("SIBLINGS", "ORDER", "BY", siblingsOrderField, (0, combi_1.star)((0, combi_1.seq)(",", siblingsOrderField)));
|
|
12
12
|
const directory = (0, combi_1.seq)("DIRECTORY", _1.CDSName, "FILTER", "BY", _1.CDSCondition);
|
|
13
|
-
const hierarchyBody = (0, combi_1.seq)("SOURCE", _1.CDSName, (0, combi_1.opt)(_1.CDSParametersSelect), "CHILD", "TO", "PARENT", "ASSOCIATION", _1.CDSName, (0, combi_1.opt)(directory), (0, combi_1.opt)((0, combi_1.seq)("START", "WHERE", _1.CDSCondition)), (0, combi_1.opt)(siblingsOrder), (0, combi_1.opt)((0, combi_1.seq)("NODETYPE", _1.CDSName)), (0, combi_1.opt)((0, combi_1.seq)("MULTIPLE", "PARENTS", "ALLOWED")), (0, combi_1.opt)((0, combi_1.seq)("ORPHANS", (0, combi_1.altPrio)("IGNORE", "ROOT"))), (0, combi_1.opt)((0, combi_1.seq)("CYCLES", "BREAKUP")), (0, combi_1.opt)((0, combi_1.seq)("CACHE", (0, combi_1.altPrio)("FORCE", "NONE", "EMPTY"))));
|
|
13
|
+
const hierarchyBody = (0, combi_1.seq)("SOURCE", _1.CDSName, (0, combi_1.opt)(_1.CDSParametersSelect), "CHILD", "TO", "PARENT", "ASSOCIATION", _1.CDSName, (0, combi_1.opt)(directory), (0, combi_1.opt)((0, combi_1.seq)("START", "WHERE", _1.CDSCondition)), (0, combi_1.opt)(siblingsOrder), (0, combi_1.opt)((0, combi_1.seq)("NODETYPE", _1.CDSName)), (0, combi_1.opt)((0, combi_1.seq)("DEPTH", _1.CDSInteger)), (0, combi_1.opt)((0, combi_1.seq)("MULTIPLE", "PARENTS", (0, combi_1.altPrio)("NOT ALLOWED", "ALLOWED"))), (0, combi_1.opt)((0, combi_1.seq)("ORPHANS", (0, combi_1.altPrio)("IGNORE", "ROOT"))), (0, combi_1.opt)((0, combi_1.seq)("CYCLES", "BREAKUP")), (0, combi_1.opt)((0, combi_1.seq)("CACHE", (0, combi_1.altPrio)("FORCE", "NONE", "EMPTY", "OFF"))));
|
|
14
14
|
return (0, combi_1.seq)((0, combi_1.star)(_1.CDSAnnotation), "DEFINE", "HIERARCHY", _1.CDSName, (0, combi_1.opt)(_1.CDSWithParameters), "AS", "PARENT", "CHILD", "HIERARCHY", "(", hierarchyBody, ")", "{", (0, combi_1.seq)(field, (0, combi_1.star)((0, combi_1.seq)(",", field))), "}", (0, combi_1.opt)(";"));
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -6,7 +6,7 @@ const __1 = require("../..");
|
|
|
6
6
|
const combi_1 = require("../../abap/2_statements/combi");
|
|
7
7
|
class CDSDefineProjection extends combi_1.Expression {
|
|
8
8
|
getRunnable() {
|
|
9
|
-
return (0, combi_1.seq)((0, combi_1.star)(_1.CDSAnnotation), "DEFINE", (0, combi_1.opt)("ROOT"), "VIEW", (0, combi_1.ver)(__1.Version.v755, (0, combi_1.opt)("ENTITY")), _1.CDSName, (0, combi_1.opt)(_1.CDSProviderContract), "AS PROJECTION ON", _1.CDSName, (0, combi_1.opt)(_1.CDSAs), (0, combi_1.str)("{"), (0, combi_1.
|
|
9
|
+
return (0, combi_1.seq)((0, combi_1.star)(_1.CDSAnnotation), "DEFINE", (0, combi_1.opt)("ROOT"), (0, combi_1.opt)("TRANSIENT"), "VIEW", (0, combi_1.ver)(__1.Version.v755, (0, combi_1.opt)("ENTITY")), _1.CDSName, (0, combi_1.opt)(_1.CDSProviderContract), (0, combi_1.opt)(_1.CDSWithParameters), "AS PROJECTION ON", _1.CDSName, (0, combi_1.opt)(_1.CDSParametersSelect), (0, combi_1.opt)(_1.CDSAs), (0, combi_1.star)(_1.CDSAssociation), (0, combi_1.str)("{"), (0, combi_1.seq)(_1.CDSElement, (0, combi_1.star)((0, combi_1.seq)(",", _1.CDSElement)), (0, combi_1.opt)(",")), (0, combi_1.str)("}"), (0, combi_1.opt)(_1.CDSWhere), (0, combi_1.opt)(";"));
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
exports.CDSDefineProjection = CDSDefineProjection;
|
|
@@ -7,7 +7,7 @@ const cds_name_1 = require("./cds_name");
|
|
|
7
7
|
class CDSDefineTableFunction extends combi_1.Expression {
|
|
8
8
|
getRunnable() {
|
|
9
9
|
const methodName = (0, combi_1.seq)(cds_name_1.CDSName, "=", ">", cds_name_1.CDSName);
|
|
10
|
-
return (0, combi_1.seq)((0, combi_1.star)(_1.CDSAnnotation), (0, combi_1.str)("DEFINE TABLE FUNCTION"), cds_name_1.CDSName, (0, combi_1.optPrio)(_1.CDSWithParameters), (0, combi_1.str)("RETURNS {"), (0, combi_1.plus)((0, combi_1.seq)((0, combi_1.optPrio)("KEY"), cds_name_1.CDSName, ":", _1.CDSType, ";")), (0, combi_1.str)("} IMPLEMENTED BY METHOD"), methodName, (0, combi_1.opt)(";"));
|
|
10
|
+
return (0, combi_1.seq)((0, combi_1.star)(_1.CDSAnnotation), (0, combi_1.str)("DEFINE TABLE FUNCTION"), cds_name_1.CDSName, (0, combi_1.optPrio)(_1.CDSWithParameters), (0, combi_1.str)("RETURNS {"), (0, combi_1.plus)((0, combi_1.seq)((0, combi_1.star)(_1.CDSAnnotation), (0, combi_1.optPrio)("KEY"), cds_name_1.CDSName, ":", _1.CDSType, ";")), (0, combi_1.str)("} IMPLEMENTED BY METHOD"), methodName, (0, combi_1.opt)(";"));
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
exports.CDSDefineTableFunction = CDSDefineTableFunction;
|
|
@@ -10,7 +10,7 @@ const cds_with_parameters_1 = require("./cds_with_parameters");
|
|
|
10
10
|
class CDSDefineView extends combi_1.Expression {
|
|
11
11
|
getRunnable() {
|
|
12
12
|
const columnAlias = (0, combi_1.seq)("(", cds_name_1.CDSName, (0, combi_1.star)((0, combi_1.seq)(",", cds_name_1.CDSName)), ")");
|
|
13
|
-
return (0, combi_1.seq)((0, combi_1.star)(_1.CDSAnnotation), "DEFINE", (0, combi_1.opt)("ROOT"), "VIEW", (0, combi_1.ver)(__1.Version.v755, (0, combi_1.opt)("ENTITY")), cds_name_1.CDSName, (0, combi_1.opt)(columnAlias), (0, combi_1.opt)(cds_with_parameters_1.CDSWithParameters), "AS", cds_select_1.CDSSelect, (0, combi_1.opt)(";"));
|
|
13
|
+
return (0, combi_1.seq)((0, combi_1.star)(_1.CDSAnnotation), (0, combi_1.opt)("DEFINE"), (0, combi_1.opt)("ROOT"), "VIEW", (0, combi_1.ver)(__1.Version.v755, (0, combi_1.opt)("ENTITY")), cds_name_1.CDSName, (0, combi_1.opt)(columnAlias), (0, combi_1.opt)(cds_with_parameters_1.CDSWithParameters), "AS", cds_select_1.CDSSelect, (0, combi_1.opt)(";"));
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
exports.CDSDefineView = CDSDefineView;
|
|
@@ -12,6 +12,7 @@ class CDSFunction extends combi_1.Expression {
|
|
|
12
12
|
const dats_add_months = (0, combi_1.seq)("DATS_ADD_MONTHS", "(", _1.CDSFunctionInput, ",", _1.CDSFunctionInput, ",", _1.CDSFunctionInput, ")");
|
|
13
13
|
const dats_days_between = (0, combi_1.seq)("DATS_DAYS_BETWEEN", "(", _1.CDSFunctionInput, ",", _1.CDSFunctionInput, ")");
|
|
14
14
|
const dats_is_valid = (0, combi_1.seq)("DATS_IS_VALID", "(", _1.CDSFunctionInput, ")");
|
|
15
|
+
const tims_is_valid = (0, combi_1.seq)("TIMS_IS_VALID", "(", _1.CDSFunctionInput, ")");
|
|
15
16
|
const substring = (0, combi_1.seq)("SUBSTRING", "(", _1.CDSFunctionInput, ",", _1.CDSFunctionInput, ",", _1.CDSFunctionInput, ")");
|
|
16
17
|
const bintohex = (0, combi_1.seq)("BINTOHEX", "(", _1.CDSFunctionInput, ")");
|
|
17
18
|
const hextobin = (0, combi_1.seq)("HEXTOBIN", "(", _1.CDSFunctionInput, ")");
|
|
@@ -29,6 +30,7 @@ class CDSFunction extends combi_1.Expression {
|
|
|
29
30
|
const dats_tims_to_tstmp = (0, combi_1.seq)("DATS_TIMS_TO_TSTMP", "(", _1.CDSFunctionInput, ",", _1.CDSFunctionInput, ",", _1.CDSFunctionInput, ",", _1.CDSFunctionInput, ",", _1.CDSFunctionInput, ")");
|
|
30
31
|
const tstmp_is_valid = (0, combi_1.seq)("TSTMP_IS_VALID", "(", _1.CDSFunctionInput, ")");
|
|
31
32
|
const tstmp_current_utctimestamp = (0, combi_1.seq)("TSTMP_CURRENT_UTCTIMESTAMP", "(", ")");
|
|
33
|
+
const utcl_current = (0, combi_1.seq)("UTCL_CURRENT", "(", ")");
|
|
32
34
|
const tstmp_seconds_between = (0, combi_1.seq)("TSTMP_SECONDS_BETWEEN", "(", _1.CDSFunctionInput, ",", _1.CDSFunctionInput, ",", _1.CDSFunctionInput, ")");
|
|
33
35
|
const tstmp_add_seconds = (0, combi_1.seq)("TSTMP_ADD_SECONDS", "(", _1.CDSFunctionInput, ",", _1.CDSFunctionInput, ",", _1.CDSFunctionInput, ")");
|
|
34
36
|
const abap_system_timezone = (0, combi_1.seq)("ABAP_SYSTEM_TIMEZONE", "(", _1.CDSFunctionInput, ",", _1.CDSFunctionInput, ")");
|
|
@@ -44,12 +46,14 @@ class CDSFunction extends combi_1.Expression {
|
|
|
44
46
|
const left = (0, combi_1.seq)("LEFT", "(", _1.CDSFunctionInput, ",", _1.CDSFunctionInput, ")");
|
|
45
47
|
const right = (0, combi_1.seq)("RIGHT", "(", _1.CDSFunctionInput, ",", _1.CDSFunctionInput, ")");
|
|
46
48
|
const fltp_to_dec = (0, combi_1.seq)("FLTP_TO_DEC", "(", _1.CDSFunctionInput, "AS", _1.CDSType, ")");
|
|
49
|
+
const curr_to_decfloat_amount = (0, combi_1.seq)("CURR_TO_DECFLOAT_AMOUNT", "(", _1.CDSFunctionInput, ")");
|
|
47
50
|
const conversionInput = (0, combi_1.seq)(_1.CDSName, "=", ">", _1.CDSFunctionInput);
|
|
48
51
|
const conversionInputs = (0, combi_1.seq)(conversionInput, (0, combi_1.starPrio)((0, combi_1.seq)(",", conversionInput)));
|
|
49
52
|
const unitConversion = (0, combi_1.seq)("UNIT_CONVERSION", "(", conversionInputs, ")");
|
|
50
53
|
const currencyConversion = (0, combi_1.seq)("CURRENCY_CONVERSION", "(", conversionInputs, ")");
|
|
51
54
|
const decimalShift = (0, combi_1.seq)("DECIMAL_SHIFT", "(", conversionInputs, ")");
|
|
52
|
-
|
|
55
|
+
const ratioOf = (0, combi_1.seq)("RATIO_OF", "(", conversionInputs, ")");
|
|
56
|
+
return (0, combi_1.altPrio)(substring, coalesce, tstmp_to_dats, concat, tstmp_to_tims, upper, lower, abs, ceil, floor, round, div, division, concat_with_space, dats_is_valid, tims_is_valid, dats_days_between, tstmp_add_seconds, tstmp_seconds_between, tstmp_current_utctimestamp, tstmp_is_valid, utcl_current, abap_system_timezone, abap_user_timezone, bintohex, hextobin, dats_add_days, dats_add_months, tstmp_to_dst, dats_tims_to_tstmp, mod, left, right, lpad, rpad, instr, length, ltrim, rtrim, replace, unitConversion, currencyConversion, decimalShift, fltp_to_dec, ratioOf, curr_to_decfloat_amount);
|
|
53
57
|
}
|
|
54
58
|
}
|
|
55
59
|
exports.CDSFunction = CDSFunction;
|
|
@@ -6,7 +6,7 @@ const combi_1 = require("../../abap/2_statements/combi");
|
|
|
6
6
|
const cds_condition_1 = require("./cds_condition");
|
|
7
7
|
class CDSJoin extends combi_1.Expression {
|
|
8
8
|
getRunnable() {
|
|
9
|
-
const joinTypes = (0, combi_1.optPrio)((0, combi_1.altPrio)("LEFT OUTER MANY TO EXACT ONE", "LEFT OUTER ONE TO EXACT ONE", "LEFT OUTER ONE TO MANY", "LEFT OUTER TO ONE", "LEFT OUTER TO MANY", "LEFT OUTER", "INNER ONE TO MANY", "INNER MANY TO ONE", "INNER ONE TO EXACT ONE", "INNER MANY TO EXACT ONE", "INNER", "CROSS", "RIGHT OUTER"));
|
|
9
|
+
const joinTypes = (0, combi_1.optPrio)((0, combi_1.altPrio)("LEFT OUTER MANY TO EXACT ONE", "LEFT OUTER ONE TO EXACT ONE", "LEFT OUTER ONE TO MANY", "LEFT OUTER TO ONE", "LEFT OUTER TO MANY", "LEFT OUTER", "INNER ONE TO MANY", "INNER MANY TO ONE", "INNER ONE TO EXACT ONE", "INNER MANY TO EXACT ONE", "INNER TO MANY", "INNER TO ONE", "INNER TO EXACT ONE", "INNER", "CROSS", "RIGHT OUTER"));
|
|
10
10
|
const cond = (0, combi_1.seq)(_1.CDSSource, "ON", cds_condition_1.CDSCondition);
|
|
11
11
|
const foo = (0, combi_1.altPrio)((0, combi_1.seq)("(", cond, ")"), cond);
|
|
12
12
|
// Parenthesized join sub-expression: JOIN (src innerJOIN src ON cond) ON outerCond
|
|
@@ -9,7 +9,8 @@ class CDSSelect extends combi_1.Expression {
|
|
|
9
9
|
getRunnable() {
|
|
10
10
|
const fields = (0, combi_1.seq)((0, combi_1.star)((0, combi_1.seq)(_1.CDSElement, ",")), _1.CDSElement);
|
|
11
11
|
const distinct = (0, combi_1.str)("DISTINCT");
|
|
12
|
-
|
|
12
|
+
// elem (,elem)* [,] — handles separator commas and optional trailing comma
|
|
13
|
+
const elementList = (0, combi_1.seq)(_1.CDSElement, (0, combi_1.star)((0, combi_1.seq)(",", _1.CDSElement)), (0, combi_1.opt)(","));
|
|
13
14
|
const elements = (0, combi_1.seq)((0, combi_1.str)("{"), (0, combi_1.altPrio)("*", elementList), (0, combi_1.str)("}"));
|
|
14
15
|
return (0, combi_1.seq)("SELECT", (0, combi_1.optPrio)(distinct), (0, combi_1.opt)((0, combi_1.altPrio)("*", fields)), "FROM", _1.CDSSource, (0, combi_1.star)(cds_join_1.CDSJoin), (0, combi_1.star)((0, combi_1.altPrio)(_1.CDSComposition, cds_association_1.CDSAssociation)), (0, combi_1.opt)(elements), (0, combi_1.optPrio)(_1.CDSWhere), (0, combi_1.optPrio)(_1.CDSGroupBy), (0, combi_1.optPrio)(_1.CDSHaving), (0, combi_1.optPrio)((0, combi_1.seq)("UNION", (0, combi_1.optPrio)("ALL"), CDSSelect)));
|
|
15
16
|
}
|
|
@@ -6,8 +6,9 @@ const combi_1 = require("../../abap/2_statements/combi");
|
|
|
6
6
|
class CDSSource extends combi_1.Expression {
|
|
7
7
|
getRunnable() {
|
|
8
8
|
const singleSource = (0, combi_1.seq)(_1.CDSName, (0, combi_1.optPrio)(_1.CDSParametersSelect), (0, combi_1.opt)((0, combi_1.altPrio)(_1.CDSAs, _1.CDSName)));
|
|
9
|
-
// FROM ( src [JOIN src ON cond]* ) — parenthesized join chain
|
|
10
|
-
|
|
9
|
+
// FROM ( src [JOIN src ON cond]* ) — parenthesized join chain, arbitrarily nested
|
|
10
|
+
// CDSSource is self-referential here to handle: (((T1 join T2) join T3) join T4)
|
|
11
|
+
const parenSource = (0, combi_1.seq)("(", (0, combi_1.altPrio)(CDSSource, singleSource), (0, combi_1.star)(_1.CDSJoin), ")");
|
|
11
12
|
return (0, combi_1.altPrio)(parenSource, singleSource);
|
|
12
13
|
}
|
|
13
14
|
}
|
|
@@ -4,8 +4,9 @@ exports.CDSString = void 0;
|
|
|
4
4
|
const combi_1 = require("../../abap/2_statements/combi");
|
|
5
5
|
class CDSString extends combi_1.Expression {
|
|
6
6
|
getRunnable() {
|
|
7
|
-
// Allow any character except unescaped single quote; ''
|
|
8
|
-
|
|
7
|
+
// Allow any character except unescaped single quote; '' or \' are escaped single quotes.
|
|
8
|
+
// Standalone backslashes (e.g. 'C:\\temp') are also valid as long as they don't form \'.
|
|
9
|
+
const reg = (0, combi_1.regex)(/^'(?:[^'\\]|''|\\'|\\(?!'))*'$/);
|
|
9
10
|
// Typed literal: abap.char 'X' — previously lexed as abap . char'X' (single token)
|
|
10
11
|
// now correctly lexed as three tokens: abap, ., char, 'value'
|
|
11
12
|
const abap = (0, combi_1.seq)("abap", ".", (0, combi_1.regex)(/^char$/), reg);
|
|
@@ -5,7 +5,7 @@ const _1 = require(".");
|
|
|
5
5
|
const combi_1 = require("../../abap/2_statements/combi");
|
|
6
6
|
class CDSWithParameters extends combi_1.Expression {
|
|
7
7
|
getRunnable() {
|
|
8
|
-
const param = (0, combi_1.seq)((0, combi_1.starPrio)(_1.CDSAnnotation), _1.CDSName, ":", _1.CDSType);
|
|
8
|
+
const param = (0, combi_1.seq)((0, combi_1.starPrio)(_1.CDSAnnotation), _1.CDSName, ":", _1.CDSType, (0, combi_1.starPrio)(_1.CDSAnnotation));
|
|
9
9
|
return (0, combi_1.seq)("WITH PARAMETERS", param, (0, combi_1.star)((0, combi_1.seq)(",", param)));
|
|
10
10
|
}
|
|
11
11
|
}
|
package/build/src/registry.js
CHANGED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AFFAndXML = exports.AFFAndXMLConf = void 0;
|
|
4
|
+
const issue_1 = require("../issue");
|
|
5
|
+
const _irule_1 = require("./_irule");
|
|
6
|
+
const _basic_rule_config_1 = require("./_basic_rule_config");
|
|
7
|
+
class AFFAndXMLConf extends _basic_rule_config_1.BasicRuleConfig {
|
|
8
|
+
}
|
|
9
|
+
exports.AFFAndXMLConf = AFFAndXMLConf;
|
|
10
|
+
class AFFAndXML {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.conf = new AFFAndXMLConf();
|
|
13
|
+
}
|
|
14
|
+
getMetadata() {
|
|
15
|
+
return {
|
|
16
|
+
key: "aff_and_xml",
|
|
17
|
+
title: "AFF and XML",
|
|
18
|
+
shortDescription: `Checks for objects that have both AFF (.json) and XML (.xml) files`,
|
|
19
|
+
extendedInformation: `If an object has both an ABAP file format JSON file and an XML file, the XML file should be removed`,
|
|
20
|
+
tags: [_irule_1.RuleTag.Syntax],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
getConfig() {
|
|
24
|
+
return this.conf;
|
|
25
|
+
}
|
|
26
|
+
setConfig(conf) {
|
|
27
|
+
this.conf = conf;
|
|
28
|
+
}
|
|
29
|
+
initialize(_reg) {
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
run(obj) {
|
|
33
|
+
const files = obj.getFiles();
|
|
34
|
+
let hasJSON = false;
|
|
35
|
+
let hasXML = false;
|
|
36
|
+
const type = obj.getType().toLowerCase();
|
|
37
|
+
for (const file of files) {
|
|
38
|
+
const filename = file.getFilename().toLowerCase();
|
|
39
|
+
if (filename.endsWith("." + type + ".json")) {
|
|
40
|
+
hasJSON = true;
|
|
41
|
+
}
|
|
42
|
+
else if (filename.endsWith("." + type + ".xml")) {
|
|
43
|
+
hasXML = true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (hasJSON && hasXML) {
|
|
47
|
+
const xmlFile = obj.getXMLFile();
|
|
48
|
+
if (xmlFile) {
|
|
49
|
+
const message = "Object has both AFF JSON and XML files, remove the XML";
|
|
50
|
+
return [issue_1.Issue.atRow(xmlFile, 1, message, this.getMetadata().key, this.conf.severity)];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.AFFAndXML = AFFAndXML;
|
|
57
|
+
//# sourceMappingURL=aff_and_xml.js.map
|
|
@@ -30,11 +30,11 @@ class DefinitionsTop extends _abap_rule_1.ABAPRule {
|
|
|
30
30
|
shortDescription: `Checks that definitions are placed at the beginning of METHODs, FORMs and FUNCTIONs.`,
|
|
31
31
|
extendedInformation: `https://docs.abapopenchecks.org/checks/17/`,
|
|
32
32
|
tags: [_irule_1.RuleTag.SingleFile, _irule_1.RuleTag.Quickfix],
|
|
33
|
-
badExample: `
|
|
33
|
+
badExample: `FORM foo.
|
|
34
34
|
WRITE 'hello'.
|
|
35
35
|
DATA int TYPE i.
|
|
36
36
|
ENDFORM.`,
|
|
37
|
-
goodExample: `
|
|
37
|
+
goodExample: `FORM foo.
|
|
38
38
|
DATA int TYPE i.
|
|
39
39
|
WRITE 'hello'.
|
|
40
40
|
ENDFORM.`,
|
package/build/src/rules/index.js
CHANGED
|
@@ -17,6 +17,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./7bit_ascii"), exports);
|
|
18
18
|
__exportStar(require("./abapdoc"), exports);
|
|
19
19
|
__exportStar(require("./add_test_attributes"), exports);
|
|
20
|
+
__exportStar(require("./aff_and_xml"), exports);
|
|
20
21
|
__exportStar(require("./align_parameters"), exports);
|
|
21
22
|
__exportStar(require("./align_pseudo_comments"), exports);
|
|
22
23
|
__exportStar(require("./align_type_expressions"), exports);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abaplint/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.116.0",
|
|
4
4
|
"description": "abaplint - Core API",
|
|
5
5
|
"main": "build/src/index.js",
|
|
6
6
|
"typings": "build/abaplint.d.ts",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@microsoft/api-extractor": "^7.57.6",
|
|
54
54
|
"@types/chai": "^4.3.20",
|
|
55
55
|
"@types/mocha": "^10.0.10",
|
|
56
|
-
"@types/node": "^24.
|
|
56
|
+
"@types/node": "^24.12.0",
|
|
57
57
|
"chai": "^4.5.0",
|
|
58
58
|
"eslint": "^9.39.2",
|
|
59
59
|
"mocha": "^11.7.5",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"typescript": "^5.9.3"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"fast-xml-parser": "^5.
|
|
66
|
+
"fast-xml-parser": "^5.4.2",
|
|
67
67
|
"json5": "^2.2.3",
|
|
68
68
|
"vscode-languageserver-types": "^3.17.5"
|
|
69
69
|
}
|