Mirror: The magical sticky regex-based parser generator 🧙

Fix edge case for optional groups not being atomic

Changed files
+31 -22
src
+20 -16
src/babel/__snapshots__/plugin.test.js.snap
···
return;
}
-
loop_3: for (;;) {
+
group_3: for (;;) {
var y3 = state.y,
x3 = state.x;
if ((x = _node_expression3(state)) == null) {
state.y = y3;
state.x = x3;
-
break loop_3;
+
break group_3;
}
}
}
···
break block_2;
}
-
loop_2: for (;;) {
+
group_2: for (;;) {
var y2 = state.y,
x2 = state.x;
···
} else {
state.y = y2;
state.x = x2;
-
break loop_2;
+
break group_2;
}
}
···
return;
}
-
loop_2: for (;;) {
+
group_2: for (;;) {
var y2 = state.y,
x2 = state.x;
···
} else {
state.y = y2;
state.x = x2;
-
break loop_2;
+
break group_2;
}
}
-
loop_2: for (;;) {
+
group_2: for (;;) {
var y2 = state.y,
x2 = state.x;
var ln2 = node.length;
···
state.y = y2;
state.x = x2;
node.length = ln2;
-
break loop_2;
+
break group_2;
}
-
var y4 = state.y,
-
x4 = state.x;
+
group_4: {
+
var y4 = state.y,
+
x4 = state.x;
-
if ((x = _node_expression4(state)) != null) {
-
node.push(x);
-
} else {
-
state.y = y4;
-
state.x = x4;
+
if ((x = _node_expression4(state)) != null) {
+
node.push(x);
+
} else {
+
state.y = y4;
+
state.x = x4;
+
node.length = ln2;
+
break group_4;
+
}
}
if ((x = _node_expression5(state)) != null) {
···
state.y = y2;
state.x = x2;
node.length = ln2;
-
break loop_2;
+
break group_2;
}
}
}
+8 -6
src/codegen.js
···
const astQuantifier = (ast, depth, opts) => {
const { index, abort } = opts;
const invert = `inv_${depth}`;
-
const loop = `loop_${depth}`;
+
const group = `group_${depth}`;
opts = copy(opts);
if (ast.capture === '!') {
···
} else if (ast.quantifier === '*') {
opts.length = 0;
opts.index = depth;
-
opts.abort = js`break ${loop};`;
+
opts.abort = js`break ${group};`;
child = js`
-
${loop}: for (;;) {
+
${group}: for (;;) {
${assignIndex(depth)}
${astChild(ast, depth, opts)}
}
`;
} else if (ast.quantifier === '?') {
opts.index = depth;
-
opts.abort = '';
+
opts.abort = js`break ${group}`;
child = js`
-
${assignIndex(depth)}
-
${astChild(ast, depth, opts)}
+
${group}: {
+
${assignIndex(depth)}
+
${astChild(ast, depth, opts)}
+
}
`;
} else {
child = astChild(ast, depth, opts);
+3
src/core.test.js
···
input | result
${'123'} | ${['1', '2', '3']}
${'3'} | ${['3']}
+
${'23'} | ${undefined}
${'_'} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
···
${'123'} | ${['1', '2', '3']}
${'12123'} | ${['1', '2', '1', '2', '3']}
${'3'} | ${['3']}
+
${'23'} | ${undefined}
${'13'} | ${undefined}
${'_'} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
···
input | result
${'123'} | ${['1', '2', '3']}
${'12123'} | ${['1', '2', '1', '2', '3']}
+
${'23'} | ${undefined}
${'3'} | ${undefined}
${'13'} | ${undefined}
${'_'} | ${undefined}