search-input-query-parser 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.
Files changed (58) hide show
  1. package/dist/cjs/first-pass-parser.js +77 -0
  2. package/dist/cjs/lexer.js +322 -0
  3. package/dist/cjs/parse-in-values.js +65 -0
  4. package/dist/cjs/parse-primary.js +154 -0
  5. package/dist/cjs/parse-range-expression.js +174 -0
  6. package/dist/cjs/parser.js +85 -0
  7. package/dist/cjs/search-query-to-sql.js +346 -0
  8. package/dist/cjs/transform-to-expression.js +130 -0
  9. package/dist/cjs/validate-expression-fields.js +244 -0
  10. package/dist/cjs/validate-in-expression.js +33 -0
  11. package/dist/cjs/validate-string.js +65 -0
  12. package/dist/cjs/validate-wildcard.js +40 -0
  13. package/dist/cjs/validator.js +34 -0
  14. package/dist/esm/first-pass-parser.js +73 -0
  15. package/dist/esm/lexer.js +315 -0
  16. package/dist/esm/parse-in-values.js +61 -0
  17. package/dist/esm/parse-primary.js +147 -0
  18. package/dist/esm/parse-range-expression.js +170 -0
  19. package/dist/esm/parser.js +81 -0
  20. package/dist/esm/search-query-to-sql.js +341 -0
  21. package/dist/esm/transform-to-expression.js +126 -0
  22. package/dist/esm/validate-expression-fields.js +240 -0
  23. package/dist/esm/validate-in-expression.js +29 -0
  24. package/dist/esm/validate-string.js +61 -0
  25. package/dist/esm/validate-wildcard.js +36 -0
  26. package/dist/esm/validator.js +30 -0
  27. package/dist/types/first-pass-parser.d.ts +40 -0
  28. package/dist/types/lexer.d.ts +27 -0
  29. package/dist/types/parse-in-values.d.ts +3 -0
  30. package/dist/types/parse-primary.d.ts +6 -0
  31. package/dist/types/parse-range-expression.d.ts +2 -0
  32. package/dist/types/parser.d.ts +68 -0
  33. package/dist/types/search-query-to-sql.d.ts +18 -0
  34. package/dist/types/transform-to-expression.d.ts +3 -0
  35. package/dist/types/validate-expression-fields.d.ts +4 -0
  36. package/dist/types/validate-in-expression.d.ts +3 -0
  37. package/dist/types/validate-string.d.ts +3 -0
  38. package/dist/types/validate-wildcard.d.ts +3 -0
  39. package/dist/types/validator.d.ts +8 -0
  40. package/package.json +52 -0
  41. package/src/first-pass-parser.test.ts +441 -0
  42. package/src/first-pass-parser.ts +144 -0
  43. package/src/lexer.test.ts +439 -0
  44. package/src/lexer.ts +387 -0
  45. package/src/parse-in-values.ts +74 -0
  46. package/src/parse-primary.ts +179 -0
  47. package/src/parse-range-expression.ts +187 -0
  48. package/src/parser.test.ts +982 -0
  49. package/src/parser.ts +219 -0
  50. package/src/search-query-to-sql.test.ts +503 -0
  51. package/src/search-query-to-sql.ts +506 -0
  52. package/src/transform-to-expression.ts +153 -0
  53. package/src/validate-expression-fields.ts +296 -0
  54. package/src/validate-in-expression.ts +36 -0
  55. package/src/validate-string.ts +73 -0
  56. package/src/validate-wildcard.ts +45 -0
  57. package/src/validator.test.ts +192 -0
  58. package/src/validator.ts +53 -0
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseExpression = void 0;
4
+ const lexer_1 = require("./lexer");
5
+ const parse_primary_1 = require("./parse-primary");
6
+ const getOperatorPrecedence = (type) => type === lexer_1.TokenType.AND ? 2 : type === lexer_1.TokenType.OR ? 1 : 0;
7
+ const parseExpression = (stream, minPrecedence = 0) => {
8
+ const token = (0, lexer_1.currentToken)(stream);
9
+ if (token.type === lexer_1.TokenType.STRING && token.value === "*") {
10
+ return {
11
+ result: {
12
+ type: "WILDCARD",
13
+ prefix: "",
14
+ quoted: false,
15
+ position: token.position,
16
+ length: token.length,
17
+ },
18
+ stream: (0, lexer_1.advanceStream)(stream),
19
+ };
20
+ }
21
+ let result = (0, parse_primary_1.parsePrimary)(stream);
22
+ while (true) {
23
+ const token = (0, lexer_1.currentToken)(result.stream);
24
+ if (token.type === lexer_1.TokenType.EOF)
25
+ break;
26
+ if (token.type === lexer_1.TokenType.AND || token.type === lexer_1.TokenType.OR) {
27
+ const precedence = getOperatorPrecedence(token.type);
28
+ if (precedence < minPrecedence)
29
+ break;
30
+ const operator = token.type;
31
+ const nextStream = (0, lexer_1.advanceStream)(result.stream);
32
+ const nextToken = (0, lexer_1.currentToken)(nextStream);
33
+ if (nextToken.type === lexer_1.TokenType.EOF) {
34
+ throw {
35
+ message: `Unexpected token: ${token.value}`,
36
+ position: token.position,
37
+ length: token.length,
38
+ };
39
+ }
40
+ const right = (0, exports.parseExpression)(nextStream, precedence + 1);
41
+ result = {
42
+ result: {
43
+ type: operator,
44
+ left: result.result,
45
+ right: right.result,
46
+ position: token.position,
47
+ length: token.length,
48
+ },
49
+ stream: right.stream,
50
+ };
51
+ continue;
52
+ }
53
+ if (token.type === lexer_1.TokenType.STRING ||
54
+ token.type === lexer_1.TokenType.QUOTED_STRING ||
55
+ token.type === lexer_1.TokenType.LPAREN ||
56
+ token.type === lexer_1.TokenType.NOT) {
57
+ const precedence = getOperatorPrecedence(lexer_1.TokenType.AND);
58
+ if (precedence < minPrecedence)
59
+ break;
60
+ const right = (0, exports.parseExpression)(result.stream, precedence + 1);
61
+ result = {
62
+ result: {
63
+ type: lexer_1.TokenType.AND,
64
+ left: result.result,
65
+ right: right.result,
66
+ position: token.position,
67
+ length: token.length,
68
+ },
69
+ stream: right.stream,
70
+ };
71
+ continue;
72
+ }
73
+ break;
74
+ }
75
+ return result;
76
+ };
77
+ exports.parseExpression = parseExpression;
@@ -0,0 +1,322 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.tokenize = exports.advanceStream = exports.currentToken = exports.createStream = exports.TokenType = void 0;
4
+ // Token types and data structures
5
+ var TokenType;
6
+ (function (TokenType) {
7
+ TokenType["STRING"] = "STRING";
8
+ TokenType["QUOTED_STRING"] = "QUOTED_STRING";
9
+ TokenType["LPAREN"] = "LPAREN";
10
+ TokenType["RPAREN"] = "RPAREN";
11
+ TokenType["AND"] = "AND";
12
+ TokenType["OR"] = "OR";
13
+ TokenType["NOT"] = "NOT";
14
+ TokenType["EOF"] = "EOF";
15
+ TokenType["IN"] = "IN";
16
+ TokenType["COMMA"] = "COMMA";
17
+ TokenType["NUMBER"] = "NUMBER";
18
+ })(TokenType || (exports.TokenType = TokenType = {}));
19
+ // Tokenizer functions
20
+ const createStream = (tokens) => ({
21
+ tokens,
22
+ position: 0,
23
+ });
24
+ exports.createStream = createStream;
25
+ const currentToken = (stream) => stream.position < stream.tokens.length
26
+ ? stream.tokens[stream.position]
27
+ : { type: TokenType.EOF, value: "", position: stream.position, length: 0 };
28
+ exports.currentToken = currentToken;
29
+ const advanceStream = (stream) => ({
30
+ ...stream,
31
+ position: stream.position + 1,
32
+ });
33
+ exports.advanceStream = advanceStream;
34
+ const isSpecialChar = (char) => /[\s"():(),]/.test(char);
35
+ const isEscapeChar = (char) => char === "\\";
36
+ const isQuoteChar = (char) => char === '"';
37
+ const isWhitespace = (char) => /\s/.test(char);
38
+ const isWildcard = (char) => char === "*";
39
+ const readUntil = (input, start, predicate) => {
40
+ let result = "";
41
+ let pos = start;
42
+ let foundWildcard = false;
43
+ while (pos < input.length) {
44
+ const char = input[pos];
45
+ // Once we find a wildcard, include everything up to the next whitespace or special char
46
+ if (isWildcard(char)) {
47
+ foundWildcard = true;
48
+ }
49
+ if (isWhitespace(char) || (!foundWildcard && !predicate(char))) {
50
+ break;
51
+ }
52
+ result += char;
53
+ pos++;
54
+ }
55
+ return result;
56
+ };
57
+ const tokenizeQuotedString = (input, position) => {
58
+ let value = '"'; // Start with opening quote
59
+ let pos = position + 1; // Skip opening quote in input processing
60
+ let length = 2; // Start with 2 for the quotes
61
+ while (pos < input.length) {
62
+ const char = input[pos];
63
+ if (isQuoteChar(char)) {
64
+ // Add closing quote
65
+ value += '"';
66
+ // Move past closing quote
67
+ pos++;
68
+ // Read any wildcards after the closing quote
69
+ let wildcards = "";
70
+ while (pos < input.length && isWildcard(input[pos])) {
71
+ wildcards += "*";
72
+ pos++;
73
+ length++;
74
+ }
75
+ if (wildcards) {
76
+ value += wildcards;
77
+ }
78
+ return [
79
+ {
80
+ type: TokenType.QUOTED_STRING,
81
+ value,
82
+ position,
83
+ length,
84
+ },
85
+ pos,
86
+ ];
87
+ }
88
+ if (isEscapeChar(char) && pos + 1 < input.length) {
89
+ value += input[pos] + input[pos + 1]; // Include escape char and escaped char
90
+ length += 2;
91
+ pos += 2;
92
+ }
93
+ else {
94
+ value += char;
95
+ length++;
96
+ pos++;
97
+ }
98
+ }
99
+ throw { message: "Unterminated quoted string", position, length };
100
+ };
101
+ const tokenizeString = (input, position) => {
102
+ let pos = position;
103
+ if (/^-?\d+(\.\d+)?/.test(input.slice(pos))) {
104
+ const match = input.slice(pos).match(/^-?\d+(\.\d+)?/);
105
+ if (match) {
106
+ const numValue = match[0];
107
+ return [
108
+ {
109
+ type: TokenType.NUMBER,
110
+ value: numValue,
111
+ position: pos,
112
+ length: numValue.length,
113
+ },
114
+ pos + numValue.length,
115
+ ];
116
+ }
117
+ }
118
+ // Read until we hit a special character, whitespace, or colon
119
+ const fieldPart = readUntil(input, pos, (char) => !isWhitespace(char) && char !== ":" && !isSpecialChar(char));
120
+ pos += fieldPart.length;
121
+ // Check if this is a field:value pattern
122
+ if (pos < input.length && input[pos] === ":") {
123
+ // Skip colon
124
+ pos++;
125
+ // Handle quoted values
126
+ if (pos < input.length && input[pos] === '"') {
127
+ const [quotedToken, newPos] = tokenizeQuotedString(input, pos);
128
+ return [
129
+ {
130
+ type: TokenType.QUOTED_STRING,
131
+ value: `${fieldPart}:${quotedToken.value}`,
132
+ position: position,
133
+ length: newPos - position,
134
+ },
135
+ newPos,
136
+ ];
137
+ }
138
+ // Handle unquoted values
139
+ const valuePart = readUntil(input, pos, (char) => !isWhitespace(char) && !isSpecialChar(char));
140
+ pos += valuePart.length;
141
+ // Check for wildcard after the value
142
+ if (pos < input.length && isWildcard(input[pos])) {
143
+ return [
144
+ {
145
+ type: TokenType.STRING,
146
+ value: `${fieldPart}:${valuePart}*`,
147
+ position,
148
+ length: pos + 1 - position,
149
+ },
150
+ pos + 1,
151
+ ];
152
+ }
153
+ return [
154
+ {
155
+ type: TokenType.STRING,
156
+ value: `${fieldPart}:${valuePart}`,
157
+ position,
158
+ length: pos - position,
159
+ },
160
+ pos,
161
+ ];
162
+ }
163
+ // Handle logical operators (case-insensitive)
164
+ const upperFieldPart = fieldPart.toUpperCase();
165
+ if (upperFieldPart === "AND" ||
166
+ upperFieldPart === "OR" ||
167
+ upperFieldPart === "NOT") {
168
+ return [
169
+ {
170
+ type: upperFieldPart === "AND"
171
+ ? TokenType.AND
172
+ : upperFieldPart === "OR"
173
+ ? TokenType.OR
174
+ : TokenType.NOT,
175
+ value: upperFieldPart,
176
+ position,
177
+ length: fieldPart.length,
178
+ },
179
+ pos,
180
+ ];
181
+ }
182
+ // Handle IN operator (case-insensitive)
183
+ if (upperFieldPart === "IN") {
184
+ return [
185
+ {
186
+ type: TokenType.IN,
187
+ value: "IN",
188
+ position,
189
+ length: fieldPart.length,
190
+ },
191
+ pos,
192
+ ];
193
+ }
194
+ // Read any wildcards after the string
195
+ let wildcards = "";
196
+ while (pos < input.length && isWildcard(input[pos])) {
197
+ wildcards += "*";
198
+ pos++;
199
+ }
200
+ if (wildcards) {
201
+ return [
202
+ {
203
+ type: TokenType.STRING,
204
+ value: fieldPart + wildcards,
205
+ position,
206
+ length: pos - position,
207
+ },
208
+ pos,
209
+ ];
210
+ }
211
+ // Handle plain strings
212
+ return [
213
+ {
214
+ type: TokenType.STRING,
215
+ value: fieldPart,
216
+ position,
217
+ length: fieldPart.length,
218
+ },
219
+ pos,
220
+ ];
221
+ };
222
+ const tokenize = (input) => {
223
+ const tokens = [];
224
+ let position = 0;
225
+ while (position < input.length) {
226
+ const char = input[position];
227
+ if (isWhitespace(char)) {
228
+ position++;
229
+ continue;
230
+ }
231
+ switch (char) {
232
+ case "-": {
233
+ // Check if this is the start of a term/expression
234
+ if (position === 0 || isWhitespace(input[position - 1])) {
235
+ tokens.push({
236
+ type: TokenType.NOT,
237
+ value: "NOT",
238
+ position,
239
+ length: 1,
240
+ });
241
+ position++;
242
+ }
243
+ else {
244
+ // If minus is not at start of term, treat it as part of the term
245
+ const [token, newPos] = tokenizeString(input, position);
246
+ tokens.push(token);
247
+ position = newPos;
248
+ }
249
+ break;
250
+ }
251
+ case '"': {
252
+ // Before tokenizing a quoted string, check if it's adjacent to a previous quoted string
253
+ if (tokens.length > 0) {
254
+ const prevToken = tokens[tokens.length - 1];
255
+ const prevEnd = prevToken.position + prevToken.length;
256
+ // If there's no whitespace between this quote and the previous token's end
257
+ if (position === prevEnd &&
258
+ prevToken.type !== TokenType.COMMA &&
259
+ (prevToken.type === TokenType.QUOTED_STRING ||
260
+ prevToken.type === TokenType.STRING)) {
261
+ throw {
262
+ message: "Invalid syntax: Missing operator or whitespace between terms",
263
+ position: position,
264
+ length: 1,
265
+ };
266
+ }
267
+ }
268
+ const [token, newPos] = tokenizeQuotedString(input, position);
269
+ // After tokenizing, check if the next character is not a whitespace or special character
270
+ if (newPos < input.length &&
271
+ !isWhitespace(input[newPos]) &&
272
+ !isSpecialChar(input[newPos])) {
273
+ throw {
274
+ message: "Invalid syntax: Missing operator or whitespace between terms",
275
+ position: newPos,
276
+ length: 1,
277
+ };
278
+ }
279
+ tokens.push(token);
280
+ position = newPos;
281
+ break;
282
+ }
283
+ case "(": {
284
+ tokens.push({
285
+ type: TokenType.LPAREN,
286
+ value: "(",
287
+ position,
288
+ length: 1,
289
+ });
290
+ position++;
291
+ break;
292
+ }
293
+ case ")": {
294
+ tokens.push({
295
+ type: TokenType.RPAREN,
296
+ value: ")",
297
+ position,
298
+ length: 1,
299
+ });
300
+ position++;
301
+ break;
302
+ }
303
+ case ",": {
304
+ tokens.push({
305
+ type: TokenType.COMMA,
306
+ value: ",",
307
+ position,
308
+ length: 1,
309
+ });
310
+ position++;
311
+ break;
312
+ }
313
+ default: {
314
+ const [token, newPos] = tokenizeString(input, position);
315
+ tokens.push(token);
316
+ position = newPos;
317
+ }
318
+ }
319
+ }
320
+ return tokens;
321
+ };
322
+ exports.tokenize = tokenize;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseInValues = void 0;
4
+ const lexer_1 = require("./lexer");
5
+ const parseInValues = (stream, inValuePosition) => {
6
+ const values = [];
7
+ let currentStream = stream;
8
+ // Expect opening parenthesis
9
+ if ((0, lexer_1.currentToken)(currentStream).type !== lexer_1.TokenType.LPAREN) {
10
+ throw {
11
+ message: "Expected '(' after IN",
12
+ position: inValuePosition, // Use the position passed from the caller
13
+ length: 1,
14
+ };
15
+ }
16
+ currentStream = (0, lexer_1.advanceStream)(currentStream);
17
+ while (true) {
18
+ const token = (0, lexer_1.currentToken)(currentStream);
19
+ if (token.type === lexer_1.TokenType.RPAREN) {
20
+ if (values.length === 0) {
21
+ throw {
22
+ message: "IN operator requires at least one value",
23
+ position: token.position,
24
+ length: 1,
25
+ };
26
+ }
27
+ return {
28
+ result: values,
29
+ stream: (0, lexer_1.advanceStream)(currentStream),
30
+ };
31
+ }
32
+ if (token.type === lexer_1.TokenType.EOF ||
33
+ (token.type !== lexer_1.TokenType.STRING &&
34
+ token.type !== lexer_1.TokenType.QUOTED_STRING &&
35
+ token.type !== lexer_1.TokenType.NUMBER &&
36
+ token.type !== lexer_1.TokenType.COMMA)) {
37
+ throw {
38
+ message: "Expected ',' or ')' after IN value",
39
+ position: token.position,
40
+ length: 1,
41
+ };
42
+ }
43
+ if (token.type === lexer_1.TokenType.STRING ||
44
+ token.type === lexer_1.TokenType.QUOTED_STRING ||
45
+ token.type === lexer_1.TokenType.NUMBER) {
46
+ values.push(token.value);
47
+ currentStream = (0, lexer_1.advanceStream)(currentStream);
48
+ const nextToken = (0, lexer_1.currentToken)(currentStream);
49
+ if (nextToken.type === lexer_1.TokenType.COMMA) {
50
+ currentStream = (0, lexer_1.advanceStream)(currentStream);
51
+ continue;
52
+ }
53
+ if (nextToken.type === lexer_1.TokenType.RPAREN) {
54
+ continue;
55
+ }
56
+ throw {
57
+ message: "Expected ',' or ')' after IN value",
58
+ position: nextToken.position,
59
+ length: 1,
60
+ };
61
+ }
62
+ currentStream = (0, lexer_1.advanceStream)(currentStream);
63
+ }
64
+ };
65
+ exports.parseInValues = parseInValues;
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parsePrimary = exports.extractFieldValue = exports.isFieldValuePattern = exports.expectToken = void 0;
4
+ const first_pass_parser_1 = require("./first-pass-parser");
5
+ const parse_in_values_1 = require("./parse-in-values");
6
+ const lexer_1 = require("./lexer");
7
+ const expectToken = (stream, type, message) => {
8
+ const token = (0, lexer_1.currentToken)(stream);
9
+ if (token.type !== type) {
10
+ throw {
11
+ message: message ? message : `Expected ${type}`,
12
+ position: token.position,
13
+ length: token.length,
14
+ };
15
+ }
16
+ return (0, lexer_1.advanceStream)(stream);
17
+ };
18
+ exports.expectToken = expectToken;
19
+ // Helper to check if a string value represents a field:value pattern
20
+ const isFieldValuePattern = (value) => {
21
+ return value.includes(":");
22
+ };
23
+ exports.isFieldValuePattern = isFieldValuePattern;
24
+ // Helper to extract field and value from a field:value pattern
25
+ const extractFieldValue = (value) => {
26
+ const [field, ...valueParts] = value.split(":");
27
+ return [field, valueParts.join(":")];
28
+ };
29
+ exports.extractFieldValue = extractFieldValue;
30
+ const parsePrimary = (stream) => {
31
+ const token = (0, lexer_1.currentToken)(stream);
32
+ switch (token.type) {
33
+ case lexer_1.TokenType.NOT: {
34
+ const nextStream = (0, lexer_1.advanceStream)(stream);
35
+ const nextToken = (0, lexer_1.currentToken)(nextStream);
36
+ if (nextToken.type === lexer_1.TokenType.LPAREN) {
37
+ const afterLParen = (0, lexer_1.advanceStream)(nextStream);
38
+ const exprResult = (0, first_pass_parser_1.parseExpression)(afterLParen);
39
+ const finalStream = (0, exports.expectToken)(exprResult.stream, lexer_1.TokenType.RPAREN, "Expected ')'");
40
+ return {
41
+ result: {
42
+ type: "NOT",
43
+ expression: exprResult.result,
44
+ position: token.position,
45
+ length: token.length,
46
+ },
47
+ stream: finalStream,
48
+ };
49
+ }
50
+ const exprResult = (0, exports.parsePrimary)(nextStream);
51
+ return {
52
+ result: {
53
+ type: "NOT",
54
+ expression: exprResult.result,
55
+ position: token.position,
56
+ length: token.length,
57
+ },
58
+ stream: exprResult.stream,
59
+ };
60
+ }
61
+ case lexer_1.TokenType.LPAREN: {
62
+ const innerStream = (0, lexer_1.advanceStream)(stream);
63
+ const exprResult = (0, first_pass_parser_1.parseExpression)(innerStream);
64
+ const finalStream = (0, exports.expectToken)(exprResult.stream, lexer_1.TokenType.RPAREN, "Expected ')'");
65
+ return { result: exprResult.result, stream: finalStream };
66
+ }
67
+ case lexer_1.TokenType.STRING:
68
+ case lexer_1.TokenType.QUOTED_STRING: {
69
+ const { value } = token;
70
+ const isQuoted = token.type === lexer_1.TokenType.QUOTED_STRING;
71
+ // Check for field:IN pattern
72
+ if (value.includes(":")) {
73
+ const [field, remainder] = value.split(":");
74
+ if (remainder.toUpperCase() === "IN") {
75
+ const nextStream = (0, lexer_1.advanceStream)(stream);
76
+ const colonIndex = value.indexOf(":");
77
+ const inValuePosition = token.position + colonIndex + 2; // After field:IN
78
+ const inValuesResult = (0, parse_in_values_1.parseInValues)(nextStream, inValuePosition);
79
+ return {
80
+ result: {
81
+ type: "IN",
82
+ field,
83
+ values: inValuesResult.result,
84
+ position: token.position,
85
+ length: token.length + inValuesResult.stream.position - nextStream.position,
86
+ },
87
+ stream: inValuesResult.stream,
88
+ };
89
+ }
90
+ }
91
+ // Handle field:value patterns
92
+ if ((0, exports.isFieldValuePattern)(value)) {
93
+ const [field, rawValue] = (0, exports.extractFieldValue)(value);
94
+ // If it has a trailing wildcard
95
+ if (rawValue.endsWith("*")) {
96
+ return {
97
+ result: {
98
+ type: "WILDCARD",
99
+ prefix: `${field}:${rawValue.slice(0, -1)}`,
100
+ quoted: isQuoted,
101
+ position: token.position,
102
+ length: token.length,
103
+ },
104
+ stream: (0, lexer_1.advanceStream)(stream),
105
+ };
106
+ }
107
+ }
108
+ // Handle regular terms with wildcards
109
+ if (value.endsWith("*")) {
110
+ return {
111
+ result: {
112
+ type: "WILDCARD",
113
+ prefix: value.slice(0, -1),
114
+ quoted: isQuoted,
115
+ position: token.position,
116
+ length: token.length,
117
+ },
118
+ stream: (0, lexer_1.advanceStream)(stream),
119
+ };
120
+ }
121
+ // Regular string without wildcards
122
+ return {
123
+ result: {
124
+ type: "STRING",
125
+ value,
126
+ quoted: token.type === lexer_1.TokenType.QUOTED_STRING,
127
+ position: token.position,
128
+ length: token.length,
129
+ },
130
+ stream: (0, lexer_1.advanceStream)(stream),
131
+ };
132
+ }
133
+ case lexer_1.TokenType.AND:
134
+ case lexer_1.TokenType.OR:
135
+ throw {
136
+ message: `${token.value} is a reserved word`,
137
+ position: token.position,
138
+ length: token.length,
139
+ };
140
+ case lexer_1.TokenType.RPAREN:
141
+ throw {
142
+ message: 'Unexpected ")"',
143
+ position: token.position,
144
+ length: token.length,
145
+ };
146
+ default:
147
+ throw {
148
+ message: "Unexpected token",
149
+ position: token.position,
150
+ length: token.length,
151
+ };
152
+ }
153
+ };
154
+ exports.parsePrimary = parsePrimary;