@tellaro/tql 0.1.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/dist/_virtual/_plugin-vue_export-helper.js +9 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +37 -0
- package/dist/src/completionProvider.d.ts +22 -0
- package/dist/src/completionProvider.js +619 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/parser.d.ts +81 -0
- package/dist/src/parser.js +511 -0
- package/dist/src/tokenizer.d.ts +113 -0
- package/dist/src/tokenizer.js +430 -0
- package/dist/src/types.d.ts +221 -0
- package/dist/src/validator.d.ts +46 -0
- package/dist/src/validator.js +315 -0
- package/dist/style.css +1 -0
- package/dist/vue/TqlAutocomplete.vue.d.ts +2 -0
- package/dist/vue/TqlAutocomplete.vue.js +7 -0
- package/dist/vue/TqlAutocomplete.vue2.js +136 -0
- package/dist/vue/TqlInput.vue.d.ts +2 -0
- package/dist/vue/TqlInput.vue.js +7 -0
- package/dist/vue/TqlInput.vue2.js +326 -0
- package/dist/vue/index.d.ts +10 -0
- package/dist/vue/index.js +8 -0
- package/dist/vue/useTqlInput.js +166 -0
- package/package.json +79 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @tellaro/tql
|
|
3
|
+
*
|
|
4
|
+
* Tellaro Query Language (TQL) parser, validator, and autocomplete for JavaScript/TypeScript.
|
|
5
|
+
* Synchronized with tellaro-query-language Python/Rust implementations.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
export type { TqlToken, TqlTokenType, TqlExpression, TqlBinaryExpression, TqlUnaryExpression, TqlComparisonExpression, TqlCollectionExpression, TqlMutatorExpression, TqlAggregationExpression, TqlStatsExpression, TqlGroupExpression, TqlLiteral, TqlFieldReference, TqlComparisonOperator, TqlLogicalOperator, TqlCollectionOperator, TqlMutator, TqlAggregationFunction, TqlTypeHint, TqlVisualizationType, TqlParseResult, TqlParseError, TqlParseWarning, TqlErrorCode, TqlWarningCode, TqlValidationResult, TqlValidationOptions, TqlFieldType, TqlCompletionItem, TqlCompletionKind, TqlCompletionContext, TqlCompletionOptions, TqlFieldSchema, TqlSnippet, TqlInputProps, TqlInputEmits, TqlInputExpose } from './types';
|
|
10
|
+
export { TqlTokenizer, tokenizeTql, tokenizeTqlNoWhitespace, findTokenAtPosition, findTokensInRange, findTokenBefore, findPreviousToken, COMPARISON_OPERATORS, SYMBOL_OPERATORS, NEGATION_SYMBOL_OPERATORS, LOGICAL_OPERATORS, COLLECTION_OPERATORS, MUTATORS, AGGREGATION_FUNCTIONS, TYPE_HINTS, VISUALIZATION_TYPES, KEYWORDS } from './tokenizer';
|
|
11
|
+
export { TqlParser, parseTql, isValidTql, getTqlErrors, formatParseError } from './parser';
|
|
12
|
+
export { TqlValidator, validateTql, isValidTqlSyntax, getTqlIssues, formatValidationResult } from './validator';
|
|
13
|
+
export { getCompletions, getWordAtPosition, getCompletionTriggerCharacters, shouldTriggerCompletion } from './completionProvider';
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { TqlParseError, TqlParseResult } from './types';
|
|
2
|
+
|
|
3
|
+
export declare class TqlParser {
|
|
4
|
+
private tokens;
|
|
5
|
+
private allTokens;
|
|
6
|
+
private current;
|
|
7
|
+
private errors;
|
|
8
|
+
private warnings;
|
|
9
|
+
private depth;
|
|
10
|
+
constructor(input: string);
|
|
11
|
+
/**
|
|
12
|
+
* Parse the input and return the result
|
|
13
|
+
*/
|
|
14
|
+
parse(): TqlParseResult;
|
|
15
|
+
/**
|
|
16
|
+
* Parse a full expression (lowest precedence)
|
|
17
|
+
*/
|
|
18
|
+
private parseExpression;
|
|
19
|
+
/**
|
|
20
|
+
* Parse OR expressions
|
|
21
|
+
*/
|
|
22
|
+
private parseOr;
|
|
23
|
+
/**
|
|
24
|
+
* Parse AND expressions
|
|
25
|
+
*/
|
|
26
|
+
private parseAnd;
|
|
27
|
+
/**
|
|
28
|
+
* Parse NOT expressions
|
|
29
|
+
*/
|
|
30
|
+
private parseUnary;
|
|
31
|
+
/**
|
|
32
|
+
* Parse primary expressions (comparisons, groups, literals)
|
|
33
|
+
*/
|
|
34
|
+
private parsePrimary;
|
|
35
|
+
/**
|
|
36
|
+
* Parse a grouped expression
|
|
37
|
+
*/
|
|
38
|
+
private parseGroup;
|
|
39
|
+
/**
|
|
40
|
+
* Parse a comparison expression (field [::type] [| mutator]* operator value)
|
|
41
|
+
* Supports:
|
|
42
|
+
* - Optional type hint: field::string eq "value"
|
|
43
|
+
* - Optional mutator chain: field | lowercase | trim eq "value"
|
|
44
|
+
*/
|
|
45
|
+
private parseComparison;
|
|
46
|
+
/**
|
|
47
|
+
* Parse a value (string, number, boolean, null)
|
|
48
|
+
*/
|
|
49
|
+
private parseValue;
|
|
50
|
+
/**
|
|
51
|
+
* Parse a list of values for 'in' operator
|
|
52
|
+
*/
|
|
53
|
+
private parseValueList;
|
|
54
|
+
private createFieldReference;
|
|
55
|
+
private normalizeOperator;
|
|
56
|
+
private isAtEnd;
|
|
57
|
+
private peek;
|
|
58
|
+
private previous;
|
|
59
|
+
private advance;
|
|
60
|
+
private check;
|
|
61
|
+
private match;
|
|
62
|
+
private matchLogical;
|
|
63
|
+
private addError;
|
|
64
|
+
private addWarning;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Parse a TQL query string
|
|
68
|
+
*/
|
|
69
|
+
export declare function parseTql(query: string): TqlParseResult;
|
|
70
|
+
/**
|
|
71
|
+
* Check if a TQL query is syntactically valid
|
|
72
|
+
*/
|
|
73
|
+
export declare function isValidTql(query: string): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Get parse errors for a TQL query
|
|
76
|
+
*/
|
|
77
|
+
export declare function getTqlErrors(query: string): TqlParseError[];
|
|
78
|
+
/**
|
|
79
|
+
* Format a parse error for display
|
|
80
|
+
*/
|
|
81
|
+
export declare function formatParseError(error: TqlParseError): string;
|
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
var c = Object.defineProperty;
|
|
2
|
+
var p = (r, e, t) => e in r ? c(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
|
|
3
|
+
var o = (r, e, t) => p(r, typeof e != "symbol" ? e + "" : e, t);
|
|
4
|
+
import { tokenizeTql as d, COMPARISON_OPERATORS as v } from "./tokenizer.js";
|
|
5
|
+
const k = 50;
|
|
6
|
+
class f {
|
|
7
|
+
constructor(e) {
|
|
8
|
+
o(this, "tokens");
|
|
9
|
+
o(this, "allTokens");
|
|
10
|
+
// Including whitespace for highlighting
|
|
11
|
+
o(this, "current");
|
|
12
|
+
o(this, "errors");
|
|
13
|
+
o(this, "warnings");
|
|
14
|
+
o(this, "depth");
|
|
15
|
+
this.allTokens = d(e), this.tokens = this.allTokens.filter((t) => t.type !== "whitespace"), this.current = 0, this.errors = [], this.warnings = [], this.depth = 0;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Parse the input and return the result
|
|
19
|
+
*/
|
|
20
|
+
parse() {
|
|
21
|
+
if (this.current = 0, this.errors = [], this.warnings = [], this.depth = 0, this.tokens.length === 0)
|
|
22
|
+
return {
|
|
23
|
+
ast: null,
|
|
24
|
+
tokens: this.allTokens,
|
|
25
|
+
errors: [],
|
|
26
|
+
warnings: [],
|
|
27
|
+
success: !0
|
|
28
|
+
};
|
|
29
|
+
try {
|
|
30
|
+
const e = this.parseExpression();
|
|
31
|
+
if (!this.isAtEnd()) {
|
|
32
|
+
const t = this.peek();
|
|
33
|
+
this.addError(
|
|
34
|
+
`Unexpected token '${t.value}'`,
|
|
35
|
+
t,
|
|
36
|
+
"UNEXPECTED_TOKEN",
|
|
37
|
+
"Check for missing operators or extra characters"
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
ast: e,
|
|
42
|
+
tokens: this.allTokens,
|
|
43
|
+
errors: this.errors,
|
|
44
|
+
warnings: this.warnings,
|
|
45
|
+
success: this.errors.length === 0
|
|
46
|
+
};
|
|
47
|
+
} catch {
|
|
48
|
+
return {
|
|
49
|
+
ast: null,
|
|
50
|
+
tokens: this.allTokens,
|
|
51
|
+
errors: this.errors,
|
|
52
|
+
warnings: this.warnings,
|
|
53
|
+
success: !1
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// Expression Parsing (Precedence: OR < AND < NOT < Comparison)
|
|
59
|
+
// ============================================================================
|
|
60
|
+
/**
|
|
61
|
+
* Parse a full expression (lowest precedence)
|
|
62
|
+
*/
|
|
63
|
+
parseExpression() {
|
|
64
|
+
if (this.depth++, this.depth > k)
|
|
65
|
+
throw new Error("Maximum query depth exceeded");
|
|
66
|
+
try {
|
|
67
|
+
return this.parseOr();
|
|
68
|
+
} finally {
|
|
69
|
+
this.depth--;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Parse OR expressions
|
|
74
|
+
*/
|
|
75
|
+
parseOr() {
|
|
76
|
+
let e = this.parseAnd();
|
|
77
|
+
for (; this.matchLogical("OR"); ) {
|
|
78
|
+
const t = this.parseAnd();
|
|
79
|
+
e = {
|
|
80
|
+
type: "binary",
|
|
81
|
+
operator: "OR",
|
|
82
|
+
left: e,
|
|
83
|
+
right: t,
|
|
84
|
+
start: e.start,
|
|
85
|
+
end: t.end
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return e;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Parse AND expressions
|
|
92
|
+
*/
|
|
93
|
+
parseAnd() {
|
|
94
|
+
let e = this.parseUnary();
|
|
95
|
+
for (; this.matchLogical("AND"); ) {
|
|
96
|
+
const t = this.parseUnary();
|
|
97
|
+
e = {
|
|
98
|
+
type: "binary",
|
|
99
|
+
operator: "AND",
|
|
100
|
+
left: e,
|
|
101
|
+
right: t,
|
|
102
|
+
start: e.start,
|
|
103
|
+
end: t.end
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return e;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Parse NOT expressions
|
|
110
|
+
*/
|
|
111
|
+
parseUnary() {
|
|
112
|
+
if (this.matchLogical("NOT")) {
|
|
113
|
+
const e = this.previous(), t = this.parseUnary();
|
|
114
|
+
return {
|
|
115
|
+
type: "unary",
|
|
116
|
+
operator: "NOT",
|
|
117
|
+
operand: t,
|
|
118
|
+
start: e.start,
|
|
119
|
+
end: t.end
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return this.parsePrimary();
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Parse primary expressions (comparisons, groups, literals)
|
|
126
|
+
*/
|
|
127
|
+
parsePrimary() {
|
|
128
|
+
if (this.check("paren") && this.peek().value === "(")
|
|
129
|
+
return this.parseGroup();
|
|
130
|
+
if (this.check("field") || this.check("aggregation") || this.check("mutator") || this.check("typeHint"))
|
|
131
|
+
return this.parseComparison();
|
|
132
|
+
const e = this.peek();
|
|
133
|
+
if (this.addError(
|
|
134
|
+
`Expected field name or '(' but found '${e.value}'`,
|
|
135
|
+
e,
|
|
136
|
+
"UNEXPECTED_TOKEN",
|
|
137
|
+
"Start with a field name like 'hostname' or 'status'"
|
|
138
|
+
), this.advance(), !this.isAtEnd())
|
|
139
|
+
return this.parsePrimary();
|
|
140
|
+
throw new Error("Parser recovery failed");
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Parse a grouped expression
|
|
144
|
+
*/
|
|
145
|
+
parseGroup() {
|
|
146
|
+
const e = this.advance(), t = this.parseExpression();
|
|
147
|
+
return !this.check("paren") || this.peek().value !== ")" ? this.addError(
|
|
148
|
+
"Missing closing parenthesis ')'",
|
|
149
|
+
this.isAtEnd() ? this.previous() : this.peek(),
|
|
150
|
+
"UNBALANCED_PARENS",
|
|
151
|
+
"Add a closing ')' to match the opening '('"
|
|
152
|
+
) : this.advance(), {
|
|
153
|
+
type: "group",
|
|
154
|
+
expression: t,
|
|
155
|
+
start: e.start,
|
|
156
|
+
end: this.previous().end
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Parse a comparison expression (field [::type] [| mutator]* operator value)
|
|
161
|
+
* Supports:
|
|
162
|
+
* - Optional type hint: field::string eq "value"
|
|
163
|
+
* - Optional mutator chain: field | lowercase | trim eq "value"
|
|
164
|
+
*/
|
|
165
|
+
parseComparison() {
|
|
166
|
+
const e = this.advance();
|
|
167
|
+
let t = this.createFieldReference(e);
|
|
168
|
+
if (this.check("doubleColon"))
|
|
169
|
+
if (this.advance(), this.check("typeHint") || this.check("field")) {
|
|
170
|
+
const s = this.advance();
|
|
171
|
+
t = {
|
|
172
|
+
...t,
|
|
173
|
+
typeHint: s.value.toLowerCase(),
|
|
174
|
+
end: s.end
|
|
175
|
+
};
|
|
176
|
+
} else {
|
|
177
|
+
const s = this.isAtEnd() ? this.previous() : this.peek();
|
|
178
|
+
this.addWarning(
|
|
179
|
+
"Expected type hint after '::'",
|
|
180
|
+
s,
|
|
181
|
+
"Valid types: string, int, float, date, bool, array"
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
const i = [];
|
|
185
|
+
for (; this.check("pipe"); )
|
|
186
|
+
if (this.advance(), this.check("mutator")) {
|
|
187
|
+
const s = this.advance();
|
|
188
|
+
i.push(s.value.toLowerCase());
|
|
189
|
+
} else if (this.check("field")) {
|
|
190
|
+
const s = this.advance();
|
|
191
|
+
i.push(s.value.toLowerCase()), this.addWarning(`Unknown mutator '${s.value}'`, s, "Check the mutator name spelling");
|
|
192
|
+
} else {
|
|
193
|
+
const s = this.isAtEnd() ? this.previous() : this.peek();
|
|
194
|
+
this.addError(
|
|
195
|
+
"Expected mutator name after '|'",
|
|
196
|
+
s,
|
|
197
|
+
"MISSING_MUTATOR",
|
|
198
|
+
"Add a mutator like 'lowercase', 'uppercase', 'trim', etc."
|
|
199
|
+
);
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
if (i.length > 0 && (t = {
|
|
203
|
+
...t,
|
|
204
|
+
mutators: i,
|
|
205
|
+
end: this.previous().end
|
|
206
|
+
}), !this.check("operator")) {
|
|
207
|
+
const s = this.isAtEnd() ? this.previous() : this.peek(), h = i.length > 0 ? `'${e.value}' with mutator` : `'${e.value}'`;
|
|
208
|
+
return this.addError(
|
|
209
|
+
`Expected operator after field ${h}`,
|
|
210
|
+
s,
|
|
211
|
+
"MISSING_OPERATOR",
|
|
212
|
+
"Add an operator like 'eq', 'contains', '==', etc."
|
|
213
|
+
), {
|
|
214
|
+
type: "comparison",
|
|
215
|
+
field: t,
|
|
216
|
+
operator: "exists",
|
|
217
|
+
value: {
|
|
218
|
+
type: "literal",
|
|
219
|
+
valueType: "boolean",
|
|
220
|
+
value: !0,
|
|
221
|
+
start: t.end,
|
|
222
|
+
end: t.end
|
|
223
|
+
},
|
|
224
|
+
start: t.start,
|
|
225
|
+
end: t.end
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
const n = this.advance(), a = this.normalizeOperator(n.value);
|
|
229
|
+
if (a === "exists" || a === "not_exists")
|
|
230
|
+
return {
|
|
231
|
+
type: "comparison",
|
|
232
|
+
field: t,
|
|
233
|
+
operator: a,
|
|
234
|
+
value: {
|
|
235
|
+
type: "literal",
|
|
236
|
+
valueType: "boolean",
|
|
237
|
+
value: !0,
|
|
238
|
+
start: n.end,
|
|
239
|
+
end: n.end
|
|
240
|
+
},
|
|
241
|
+
start: t.start,
|
|
242
|
+
end: n.end
|
|
243
|
+
};
|
|
244
|
+
if ((a === "is" || a === "is_not") && this.check("null")) {
|
|
245
|
+
const s = this.advance();
|
|
246
|
+
return {
|
|
247
|
+
type: "comparison",
|
|
248
|
+
field: t,
|
|
249
|
+
operator: a,
|
|
250
|
+
value: {
|
|
251
|
+
type: "literal",
|
|
252
|
+
valueType: "null",
|
|
253
|
+
value: null,
|
|
254
|
+
start: s.start,
|
|
255
|
+
end: s.end
|
|
256
|
+
},
|
|
257
|
+
start: t.start,
|
|
258
|
+
end: s.end
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
if (a === "in" || a === "not_in") {
|
|
262
|
+
const s = this.parseValueList();
|
|
263
|
+
return {
|
|
264
|
+
type: "comparison",
|
|
265
|
+
field: t,
|
|
266
|
+
operator: a,
|
|
267
|
+
value: s,
|
|
268
|
+
start: t.start,
|
|
269
|
+
end: this.previous().end
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
if (a === "between" || a === "not_between") {
|
|
273
|
+
const s = this.parseValue();
|
|
274
|
+
this.matchLogical("AND") || this.addError(
|
|
275
|
+
"Expected 'AND' between values in 'between' operator",
|
|
276
|
+
this.isAtEnd() ? this.previous() : this.peek(),
|
|
277
|
+
"INVALID_SYNTAX",
|
|
278
|
+
"Use format: field between value1 AND value2"
|
|
279
|
+
);
|
|
280
|
+
const h = this.parseValue();
|
|
281
|
+
return {
|
|
282
|
+
type: "comparison",
|
|
283
|
+
field: t,
|
|
284
|
+
operator: a,
|
|
285
|
+
value: [s, h],
|
|
286
|
+
start: t.start,
|
|
287
|
+
end: h.end
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
if (this.isAtEnd())
|
|
291
|
+
return this.addError(
|
|
292
|
+
`Expected a value after operator '${a}'`,
|
|
293
|
+
n,
|
|
294
|
+
"MISSING_VALUE",
|
|
295
|
+
'Add a value like "text", 123, or true'
|
|
296
|
+
), {
|
|
297
|
+
type: "comparison",
|
|
298
|
+
field: t,
|
|
299
|
+
operator: a,
|
|
300
|
+
value: {
|
|
301
|
+
type: "literal",
|
|
302
|
+
valueType: "string",
|
|
303
|
+
value: "",
|
|
304
|
+
start: n.end,
|
|
305
|
+
end: n.end
|
|
306
|
+
},
|
|
307
|
+
start: t.start,
|
|
308
|
+
end: n.end
|
|
309
|
+
};
|
|
310
|
+
const l = this.parseValue();
|
|
311
|
+
return {
|
|
312
|
+
type: "comparison",
|
|
313
|
+
field: t,
|
|
314
|
+
operator: a,
|
|
315
|
+
value: l,
|
|
316
|
+
start: t.start,
|
|
317
|
+
end: l.end
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Parse a value (string, number, boolean, null)
|
|
322
|
+
*/
|
|
323
|
+
parseValue() {
|
|
324
|
+
if (this.isAtEnd()) {
|
|
325
|
+
const t = this.previous();
|
|
326
|
+
return this.addError(
|
|
327
|
+
"Expected a value",
|
|
328
|
+
t,
|
|
329
|
+
"MISSING_VALUE",
|
|
330
|
+
'Add a value like "text", 123, or true'
|
|
331
|
+
), {
|
|
332
|
+
type: "literal",
|
|
333
|
+
valueType: "string",
|
|
334
|
+
value: "",
|
|
335
|
+
start: t.end,
|
|
336
|
+
end: t.end
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
const e = this.peek();
|
|
340
|
+
return this.check("string") ? (this.advance(), {
|
|
341
|
+
type: "literal",
|
|
342
|
+
valueType: "string",
|
|
343
|
+
value: e.value,
|
|
344
|
+
start: e.start,
|
|
345
|
+
end: e.end
|
|
346
|
+
}) : this.check("number") ? (this.advance(), {
|
|
347
|
+
type: "literal",
|
|
348
|
+
valueType: "number",
|
|
349
|
+
value: e.value.includes(".") ? parseFloat(e.value) : parseInt(e.value, 10),
|
|
350
|
+
start: e.start,
|
|
351
|
+
end: e.end
|
|
352
|
+
}) : this.check("boolean") ? (this.advance(), {
|
|
353
|
+
type: "literal",
|
|
354
|
+
valueType: "boolean",
|
|
355
|
+
value: e.value.toLowerCase() === "true",
|
|
356
|
+
start: e.start,
|
|
357
|
+
end: e.end
|
|
358
|
+
}) : this.check("null") ? (this.advance(), {
|
|
359
|
+
type: "literal",
|
|
360
|
+
valueType: "null",
|
|
361
|
+
value: null,
|
|
362
|
+
start: e.start,
|
|
363
|
+
end: e.end
|
|
364
|
+
}) : this.check("field") ? (this.advance(), this.addWarning(`Unquoted value '${e.value}' - consider using quotes`, e, "Consider wrapping string values in quotes"), {
|
|
365
|
+
type: "literal",
|
|
366
|
+
valueType: "string",
|
|
367
|
+
value: e.value,
|
|
368
|
+
start: e.start,
|
|
369
|
+
end: e.end
|
|
370
|
+
}) : (this.addError(
|
|
371
|
+
"Expected a value (string, number, or boolean)",
|
|
372
|
+
e,
|
|
373
|
+
"MISSING_VALUE",
|
|
374
|
+
'Add a value like "text", 123, or true'
|
|
375
|
+
), {
|
|
376
|
+
type: "literal",
|
|
377
|
+
valueType: "string",
|
|
378
|
+
value: "",
|
|
379
|
+
start: e.start,
|
|
380
|
+
end: e.start
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Parse a list of values for 'in' operator
|
|
385
|
+
*/
|
|
386
|
+
parseValueList() {
|
|
387
|
+
const e = [];
|
|
388
|
+
this.check("bracket") && this.peek().value === "[" ? this.advance() : this.check("paren") && this.peek().value === "(" && this.advance();
|
|
389
|
+
do {
|
|
390
|
+
if (this.check("bracket") && this.peek().value === "]" || this.check("paren") && this.peek().value === ")") break;
|
|
391
|
+
e.push(this.parseValue());
|
|
392
|
+
} while (this.match("comma"));
|
|
393
|
+
return this.check("bracket") && this.peek().value === "]" ? this.advance() : this.check("paren") && this.peek().value === ")" && this.advance(), e;
|
|
394
|
+
}
|
|
395
|
+
// ============================================================================
|
|
396
|
+
// Helper Methods
|
|
397
|
+
// ============================================================================
|
|
398
|
+
createFieldReference(e) {
|
|
399
|
+
return {
|
|
400
|
+
type: "field",
|
|
401
|
+
name: e.value,
|
|
402
|
+
path: e.value.split("."),
|
|
403
|
+
start: e.start,
|
|
404
|
+
end: e.end
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
normalizeOperator(e) {
|
|
408
|
+
const t = e.toLowerCase(), i = {
|
|
409
|
+
"==": "eq",
|
|
410
|
+
"=": "eq",
|
|
411
|
+
"!=": "ne",
|
|
412
|
+
">": "gt",
|
|
413
|
+
">=": "gte",
|
|
414
|
+
"<": "lt",
|
|
415
|
+
"<=": "lte"
|
|
416
|
+
};
|
|
417
|
+
if (e in i)
|
|
418
|
+
return i[e];
|
|
419
|
+
const n = {
|
|
420
|
+
"!in": "not_in",
|
|
421
|
+
"!contains": "not_contains",
|
|
422
|
+
"!startswith": "not_startswith",
|
|
423
|
+
"!endswith": "not_endswith",
|
|
424
|
+
"!regexp": "not_regexp",
|
|
425
|
+
"!matches": "not_matches",
|
|
426
|
+
"!between": "not_between",
|
|
427
|
+
"!exists": "not_exists",
|
|
428
|
+
"!is": "is_not",
|
|
429
|
+
"!cidr": "not_cidr",
|
|
430
|
+
"!any": "not_any",
|
|
431
|
+
"!all": "not_all",
|
|
432
|
+
"!none": "not_none"
|
|
433
|
+
};
|
|
434
|
+
if (t in n)
|
|
435
|
+
return n[t];
|
|
436
|
+
const a = {
|
|
437
|
+
regex: "regexp",
|
|
438
|
+
starts_with: "startswith",
|
|
439
|
+
ends_with: "endswith"
|
|
440
|
+
};
|
|
441
|
+
return t in a ? a[t] : (v.includes(t), t);
|
|
442
|
+
}
|
|
443
|
+
isAtEnd() {
|
|
444
|
+
return this.current >= this.tokens.length;
|
|
445
|
+
}
|
|
446
|
+
peek() {
|
|
447
|
+
return this.tokens[this.current];
|
|
448
|
+
}
|
|
449
|
+
previous() {
|
|
450
|
+
return this.tokens[this.current - 1];
|
|
451
|
+
}
|
|
452
|
+
advance() {
|
|
453
|
+
return this.isAtEnd() || this.current++, this.previous();
|
|
454
|
+
}
|
|
455
|
+
check(e) {
|
|
456
|
+
return this.isAtEnd() ? !1 : this.peek().type === e;
|
|
457
|
+
}
|
|
458
|
+
match(...e) {
|
|
459
|
+
for (const t of e)
|
|
460
|
+
if (this.check(t))
|
|
461
|
+
return this.advance(), !0;
|
|
462
|
+
return !1;
|
|
463
|
+
}
|
|
464
|
+
matchLogical(e) {
|
|
465
|
+
return this.check("logicalOp") && this.peek().value.toUpperCase() === e ? (this.advance(), !0) : !1;
|
|
466
|
+
}
|
|
467
|
+
addError(e, t, i, n) {
|
|
468
|
+
this.errors.push({
|
|
469
|
+
message: e,
|
|
470
|
+
start: t.start,
|
|
471
|
+
end: t.end,
|
|
472
|
+
line: t.line,
|
|
473
|
+
column: t.column,
|
|
474
|
+
severity: "error",
|
|
475
|
+
code: i,
|
|
476
|
+
suggestion: n
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
addWarning(e, t, i) {
|
|
480
|
+
this.warnings.push({
|
|
481
|
+
message: e,
|
|
482
|
+
start: t.start,
|
|
483
|
+
end: t.end,
|
|
484
|
+
line: t.line,
|
|
485
|
+
column: t.column,
|
|
486
|
+
severity: "warning",
|
|
487
|
+
code: "TYPE_MISMATCH",
|
|
488
|
+
suggestion: i
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
function u(r) {
|
|
493
|
+
return new f(r).parse();
|
|
494
|
+
}
|
|
495
|
+
function g(r) {
|
|
496
|
+
return u(r).success;
|
|
497
|
+
}
|
|
498
|
+
function E(r) {
|
|
499
|
+
return u(r).errors;
|
|
500
|
+
}
|
|
501
|
+
function w(r) {
|
|
502
|
+
let e = `Error at line ${r.line}, column ${r.column}: ${r.message}`;
|
|
503
|
+
return r.suggestion && (e += ` (${r.suggestion})`), e;
|
|
504
|
+
}
|
|
505
|
+
export {
|
|
506
|
+
f as TqlParser,
|
|
507
|
+
w as formatParseError,
|
|
508
|
+
E as getTqlErrors,
|
|
509
|
+
g as isValidTql,
|
|
510
|
+
u as parseTql
|
|
511
|
+
};
|