Mirror: The magical sticky regex-based parser generator 🧙

Refactor AST to return arrays for sequences

Changed files
+45 -86
src
+2 -5
src/codegen.js
···
};
const astGroup = (ast, depth, opts) => {
-
if (ast.sequence.length === 1)
-
return astExpression(ast.sequence[0], depth, opts);
-
const capture = !!opts.capture && !ast.capture;
let group = '';
···
}
let sequence = '';
-
for (let i = 0; i < ast.sequence.length; i++)
-
sequence += astQuantifier(ast.sequence[i], depth, childOpts);
+
for (let i = 0; i < ast.length; i++)
+
sequence += astQuantifier(ast[i], depth, childOpts);
if (!ast.alternation) {
body += sequence;
+9 -21
src/parser.js
···
let stackIndex = 0;
const sequenceStack = [];
-
const rootSequence = {
-
sequence: [],
-
};
+
const rootSequence = [];
let currentGroup = null;
let lastMatch;
···
while (stackIndex < quasis.length + expressions.length) {
if (stackIndex % 2 !== 0) {
-
currentSequence.sequence.push({
+
currentSequence.push({
expression: expressions[stackIndex++ >> 1],
});
}
···
if (char === ' ' || char === '\t' || char === '\r' || char === '\n') {
continue;
-
} else if (char === '|' && currentSequence.sequence.length > 0) {
-
currentSequence = currentSequence.alternation = {
-
sequence: [],
-
};
-
+
} else if (char === '|' && currentSequence.length) {
+
currentSequence = currentSequence.alternation = [];
continue;
-
} else if (char === ')' && currentSequence.sequence.length > 0) {
+
} else if (char === ')' && currentSequence.length) {
currentGroup = null;
currentSequence = sequenceStack.pop();
if (currentSequence) continue;
} else if (char === '(') {
currentGroup = {
-
sequence: {
-
sequence: [],
-
},
+
sequence: [],
};
sequenceStack.push(currentSequence);
-
currentSequence.sequence.push(currentGroup);
+
currentSequence.push(currentGroup);
currentSequence = currentGroup.sequence;
continue;
-
} else if (
-
char === '?' &&
-
currentSequence.sequence.length === 0 &&
-
currentGroup
-
) {
+
} else if (char === '?' && !currentSequence.length && currentGroup) {
const nextChar = quasi[quasiIndex++];
if (nextChar === ':') {
currentGroup.capture = nextChar;
···
}
} else if (
(char === '?' || char === '+' || char === '*') &&
-
(lastMatch =
-
currentSequence.sequence[currentSequence.sequence.length - 1])
+
(lastMatch = currentSequence[currentSequence.length - 1])
) {
lastMatch.quantifier = char;
continue;
+34 -60
src/parser.test.js
···
const parseTag = (quasis, ...expressions) => parse(quasis, expressions);
-
it('supports parsing expressions', () => {
-
expect(parseTag`${1}`).toEqual({
-
sequence: [
-
{
-
expression: 1,
-
quantifier: undefined,
-
},
-
],
-
alternation: undefined,
-
});
-
});
-
it('supports parsing expressions with quantifiers', () => {
let ast;
ast = parseTag`${1}?`;
-
expect(ast).toHaveProperty('sequence.0.quantifier', '?');
+
expect(ast).toHaveProperty('0.quantifier', '?');
ast = parseTag`${1}+`;
-
expect(ast).toHaveProperty('sequence.0.quantifier', '+');
+
expect(ast).toHaveProperty('0.quantifier', '+');
ast = parseTag`${1}*`;
-
expect(ast).toHaveProperty('sequence.0.quantifier', '*');
+
expect(ast).toHaveProperty('0.quantifier', '*');
});
it('supports top-level alternations', () => {
let ast;
ast = parseTag`${1} | ${2}`;
-
expect(ast).toHaveProperty('sequence.length', 1);
-
expect(ast).toHaveProperty('sequence.0.expression', 1);
-
expect(ast).toHaveProperty('alternation.sequence.0.expression', 2);
+
expect(ast).toHaveProperty('length', 1);
+
expect(ast).toHaveProperty('0.expression', 1);
+
expect(ast).toHaveProperty('alternation.0.expression', 2);
ast = parseTag`${1}? | ${2}?`;
-
expect(ast).toHaveProperty('sequence.0.quantifier', '?');
+
expect(ast).toHaveProperty('0.quantifier', '?');
});
it('supports groups with quantifiers', () => {
let ast;
ast = parseTag`(${1} ${2})`;
-
expect(ast).toHaveProperty('sequence.length', 1);
-
expect(ast).toHaveProperty('sequence.0.sequence.sequence.length', 2);
-
expect(ast).toHaveProperty('sequence.0.sequence.sequence.0.expression', 1);
-
expect(ast).toHaveProperty('sequence.0.sequence.sequence.1.expression', 2);
+
expect(ast).toHaveProperty('length', 1);
+
expect(ast).toHaveProperty('0.sequence.length', 2);
+
expect(ast).toHaveProperty('0.sequence.0.expression', 1);
+
expect(ast).toHaveProperty('0.sequence.1.expression', 2);
ast = parseTag`(${1} ${2}?)?`;
-
expect(ast).toHaveProperty('sequence.length', 1);
-
expect(ast).toHaveProperty('sequence.0.quantifier', '?');
-
expect(ast).toHaveProperty(
-
'sequence.0.sequence.sequence.0.quantifier',
-
undefined
-
);
+
expect(ast).toHaveProperty('length', 1);
+
expect(ast).toHaveProperty('0.quantifier', '?');
+
expect(ast).toHaveProperty('0.sequence.0.quantifier', undefined);
});
it('supports non-capturing groups', () => {
const ast = parseTag`(?: ${1})`;
-
expect(ast).toHaveProperty('sequence.length', 1);
-
expect(ast).toHaveProperty('sequence.0.capture', ':');
-
expect(ast).toHaveProperty('sequence.0.sequence.sequence.length', 1);
+
expect(ast).toHaveProperty('length', 1);
+
expect(ast).toHaveProperty('0.capture', ':');
+
expect(ast).toHaveProperty('0.sequence.length', 1);
});
it('supports positive lookahead groups', () => {
const ast = parseTag`(?= ${1})`;
-
expect(ast).toHaveProperty('sequence.length', 1);
-
expect(ast).toHaveProperty('sequence.0.capture', '=');
-
expect(ast).toHaveProperty('sequence.0.sequence.sequence.length', 1);
+
expect(ast).toHaveProperty('length', 1);
+
expect(ast).toHaveProperty('0.capture', '=');
+
expect(ast).toHaveProperty('0.sequence.length', 1);
});
it('supports negative lookahead groups', () => {
const ast = parseTag`(?! ${1})`;
-
expect(ast).toHaveProperty('sequence.length', 1);
-
expect(ast).toHaveProperty('sequence.0.capture', '!');
-
expect(ast).toHaveProperty('sequence.0.sequence.sequence.length', 1);
+
expect(ast).toHaveProperty('length', 1);
+
expect(ast).toHaveProperty('0.capture', '!');
+
expect(ast).toHaveProperty('0.sequence.length', 1);
});
it('supports groups with alternates', () => {
expect(parseTag`(${1} | ${2}) ${3}`).toMatchInlineSnapshot(`
-
Object {
-
"sequence": Array [
-
Object {
-
"sequence": Object {
-
"alternation": Object {
-
"sequence": Array [
-
Object {
-
"expression": 2,
-
},
-
],
-
},
-
"sequence": Array [
-
Object {
-
"expression": 1,
-
},
-
],
+
Array [
+
Object {
+
"sequence": Array [
+
Object {
+
"expression": 1,
},
-
},
-
Object {
-
"expression": 3,
-
},
-
],
-
}
+
],
+
},
+
Object {
+
"expression": 3,
+
},
+
]
`);
});