Skip to content

Commit

Permalink
pegjs#30: realize range feature in optimize=size mode.
Browse files Browse the repository at this point in the history
  • Loading branch information
Mingun committed Sep 19, 2013
1 parent 90d8d08 commit 8997f19
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 74 deletions.
17 changes: 8 additions & 9 deletions lib/compiler/passes/generate-bytecode.js
Expand Up @@ -576,15 +576,14 @@ module.exports = function(ast, options) {
expressionCode // elem = expr(); stack:[ [elem...], elem ]
)
); // } stack:[ [elem...], elem ]
var placeholderOffset = 2 + 1 + 2 + 3 + 1;
// ^ ^ ^ ^ ^
// | | | | '- op.BREAK
// | | | '----- op.IF_ARRLEN_MAX + `then` len + `else` len
// | | '--------- op.PUSH, maxIndex
// | '------------- op.APPEND
// '----------------- op.WHILE_NOT_ERROR + loop body len
// Replace placeholder after op.BREAK - offset to next executable instruction after BREAK.
loopCode[placeholderOffset] = loopCode.length - placeholderOffset + 1;
var placeholderOffset = 2 + 1 + 2 + 3;
// ^ ^ ^ ^
// | | | '- op.IF_ARRLEN_MAX + `then` len + `else` len
// | | '----- op.PUSH, maxIndex
// | '--------- op.APPEND
// '------------- op.WHILE_NOT_ERROR + loop body len
// Replace placeholder after op.BREAK to offset to breakable instruction.
loopCode[placeholderOffset+1] = -placeholderOffset;// Go to op.WHILE_NOT_ERROR

return buildSequence(
[op.PUSH, emptyArrayIndex], // var result = [] stack:[ [] ]
Expand Down
151 changes: 86 additions & 65 deletions lib/compiler/passes/generate-javascript.js
Expand Up @@ -8,7 +8,7 @@ module.exports = function(ast, options) {
function indent4(code) { return code.replace(/^(.+)$/gm, ' $1'); }
function indent8(code) { return code.replace(/^(.+)$/gm, ' $1'); }
function indent10(code) { return code.replace(/^(.+)$/gm, ' $1'); }

function getOpcodeName(opcode) {
for (var k in op) {
if (op[k] === opcode) return k+'='+opcode;
Expand All @@ -19,6 +19,7 @@ module.exports = function(ast, options) {
function generateTables() {
if (options.optimize === "size") {
return [
'//{ Tables',
'peg$consts = [',
indent2(ast.consts.join(',\n')),
'],',
Expand All @@ -35,7 +36,8 @@ module.exports = function(ast, options) {
+ ')';
}
).join(',\n')),
'],'
'],',
'//} Tables'
].join('\n');
} else {
return utils.map(
Expand Down Expand Up @@ -141,11 +143,11 @@ module.exports = function(ast, options) {
'}',
'',
'function peg$parseRule(index) {',
' var bc = peg$bytecode[index],',
' ip = 0,',
' ips = [],',
' end = bc.length,',
' ends = [],',
' var bc = peg$bytecode[index],',// rule bytecode
' ip = 0,', // instruction pointer
' ips = [],', // stack of instruction pointers for run nested blocks
' end = bc.length,',// end of current executable region of bytecode
' ends = [],', // parallel array to `ips`
' stack = [],',
' params, i;',
''
Expand All @@ -159,7 +161,6 @@ module.exports = function(ast, options) {
' function protect(object) {',
' return Object.prototype.toString.apply(object) === "[object Array]" ? [] : object;',
' }',
'',
/*
* The point of the outer loop and the |ips| & |ends| stacks is to avoid
* recursive calls for interpreting parts of bytecode. In other words, we
Expand All @@ -170,148 +171,168 @@ module.exports = function(ast, options) {
' while (true) {',
' while (ip < end) {',
' switch (bc[ip]) {',
' case ' + op.PUSH + ':', // PUSH c
' case ' + op.PUSH + ': {', // PUSH c
/*
* Hack: One of the constants can be an empty array. It needs to be cloned
* because it can be modified later on the stack by |APPEND|.
*/
' stack.push(protect(peg$consts[bc[ip + 1]]));',
' ip += 2;',
' break;',
'',
' case ' + op.PUSH_CURR_POS + ':', // PUSH_CURR_POS
' }',
' case ' + op.PUSH_CURR_POS + ': {', // PUSH_CURR_POS
' stack.push(peg$currPos);',
' ip++;',
' break;',
'',
' case ' + op.POP + ':', // POP
' }',
' case ' + op.POP + ': {', // POP
' stack.pop();',
' ip++;',
' break;',
'',
' case ' + op.POP_CURR_POS + ':', // POP_CURR_POS
' }',
' case ' + op.POP_CURR_POS + ': {', // POP_CURR_POS
' peg$currPos = stack.pop();',
' ip++;',
' break;',
'',
' case ' + op.POP_N + ':', // POP_N n
' }',
' case ' + op.POP_N + ': {', // POP_N n
' stack.length -= bc[ip + 1];',
' ip += 2;',
' break;',
'',
' case ' + op.NIP + ':', // NIP
' }',
' case ' + op.NIP + ': {', // NIP
' stack.splice(-2, 1);',
' ip++;',
' break;',
'',
' case ' + op.NIP_CURR_POS + ':', // NIP_CURR_POS
' }',
' case ' + op.NIP_CURR_POS + ': {', // NIP_CURR_POS
' peg$currPos = stack.splice(-2, 1)[0];',
' ip++;',
' break;',
'',
' case ' + op.APPEND + ':', // APPEND
' }',
' case ' + op.APPEND + ': {', // APPEND
' stack[stack.length - 2].push(stack.pop());',
' ip++;',
' break;',
'',
' case ' + op.WRAP + ':', // WRAP n
' }',
' case ' + op.WRAP + ': {', // WRAP n
' stack.push(stack.splice(stack.length - bc[ip + 1]));',
' ip += 2;',
' break;',
'',
' case ' + op.TEXT + ':', // TEXT
' }',
' case ' + op.TEXT + ': {', // TEXT
' stack.pop();',
' stack.push(input.substring(stack[stack.length - 1], peg$currPos));',
' ip++;',
' break;',
'',
' case ' + op.IF + ':', // IF t, f
' }',
' case ' + op.IF + ': {', // IF t, f
indent10(generateCondition('stack[stack.length - 1]', 0)),
'',
' case ' + op.IF_ERROR + ':', // IF_ERROR t, f
' }',
' case ' + op.IF_ERROR + ': {', // IF_ERROR t, f
indent10(generateCondition(
'stack[stack.length - 1] === null',
0
)),
'',
' case ' + op.IF_NOT_ERROR + ':', // IF_NOT_ERROR t, f
' }',
' case ' + op.IF_NOT_ERROR + ': {', // IF_NOT_ERROR t, f
indent10(
generateCondition('stack[stack.length - 1] !== null',
0
)),
'',
' case ' + op.WHILE_NOT_ERROR + ':', // WHILE_NOT_ERROR b
' }',
' case ' + op.WHILE_NOT_ERROR + ': {', // WHILE_NOT_ERROR b
indent10(generateLoop('stack[stack.length - 1] !== null')),
'',
' case ' + op.MATCH_ANY + ':', // MATCH_ANY a, f, ...
' }',
' case ' + op.MATCH_ANY + ': {', // MATCH_ANY a, f, ...
indent10(generateCondition('input.length > peg$currPos', 0)),
'',
' case ' + op.MATCH_STRING + ':', // MATCH_STRING s, a, f, ...
' }',
' case ' + op.MATCH_STRING + ': {', // MATCH_STRING s, a, f, ...
indent10(generateCondition(
'input.substr(peg$currPos, peg$consts[bc[ip + 1]].length) === peg$consts[bc[ip + 1]]',
1
)),
'',
' case ' + op.MATCH_STRING_IC + ':', // MATCH_STRING_IC s, a, f, ...
' }',
' case ' + op.MATCH_STRING_IC + ': {', // MATCH_STRING_IC s, a, f, ...
indent10(generateCondition(
'input.substr(peg$currPos, peg$consts[bc[ip + 1]].length).toLowerCase() === peg$consts[bc[ip + 1]]',
1
)),
'',
' case ' + op.MATCH_REGEXP + ':', // MATCH_REGEXP r, a, f, ...
' }',
' case ' + op.MATCH_REGEXP + ': {', // MATCH_REGEXP r, a, f, ...
indent10(generateCondition(
'peg$consts[bc[ip + 1]].test(input.charAt(peg$currPos))',
1
)),
'',
' case ' + op.ACCEPT_N + ':', // ACCEPT_N n
' }',
' case ' + op.ACCEPT_N + ': {', // ACCEPT_N n
' stack.push(input.substr(peg$currPos, bc[ip + 1]));',
' peg$currPos += bc[ip + 1];',
' ip += 2;',
' break;',
'',
' case ' + op.ACCEPT_STRING + ':', // ACCEPT_STRING s
' }',
' case ' + op.ACCEPT_STRING + ': {', // ACCEPT_STRING s
' stack.push(peg$consts[bc[ip + 1]]);',
' peg$currPos += peg$consts[bc[ip + 1]].length;',
' ip += 2;',
' break;',
'',
' case ' + op.FAIL + ':', // FAIL e
' }',
' case ' + op.FAIL + ': {', // FAIL e
' stack.push(null);',
' if (peg$silentFails === 0) {',
' peg$fail(peg$consts[bc[ip + 1]]);',
' }',
' ip += 2;',
' break;',
'',
' case ' + op.REPORT_SAVED_POS + ':', // REPORT_SAVED_POS p
' }',
' case ' + op.REPORT_SAVED_POS + ': {', // REPORT_SAVED_POS p
' peg$reportedPos = stack[stack.length - 1 - bc[ip + 1]];',
' ip += 2;',
' break;',
'',
' case ' + op.REPORT_CURR_POS + ':', // REPORT_CURR_POS
' }',
' case ' + op.REPORT_CURR_POS + ': {', // REPORT_CURR_POS
' peg$reportedPos = peg$currPos;',
' ip++;',
' break;',
'',
' case ' + op.CALL + ':', // CALL f, n, pc, p1, p2, ..., pN
' }',
' case ' + op.CALL + ': {', // CALL f, n, pc, p1, p2, ..., pN
indent10(generateCall()),
'',
' case ' + op.RULE + ':', // RULE r
' }',
' case ' + op.RULE + ': {', // RULE r
' stack.push(peg$parseRule(bc[ip + 1]));',
' ip += 2;',
' break;',
'',
' case ' + op.SILENT_FAILS_ON + ':', // SILENT_FAILS_ON
' }',
' case ' + op.SILENT_FAILS_ON + ': {', // SILENT_FAILS_ON
' peg$silentFails++;',
' ip++;',
' break;',
'',
' case ' + op.SILENT_FAILS_OFF + ':', // SILENT_FAILS_OFF
' }',
' case ' + op.SILENT_FAILS_OFF + ': {', // SILENT_FAILS_OFF
' peg$silentFails--;',
' ip++;',
' break;',
'',
' }',
' case ' + op.IF_ARRLEN_MIN + ': {', // IF_ARRLEN_MIN t f
' i = stack.pop();',
indent10(generateCondition('typeof(i)!=="undefined" && stack[stack.length - 1].length < i', 0)),
' }',
' case ' + op.IF_ARRLEN_MAX + ': {', // IF_ARRLEN_MAX t f
' i = stack.pop();',
indent10(generateCondition('typeof(i)!=="undefined" && stack[stack.length - 1].length >= i', 0)),
' }',
' case ' + op.BREAK + ': {', // BREAK offsetToBreakableInstruction
' if (ips.length == 0) throw new Error("Illegal bytecode: BREAK instruction not in loop: "+ip);',
// <current ip> + <breakable instruction ip offset>
' i = ip + bc[ip + 1];',// get breakable instruction ip
' do {ip = ips.pop(); end = ends.pop();} while (ip !== i);',
// push element as `null`, that mean 'too many elements, this element match failed'.
' stack.push(null);',
//' ip = (ip + bc[ip + 1]) + 1;',
// <opcode + field for len> + <len of breakable instruction body>
' ip += 2 + bc[ip + 1];',
' break;',
' }',
' default:',
' throw new Error("Invalid opcode: " + bc[ip] + ".");',
' }',
Expand Down Expand Up @@ -626,17 +647,17 @@ module.exports = function(ast, options) {
ip++;
break;

case op.IF_ARRLEN_MIN: // IF_ARRLEN_MIN t f
case op.IF_ARRLEN_MIN: // IF_ARRLEN_MIN t f
value = stack.pop();
compileCondition('typeof('+value+')!=="undefined" && '+stack.top() + '.length < '+value, 0);
break;

case op.IF_ARRLEN_MAX: // IF_ARRLEN_MAX t f
case op.IF_ARRLEN_MAX: // IF_ARRLEN_MAX t f
value = stack.pop();
compileCondition('typeof('+value+')!=="undefined" && '+stack.top() + '.length >= '+value, 0);
break;

case op.BREAK: // BREAK endCodeBlock
case op.BREAK: // BREAK endCodeBlock
parts.push('break;');
ip += 2;
break;
Expand Down

0 comments on commit 8997f19

Please sign in to comment.