| 54 | // every length, which backtracks across whitespace runs and is O(n²) — a |
| 55 | // small padded query could freeze the event loop for tens of seconds. |
| 56 | function parseBlock(all, block, qs) { |
| 57 | if (block.length === 0) return |
| 58 | |
| 59 | var len = block.length |
| 60 | var clauseStart = 0 |
| 61 | // Compose for the clause currently being read; the leftmost clause is `or`. |
| 62 | var compose = 'or' |
| 63 | var i = 0 |
| 64 | |
| 65 | while (i < len) { |
| 66 | var ch = block[i] |
| 67 | |
| 68 | if (ch === ',') { |
| 69 | // `,\s*` delimiter. A delimiter at the very start (i === 0) has no left |
| 70 | // clause — the original never emits one there. |
| 71 | if (i !== 0) pushClause(all, qs, block.slice(clauseStart, i), compose) |
| 72 | i++ |
| 73 | while (i < len && SPACE.test(block[i])) i++ |
| 74 | compose = 'or' |
| 75 | clauseStart = i |
| 76 | continue |
| 77 | } |
| 78 | |
| 79 | if (SPACE.test(ch)) { |
| 80 | // Possible `\s+and\s+` or `\s+or\s+`. Scan the whitespace run once |
| 81 | // (linear, no backtracking), then check the following keyword. |
| 82 | var q = i |
| 83 | while (q < len && SPACE.test(block[q])) q++ |
| 84 | |
| 85 | if ( |
| 86 | q + 3 < len && |
| 87 | (block[q] === 'a' || block[q] === 'A') && |
| 88 | (block[q + 1] === 'n' || block[q + 1] === 'N') && |
| 89 | (block[q + 2] === 'd' || block[q + 2] === 'D') && |
| 90 | SPACE.test(block[q + 3]) |
| 91 | ) { |
| 92 | // The leading `\s+` of `\s+and\s+` absorbs whitespace at the block |
| 93 | // start, so a delimiter at i === 0 has no left clause. |
| 94 | if (i !== 0) pushClause(all, qs, block.slice(clauseStart, i), compose) |
| 95 | var afterAnd = q + 3 |
| 96 | while (afterAnd < len && SPACE.test(block[afterAnd])) afterAnd++ |
| 97 | compose = 'and' |
| 98 | i = afterAnd |
| 99 | clauseStart = afterAnd |
| 100 | continue |
| 101 | } else if ( |
| 102 | q + 2 < len && |
| 103 | (block[q] === 'o' || block[q] === 'O') && |
| 104 | (block[q + 1] === 'r' || block[q + 1] === 'R') && |
| 105 | SPACE.test(block[q + 2]) |
| 106 | ) { |
| 107 | if (i !== 0) pushClause(all, qs, block.slice(clauseStart, i), compose) |
| 108 | var afterOr = q + 2 |
| 109 | while (afterOr < len && SPACE.test(block[afterOr])) afterOr++ |
| 110 | compose = 'or' |
| 111 | i = afterOr |
| 112 | clauseStart = afterOr |
| 113 | continue |