@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
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: no-git-interactions
|
|
3
|
+
description: User does not want Claude to interact with git (no commits, no staging, no git commands)
|
|
4
|
+
type: feedback
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Do not interact with git — no commits, no staging, no git commands. Focus purely on implementation and testing.
|
|
8
|
+
|
|
9
|
+
**Why:** User manages their own git workflow.
|
|
10
|
+
|
|
11
|
+
**How to apply:** Skip all git-related steps in plans. When dispatching subagents, explicitly tell them not to run any git commands. Remove commit steps from task instructions.
|
|
@@ -2,35 +2,20 @@
|
|
|
2
2
|
"permissions": {
|
|
3
3
|
"allow": [
|
|
4
4
|
"Bash(npm test:*)",
|
|
5
|
-
"Bash(
|
|
6
|
-
"Bash(
|
|
7
|
-
"Bash(
|
|
8
|
-
"Bash(node:*)",
|
|
9
|
-
"Bash(npm install)",
|
|
5
|
+
"Bash(git checkout:*)",
|
|
6
|
+
"Bash(ls:*)",
|
|
7
|
+
"Bash(npm run lint:*)",
|
|
10
8
|
"Bash(grep:*)",
|
|
11
|
-
"Bash(git -C /Users/justinalbrecht/git/xs-language-server log --all --pretty=format:\"%h %s\")",
|
|
12
|
-
"Bash(git -C /Users/justinalbrecht/git/xs-language-server log --all -S \"password\\\\|secret\\\\|api_key\\\\|token\" --oneline)",
|
|
13
|
-
"Bash(xargs file:*)",
|
|
14
9
|
"Bash(find:*)",
|
|
15
|
-
"
|
|
16
|
-
"Bash(git
|
|
17
|
-
"Bash(
|
|
18
|
-
"Bash(
|
|
19
|
-
"Bash(
|
|
20
|
-
"Bash(
|
|
21
|
-
"Bash(
|
|
22
|
-
"Bash(
|
|
23
|
-
"Bash(
|
|
24
|
-
|
|
25
|
-
"Bash(git push)",
|
|
26
|
-
"Bash(npm whoami:*)",
|
|
27
|
-
"Bash(npm view:*)",
|
|
28
|
-
"Bash(npm version:*)",
|
|
29
|
-
"Bash(npm publish:*)",
|
|
30
|
-
"Bash(wc:*)",
|
|
31
|
-
"Bash(git push:*)"
|
|
32
|
-
],
|
|
33
|
-
"deny": [],
|
|
34
|
-
"ask": []
|
|
10
|
+
"Read(//tmp/**)",
|
|
11
|
+
"Bash(git stash:*)",
|
|
12
|
+
"Bash(do)",
|
|
13
|
+
"Bash(if ! grep -q \"guidFieldAttribute\" \"$f\")",
|
|
14
|
+
"Bash(then)",
|
|
15
|
+
"Bash(echo:*)",
|
|
16
|
+
"Bash(fi)",
|
|
17
|
+
"Bash(done)",
|
|
18
|
+
"Bash(npx eslint:*)"
|
|
19
|
+
]
|
|
35
20
|
}
|
|
36
21
|
}
|
package/lexer/cast.js
CHANGED
|
@@ -23,14 +23,14 @@ export const BoolCastToken = createUniqToken({
|
|
|
23
23
|
// !array
|
|
24
24
|
export const ArrayCastToken = createUniqToken({
|
|
25
25
|
name: "ArrayCast",
|
|
26
|
-
pattern: /!array/,
|
|
26
|
+
pattern: /!array(?!\.)/,
|
|
27
27
|
label: "!array",
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
// !text
|
|
31
31
|
export const TextCastToken = createUniqToken({
|
|
32
32
|
name: "TextCast",
|
|
33
|
-
pattern: /!text/,
|
|
33
|
+
pattern: /!text(?!\.)/,
|
|
34
34
|
label: "!text",
|
|
35
35
|
});
|
|
36
36
|
|
|
@@ -51,7 +51,7 @@ export const DecimalCastToken = createUniqToken({
|
|
|
51
51
|
// !object
|
|
52
52
|
export const ObjectCastToken = createUniqToken({
|
|
53
53
|
name: "ObjectCast",
|
|
54
|
-
pattern: /!object/,
|
|
54
|
+
pattern: /!object(?!\.)/,
|
|
55
55
|
label: "!object",
|
|
56
56
|
});
|
|
57
57
|
|
package/lexer/control.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Identifier } from "./identifier.js";
|
|
2
|
-
import { IntegerLiteral } from "./literal.js";
|
|
3
2
|
import { createTokenByName, createUniqToken } from "./utils.js";
|
|
4
3
|
|
|
5
4
|
// conditional
|
|
@@ -173,7 +172,6 @@ export const Minus = createUniqToken({
|
|
|
173
172
|
name: "Minus",
|
|
174
173
|
pattern: /-/,
|
|
175
174
|
label: "-",
|
|
176
|
-
longer_alt: IntegerLiteral,
|
|
177
175
|
});
|
|
178
176
|
|
|
179
177
|
// +
|
package/lexer/literal.js
CHANGED
|
@@ -24,13 +24,13 @@ export const SingleQuotedStringLiteral = createUniqToken({
|
|
|
24
24
|
export const FloatLiteral = createUniqToken({
|
|
25
25
|
name: "FloatLiteral",
|
|
26
26
|
label: "floating point number",
|
|
27
|
-
pattern:
|
|
27
|
+
pattern: /\d+\.\d+([eE][+-]?\d+)?/,
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
export const IntegerLiteral = createUniqToken({
|
|
31
31
|
name: "IntegerLiteral",
|
|
32
32
|
label: "integer",
|
|
33
|
-
pattern:
|
|
33
|
+
pattern: /\d+/,
|
|
34
34
|
longer_alt: FloatLiteral,
|
|
35
35
|
});
|
|
36
36
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import { describe, it } from "mocha";
|
|
3
|
+
import { lexDocument } from "./lexer.js";
|
|
4
|
+
|
|
5
|
+
function getTokenTypes(text) {
|
|
6
|
+
const result = lexDocument(text);
|
|
7
|
+
return {
|
|
8
|
+
tokens: result.tokens.map((t) => ({
|
|
9
|
+
type: t.tokenType.name,
|
|
10
|
+
image: t.image,
|
|
11
|
+
})),
|
|
12
|
+
errors: result.errors,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe("RegExpToken lexer", () => {
|
|
17
|
+
it("should not lex a standalone regex without preceding colon", () => {
|
|
18
|
+
const { tokens } = getTokenTypes("/[^213]*/");
|
|
19
|
+
const regexpTokens = tokens.filter((t) => t.type === "RegExpToken");
|
|
20
|
+
expect(regexpTokens).to.have.length(0);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("should lex a regex after a colon", () => {
|
|
24
|
+
const { tokens, errors } = getTokenTypes("pattern:/[^213]*/");
|
|
25
|
+
expect(errors).to.be.empty;
|
|
26
|
+
const regexpTokens = tokens.filter((t) => t.type === "RegExpToken");
|
|
27
|
+
expect(regexpTokens).to.have.length(1);
|
|
28
|
+
expect(regexpTokens[0].image).to.equal("/[^213]*/");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should lex / as divide after an identifier", () => {
|
|
32
|
+
const { tokens } = getTokenTypes("var_rel/55");
|
|
33
|
+
const divideTokens = tokens.filter((t) => t.type === "Divide");
|
|
34
|
+
expect(divideTokens).to.have.length(1);
|
|
35
|
+
const regexpTokens = tokens.filter((t) => t.type === "RegExpToken");
|
|
36
|
+
expect(regexpTokens).to.have.length(0);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should lex / as divide after a number", () => {
|
|
40
|
+
const { tokens } = getTokenTypes("55/2");
|
|
41
|
+
const divideTokens = tokens.filter((t) => t.type === "Divide");
|
|
42
|
+
expect(divideTokens).to.have.length(1);
|
|
43
|
+
const regexpTokens = tokens.filter((t) => t.type === "RegExpToken");
|
|
44
|
+
expect(regexpTokens).to.have.length(0);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("should lex / as divide after closing paren", () => {
|
|
48
|
+
const { tokens } = getTokenTypes("(1+2)/3");
|
|
49
|
+
const divideTokens = tokens.filter((t) => t.type === "Divide");
|
|
50
|
+
expect(divideTokens).to.have.length(1);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should not capture division as regex in complex expressions", () => {
|
|
54
|
+
const { tokens, errors } = getTokenTypes("$SomeVar.0.var_rel/55");
|
|
55
|
+
expect(errors).to.be.empty;
|
|
56
|
+
const regexpTokens = tokens.filter((t) => t.type === "RegExpToken");
|
|
57
|
+
expect(regexpTokens).to.have.length(0);
|
|
58
|
+
const divideTokens = tokens.filter((t) => t.type === "Divide");
|
|
59
|
+
expect(divideTokens).to.have.length(1);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should lex / as divide with spaces around it", () => {
|
|
63
|
+
const { tokens, errors } = getTokenTypes("$var /64");
|
|
64
|
+
expect(errors).to.be.empty;
|
|
65
|
+
const regexpTokens = tokens.filter((t) => t.type === "RegExpToken");
|
|
66
|
+
expect(regexpTokens).to.have.length(0);
|
|
67
|
+
const divideTokens = tokens.filter((t) => t.type === "Divide");
|
|
68
|
+
expect(divideTokens).to.have.length(1);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("should lex / as divide with space only before it", () => {
|
|
72
|
+
const { tokens, errors } = getTokenTypes("$var/ 64");
|
|
73
|
+
expect(errors).to.be.empty;
|
|
74
|
+
const divideTokens = tokens.filter((t) => t.type === "Divide");
|
|
75
|
+
expect(divideTokens).to.have.length(1);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("should lex / as divide when no spaces around a value", () => {
|
|
79
|
+
const { tokens, errors } = getTokenTypes("$var /64/ 34");
|
|
80
|
+
expect(errors).to.be.empty;
|
|
81
|
+
const divideTokens = tokens.filter((t) => t.type === "Divide");
|
|
82
|
+
expect(divideTokens).to.have.length(2);
|
|
83
|
+
});
|
|
84
|
+
});
|
package/lexer/security.js
CHANGED
|
@@ -51,13 +51,13 @@ export const CreateSecretKeyToken = createTokenByName("create_secret_key", {
|
|
|
51
51
|
|
|
52
52
|
// decrypt
|
|
53
53
|
export const DecryptToken = createTokenByName("decrypt", {
|
|
54
|
-
|
|
54
|
+
longer_alt: Identifier,
|
|
55
55
|
categories: [Identifier],
|
|
56
56
|
});
|
|
57
57
|
|
|
58
58
|
// encrypt
|
|
59
59
|
export const EncryptToken = createTokenByName("encrypt", {
|
|
60
|
-
|
|
60
|
+
longer_alt: Identifier,
|
|
61
61
|
categories: [Identifier],
|
|
62
62
|
});
|
|
63
63
|
|
package/lexer/tokens.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Lexer } from "chevrotain";
|
|
1
|
+
import { createToken as chevrotainCreateToken, Lexer } from "chevrotain";
|
|
2
2
|
import { Identifier } from "./identifier.js";
|
|
3
3
|
import {
|
|
4
4
|
createTokenByName,
|
|
@@ -150,10 +150,29 @@ export const NullToken = createTokenByName("null", {
|
|
|
150
150
|
});
|
|
151
151
|
|
|
152
152
|
// regular expression (regexp) pattern
|
|
153
|
-
|
|
153
|
+
// A regexp literal only appears after a colon (e.g., filter:pattern:/[^abc]*/)
|
|
154
|
+
const regexpPattern = /\/(?:[^/\n\\ ]|\\.)(?:[^/\n\\]|\\.)*\//y;
|
|
155
|
+
function matchRegExp(text, startOffset, tokens) {
|
|
156
|
+
if (
|
|
157
|
+
tokens.length === 0 ||
|
|
158
|
+
tokens[tokens.length - 1].tokenType.name !== "ColonToken"
|
|
159
|
+
) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
regexpPattern.lastIndex = startOffset;
|
|
163
|
+
const result = regexpPattern.exec(text);
|
|
164
|
+
if (result) {
|
|
165
|
+
return [result[0]];
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export const RegExpToken = chevrotainCreateToken({
|
|
154
171
|
name: "RegExpToken",
|
|
155
172
|
label: "regexp",
|
|
156
|
-
pattern:
|
|
173
|
+
pattern: { exec: matchRegExp },
|
|
174
|
+
line_breaks: false,
|
|
175
|
+
start_chars_hint: ["/"],
|
|
157
176
|
});
|
|
158
177
|
|
|
159
178
|
export const WhiteSpace = createToken({
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EqualToken } from "../../lexer/control.js";
|
|
1
|
+
import { EqualToken, Minus } from "../../lexer/control.js";
|
|
2
2
|
import {
|
|
3
3
|
FloatLiteral,
|
|
4
4
|
IntegerLiteral,
|
|
@@ -21,8 +21,15 @@ export function columnDefaultValueAttribute($) {
|
|
|
21
21
|
$.CONSUME(EqualToken); // "="
|
|
22
22
|
$.OR([
|
|
23
23
|
{ ALT: () => $.CONSUME(StringLiteral) }, // e.g., "default value"
|
|
24
|
-
{
|
|
25
|
-
|
|
24
|
+
{
|
|
25
|
+
ALT: () => {
|
|
26
|
+
$.OPTION(() => $.CONSUME(Minus));
|
|
27
|
+
return $.OR1([
|
|
28
|
+
{ ALT: () => $.CONSUME(IntegerLiteral) },
|
|
29
|
+
{ ALT: () => $.CONSUME(FloatLiteral) },
|
|
30
|
+
]);
|
|
31
|
+
},
|
|
32
|
+
},
|
|
26
33
|
{ ALT: () => $.CONSUME(TrueToken) }, // true
|
|
27
34
|
{ ALT: () => $.CONSUME(FalseToken) }, // false
|
|
28
35
|
{ ALT: () => $.CONSUME(NowToken) }, // now
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ExclamationToken } from "../../lexer/cast.js";
|
|
2
|
-
import { ColonToken } from "../../lexer/control.js";
|
|
2
|
+
import { ColonToken, Minus } from "../../lexer/control.js";
|
|
3
3
|
import {
|
|
4
4
|
FloatLiteral,
|
|
5
5
|
IntegerLiteral,
|
|
@@ -36,8 +36,15 @@ export function inputFilterFn($) {
|
|
|
36
36
|
{ ALT: () => $.CONSUME2(Identifier) },
|
|
37
37
|
{ ALT: () => $.CONSUME1(StringLiteral) },
|
|
38
38
|
{ ALT: () => $.CONSUME1(RegExpToken) },
|
|
39
|
-
{
|
|
40
|
-
|
|
39
|
+
{
|
|
40
|
+
ALT: () => {
|
|
41
|
+
$.OPTION1(() => $.CONSUME(Minus));
|
|
42
|
+
$.OR1([
|
|
43
|
+
{ ALT: () => $.CONSUME(IntegerLiteral) },
|
|
44
|
+
{ ALT: () => $.CONSUME(FloatLiteral) },
|
|
45
|
+
]);
|
|
46
|
+
},
|
|
47
|
+
},
|
|
41
48
|
{ ALT: () => $.CONSUME1(FalseToken) },
|
|
42
49
|
{ ALT: () => $.CONSUME1(TrueToken) },
|
|
43
50
|
]);
|
|
@@ -52,7 +59,7 @@ export function inputFilterFn($) {
|
|
|
52
59
|
) {
|
|
53
60
|
$.addInvalidValueError(
|
|
54
61
|
filter,
|
|
55
|
-
`Filter '${filter.image}' cannot be applied to input of type '${inputTypeToken.image}'
|
|
62
|
+
`Filter '${filter.image}' cannot be applied to input of type '${inputTypeToken.image}'`,
|
|
56
63
|
);
|
|
57
64
|
}
|
|
58
65
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EqualToken } from "../../lexer/control.js";
|
|
2
|
-
import { Identifier } from "../../lexer/identifier.js";
|
|
3
2
|
import { StringLiteral } from "../../lexer/literal.js";
|
|
4
3
|
import { MultiLineStringToken } from "../../lexer/multiline.js";
|
|
4
|
+
import { InstructionsToken } from "../../lexer/tool.js";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @param {import('../base_parser.js').XanoBaseParser} $
|
|
@@ -14,7 +14,7 @@ export function flexibleStringClause($) {
|
|
|
14
14
|
return (args) => {
|
|
15
15
|
// During parser initialization, args is undefined
|
|
16
16
|
// Use a placeholder token for grammar recording
|
|
17
|
-
const token = args?.[0] ||
|
|
17
|
+
const token = args?.[0] || InstructionsToken;
|
|
18
18
|
const allowEmpty = args?.[1] ?? true;
|
|
19
19
|
|
|
20
20
|
$.sectionStack.push(token.name + "Clause");
|
|
@@ -20,46 +20,53 @@ export function nakedStackFn($) {
|
|
|
20
20
|
$.CONSUME(LCurly); // "{"
|
|
21
21
|
$.MANY(() => {
|
|
22
22
|
$.AT_LEAST_ONE1(() => $.CONSUME1(NewlineToken)); // required new line before each clause
|
|
23
|
-
$.OPTION(() => $.CONSUME(ExclamationToken)); // optional "!" to continue on error
|
|
23
|
+
// $.OPTION(() => $.CONSUME(ExclamationToken)); // optional "!" to continue on error
|
|
24
24
|
$.OR([
|
|
25
25
|
{ ALT: () => $.CONSUME(CommentToken) },
|
|
26
|
-
{ ALT: () => $.SUBRULE($.aiFn) },
|
|
27
|
-
{ ALT: () => $.SUBRULE($.dbFn) },
|
|
28
|
-
{ ALT: () => $.SUBRULE($.varFn) },
|
|
29
|
-
{ ALT: () => $.SUBRULE($.arrayFn) },
|
|
30
|
-
{ ALT: () => $.SUBRULE($.cloudFn) },
|
|
31
|
-
{ ALT: () => $.SUBRULE($.securityFn) },
|
|
32
|
-
{ ALT: () => $.SUBRULE($.mathFn) },
|
|
33
|
-
{ ALT: () => $.SUBRULE($.textFn) },
|
|
34
|
-
{ ALT: () => $.SUBRULE($.utilFn) },
|
|
35
|
-
{ ALT: () => $.SUBRULE($.preconditionFn) },
|
|
36
|
-
{ ALT: () => $.SUBRULE($.zipFn) },
|
|
37
|
-
{ ALT: () => $.SUBRULE($.objectFn) },
|
|
38
|
-
{ ALT: () => $.SUBRULE($.apiFn, { ARGS: [options] }) },
|
|
39
|
-
{ ALT: () => $.SUBRULE($.webflowFn) },
|
|
40
|
-
{ ALT: () => $.SUBRULE($.redisFn) },
|
|
41
|
-
{ ALT: () => $.SUBRULE($.controlFn, { ARGS: [options] }) },
|
|
42
|
-
{ ALT: () => $.SUBRULE($.debugFn) },
|
|
43
|
-
{ ALT: () => $.SUBRULE($.storageFn) },
|
|
44
|
-
{ ALT: () => $.SUBRULE($.streamFn) },
|
|
45
|
-
{ ALT: () => $.SUBRULE($.actionCallFn) },
|
|
46
26
|
{
|
|
47
|
-
|
|
48
|
-
|
|
27
|
+
ALT: () => {
|
|
28
|
+
$.OPTION(() => $.CONSUME(ExclamationToken)); // optional "!" to continue on error
|
|
29
|
+
$.OR1([
|
|
30
|
+
{ ALT: () => $.SUBRULE($.aiFn) },
|
|
31
|
+
{ ALT: () => $.SUBRULE($.dbFn) },
|
|
32
|
+
{ ALT: () => $.SUBRULE($.varFn) },
|
|
33
|
+
{ ALT: () => $.SUBRULE($.arrayFn) },
|
|
34
|
+
{ ALT: () => $.SUBRULE($.cloudFn) },
|
|
35
|
+
{ ALT: () => $.SUBRULE($.securityFn) },
|
|
36
|
+
{ ALT: () => $.SUBRULE($.mathFn) },
|
|
37
|
+
{ ALT: () => $.SUBRULE($.textFn) },
|
|
38
|
+
{ ALT: () => $.SUBRULE($.utilFn) },
|
|
39
|
+
{ ALT: () => $.SUBRULE($.preconditionFn) },
|
|
40
|
+
{ ALT: () => $.SUBRULE($.zipFn) },
|
|
41
|
+
{ ALT: () => $.SUBRULE($.objectFn) },
|
|
42
|
+
{ ALT: () => $.SUBRULE($.apiFn, { ARGS: [options] }) },
|
|
43
|
+
{ ALT: () => $.SUBRULE($.webflowFn) },
|
|
44
|
+
{ ALT: () => $.SUBRULE($.redisFn) },
|
|
45
|
+
{ ALT: () => $.SUBRULE($.controlFn, { ARGS: [options] }) },
|
|
46
|
+
{ ALT: () => $.SUBRULE($.debugFn) },
|
|
47
|
+
{ ALT: () => $.SUBRULE($.storageFn) },
|
|
48
|
+
{ ALT: () => $.SUBRULE($.streamFn) },
|
|
49
|
+
{ ALT: () => $.SUBRULE($.actionCallFn) },
|
|
50
|
+
{
|
|
51
|
+
GATE: () => options.allowCallStatements,
|
|
52
|
+
ALT: () => $.SUBRULE($.toolCallFn),
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
GATE: () => options.allowCallStatements,
|
|
56
|
+
ALT: () => $.SUBRULE($.addonCallFn),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
GATE: () => options.allowCallStatements,
|
|
60
|
+
ALT: () => $.SUBRULE($.middlewareCallFn),
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
GATE: () => options.allowCallStatements,
|
|
64
|
+
ALT: () => $.SUBRULE($.triggerCallFn),
|
|
65
|
+
},
|
|
66
|
+
{ ALT: () => $.SUBRULE($.workflowExpectFn) },
|
|
67
|
+
]);
|
|
68
|
+
},
|
|
49
69
|
},
|
|
50
|
-
{
|
|
51
|
-
GATE: () => options.allowCallStatements,
|
|
52
|
-
ALT: () => $.SUBRULE($.addonCallFn),
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
GATE: () => options.allowCallStatements,
|
|
56
|
-
ALT: () => $.SUBRULE($.middlewareCallFn),
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
GATE: () => options.allowCallStatements,
|
|
60
|
-
ALT: () => $.SUBRULE($.triggerCallFn),
|
|
61
|
-
},
|
|
62
|
-
{ ALT: () => $.SUBRULE($.workflowExpectFn) },
|
|
63
70
|
]);
|
|
64
71
|
});
|
|
65
72
|
$.AT_LEAST_ONE2(() => $.CONSUME2(NewlineToken));
|
|
@@ -31,6 +31,16 @@ describe("nakedStack", () => {
|
|
|
31
31
|
expect(parser.errors).to.be.empty;
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
+
it("nakedStack accepts a disabled array statement", () => {
|
|
35
|
+
const parser = parse(`{
|
|
36
|
+
!array.push $my_array {
|
|
37
|
+
value = 1
|
|
38
|
+
}
|
|
39
|
+
}`);
|
|
40
|
+
console.log(parser.errors);
|
|
41
|
+
expect(parser.errors).to.be.empty;
|
|
42
|
+
});
|
|
43
|
+
|
|
34
44
|
it("nakedStack accepts a multiline statement", () => {
|
|
35
45
|
const parser = parse(`{
|
|
36
46
|
var $some_var {
|
|
@@ -27,6 +27,11 @@ describe("responseClause", () => {
|
|
|
27
27
|
expect(parser.errors).to.be.empty;
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
it("responseClause accepts a string", () => {
|
|
31
|
+
const parser = parse(`response = "no exception"`);
|
|
32
|
+
expect(parser.errors).to.be.empty;
|
|
33
|
+
});
|
|
34
|
+
|
|
30
35
|
it("responseClause accepts an object", () => {
|
|
31
36
|
const parser = parse(`response = {result1: $x1, x2: $x2}`);
|
|
32
37
|
expect(parser.errors).to.be.empty;
|
|
@@ -115,7 +115,11 @@ export function columnDefinition($) {
|
|
|
115
115
|
|
|
116
116
|
// Track table reference from FK column definition
|
|
117
117
|
if (captured.table?.value) {
|
|
118
|
-
$.addReference(
|
|
118
|
+
$.addReference(
|
|
119
|
+
"table",
|
|
120
|
+
getVarName(captured.table.value),
|
|
121
|
+
captured.table.value,
|
|
122
|
+
);
|
|
119
123
|
}
|
|
120
124
|
|
|
121
125
|
if (inputTypeToken.image == "vector" && !captured.size) {
|
|
@@ -126,7 +130,8 @@ export function columnDefinition($) {
|
|
|
126
130
|
}
|
|
127
131
|
|
|
128
132
|
const inTable = $.sectionStack.includes("tableDeclaration");
|
|
129
|
-
|
|
133
|
+
const inIntput = $.sectionStack.includes("inputClause");
|
|
134
|
+
if (captured.visibility && !inTable && !inIntput) {
|
|
130
135
|
$.addInvalidValueError(
|
|
131
136
|
nameToken,
|
|
132
137
|
'The "visibility" attribute is only valid within a table schema definition.',
|
|
@@ -19,6 +19,14 @@ describe("forEachFn", () => {
|
|
|
19
19
|
expect(parser.errors).to.be.empty;
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
+
it("forEachFn should warn when using a reserved variable name", () => {
|
|
23
|
+
const parser = parse(`foreach ([]|push:1|push:2|push:3|push:4) {
|
|
24
|
+
each as $env
|
|
25
|
+
}`);
|
|
26
|
+
expect(parser.errors).to.be.empty;
|
|
27
|
+
expect(parser.warnings).to.not.be.empty;
|
|
28
|
+
});
|
|
29
|
+
|
|
22
30
|
it("forEachFn can also be foreach.remove", () => {
|
|
23
31
|
const parser = parse(`foreach.remove`);
|
|
24
32
|
expect(parser.errors).to.be.empty;
|
|
@@ -26,6 +26,16 @@ describe("functionRunFn", () => {
|
|
|
26
26
|
expect(parser.errors).to.be.empty;
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
+
it("functionRunFn accepts complex expression as input", () => {
|
|
30
|
+
const parser = parse(`function.run "my_complex_function" {
|
|
31
|
+
input = {
|
|
32
|
+
Snumber : (($SomeVar.0.var_rel/55) > 0 ? ($SomeVar.0.var_rel1/61) : 6)+(($My_Var_S2.0.elo/ 60) > 7 ? ($My_Var_S2.0.elo/62) : 9)+(($Optional_var2.0.det/63) > 4 ? ($Optional_var2.0.det /64) : 2)+(($PtSgeS_sge.0.elo/65) > 1 ? ($PtSgeS_sge.0.elo/66) : 3)
|
|
33
|
+
NumberObject: []
|
|
34
|
+
}
|
|
35
|
+
} as $Var_Round`);
|
|
36
|
+
expect(parser.errors).to.be.empty;
|
|
37
|
+
});
|
|
38
|
+
|
|
29
39
|
it("functionRunFn accepts a string for function name", () => {
|
|
30
40
|
const parser = parse(`function.run "my/function" {
|
|
31
41
|
input = {a: $input.a, b: $b}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LParent, RParent } from "../../../lexer/control.js";
|
|
2
|
+
import { NewlineToken } from "../../../lexer/tokens.js";
|
|
2
3
|
import { PreconditionToken } from "../../../lexer/util.js";
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -13,7 +14,9 @@ export function preconditionFn($) {
|
|
|
13
14
|
$.sectionStack.push("preconditionFn");
|
|
14
15
|
const tokenFn = $.CONSUME(PreconditionToken); // "precondition"
|
|
15
16
|
$.CONSUME(LParent);
|
|
17
|
+
$.MANY(() => $.CONSUME(NewlineToken));
|
|
16
18
|
$.SUBRULE($.expressionFn);
|
|
19
|
+
$.MANY1(() => $.CONSUME1(NewlineToken));
|
|
17
20
|
$.CONSUME(RParent);
|
|
18
21
|
$.SUBRULE($.schemaParseAttributeFn, {
|
|
19
22
|
ARGS: [
|
|
@@ -346,4 +346,35 @@ describe("dbQueryFn", () => {
|
|
|
346
346
|
const parser = parse(`query user as $user`);
|
|
347
347
|
expect(parser.errors).to.be.empty;
|
|
348
348
|
});
|
|
349
|
+
|
|
350
|
+
describe("aggregate eval with aggregator pipes", () => {
|
|
351
|
+
const aggregators = [
|
|
352
|
+
"avg",
|
|
353
|
+
"count",
|
|
354
|
+
"count_distinct",
|
|
355
|
+
"max",
|
|
356
|
+
"median",
|
|
357
|
+
"min",
|
|
358
|
+
"sum",
|
|
359
|
+
"to_distinct_list",
|
|
360
|
+
"to_distinct_list_asc",
|
|
361
|
+
"to_distinct_list_desc",
|
|
362
|
+
"to_list",
|
|
363
|
+
"to_list_asc",
|
|
364
|
+
"to_list_desc",
|
|
365
|
+
];
|
|
366
|
+
|
|
367
|
+
for (const agg of aggregators) {
|
|
368
|
+
it(`dbQueryFn return eval accepts |${agg} aggregator`, () => {
|
|
369
|
+
const parser = parse(`query user {
|
|
370
|
+
return = {
|
|
371
|
+
type: "aggregate"
|
|
372
|
+
group: {user_name: $db.user.name}
|
|
373
|
+
eval: {result: $db.user.name|${agg}}
|
|
374
|
+
}
|
|
375
|
+
} as $user`);
|
|
376
|
+
expect(parser.errors).to.be.empty;
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
});
|
|
349
380
|
});
|