@xano/xanoscript-language-server 11.12.0 → 11.12.2
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/.claude/memory/feedback_no_git.md +11 -0
- package/.claude/settings.local.json +13 -28
- package/lexer/cast.js +3 -3
- package/lexer/control.js +0 -2
- package/lexer/literal.js +2 -2
- package/lexer/regexp.spec.js +84 -0
- package/lexer/security.js +2 -2
- package/lexer/tokens.js +22 -3
- package/package.json +1 -1
- package/parser/attributes/columnDefaultValueAttribute.js +10 -3
- package/parser/attributes/inputFilterFn.js +11 -4
- package/parser/clauses/flexibleStringClause.js +2 -2
- package/parser/clauses/nakedStackFn.js +43 -36
- package/parser/clauses/nakedStackFn.spec.js +10 -0
- package/parser/clauses/responseClause.spec.js +5 -0
- package/parser/definitions/columnDefinition.js +7 -2
- package/parser/functions/array/arrayNoValueAs.js +1 -1
- package/parser/functions/array/arrayPopFn.js +1 -3
- package/parser/functions/controls/forEachFn.spec.js +8 -0
- package/parser/functions/controls/functionRunFn.spec.js +10 -0
- package/parser/functions/controls/preconditionFn.js +3 -0
- package/parser/functions/db/dbExternalDirectQueryFn.js +1 -0
- package/parser/functions/db/dbQueryFn.spec.js +31 -0
- package/parser/functions/schema/schemaParseConstantFn.js +11 -10
- package/parser/functions/varFn.js +14 -2
- package/parser/functions/varFn.spec.js +20 -12
- package/parser/generic/asVariable.spec.js +12 -8
- package/parser/generic/assignableVariableAs.js +50 -11
- package/parser/generic/assignableVariableAs.spec.js +29 -7
- package/parser/generic/assignableVariableProperty.js +22 -10
- package/parser/generic/assignableVariableProperty.spec.js +21 -12
- package/parser/generic/expressionFn.js +9 -2
- package/parser/generic/expressionFn.spec.js +5 -0
- package/parser/generic/filterNames.js +9 -0
- package/parser/multidoc.js +38 -17
- package/parser/multidoc.spec.js +7 -1
- package/parser/query_parser.js +11 -3
- package/parser/query_parser.spec.js +0 -3
- package/parser/tests/function/function_parser.spec.js +4 -4
- package/parser/tests/query/query_parser.spec.js +3 -3
- package/parser/tool_parser.js +12 -3
- package/parser/tool_parser.spec.js +19 -6
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isArray, some } from "lodash-es";
|
|
2
|
+
import { Minus } from "../../../lexer/control.js";
|
|
2
3
|
import {
|
|
3
4
|
FloatLiteral,
|
|
4
5
|
IntegerLiteral,
|
|
@@ -37,18 +38,18 @@ export function schemaParseConstantFn($) {
|
|
|
37
38
|
return $.CONSUME(SingleQuotedStringLiteral); // e.g., 'field instructions'
|
|
38
39
|
},
|
|
39
40
|
},
|
|
41
|
+
|
|
40
42
|
{
|
|
41
43
|
ALT: () => {
|
|
44
|
+
$.OPTION(() => $.CONSUME(Minus));
|
|
42
45
|
attributeCapturedType = "number";
|
|
43
|
-
return $.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
ALT: () => {
|
|
48
|
-
attributeCapturedType = "number";
|
|
49
|
-
return $.CONSUME(FloatLiteral); // e.g., 42.54
|
|
46
|
+
return $.OR1([
|
|
47
|
+
{ ALT: () => $.CONSUME(IntegerLiteral) },
|
|
48
|
+
{ ALT: () => $.CONSUME(FloatLiteral) },
|
|
49
|
+
]);
|
|
50
50
|
},
|
|
51
51
|
},
|
|
52
|
+
|
|
52
53
|
{
|
|
53
54
|
ALT: () => {
|
|
54
55
|
attributeCapturedType = "boolean";
|
|
@@ -76,7 +77,7 @@ export function schemaParseConstantFn($) {
|
|
|
76
77
|
const isMatch = matchValueToSchema(
|
|
77
78
|
attributeCapturedType,
|
|
78
79
|
capturedValue.image,
|
|
79
|
-
schema
|
|
80
|
+
schema,
|
|
80
81
|
);
|
|
81
82
|
|
|
82
83
|
if (!isMatch) {
|
|
@@ -92,7 +93,7 @@ function matchValueToSchema(attributeCapturedType, value, schema) {
|
|
|
92
93
|
// schema can be an array of type/values required
|
|
93
94
|
if (isArray(schema)) {
|
|
94
95
|
return some(schema, (s) =>
|
|
95
|
-
matchValueToSchema(attributeCapturedType, value, s)
|
|
96
|
+
matchValueToSchema(attributeCapturedType, value, s),
|
|
96
97
|
);
|
|
97
98
|
}
|
|
98
99
|
|
|
@@ -115,7 +116,7 @@ function matchValueToSchema(attributeCapturedType, value, schema) {
|
|
|
115
116
|
return schemaValue === null;
|
|
116
117
|
default:
|
|
117
118
|
throw new Error(
|
|
118
|
-
`schemaParseConstantFn called with non-constant attribute: ${schema}
|
|
119
|
+
`schemaParseConstantFn called with non-constant attribute: ${schema}`,
|
|
119
120
|
);
|
|
120
121
|
}
|
|
121
122
|
}
|
|
@@ -2,9 +2,14 @@ import { DotToken } from "../../lexer/tokens.js";
|
|
|
2
2
|
import { UpdateToken, VarToken } from "../../lexer/var.js";
|
|
3
3
|
import {
|
|
4
4
|
ApiBaseUrlVariable,
|
|
5
|
+
AuthVariable,
|
|
5
6
|
BranchVariable,
|
|
6
7
|
DatasourceVariable,
|
|
8
|
+
EnvVariable,
|
|
9
|
+
ErrorVariable,
|
|
7
10
|
HttpHeadersVariable,
|
|
11
|
+
InputVariable,
|
|
12
|
+
LongFormVariable,
|
|
8
13
|
RemoteHostVariable,
|
|
9
14
|
RemoteIpVariable,
|
|
10
15
|
RemotePasswordVariable,
|
|
@@ -53,16 +58,23 @@ export function varFn($) {
|
|
|
53
58
|
{ ALT: () => $.CONSUME(RequestUriVariable) },
|
|
54
59
|
{ ALT: () => $.CONSUME(TenantVariable) },
|
|
55
60
|
{ ALT: () => $.CONSUME(WebflowVariable) },
|
|
61
|
+
// Not recommended but still valid
|
|
62
|
+
{ ALT: () => $.CONSUME(LongFormVariable) },
|
|
63
|
+
{ ALT: () => $.CONSUME(EnvVariable) },
|
|
64
|
+
{ ALT: () => $.CONSUME(AuthVariable) },
|
|
65
|
+
{ ALT: () => $.CONSUME(InputVariable) },
|
|
66
|
+
{ ALT: () => $.CONSUME(ErrorVariable) },
|
|
56
67
|
]);
|
|
68
|
+
|
|
57
69
|
if (variable.image) {
|
|
58
70
|
// Check for blacklisted variable names
|
|
59
71
|
const nameWithoutPrefix = variable.image.startsWith("$")
|
|
60
72
|
? variable.image.slice(1)
|
|
61
73
|
: variable.image;
|
|
62
74
|
if (isBlacklistedVariableName(nameWithoutPrefix)) {
|
|
63
|
-
$.
|
|
64
|
-
variable,
|
|
75
|
+
$.addWarning(
|
|
65
76
|
`'${variable.image}' is a reserved variable name and should not be used as a variable.`,
|
|
77
|
+
variable,
|
|
66
78
|
);
|
|
67
79
|
}
|
|
68
80
|
// we can directly add the variable here but
|
|
@@ -225,16 +225,18 @@ describe("varFn", () => {
|
|
|
225
225
|
const parser = parse(`var $${name} {
|
|
226
226
|
value = "test"
|
|
227
227
|
}`);
|
|
228
|
-
expect(parser.errors).to.
|
|
229
|
-
expect(parser.
|
|
228
|
+
expect(parser.errors).to.be.empty;
|
|
229
|
+
expect(parser.warnings).to.not.be.empty;
|
|
230
|
+
expect(parser.warnings[0].message).to.include("reserved");
|
|
230
231
|
});
|
|
231
232
|
|
|
232
233
|
it(`rejects var.update $${name} as variable name (short form)`, () => {
|
|
233
234
|
const parser = parse(`var.update $${name} {
|
|
234
235
|
value = "test"
|
|
235
236
|
}`);
|
|
236
|
-
expect(parser.errors).to.
|
|
237
|
-
expect(parser.
|
|
237
|
+
expect(parser.errors).to.be.empty;
|
|
238
|
+
expect(parser.warnings).to.not.be.empty;
|
|
239
|
+
expect(parser.warnings[0].message).to.include("reserved");
|
|
238
240
|
});
|
|
239
241
|
}
|
|
240
242
|
|
|
@@ -249,30 +251,35 @@ describe("varFn", () => {
|
|
|
249
251
|
];
|
|
250
252
|
|
|
251
253
|
for (const name of separateTokenBlacklisted) {
|
|
252
|
-
it(`
|
|
254
|
+
it(`warns var $${name} as variable name (separate token)`, () => {
|
|
253
255
|
const parser = parse(`var $${name} {
|
|
254
256
|
value = "test"
|
|
255
257
|
}`);
|
|
256
258
|
if (parser.errors.length === 0) {
|
|
257
259
|
console.log(parser.errors, name);
|
|
258
260
|
}
|
|
259
|
-
expect(parser.errors).to.
|
|
261
|
+
expect(parser.errors).to.be.empty;
|
|
262
|
+
expect(parser.warnings).to.not.be.empty;
|
|
263
|
+
expect(parser.warnings[0].message).to.include("reserved");
|
|
260
264
|
});
|
|
261
265
|
|
|
262
|
-
it(`
|
|
266
|
+
it(`warns var.update $${name} as variable name (separate token)`, () => {
|
|
263
267
|
const parser = parse(`var.update $${name} {
|
|
264
268
|
value = "test"
|
|
265
269
|
}`);
|
|
266
|
-
expect(parser.errors).to.
|
|
270
|
+
expect(parser.errors).to.be.empty;
|
|
271
|
+
expect(parser.warnings).to.not.be.empty;
|
|
272
|
+
expect(parser.warnings[0].message).to.include("reserved");
|
|
267
273
|
});
|
|
268
274
|
}
|
|
269
275
|
|
|
270
276
|
// $var is the LongFormVariable prefix, parser expects a dot after it
|
|
271
|
-
it("
|
|
277
|
+
it("warns var $var as variable name (long form prefix)", () => {
|
|
272
278
|
const parser = parse(`var $var {
|
|
273
279
|
value = "test"
|
|
274
280
|
}`);
|
|
275
|
-
expect(parser.
|
|
281
|
+
expect(parser.warnings).to.not.be.empty;
|
|
282
|
+
expect(parser.errors).to.be.empty;
|
|
276
283
|
});
|
|
277
284
|
|
|
278
285
|
it("accepts a backtick expression as value", () => {
|
|
@@ -301,8 +308,9 @@ describe("varFn", () => {
|
|
|
301
308
|
const parser = parse(`var.update $var.${name} {
|
|
302
309
|
value = "test"
|
|
303
310
|
}`);
|
|
304
|
-
expect(parser.errors).to.
|
|
305
|
-
expect(parser.
|
|
311
|
+
expect(parser.errors).to.be.empty;
|
|
312
|
+
expect(parser.warnings).to.not.be.empty;
|
|
313
|
+
expect(parser.warnings[0].message).to.include("reserved");
|
|
306
314
|
});
|
|
307
315
|
}
|
|
308
316
|
|
|
@@ -44,8 +44,9 @@ describe("asVariable", () => {
|
|
|
44
44
|
for (const name of shortFormBlacklisted) {
|
|
45
45
|
it(`rejects as $${name} (short form)`, () => {
|
|
46
46
|
const parser = parse(`as $${name}`);
|
|
47
|
-
expect(parser.errors).to.
|
|
48
|
-
expect(parser.
|
|
47
|
+
expect(parser.errors).to.be.empty;
|
|
48
|
+
expect(parser.warnings).to.not.be.empty;
|
|
49
|
+
expect(parser.warnings[0].message).to.include("reserved");
|
|
49
50
|
});
|
|
50
51
|
}
|
|
51
52
|
|
|
@@ -62,14 +63,16 @@ describe("asVariable", () => {
|
|
|
62
63
|
for (const name of separateTokenBlacklisted) {
|
|
63
64
|
it(`rejects as $${name} (separate token)`, () => {
|
|
64
65
|
const parser = parse(`as $${name}`);
|
|
65
|
-
expect(parser.
|
|
66
|
+
expect(parser.warnings).to.not.be.empty;
|
|
66
67
|
});
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
// $var is the LongFormVariable prefix, parser expects a dot after it
|
|
71
|
+
// FIXME: shoud be a warning, not an error, since it's still a valid variable name (albeit confusing)
|
|
70
72
|
it("rejects as $var (long form prefix)", () => {
|
|
71
73
|
const parser = parse("as $var");
|
|
72
|
-
expect(parser.errors).to.
|
|
74
|
+
expect(parser.errors).to.be.empty;
|
|
75
|
+
expect(parser.warnings).to.not.be.empty;
|
|
73
76
|
});
|
|
74
77
|
|
|
75
78
|
// All blacklisted names should be rejected in long form ($var.name)
|
|
@@ -89,25 +92,26 @@ describe("asVariable", () => {
|
|
|
89
92
|
for (const name of allBlacklisted) {
|
|
90
93
|
it(`rejects as $var.${name} (long form)`, () => {
|
|
91
94
|
const parser = parse(`as $var.${name}`);
|
|
92
|
-
expect(parser.
|
|
93
|
-
expect(parser.
|
|
95
|
+
expect(parser.warnings).to.not.be.empty;
|
|
96
|
+
expect(parser.warnings[0].message).to.include("reserved");
|
|
94
97
|
});
|
|
95
98
|
}
|
|
96
99
|
|
|
97
100
|
// Ensure valid names still work
|
|
98
101
|
it("accepts as $user", () => {
|
|
99
102
|
const parser = parse("as $user");
|
|
100
|
-
expect(parser.
|
|
103
|
+
expect(parser.warnings).to.be.empty;
|
|
101
104
|
});
|
|
102
105
|
|
|
103
106
|
it("accepts as $my_input", () => {
|
|
104
107
|
const parser = parse("as $my_input");
|
|
105
108
|
expect(parser.errors).to.be.empty;
|
|
109
|
+
expect(parser.warnings).to.be.empty;
|
|
106
110
|
});
|
|
107
111
|
|
|
108
112
|
it("accepts as $var.my_var", () => {
|
|
109
113
|
const parser = parse("as $var.my_var");
|
|
110
|
-
expect(parser.
|
|
114
|
+
expect(parser.warnings).to.be.empty;
|
|
111
115
|
});
|
|
112
116
|
});
|
|
113
117
|
});
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import { MismatchedTokenException } from "chevrotain";
|
|
2
1
|
import { DotToken, Identifier } from "../../lexer/tokens.js";
|
|
3
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
AuthVariable,
|
|
4
|
+
EnvVariable,
|
|
5
|
+
ErrorVariable,
|
|
6
|
+
InputVariable,
|
|
7
|
+
LongFormVariable,
|
|
8
|
+
ShortFormVariable,
|
|
9
|
+
} from "../../lexer/variables.js";
|
|
4
10
|
import { isBlacklistedVariableName } from "./assignableVariableProperty.js";
|
|
5
11
|
|
|
6
12
|
/**
|
|
@@ -23,10 +29,45 @@ export function assignableVariableAs($) {
|
|
|
23
29
|
// "$var.users"
|
|
24
30
|
{
|
|
25
31
|
ALT: () => {
|
|
26
|
-
$.CONSUME(LongFormVariable);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
const variable = $.CONSUME(LongFormVariable);
|
|
33
|
+
variableName = variable.image;
|
|
34
|
+
variableToken = variable;
|
|
35
|
+
|
|
36
|
+
// Handle optional property access, e.g., "$var.users" because "$var" is a valid
|
|
37
|
+
// variable name even if it's not recommended and confusing
|
|
38
|
+
$.OPTION(() => {
|
|
39
|
+
$.CONSUME(DotToken);
|
|
40
|
+
const variable = $.CONSUME(Identifier);
|
|
41
|
+
variableName = `$${variable.image}`;
|
|
42
|
+
variableToken = variable;
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
ALT: () => {
|
|
48
|
+
const variable = $.CONSUME(EnvVariable);
|
|
49
|
+
variableName = variable.image;
|
|
50
|
+
variableToken = variable;
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
ALT: () => {
|
|
55
|
+
const variable = $.CONSUME(AuthVariable);
|
|
56
|
+
variableName = variable.image;
|
|
57
|
+
variableToken = variable;
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
ALT: () => {
|
|
62
|
+
const variable = $.CONSUME(InputVariable);
|
|
63
|
+
variableName = variable.image;
|
|
64
|
+
variableToken = variable;
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
ALT: () => {
|
|
69
|
+
const variable = $.CONSUME(ErrorVariable);
|
|
70
|
+
variableName = variable.image;
|
|
30
71
|
variableToken = variable;
|
|
31
72
|
},
|
|
32
73
|
},
|
|
@@ -38,11 +79,9 @@ export function assignableVariableAs($) {
|
|
|
38
79
|
? variableName.slice(1)
|
|
39
80
|
: variableName;
|
|
40
81
|
if (isBlacklistedVariableName(nameWithoutPrefix)) {
|
|
41
|
-
$.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
variableToken
|
|
45
|
-
)
|
|
82
|
+
$.addWarning(
|
|
83
|
+
`'${variableToken.image}' is a reserved variable name and should not be used as a variable.`,
|
|
84
|
+
variableToken,
|
|
46
85
|
);
|
|
47
86
|
}
|
|
48
87
|
$.addVariable(variableName, "unknown", variableToken);
|
|
@@ -25,6 +25,12 @@ describe("assignableVariableAs", () => {
|
|
|
25
25
|
expect(parser.errors).to.not.be.empty;
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
+
it("assignableVariableAs should warn on $var reserved variable names", () => {
|
|
29
|
+
const parser = parse("$var");
|
|
30
|
+
expect(parser.errors).to.be.empty;
|
|
31
|
+
expect(parser.warnings).to.not.be.empty;
|
|
32
|
+
});
|
|
33
|
+
|
|
28
34
|
it("assignableVariableAs cannot be a short named variable with sub props", () => {
|
|
29
35
|
const parser = parse("$users.x.y[1].z");
|
|
30
36
|
expect(parser.errors).to.not.be.empty;
|
|
@@ -53,13 +59,28 @@ describe("assignableVariableAs", () => {
|
|
|
53
59
|
// Blacklisted variable names - used in loop contexts (each as $variable)
|
|
54
60
|
describe("blacklisted variable names", () => {
|
|
55
61
|
// These are lexed as ShortFormVariable and need explicit blacklisting
|
|
56
|
-
const shortFormBlacklisted = [
|
|
62
|
+
const shortFormBlacklisted = [
|
|
63
|
+
"output",
|
|
64
|
+
"db",
|
|
65
|
+
"toolset",
|
|
66
|
+
"auth",
|
|
67
|
+
"input",
|
|
68
|
+
"env",
|
|
69
|
+
"response",
|
|
70
|
+
"output",
|
|
71
|
+
"var",
|
|
72
|
+
"db",
|
|
73
|
+
"error",
|
|
74
|
+
"toolset",
|
|
75
|
+
"this",
|
|
76
|
+
];
|
|
57
77
|
|
|
58
78
|
for (const name of shortFormBlacklisted) {
|
|
59
|
-
it(`
|
|
79
|
+
it(`warns on $${name} (short form)`, () => {
|
|
60
80
|
const parser = parse(`$${name}`);
|
|
61
|
-
expect(parser.errors).to.
|
|
62
|
-
expect(parser.
|
|
81
|
+
expect(parser.errors).to.be.empty;
|
|
82
|
+
expect(parser.warnings).to.not.be.empty;
|
|
83
|
+
expect(parser.warnings[0].message).to.include("reserved");
|
|
63
84
|
});
|
|
64
85
|
}
|
|
65
86
|
|
|
@@ -78,10 +99,11 @@ describe("assignableVariableAs", () => {
|
|
|
78
99
|
];
|
|
79
100
|
|
|
80
101
|
for (const name of allBlacklisted) {
|
|
81
|
-
it(`
|
|
102
|
+
it(`warns on $var.${name} (long form)`, () => {
|
|
82
103
|
const parser = parse(`$var.${name}`);
|
|
83
|
-
expect(parser.errors).to.
|
|
84
|
-
expect(parser.
|
|
104
|
+
expect(parser.errors).to.be.empty;
|
|
105
|
+
expect(parser.warnings).to.not.be.empty;
|
|
106
|
+
expect(parser.warnings[0].message).to.include("reserved");
|
|
85
107
|
});
|
|
86
108
|
}
|
|
87
109
|
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { MismatchedTokenException } from "chevrotain";
|
|
2
1
|
import { PipeToken } from "../../lexer/control.js";
|
|
3
2
|
import { DotToken, Identifier, NewlineToken } from "../../lexer/tokens.js";
|
|
4
3
|
import {
|
|
5
4
|
ApiBaseUrlVariable,
|
|
5
|
+
AuthVariable,
|
|
6
6
|
BranchVariable,
|
|
7
7
|
DatasourceVariable,
|
|
8
|
+
EnvVariable,
|
|
9
|
+
ErrorVariable,
|
|
8
10
|
HttpHeadersVariable,
|
|
11
|
+
InputVariable,
|
|
9
12
|
LongFormVariable,
|
|
10
13
|
RemoteHostVariable,
|
|
11
14
|
RemoteIpVariable,
|
|
@@ -78,6 +81,11 @@ export function assignableVariableProperty($) {
|
|
|
78
81
|
{ ALT: () => $.CONSUME(RequestUriVariable) },
|
|
79
82
|
{ ALT: () => $.CONSUME(TenantVariable) },
|
|
80
83
|
{ ALT: () => $.CONSUME(WebflowVariable) },
|
|
84
|
+
// Not recommended but still valid
|
|
85
|
+
{ ALT: () => $.CONSUME(EnvVariable) },
|
|
86
|
+
{ ALT: () => $.CONSUME(AuthVariable) },
|
|
87
|
+
{ ALT: () => $.CONSUME(InputVariable) },
|
|
88
|
+
{ ALT: () => $.CONSUME(ErrorVariable) },
|
|
81
89
|
]);
|
|
82
90
|
variableName = variable.image;
|
|
83
91
|
variableToken = variable;
|
|
@@ -86,11 +94,17 @@ export function assignableVariableProperty($) {
|
|
|
86
94
|
// "$var.users"
|
|
87
95
|
{
|
|
88
96
|
ALT: () => {
|
|
89
|
-
$.CONSUME(LongFormVariable);
|
|
90
|
-
|
|
91
|
-
const variable = $.CONSUME(Identifier);
|
|
92
|
-
variableName = `$${variable.image}`;
|
|
97
|
+
const variable = $.CONSUME(LongFormVariable);
|
|
98
|
+
variableName = variable.image;
|
|
93
99
|
variableToken = variable;
|
|
100
|
+
|
|
101
|
+
// because we want to allow "$var" we need to optionally consume a dot and an identifier
|
|
102
|
+
$.OPTION(() => {
|
|
103
|
+
$.CONSUME(DotToken);
|
|
104
|
+
const variable = $.CONSUME(Identifier);
|
|
105
|
+
variableName = `$${variable.image}`;
|
|
106
|
+
variableToken = variable;
|
|
107
|
+
});
|
|
94
108
|
},
|
|
95
109
|
},
|
|
96
110
|
],
|
|
@@ -104,11 +118,9 @@ export function assignableVariableProperty($) {
|
|
|
104
118
|
? variableName.slice(1)
|
|
105
119
|
: variableName;
|
|
106
120
|
if (isBlacklistedVariableName(nameWithoutPrefix)) {
|
|
107
|
-
$.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
variableToken
|
|
111
|
-
)
|
|
121
|
+
$.addWarning(
|
|
122
|
+
`'${variableName}' is a reserved variable name and should not be used`,
|
|
123
|
+
variableToken,
|
|
112
124
|
);
|
|
113
125
|
}
|
|
114
126
|
}
|
|
@@ -30,17 +30,21 @@ describe("assignableVariableProperty", () => {
|
|
|
30
30
|
expect(parser.errors).to.be.empty;
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
it("assignableVariableProperty
|
|
33
|
+
it("assignableVariableProperty should warn on $env variable", () => {
|
|
34
34
|
const parser = parse("$env.my_api_key");
|
|
35
|
-
expect(parser.errors).to.
|
|
35
|
+
expect(parser.errors).to.be.empty;
|
|
36
|
+
expect(parser.warnings).to.not.be.empty;
|
|
37
|
+
expect(parser.warnings[0].message).to.include("reserved");
|
|
36
38
|
});
|
|
37
39
|
|
|
38
|
-
it("assignableVariableProperty
|
|
40
|
+
it("assignableVariableProperty should warn on $input variable", () => {
|
|
39
41
|
const parser = parse("$input.remote_ip");
|
|
40
|
-
expect(parser.errors).to.
|
|
42
|
+
expect(parser.errors).to.be.empty;
|
|
43
|
+
expect(parser.warnings).to.not.be.empty;
|
|
44
|
+
expect(parser.warnings[0].message).to.include("reserved");
|
|
41
45
|
});
|
|
42
46
|
|
|
43
|
-
it("assignableVariableProperty cannot be
|
|
47
|
+
it("assignableVariableProperty cannot be an indentifier", () => {
|
|
44
48
|
const parser = parse(`"someting"`);
|
|
45
49
|
expect(parser.errors).to.not.be.empty;
|
|
46
50
|
});
|
|
@@ -59,8 +63,9 @@ describe("assignableVariableProperty", () => {
|
|
|
59
63
|
for (const name of shortFormBlacklisted) {
|
|
60
64
|
it(`rejects $${name} as assignable variable (short form)`, () => {
|
|
61
65
|
const parser = parse(`$${name}`);
|
|
62
|
-
expect(parser.errors).to.
|
|
63
|
-
expect(parser.
|
|
66
|
+
expect(parser.errors).to.be.empty;
|
|
67
|
+
expect(parser.warnings).to.not.be.empty;
|
|
68
|
+
expect(parser.warnings[0].message).to.include("reserved");
|
|
64
69
|
});
|
|
65
70
|
}
|
|
66
71
|
|
|
@@ -78,14 +83,17 @@ describe("assignableVariableProperty", () => {
|
|
|
78
83
|
for (const name of separateTokenBlacklisted) {
|
|
79
84
|
it(`rejects $${name} as assignable variable (separate token)`, () => {
|
|
80
85
|
const parser = parse(`$${name}`);
|
|
81
|
-
expect(parser.errors).to.
|
|
86
|
+
expect(parser.errors).to.be.empty;
|
|
87
|
+
expect(parser.warnings).to.not.be.empty;
|
|
88
|
+
expect(parser.warnings[0].message).to.include("reserved");
|
|
82
89
|
});
|
|
83
90
|
}
|
|
84
91
|
|
|
85
92
|
// $var is the LongFormVariable prefix, parser expects a dot after it
|
|
86
|
-
it("
|
|
93
|
+
it("warns when using $var as assignable variable (long form prefix)", () => {
|
|
87
94
|
const parser = parse("$var");
|
|
88
|
-
expect(parser.errors).to.
|
|
95
|
+
expect(parser.errors).to.be.empty;
|
|
96
|
+
expect(parser.warnings).to.not.be.empty;
|
|
89
97
|
});
|
|
90
98
|
|
|
91
99
|
// All blacklisted names should be rejected in long form ($var.name)
|
|
@@ -105,8 +113,9 @@ describe("assignableVariableProperty", () => {
|
|
|
105
113
|
for (const name of allBlacklisted) {
|
|
106
114
|
it(`rejects $var.${name} as assignable variable (long form)`, () => {
|
|
107
115
|
const parser = parse(`$var.${name}`);
|
|
108
|
-
expect(parser.errors).to.
|
|
109
|
-
expect(parser.
|
|
116
|
+
expect(parser.errors).to.be.empty;
|
|
117
|
+
expect(parser.warnings).to.not.be.empty;
|
|
118
|
+
expect(parser.warnings[0].message).to.include("reserved");
|
|
110
119
|
});
|
|
111
120
|
}
|
|
112
121
|
|
|
@@ -366,8 +366,15 @@ export function simpleExpressionFn($) {
|
|
|
366
366
|
// Group 1: Literals (fast path - most common, distinct tokens)
|
|
367
367
|
{ ALT: () => $.CONSUME(StringLiteral) },
|
|
368
368
|
{ ALT: () => $.CONSUME(SingleQuotedStringLiteral) },
|
|
369
|
-
{
|
|
370
|
-
|
|
369
|
+
{
|
|
370
|
+
ALT: () => {
|
|
371
|
+
$.OPTION(() => $.CONSUME(Minus));
|
|
372
|
+
$.OR1([
|
|
373
|
+
{ ALT: () => $.CONSUME(IntegerLiteral) },
|
|
374
|
+
{ ALT: () => $.CONSUME(FloatLiteral) },
|
|
375
|
+
]);
|
|
376
|
+
},
|
|
377
|
+
},
|
|
371
378
|
{ ALT: () => $.CONSUME(TrueToken) },
|
|
372
379
|
{ ALT: () => $.CONSUME(FalseToken) },
|
|
373
380
|
{ ALT: () => $.CONSUME(NullToken) },
|
|
@@ -570,6 +570,11 @@ describe("expressionFn", () => {
|
|
|
570
570
|
expect(parser.errors).to.be.empty;
|
|
571
571
|
});
|
|
572
572
|
|
|
573
|
+
it("expressionFn accept parenthesis for priority for math", () => {
|
|
574
|
+
const parser = parse(`(1+2+4)*5-2`);
|
|
575
|
+
expect(parser.errors).to.be.empty;
|
|
576
|
+
});
|
|
577
|
+
|
|
573
578
|
it("expressionFn accept math with variable", () => {
|
|
574
579
|
const parser = parse(`2 * $input.a + 2 / $input.b - 2`);
|
|
575
580
|
expect(parser.errors).to.be.empty;
|
|
@@ -286,6 +286,15 @@ export const filterNames = [
|
|
|
286
286
|
"sql_esc",
|
|
287
287
|
"sql_alias",
|
|
288
288
|
"is_uuid",
|
|
289
|
+
"count_distinct",
|
|
290
|
+
"median",
|
|
291
|
+
"to_list",
|
|
292
|
+
"to_list_asc",
|
|
293
|
+
"to_list_desc",
|
|
294
|
+
"to_distinct_list",
|
|
295
|
+
"to_distinct_list_asc",
|
|
296
|
+
"to_distinct_list_desc",
|
|
297
|
+
"lambda",
|
|
289
298
|
];
|
|
290
299
|
|
|
291
300
|
// List of input filters allowed for each input type
|