Mirror: The magical sticky regex-based parser generator 🧙

Simplify AST for non-capturing groups

Changed files
+18 -23
src
+10 -10
src/codegen.js
···
length: next.length != null ? next.length : prev.length,
onAbort: next.onAbort != null ? next.onAbort : prev.onAbort,
abort: next.abort != null ? next.abort : prev.abort,
-
capturing: next.capturing != null ? next.capturing : prev.capturing,
});
const assignIndex = (depth) =>
···
${opts.abort || ''}
`;
-
if (!opts.capturing) {
return js`
if (!(${ast.expression})) {
${abort}
···
if (ast.sequence.length === 1)
return astExpression(ast.sequence[0], depth, opts);
-
const capturing = !!opts.capturing && !!ast.capturing;
let group = '';
-
if (!opts.length && capturing) {
return js`
${js`var length_${depth} = ${_node}.length;`}
${astSequence(
···
depth + 1,
newOpts(opts, {
length: depth,
-
capturing,
})
)}
`;
···
ast.sequence,
depth + 1,
newOpts(opts, {
-
capturing,
})
);
};
···
const { index, abort } = opts;
const label = `invert_${depth}`;
-
if (ast.lookahead === 'negative') {
opts = newOpts(opts, {
index: depth,
abort: js`break ${label};`,
···
else if (ast.quantifier === 'optional') child = astOptional(ast, depth, opts);
else child = astChild(ast, depth, opts);
-
if (ast.lookahead === 'negative') {
return js`
${label}: {
${assignIndex(depth)}
···
${abort}
}
`;
-
} else if (ast.lookahead) {
return js`
${assignIndex(depth)}
${child}
···
length: 0,
onAbort: '',
abort: js`return;`,
-
capturing: true,
})}
${_node}.tag = ${name};
···
length: next.length != null ? next.length : prev.length,
onAbort: next.onAbort != null ? next.onAbort : prev.onAbort,
abort: next.abort != null ? next.abort : prev.abort,
+
capture: next.capture != null ? next.capture : prev.capture,
});
const assignIndex = (depth) =>
···
${opts.abort || ''}
`;
+
if (!opts.capture) {
return js`
if (!(${ast.expression})) {
${abort}
···
if (ast.sequence.length === 1)
return astExpression(ast.sequence[0], depth, opts);
+
const capture = !!opts.capture && !ast.capture;
let group = '';
+
if (!opts.length && capture) {
return js`
${js`var length_${depth} = ${_node}.length;`}
${astSequence(
···
depth + 1,
newOpts(opts, {
length: depth,
+
capture,
})
)}
`;
···
ast.sequence,
depth + 1,
newOpts(opts, {
+
capture,
})
);
};
···
const { index, abort } = opts;
const label = `invert_${depth}`;
+
if (ast.capture === '!') {
opts = newOpts(opts, {
index: depth,
abort: js`break ${label};`,
···
else if (ast.quantifier === 'optional') child = astOptional(ast, depth, opts);
else child = astChild(ast, depth, opts);
+
if (ast.capture === '!') {
return js`
${label}: {
${assignIndex(depth)}
···
${abort}
}
`;
+
} else if (ast.capture === '=') {
return js`
${assignIndex(depth)}
${child}
···
length: 0,
onAbort: '',
abort: js`return;`,
+
capture: true,
})}
${_node}.tag = ${name};
+4 -6
src/parser.js
···
sequence: [],
alternation: null,
},
-
capturing: true,
lookahead: null,
quantifier: null,
};
···
}
if (nextChar === ':') {
-
currentGroup.capturing = false;
continue;
} else if (nextChar === '=') {
-
currentGroup.capturing = false;
-
currentGroup.lookahead = 'positive';
continue;
} else if (nextChar === '!') {
-
currentGroup.capturing = false;
-
currentGroup.lookahead = 'negative';
continue;
}
} else if (
···
sequence: [],
alternation: null,
},
+
capture: null,
lookahead: null,
quantifier: null,
};
···
}
if (nextChar === ':') {
+
currentGroup.capture = nextChar;
continue;
} else if (nextChar === '=') {
+
currentGroup.capture = nextChar;
continue;
} else if (nextChar === '!') {
+
currentGroup.capture = nextChar;
continue;
}
} else if (
+4 -7
src/parser.test.js
···
const ast = parseTag`(?: ${1})`;
expect(ast).toHaveProperty('sequence.length', 1);
expect(ast).toHaveProperty('sequence.0.type', 'group');
-
expect(ast).toHaveProperty('sequence.0.capturing', false);
-
expect(ast).toHaveProperty('sequence.0.lookahead', null);
expect(ast).toHaveProperty('sequence.0.sequence.sequence.length', 1);
});
···
const ast = parseTag`(?= ${1})`;
expect(ast).toHaveProperty('sequence.length', 1);
expect(ast).toHaveProperty('sequence.0.type', 'group');
-
expect(ast).toHaveProperty('sequence.0.capturing', false);
-
expect(ast).toHaveProperty('sequence.0.lookahead', 'positive');
expect(ast).toHaveProperty('sequence.0.sequence.sequence.length', 1);
});
···
const ast = parseTag`(?! ${1})`;
expect(ast).toHaveProperty('sequence.length', 1);
expect(ast).toHaveProperty('sequence.0.type', 'group');
-
expect(ast).toHaveProperty('sequence.0.capturing', false);
-
expect(ast).toHaveProperty('sequence.0.lookahead', 'negative');
expect(ast).toHaveProperty('sequence.0.sequence.sequence.length', 1);
});
···
"alternation": null,
"sequence": Array [
Object {
-
"capturing": true,
"lookahead": null,
"quantifier": null,
"sequence": Object {
···
const ast = parseTag`(?: ${1})`;
expect(ast).toHaveProperty('sequence.length', 1);
expect(ast).toHaveProperty('sequence.0.type', 'group');
+
expect(ast).toHaveProperty('sequence.0.capture', ':');
expect(ast).toHaveProperty('sequence.0.sequence.sequence.length', 1);
});
···
const ast = parseTag`(?= ${1})`;
expect(ast).toHaveProperty('sequence.length', 1);
expect(ast).toHaveProperty('sequence.0.type', 'group');
+
expect(ast).toHaveProperty('sequence.0.capture', '=');
expect(ast).toHaveProperty('sequence.0.sequence.sequence.length', 1);
});
···
const ast = parseTag`(?! ${1})`;
expect(ast).toHaveProperty('sequence.length', 1);
expect(ast).toHaveProperty('sequence.0.type', 'group');
+
expect(ast).toHaveProperty('sequence.0.capture', '!');
expect(ast).toHaveProperty('sequence.0.sequence.sequence.length', 1);
});
···
"alternation": null,
"sequence": Array [
Object {
+
"capture": null,
"lookahead": null,
"quantifier": null,
"sequence": Object {