Skip to content

Commit

Permalink
issue pegjs#30: Realize range in bytecode and in javascript generator…
Browse files Browse the repository at this point in the history
…, optimized for speed.
  • Loading branch information
Mingun committed Sep 18, 2013
1 parent f2b2c9d commit 3db3908
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 24 deletions.
7 changes: 4 additions & 3 deletions lib/compiler/opcodes.js
Expand Up @@ -17,7 +17,7 @@ module.exports = {
IF: 10, // IF t, f
IF_ERROR: 11, // IF_ERROR t, f
IF_NOT_ERROR: 12, // IF_NOT_ERROR t, f
WHILE_NOT_ERROR: 13, // WHILE_NOT_ERROR b
WHILE_NOT_ERROR: 13, // WHILE_NOT_ERROR b (opcode, bodyLen, bodyCode[])

/* Matching */

Expand Down Expand Up @@ -46,6 +46,7 @@ module.exports = {

/* Checking array length */

CHECK_MIN_LEN: 27, // CHECK_MIN_LEN len
CHECK_MAX_LEN: 28 // CHECK_MAX_LEN len
IF_ARRLEN_MIN: 27, // IF_ARRLEN_MIN t f
IF_ARRLEN_MAX: 28, // IF_ARRLEN_MAX t f
BREAK: 29, // BREAK
};
89 changes: 71 additions & 18 deletions lib/compiler/passes/generate-bytecode.js
Expand Up @@ -2,6 +2,7 @@ var utils = require("../../utils"),
op = require("../opcodes");

/* Generates bytecode.
* function interpret(min, max) {...} -- run part of bytecode: bytecode[min:max]
*
* Instructions
* ============
Expand Down Expand Up @@ -178,13 +179,28 @@ var utils = require("../../utils"),
* Checking array length
* ---------------------
*
* [27] CHECK_MIN_LEN len
* [27] IF_ARRLEN_MIN t f
*
* stack.push(len !== undefined && stack.top().length < len)
* min = stack.pop();
* if (typeof min !== "undefined" && stack.top().length < min) {
* interpret(ip + 3, ip + 3 + t);
* } else {
* interpret(ip + 3 + t, ip + 3 + t + f);
* }
*
* [28] IF_ARRLEN_MAX t f
*
* max = stack.pop();
* if (typeof max !== "undefined" && stack.top().length >= max) {
* interpret(ip + 3, ip + 3 + t);
* } else {
* interpret(ip + 3 + t, ip + 3 + t + f);
* }
*
* [28] CHECK_MAX_LEN len
* [29] BREAK x
*
* stack.push(len !== undefined && stack.top().length >=len)
* break;
* interpret(ip + 1 + x);
*/
module.exports = function(ast, options) {
var consts = [];
Expand Down Expand Up @@ -529,26 +545,63 @@ module.exports = function(ast, options) {

range: function(node, context) {
var emptyArrayIndex = addConst('[]');
var nullIndex = addConst('null');
// expressionCode put result on stack
expressionCode = generate(node.expression, {
sp: context.sp + 1,
env: { },
action: null
});
/*minIndex = generate(node.min, {
sp: context.sp + 1,
env: { },
action: null
});
maxIndex = generate(node.max, {
sp: context.sp + 1,
env: { },
action: null
});*/
var minIndex = addConst(node.min);
var maxIndex = addConst(node.max);
var loopCode = buildLoop(
[op.WHILE_NOT_ERROR], // while (elem !== null) {
buildSequence(
[op.APPEND], // result.push(elem); stack:[ [elem...] ]
[op.PUSH, maxIndex], // stack:[ [elem...], min ]
buildCondition( // if (max !== undefined && result.length >= max)
[op.IF_ARRLEN_MAX], // stack:[ [elem...] ]
[op.BREAK, 0], [] // break;
),
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;

return buildSequence(
[op.PUSH, emptyArrayIndex], // var result = []
expressionCode, // var elem = expr();
buildLoop(
[op.WHILE_NOT_ERROR], // while (elem !== null) {
buildSequence(
[op.APPEND], // result.push(elem);
/*TODO: new opcode*/ // if (max !== undefined && result.length >= max) break;
expressionCode // elem = expr();
)
), // }
/*TODO: new opcode*/ // if (min !== undefined && result.length < min) result = null;
[op.POP] // return result;
);
[op.PUSH, emptyArrayIndex], // var result = [] stack:[ [] ]
expressionCode, // var elem = expr(); stack:[ [], elem ]
loopCode, // while (elem !== null) {...}
[op.POP], // stack:[ [elem...] ] (pop elem=`null`)
[op.PUSH, minIndex], // stack:[ [elem...], min ]

// Check low boundary.
buildCondition( // if (min !== undefined && result.length < min)
[op.IF_ARRLEN_MIN], // stack:[ [elem...] ]
buildSequence( // result = null;
[op.POP], [op.PUSH, nullIndex]// stack:[ null ]
),
[]
)
); // return result; stack:[ result ]
},

rule_ref: function(node) {
Expand Down Expand Up @@ -618,7 +671,7 @@ module.exports = function(ast, options) {
);
},

any: function(node) {
any: function(node) {//stack: -0 +1
var expectedIndex = addConst(utils.quote("any character"));

return buildCondition(
Expand Down
28 changes: 25 additions & 3 deletions lib/compiler/passes/generate-javascript.js
Expand Up @@ -8,6 +8,13 @@ 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;
}
return opcode;
}

function generateTables() {
if (options.optimize === "size") {
Expand Down Expand Up @@ -147,7 +154,7 @@ module.exports = function(ast, options) {
if (options.cache) {
parts.push(indent2(generateCacheHeader('index')));
}

//{
parts.push([
' function protect(object) {',
' return Object.prototype.toString.apply(object) === "[object Array]" ? [] : object;',
Expand Down Expand Up @@ -317,7 +324,7 @@ module.exports = function(ast, options) {
' break;',
' }',
' }'
].join('\n'));
].join('\n'));//}

if (options.cache) {
parts.push(indent2(generateCacheFooter('stack[0]')));
Expand Down Expand Up @@ -619,8 +626,23 @@ module.exports = function(ast, options) {
ip++;
break;

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
value = stack.pop();
compileCondition('typeof('+value+')!=="undefined" && '+stack.top() + '.length >= '+value, 0);
break;

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

default:
throw new Error("Invalid opcode: " + bc[ip] + ".");
throw new Error("Invalid opcode: " + getOpcodeName(bc[ip]) + ".");
}
}

Expand Down

0 comments on commit 3db3908

Please sign in to comment.