Switch to two arrays for instructions.

This commit is contained in:
Brendan Dahl 2011-12-28 20:08:18 -08:00
parent 971f35d165
commit 27b0d0c941
2 changed files with 48 additions and 37 deletions

View File

@ -360,7 +360,7 @@ var PDFFunction = (function PDFFunctionClosure() {
var range = IR[2]; var range = IR[2];
var code = IR[3]; var code = IR[3];
var numOutputs = range.length / 2; var numOutputs = range.length / 2;
var evaluator = new PostScriptEvaluator(code); var evaluator = new PostScriptEvaluator(code[0], code[1]);
// Cache the values for a big speed up, the cache size is limited though // Cache the values for a big speed up, the cache size is limited though
// since the number of possible values can be huge from a PS function. // since the number of possible values can be huge from a PS function.
var cache = new FunctionCache(); var cache = new FunctionCache();
@ -455,31 +455,36 @@ var PostScriptStack = (function PostScriptStack() {
return PostScriptStack; return PostScriptStack;
})(); })();
var PostScriptEvaluator = (function PostScriptEvaluator() { var PostScriptEvaluator = (function PostScriptEvaluator() {
function PostScriptEvaluator(code) { function PostScriptEvaluator(operators, operands) {
this.code = code; this.operators = operators;
console.log(code); this.operands = operands;
} }
PostScriptEvaluator.prototype = { PostScriptEvaluator.prototype = {
execute: function(initialStack) { execute: function(initialStack) {
var stack = new PostScriptStack(initialStack); var stack = new PostScriptStack(initialStack);
var counter = 0; var counter = 0;
var code = this.code; var operators = this.operators;
var a, b; var operands = this.operands;
while (counter < this.code.length) { var length = operators.length;
var instruction = this.code[counter++]; var a, b, operand;
var operator = instruction[0]; while (counter < length) {
var operator = operators[counter];
++counter;
switch (operator) { switch (operator) {
// non standard ps operators // non standard ps operators
case 'push': case 'push':
stack.push(instruction[1]); operand = operands[counter - 1];
stack.push(operand);
break; break;
case 'jz': // jump if false case 'jz': // jump if false
operand = operands[counter - 1];
a = stack.pop(); a = stack.pop();
if (!a) if (!a)
counter = instruction[1]; counter = operand;
break; break;
case 'j': // jump case 'j': // jump
counter = instruction[1]; operand = operands[counter - 1];
counter = operand;
break; break;
// all ps operators in alphabetical order (excluding if/ifelse) // all ps operators in alphabetical order (excluding if/ifelse)
@ -690,7 +695,8 @@ var PostScriptEvaluator = (function PostScriptEvaluator() {
var PostScriptParser = (function PostScriptParser() { var PostScriptParser = (function PostScriptParser() {
function PostScriptParser(lexer) { function PostScriptParser(lexer) {
this.lexer = lexer; this.lexer = lexer;
this.code = []; this.operators = [];
this.operands = [];
this.token; this.token;
this.prev; this.prev;
} }
@ -717,14 +723,16 @@ var PostScriptParser = (function PostScriptParser() {
this.expect(PostScriptTokenTypes.LBRACE); this.expect(PostScriptTokenTypes.LBRACE);
this.parseBlock(); this.parseBlock();
this.expect(PostScriptTokenTypes.RBRACE); this.expect(PostScriptTokenTypes.RBRACE);
return this.code; return [this.operators, this.operands];
}, },
parseBlock: function parseBlock() { parseBlock: function parseBlock() {
while (true) { while (true) {
if (this.accept(PostScriptTokenTypes.NUMBER)) { if (this.accept(PostScriptTokenTypes.NUMBER)) {
this.code.push(['push', this.prev.value]); this.operators.push('push');
this.operands.push(this.prev.value);
} else if (this.accept(PostScriptTokenTypes.OPERATOR)) { } else if (this.accept(PostScriptTokenTypes.OPERATOR)) {
this.code.push([this.prev.value]); this.operators.push(this.prev.value);
this.operands.push(null);
} else if (this.accept(PostScriptTokenTypes.LBRACE)) { } else if (this.accept(PostScriptTokenTypes.LBRACE)) {
this.parseCondition(); this.parseCondition();
} else { } else {
@ -733,26 +741,33 @@ var PostScriptParser = (function PostScriptParser() {
} }
}, },
parseCondition: function parseCondition() { parseCondition: function parseCondition() {
var counter = this.code.length - 1; // Add two place holders that will be updated later
var condition = []; var conditionLocation = this.operators.length;
this.code.push(condition); this.operators.push(null);
this.operands.push(null);
this.parseBlock(); this.parseBlock();
this.expect(PostScriptTokenTypes.RBRACE); this.expect(PostScriptTokenTypes.RBRACE);
if (this.accept(PostScriptTokenTypes.IF)) { if (this.accept(PostScriptTokenTypes.IF)) {
// The true block is right after the 'if' so it just falls through on // The true block is right after the 'if' so it just falls through on
// true else it jumps and skips the true block. // true else it jumps and skips the true block.
condition.push('jz', this.code.length); this.operators[conditionLocation] = 'jz';
this.operands[conditionLocation] = this.operators.length;
} else if (this.accept(PostScriptTokenTypes.LBRACE)) { } else if (this.accept(PostScriptTokenTypes.LBRACE)) {
var jump = []; var jumpLocation = this.operators.length;
this.code.push(jump); this.operators.push(null);
var endOfTrue = this.code.length; this.operands.push(null);
var endOfTrue = this.operators.length;
this.parseBlock(); this.parseBlock();
this.expect(PostScriptTokenTypes.RBRACE); this.expect(PostScriptTokenTypes.RBRACE);
this.expect(PostScriptTokenTypes.IFELSE); this.expect(PostScriptTokenTypes.IFELSE);
// The jump is added at the end of the true block to skip the false // The jump is added at the end of the true block to skip the false
// block. // block.
jump.push('j', this.code.length); this.operators[jumpLocation] = 'j';
condition.push('jz', endOfTrue); this.operands[jumpLocation] = this.operands.length;
this.operators[conditionLocation] = 'jz';
this.operands[conditionLocation] = endOfTrue;
} else { } else {
error('PS Function: error parsing conditional.'); error('PS Function: error parsing conditional.');
} }

View File

@ -38,13 +38,13 @@ describe('function', function() {
} }
it('parses empty programs', function() { it('parses empty programs', function() {
var output = parse('{}'); var output = parse('{}');
expect(output.length).toEqual(0); expect(output[0].length).toEqual(0);
}); });
it('parses positive numbers', function() { it('parses positive numbers', function() {
var number = 999; var number = 999;
var program = parse('{ ' + number + ' }'); var program = parse('{ ' + number + ' }');
var expectedProgram = [ var expectedProgram = [
['push', number] ['push'], [number]
]; ];
expect(program).toMatchArray(expectedProgram); expect(program).toMatchArray(expectedProgram);
}); });
@ -52,7 +52,7 @@ describe('function', function() {
var number = -999; var number = -999;
var program = parse('{ ' + number + ' }'); var program = parse('{ ' + number + ' }');
var expectedProgram = [ var expectedProgram = [
['push', number] ['push'], [number]
]; ];
expect(program).toMatchArray(expectedProgram); expect(program).toMatchArray(expectedProgram);
}); });
@ -60,32 +60,28 @@ describe('function', function() {
var number = 3.3; var number = 3.3;
var program = parse('{ ' + number + ' }'); var program = parse('{ ' + number + ' }');
var expectedProgram = [ var expectedProgram = [
['push', number] ['push'], [number]
]; ];
expect(program).toMatchArray(expectedProgram); expect(program).toMatchArray(expectedProgram);
}); });
it('parses operators', function() { it('parses operators', function() {
var program = parse('{ sub }'); var program = parse('{ sub }');
var expectedProgram = [ var expectedProgram = [
['sub'] ['sub'], [null]
]; ];
expect(program).toMatchArray(expectedProgram); expect(program).toMatchArray(expectedProgram);
}); });
it('parses if statements', function() { it('parses if statements', function() {
var program = parse('{ { 99 } if }'); var program = parse('{ { 99 } if }');
var expectedProgram = [ var expectedProgram = [
['jz', 2], ['jz', 'push'], [2, 99]
['push', 99]
]; ];
expect(program).toMatchArray(expectedProgram); expect(program).toMatchArray(expectedProgram);
}); });
it('parses ifelse statements', function() { it('parses ifelse statements', function() {
var program = parse('{ { 99 } { 44 } ifelse }'); var program = parse('{ { 99 } { 44 } ifelse }');
var expectedProgram = [ var expectedProgram = [
['jz', 3], ['jz', 'push', 'j', 'push'], [3, 99, 4, 44]
['push', 99],
['j', 4],
['push', 44]
]; ];
expect(program).toMatchArray(expectedProgram); expect(program).toMatchArray(expectedProgram);
}); });
@ -100,7 +96,7 @@ describe('function', function() {
var stream = new StringStream(program); var stream = new StringStream(program);
var parser = new PostScriptParser(new PostScriptLexer(stream)); var parser = new PostScriptParser(new PostScriptLexer(stream));
var code = parser.parse(); var code = parser.parse();
var evaluator = new PostScriptEvaluator(code); var evaluator = new PostScriptEvaluator(code[0], code[1]);
var output = evaluator.execute(); var output = evaluator.execute();
return output; return output;
} }