subscript
Version:
Modular expression parser & evaluator
57 lines (50 loc) • 2.22 kB
JavaScript
// Switch/case/default - parse half
// AST: ['switch', val, ['case', test, body], ['default', body], ...]
import { parse, expr, skip, keyword, parens, idx, err, seek, lookup, word } from '../parse.js';
const STATEMENT = 5, ASSIGN = 20, COLON = 58, SEMI = 59, CBRACE = 125;
// Flag to track if we're inside switch body (case/default parsing)
let inSwitch = 0;
// Reserve 'case' and 'default' as keywords that fail outside switch body
// Allows property names like {case:1} ONLY when not in switch context
const reserve = (w, l = w.length, c = w.charCodeAt(0), prev = lookup[c]) =>
lookup[c] = (a, prec, op) => (word(w) && !a && inSwitch && (!parse.prop || parse.prop(idx + l))) || prev?.(a, prec, op);
reserve('case');
reserve('default');
// caseBody() - parse statements until next case/default/}.
// Each iteration clears parse.semi: a preceding `;\n` sets the ASI sticky
// flag, which would otherwise make parse.step bail (`parse.semi && p >= lvl`)
// at the very first token of the next statement — the loop would then push
// nulls forever (see if.js's `else` body for the same pattern).
const caseBody = (c) => {
const stmts = [];
while ((c = parse.space()) !== CBRACE && !word('case') && !word('default')) {
if (c === SEMI) { skip(); continue; }
parse.semi = false;
stmts.push(expr(STATEMENT + .5)) || err();
}
return stmts.length > 1 ? [';', ...stmts] : stmts[0] || null;
};
// switchBody() - parse case/default statements
const switchBody = () => {
parse.space() === 123 || err('Expected {'); skip();
inSwitch++;
const cases = [];
try {
while (parse.space() !== CBRACE) {
if (word('case')) {
seek(idx + 4); parse.space();
parse.semi = false;
const test = expr(ASSIGN - .5);
parse.space() === COLON && skip();
cases.push(['case', test, caseBody()]);
} else if (word('default')) {
seek(idx + 7); parse.space() === COLON && skip();
cases.push(['default', caseBody()]);
} else err('Expected case or default');
}
} finally { inSwitch--; }
skip();
return cases;
};
// switch (x) { ... }
keyword('switch', STATEMENT + 1, () => parse.space() === 40 && ['switch', parens(), ...switchBody()]);