├── .gitignore
├── package.json
├── README.rst
├── lucene-query.grammar
├── test
└── lucene-query-parser-spec.js
└── lucene-query-parser.js
/.gitignore:
--------------------------------------------------------------------------------
1 | lib-cov
2 | *.seed
3 | *.log
4 | *.csv
5 | *.dat
6 | *.out
7 | *.pid
8 | *.gz
9 |
10 | pids
11 | logs
12 | results
13 |
14 | .DS_Store
15 | node_modules
16 | *.sock
17 |
18 | npm-debug.log
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lucene-query-parser",
3 | "version": "0.0.2",
4 | "description": "Lucene Query Parser for Javascript created using PEG.js",
5 | "main": "lucene-query-parser.js",
6 | "directories": {
7 | "test": "test"
8 | },
9 | "scripts": {
10 | "build": "./node_modules/.bin/pegjs ./lucene-query.grammar lucene-query-parser.js",
11 | "test": "./node_modules/.bin/jasmine-node ./test/ --verbose",
12 | "prepublish": "npm run-script build && npm test"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git://github.com/mahnunchik/lucene-query-parser.git"
17 | },
18 | "keywords": [
19 | "lucene",
20 | "query",
21 | "parser",
22 | "pegjs"
23 | ],
24 | "author": "",
25 | "license": "MIT",
26 | "bugs": {
27 | "url": "https://github.com/mahnunchik/lucene-query-parser/issues"
28 | },
29 | "devDependencies": {
30 | "pegjs": "0.7.x",
31 | "jasmine-node": "1.10.x"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | ========================================
2 | Lucene Query Parser for Javascript
3 | ========================================
4 |
5 | This is an implementation of the Lucene Query Parser developed using PEG.js.
6 |
7 | Example
8 | ========================================
9 |
10 | A quick example of how to use it::
11 |
12 | // return the expression tree
13 | var results = lucenequeryparser.parse('title:"The Right Way" AND text:go');
14 |
15 | alert(results['left']['field']); // title
16 | alert(results['left']['term']); // The Right Way
17 | alert(results['operator']); // AND
18 | alert(results['right']['field']); // text
19 | alert(results['right']['term']); // go
20 |
21 |
22 | A slightly more complicated example::
23 |
24 | // return the expression tree
25 | var results = lucenequeryparser.parse('test AND (foo OR bar)');
26 |
27 | alert(results['left']['term']); // test
28 | alert(results['operator']); // AND
29 |
30 | // the parantheses group becomes it's own nested node
31 | var rightNode = results['right'];
32 |
33 | alert(rightNode['left']['term']); // foo
34 | alert(rightNode['operator']); // OR
35 | alert(rightNode['right']['term']); // bar
36 |
37 |
38 | Unit Tests
39 | ========================================
40 |
41 | To run the unit tests, just open SpecRunner.html in any browser. Unit tests are built with
42 | `Jasmine `_.
43 |
44 |
45 |
46 | Grammar
47 | ========================================
48 |
49 | The parser is auto-generated from a PEG implementation in Javascript called
50 | `PEG.js `_.
51 |
52 |
53 | To test the grammar without using the generated parser, or if you want to modify it, try out `PEG.js
54 | online `_. This is a handy way to test an abritrary query and see
55 | what the results will be like or debug a problem with the parser for a given piece of data.
56 |
57 |
58 |
59 | Community
60 | ========================================
61 |
62 | If you'd like to help out with the project, or if you have a question, feel free to contact
63 | Troy Howard at thoward37@gmail.com.
64 |
65 | Bug reports or feature requests should go in the bitbucket issue tracker. Please include relevant
66 | sample data (the query) and a good description of the challenges you're facing.
67 |
68 | Look to the wiki for documentation and other resources.
69 |
--------------------------------------------------------------------------------
/lucene-query.grammar:
--------------------------------------------------------------------------------
1 | /*
2 | * Lucene Query Grammar for PEG.js
3 | * ========================================
4 | *
5 | * This grammar supports many of the constructs contained in the Lucene Query Syntax.
6 | *
7 | * Supported features:
8 | * - conjunction operators (AND, OR, ||, &&, NOT)
9 | * - prefix operators (+, -)
10 | * - quoted values ("foo bar")
11 | * - named fields (foo:bar)
12 | * - range expressions (foo:[bar TO baz], foo:{bar TO baz})
13 | * - proximity search expressions ("foo bar"~5)
14 | * - boost expressions (foo^5, "foo bar"^5)
15 | * - fuzzy search expressions (foo~, foo~0.5)
16 | * - parentheses grouping ( (foo OR bar) AND baz )
17 | * - field groups ( foo:(bar OR baz) )
18 | *
19 | * The grammar will create a parser which returns an AST for the query in the form of a tree
20 | * of nodes, which are dictionaries. There are three basic types of expression dictionaries:
21 | *
22 | * A node expression generally has the following structure:
23 | *
24 | * {
25 | * 'left' : dictionary, // field expression or node
26 | * 'operator': string, // operator value
27 | * 'right': dictionary, // field expression OR node
28 | * 'field': string // field name (for field group syntax) [OPTIONAL]
29 | * }
30 | *
31 | *
32 | * A field expression has the following structure:
33 | *
34 | * {
35 | * 'field': string, // field name
36 | * 'term': string, // term value
37 | * 'prefix': string // prefix operator (+/-) [OPTIONAL]
38 | * 'boost': float // boost value, (value > 1 must be integer) [OPTIONAL]
39 | * 'similarity': float // similarity value, (value must be > 0 and < 1) [OPTIONAL]
40 | * 'proximity': integer // proximity value [OPTIONAL]
41 | * }
42 | *
43 | *
44 | * A range expression has the following structure:
45 | *
46 | * {
47 | * 'field': string, // field name
48 | * 'term_min': string, // minimum value (left side) of range
49 | * 'term_max': string, // maximum value (right side) of range
50 | * 'inclusive': boolean // inclusive ([...]) or exclusive ({...})
51 | * }
52 | *
53 | * Other Notes:
54 | *
55 | * - For any field name, unnamed/default fields will have the value "".
56 | * - Wildcards (fo*, f?o) and fuzzy search modifiers (foo~.8) will be part of the term value.
57 | * - Escaping is not supported and generally speaking, will break the parser.
58 | * - Conjunction operators that appear at the beginning of the query violate the logic of the
59 | * syntax, and are currently "mostly" ignored. The last element will be returned.
60 | *
61 | * For example:
62 | * Query: OR
63 | * Return: { "operator": "OR" }
64 | *
65 | * Query: OR AND
66 | * Return: { "operator": "AND" }
67 | *
68 | * Query: OR AND foo
69 | * Return: { "left": { "field": "", "term": "foo" } }
70 | *
71 | * To test the grammar, use the online parser generator at http://pegjs.majda.cz/online
72 | *
73 | */
74 |
75 | start
76 | = _* node:node+
77 | {
78 | return node[0];
79 | }
80 | / _*
81 | {
82 | return {};
83 | }
84 | / EOF
85 | {
86 | return {};
87 | }
88 |
89 | node
90 | = operator:operator_exp EOF
91 | {
92 | return {
93 | 'operator': operator
94 | };
95 | }
96 | / operator:operator_exp right:node
97 | {
98 | return right;
99 | }
100 | / left:group_exp operator:operator_exp* right:node*
101 | {
102 | var node= {
103 | 'left':left
104 | };
105 |
106 | var right =
107 | right.length == 0
108 | ? null
109 | : right[0]['right'] == null
110 | ? right[0]['left']
111 | : right[0];
112 |
113 | if (right != null)
114 | {
115 | node['operator'] = operator==''? '' : operator[0];
116 | node['right'] = right;
117 | }
118 |
119 | return node;
120 | }
121 |
122 | group_exp
123 | = field_exp:field_exp _*
124 | {
125 | return field_exp;
126 | }
127 | / paren_exp
128 |
129 | paren_exp
130 | = "(" node:node+ ")" _*
131 | {
132 | return node[0];
133 | }
134 |
135 | field_exp
136 | = fieldname:fieldname? range:range_operator_exp
137 | {
138 | range['field'] =
139 | fieldname == ''
140 | ? ""
141 | : fieldname;
142 |
143 | return range;
144 | }
145 | / fieldname:fieldname node:paren_exp
146 | {
147 | node['field']= fieldname;
148 | return node;
149 | }
150 | / fieldname:fieldname? term:term
151 | {
152 | var fieldexp = {
153 | 'field':
154 | fieldname == ''
155 | ? ""
156 | : fieldname
157 | };
158 |
159 | for(var key in term)
160 | fieldexp[key] = term[key];
161 |
162 | return fieldexp;
163 | }
164 |
165 | fieldname
166 | = fieldname:unquoted_term [:]
167 | {
168 | return fieldname;
169 | }
170 |
171 | term
172 | = op:prefix_operator_exp? term:quoted_term proximity:proximity_modifier? boost:boost_modifier? _*
173 | {
174 | var result = { 'term': term };
175 |
176 | if('' != proximity)
177 | {
178 | result['proximity'] = proximity;
179 | }
180 | if('' != boost)
181 | {
182 | result['boost'] = boost;
183 | }
184 | if('' != op)
185 | {
186 | result['prefix'] = op;
187 | }
188 |
189 | return result;
190 | }
191 | / op:prefix_operator_exp? term:unquoted_term similarity:fuzzy_modifier? boost:boost_modifier? _*
192 | {
193 | var result = { 'term': term };
194 | if('' != similarity)
195 | {
196 | result['similarity'] = similarity;
197 | }
198 | if('' != boost)
199 | {
200 | result['boost'] = boost;
201 | }
202 | if('' != op)
203 | {
204 | result['prefix'] = op;
205 | }
206 | return result;
207 | }
208 |
209 | unquoted_term
210 | = term:term_char+
211 | {
212 | return term.join('');
213 | }
214 |
215 | term_char
216 | = '.' / [^: \t\r\n\f\{\}()"+-/^~\[\]]
217 |
218 |
219 | quoted_term
220 | = '"' term:[^"]+ '"'
221 | {
222 | return term.join('');
223 | }
224 |
225 | proximity_modifier
226 | = '~' proximity:int_exp
227 | {
228 | return proximity;
229 | }
230 |
231 | boost_modifier
232 | = '^' boost:decimal_or_int_exp
233 | {
234 | return boost;
235 | }
236 |
237 | fuzzy_modifier
238 | = '~' fuzziness:decimal_exp?
239 | {
240 | return fuzziness == '' ? 0.5 : fuzziness;
241 | }
242 |
243 | decimal_or_int_exp
244 | = decimal_exp
245 | / int_exp
246 |
247 | decimal_exp
248 | = '0.' val:[0-9]+
249 | {
250 | return parseFloat("0." + val.join(''));
251 | }
252 |
253 | int_exp
254 | = val:[0-9]+
255 | {
256 | return parseInt(val.join(''));
257 | }
258 |
259 | range_operator_exp
260 | = '[' term_min:unquoted_term _* 'TO' _+ term_max:unquoted_term ']'
261 | {
262 | return {
263 | 'term_min': term_min,
264 | 'term_max': term_max,
265 | 'inclusive': true
266 | };
267 | }
268 | / '{' term_min:unquoted_term _* 'TO' _+ term_max:unquoted_term '}'
269 | {
270 | return {
271 | 'term_min': term_min,
272 | 'term_max': term_max,
273 | 'inclusive': false
274 | };
275 | }
276 |
277 | operator_exp
278 | = _* operator:operator _+
279 | {
280 | return operator;
281 | }
282 | / _* operator:operator EOF
283 | {
284 | return operator;
285 | }
286 |
287 | operator
288 | = 'OR'
289 | / 'AND'
290 | / 'NOT'
291 | / '||' { return 'OR'; }
292 | / '&&' { return 'AND'; }
293 |
294 | prefix_operator_exp
295 | = _* operator:prefix_operator
296 | {
297 | return operator;
298 | }
299 |
300 | prefix_operator
301 | = '+'
302 | / '-'
303 |
304 | _ "whitespace"
305 | = [ \t\r\n\f]+
306 |
307 | EOF
308 | = !.
309 |
--------------------------------------------------------------------------------
/test/lucene-query-parser-spec.js:
--------------------------------------------------------------------------------
1 | var lucenequeryparser = require('../')
2 |
3 | describe("lucenequeryparser: whitespace handling", function() {
4 |
5 | // term parsing
6 | it("handles empty string", function() {
7 | var results = lucenequeryparser.parse('');
8 |
9 | expect(isEmpty(results)).toBe(true);
10 | });
11 |
12 | it("handles leading whitespace with no contents", function() {
13 | var results = lucenequeryparser.parse(' \r\n');
14 |
15 | expect(isEmpty(results)).toBe(true);
16 | });
17 |
18 | it("handles leading whitespace before an expression string", function() {
19 | var results = lucenequeryparser.parse(' Test:Foo');
20 |
21 | expect(results['left']['field']).toBe('Test');
22 | expect(results['left']['term']).toBe('Foo');
23 | });
24 |
25 | function isEmpty(arr)
26 | {
27 | for(var i in arr)
28 | {
29 | return false;
30 | }
31 | return true;
32 | }
33 | });
34 |
35 | describe("lucenequeryparser: term parsing", function() {
36 |
37 | // term parsing
38 | it("parses terms", function() {
39 | var results = lucenequeryparser.parse('bar');
40 |
41 | expect(results['left']['term']).toBe('bar');
42 | });
43 |
44 | it("parses quoted terms", function() {
45 | var results = lucenequeryparser.parse('"fizz buzz"');
46 |
47 | expect(results['left']['term']).toBe('fizz buzz');
48 | });
49 | });
50 |
51 | describe("lucenequeryparser: term prefix operators", function() {
52 |
53 | it("parses prefix operators (-)", function() {
54 | var results = lucenequeryparser.parse('-bar');
55 |
56 | expect(results['left']['term']).toBe('bar');
57 | expect(results['left']['prefix']).toBe('-');
58 | });
59 |
60 | it("parses prefix operator (+)", function() {
61 | var results = lucenequeryparser.parse('+bar');
62 |
63 | expect(results['left']['term']).toBe('bar');
64 | expect(results['left']['prefix']).toBe('+');
65 | });
66 |
67 | it("parses prefix operator on quoted term (-)", function() {
68 | var results = lucenequeryparser.parse('-"fizz buzz"');
69 |
70 | expect(results['left']['term']).toBe('fizz buzz');
71 | expect(results['left']['prefix']).toBe('-');
72 | });
73 |
74 | it("parses prefix operator on quoted term (+)", function() {
75 | var results = lucenequeryparser.parse('+"fizz buzz"');
76 |
77 | expect(results['left']['term']).toBe('fizz buzz');
78 | expect(results['left']['prefix']).toBe('+');
79 | });
80 | });
81 |
82 | describe("lucenequeryparser: field name support", function() {
83 |
84 | it("parses implicit field name for term", function() {
85 | var results = lucenequeryparser.parse('bar');
86 |
87 | expect(results['left']['field']).toBe('');
88 | expect(results['left']['term']).toBe('bar');
89 | });
90 |
91 | it("parses implicit field name for quoted term", function() {
92 | var results = lucenequeryparser.parse('"fizz buzz"');
93 |
94 | expect(results['left']['field']).toBe('');
95 | expect(results['left']['term']).toBe('fizz buzz');
96 | });
97 |
98 | it("parses explicit field name for term", function() {
99 | var results = lucenequeryparser.parse('foo:bar');
100 |
101 | expect(results['left']['field']).toBe('foo');
102 | expect(results['left']['term']).toBe('bar');
103 | });
104 |
105 | it("parses explicit field name including dots (e.g 'sub.field') for term", function() {
106 | var results = lucenequeryparser.parse('sub.foo:bar');
107 |
108 | expect(results['left']['field']).toBe('sub.foo');
109 | expect(results['left']['term']).toBe('bar');
110 | });
111 |
112 |
113 | it("parses explicit field name for quoted term", function() {
114 | var results = lucenequeryparser.parse('foo:"fizz buzz"');
115 |
116 | expect(results['left']['field']).toBe('foo');
117 | expect(results['left']['term']).toBe('fizz buzz');
118 | });
119 |
120 | it("parses explicit field name for term with prefix", function() {
121 | var results = lucenequeryparser.parse('foo:-bar');
122 |
123 | expect(results['left']['field']).toBe('foo');
124 | expect(results['left']['term']).toBe('bar');
125 | expect(results['left']['prefix']).toBe('-');
126 |
127 | results = lucenequeryparser.parse('foo:+bar');
128 |
129 | expect(results['left']['field']).toBe('foo');
130 | expect(results['left']['term']).toBe('bar');
131 | expect(results['left']['prefix']).toBe('+');
132 | });
133 |
134 | it("parses explicit field name for quoted term with prefix", function() {
135 | var results = lucenequeryparser.parse('foo:-"fizz buzz"');
136 |
137 | expect(results['left']['field']).toBe('foo');
138 | expect(results['left']['term']).toBe('fizz buzz');
139 | expect(results['left']['prefix']).toBe('-');
140 |
141 | results = lucenequeryparser.parse('foo:+"fizz buzz"');
142 |
143 | expect(results['left']['field']).toBe('foo');
144 | expect(results['left']['term']).toBe('fizz buzz');
145 | expect(results['left']['prefix']).toBe('+');
146 | });
147 |
148 | });
149 |
150 | describe("lucenequeryparser: conjunction operators", function() {
151 |
152 | it("parses implicit conjunction operator (OR)", function() {
153 | var results = lucenequeryparser.parse('fizz buzz');
154 |
155 | expect(results['left']['term']).toBe('fizz');
156 | expect(results['operator']).toBe('');
157 | expect(results['right']['term']).toBe('buzz');
158 | });
159 |
160 | it("parses explicit conjunction operator (AND)", function() {
161 | var results = lucenequeryparser.parse('fizz AND buzz');
162 |
163 | expect(results['left']['term']).toBe('fizz');
164 | expect(results['operator']).toBe('AND');
165 | expect(results['right']['term']).toBe('buzz');
166 | });
167 |
168 | it("parses explicit conjunction operator (OR)", function() {
169 | var results = lucenequeryparser.parse('fizz OR buzz');
170 |
171 | expect(results['left']['term']).toBe('fizz');
172 | expect(results['operator']).toBe('OR');
173 | expect(results['right']['term']).toBe('buzz');
174 | });
175 |
176 | it("parses explicit conjunction operator (NOT)", function() {
177 | var results = lucenequeryparser.parse('fizz NOT buzz');
178 |
179 | expect(results['left']['term']).toBe('fizz');
180 | expect(results['operator']).toBe('NOT');
181 | expect(results['right']['term']).toBe('buzz');
182 | });
183 |
184 | it("parses explicit conjunction operator (&&)", function() {
185 | var results = lucenequeryparser.parse('fizz && buzz');
186 |
187 | expect(results['left']['term']).toBe('fizz');
188 | expect(results['operator']).toBe('AND');
189 | expect(results['right']['term']).toBe('buzz');
190 | });
191 |
192 | it("parses explicit conjunction operator (||)", function() {
193 | var results = lucenequeryparser.parse('fizz || buzz');
194 |
195 | expect(results['left']['term']).toBe('fizz');
196 | expect(results['operator']).toBe('OR');
197 | expect(results['right']['term']).toBe('buzz');
198 | });
199 | });
200 |
201 | describe("lucenequeryparser: parentheses groups", function() {
202 |
203 | it("parses parentheses group", function() {
204 | var results = lucenequeryparser.parse('fizz (buzz baz)');
205 |
206 | expect(results['left']['term']).toBe('fizz');
207 | expect(results['operator']).toBe('');
208 |
209 | var rightNode = results['right'];
210 |
211 | expect(rightNode['left']['term']).toBe('buzz');
212 | expect(rightNode['operator']).toBe('');
213 | expect(rightNode['right']['term']).toBe('baz');
214 | });
215 |
216 | it("parses parentheses groups with explicit conjunction operators ", function() {
217 | var results = lucenequeryparser.parse('fizz AND (buzz OR baz)');
218 |
219 | expect(results['left']['term']).toBe('fizz');
220 | expect(results['operator']).toBe('AND');
221 |
222 | var rightNode = results['right'];
223 |
224 | expect(rightNode['left']['term']).toBe('buzz');
225 | expect(rightNode['operator']).toBe('OR');
226 | expect(rightNode['right']['term']).toBe('baz');
227 | });
228 | });
229 |
230 | describe("lucenequeryparser: range expressions", function() {
231 |
232 | it("parses inclusive range expression", function() {
233 | var results = lucenequeryparser.parse('foo:[bar TO baz]');
234 |
235 | expect(results['left']['field']).toBe('foo');
236 | expect(results['left']['term_min']).toBe('bar');
237 | expect(results['left']['term_max']).toBe('baz');
238 | expect(results['left']['inclusive']).toBe(true);
239 | });
240 |
241 | it("parses inclusive range expression", function() {
242 | var results = lucenequeryparser.parse('foo:{bar TO baz}');
243 |
244 | expect(results['left']['field']).toBe('foo');
245 | expect(results['left']['term_min']).toBe('bar');
246 | expect(results['left']['term_max']).toBe('baz');
247 | expect(results['left']['inclusive']).toBe(false);
248 | });
249 | });
250 |
251 | describe("lucenequeryparser: Lucene Query syntax documentation examples", function() {
252 |
253 | /*
254 | Examples from Lucene documentation at
255 |
256 | http://lucene.apache.org/java/2_9_4/queryparsersyntax.html
257 |
258 | title:"The Right Way" AND text:go
259 | title:"Do it right" AND right
260 | title:Do it right
261 |
262 | te?t
263 | test*
264 | te*t
265 |
266 | roam~
267 | roam~0.8
268 |
269 | "jakarta apache"~10
270 | mod_date:[20020101 TO 20030101]
271 | title:{Aida TO Carmen}
272 |
273 | jakarta apache
274 | jakarta^4 apache
275 | "jakarta apache"^4 "Apache Lucene"
276 | "jakarta apache" jakarta
277 | "jakarta apache" OR jakarta
278 | "jakarta apache" AND "Apache Lucene"
279 | +jakarta lucene
280 | "jakarta apache" NOT "Apache Lucene"
281 | NOT "jakarta apache"
282 | "jakarta apache" -"Apache Lucene"
283 | (jakarta OR apache) AND website
284 | title:(+return +"pink panther")
285 | */
286 |
287 | it('parses example: title:"The Right Way" AND text:go', function() {
288 | var results = lucenequeryparser.parse('title:"The Right Way" AND text:go');
289 |
290 | expect(results['left']['field']).toBe('title');
291 | expect(results['left']['term']).toBe('The Right Way');
292 | expect(results['operator']).toBe('AND');
293 | expect(results['right']['field']).toBe('text');
294 | expect(results['right']['term']).toBe('go');
295 | });
296 |
297 | it('parses example: title:"Do it right" AND right', function() {
298 | var results = lucenequeryparser.parse('title:"Do it right" AND right');
299 |
300 | expect(results['left']['field']).toBe('title');
301 | expect(results['left']['term']).toBe('Do it right');
302 | expect(results['operator']).toBe('AND');
303 | expect(results['right']['field']).toBe('');
304 | expect(results['right']['term']).toBe('right');
305 | });
306 |
307 | it('parses example: title:Do it right', function() {
308 | var results = lucenequeryparser.parse('title:Do it right');
309 |
310 | expect(results['left']['field']).toBe('title');
311 | expect(results['left']['term']).toBe('Do');
312 | expect(results['operator']).toBe('');
313 |
314 | var rightNode = results['right'];
315 |
316 | expect(rightNode['left']['field']).toBe('');
317 | expect(rightNode['left']['term']).toBe('it');
318 | expect(rightNode['operator']).toBe('');
319 |
320 | expect(rightNode['right']['field']).toBe('');
321 | expect(rightNode['right']['term']).toBe('right');
322 | });
323 |
324 | it('parses example: te?t', function() {
325 | var results = lucenequeryparser.parse('te?t');
326 |
327 | expect(results['left']['field']).toBe('');
328 | expect(results['left']['term']).toBe('te?t');
329 | });
330 |
331 | it('parses example: test*', function() {
332 | var results = lucenequeryparser.parse('test*');
333 |
334 | expect(results['left']['field']).toBe('');
335 | expect(results['left']['term']).toBe('test*');
336 | });
337 |
338 | it('parses example: te*t', function() {
339 | var results = lucenequeryparser.parse('te*t');
340 |
341 | expect(results['left']['field']).toBe('');
342 | expect(results['left']['term']).toBe('te*t');
343 | });
344 |
345 | it('parses example: roam~', function() {
346 | var results = lucenequeryparser.parse('roam~');
347 |
348 | expect(results['left']['field']).toBe('');
349 | expect(results['left']['term']).toBe('roam');
350 | expect(results['left']['similarity']).toBe(0.5);
351 | });
352 |
353 | it('parses example: roam~0.8', function() {
354 | var results = lucenequeryparser.parse('roam~0.8');
355 |
356 | expect(results['left']['field']).toBe('');
357 | expect(results['left']['term']).toBe('roam');
358 | expect(results['left']['similarity']).toBe(0.8);
359 | });
360 |
361 | it('parses example: "jakarta apache"~10', function() {
362 | var results = lucenequeryparser.parse('"jakarta apache"~10');
363 |
364 | expect(results['left']['field']).toBe('');
365 | expect(results['left']['term']).toBe('jakarta apache');
366 | expect(results['left']['proximity']).toBe(10);
367 | });
368 |
369 | it('parses example: mod_date:[20020101 TO 20030101]', function() {
370 | var results = lucenequeryparser.parse('mod_date:[20020101 TO 20030101]');
371 |
372 | expect(results['left']['field']).toBe('mod_date');
373 | expect(results['left']['term_min']).toBe('20020101');
374 | expect(results['left']['term_max']).toBe('20030101');
375 | expect(results['left']['inclusive']).toBe(true);
376 | });
377 |
378 | it('parses example: title:{Aida TO Carmen}', function() {
379 | var results = lucenequeryparser.parse('title:{Aida TO Carmen}');
380 |
381 | expect(results['left']['field']).toBe('title');
382 | expect(results['left']['term_min']).toBe('Aida');
383 | expect(results['left']['term_max']).toBe('Carmen');
384 | expect(results['left']['inclusive']).toBe(false);
385 | });
386 |
387 | it('parses example: jakarta apache', function() {
388 | var results = lucenequeryparser.parse('jakarta apache');
389 |
390 | expect(results['left']['field']).toBe('');
391 | expect(results['left']['term']).toBe('jakarta');
392 | expect(results['operator']).toBe('');
393 | expect(results['right']['field']).toBe('');
394 | expect(results['right']['term']).toBe('apache');
395 | });
396 |
397 | it('parses example: jakarta^4 apache', function() {
398 | var results = lucenequeryparser.parse('jakarta^4 apache');
399 |
400 | expect(results['left']['field']).toBe('');
401 | expect(results['left']['term']).toBe('jakarta');
402 | expect(results['left']['boost']).toBe(4);
403 | expect(results['operator']).toBe('');
404 | expect(results['right']['field']).toBe('');
405 | expect(results['right']['term']).toBe('apache');
406 | });
407 |
408 | it('parses example: "jakarta apache"^4 "Apache Lucene"', function() {
409 | var results = lucenequeryparser.parse('"jakarta apache"^4 "Apache Lucene"');
410 |
411 |
412 | expect(results['left']['field']).toBe('');
413 | expect(results['left']['term']).toBe('jakarta apache');
414 | expect(results['left']['boost']).toBe(4);
415 | expect(results['operator']).toBe('');
416 | expect(results['right']['field']).toBe('');
417 | expect(results['right']['term']).toBe('Apache Lucene');
418 |
419 | });
420 |
421 | it('parses example: "jakarta apache" jakarta', function() {
422 | var results = lucenequeryparser.parse('"jakarta apache" jakarta');
423 |
424 | expect(results['left']['field']).toBe('');
425 | expect(results['left']['term']).toBe('jakarta apache');
426 | expect(results['operator']).toBe('');
427 | expect(results['right']['field']).toBe('');
428 | expect(results['right']['term']).toBe('jakarta');
429 | });
430 |
431 | it('parses example: "jakarta apache" OR jakarta', function() {
432 | var results = lucenequeryparser.parse('"jakarta apache" OR jakarta');
433 |
434 | expect(results['left']['field']).toBe('');
435 | expect(results['left']['term']).toBe('jakarta apache');
436 | expect(results['operator']).toBe('OR');
437 | expect(results['right']['field']).toBe('');
438 | expect(results['right']['term']).toBe('jakarta');
439 | });
440 |
441 | it('parses example: "jakarta apache" AND "Apache Lucene"', function() {
442 | var results = lucenequeryparser.parse('"jakarta apache" AND "Apache Lucene"');
443 |
444 | expect(results['left']['field']).toBe('');
445 | expect(results['left']['term']).toBe('jakarta apache');
446 | expect(results['operator']).toBe('AND');
447 | expect(results['right']['field']).toBe('');
448 | expect(results['right']['term']).toBe('Apache Lucene');
449 | });
450 |
451 | it('parses example: +jakarta lucene', function() {
452 | var results = lucenequeryparser.parse('+jakarta lucene');
453 |
454 | expect(results['left']['field']).toBe('');
455 | expect(results['left']['term']).toBe('jakarta');
456 | expect(results['left']['prefix']).toBe('+');
457 | });
458 |
459 | it('parses example: "jakarta apache" NOT "Apache Lucene"', function() {
460 | var results = lucenequeryparser.parse('"jakarta apache" NOT "Apache Lucene"');
461 |
462 | expect(results['left']['field']).toBe('');
463 | expect(results['left']['term']).toBe('jakarta apache');
464 | expect(results['operator']).toBe('NOT');
465 | expect(results['right']['field']).toBe('');
466 | expect(results['right']['term']).toBe('Apache Lucene');
467 | });
468 |
469 | it('parses example: NOT "jakarta apache"', function() {
470 | var results = lucenequeryparser.parse('NOT "jakarta apache"');
471 |
472 | // not a valid query, so operator is ignored.
473 | expect(results['left']['field']).toBe('');
474 | expect(results['left']['term']).toBe('jakarta apache');
475 | expect(results['operator']).toBe(undefined);
476 | });
477 |
478 | it('parses example: "jakarta apache" -"Apache Lucene"', function() {
479 | var results = lucenequeryparser.parse('"jakarta apache" -"Apache Lucene"');
480 |
481 | expect(results['left']['field']).toBe('');
482 | expect(results['left']['term']).toBe('jakarta apache');
483 | expect(results['operator']).toBe('');
484 | expect(results['right']['field']).toBe('');
485 | expect(results['right']['term']).toBe('Apache Lucene');
486 | expect(results['right']['prefix']).toBe('-');
487 | });
488 |
489 | it('parses example: (jakarta OR apache) AND website', function() {
490 | var results = lucenequeryparser.parse('(jakarta OR apache) AND website');
491 |
492 | var leftNode = results['left'];
493 | expect(leftNode['left']['field']).toBe('');
494 | expect(leftNode['left']['term']).toBe('jakarta');
495 | expect(leftNode['operator']).toBe('OR');
496 | expect(leftNode['right']['field']).toBe('');
497 | expect(leftNode['right']['term']).toBe('apache');
498 |
499 | expect(results['operator']).toBe('AND');
500 | expect(results['right']['field']).toBe('');
501 | expect(results['right']['term']).toBe('website');
502 | });
503 |
504 | it('parses example: title:(+return +"pink panther")', function() {
505 | var results = lucenequeryparser.parse('title:(+return +"pink panther")');
506 |
507 | var leftNode = results['left'];
508 |
509 | expect(leftNode['left']['field']).toBe('');
510 | expect(leftNode['left']['term']).toBe('return');
511 | expect(leftNode['left']['prefix']).toBe('+');
512 | expect(leftNode['operator']).toBe('');
513 | expect(leftNode['right']['field']).toBe('');
514 | expect(leftNode['right']['term']).toBe('pink panther');
515 | expect(leftNode['right']['prefix']).toBe('+');
516 | expect(leftNode['field']).toBe('title');
517 | });
518 |
519 | });
520 |
--------------------------------------------------------------------------------
/lucene-query-parser.js:
--------------------------------------------------------------------------------
1 | module.exports = (function(){
2 | /*
3 | * Generated by PEG.js 0.7.0.
4 | *
5 | * http://pegjs.majda.cz/
6 | */
7 |
8 | function quote(s) {
9 | /*
10 | * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a
11 | * string literal except for the closing quote character, backslash,
12 | * carriage return, line separator, paragraph separator, and line feed.
13 | * Any character may appear in the form of an escape sequence.
14 | *
15 | * For portability, we also escape escape all control and non-ASCII
16 | * characters. Note that "\0" and "\v" escape sequences are not used
17 | * because JSHint does not like the first and IE the second.
18 | */
19 | return '"' + s
20 | .replace(/\\/g, '\\\\') // backslash
21 | .replace(/"/g, '\\"') // closing quote character
22 | .replace(/\x08/g, '\\b') // backspace
23 | .replace(/\t/g, '\\t') // horizontal tab
24 | .replace(/\n/g, '\\n') // line feed
25 | .replace(/\f/g, '\\f') // form feed
26 | .replace(/\r/g, '\\r') // carriage return
27 | .replace(/[\x00-\x07\x0B\x0E-\x1F\x80-\uFFFF]/g, escape)
28 | + '"';
29 | }
30 |
31 | var result = {
32 | /*
33 | * Parses the input with a generated parser. If the parsing is successfull,
34 | * returns a value explicitly or implicitly specified by the grammar from
35 | * which the parser was generated (see |PEG.buildParser|). If the parsing is
36 | * unsuccessful, throws |PEG.parser.SyntaxError| describing the error.
37 | */
38 | parse: function(input, startRule) {
39 | var parseFunctions = {
40 | "start": parse_start,
41 | "node": parse_node,
42 | "group_exp": parse_group_exp,
43 | "paren_exp": parse_paren_exp,
44 | "field_exp": parse_field_exp,
45 | "fieldname": parse_fieldname,
46 | "term": parse_term,
47 | "unquoted_term": parse_unquoted_term,
48 | "term_char": parse_term_char,
49 | "quoted_term": parse_quoted_term,
50 | "proximity_modifier": parse_proximity_modifier,
51 | "boost_modifier": parse_boost_modifier,
52 | "fuzzy_modifier": parse_fuzzy_modifier,
53 | "decimal_or_int_exp": parse_decimal_or_int_exp,
54 | "decimal_exp": parse_decimal_exp,
55 | "int_exp": parse_int_exp,
56 | "range_operator_exp": parse_range_operator_exp,
57 | "operator_exp": parse_operator_exp,
58 | "operator": parse_operator,
59 | "prefix_operator_exp": parse_prefix_operator_exp,
60 | "prefix_operator": parse_prefix_operator,
61 | "_": parse__,
62 | "EOF": parse_EOF
63 | };
64 |
65 | if (startRule !== undefined) {
66 | if (parseFunctions[startRule] === undefined) {
67 | throw new Error("Invalid rule name: " + quote(startRule) + ".");
68 | }
69 | } else {
70 | startRule = "start";
71 | }
72 |
73 | var pos = 0;
74 | var reportFailures = 0;
75 | var rightmostFailuresPos = 0;
76 | var rightmostFailuresExpected = [];
77 |
78 | function padLeft(input, padding, length) {
79 | var result = input;
80 |
81 | var padLength = length - input.length;
82 | for (var i = 0; i < padLength; i++) {
83 | result = padding + result;
84 | }
85 |
86 | return result;
87 | }
88 |
89 | function escape(ch) {
90 | var charCode = ch.charCodeAt(0);
91 | var escapeChar;
92 | var length;
93 |
94 | if (charCode <= 0xFF) {
95 | escapeChar = 'x';
96 | length = 2;
97 | } else {
98 | escapeChar = 'u';
99 | length = 4;
100 | }
101 |
102 | return '\\' + escapeChar + padLeft(charCode.toString(16).toUpperCase(), '0', length);
103 | }
104 |
105 | function matchFailed(failure) {
106 | if (pos < rightmostFailuresPos) {
107 | return;
108 | }
109 |
110 | if (pos > rightmostFailuresPos) {
111 | rightmostFailuresPos = pos;
112 | rightmostFailuresExpected = [];
113 | }
114 |
115 | rightmostFailuresExpected.push(failure);
116 | }
117 |
118 | function parse_start() {
119 | var result0, result1, result2;
120 | var pos0, pos1;
121 |
122 | pos0 = pos;
123 | pos1 = pos;
124 | result0 = [];
125 | result1 = parse__();
126 | while (result1 !== null) {
127 | result0.push(result1);
128 | result1 = parse__();
129 | }
130 | if (result0 !== null) {
131 | result2 = parse_node();
132 | if (result2 !== null) {
133 | result1 = [];
134 | while (result2 !== null) {
135 | result1.push(result2);
136 | result2 = parse_node();
137 | }
138 | } else {
139 | result1 = null;
140 | }
141 | if (result1 !== null) {
142 | result0 = [result0, result1];
143 | } else {
144 | result0 = null;
145 | pos = pos1;
146 | }
147 | } else {
148 | result0 = null;
149 | pos = pos1;
150 | }
151 | if (result0 !== null) {
152 | result0 = (function(offset, node) {
153 | return node[0];
154 | })(pos0, result0[1]);
155 | }
156 | if (result0 === null) {
157 | pos = pos0;
158 | }
159 | if (result0 === null) {
160 | pos0 = pos;
161 | result0 = [];
162 | result1 = parse__();
163 | while (result1 !== null) {
164 | result0.push(result1);
165 | result1 = parse__();
166 | }
167 | if (result0 !== null) {
168 | result0 = (function(offset) {
169 | return {};
170 | })(pos0);
171 | }
172 | if (result0 === null) {
173 | pos = pos0;
174 | }
175 | if (result0 === null) {
176 | pos0 = pos;
177 | result0 = parse_EOF();
178 | if (result0 !== null) {
179 | result0 = (function(offset) {
180 | return {};
181 | })(pos0);
182 | }
183 | if (result0 === null) {
184 | pos = pos0;
185 | }
186 | }
187 | }
188 | return result0;
189 | }
190 |
191 | function parse_node() {
192 | var result0, result1, result2, result3;
193 | var pos0, pos1;
194 |
195 | pos0 = pos;
196 | pos1 = pos;
197 | result0 = parse_operator_exp();
198 | if (result0 !== null) {
199 | result1 = parse_EOF();
200 | if (result1 !== null) {
201 | result0 = [result0, result1];
202 | } else {
203 | result0 = null;
204 | pos = pos1;
205 | }
206 | } else {
207 | result0 = null;
208 | pos = pos1;
209 | }
210 | if (result0 !== null) {
211 | result0 = (function(offset, operator) {
212 | return {
213 | 'operator': operator
214 | };
215 | })(pos0, result0[0]);
216 | }
217 | if (result0 === null) {
218 | pos = pos0;
219 | }
220 | if (result0 === null) {
221 | pos0 = pos;
222 | pos1 = pos;
223 | result0 = parse_operator_exp();
224 | if (result0 !== null) {
225 | result1 = parse_node();
226 | if (result1 !== null) {
227 | result0 = [result0, result1];
228 | } else {
229 | result0 = null;
230 | pos = pos1;
231 | }
232 | } else {
233 | result0 = null;
234 | pos = pos1;
235 | }
236 | if (result0 !== null) {
237 | result0 = (function(offset, operator, right) {
238 | return right;
239 | })(pos0, result0[0], result0[1]);
240 | }
241 | if (result0 === null) {
242 | pos = pos0;
243 | }
244 | if (result0 === null) {
245 | pos0 = pos;
246 | pos1 = pos;
247 | result0 = parse_group_exp();
248 | if (result0 !== null) {
249 | result1 = [];
250 | result2 = parse_operator_exp();
251 | while (result2 !== null) {
252 | result1.push(result2);
253 | result2 = parse_operator_exp();
254 | }
255 | if (result1 !== null) {
256 | result2 = [];
257 | result3 = parse_node();
258 | while (result3 !== null) {
259 | result2.push(result3);
260 | result3 = parse_node();
261 | }
262 | if (result2 !== null) {
263 | result0 = [result0, result1, result2];
264 | } else {
265 | result0 = null;
266 | pos = pos1;
267 | }
268 | } else {
269 | result0 = null;
270 | pos = pos1;
271 | }
272 | } else {
273 | result0 = null;
274 | pos = pos1;
275 | }
276 | if (result0 !== null) {
277 | result0 = (function(offset, left, operator, right) {
278 | var node= {
279 | 'left':left
280 | };
281 |
282 | var right =
283 | right.length == 0
284 | ? null
285 | : right[0]['right'] == null
286 | ? right[0]['left']
287 | : right[0];
288 |
289 | if (right != null)
290 | {
291 | node['operator'] = operator==''? '' : operator[0];
292 | node['right'] = right;
293 | }
294 |
295 | return node;
296 | })(pos0, result0[0], result0[1], result0[2]);
297 | }
298 | if (result0 === null) {
299 | pos = pos0;
300 | }
301 | }
302 | }
303 | return result0;
304 | }
305 |
306 | function parse_group_exp() {
307 | var result0, result1, result2;
308 | var pos0, pos1;
309 |
310 | pos0 = pos;
311 | pos1 = pos;
312 | result0 = parse_field_exp();
313 | if (result0 !== null) {
314 | result1 = [];
315 | result2 = parse__();
316 | while (result2 !== null) {
317 | result1.push(result2);
318 | result2 = parse__();
319 | }
320 | if (result1 !== null) {
321 | result0 = [result0, result1];
322 | } else {
323 | result0 = null;
324 | pos = pos1;
325 | }
326 | } else {
327 | result0 = null;
328 | pos = pos1;
329 | }
330 | if (result0 !== null) {
331 | result0 = (function(offset, field_exp) {
332 | return field_exp;
333 | })(pos0, result0[0]);
334 | }
335 | if (result0 === null) {
336 | pos = pos0;
337 | }
338 | if (result0 === null) {
339 | result0 = parse_paren_exp();
340 | }
341 | return result0;
342 | }
343 |
344 | function parse_paren_exp() {
345 | var result0, result1, result2, result3, result4;
346 | var pos0, pos1;
347 |
348 | pos0 = pos;
349 | pos1 = pos;
350 | if (input.charCodeAt(pos) === 40) {
351 | result0 = "(";
352 | pos++;
353 | } else {
354 | result0 = null;
355 | if (reportFailures === 0) {
356 | matchFailed("\"(\"");
357 | }
358 | }
359 | if (result0 !== null) {
360 | result2 = parse_node();
361 | if (result2 !== null) {
362 | result1 = [];
363 | while (result2 !== null) {
364 | result1.push(result2);
365 | result2 = parse_node();
366 | }
367 | } else {
368 | result1 = null;
369 | }
370 | if (result1 !== null) {
371 | if (input.charCodeAt(pos) === 41) {
372 | result2 = ")";
373 | pos++;
374 | } else {
375 | result2 = null;
376 | if (reportFailures === 0) {
377 | matchFailed("\")\"");
378 | }
379 | }
380 | if (result2 !== null) {
381 | result3 = [];
382 | result4 = parse__();
383 | while (result4 !== null) {
384 | result3.push(result4);
385 | result4 = parse__();
386 | }
387 | if (result3 !== null) {
388 | result0 = [result0, result1, result2, result3];
389 | } else {
390 | result0 = null;
391 | pos = pos1;
392 | }
393 | } else {
394 | result0 = null;
395 | pos = pos1;
396 | }
397 | } else {
398 | result0 = null;
399 | pos = pos1;
400 | }
401 | } else {
402 | result0 = null;
403 | pos = pos1;
404 | }
405 | if (result0 !== null) {
406 | result0 = (function(offset, node) {
407 | return node[0];
408 | })(pos0, result0[1]);
409 | }
410 | if (result0 === null) {
411 | pos = pos0;
412 | }
413 | return result0;
414 | }
415 |
416 | function parse_field_exp() {
417 | var result0, result1;
418 | var pos0, pos1;
419 |
420 | pos0 = pos;
421 | pos1 = pos;
422 | result0 = parse_fieldname();
423 | result0 = result0 !== null ? result0 : "";
424 | if (result0 !== null) {
425 | result1 = parse_range_operator_exp();
426 | if (result1 !== null) {
427 | result0 = [result0, result1];
428 | } else {
429 | result0 = null;
430 | pos = pos1;
431 | }
432 | } else {
433 | result0 = null;
434 | pos = pos1;
435 | }
436 | if (result0 !== null) {
437 | result0 = (function(offset, fieldname, range) {
438 | range['field'] =
439 | fieldname == ''
440 | ? ""
441 | : fieldname;
442 |
443 | return range;
444 | })(pos0, result0[0], result0[1]);
445 | }
446 | if (result0 === null) {
447 | pos = pos0;
448 | }
449 | if (result0 === null) {
450 | pos0 = pos;
451 | pos1 = pos;
452 | result0 = parse_fieldname();
453 | if (result0 !== null) {
454 | result1 = parse_paren_exp();
455 | if (result1 !== null) {
456 | result0 = [result0, result1];
457 | } else {
458 | result0 = null;
459 | pos = pos1;
460 | }
461 | } else {
462 | result0 = null;
463 | pos = pos1;
464 | }
465 | if (result0 !== null) {
466 | result0 = (function(offset, fieldname, node) {
467 | node['field']= fieldname;
468 | return node;
469 | })(pos0, result0[0], result0[1]);
470 | }
471 | if (result0 === null) {
472 | pos = pos0;
473 | }
474 | if (result0 === null) {
475 | pos0 = pos;
476 | pos1 = pos;
477 | result0 = parse_fieldname();
478 | result0 = result0 !== null ? result0 : "";
479 | if (result0 !== null) {
480 | result1 = parse_term();
481 | if (result1 !== null) {
482 | result0 = [result0, result1];
483 | } else {
484 | result0 = null;
485 | pos = pos1;
486 | }
487 | } else {
488 | result0 = null;
489 | pos = pos1;
490 | }
491 | if (result0 !== null) {
492 | result0 = (function(offset, fieldname, term) {
493 | var fieldexp = {
494 | 'field':
495 | fieldname == ''
496 | ? ""
497 | : fieldname
498 | };
499 |
500 | for(var key in term)
501 | fieldexp[key] = term[key];
502 |
503 | return fieldexp;
504 | })(pos0, result0[0], result0[1]);
505 | }
506 | if (result0 === null) {
507 | pos = pos0;
508 | }
509 | }
510 | }
511 | return result0;
512 | }
513 |
514 | function parse_fieldname() {
515 | var result0, result1;
516 | var pos0, pos1;
517 |
518 | pos0 = pos;
519 | pos1 = pos;
520 | result0 = parse_unquoted_term();
521 | if (result0 !== null) {
522 | if (/^[:]/.test(input.charAt(pos))) {
523 | result1 = input.charAt(pos);
524 | pos++;
525 | } else {
526 | result1 = null;
527 | if (reportFailures === 0) {
528 | matchFailed("[:]");
529 | }
530 | }
531 | if (result1 !== null) {
532 | result0 = [result0, result1];
533 | } else {
534 | result0 = null;
535 | pos = pos1;
536 | }
537 | } else {
538 | result0 = null;
539 | pos = pos1;
540 | }
541 | if (result0 !== null) {
542 | result0 = (function(offset, fieldname) {
543 | return fieldname;
544 | })(pos0, result0[0]);
545 | }
546 | if (result0 === null) {
547 | pos = pos0;
548 | }
549 | return result0;
550 | }
551 |
552 | function parse_term() {
553 | var result0, result1, result2, result3, result4, result5;
554 | var pos0, pos1;
555 |
556 | pos0 = pos;
557 | pos1 = pos;
558 | result0 = parse_prefix_operator_exp();
559 | result0 = result0 !== null ? result0 : "";
560 | if (result0 !== null) {
561 | result1 = parse_quoted_term();
562 | if (result1 !== null) {
563 | result2 = parse_proximity_modifier();
564 | result2 = result2 !== null ? result2 : "";
565 | if (result2 !== null) {
566 | result3 = parse_boost_modifier();
567 | result3 = result3 !== null ? result3 : "";
568 | if (result3 !== null) {
569 | result4 = [];
570 | result5 = parse__();
571 | while (result5 !== null) {
572 | result4.push(result5);
573 | result5 = parse__();
574 | }
575 | if (result4 !== null) {
576 | result0 = [result0, result1, result2, result3, result4];
577 | } else {
578 | result0 = null;
579 | pos = pos1;
580 | }
581 | } else {
582 | result0 = null;
583 | pos = pos1;
584 | }
585 | } else {
586 | result0 = null;
587 | pos = pos1;
588 | }
589 | } else {
590 | result0 = null;
591 | pos = pos1;
592 | }
593 | } else {
594 | result0 = null;
595 | pos = pos1;
596 | }
597 | if (result0 !== null) {
598 | result0 = (function(offset, op, term, proximity, boost) {
599 | var result = { 'term': term };
600 |
601 | if('' != proximity)
602 | {
603 | result['proximity'] = proximity;
604 | }
605 | if('' != boost)
606 | {
607 | result['boost'] = boost;
608 | }
609 | if('' != op)
610 | {
611 | result['prefix'] = op;
612 | }
613 |
614 | return result;
615 | })(pos0, result0[0], result0[1], result0[2], result0[3]);
616 | }
617 | if (result0 === null) {
618 | pos = pos0;
619 | }
620 | if (result0 === null) {
621 | pos0 = pos;
622 | pos1 = pos;
623 | result0 = parse_prefix_operator_exp();
624 | result0 = result0 !== null ? result0 : "";
625 | if (result0 !== null) {
626 | result1 = parse_unquoted_term();
627 | if (result1 !== null) {
628 | result2 = parse_fuzzy_modifier();
629 | result2 = result2 !== null ? result2 : "";
630 | if (result2 !== null) {
631 | result3 = parse_boost_modifier();
632 | result3 = result3 !== null ? result3 : "";
633 | if (result3 !== null) {
634 | result4 = [];
635 | result5 = parse__();
636 | while (result5 !== null) {
637 | result4.push(result5);
638 | result5 = parse__();
639 | }
640 | if (result4 !== null) {
641 | result0 = [result0, result1, result2, result3, result4];
642 | } else {
643 | result0 = null;
644 | pos = pos1;
645 | }
646 | } else {
647 | result0 = null;
648 | pos = pos1;
649 | }
650 | } else {
651 | result0 = null;
652 | pos = pos1;
653 | }
654 | } else {
655 | result0 = null;
656 | pos = pos1;
657 | }
658 | } else {
659 | result0 = null;
660 | pos = pos1;
661 | }
662 | if (result0 !== null) {
663 | result0 = (function(offset, op, term, similarity, boost) {
664 | var result = { 'term': term };
665 | if('' != similarity)
666 | {
667 | result['similarity'] = similarity;
668 | }
669 | if('' != boost)
670 | {
671 | result['boost'] = boost;
672 | }
673 | if('' != op)
674 | {
675 | result['prefix'] = op;
676 | }
677 | return result;
678 | })(pos0, result0[0], result0[1], result0[2], result0[3]);
679 | }
680 | if (result0 === null) {
681 | pos = pos0;
682 | }
683 | }
684 | return result0;
685 | }
686 |
687 | function parse_unquoted_term() {
688 | var result0, result1;
689 | var pos0;
690 |
691 | pos0 = pos;
692 | result1 = parse_term_char();
693 | if (result1 !== null) {
694 | result0 = [];
695 | while (result1 !== null) {
696 | result0.push(result1);
697 | result1 = parse_term_char();
698 | }
699 | } else {
700 | result0 = null;
701 | }
702 | if (result0 !== null) {
703 | result0 = (function(offset, term) {
704 | return term.join('');
705 | })(pos0, result0);
706 | }
707 | if (result0 === null) {
708 | pos = pos0;
709 | }
710 | return result0;
711 | }
712 |
713 | function parse_term_char() {
714 | var result0;
715 |
716 | if (input.charCodeAt(pos) === 46) {
717 | result0 = ".";
718 | pos++;
719 | } else {
720 | result0 = null;
721 | if (reportFailures === 0) {
722 | matchFailed("\".\"");
723 | }
724 | }
725 | if (result0 === null) {
726 | if (/^[^: \t\r\n\f{}()"+-\/^~[\]]/.test(input.charAt(pos))) {
727 | result0 = input.charAt(pos);
728 | pos++;
729 | } else {
730 | result0 = null;
731 | if (reportFailures === 0) {
732 | matchFailed("[^: \\t\\r\\n\\f{}()\"+-\\/^~[\\]]");
733 | }
734 | }
735 | }
736 | return result0;
737 | }
738 |
739 | function parse_quoted_term() {
740 | var result0, result1, result2;
741 | var pos0, pos1;
742 |
743 | pos0 = pos;
744 | pos1 = pos;
745 | if (input.charCodeAt(pos) === 34) {
746 | result0 = "\"";
747 | pos++;
748 | } else {
749 | result0 = null;
750 | if (reportFailures === 0) {
751 | matchFailed("\"\\\"\"");
752 | }
753 | }
754 | if (result0 !== null) {
755 | if (/^[^"]/.test(input.charAt(pos))) {
756 | result2 = input.charAt(pos);
757 | pos++;
758 | } else {
759 | result2 = null;
760 | if (reportFailures === 0) {
761 | matchFailed("[^\"]");
762 | }
763 | }
764 | if (result2 !== null) {
765 | result1 = [];
766 | while (result2 !== null) {
767 | result1.push(result2);
768 | if (/^[^"]/.test(input.charAt(pos))) {
769 | result2 = input.charAt(pos);
770 | pos++;
771 | } else {
772 | result2 = null;
773 | if (reportFailures === 0) {
774 | matchFailed("[^\"]");
775 | }
776 | }
777 | }
778 | } else {
779 | result1 = null;
780 | }
781 | if (result1 !== null) {
782 | if (input.charCodeAt(pos) === 34) {
783 | result2 = "\"";
784 | pos++;
785 | } else {
786 | result2 = null;
787 | if (reportFailures === 0) {
788 | matchFailed("\"\\\"\"");
789 | }
790 | }
791 | if (result2 !== null) {
792 | result0 = [result0, result1, result2];
793 | } else {
794 | result0 = null;
795 | pos = pos1;
796 | }
797 | } else {
798 | result0 = null;
799 | pos = pos1;
800 | }
801 | } else {
802 | result0 = null;
803 | pos = pos1;
804 | }
805 | if (result0 !== null) {
806 | result0 = (function(offset, term) {
807 | return term.join('');
808 | })(pos0, result0[1]);
809 | }
810 | if (result0 === null) {
811 | pos = pos0;
812 | }
813 | return result0;
814 | }
815 |
816 | function parse_proximity_modifier() {
817 | var result0, result1;
818 | var pos0, pos1;
819 |
820 | pos0 = pos;
821 | pos1 = pos;
822 | if (input.charCodeAt(pos) === 126) {
823 | result0 = "~";
824 | pos++;
825 | } else {
826 | result0 = null;
827 | if (reportFailures === 0) {
828 | matchFailed("\"~\"");
829 | }
830 | }
831 | if (result0 !== null) {
832 | result1 = parse_int_exp();
833 | if (result1 !== null) {
834 | result0 = [result0, result1];
835 | } else {
836 | result0 = null;
837 | pos = pos1;
838 | }
839 | } else {
840 | result0 = null;
841 | pos = pos1;
842 | }
843 | if (result0 !== null) {
844 | result0 = (function(offset, proximity) {
845 | return proximity;
846 | })(pos0, result0[1]);
847 | }
848 | if (result0 === null) {
849 | pos = pos0;
850 | }
851 | return result0;
852 | }
853 |
854 | function parse_boost_modifier() {
855 | var result0, result1;
856 | var pos0, pos1;
857 |
858 | pos0 = pos;
859 | pos1 = pos;
860 | if (input.charCodeAt(pos) === 94) {
861 | result0 = "^";
862 | pos++;
863 | } else {
864 | result0 = null;
865 | if (reportFailures === 0) {
866 | matchFailed("\"^\"");
867 | }
868 | }
869 | if (result0 !== null) {
870 | result1 = parse_decimal_or_int_exp();
871 | if (result1 !== null) {
872 | result0 = [result0, result1];
873 | } else {
874 | result0 = null;
875 | pos = pos1;
876 | }
877 | } else {
878 | result0 = null;
879 | pos = pos1;
880 | }
881 | if (result0 !== null) {
882 | result0 = (function(offset, boost) {
883 | return boost;
884 | })(pos0, result0[1]);
885 | }
886 | if (result0 === null) {
887 | pos = pos0;
888 | }
889 | return result0;
890 | }
891 |
892 | function parse_fuzzy_modifier() {
893 | var result0, result1;
894 | var pos0, pos1;
895 |
896 | pos0 = pos;
897 | pos1 = pos;
898 | if (input.charCodeAt(pos) === 126) {
899 | result0 = "~";
900 | pos++;
901 | } else {
902 | result0 = null;
903 | if (reportFailures === 0) {
904 | matchFailed("\"~\"");
905 | }
906 | }
907 | if (result0 !== null) {
908 | result1 = parse_decimal_exp();
909 | result1 = result1 !== null ? result1 : "";
910 | if (result1 !== null) {
911 | result0 = [result0, result1];
912 | } else {
913 | result0 = null;
914 | pos = pos1;
915 | }
916 | } else {
917 | result0 = null;
918 | pos = pos1;
919 | }
920 | if (result0 !== null) {
921 | result0 = (function(offset, fuzziness) {
922 | return fuzziness == '' ? 0.5 : fuzziness;
923 | })(pos0, result0[1]);
924 | }
925 | if (result0 === null) {
926 | pos = pos0;
927 | }
928 | return result0;
929 | }
930 |
931 | function parse_decimal_or_int_exp() {
932 | var result0;
933 |
934 | result0 = parse_decimal_exp();
935 | if (result0 === null) {
936 | result0 = parse_int_exp();
937 | }
938 | return result0;
939 | }
940 |
941 | function parse_decimal_exp() {
942 | var result0, result1, result2;
943 | var pos0, pos1;
944 |
945 | pos0 = pos;
946 | pos1 = pos;
947 | if (input.substr(pos, 2) === "0.") {
948 | result0 = "0.";
949 | pos += 2;
950 | } else {
951 | result0 = null;
952 | if (reportFailures === 0) {
953 | matchFailed("\"0.\"");
954 | }
955 | }
956 | if (result0 !== null) {
957 | if (/^[0-9]/.test(input.charAt(pos))) {
958 | result2 = input.charAt(pos);
959 | pos++;
960 | } else {
961 | result2 = null;
962 | if (reportFailures === 0) {
963 | matchFailed("[0-9]");
964 | }
965 | }
966 | if (result2 !== null) {
967 | result1 = [];
968 | while (result2 !== null) {
969 | result1.push(result2);
970 | if (/^[0-9]/.test(input.charAt(pos))) {
971 | result2 = input.charAt(pos);
972 | pos++;
973 | } else {
974 | result2 = null;
975 | if (reportFailures === 0) {
976 | matchFailed("[0-9]");
977 | }
978 | }
979 | }
980 | } else {
981 | result1 = null;
982 | }
983 | if (result1 !== null) {
984 | result0 = [result0, result1];
985 | } else {
986 | result0 = null;
987 | pos = pos1;
988 | }
989 | } else {
990 | result0 = null;
991 | pos = pos1;
992 | }
993 | if (result0 !== null) {
994 | result0 = (function(offset, val) {
995 | return parseFloat("0." + val.join(''));
996 | })(pos0, result0[1]);
997 | }
998 | if (result0 === null) {
999 | pos = pos0;
1000 | }
1001 | return result0;
1002 | }
1003 |
1004 | function parse_int_exp() {
1005 | var result0, result1;
1006 | var pos0;
1007 |
1008 | pos0 = pos;
1009 | if (/^[0-9]/.test(input.charAt(pos))) {
1010 | result1 = input.charAt(pos);
1011 | pos++;
1012 | } else {
1013 | result1 = null;
1014 | if (reportFailures === 0) {
1015 | matchFailed("[0-9]");
1016 | }
1017 | }
1018 | if (result1 !== null) {
1019 | result0 = [];
1020 | while (result1 !== null) {
1021 | result0.push(result1);
1022 | if (/^[0-9]/.test(input.charAt(pos))) {
1023 | result1 = input.charAt(pos);
1024 | pos++;
1025 | } else {
1026 | result1 = null;
1027 | if (reportFailures === 0) {
1028 | matchFailed("[0-9]");
1029 | }
1030 | }
1031 | }
1032 | } else {
1033 | result0 = null;
1034 | }
1035 | if (result0 !== null) {
1036 | result0 = (function(offset, val) {
1037 | return parseInt(val.join(''));
1038 | })(pos0, result0);
1039 | }
1040 | if (result0 === null) {
1041 | pos = pos0;
1042 | }
1043 | return result0;
1044 | }
1045 |
1046 | function parse_range_operator_exp() {
1047 | var result0, result1, result2, result3, result4, result5, result6;
1048 | var pos0, pos1;
1049 |
1050 | pos0 = pos;
1051 | pos1 = pos;
1052 | if (input.charCodeAt(pos) === 91) {
1053 | result0 = "[";
1054 | pos++;
1055 | } else {
1056 | result0 = null;
1057 | if (reportFailures === 0) {
1058 | matchFailed("\"[\"");
1059 | }
1060 | }
1061 | if (result0 !== null) {
1062 | result1 = parse_unquoted_term();
1063 | if (result1 !== null) {
1064 | result2 = [];
1065 | result3 = parse__();
1066 | while (result3 !== null) {
1067 | result2.push(result3);
1068 | result3 = parse__();
1069 | }
1070 | if (result2 !== null) {
1071 | if (input.substr(pos, 2) === "TO") {
1072 | result3 = "TO";
1073 | pos += 2;
1074 | } else {
1075 | result3 = null;
1076 | if (reportFailures === 0) {
1077 | matchFailed("\"TO\"");
1078 | }
1079 | }
1080 | if (result3 !== null) {
1081 | result5 = parse__();
1082 | if (result5 !== null) {
1083 | result4 = [];
1084 | while (result5 !== null) {
1085 | result4.push(result5);
1086 | result5 = parse__();
1087 | }
1088 | } else {
1089 | result4 = null;
1090 | }
1091 | if (result4 !== null) {
1092 | result5 = parse_unquoted_term();
1093 | if (result5 !== null) {
1094 | if (input.charCodeAt(pos) === 93) {
1095 | result6 = "]";
1096 | pos++;
1097 | } else {
1098 | result6 = null;
1099 | if (reportFailures === 0) {
1100 | matchFailed("\"]\"");
1101 | }
1102 | }
1103 | if (result6 !== null) {
1104 | result0 = [result0, result1, result2, result3, result4, result5, result6];
1105 | } else {
1106 | result0 = null;
1107 | pos = pos1;
1108 | }
1109 | } else {
1110 | result0 = null;
1111 | pos = pos1;
1112 | }
1113 | } else {
1114 | result0 = null;
1115 | pos = pos1;
1116 | }
1117 | } else {
1118 | result0 = null;
1119 | pos = pos1;
1120 | }
1121 | } else {
1122 | result0 = null;
1123 | pos = pos1;
1124 | }
1125 | } else {
1126 | result0 = null;
1127 | pos = pos1;
1128 | }
1129 | } else {
1130 | result0 = null;
1131 | pos = pos1;
1132 | }
1133 | if (result0 !== null) {
1134 | result0 = (function(offset, term_min, term_max) {
1135 | return {
1136 | 'term_min': term_min,
1137 | 'term_max': term_max,
1138 | 'inclusive': true
1139 | };
1140 | })(pos0, result0[1], result0[5]);
1141 | }
1142 | if (result0 === null) {
1143 | pos = pos0;
1144 | }
1145 | if (result0 === null) {
1146 | pos0 = pos;
1147 | pos1 = pos;
1148 | if (input.charCodeAt(pos) === 123) {
1149 | result0 = "{";
1150 | pos++;
1151 | } else {
1152 | result0 = null;
1153 | if (reportFailures === 0) {
1154 | matchFailed("\"{\"");
1155 | }
1156 | }
1157 | if (result0 !== null) {
1158 | result1 = parse_unquoted_term();
1159 | if (result1 !== null) {
1160 | result2 = [];
1161 | result3 = parse__();
1162 | while (result3 !== null) {
1163 | result2.push(result3);
1164 | result3 = parse__();
1165 | }
1166 | if (result2 !== null) {
1167 | if (input.substr(pos, 2) === "TO") {
1168 | result3 = "TO";
1169 | pos += 2;
1170 | } else {
1171 | result3 = null;
1172 | if (reportFailures === 0) {
1173 | matchFailed("\"TO\"");
1174 | }
1175 | }
1176 | if (result3 !== null) {
1177 | result5 = parse__();
1178 | if (result5 !== null) {
1179 | result4 = [];
1180 | while (result5 !== null) {
1181 | result4.push(result5);
1182 | result5 = parse__();
1183 | }
1184 | } else {
1185 | result4 = null;
1186 | }
1187 | if (result4 !== null) {
1188 | result5 = parse_unquoted_term();
1189 | if (result5 !== null) {
1190 | if (input.charCodeAt(pos) === 125) {
1191 | result6 = "}";
1192 | pos++;
1193 | } else {
1194 | result6 = null;
1195 | if (reportFailures === 0) {
1196 | matchFailed("\"}\"");
1197 | }
1198 | }
1199 | if (result6 !== null) {
1200 | result0 = [result0, result1, result2, result3, result4, result5, result6];
1201 | } else {
1202 | result0 = null;
1203 | pos = pos1;
1204 | }
1205 | } else {
1206 | result0 = null;
1207 | pos = pos1;
1208 | }
1209 | } else {
1210 | result0 = null;
1211 | pos = pos1;
1212 | }
1213 | } else {
1214 | result0 = null;
1215 | pos = pos1;
1216 | }
1217 | } else {
1218 | result0 = null;
1219 | pos = pos1;
1220 | }
1221 | } else {
1222 | result0 = null;
1223 | pos = pos1;
1224 | }
1225 | } else {
1226 | result0 = null;
1227 | pos = pos1;
1228 | }
1229 | if (result0 !== null) {
1230 | result0 = (function(offset, term_min, term_max) {
1231 | return {
1232 | 'term_min': term_min,
1233 | 'term_max': term_max,
1234 | 'inclusive': false
1235 | };
1236 | })(pos0, result0[1], result0[5]);
1237 | }
1238 | if (result0 === null) {
1239 | pos = pos0;
1240 | }
1241 | }
1242 | return result0;
1243 | }
1244 |
1245 | function parse_operator_exp() {
1246 | var result0, result1, result2, result3;
1247 | var pos0, pos1;
1248 |
1249 | pos0 = pos;
1250 | pos1 = pos;
1251 | result0 = [];
1252 | result1 = parse__();
1253 | while (result1 !== null) {
1254 | result0.push(result1);
1255 | result1 = parse__();
1256 | }
1257 | if (result0 !== null) {
1258 | result1 = parse_operator();
1259 | if (result1 !== null) {
1260 | result3 = parse__();
1261 | if (result3 !== null) {
1262 | result2 = [];
1263 | while (result3 !== null) {
1264 | result2.push(result3);
1265 | result3 = parse__();
1266 | }
1267 | } else {
1268 | result2 = null;
1269 | }
1270 | if (result2 !== null) {
1271 | result0 = [result0, result1, result2];
1272 | } else {
1273 | result0 = null;
1274 | pos = pos1;
1275 | }
1276 | } else {
1277 | result0 = null;
1278 | pos = pos1;
1279 | }
1280 | } else {
1281 | result0 = null;
1282 | pos = pos1;
1283 | }
1284 | if (result0 !== null) {
1285 | result0 = (function(offset, operator) {
1286 | return operator;
1287 | })(pos0, result0[1]);
1288 | }
1289 | if (result0 === null) {
1290 | pos = pos0;
1291 | }
1292 | if (result0 === null) {
1293 | pos0 = pos;
1294 | pos1 = pos;
1295 | result0 = [];
1296 | result1 = parse__();
1297 | while (result1 !== null) {
1298 | result0.push(result1);
1299 | result1 = parse__();
1300 | }
1301 | if (result0 !== null) {
1302 | result1 = parse_operator();
1303 | if (result1 !== null) {
1304 | result2 = parse_EOF();
1305 | if (result2 !== null) {
1306 | result0 = [result0, result1, result2];
1307 | } else {
1308 | result0 = null;
1309 | pos = pos1;
1310 | }
1311 | } else {
1312 | result0 = null;
1313 | pos = pos1;
1314 | }
1315 | } else {
1316 | result0 = null;
1317 | pos = pos1;
1318 | }
1319 | if (result0 !== null) {
1320 | result0 = (function(offset, operator) {
1321 | return operator;
1322 | })(pos0, result0[1]);
1323 | }
1324 | if (result0 === null) {
1325 | pos = pos0;
1326 | }
1327 | }
1328 | return result0;
1329 | }
1330 |
1331 | function parse_operator() {
1332 | var result0;
1333 | var pos0;
1334 |
1335 | if (input.substr(pos, 2) === "OR") {
1336 | result0 = "OR";
1337 | pos += 2;
1338 | } else {
1339 | result0 = null;
1340 | if (reportFailures === 0) {
1341 | matchFailed("\"OR\"");
1342 | }
1343 | }
1344 | if (result0 === null) {
1345 | if (input.substr(pos, 3) === "AND") {
1346 | result0 = "AND";
1347 | pos += 3;
1348 | } else {
1349 | result0 = null;
1350 | if (reportFailures === 0) {
1351 | matchFailed("\"AND\"");
1352 | }
1353 | }
1354 | if (result0 === null) {
1355 | if (input.substr(pos, 3) === "NOT") {
1356 | result0 = "NOT";
1357 | pos += 3;
1358 | } else {
1359 | result0 = null;
1360 | if (reportFailures === 0) {
1361 | matchFailed("\"NOT\"");
1362 | }
1363 | }
1364 | if (result0 === null) {
1365 | pos0 = pos;
1366 | if (input.substr(pos, 2) === "||") {
1367 | result0 = "||";
1368 | pos += 2;
1369 | } else {
1370 | result0 = null;
1371 | if (reportFailures === 0) {
1372 | matchFailed("\"||\"");
1373 | }
1374 | }
1375 | if (result0 !== null) {
1376 | result0 = (function(offset) { return 'OR'; })(pos0);
1377 | }
1378 | if (result0 === null) {
1379 | pos = pos0;
1380 | }
1381 | if (result0 === null) {
1382 | pos0 = pos;
1383 | if (input.substr(pos, 2) === "&&") {
1384 | result0 = "&&";
1385 | pos += 2;
1386 | } else {
1387 | result0 = null;
1388 | if (reportFailures === 0) {
1389 | matchFailed("\"&&\"");
1390 | }
1391 | }
1392 | if (result0 !== null) {
1393 | result0 = (function(offset) { return 'AND'; })(pos0);
1394 | }
1395 | if (result0 === null) {
1396 | pos = pos0;
1397 | }
1398 | }
1399 | }
1400 | }
1401 | }
1402 | return result0;
1403 | }
1404 |
1405 | function parse_prefix_operator_exp() {
1406 | var result0, result1;
1407 | var pos0, pos1;
1408 |
1409 | pos0 = pos;
1410 | pos1 = pos;
1411 | result0 = [];
1412 | result1 = parse__();
1413 | while (result1 !== null) {
1414 | result0.push(result1);
1415 | result1 = parse__();
1416 | }
1417 | if (result0 !== null) {
1418 | result1 = parse_prefix_operator();
1419 | if (result1 !== null) {
1420 | result0 = [result0, result1];
1421 | } else {
1422 | result0 = null;
1423 | pos = pos1;
1424 | }
1425 | } else {
1426 | result0 = null;
1427 | pos = pos1;
1428 | }
1429 | if (result0 !== null) {
1430 | result0 = (function(offset, operator) {
1431 | return operator;
1432 | })(pos0, result0[1]);
1433 | }
1434 | if (result0 === null) {
1435 | pos = pos0;
1436 | }
1437 | return result0;
1438 | }
1439 |
1440 | function parse_prefix_operator() {
1441 | var result0;
1442 |
1443 | if (input.charCodeAt(pos) === 43) {
1444 | result0 = "+";
1445 | pos++;
1446 | } else {
1447 | result0 = null;
1448 | if (reportFailures === 0) {
1449 | matchFailed("\"+\"");
1450 | }
1451 | }
1452 | if (result0 === null) {
1453 | if (input.charCodeAt(pos) === 45) {
1454 | result0 = "-";
1455 | pos++;
1456 | } else {
1457 | result0 = null;
1458 | if (reportFailures === 0) {
1459 | matchFailed("\"-\"");
1460 | }
1461 | }
1462 | }
1463 | return result0;
1464 | }
1465 |
1466 | function parse__() {
1467 | var result0, result1;
1468 |
1469 | reportFailures++;
1470 | if (/^[ \t\r\n\f]/.test(input.charAt(pos))) {
1471 | result1 = input.charAt(pos);
1472 | pos++;
1473 | } else {
1474 | result1 = null;
1475 | if (reportFailures === 0) {
1476 | matchFailed("[ \\t\\r\\n\\f]");
1477 | }
1478 | }
1479 | if (result1 !== null) {
1480 | result0 = [];
1481 | while (result1 !== null) {
1482 | result0.push(result1);
1483 | if (/^[ \t\r\n\f]/.test(input.charAt(pos))) {
1484 | result1 = input.charAt(pos);
1485 | pos++;
1486 | } else {
1487 | result1 = null;
1488 | if (reportFailures === 0) {
1489 | matchFailed("[ \\t\\r\\n\\f]");
1490 | }
1491 | }
1492 | }
1493 | } else {
1494 | result0 = null;
1495 | }
1496 | reportFailures--;
1497 | if (reportFailures === 0 && result0 === null) {
1498 | matchFailed("whitespace");
1499 | }
1500 | return result0;
1501 | }
1502 |
1503 | function parse_EOF() {
1504 | var result0;
1505 | var pos0;
1506 |
1507 | pos0 = pos;
1508 | reportFailures++;
1509 | if (input.length > pos) {
1510 | result0 = input.charAt(pos);
1511 | pos++;
1512 | } else {
1513 | result0 = null;
1514 | if (reportFailures === 0) {
1515 | matchFailed("any character");
1516 | }
1517 | }
1518 | reportFailures--;
1519 | if (result0 === null) {
1520 | result0 = "";
1521 | } else {
1522 | result0 = null;
1523 | pos = pos0;
1524 | }
1525 | return result0;
1526 | }
1527 |
1528 |
1529 | function cleanupExpected(expected) {
1530 | expected.sort();
1531 |
1532 | var lastExpected = null;
1533 | var cleanExpected = [];
1534 | for (var i = 0; i < expected.length; i++) {
1535 | if (expected[i] !== lastExpected) {
1536 | cleanExpected.push(expected[i]);
1537 | lastExpected = expected[i];
1538 | }
1539 | }
1540 | return cleanExpected;
1541 | }
1542 |
1543 | function computeErrorPosition() {
1544 | /*
1545 | * The first idea was to use |String.split| to break the input up to the
1546 | * error position along newlines and derive the line and column from
1547 | * there. However IE's |split| implementation is so broken that it was
1548 | * enough to prevent it.
1549 | */
1550 |
1551 | var line = 1;
1552 | var column = 1;
1553 | var seenCR = false;
1554 |
1555 | for (var i = 0; i < Math.max(pos, rightmostFailuresPos); i++) {
1556 | var ch = input.charAt(i);
1557 | if (ch === "\n") {
1558 | if (!seenCR) { line++; }
1559 | column = 1;
1560 | seenCR = false;
1561 | } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") {
1562 | line++;
1563 | column = 1;
1564 | seenCR = true;
1565 | } else {
1566 | column++;
1567 | seenCR = false;
1568 | }
1569 | }
1570 |
1571 | return { line: line, column: column };
1572 | }
1573 |
1574 |
1575 | var result = parseFunctions[startRule]();
1576 |
1577 | /*
1578 | * The parser is now in one of the following three states:
1579 | *
1580 | * 1. The parser successfully parsed the whole input.
1581 | *
1582 | * - |result !== null|
1583 | * - |pos === input.length|
1584 | * - |rightmostFailuresExpected| may or may not contain something
1585 | *
1586 | * 2. The parser successfully parsed only a part of the input.
1587 | *
1588 | * - |result !== null|
1589 | * - |pos < input.length|
1590 | * - |rightmostFailuresExpected| may or may not contain something
1591 | *
1592 | * 3. The parser did not successfully parse any part of the input.
1593 | *
1594 | * - |result === null|
1595 | * - |pos === 0|
1596 | * - |rightmostFailuresExpected| contains at least one failure
1597 | *
1598 | * All code following this comment (including called functions) must
1599 | * handle these states.
1600 | */
1601 | if (result === null || pos !== input.length) {
1602 | var offset = Math.max(pos, rightmostFailuresPos);
1603 | var found = offset < input.length ? input.charAt(offset) : null;
1604 | var errorPosition = computeErrorPosition();
1605 |
1606 | throw new this.SyntaxError(
1607 | cleanupExpected(rightmostFailuresExpected),
1608 | found,
1609 | offset,
1610 | errorPosition.line,
1611 | errorPosition.column
1612 | );
1613 | }
1614 |
1615 | return result;
1616 | },
1617 |
1618 | /* Returns the parser source code. */
1619 | toSource: function() { return this._source; }
1620 | };
1621 |
1622 | /* Thrown when a parser encounters a syntax error. */
1623 |
1624 | result.SyntaxError = function(expected, found, offset, line, column) {
1625 | function buildMessage(expected, found) {
1626 | var expectedHumanized, foundHumanized;
1627 |
1628 | switch (expected.length) {
1629 | case 0:
1630 | expectedHumanized = "end of input";
1631 | break;
1632 | case 1:
1633 | expectedHumanized = expected[0];
1634 | break;
1635 | default:
1636 | expectedHumanized = expected.slice(0, expected.length - 1).join(", ")
1637 | + " or "
1638 | + expected[expected.length - 1];
1639 | }
1640 |
1641 | foundHumanized = found ? quote(found) : "end of input";
1642 |
1643 | return "Expected " + expectedHumanized + " but " + foundHumanized + " found.";
1644 | }
1645 |
1646 | this.name = "SyntaxError";
1647 | this.expected = expected;
1648 | this.found = found;
1649 | this.message = buildMessage(expected, found);
1650 | this.offset = offset;
1651 | this.line = line;
1652 | this.column = column;
1653 | };
1654 |
1655 | result.SyntaxError.prototype = Error.prototype;
1656 |
1657 | return result;
1658 | })();
1659 |
--------------------------------------------------------------------------------