UNPKG

subscript

Version:

Modular expression parser & evaluator

59 lines (51 loc) 2.19 kB
/** * Numbers with configurable prefix notation * * Configurable via parse.number: { '0x': 16, '0b': 2, '0o': 8 } */ import { parse, lookup, next, err, skip, idx, cur } from '../parse.js'; const PERIOD = 46, _0 = 48, _9 = 57, _E = 69, _e = 101, PLUS = 43, MINUS = 45, UNDERSCORE = 95, _n = 110; const _a = 97, _f = 102, _A = 65, _F = 70; // Strip underscores only if present (avoid allocation for common case) const strip = s => s.indexOf('_') < 0 ? s : s.replaceAll('_', ''); // Decimal number - check for .. range operator (don't consume . if followed by .) // Supports numeric separators: 1_000_000 and BigInt suffix: 123n const num = a => { let str = strip(next(c => // . is decimal only if NOT range (..) and NOT member access (.name) // Allows trailing decimal: 1. → 1, 0.95.toFixed → stops at second . (c === PERIOD && (c = cur.charCodeAt(idx + 1)) !== PERIOD && !(parse.id(c) && c > _9 && c !== _e && c !== _E)) || (c >= _0 && c <= _9) || c === UNDERSCORE || ((c === _E || c === _e) && ((c = cur.charCodeAt(idx + 1)) >= _0 && c <= _9 || c === PLUS || c === MINUS) ? 2 : 0) )); // BigInt suffix if (cur.charCodeAt(idx) === _n) { skip(); return [, BigInt(str)]; } return (a = +str) != a ? err() : [, a]; }; // Char test for prefix base (with underscore support) const charTest = base => c => c === UNDERSCORE || (c >= _0 && c <= _9 && c - _0 < base) || (base === 16 && (c >= _a && c <= _f || c >= _A && c <= _F)); // Default: no prefixes parse.number = null; // .1 (but not .. range) lookup[PERIOD] = a => !a && cur.charCodeAt(idx + 1) >= _0 && cur.charCodeAt(idx + 1) <= _9 && num(); // 0-9: check parse.number for prefix config for (let i = _0; i <= _9; i++) lookup[i] = a => a ? void 0 : num(); lookup[_0] = a => { if (a) return; const cfg = parse.number; if (cfg) { for (const [pre, base] of Object.entries(cfg)) { if (pre[0] === '0' && cur[idx + 1]?.toLowerCase() === pre[1]) { skip(2); const str = strip(next(charTest(base))); if (cur.charCodeAt(idx) === _n) { skip(); return [, BigInt('0' + pre[1] + str)]; } return [, parseInt(str, base)]; } } } return num(); };