@xano/xanoscript-language-server 11.4.0 → 11.6.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/.claude/settings.local.json +11 -0
- package/package.json +1 -1
- package/parser/agent_parser.js +7 -9
- package/parser/api_group_parser.js +10 -23
- package/parser/clauses/cacheClause.js +9 -10
- package/parser/functions/security/securityCreateAuthTokenFn.js +14 -10
- package/parser/functions/security/securityCreateAuthTokenFn.spec.js +28 -1
- package/parser/generic/register.js +10 -22
- package/parser/mcp_server_parser.js +7 -9
- package/parser/mcp_server_trigger_parser.js +2 -6
- package/parser/realtime_trigger_parser.js +3 -7
- package/parser/table_trigger_parser.js +9 -11
- package/parser/test_parser.js +0 -4
- package/parser/workspace_trigger_parser.js +4 -8
- package/parser/generic/arrayOfObjectAttrReq.js +0 -47
- package/parser/generic/arrayOfObjectAttrReq.spec.js +0 -254
- package/parser/generic/arrayOfStringLiterals.js +0 -45
- package/parser/generic/arrayOfStringLiterals.spec.js +0 -56
- package/parser/generic/objectAttrReq.js +0 -243
- package/parser/generic/objectAttrReq.spec.js +0 -412
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
import { expect } from "chai";
|
|
2
|
-
import { describe, it } from "mocha";
|
|
3
|
-
import { EqualToken } from "../../lexer/control.js";
|
|
4
|
-
import { lexDocument } from "../../lexer/lexer.js";
|
|
5
|
-
import { Identifier } from "../../lexer/tokens.js";
|
|
6
|
-
import { parser } from "../test_parser.js";
|
|
7
|
-
|
|
8
|
-
export function parserExtension() {
|
|
9
|
-
// this rule requires a foo field to be defined
|
|
10
|
-
this.arrayOfObjectAttrReq_test_required = this.RULE(
|
|
11
|
-
"arrayOfObjectAttrReq_test_required",
|
|
12
|
-
() => {
|
|
13
|
-
const name = this.CONSUME(Identifier);
|
|
14
|
-
this.CONSUME(EqualToken); // "="
|
|
15
|
-
this.SUBRULE(this.arrayOfObjectAttrReq, { ARGS: [name, ["foo"]] });
|
|
16
|
-
}
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
this.arrayOfObjectAttrReq_test_optionals = this.RULE(
|
|
20
|
-
"arrayOfObjectAttrReq_test_optionals",
|
|
21
|
-
() => {
|
|
22
|
-
const name = this.CONSUME(Identifier);
|
|
23
|
-
this.CONSUME(EqualToken); // "="
|
|
24
|
-
this.SUBRULE(this.arrayOfObjectAttrReq, {
|
|
25
|
-
ARGS: [name, [], ["foo", "bar"]],
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
this.arrayOfObjectAttrReq_test_index = this.RULE(
|
|
31
|
-
"arrayOfObjectAttrReq_test_index",
|
|
32
|
-
() => {
|
|
33
|
-
const parent = this.CONSUME(Identifier);
|
|
34
|
-
this.CONSUME(EqualToken); // "="
|
|
35
|
-
this.SUBRULE(this.arrayOfObjectAttrReq, {
|
|
36
|
-
ARGS: [
|
|
37
|
-
parent,
|
|
38
|
-
["type", "field"],
|
|
39
|
-
[],
|
|
40
|
-
{
|
|
41
|
-
types: {
|
|
42
|
-
type: "string",
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
],
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
this.arrayOfObjectAttrReq_test_recursive_value = this.RULE(
|
|
51
|
-
"test_recursive_value",
|
|
52
|
-
() => {
|
|
53
|
-
const name = this.CONSUME(Identifier);
|
|
54
|
-
this.CONSUME(EqualToken); // "="
|
|
55
|
-
this.SUBRULE(this.arrayOfObjectAttrReq, {
|
|
56
|
-
ARGS: [
|
|
57
|
-
name,
|
|
58
|
-
["name", "as", "input"],
|
|
59
|
-
["addon"],
|
|
60
|
-
{ recursive: ["addon"] },
|
|
61
|
-
],
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
);
|
|
65
|
-
this.arrayOfObjectAttrReq_test_recursive_array_addon = this.RULE(
|
|
66
|
-
"test_recursive_array_addon",
|
|
67
|
-
() => {
|
|
68
|
-
const name = this.CONSUME(Identifier);
|
|
69
|
-
this.CONSUME(EqualToken); // "="
|
|
70
|
-
this.SUBRULE(this.arrayOfObjectAttrReq, {
|
|
71
|
-
ARGS: [
|
|
72
|
-
name,
|
|
73
|
-
["name", "as", "input"],
|
|
74
|
-
["addon"],
|
|
75
|
-
{ recursiveArray: ["addon"] },
|
|
76
|
-
],
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function parse(inputText) {
|
|
83
|
-
parser.reset();
|
|
84
|
-
const lexResult = lexDocument(inputText);
|
|
85
|
-
parser.input = lexResult.tokens;
|
|
86
|
-
return parser;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
describe("arrayOfObjectAttrReq", () => {
|
|
90
|
-
it("arrayOfObjectAttrReq accepts required attribute", () => {
|
|
91
|
-
parse(`something = [ {foo: "true"}]`).arrayOfObjectAttrReq_test_required();
|
|
92
|
-
expect(parser.errors).to.be.empty;
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("arrayOfObjectAttrReq accepts multilines attributes", () => {
|
|
96
|
-
parse(`something = [
|
|
97
|
-
{
|
|
98
|
-
foo: true
|
|
99
|
-
bar: 12
|
|
100
|
-
}
|
|
101
|
-
]`).arrayOfObjectAttrReq_test_optionals();
|
|
102
|
-
expect(parser.errors).to.be.empty;
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it("arrayOfObjectAttrReq accepts list of single lines", () => {
|
|
106
|
-
parse(`something = [
|
|
107
|
-
{foo: true, bar: 12}
|
|
108
|
-
]`).arrayOfObjectAttrReq_test_optionals();
|
|
109
|
-
expect(parser.errors).to.be.empty;
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it("arrayOfObjectAttrReq accepts multiple values", () => {
|
|
113
|
-
parse(`something = [
|
|
114
|
-
{
|
|
115
|
-
foo: true
|
|
116
|
-
}
|
|
117
|
-
{
|
|
118
|
-
bar: 12
|
|
119
|
-
}
|
|
120
|
-
]`).arrayOfObjectAttrReq_test_optionals();
|
|
121
|
-
expect(parser.errors).to.be.empty;
|
|
122
|
-
|
|
123
|
-
parse(
|
|
124
|
-
`something = [{ foo: true }, {bar: 12}]`
|
|
125
|
-
).arrayOfObjectAttrReq_test_optionals();
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("arrayOfObjectAttrReq detects missing required attribute", () => {
|
|
129
|
-
parse(`something = [ {}]`).arrayOfObjectAttrReq_test_required();
|
|
130
|
-
expect(parser.errors).to.not.be.empty;
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it("arrayOfObjectAttrReq allow for recursive definition", () => {
|
|
134
|
-
parse(`addon = [
|
|
135
|
-
{
|
|
136
|
-
name : "client"
|
|
137
|
-
as : "_client"
|
|
138
|
-
input: {client_id: ""}
|
|
139
|
-
addon: {
|
|
140
|
-
name : "client"
|
|
141
|
-
as : "_client"
|
|
142
|
-
input: {client_id: ""}
|
|
143
|
-
addon: {
|
|
144
|
-
name : "client"
|
|
145
|
-
as : "_client"
|
|
146
|
-
input: {client_id: ""}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
]`).arrayOfObjectAttrReq_test_recursive_value();
|
|
151
|
-
expect(parser.errors).to.be.empty;
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it("arrayOfObjectAttrReq detects missing definition on recursive attributes", () => {
|
|
155
|
-
parse(`addon = [
|
|
156
|
-
{
|
|
157
|
-
name : "client"
|
|
158
|
-
as : "_client"
|
|
159
|
-
input: {client_id: ""}
|
|
160
|
-
addon: {
|
|
161
|
-
name : "client"
|
|
162
|
-
input: {client_id: ""}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
]`).arrayOfObjectAttrReq_test_recursive_value();
|
|
166
|
-
expect(parser.errors).to.not.be.empty;
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it("arrayOfObjectAttrReq allow for recursive array", () => {
|
|
170
|
-
parse(`addon = [
|
|
171
|
-
{
|
|
172
|
-
name : "client"
|
|
173
|
-
as : "_client"
|
|
174
|
-
input: {client_id: ""}
|
|
175
|
-
addon: [
|
|
176
|
-
{
|
|
177
|
-
name : "client"
|
|
178
|
-
as : "_client"
|
|
179
|
-
input: {client_id: ""}
|
|
180
|
-
addon: [
|
|
181
|
-
{
|
|
182
|
-
name : "client"
|
|
183
|
-
as : "_client"
|
|
184
|
-
input: {client_id: ""}
|
|
185
|
-
addon: []
|
|
186
|
-
}
|
|
187
|
-
]
|
|
188
|
-
}
|
|
189
|
-
]
|
|
190
|
-
}
|
|
191
|
-
]`).arrayOfObjectAttrReq_test_recursive_array_addon();
|
|
192
|
-
expect(parser.errors).to.be.empty;
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it("arrayOfObjectAttrReq detects missing definition on recursive array", () => {
|
|
196
|
-
parse(`addon = [
|
|
197
|
-
{
|
|
198
|
-
name : "client"
|
|
199
|
-
as : "_client"
|
|
200
|
-
input: {client_id: ""}
|
|
201
|
-
addon: [
|
|
202
|
-
{
|
|
203
|
-
name : "client"
|
|
204
|
-
input: {client_id: ""}
|
|
205
|
-
}
|
|
206
|
-
]
|
|
207
|
-
}
|
|
208
|
-
]`).arrayOfObjectAttrReq_test_recursive_array_addon();
|
|
209
|
-
expect(parser.errors).to.not.be.empty;
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
it("arrayOfObjectAttrReq accepts optional attributes", () => {
|
|
213
|
-
parse(`something = [{}]`).arrayOfObjectAttrReq_test_optionals();
|
|
214
|
-
expect(parser.errors).to.be.empty;
|
|
215
|
-
|
|
216
|
-
parse(`something = [{foo: true}]`).arrayOfObjectAttrReq_test_optionals();
|
|
217
|
-
expect(parser.errors).to.be.empty;
|
|
218
|
-
|
|
219
|
-
parse(`something = [{bar: "ok"}]`).arrayOfObjectAttrReq_test_optionals();
|
|
220
|
-
expect(parser.errors).to.be.empty;
|
|
221
|
-
|
|
222
|
-
parse(
|
|
223
|
-
`something = [{bar: "ok", "foo": 12.1}]`
|
|
224
|
-
).arrayOfObjectAttrReq_test_optionals();
|
|
225
|
-
expect(parser.errors).to.be.empty;
|
|
226
|
-
|
|
227
|
-
parse(`something = [
|
|
228
|
-
{bar: "ok", "foo": 12.1},
|
|
229
|
-
{bar: "ok", "foo": 12.1}
|
|
230
|
-
]`).arrayOfObjectAttrReq_test_optionals();
|
|
231
|
-
expect(parser.errors).to.be.empty;
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
it("arrayOfObjectAttrReq can be used for single line index clause", () => {
|
|
235
|
-
parse(
|
|
236
|
-
`index = [{type: "primary", field: [{name: "id"}]}]`
|
|
237
|
-
).arrayOfObjectAttrReq_test_index();
|
|
238
|
-
expect(parser.errors).to.be.empty;
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it("arrayOfObjectAttrReq can be used for index clause", () => {
|
|
242
|
-
parse(`index = [
|
|
243
|
-
{type: "primary", field: [{name: "id"}]}
|
|
244
|
-
{type: "gin", field: [{name: "xdo", op: "jsonb_path_op"}]}
|
|
245
|
-
{type: "btree", field: [{name: "created_at", op: "desc"}]}
|
|
246
|
-
]`).arrayOfObjectAttrReq_test_index();
|
|
247
|
-
expect(parser.errors).to.be.empty;
|
|
248
|
-
|
|
249
|
-
parse(`index = [
|
|
250
|
-
{type: "primary", field: [{name: "id"}]}
|
|
251
|
-
]`).arrayOfObjectAttrReq_test_index();
|
|
252
|
-
expect(parser.errors).to.be.empty;
|
|
253
|
-
});
|
|
254
|
-
});
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { CommaToken, LSquare, RSquare } from "../../lexer/control.js";
|
|
2
|
-
import { StringLiteral } from "../../lexer/literal.js";
|
|
3
|
-
import { NewlineToken } from "../../lexer/tokens.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Dynamicly raises error for missing and required attributes
|
|
7
|
-
* @param {import('../base_parser.js').XanoBaseParser} $
|
|
8
|
-
*/
|
|
9
|
-
export function arrayOfStringLiterals($) {
|
|
10
|
-
return () => {
|
|
11
|
-
$.CONSUME(LSquare); // "["
|
|
12
|
-
$.OPTION(() => {
|
|
13
|
-
$.OR([
|
|
14
|
-
// multi line, each value on a new line
|
|
15
|
-
// [
|
|
16
|
-
// value
|
|
17
|
-
// value2
|
|
18
|
-
// ]
|
|
19
|
-
{
|
|
20
|
-
ALT: () => {
|
|
21
|
-
$.CONSUME(NewlineToken);
|
|
22
|
-
$.AT_LEAST_ONE(() => {
|
|
23
|
-
$.CONSUME(StringLiteral);
|
|
24
|
-
$.OPTION1(() => $.CONSUME(CommaToken)); // optional comma
|
|
25
|
-
$.AT_LEAST_ONE1(() => $.CONSUME3(NewlineToken));
|
|
26
|
-
});
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
// one line, comma separated [ value, value2 ]
|
|
30
|
-
{
|
|
31
|
-
ALT: () => {
|
|
32
|
-
$.AT_LEAST_ONE_SEP({
|
|
33
|
-
SEP: CommaToken,
|
|
34
|
-
DEF: () => {
|
|
35
|
-
$.CONSUME1(StringLiteral);
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
$.MANY(() => $.CONSUME2(NewlineToken));
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
]);
|
|
42
|
-
});
|
|
43
|
-
$.CONSUME(RSquare); // "]"
|
|
44
|
-
};
|
|
45
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { expect } from "chai";
|
|
2
|
-
import { describe, it } from "mocha";
|
|
3
|
-
import { lexDocument } from "../../lexer/lexer.js";
|
|
4
|
-
import { parser } from "../test_parser.js";
|
|
5
|
-
|
|
6
|
-
function parse(inputText) {
|
|
7
|
-
parser.reset();
|
|
8
|
-
const lexResult = lexDocument(inputText);
|
|
9
|
-
parser.input = lexResult.tokens;
|
|
10
|
-
parser.arrayOfStringLiterals();
|
|
11
|
-
return parser;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
describe("arrayOfStringLiterals", () => {
|
|
15
|
-
it("arrayOfStringLiterals accepts a single string ", () => {
|
|
16
|
-
const parser = parse(`[ "true" ]`);
|
|
17
|
-
expect(parser.errors).to.be.empty;
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("arrayOfStringLiterals can be empty ", () => {
|
|
21
|
-
const parser = parse(`[]`);
|
|
22
|
-
expect(parser.errors).to.be.empty;
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("arrayOfStringLiterals can be multilines without trailing commas", () => {
|
|
26
|
-
const parser = parse(`[
|
|
27
|
-
"foo"
|
|
28
|
-
"bar"
|
|
29
|
-
]`);
|
|
30
|
-
expect(parser.errors).to.be.empty;
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("arrayOfStringLiterals can be multilines with trailing commas ", () => {
|
|
34
|
-
const parser = parse(`[
|
|
35
|
-
"foo",
|
|
36
|
-
"bar"
|
|
37
|
-
]`);
|
|
38
|
-
expect(parser.errors).to.be.empty;
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("arrayOfStringLiterals can be compact ", () => {
|
|
42
|
-
const parser = parse(`["foo","bar"]`);
|
|
43
|
-
expect(parser.errors).to.be.empty;
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("arrayOfStringLiterals cannot contain a non string ", () => {
|
|
47
|
-
let parser = parse(`[4]`);
|
|
48
|
-
expect(parser.errors).to.not.be.empty;
|
|
49
|
-
|
|
50
|
-
parser = parse(`[true]`);
|
|
51
|
-
expect(parser.errors).to.not.be.empty;
|
|
52
|
-
|
|
53
|
-
parser = parse(`["test", 4]`);
|
|
54
|
-
expect(parser.errors).to.not.be.empty;
|
|
55
|
-
});
|
|
56
|
-
});
|
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
import { MismatchedTokenException } from "chevrotain";
|
|
2
|
-
import { ColonToken, CommaToken, LCurly, RCurly } from "../../lexer/control.js";
|
|
3
|
-
import { StringLiteral } from "../../lexer/literal.js";
|
|
4
|
-
import { MultiLineStringToken } from "../../lexer/multiline.js";
|
|
5
|
-
import { Identifier, NewlineToken } from "../../lexer/tokens.js";
|
|
6
|
-
import { getVarName } from "./utils.js";
|
|
7
|
-
|
|
8
|
-
const DEFAULT_OPTIONS = {
|
|
9
|
-
allowDuplicates: [],
|
|
10
|
-
allowQueryExpression: false,
|
|
11
|
-
recursive: [],
|
|
12
|
-
recursiveArray: [],
|
|
13
|
-
allowVariables: true,
|
|
14
|
-
types: {},
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Dynamicly raises error for missing and required attributes
|
|
19
|
-
* @param {import('../base_parser.js').XanoBaseParser} $
|
|
20
|
-
*/
|
|
21
|
-
export function objectAttrReq($) {
|
|
22
|
-
/**
|
|
23
|
-
* @param {import('chevrotain').IToken} parent
|
|
24
|
-
* @param {string[]} required the required attributes
|
|
25
|
-
* @param {string[]} optional the optional attributes
|
|
26
|
-
* @param {Object} options additional options
|
|
27
|
-
* @param {string[]} options.allowDuplicates attributes that are allowed to be duplicated
|
|
28
|
-
* @param {string[]} options.allowQueryExpression attributes that are allowed to be duplicated
|
|
29
|
-
* @param {string[]} options.recursive the same rules applies to sub-objects deeper in the tree
|
|
30
|
-
* @param {string[]} options.recursiveArray the same rules applies to sub-arrays deeper in the tree
|
|
31
|
-
* @param {boolean} options.allowVariables whether to allow variables as values
|
|
32
|
-
* @param {Object} options.types a map of attribute names to their expected types (string, number, boolean)
|
|
33
|
-
* if the type is not specified, any value is allowed
|
|
34
|
-
**/
|
|
35
|
-
return (parent, required, optional, options = {}) => {
|
|
36
|
-
// setup basic defaults
|
|
37
|
-
required = required || [];
|
|
38
|
-
optional = optional || [];
|
|
39
|
-
|
|
40
|
-
options = {
|
|
41
|
-
...DEFAULT_OPTIONS,
|
|
42
|
-
...options,
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const definedAttributes = [];
|
|
46
|
-
const allowedAttributes = [...required, ...optional];
|
|
47
|
-
let identifier;
|
|
48
|
-
|
|
49
|
-
$.CONSUME(LCurly); // "{"
|
|
50
|
-
$.OPTION(() => {
|
|
51
|
-
$.OR([
|
|
52
|
-
{
|
|
53
|
-
// one line, comma separated { key: value, key2: value2 }
|
|
54
|
-
ALT: () => {
|
|
55
|
-
$.AT_LEAST_ONE_SEP({
|
|
56
|
-
SEP: CommaToken,
|
|
57
|
-
DEF: () => {
|
|
58
|
-
identifier = $.OR1([
|
|
59
|
-
{ ALT: () => $.CONSUME(Identifier) }, // key
|
|
60
|
-
{ ALT: () => $.CONSUME(StringLiteral) }, // "value"
|
|
61
|
-
]);
|
|
62
|
-
definedAttributes.push(identifier);
|
|
63
|
-
const varName = getVarName(identifier);
|
|
64
|
-
$.CONSUME(ColonToken);
|
|
65
|
-
if (options.recursive.includes(varName)) {
|
|
66
|
-
$.SUBRULE($.objectAttrReq, {
|
|
67
|
-
ARGS: [identifier, required, optional, options],
|
|
68
|
-
});
|
|
69
|
-
} else if (options.recursiveArray.includes(varName)) {
|
|
70
|
-
$.SUBRULE($.arrayOfObjectAttrReq, {
|
|
71
|
-
ARGS: [identifier, required, optional, options],
|
|
72
|
-
});
|
|
73
|
-
} else {
|
|
74
|
-
const varType = options.types[varName];
|
|
75
|
-
if (typeof varType === "function") {
|
|
76
|
-
varType.call(null, $, identifier); // custom function handler
|
|
77
|
-
} else if (Array.isArray(varType)) {
|
|
78
|
-
// Treat array as enum - value must be one of the array elements
|
|
79
|
-
const stringToken = $.CONSUME3(StringLiteral);
|
|
80
|
-
const providedValue = getVarName(stringToken);
|
|
81
|
-
|
|
82
|
-
// Check if the provided value is in the allowed array
|
|
83
|
-
if (!varType.includes(providedValue)) {
|
|
84
|
-
$.addMissingError(
|
|
85
|
-
stringToken,
|
|
86
|
-
`Invalid value "${providedValue}". Must be one of: ${varType
|
|
87
|
-
.map((v) => `"${v}"`)
|
|
88
|
-
.join(", ")}`
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
} else {
|
|
92
|
-
$.OR4([
|
|
93
|
-
{
|
|
94
|
-
GATE: () => varType === "boolean",
|
|
95
|
-
ALT: () => $.SUBRULE($.booleanValue), // boolean
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
GATE: () => varType === "number",
|
|
99
|
-
ALT: () => $.SUBRULE($.numberValue), // number
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
GATE: () => varType === "string",
|
|
103
|
-
ALT: () => $.CONSUME3(StringLiteral), // "..."
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
GATE: () => varType === "object",
|
|
107
|
-
ALT: () =>
|
|
108
|
-
$.SUBRULE1($.objectWithAttributes, {
|
|
109
|
-
ARGS: [options],
|
|
110
|
-
}),
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
GATE: () => !varType,
|
|
114
|
-
ALT: () =>
|
|
115
|
-
$.SUBRULE($.expressionFn, {
|
|
116
|
-
ARGS: [identifier, options],
|
|
117
|
-
}), // anything goes
|
|
118
|
-
},
|
|
119
|
-
]);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
},
|
|
123
|
-
});
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
// multi line, each key value pair on a new line
|
|
128
|
-
// {
|
|
129
|
-
// key: value
|
|
130
|
-
// key2: value2
|
|
131
|
-
// }
|
|
132
|
-
ALT: () => {
|
|
133
|
-
$.CONSUME(NewlineToken);
|
|
134
|
-
$.AT_LEAST_ONE(() => {
|
|
135
|
-
const identifier = $.OR2([
|
|
136
|
-
{ ALT: () => $.CONSUME2(Identifier) }, // key
|
|
137
|
-
{ ALT: () => $.CONSUME2(StringLiteral) }, // "other key"
|
|
138
|
-
]);
|
|
139
|
-
definedAttributes.push(identifier);
|
|
140
|
-
$.CONSUME1(ColonToken);
|
|
141
|
-
const varName = getVarName(identifier);
|
|
142
|
-
if (options.recursive.includes(varName)) {
|
|
143
|
-
$.SUBRULE($.objectAttrReq, {
|
|
144
|
-
ARGS: [identifier, required, optional, options],
|
|
145
|
-
});
|
|
146
|
-
} else if (options.recursiveArray.includes(varName)) {
|
|
147
|
-
$.SUBRULE($.arrayOfObjectAttrReq, {
|
|
148
|
-
ARGS: [identifier, required, optional, options],
|
|
149
|
-
});
|
|
150
|
-
} else {
|
|
151
|
-
const varType = options.types[varName];
|
|
152
|
-
if (typeof varType === "function") {
|
|
153
|
-
varType.call(null, $, identifier); // custom function handler
|
|
154
|
-
} else if (Array.isArray(varType)) {
|
|
155
|
-
// Treat array as enum - value must be one of the array elements
|
|
156
|
-
const stringToken = $.CONSUME3(StringLiteral);
|
|
157
|
-
const providedValue = getVarName(stringToken);
|
|
158
|
-
|
|
159
|
-
// Check if the provided value is in the allowed array
|
|
160
|
-
if (!varType.includes(providedValue)) {
|
|
161
|
-
$.SAVE_ERROR(
|
|
162
|
-
new MismatchedTokenException(
|
|
163
|
-
`Invalid value "${providedValue}". Must be one of: ${varType
|
|
164
|
-
.map((v) => `"${v}"`)
|
|
165
|
-
.join(", ")}`,
|
|
166
|
-
stringToken,
|
|
167
|
-
stringToken
|
|
168
|
-
)
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
} else {
|
|
172
|
-
$.OR3([
|
|
173
|
-
{
|
|
174
|
-
GATE: () => varType === "boolean",
|
|
175
|
-
ALT: () => $.SUBRULE1($.booleanValue), // boolean
|
|
176
|
-
},
|
|
177
|
-
{
|
|
178
|
-
GATE: () => varType === "number",
|
|
179
|
-
ALT: () => $.SUBRULE1($.numberValue), // number
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
GATE: () => varType === "string",
|
|
183
|
-
ALT: () =>
|
|
184
|
-
$.OR5([
|
|
185
|
-
{ ALT: () => $.CONSUME1(StringLiteral) }, // "..."
|
|
186
|
-
{ ALT: () => $.CONSUME(MultiLineStringToken) },
|
|
187
|
-
]),
|
|
188
|
-
},
|
|
189
|
-
{
|
|
190
|
-
GATE: () => varType === "object",
|
|
191
|
-
ALT: () =>
|
|
192
|
-
$.SUBRULE($.objectWithAttributes, { ARGS: [options] }),
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
GATE: () => !varType,
|
|
196
|
-
ALT: () =>
|
|
197
|
-
$.SUBRULE1($.expressionFn, {
|
|
198
|
-
ARGS: [identifier, options],
|
|
199
|
-
}), // anything goes
|
|
200
|
-
},
|
|
201
|
-
]);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
$.OPTION1(() => $.CONSUME(CommaToken)); // ","
|
|
205
|
-
$.AT_LEAST_ONE1(() => $.CONSUME1(NewlineToken));
|
|
206
|
-
});
|
|
207
|
-
},
|
|
208
|
-
},
|
|
209
|
-
]);
|
|
210
|
-
});
|
|
211
|
-
$.CONSUME(RCurly); // "}"
|
|
212
|
-
|
|
213
|
-
// detects duplicate attributes
|
|
214
|
-
const duplicateAttributes = definedAttributes.filter(
|
|
215
|
-
(token, index) =>
|
|
216
|
-
!options.allowDuplicates.includes(getVarName(token)) &&
|
|
217
|
-
definedAttributes.findIndex(
|
|
218
|
-
(t) => getVarName(t) === getVarName(token)
|
|
219
|
-
) !== index
|
|
220
|
-
);
|
|
221
|
-
|
|
222
|
-
for (const token of duplicateAttributes) {
|
|
223
|
-
$.addDuplicateAttributeError(token);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// report any illegal attributes
|
|
227
|
-
const illegalAttributes = definedAttributes.filter(
|
|
228
|
-
(token) => !allowedAttributes.includes(getVarName(token))
|
|
229
|
-
);
|
|
230
|
-
for (const token of illegalAttributes) {
|
|
231
|
-
$.addIllegalAttributeError(token);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// report any missing attributes
|
|
235
|
-
const definedAttrNames = definedAttributes.map(getVarName);
|
|
236
|
-
const missingAttributes = required.filter(
|
|
237
|
-
(reqAttrName) => !definedAttrNames.includes(reqAttrName)
|
|
238
|
-
);
|
|
239
|
-
if (missingAttributes.length > 0) {
|
|
240
|
-
$.addMissingAttributeError(parent, missingAttributes);
|
|
241
|
-
}
|
|
242
|
-
};
|
|
243
|
-
}
|