(tokens)
| 175 | |
| 176 | // Given a token list e.g. ['2', '>', '1'], returns a function that evaluates that token list. |
| 177 | function buildExprTree(tokens) { |
| 178 | // Consume tokens array into a function tree until the tokens array is exhausted |
| 179 | // to a single root node that evaluates it. |
| 180 | while (tokens.length > 1 || typeof tokens[0] != 'function') { |
| 181 | tokens = ((tokens) => { |
| 182 | // Find the index 'i' of the operator we should evaluate next: |
| 183 | var i, j, p, operatorAndPriority = -2; |
| 184 | for (j = 0; j < tokens.length; ++j) { |
| 185 | if ((p = ['*', '/', '+', '-', '!', '<', '<=', '>', '>=', '==', '!=', '&&', '||', '('].indexOf(tokens[j])) > operatorAndPriority) { |
| 186 | i = j; |
| 187 | operatorAndPriority = p; |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | if (operatorAndPriority == 13 /* parens '(' */) { |
| 192 | // Find the closing parens position |
| 193 | j = find_closing_parens_index(tokens, i); |
| 194 | if (j) { |
| 195 | tokens.splice(i, j+1-i, buildExprTree(tokens.slice(i+1, j))); |
| 196 | return tokens; |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | if (operatorAndPriority == 4 /* unary ! */) { |
| 201 | // Special case: the unary operator ! needs to evaluate right-to-left. |
| 202 | i = tokens.lastIndexOf('!'); |
| 203 | var innerExpr = buildExprTree(tokens.slice(i+1, i+2)); |
| 204 | tokens.splice(i, 2, function() { return !innerExpr(); }) |
| 205 | return tokens; |
| 206 | } |
| 207 | |
| 208 | // A binary operator: |
| 209 | if (operatorAndPriority >= 0) { |
| 210 | var left = buildExprTree(tokens.slice(0, i)); |
| 211 | var right = buildExprTree(tokens.slice(i+1)); |
| 212 | var opers = { |
| 213 | '&&': () => left() && right(), |
| 214 | '||': () => left() || right(), |
| 215 | '==': () => left() == right(), |
| 216 | '!=': () => left() != right(), |
| 217 | '<' : () => left() < right(), |
| 218 | '<=': () => left() <= right(), |
| 219 | '>' : () => left() > right(), |
| 220 | '>=': () => left() >= right(), |
| 221 | '+': () => left() + right(), |
| 222 | '-': () => left() - right(), |
| 223 | '*': () => left() * right(), |
| 224 | '/': () => Math.floor(left() / right()) |
| 225 | }; |
| 226 | return [opers[tokens[i]]]; |
| 227 | } |
| 228 | // else a number: |
| 229 | #if ASSERTIONS |
| 230 | assert(tokens[i] !== ')', 'parse failure, mismatched parentheses in parsing' + tokens.toString()); |
| 231 | assert(operatorAndPriority == -1); |
| 232 | #endif |
| 233 | var num = Number(tokens[i]); |
| 234 | return [function() { return num; }] |
no test coverage detected