Skip to content

Instantly share code, notes, and snippets.

@niemyjski
Created February 3, 2025 16:43
Show Gist options
  • Save niemyjski/d852115272cfb0a4515616384208ea16 to your computer and use it in GitHub Desktop.
Save niemyjski/d852115272cfb0a4515616384208ea16 to your computer and use it in GitHub Desktop.
peggyjs lucene parser - wip
import { parse } from './LuceneQueryParser';
export function getTerms(query: string): string[] {
const ast = parse(query);
if (!ast) {
return [];
}
const terms = new Set<string>();
const traverse = (node: null | object): void => {
if (!node || typeof node !== 'object') {
return;
}
if ('left' in node && 'right' in node) {
traverse(node.left as object);
traverse(node.right as object);
return;
}
if ('field' in node && typeof node.field === 'string') {
terms.add(node.field);
}
};
traverse(ast);
return Array.from(terms);
}
export function getTermValues(query: string): Array<{ term: string; value: string }> {
{
const ast = parse(query);
if (!ast) {
return [];
}
const termValues: { term: string; value: string }[] = [];
const buildRawQuery = (node: null | object): string => {
if (!node || typeof node !== 'object') {
return '';
}
if ('left' in node && 'right' in node) {
const leftQuery = buildRawQuery(node.left as object);
const rightQuery = buildRawQuery(node.right as object);
let query = 'operator' in node && node.operator ? `${leftQuery} ${node.operator} ${rightQuery}` : `${leftQuery} ${rightQuery}`;
if ('hasParens' in node && node.hasParens) {
query = `(${query})`;
}
if ('prefix' in node && node.prefix) {
query = node.prefix + query;
}
if ('isNegated' in node && node.isNegated) {
query = '-' + query;
}
if ('boost' in node && node.boost) {
query = query + `^${node.boost}`;
}
return query;
}
if ('field' in node && 'term' in node) {
let termValue = node.term || '';
const prefix = 'prefix' in node && node.prefix ? node.prefix : '';
const negation = 'isNegated' in node && node.isNegated ? '-' : '';
// Wrap term appropriately.
if ('isQuotedTerm' in node && node.isQuotedTerm) {
termValue = `"${termValue}"`;
} else if ('isRegexTerm' in node && node.isRegexTerm) {
termValue = `/${termValue}/`;
}
// Append boost and proximity if defined.
const boost = 'boost' in node && node.boost ? `^${node.boost}` : '';
const proximity = 'proximity' in node && node.proximity ? `~${node.proximity}` : '';
return `${negation}${prefix}${node.field}:${termValue}${boost}${proximity}`;
}
return '';
};
const traverse = (node: null | object): void => {
if (!node || typeof node !== 'object') {
return;
}
if ('hasParens' in node && node.hasParens) {
// Rebuild the raw query for a parens node.
const innerQuery = buildRawQuery(node);
termValues.push({ term: '', value: innerQuery });
return;
}
if ('left' in node && 'right' in node) {
traverse(node.left as object);
traverse(node.right as object);
return;
}
if ('field' in node && 'term' in node) {
termValues.push({ term: node.field as string, value: node.term as string });
}
};
traverse(ast);
return termValues;
}
}
export function isValid(query: string): boolean {
if (!query) {
return true;
}
try {
parse(query);
} catch {
return false;
}
return true;
}
/* eslint-disable @typescript-eslint/no-empty-object-type */
/**
* Expected any character, with `.`
*/
export interface AnyExpectation {
readonly type: 'any';
}
/**
* Expected a class, such as `[^acd-gz]i`
*/
export interface ClassExpectation {
readonly ignoreCase: boolean;
readonly inverted: boolean;
readonly parts: ClassParts;
readonly type: 'class';
}
export interface ClassParts extends Array<ClassRange | string> {}
/**
* Range of characters, like `a-z`
*/
export type ClassRange = [start: string, end: string];
/**
* Expected the end of input.
*/
export interface EndExpectation {
readonly type: 'end';
}
export type Expectation = AnyExpectation | ClassExpectation | EndExpectation | LiteralExpectation | OtherExpectation;
/**
* Most often, you just use a string with the file name.
*/
export type GrammarSource = GrammarSourceObject | string;
/**
* Anything that can successfully be converted to a string with `String()`
* so that it can be used in error messages.
*
* The GrammarLocation class in Peggy is a good example.
*/
export interface GrammarSourceObject {
/**
* If specified, allows the grammar source to be embedded in a larger file
* at some offset.
*/
readonly offset?: ((loc: Location) => Location) | undefined;
readonly toString: () => string;
}
/**
* Expected a literal string, like `"foo"i`.
*/
export interface LiteralExpectation {
readonly ignoreCase: boolean;
readonly text: string;
readonly type: 'literal';
}
/** Provides information pointing to a location within a source. */
export interface Location {
/** Column in the parsed source (1-based). */
readonly column: number;
/** Line in the parsed source (1-based). */
readonly line: number;
/** Offset in the parsed source (0-based). */
readonly offset: number;
}
/** The `start` and `end` position's of an object within the source. */
export interface LocationRange {
/** Position after the end of the expression. */
readonly end: Location;
/**
* A string or object that was supplied to the `parse()` call as the
* `grammarSource` option.
*/
readonly source: GrammarSource;
/** Position at the beginning of the expression. */
readonly start: Location;
}
/**
* Expected some other input. These are specified with a rule's
* "human-readable name", or with the `expected(message, location)`
* function.
*/
export interface OtherExpectation {
readonly description: string;
readonly type: 'other';
}
export interface ParseOptions<T extends StartRuleNames = 'start'> {
// Extra application-specific properties
[key: string]: unknown;
/**
* String or object that will be attached to the each `LocationRange` object
* created by the parser. For example, this can be path to the parsed file
* or even the File object.
*/
readonly grammarSource?: GrammarSource;
// Internal use only:
peg$currPos?: number;
// Internal use only:
readonly peg$library?: boolean;
// Internal use only:
peg$maxFailExpected?: Expectation[];
// Internal use only:
peg$silentFails?: number;
readonly startRule?: T;
readonly tracer?: ParserTracer;
}
/**
* Trace execution of the parser.
*/
export interface ParserTracer {
trace: (event: ParserTracerEvent) => void;
}
export type ParserTracerEvent =
| {
readonly location: LocationRange;
/** Return value from the rule. */
readonly result: unknown;
readonly rule: string;
readonly type: 'rule.match';
}
| {
readonly location: LocationRange;
readonly rule: string;
readonly type: 'rule.enter';
}
| {
readonly location: LocationRange;
readonly rule: string;
readonly type: 'rule.fail';
};
/**
* Pass an array of these into `SyntaxError.prototype.format()`
*/
export interface SourceText {
/**
* Identifier of an input that was used as a grammarSource in parse().
*/
readonly source: GrammarSource;
/** Source text of the input. */
readonly text: string;
}
export type StartRuleNames = 'start';
export declare class SyntaxError extends Error {
readonly expected: Expectation[];
readonly found: null | string | undefined;
readonly location: LocationRange;
readonly message: string;
readonly name: string;
constructor(message: string, expected: Expectation[], found: null | string, location: LocationRange);
/**
* Constructs the human-readable message from the machine representation.
*
* @param expected Array of expected items, generated by the parser
* @param found Any text that will appear as found in the input instead of
* expected
*/
static buildMessage(expected: Expectation[], found?: null | string | undefined): string;
/**
* With good sources, generates a feature-rich error message pointing to the
* error in the input.
* @param sources List of {source, text} objects that map to the input.
*/
format(sources: SourceText[]): string;
}
export declare const StartRules: StartRuleNames[];
export declare const parse: typeof ParseFunction;
// Overload of ParseFunction for each allowedStartRule
declare function ParseFunction<Options extends ParseOptions<'start'>>(input: string, options?: Options): unknown;
declare function ParseFunction<Options extends ParseOptions<StartRuleNames>>(input: string, options?: Options): unknown;
/* eslint-disable no-control-regex */
/* eslint-disable no-useless-escape */
/* eslint-disable no-empty */
/* eslint-disable @typescript-eslint/no-unused-vars */
// @generated by Peggy 4.2.0.
//
// https://peggyjs.org/
function peg$subclass(child, parent) {
function C() {
this.constructor = child;
}
C.prototype = parent.prototype;
child.prototype = new C();
}
function peg$SyntaxError(message, expected, found, location) {
var self = Error.call(this, message);
// istanbul ignore next Check is a necessary evil to support older environments
if (Object.setPrototypeOf) {
Object.setPrototypeOf(self, peg$SyntaxError.prototype);
}
self.expected = expected;
self.found = found;
self.location = location;
self.name = 'SyntaxError';
return self;
}
peg$subclass(peg$SyntaxError, Error);
function peg$padEnd(str, targetLength, padString) {
padString = padString || ' ';
if (str.length > targetLength) {
return str;
}
targetLength -= str.length;
padString += padString.repeat(targetLength);
return str + padString.slice(0, targetLength);
}
peg$SyntaxError.prototype.format = function (sources) {
var str = 'Error: ' + this.message;
if (this.location) {
var src = null;
var k;
for (k = 0; k < sources.length; k++) {
if (sources[k].source === this.location.source) {
src = sources[k].text.split(/\r\n|\n|\r/g);
break;
}
}
var s = this.location.start;
var offset_s = this.location.source && typeof this.location.source.offset === 'function' ? this.location.source.offset(s) : s;
var loc = this.location.source + ':' + offset_s.line + ':' + offset_s.column;
if (src) {
var e = this.location.end;
var filler = peg$padEnd('', offset_s.line.toString().length, ' ');
var line = src[s.line - 1];
var last = s.line === e.line ? e.column : line.length + 1;
var hatLen = last - s.column || 1;
str +=
'\n --> ' +
loc +
'\n' +
filler +
' |\n' +
offset_s.line +
' | ' +
line +
'\n' +
filler +
' | ' +
peg$padEnd('', s.column - 1, ' ') +
peg$padEnd('', hatLen, '^');
} else {
str += '\n at ' + loc;
}
}
return str;
};
peg$SyntaxError.buildMessage = function (expected, found) {
var DESCRIBE_EXPECTATION_FNS = {
any: function () {
return 'any character';
},
class: function (expectation) {
var escapedParts = expectation.parts.map(function (part) {
return Array.isArray(part) ? classEscape(part[0]) + '-' + classEscape(part[1]) : classEscape(part);
});
return '[' + (expectation.inverted ? '^' : '') + escapedParts.join('') + ']';
},
end: function () {
return 'end of input';
},
literal: function (expectation) {
return '"' + literalEscape(expectation.text) + '"';
},
other: function (expectation) {
return expectation.description;
}
};
function hex(ch) {
return ch.charCodeAt(0).toString(16).toUpperCase();
}
function literalEscape(s) {
return s
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\0/g, '\\0')
.replace(/\t/g, '\\t')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/[\x00-\x0F]/g, function (ch) {
return '\\x0' + hex(ch);
})
.replace(/[\x10-\x1F\x7F-\x9F]/g, function (ch) {
return '\\x' + hex(ch);
});
}
function classEscape(s) {
return s
.replace(/\\/g, '\\\\')
.replace(/\]/g, '\\]')
.replace(/\^/g, '\\^')
.replace(/-/g, '\\-')
.replace(/\0/g, '\\0')
.replace(/\t/g, '\\t')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/[\x00-\x0F]/g, function (ch) {
return '\\x0' + hex(ch);
})
.replace(/[\x10-\x1F\x7F-\x9F]/g, function (ch) {
return '\\x' + hex(ch);
});
}
function describeExpectation(expectation) {
return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation);
}
function describeExpected(expected) {
var descriptions = expected.map(describeExpectation);
var i, j;
descriptions.sort();
if (descriptions.length > 0) {
for (i = 1, j = 1; i < descriptions.length; i++) {
if (descriptions[i - 1] !== descriptions[i]) {
descriptions[j] = descriptions[i];
j++;
}
}
descriptions.length = j;
}
switch (descriptions.length) {
case 1:
return descriptions[0];
case 2:
return descriptions[0] + ' or ' + descriptions[1];
default:
return descriptions.slice(0, -1).join(', ') + ', or ' + descriptions[descriptions.length - 1];
}
}
function describeFound(found) {
return found ? '"' + literalEscape(found) + '"' : 'end of input';
}
return 'Expected ' + describeExpected(expected) + ' but ' + describeFound(found) + ' found.';
};
function peg$parse(input, options) {
options = options !== undefined ? options : {};
var peg$FAILED = {};
var peg$source = options.grammarSource;
var peg$startRuleFunctions = { start: peg$parsestart };
var peg$startRuleFunction = peg$parsestart;
var peg$c0 = '(';
var peg$c1 = ')';
var peg$c2 = '_exists_';
var peg$c3 = ':';
var peg$c4 = '_missing_';
var peg$c5 = '\\';
var peg$c6 = '*';
var peg$c7 = '"';
var peg$c8 = '\\"';
var peg$c9 = '/';
var peg$c10 = '^';
var peg$c11 = '~';
var peg$c12 = '>=';
var peg$c13 = '>';
var peg$c14 = '<=';
var peg$c15 = '<';
var peg$c16 = 'TO';
var peg$c17 = '..';
var peg$c18 = 'NOT';
var peg$c19 = 'OR';
var peg$c20 = 'AND';
var peg$c21 = '||';
var peg$c22 = '&&';
var peg$r0 = /^[+\-!(){}[\]\^"~*?:\/]/;
var peg$r1 = /^[^: \t\r\n\f{}()"\^~[\]]/;
var peg$r2 = /^[^: .\t\r\n\f{}()"\^~[\]]/;
var peg$r3 = /^[.]/;
var peg$r4 = /^[^.]/;
var peg$r5 = /^[^"]/;
var peg$r6 = /^[^\/]/;
var peg$r7 = /^[[{]/;
var peg$r8 = /^[\]}]/;
var peg$r9 = /^[!+\-]/;
var peg$r10 = /^[ \t\r\n\f]/;
var peg$e0 = peg$literalExpectation('(', false);
var peg$e1 = peg$literalExpectation(')', false);
var peg$e2 = peg$literalExpectation('_exists_', false);
var peg$e3 = peg$literalExpectation(':', false);
var peg$e4 = peg$literalExpectation('_missing_', false);
var peg$e5 = peg$literalExpectation('\\', false);
var peg$e6 = peg$classExpectation(['+', '-', '!', '(', ')', '{', '}', '[', ']', '^', '"', '~', '*', '?', ':', '/'], false, false);
var peg$e7 = peg$classExpectation([':', ' ', '\t', '\r', '\n', '\f', '{', '}', '(', ')', '"', '^', '~', '[', ']'], true, false);
var peg$e8 = peg$classExpectation([':', ' ', '.', '\t', '\r', '\n', '\f', '{', '}', '(', ')', '"', '^', '~', '[', ']'], true, false);
var peg$e9 = peg$classExpectation(['.'], false, false);
var peg$e10 = peg$classExpectation(['.'], true, false);
var peg$e11 = peg$literalExpectation('*', false);
var peg$e12 = peg$literalExpectation('"', false);
var peg$e13 = peg$literalExpectation('\\"', false);
var peg$e14 = peg$classExpectation(['"'], true, false);
var peg$e15 = peg$literalExpectation('/', false);
var peg$e16 = peg$classExpectation(['/'], true, false);
var peg$e17 = peg$literalExpectation('^', false);
var peg$e18 = peg$literalExpectation('~', false);
var peg$e19 = peg$classExpectation(['[', '{'], false, false);
var peg$e20 = peg$classExpectation([']', '}'], false, false);
var peg$e21 = peg$literalExpectation('>=', false);
var peg$e22 = peg$literalExpectation('>', false);
var peg$e23 = peg$literalExpectation('<=', false);
var peg$e24 = peg$literalExpectation('<', false);
var peg$e25 = peg$literalExpectation('TO', false);
var peg$e26 = peg$literalExpectation('..', false);
var peg$e27 = peg$literalExpectation('NOT', false);
var peg$e28 = peg$literalExpectation('OR', false);
var peg$e29 = peg$literalExpectation('AND', false);
var peg$e30 = peg$literalExpectation('||', false);
var peg$e31 = peg$literalExpectation('&&', false);
var peg$e32 = peg$classExpectation(['!', '+', '-'], false, false);
var peg$e33 = peg$classExpectation([' ', '\t', '\r', '\n', '\f'], false, false);
var peg$e34 = peg$anyExpectation();
var peg$f0 = function (node) {
return node !== undefined ? node : new GroupNode();
};
var peg$f1 = function (op) {
let gn = new GroupNode();
gn.operator = op;
return gn;
};
var peg$f2 = function (op, right) {
return right;
};
var peg$f3 = function (left, op, right) {
let gn = new GroupNode();
gn.left = left;
let rightExp = right.length === 0 ? null : right[0].right === null || right[0].right === undefined ? right[0].left : right[0];
if (rightExp !== null) {
gn.operator = op !== null && op !== undefined ? op : null;
gn.right = rightExp;
}
return gn;
};
var peg$f4 = function (f) {
return f;
};
var peg$f5 = function (not, op, node, boost) {
node.hasParens = true;
node.prefix = op !== null && op !== undefined ? op : null;
if (boost !== null && boost !== undefined) {
node.boost = boost;
}
if (not !== null && not !== undefined) {
node.isNegated = true;
}
return node;
};
var peg$f6 = function (not, op, fieldname) {
let en = new ExistsNode();
en.isNegated = not != null;
en.prefix = op != null ? op : null;
en.field = fieldname;
return en;
};
var peg$f7 = function (not, op, fieldname) {
let mn = new MissingNode();
mn.isNegated = not != null;
mn.prefix = op != null ? op : null;
mn.field = fieldname;
return mn;
};
var peg$f8 = function (not, name, range) {
if (name != null) {
range.isNegated = not != null;
range.field = name.field;
range.prefix = name.prefix;
}
return range;
};
var peg$f9 = function (not, op, range) {
range.isNegated = not != null;
range.prefix = op != null ? op : null;
return range;
};
var peg$f10 = function (not, name, node) {
node.isNegated = not != null;
node.field = name.field;
node.prefix = name.prefix;
return node;
};
var peg$f11 = function (not, name, term) {
let tn = new TermNode();
if (not != null) {
tn.isNegated = true;
}
tn.field = name.field;
tn.prefix = name.prefix;
term.copyTo(tn);
return tn;
};
var peg$f12 = function (op, fieldname) {
let fi = new FieldInfo(fieldname, op != null ? op : null);
return fi;
};
var peg$f13 = function (not, op, term, proximity, boost) {
let tn = new TermNode();
tn.term = term.term;
tn.isQuotedTerm = term.isQuoted;
tn.isRegexTerm = term.isRegex;
if (proximity !== undefined) {
tn.proximity = proximity;
}
if (boost !== undefined) {
tn.boost = boost;
}
if (not !== undefined) {
tn.isNegated = true;
}
tn.prefix = op !== null && op !== undefined ? op : null;
return tn;
};
var peg$f14 = function (not, prefix, op) {
throw new Error("Unexpected operator '" + op + "'.");
};
var peg$f15 = function (term) {
return term.join('');
};
var peg$f16 = function (term) {
return new TermInfo(term.join(''));
};
var peg$f17 = function (term) {
return new TermInfo(Array.isArray(term) ? term.join('') : term);
};
var peg$f18 = function (term) {
return new TermInfo(term.join(''), true);
};
var peg$f19 = function (term) {
throw new Error('Unterminated quoted string');
};
var peg$f20 = function (term) {
return new TermInfo(term.join(''), false, true);
};
var peg$f21 = function (term) {
throw new Error('Unterminated regex');
};
var peg$f22 = function (boost) {
return boost.term;
};
var peg$f23 = function (proximity) {
return proximity;
};
var peg$f24 = function (left, term_min, delim, term_max, right, proximity, boost) {
let trn = new TermRangeNode();
trn.min = term_min.term;
trn.isMinQuotedTerm = term_min.isQuoted;
trn.max = term_max.term;
trn.isMaxQuotedTerm = term_max.isQuoted;
trn.minInclusive = left === '[';
trn.maxInclusive = right === ']';
trn.delimiter = delim;
if (proximity !== undefined) {
trn.proximity = proximity;
}
if (boost !== undefined) {
trn.boost = boost;
}
return trn;
};
var peg$f25 = function (op, term, proximity, boost) {
let trn = new TermRangeNode();
trn.min = op.startsWith('>') ? term.term : null;
trn.minInclusive = op === '>=';
trn.isMinQuotedTerm = op.startsWith('>') && term.isQuoted;
trn.max = op.startsWith('<') ? term.term : null;
trn.maxInclusive = op === '<=';
trn.isMaxQuotedTerm = op.startsWith('<') && term.isQuoted;
trn.operator = op;
if (proximity !== undefined) {
trn.proximity = proximity;
}
if (boost !== undefined) {
trn.boost = boost;
}
return trn;
};
var peg$f26 = function (op) {
return op;
};
var peg$f27 = function () {
return GroupOperator.Or;
};
var peg$f28 = function () {
return GroupOperator.And;
};
var peg$f29 = function () {
return GroupOperator.Or;
};
var peg$f30 = function () {
return GroupOperator.And;
};
var peg$f31 = function (op) {
return op;
};
var peg$f32 = function () {
throw new Error("Missing closing paren ')' for group expression");
};
var peg$currPos = options.peg$currPos | 0;
var peg$savedPos = peg$currPos;
var peg$posDetailsCache = [{ column: 1, line: 1 }];
var peg$maxFailPos = peg$currPos;
var peg$maxFailExpected = options.peg$maxFailExpected || [];
var peg$silentFails = options.peg$silentFails | 0;
var peg$resultsCache = {};
var peg$result;
if (options.startRule) {
if (!(options.startRule in peg$startRuleFunctions)) {
throw new Error('Can\'t start parsing from rule "' + options.startRule + '".');
}
peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
}
function text() {
return input.substring(peg$savedPos, peg$currPos);
}
function offset() {
return peg$savedPos;
}
function range() {
return {
end: peg$currPos,
source: peg$source,
start: peg$savedPos
};
}
function location() {
return peg$computeLocation(peg$savedPos, peg$currPos);
}
function expected(description, location) {
location = location !== undefined ? location : peg$computeLocation(peg$savedPos, peg$currPos);
throw peg$buildStructuredError([peg$otherExpectation(description)], input.substring(peg$savedPos, peg$currPos), location);
}
function error(message, location) {
location = location !== undefined ? location : peg$computeLocation(peg$savedPos, peg$currPos);
throw peg$buildSimpleError(message, location);
}
function peg$literalExpectation(text, ignoreCase) {
return { ignoreCase: ignoreCase, text: text, type: 'literal' };
}
function peg$classExpectation(parts, inverted, ignoreCase) {
return { ignoreCase: ignoreCase, inverted: inverted, parts: parts, type: 'class' };
}
function peg$anyExpectation() {
return { type: 'any' };
}
function peg$endExpectation() {
return { type: 'end' };
}
function peg$otherExpectation(description) {
return { description: description, type: 'other' };
}
function peg$computePosDetails(pos) {
var details = peg$posDetailsCache[pos];
var p;
if (details) {
return details;
} else {
if (pos >= peg$posDetailsCache.length) {
p = peg$posDetailsCache.length - 1;
} else {
p = pos;
while (!peg$posDetailsCache[--p]) {}
}
details = peg$posDetailsCache[p];
details = {
column: details.column,
line: details.line
};
while (p < pos) {
if (input.charCodeAt(p) === 10) {
details.line++;
details.column = 1;
} else {
details.column++;
}
p++;
}
peg$posDetailsCache[pos] = details;
return details;
}
}
function peg$computeLocation(startPos, endPos, offset) {
var startPosDetails = peg$computePosDetails(startPos);
var endPosDetails = peg$computePosDetails(endPos);
var res = {
end: {
column: endPosDetails.column,
line: endPosDetails.line,
offset: endPos
},
source: peg$source,
start: {
column: startPosDetails.column,
line: startPosDetails.line,
offset: startPos
}
};
if (offset && peg$source && typeof peg$source.offset === 'function') {
res.start = peg$source.offset(res.start);
res.end = peg$source.offset(res.end);
}
return res;
}
function peg$fail(expected) {
if (peg$currPos < peg$maxFailPos) {
return;
}
if (peg$currPos > peg$maxFailPos) {
peg$maxFailPos = peg$currPos;
peg$maxFailExpected = [];
}
peg$maxFailExpected.push(expected);
}
function peg$buildSimpleError(message, location) {
return new peg$SyntaxError(message, null, null, location);
}
function peg$buildStructuredError(expected, found, location) {
return new peg$SyntaxError(peg$SyntaxError.buildMessage(expected, found), expected, found, location);
}
function peg$parsestart() {
var s0, s1, s2, s3, s4;
var key = peg$currPos * 25 + 0;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
s1 = [];
s2 = peg$parse_();
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parse_();
}
s2 = peg$parsenode();
if (s2 === peg$FAILED) {
s2 = null;
}
s3 = [];
s4 = peg$parse_();
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$parse_();
}
s4 = peg$parseEOF();
if (s4 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f0(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parsenode() {
var s0, s1, s2, s3, s4;
var key = peg$currPos * 25 + 1;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
s1 = peg$parseoperator_exp();
if (s1 !== peg$FAILED) {
s2 = peg$parseEOF();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f1(s1);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parseoperator_exp();
if (s1 !== peg$FAILED) {
s2 = peg$parsenode();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f2(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parsegroup_exp();
if (s1 !== peg$FAILED) {
s2 = peg$parseoperator_exp();
if (s2 === peg$FAILED) {
s2 = null;
}
s3 = [];
s4 = peg$parsenode();
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$parsenode();
}
peg$savedPos = s0;
s0 = peg$f3(s1, s2, s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parsegroup_exp() {
var s0, s1, s2, s3;
var key = peg$currPos * 25 + 2;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
s1 = peg$parsefield_exp();
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$parse_();
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parse_();
}
peg$savedPos = s0;
s0 = peg$f4(s1);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$parseparen_exp();
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parseparen_exp() {
var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10;
var key = peg$currPos * 25 + 3;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
s1 = peg$parsenot_exp();
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parseprefix_operator_exp();
if (s2 === peg$FAILED) {
s2 = null;
}
if (input.charCodeAt(peg$currPos) === 40) {
s3 = peg$c0;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e0);
}
}
if (s3 !== peg$FAILED) {
s4 = [];
s5 = peg$parse_();
while (s5 !== peg$FAILED) {
s4.push(s5);
s5 = peg$parse_();
}
s5 = peg$parsenode();
if (s5 !== peg$FAILED) {
s6 = [];
s7 = peg$parse_();
while (s7 !== peg$FAILED) {
s6.push(s7);
s7 = peg$parse_();
}
if (input.charCodeAt(peg$currPos) === 41) {
s7 = peg$c1;
peg$currPos++;
} else {
s7 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e1);
}
}
if (s7 === peg$FAILED) {
s7 = peg$parsemissing_paren();
}
if (s7 !== peg$FAILED) {
s8 = peg$parseboost_modifier();
if (s8 === peg$FAILED) {
s8 = null;
}
s9 = [];
s10 = peg$parse_();
while (s10 !== peg$FAILED) {
s9.push(s10);
s10 = peg$parse_();
}
peg$savedPos = s0;
s0 = peg$f5(s1, s2, s5, s8);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parsefield_exp() {
var s0, s1, s2, s3, s4, s5, s6, s7;
var key = peg$currPos * 25 + 4;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
s1 = peg$parsenot_exp();
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parseprefix_operator_exp();
if (s2 === peg$FAILED) {
s2 = null;
}
if (input.substr(peg$currPos, 8) === peg$c2) {
s3 = peg$c2;
peg$currPos += 8;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e2);
}
}
if (s3 !== peg$FAILED) {
s4 = [];
s5 = peg$parse_();
while (s5 !== peg$FAILED) {
s4.push(s5);
s5 = peg$parse_();
}
if (input.charCodeAt(peg$currPos) === 58) {
s5 = peg$c3;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e3);
}
}
if (s5 !== peg$FAILED) {
s6 = [];
s7 = peg$parse_();
while (s7 !== peg$FAILED) {
s6.push(s7);
s7 = peg$parse_();
}
s7 = peg$parsename_term();
if (s7 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f6(s1, s2, s7);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parsenot_exp();
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parseprefix_operator_exp();
if (s2 === peg$FAILED) {
s2 = null;
}
if (input.substr(peg$currPos, 9) === peg$c4) {
s3 = peg$c4;
peg$currPos += 9;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e4);
}
}
if (s3 !== peg$FAILED) {
s4 = [];
s5 = peg$parse_();
while (s5 !== peg$FAILED) {
s4.push(s5);
s5 = peg$parse_();
}
if (input.charCodeAt(peg$currPos) === 58) {
s5 = peg$c3;
peg$currPos++;
} else {
s5 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e3);
}
}
if (s5 !== peg$FAILED) {
s6 = [];
s7 = peg$parse_();
while (s7 !== peg$FAILED) {
s6.push(s7);
s7 = peg$parse_();
}
s7 = peg$parsename_term();
if (s7 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f7(s1, s2, s7);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parsenot_exp();
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parsefieldname();
if (s2 === peg$FAILED) {
s2 = null;
}
s3 = peg$parserange_operator_exp();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f8(s1, s2, s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parsenot_exp();
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parseprefix_operator_exp();
if (s2 === peg$FAILED) {
s2 = null;
}
s3 = peg$parserange_operator_exp();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f9(s1, s2, s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parsenot_exp();
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parsefieldname();
if (s2 !== peg$FAILED) {
s3 = peg$parseparen_exp();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f10(s1, s2, s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parsenot_exp();
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parsefieldname();
if (s2 !== peg$FAILED) {
s3 = peg$parseterm();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f11(s1, s2, s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
}
}
}
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parsefieldname() {
var s0, s1, s2, s3, s4, s5, s6;
var key = peg$currPos * 25 + 5;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
s1 = peg$parseprefix_operator_exp();
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parsename_term();
if (s2 !== peg$FAILED) {
s3 = [];
s4 = peg$parse_();
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$parse_();
}
if (input.charCodeAt(peg$currPos) === 58) {
s4 = peg$c3;
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e3);
}
}
if (s4 !== peg$FAILED) {
s5 = [];
s6 = peg$parse_();
while (s6 !== peg$FAILED) {
s5.push(s6);
s6 = peg$parse_();
}
peg$savedPos = s0;
s0 = peg$f12(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parseterm() {
var s0, s1, s2, s3, s4, s5, s6, s7, s8;
var key = peg$currPos * 25 + 6;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
s1 = peg$parsenot_exp();
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parseprefix_operator_exp();
if (s2 === peg$FAILED) {
s2 = null;
}
s3 = peg$currPos;
peg$silentFails++;
s4 = peg$parseoperator_exp();
peg$silentFails--;
if (s4 === peg$FAILED) {
s3 = undefined;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
s4 = peg$parsequoted_term();
if (s4 === peg$FAILED) {
s4 = peg$parseregex_term();
if (s4 === peg$FAILED) {
s4 = peg$parseunquoted_term();
}
}
if (s4 !== peg$FAILED) {
s5 = peg$parseproximity_modifier();
if (s5 === peg$FAILED) {
s5 = null;
}
s6 = peg$parseboost_modifier();
if (s6 === peg$FAILED) {
s6 = null;
}
s7 = [];
s8 = peg$parse_();
while (s8 !== peg$FAILED) {
s7.push(s8);
s8 = peg$parse_();
}
peg$savedPos = s0;
s0 = peg$f13(s1, s2, s4, s5, s6);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = peg$parsenot_exp();
if (s1 === peg$FAILED) {
s1 = null;
}
s2 = peg$parseprefix_operator_exp();
if (s2 === peg$FAILED) {
s2 = null;
}
s3 = peg$parseoperator_exp();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f14(s1, s2, s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parseescape_sequence() {
var s0, s1, s2;
var key = peg$currPos * 25 + 7;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 92) {
s1 = peg$c5;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e5);
}
}
if (s1 !== peg$FAILED) {
s2 = input.charAt(peg$currPos);
if (peg$r0.test(s2)) {
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e6);
}
}
if (s2 !== peg$FAILED) {
s1 = [s1, s2];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parsename_term() {
var s0, s1, s2;
var key = peg$currPos * 25 + 8;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
s1 = [];
s2 = input.charAt(peg$currPos);
if (peg$r1.test(s2)) {
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e7);
}
}
if (s2 === peg$FAILED) {
s2 = peg$parseescape_sequence();
}
if (s2 !== peg$FAILED) {
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = input.charAt(peg$currPos);
if (peg$r1.test(s2)) {
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e7);
}
}
if (s2 === peg$FAILED) {
s2 = peg$parseescape_sequence();
}
}
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f15(s1);
}
s0 = s1;
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parseunquoted_term() {
var s0, s1, s2;
var key = peg$currPos * 25 + 9;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
s1 = [];
s2 = input.charAt(peg$currPos);
if (peg$r1.test(s2)) {
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e7);
}
}
if (s2 === peg$FAILED) {
s2 = peg$parseescape_sequence();
}
if (s2 !== peg$FAILED) {
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = input.charAt(peg$currPos);
if (peg$r1.test(s2)) {
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e7);
}
}
if (s2 === peg$FAILED) {
s2 = peg$parseescape_sequence();
}
}
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f16(s1);
}
s0 = s1;
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parserange_unquoted_term() {
var s0, s1, s2, s3, s4;
var key = peg$currPos * 25 + 10;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
s1 = [];
s2 = input.charAt(peg$currPos);
if (peg$r2.test(s2)) {
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s2 === peg$FAILED) {
s2 = peg$currPos;
s3 = input.charAt(peg$currPos);
if (peg$r3.test(s3)) {
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e9);
}
}
if (s3 !== peg$FAILED) {
s4 = input.charAt(peg$currPos);
if (peg$r4.test(s4)) {
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e10);
}
}
if (s4 !== peg$FAILED) {
s3 = [s3, s4];
s2 = s3;
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
if (s2 === peg$FAILED) {
s2 = peg$parseescape_sequence();
}
}
if (s2 !== peg$FAILED) {
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = input.charAt(peg$currPos);
if (peg$r2.test(s2)) {
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e8);
}
}
if (s2 === peg$FAILED) {
s2 = peg$currPos;
s3 = input.charAt(peg$currPos);
if (peg$r3.test(s3)) {
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e9);
}
}
if (s3 !== peg$FAILED) {
s4 = input.charAt(peg$currPos);
if (peg$r4.test(s4)) {
peg$currPos++;
} else {
s4 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e10);
}
}
if (s4 !== peg$FAILED) {
s3 = [s3, s4];
s2 = s3;
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
if (s2 === peg$FAILED) {
s2 = peg$parseescape_sequence();
}
}
}
} else {
s1 = peg$FAILED;
}
if (s1 === peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 42) {
s1 = peg$c6;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e11);
}
}
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f17(s1);
}
s0 = s1;
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parsequoted_term() {
var s0, s1, s2, s3;
var key = peg$currPos * 25 + 11;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 34) {
s1 = peg$c7;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e12);
}
}
if (s1 !== peg$FAILED) {
s2 = [];
if (input.substr(peg$currPos, 2) === peg$c8) {
s3 = peg$c8;
peg$currPos += 2;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e13);
}
}
if (s3 === peg$FAILED) {
s3 = input.charAt(peg$currPos);
if (peg$r5.test(s3)) {
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e14);
}
}
}
while (s3 !== peg$FAILED) {
s2.push(s3);
if (input.substr(peg$currPos, 2) === peg$c8) {
s3 = peg$c8;
peg$currPos += 2;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e13);
}
}
if (s3 === peg$FAILED) {
s3 = input.charAt(peg$currPos);
if (peg$r5.test(s3)) {
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e14);
}
}
}
}
if (input.charCodeAt(peg$currPos) === 34) {
s3 = peg$c7;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e12);
}
}
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f18(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 34) {
s1 = peg$c7;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e12);
}
}
if (s1 !== peg$FAILED) {
s2 = [];
if (input.substr(peg$currPos, 2) === peg$c8) {
s3 = peg$c8;
peg$currPos += 2;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e13);
}
}
if (s3 === peg$FAILED) {
s3 = input.charAt(peg$currPos);
if (peg$r5.test(s3)) {
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e14);
}
}
}
while (s3 !== peg$FAILED) {
s2.push(s3);
if (input.substr(peg$currPos, 2) === peg$c8) {
s3 = peg$c8;
peg$currPos += 2;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e13);
}
}
if (s3 === peg$FAILED) {
s3 = input.charAt(peg$currPos);
if (peg$r5.test(s3)) {
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e14);
}
}
}
}
peg$savedPos = s0;
s0 = peg$f19(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parseregex_term() {
var s0, s1, s2, s3;
var key = peg$currPos * 25 + 12;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 47) {
s1 = peg$c9;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
if (s1 !== peg$FAILED) {
s2 = [];
if (input.charCodeAt(peg$currPos) === 47) {
s3 = peg$c9;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
if (s3 === peg$FAILED) {
s3 = input.charAt(peg$currPos);
if (peg$r6.test(s3)) {
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
}
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
if (input.charCodeAt(peg$currPos) === 47) {
s3 = peg$c9;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
if (s3 === peg$FAILED) {
s3 = input.charAt(peg$currPos);
if (peg$r6.test(s3)) {
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
}
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 47) {
s3 = peg$c9;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f20(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 47) {
s1 = peg$c9;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
if (s1 !== peg$FAILED) {
s2 = [];
if (input.charCodeAt(peg$currPos) === 47) {
s3 = peg$c9;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
if (s3 === peg$FAILED) {
s3 = input.charAt(peg$currPos);
if (peg$r6.test(s3)) {
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
}
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
if (input.charCodeAt(peg$currPos) === 47) {
s3 = peg$c9;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e15);
}
}
if (s3 === peg$FAILED) {
s3 = input.charAt(peg$currPos);
if (peg$r6.test(s3)) {
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e16);
}
}
}
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f21(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parseboost_modifier() {
var s0, s1, s2;
var key = peg$currPos * 25 + 13;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 94) {
s1 = peg$c10;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e17);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parsequoted_term();
if (s2 === peg$FAILED) {
s2 = peg$parseunquoted_term();
}
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f22(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parseproximity_modifier() {
var s0, s1, s2;
var key = peg$currPos * 25 + 14;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 126) {
s1 = peg$c11;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e18);
}
}
if (s1 !== peg$FAILED) {
s2 = peg$parseunquoted_term();
if (s2 === peg$FAILED) {
s2 = null;
}
peg$savedPos = s0;
s0 = peg$f23(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parserange_operator_exp() {
var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9;
var key = peg$currPos * 25 + 15;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
s1 = input.charAt(peg$currPos);
if (peg$r7.test(s1)) {
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e19);
}
}
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$parse_();
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parse_();
}
s3 = peg$parserange_unquoted_term();
if (s3 === peg$FAILED) {
s3 = peg$parsequoted_term();
}
if (s3 !== peg$FAILED) {
s4 = peg$parserange_delimiter_exp();
if (s4 !== peg$FAILED) {
s5 = peg$parserange_unquoted_term();
if (s5 === peg$FAILED) {
s5 = peg$parsequoted_term();
}
if (s5 !== peg$FAILED) {
s6 = [];
s7 = peg$parse_();
while (s7 !== peg$FAILED) {
s6.push(s7);
s7 = peg$parse_();
}
s7 = input.charAt(peg$currPos);
if (peg$r8.test(s7)) {
peg$currPos++;
} else {
s7 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e20);
}
}
if (s7 !== peg$FAILED) {
s8 = peg$parseproximity_modifier();
if (s8 === peg$FAILED) {
s8 = null;
}
s9 = peg$parseboost_modifier();
if (s9 === peg$FAILED) {
s9 = null;
}
peg$savedPos = s0;
s0 = peg$f24(s1, s3, s4, s5, s7, s8, s9);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c12) {
s1 = peg$c12;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e21);
}
}
if (s1 === peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 62) {
s1 = peg$c13;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e22);
}
}
if (s1 === peg$FAILED) {
if (input.substr(peg$currPos, 2) === peg$c14) {
s1 = peg$c14;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e23);
}
}
if (s1 === peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 60) {
s1 = peg$c15;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e24);
}
}
}
}
}
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$parse_();
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parse_();
}
s3 = peg$parserange_unquoted_term();
if (s3 === peg$FAILED) {
s3 = peg$parsequoted_term();
}
if (s3 !== peg$FAILED) {
s4 = peg$parseproximity_modifier();
if (s4 === peg$FAILED) {
s4 = null;
}
s5 = peg$parseboost_modifier();
if (s5 === peg$FAILED) {
s5 = null;
}
peg$savedPos = s0;
s0 = peg$f25(s1, s3, s4, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parserange_delimiter_exp() {
var s0, s1, s2, s3, s4;
var key = peg$currPos * 25 + 16;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
s1 = [];
s2 = peg$parse_();
if (s2 !== peg$FAILED) {
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parse_();
}
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
if (input.substr(peg$currPos, 2) === peg$c16) {
s2 = peg$c16;
peg$currPos += 2;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e25);
}
}
if (s2 !== peg$FAILED) {
s3 = [];
s4 = peg$parse_();
if (s4 !== peg$FAILED) {
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$parse_();
}
} else {
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
s1 = [s1, s2, s3];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
if (s0 === peg$FAILED) {
s0 = peg$currPos;
s1 = [];
s2 = peg$parse_();
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parse_();
}
if (input.substr(peg$currPos, 2) === peg$c17) {
s2 = peg$c17;
peg$currPos += 2;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e26);
}
}
if (s2 !== peg$FAILED) {
s3 = [];
s4 = peg$parse_();
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$parse_();
}
s1 = [s1, s2, s3];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parsenot_exp() {
var s0, s1, s2, s3;
var key = peg$currPos * 25 + 17;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
if (input.substr(peg$currPos, 3) === peg$c18) {
s1 = peg$c18;
peg$currPos += 3;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e27);
}
}
if (s1 !== peg$FAILED) {
s2 = [];
s3 = peg$parse_();
if (s3 !== peg$FAILED) {
while (s3 !== peg$FAILED) {
s2.push(s3);
s3 = peg$parse_();
}
} else {
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
s1 = [s1, s2];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parseoperator_exp() {
var s0, s1, s2, s3, s4;
var key = peg$currPos * 25 + 18;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
s1 = [];
s2 = peg$parse_();
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parse_();
}
s2 = peg$parseoperator();
if (s2 !== peg$FAILED) {
s3 = [];
s4 = peg$parse_();
if (s4 !== peg$FAILED) {
while (s4 !== peg$FAILED) {
s3.push(s4);
s4 = peg$parse_();
}
} else {
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f26(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parseoperator() {
var s0, s1;
var key = peg$currPos * 25 + 19;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c19) {
s1 = peg$c19;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e28);
}
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f27();
}
s0 = s1;
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 3) === peg$c20) {
s1 = peg$c20;
peg$currPos += 3;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e29);
}
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f28();
}
s0 = s1;
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c21) {
s1 = peg$c21;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e30);
}
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f29();
}
s0 = s1;
if (s0 === peg$FAILED) {
s0 = peg$currPos;
if (input.substr(peg$currPos, 2) === peg$c22) {
s1 = peg$c22;
peg$currPos += 2;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e31);
}
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f30();
}
s0 = s1;
}
}
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parseprefix_operator_exp() {
var s0, s1, s2, s3, s4;
var key = peg$currPos * 25 + 20;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
s1 = [];
s2 = peg$parse_();
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$parse_();
}
s2 = peg$parseprefix_operator();
if (s2 !== peg$FAILED) {
s3 = peg$currPos;
peg$silentFails++;
s4 = peg$parse_();
peg$silentFails--;
if (s4 === peg$FAILED) {
s3 = undefined;
} else {
peg$currPos = s3;
s3 = peg$FAILED;
}
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f31(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parseprefix_operator() {
var s0;
var key = peg$currPos * 25 + 21;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = input.charAt(peg$currPos);
if (peg$r9.test(s0)) {
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e32);
}
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parse_() {
var s0, s1;
var key = peg$currPos * 25 + 22;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = [];
s1 = input.charAt(peg$currPos);
if (peg$r10.test(s1)) {
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e33);
}
}
if (s1 !== peg$FAILED) {
while (s1 !== peg$FAILED) {
s0.push(s1);
s1 = input.charAt(peg$currPos);
if (peg$r10.test(s1)) {
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e33);
}
}
}
} else {
s0 = peg$FAILED;
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parsemissing_paren() {
var s0, s1;
var key = peg$currPos * 25 + 23;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
s1 = '';
peg$savedPos = s0;
s1 = peg$f32();
s0 = s1;
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
function peg$parseEOF() {
var s0, s1;
var key = peg$currPos * 25 + 24;
var cached = peg$resultsCache[key];
if (cached) {
peg$currPos = cached.nextPos;
return cached.result;
}
s0 = peg$currPos;
peg$silentFails++;
if (input.length > peg$currPos) {
s1 = input.charAt(peg$currPos);
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) {
peg$fail(peg$e34);
}
}
peg$silentFails--;
if (s1 === peg$FAILED) {
s0 = undefined;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
peg$resultsCache[key] = { nextPos: peg$currPos, result: s0 };
return s0;
}
class FieldInfo {
constructor(field, prefix = null) {
this.field = field;
this.prefix = prefix;
}
}
class TermInfo {
constructor(term, isQuoted = false, isRegex = false) {
this.term = term;
this.isQuoted = isQuoted;
this.isRegex = isRegex;
}
}
class GroupNode {
constructor() {
this.operator = null;
this.left = null;
this.right = null;
this.hasParens = false;
this.prefix = null;
this.boost = null;
this.isNegated = false;
// Added for safety:
this.field = null;
}
}
class ExistsNode {
constructor() {
this.isNegated = false;
this.prefix = null;
this.field = null;
}
}
class MissingNode {
constructor() {
this.isNegated = false;
this.prefix = null;
this.field = null;
}
}
class TermNode {
constructor() {
this.term = null;
this.isQuotedTerm = false;
this.isRegexTerm = false;
this.proximity = null;
this.boost = null;
this.isNegated = false;
this.prefix = null;
this.field = null;
}
copyTo(target) {
target.term = this.term;
target.isQuotedTerm = this.isQuotedTerm;
target.isRegexTerm = this.isRegexTerm;
}
}
class TermRangeNode {
constructor() {
this.min = null;
this.isMinQuotedTerm = false;
this.max = null;
this.isMaxQuotedTerm = false;
this.minInclusive = false;
this.maxInclusive = false;
this.delimiter = null;
this.proximity = null;
this.boost = null;
this.operator = null;
}
}
const GroupOperator = {
And: 'AND',
Or: 'OR'
};
peg$result = peg$startRuleFunction();
if (options.peg$library) {
return /** @type {any} */ ({
peg$currPos,
peg$FAILED,
peg$maxFailExpected,
peg$maxFailPos,
peg$result
});
}
if (peg$result !== peg$FAILED && peg$currPos === input.length) {
return peg$result;
} else {
if (peg$result !== peg$FAILED && peg$currPos < input.length) {
peg$fail(peg$endExpectation());
}
throw peg$buildStructuredError(
peg$maxFailExpected,
peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null,
peg$maxFailPos < input.length ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1) : peg$computeLocation(peg$maxFailPos, peg$maxFailPos)
);
}
}
const peg$allowedStartRules = ['start'];
export { peg$parse as parse, peg$allowedStartRules as StartRules, peg$SyntaxError as SyntaxError };
@namespace Foundatio.Parsers.LuceneQueries
@classname LuceneQueryParser
@trace true
@using System.Linq;
@using Foundatio.Parsers.LuceneQueries.Nodes;
@using Foundatio.Parsers.LuceneQueries.Extensions;
@members
{
public class FieldInfo {
public string Field { get; set; }
public string Prefix { get; set; }
}
public class TermInfo {
public string Term { get; set; }
public bool IsQuoted { get; set; }
public bool IsRegex { get; set; }
}
}
start<GroupNode>
= _* node:node? _* EOF
{
node.SingleOrDefault() ?? new GroupNode()
}
node<GroupNode>
= op:operator_exp EOF
{
new GroupNode {
Operator = op
}
}
/ op:operator_exp right:node
{
right
}
/ left:group_exp op:operator_exp? right:node*
{{
var node= new GroupNode {
Left = left
};
left.Parent = node;
var rightExp =
right.Count == 0
? (TermNode)null
: right[0].Right == null
? right[0].Left
: right[0];
if (rightExp != null)
{
node.Operator = op.SingleOrDefault();
node.Right = rightExp;
rightExp.Parent = node;
}
return node;
}}
group_exp<IQueryNode>
= field_exp:field_exp _*
{
field_exp
}
/ paren_exp
paren_exp<GroupNode>
= not:not_exp? op:prefix_operator_exp? "(" _* node:node _* (")" / #error{ "Missing closing paren ')' for group expression" }) boost:boost_modifier? _*
{{
node.HasParens = true;
node.Prefix = op.SingleOrDefault();
if (boost.Count > 0)
node.Boost = boost.SingleOrDefault();
if (not.Any())
node.IsNegated = true;
return node;
}}
field_exp<IQueryNode>
= not:not_exp? op:prefix_operator_exp? '_exists_' _* ':' _* fieldname:name_term
{{
return new ExistsNode { IsNegated = not.Any(), Prefix = op.SingleOrDefault(), Field = fieldname };
}}
/ not:not_exp? op:prefix_operator_exp? '_missing_' _* ':' _* fieldname:name_term
{{
return new MissingNode { IsNegated = not.Any(), Prefix = op.SingleOrDefault(), Field = fieldname };
}}
/ not:not_exp? name:fieldname? range:range_operator_exp
{{
if (name.Count == 1) {
range.IsNegated = not.Any();
range.Field = name[0].Field;
range.Prefix = name[0].Prefix;
}
return range;
}}
/ not:not_exp? op:prefix_operator_exp? range:range_operator_exp
{{
range.IsNegated = not.Any();
range.Prefix = op.SingleOrDefault();
return range;
}}
/ not:not_exp? name:fieldname node:paren_exp
{{
node.IsNegated = not.Any();
node.Field = name.Field;
node.Prefix = name.Prefix;
return node;
}}
/ not:not_exp? name:fieldname? term:term
{{
var query = new TermNode();
if (not.Any())
query.IsNegated = true;
if (name.Count == 1) {
query.Field = name[0].Field;
query.Prefix = name[0].Prefix;
}
term.CopyTo(query);
return query;
}}
fieldname<FieldInfo> -lexical
= op:prefix_operator_exp? fieldname:name_term _* ':' _*
{{
var result = new FieldInfo { Field = fieldname };
result.Prefix = op.SingleOrDefault();
return result;
}}
term<TermNode>
= not:not_exp? op:prefix_operator_exp? !operator_exp term:(quoted_term / regex_term / unquoted_term) proximity:proximity_modifier? boost:boost_modifier? _*
{{
var result = new TermNode { Term = term.Term, IsQuotedTerm = term.IsQuoted, IsRegexTerm = term.IsRegex };
if (proximity.Count > 0)
result.Proximity = proximity.SingleOrDefault();
if (boost.Count > 0)
result.Boost = boost.SingleOrDefault();
if (not.Any())
result.IsNegated = true;
result.Prefix = op.SingleOrDefault();
return result;
}}
/ not:not_exp? prefix:prefix_operator_exp? op:operator_exp #error{ "Unexpected operator '" + op + "'." }
// https://lucene.apache.org/core/2_9_4/queryparsersyntax.html#Escaping%20Special%20Characters
escape_sequence
= '\\' [ \+\-\!\(\)\{\}\[\]\^\"\~\*\?\:\\\/]
name_term
= term:(([^: \\\t\r\n\f\{\}\(\)"^~\[\]] / escape_sequence)+ "")
{
term
}
unquoted_term<TermInfo>
= term:(([^: \\\t\r\n\f\{\}\(\)"^~\[\]] / escape_sequence)+ "")
{
new TermInfo { Term = term }
}
range_unquoted_term<TermInfo>
= term:((([^: \\\.\t\r\n\f\{\}\(\)"^~\[\]] / [\.][^\.] / escape_sequence)+ "") / '*')
{
new TermInfo { Term = term }
}
quoted_term<TermInfo>
= '"' term:(('\\"' / [^"])* "") ('"' / #error{ "Unterminated quoted string" })
{
new TermInfo { Term = term, IsQuoted = true }
}
regex_term<TermInfo>
= '/' term:(('\\/' / [^/])+ "") ('/' / #error{ "Unterminated regex" })
{
new TermInfo { Term = term, IsRegex = true }
}
boost_modifier<string>
= '^' boost:(quoted_term / unquoted_term)
{
boost.Term
}
proximity_modifier<string>
= '~' proximity:(unquoted_term? "")
{
proximity
}
range_operator_exp<TermRangeNode>
= left:('[' / '{') _* term_min:(range_unquoted_term / quoted_term) delim:range_delimiter_exp term_max:(range_unquoted_term / quoted_term) _* right:(']' / '}') proximity:proximity_modifier? boost:boost_modifier?
{{
var result = new TermRangeNode {
Min = term_min.Term,
IsMinQuotedTerm = term_min.IsQuoted,
Max = term_max.Term,
IsMaxQuotedTerm = term_max.IsQuoted,
MinInclusive = left.SingleOrDefault() == '[' ? true : false,
MaxInclusive = right.SingleOrDefault() == ']' ? true : false,
Delimiter = delim
};
if (proximity.Count > 0)
result.Proximity = proximity.SingleOrDefault();
if (boost.Count > 0)
result.Boost = boost.SingleOrDefault();
return result;
}}
/ op:(">=" / ">" / "<=" / "<") _* term:(range_unquoted_term / quoted_term) proximity:proximity_modifier? boost:boost_modifier?
{{
var result = new TermRangeNode {
Min = op.StartsWith(">") ? term.Term : null,
MinInclusive = op == ">=",
IsMinQuotedTerm = op.StartsWith(">") && term.IsQuoted,
Max = op.StartsWith("<") ? term.Term : null,
MaxInclusive = op == "<=",
IsMaxQuotedTerm = op.StartsWith("<") && term.IsQuoted,
Operator = op
};
if (proximity.Count > 0)
result.Proximity = proximity.SingleOrDefault();
if (boost.Count > 0)
result.Boost = boost.SingleOrDefault();
return result;
}}
range_delimiter_exp -lexical
= _+ 'TO' _+
/ _* '..' _*
not_exp
= 'NOT' _+
operator_exp<GroupOperator>
= _* op:operator _+
{
op
}
operator<GroupOperator> -lexical
= "OR" { GroupOperator.Or }
/ "AND"{ GroupOperator.And }
/ "||" { GroupOperator.Or }
/ "&&" { GroupOperator.And }
prefix_operator_exp
= _* op:prefix_operator !_
{
op
}
prefix_operator -lexical
= '+'
/ '-'
/ '!'
_
= [ \t\r\n\f]+
EOF
= !.
/ unexpected:. #error{ "Unexpected character '" + unexpected + "'." }
{
class FieldInfo {
constructor(field, prefix = null) {
this.field = field;
this.prefix = prefix;
}
}
class TermInfo {
constructor(term, isQuoted = false, isRegex = false) {
this.term = term;
this.isQuoted = isQuoted;
this.isRegex = isRegex;
}
}
class GroupNode {
constructor() {
this.operator = null;
this.left = null;
this.right = null;
this.hasParens = false;
this.prefix = null;
this.boost = null;
this.isNegated = false;
// Added for safety:
this.field = null;
}
}
class ExistsNode {
constructor() {
this.isNegated = false;
this.prefix = null;
this.field = null;
}
}
class MissingNode {
constructor() {
this.isNegated = false;
this.prefix = null;
this.field = null;
}
}
class TermNode {
constructor() {
this.term = null;
this.isQuotedTerm = false;
this.isRegexTerm = false;
this.proximity = null;
this.boost = null;
this.isNegated = false;
this.prefix = null;
this.field = null;
}
copyTo(target) {
target.term = this.term;
target.isQuotedTerm = this.isQuotedTerm;
target.isRegexTerm = this.isRegexTerm;
}
}
class TermRangeNode {
constructor() {
this.min = null;
this.isMinQuotedTerm = false;
this.max = null;
this.isMaxQuotedTerm = false;
this.minInclusive = false;
this.maxInclusive = false;
this.delimiter = null;
this.proximity = null;
this.boost = null;
this.operator = null;
}
}
const GroupOperator = {
Or: 'OR',
And: 'AND'
};
}
start
= _* node:node? _* EOF {
return (node !== undefined ? node : new GroupNode());
}
node
= op:operator_exp EOF {
let gn = new GroupNode();
gn.operator = op;
return gn;
}
/ op:operator_exp right:node {
return right;
}
/ left:group_exp op:operator_exp? right:node* {
let gn = new GroupNode();
gn.left = left;
let rightExp = (right.length === 0)
? null
: (right[0].right === null || right[0].right === undefined)
? right[0].left : right[0];
if (rightExp !== null) {
gn.operator = (op !== null && op !== undefined) ? op : null;
gn.right = rightExp;
}
return gn;
}
group_exp
= f:field_exp _* { return f; }
/ paren_exp
paren_exp
= not:not_exp? op:prefix_operator_exp? "(" _* node:node _* (")" / missing_paren) boost:boost_modifier? _* {
node.hasParens = true;
node.prefix = (op !== null && op !== undefined) ? op : null;
if (boost !== null && boost !== undefined) { node.boost = boost; }
if (not !== null && not !== undefined) { node.isNegated = true; }
return node;
}
field_exp
= not:not_exp? op:prefix_operator_exp? '_exists_' _* ':' _* fieldname:name_term {
let en = new ExistsNode();
en.isNegated = (not != null);
en.prefix = (op != null) ? op : null;
en.field = fieldname;
return en;
}
/ not:not_exp? op:prefix_operator_exp? '_missing_' _* ':' _* fieldname:name_term {
let mn = new MissingNode();
mn.isNegated = (not != null);
mn.prefix = (op != null) ? op : null;
mn.field = fieldname;
return mn;
}
/ not:not_exp? name:fieldname? range:range_operator_exp {
if (name != null) {
range.isNegated = (not != null);
range.field = name.field;
range.prefix = name.prefix;
}
return range;
}
/ not:not_exp? op:prefix_operator_exp? range:range_operator_exp {
range.isNegated = (not != null);
range.prefix = (op != null) ? op : null;
return range;
}
/ not:not_exp? name:fieldname node:paren_exp {
node.isNegated = (not != null);
node.field = name.field;
node.prefix = name.prefix;
return node;
}
/ not:not_exp? name:fieldname term:term {
let tn = new TermNode();
if (not != null) { tn.isNegated = true; }
tn.field = name.field;
tn.prefix = name.prefix;
term.copyTo(tn);
return tn;
}
fieldname
= op:prefix_operator_exp? fieldname:name_term _* ':' _* {
let fi = new FieldInfo(fieldname, (op != null) ? op : null);
return fi;
}
term
= not:not_exp? op:prefix_operator_exp? !operator_exp term:(quoted_term / regex_term / unquoted_term) proximity:proximity_modifier? boost:boost_modifier? _* {
let tn = new TermNode();
tn.term = term.term;
tn.isQuotedTerm = term.isQuoted;
tn.isRegexTerm = term.isRegex;
if (proximity !== undefined) { tn.proximity = proximity; }
if (boost !== undefined) { tn.boost = boost; }
if (not !== undefined) { tn.isNegated = true; }
tn.prefix = (op !== null && op !== undefined) ? op : null;
return tn;
}
/ not:not_exp? prefix:prefix_operator_exp? op:operator_exp { throw new Error("Unexpected operator '" + op + "'."); }
// https://lucene.apache.org/core/2_9_4/queryparsersyntax.html#Escaping%20Special%20Characters
escape_sequence
= '\\' [+\-!(){}[\]^"~*?:\/]
name_term
= term:(([^: \t\r\n\f{}()"^~\[\]] / escape_sequence)+) {
return term.join('');
}
unquoted_term
= term:(([^: \t\r\n\f{}()"^~\[\]] / escape_sequence)+) {
return new TermInfo(term.join(''));
}
range_unquoted_term
= term:((( [^: \.\t\r\n\f{}()"^~\[\]] / ([.][^.]) / escape_sequence)+) / '*') {
return new TermInfo(Array.isArray(term) ? term.join('') : term);
}
quoted_term
= '"' term:(('\\"' / [^"])* ) '"' {
return new TermInfo(term.join(''), true);
}
/ '"' term:(('\\"' / [^"])* ) {
throw new Error("Unterminated quoted string");
}
regex_term
= '/' term:(('\/' / [^/])+ ) '/' {
return new TermInfo(term.join(''), false, true);
}
/ '/' term:(('\/' / [^/])+ ) {
throw new Error("Unterminated regex");
}
boost_modifier
= '^' boost:(quoted_term / unquoted_term) { return boost.term; }
proximity_modifier
= '~' proximity:(unquoted_term?) { return proximity; }
range_operator_exp
= left:('[' / '{') _* term_min:(range_unquoted_term / quoted_term) delim:range_delimiter_exp term_max:(range_unquoted_term / quoted_term) _* right:(']' / '}') proximity:proximity_modifier? boost:boost_modifier? {
let trn = new TermRangeNode();
trn.min = term_min.term;
trn.isMinQuotedTerm = term_min.isQuoted;
trn.max = term_max.term;
trn.isMaxQuotedTerm = term_max.isQuoted;
trn.minInclusive = left === '[';
trn.maxInclusive = right === ']';
trn.delimiter = delim;
if (proximity !== undefined) { trn.proximity = proximity; }
if (boost !== undefined) { trn.boost = boost; }
return trn;
}
/ op:(">=" / ">" / "<=" / "<") _* term:(range_unquoted_term / quoted_term) proximity:proximity_modifier? boost:boost_modifier? {
let trn = new TermRangeNode();
trn.min = op.startsWith(">") ? term.term : null;
trn.minInclusive = op === ">=";
trn.isMinQuotedTerm = op.startsWith(">") && term.isQuoted;
trn.max = op.startsWith("<") ? term.term : null;
trn.maxInclusive = op === "<=";
trn.isMaxQuotedTerm = op.startsWith("<") && term.isQuoted;
trn.operator = op;
if (proximity !== undefined) { trn.proximity = proximity; }
if (boost !== undefined) { trn.boost = boost; }
return trn;
}
range_delimiter_exp
= _+ "TO" _+
/ _* ".." _*
not_exp
= "NOT" _+
operator_exp
= _* op:operator _+ { return op; }
operator
= "OR" { return GroupOperator.Or; }
/ "AND" { return GroupOperator.And; }
/ "||" { return GroupOperator.Or; }
/ "&&" { return GroupOperator.And; }
prefix_operator_exp
= _* op:prefix_operator !_ { return op; }
prefix_operator
= '+' / '-' / '!'
_
= [ \t\r\n\f]+
missing_paren
= "" { throw new Error("Missing closing paren ')' for group expression"); }
EOF
= !.
@niemyjski
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment