├── .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 | --------------------------------------------------------------------------------