| var SPACE = /\s/ |
|
|
| function flatten(array) { |
| if (!Array.isArray(array)) return [array] |
| |
| |
| var result = [] |
| var stack = [array] |
| while (stack.length) { |
| var item = stack.pop() |
| if (Array.isArray(item)) { |
| for (var i = item.length - 1; i >= 0; i--) { |
| stack.push(item[i]) |
| } |
| } else { |
| result.push(item) |
| } |
| } |
| return result |
| } |
|
|
| function matchQuery(all, query) { |
| var node = { query: query } |
| if (query.indexOf('not ') === 0) { |
| node.not = true |
| query = query.slice(4) |
| } |
|
|
| for (var name in all) { |
| var type = all[name] |
| var match = query.match(type.regexp) |
| if (match) { |
| node.type = name |
| for (var i = 0; i < type.matches.length; i++) { |
| node[type.matches[i]] = match[i + 1] |
| } |
| return node |
| } |
| } |
|
|
| node.type = 'unknown' |
| return node |
| } |
|
|
| function pushClause(all, qs, text, compose) { |
| var node = matchQuery(all, text.trim()) |
| node.compose = compose |
| qs.push(node) |
| } |
|
|
| |
| |
| |
| |
| |
| function parseBlock(all, block, qs) { |
| if (block.length === 0) return |
|
|
| var len = block.length |
| var clauseStart = 0 |
| |
| var compose = 'or' |
| var i = 0 |
|
|
| while (i < len) { |
| var ch = block[i] |
|
|
| if (ch === ',') { |
| |
| |
| if (i !== 0) pushClause(all, qs, block.slice(clauseStart, i), compose) |
| i++ |
| while (i < len && SPACE.test(block[i])) i++ |
| compose = 'or' |
| clauseStart = i |
| continue |
| } |
|
|
| if (SPACE.test(ch)) { |
| |
| |
| var q = i |
| while (q < len && SPACE.test(block[q])) q++ |
|
|
| if ( |
| q + 3 < len && |
| (block[q] === 'a' || block[q] === 'A') && |
| (block[q + 1] === 'n' || block[q + 1] === 'N') && |
| (block[q + 2] === 'd' || block[q + 2] === 'D') && |
| SPACE.test(block[q + 3]) |
| ) { |
| |
| |
| if (i !== 0) pushClause(all, qs, block.slice(clauseStart, i), compose) |
| var afterAnd = q + 3 |
| while (afterAnd < len && SPACE.test(block[afterAnd])) afterAnd++ |
| compose = 'and' |
| i = afterAnd |
| clauseStart = afterAnd |
| continue |
| } else if ( |
| q + 2 < len && |
| (block[q] === 'o' || block[q] === 'O') && |
| (block[q + 1] === 'r' || block[q + 1] === 'R') && |
| SPACE.test(block[q + 2]) |
| ) { |
| if (i !== 0) pushClause(all, qs, block.slice(clauseStart, i), compose) |
| var afterOr = q + 2 |
| while (afterOr < len && SPACE.test(block[afterOr])) afterOr++ |
| compose = 'or' |
| i = afterOr |
| clauseStart = afterOr |
| continue |
| } else { |
| |
| i = q |
| continue |
| } |
| } |
|
|
| i++ |
| } |
|
|
| pushClause(all, qs, block.slice(clauseStart), compose) |
| } |
|
|
| module.exports = function parse(all, queries) { |
| if (!Array.isArray(queries)) queries = [queries] |
| return flatten( |
| queries.map(function (block) { |
| var qs = [] |
| parseBlock(all, block, qs) |
| return qs |
| }) |
| ) |
| } |
|
|