diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/paca.mjs | 128 |
1 files changed, 125 insertions, 3 deletions
diff --git a/src/paca.mjs b/src/paca.mjs index 0b64a87..b92bcdc 100644 --- a/src/paca.mjs +++ b/src/paca.mjs @@ -1,5 +1,6 @@ import { - butlast, explode, isNumeric, last, mapValues, max, reduce, reduced, + butlast, dissoc, explode, isNumeric, last, mapValues, max, reduce, + reduced, } from "sjs"; @@ -11,6 +12,7 @@ const ConcatStep = { ACCEPTING: "accepting", ESCAPING: "escaping", RANGE: "range", + CLASS: "class", }; const numFromDigits = digits => @@ -107,9 +109,130 @@ const rangeStateStep = ({ out, state, context }, char, _index, _next) => { }; }; +const classStateStep = ({ out, state, context }, char, _index, _next) => { + if (context.escaping) { + return { + out, + state, + context: dissoc({ + ...context, + set: context.set.concat(char), + }, "escaping"), + }; + } + + if (char === "]") { + if (context.range.where === "to") { + return reduced({ + out, + state, + context, + error: new SyntaxError( + "unfinished character class range", + ), + }); + } + + if (context.set.length === 0) { + return reduced({ + out, + state, + context, + error: new ValueError("empty character class"), + }); + } + + return { + out: out.concat({ + operator: "class", + set: context.set, + }), + state: ConcatStep.ACCEPTING, + context: null, + }; + } + + if (char === "\\") { + return { + out, + state, + context: { + ...context, + escaping: true, + }, + }; + } + + if (context.range.where === "to") { + const from = context.range.from; + const to = char; + + if (from.charCodeAt(0) > to.charCodeAt(0)) { + return reduced({ + out, + state, + context, + error: new ValueError( + "bad class range values: " + + `[${from}-${to}]`, + ), + }); + } + + return { + out, + state, + context: { + ...context, + set: context.set.concat({ from, to }), + range: { + from: null, + where: "from", + }, + }, + }; + } + + if (char === "-" && context.set.length !== 0) { + return { + out, + state, + context: { + ...context, + set: butlast(context.set), + range: { + from: last(context.set), + where: "to", + }, + }, + }; + } + + if (char === "^" && context.set.length === 0) { + return { + out, + state, + context: { + ...context, + caret: true, + }, + }; + } + + return { + out, + state, + context: { + ...context, + set: context.set.concat(char), + }, + }; +}; + const STATE_FNS = { [ConcatStep.ESCAPING]: escapingStateStep, [ConcatStep.RANGE ]: rangeStateStep, + [ConcatStep.CLASS ]: classStateStep, }; const TRANSITION_FNS = { @@ -133,8 +256,7 @@ const TRANSITION_FNS = { context: { set: [], range: { - from: [], - to: [], + from: null, where: "from", }, }, |
