|
1 | | -// Pratt parser core + operator registry + compile |
| 1 | +// Pratt parser core + operator registry + compile. |
| 2 | +// |
| 3 | +// Language-agnostic by design: the core (token / lookup / prec / the expr loop) |
| 4 | +// assumes no particular language. The registrars below — binary, unary, nary, |
| 5 | +// literal, group, access, member, keyword — are a shared toolkit of common |
| 6 | +// operator *shapes*; each is parameterized by operator string + precedence, so a |
| 7 | +// dialect composes its grammar from them. Keep language-specific rules in |
| 8 | +// feature/*, not here. |
| 9 | + |
2 | 10 | // Character codes |
3 | 11 | const SPACE = 32; |
4 | 12 |
|
@@ -116,6 +124,16 @@ export let idx, cur, |
116 | 124 |
|
117 | 125 | access = (op, p) => token(op[0], p, a => (a && [op, a, expr(0, op.charCodeAt(1)) || null])), |
118 | 126 |
|
| 127 | + // propName(p) - parse the right side of a name-access operator. A bare name |
| 128 | + // beats keyword/operator matching, so reserved words read as plain identifiers |
| 129 | + // (a.class). Non-name starts (digit, #, ...) fall back to expr(p), keeping the |
| 130 | + // door open for any dialect-defined token there. Uses the live parse.id. |
| 131 | + propName = (p, c) => (parse.space(), c = cur.charCodeAt(idx), parse.id(c) && (c < 48 || c > 57) ? next(parse.id) : expr(p)), |
| 132 | + |
| 133 | + // member(op, p) - binary operator whose right side is a name, not an expression |
| 134 | + // (a.b, a::b, a->b). Same [op, a, b] shape as binary(). |
| 135 | + member = (op, p) => token(op, p, a => a && (b => b && [op, a, b])(propName(p))), |
| 136 | + |
119 | 137 | // keyword(op, prec, fn) - prefix word token with property name support |
120 | 138 | // parse.prop set by collection.js to prevent matching {keyword: value} |
121 | 139 | keyword = (op, prec, map, c = op.charCodeAt(0), l = op.length, prev = lookup[c], r) => |
|
0 commit comments