Merge pull request #15309 from Snuffleupagus/function-rm-closure
Remove the remaining closure in the `src/core/function.js` file
This commit is contained in:
commit
c81903d72d
@ -815,439 +815,434 @@ class PostScriptEvaluator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AstNode {
|
||||||
|
constructor(type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor) {
|
||||||
|
unreachable("abstract method");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AstArgument extends AstNode {
|
||||||
|
constructor(index, min, max) {
|
||||||
|
super("args");
|
||||||
|
this.index = index;
|
||||||
|
this.min = min;
|
||||||
|
this.max = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor) {
|
||||||
|
visitor.visitArgument(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AstLiteral extends AstNode {
|
||||||
|
constructor(number) {
|
||||||
|
super("literal");
|
||||||
|
this.number = number;
|
||||||
|
this.min = number;
|
||||||
|
this.max = number;
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor) {
|
||||||
|
visitor.visitLiteral(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AstBinaryOperation extends AstNode {
|
||||||
|
constructor(op, arg1, arg2, min, max) {
|
||||||
|
super("binary");
|
||||||
|
this.op = op;
|
||||||
|
this.arg1 = arg1;
|
||||||
|
this.arg2 = arg2;
|
||||||
|
this.min = min;
|
||||||
|
this.max = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor) {
|
||||||
|
visitor.visitBinaryOperation(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AstMin extends AstNode {
|
||||||
|
constructor(arg, max) {
|
||||||
|
super("max");
|
||||||
|
this.arg = arg;
|
||||||
|
this.min = arg.min;
|
||||||
|
this.max = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor) {
|
||||||
|
visitor.visitMin(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AstVariable extends AstNode {
|
||||||
|
constructor(index, min, max) {
|
||||||
|
super("var");
|
||||||
|
this.index = index;
|
||||||
|
this.min = min;
|
||||||
|
this.max = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor) {
|
||||||
|
visitor.visitVariable(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AstVariableDefinition extends AstNode {
|
||||||
|
constructor(variable, arg) {
|
||||||
|
super("definition");
|
||||||
|
this.variable = variable;
|
||||||
|
this.arg = arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(visitor) {
|
||||||
|
visitor.visitVariableDefinition(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExpressionBuilderVisitor {
|
||||||
|
constructor() {
|
||||||
|
this.parts = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
visitArgument(arg) {
|
||||||
|
this.parts.push(
|
||||||
|
"Math.max(",
|
||||||
|
arg.min,
|
||||||
|
", Math.min(",
|
||||||
|
arg.max,
|
||||||
|
", src[srcOffset + ",
|
||||||
|
arg.index,
|
||||||
|
"]))"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitVariable(variable) {
|
||||||
|
this.parts.push("v", variable.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitLiteral(literal) {
|
||||||
|
this.parts.push(literal.number);
|
||||||
|
}
|
||||||
|
|
||||||
|
visitBinaryOperation(operation) {
|
||||||
|
this.parts.push("(");
|
||||||
|
operation.arg1.visit(this);
|
||||||
|
this.parts.push(" ", operation.op, " ");
|
||||||
|
operation.arg2.visit(this);
|
||||||
|
this.parts.push(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
visitVariableDefinition(definition) {
|
||||||
|
this.parts.push("var ");
|
||||||
|
definition.variable.visit(this);
|
||||||
|
this.parts.push(" = ");
|
||||||
|
definition.arg.visit(this);
|
||||||
|
this.parts.push(";");
|
||||||
|
}
|
||||||
|
|
||||||
|
visitMin(max) {
|
||||||
|
this.parts.push("Math.min(");
|
||||||
|
max.arg.visit(this);
|
||||||
|
this.parts.push(", ", max.max, ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
return this.parts.join("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildAddOperation(num1, num2) {
|
||||||
|
if (num2.type === "literal" && num2.number === 0) {
|
||||||
|
// optimization: second operand is 0
|
||||||
|
return num1;
|
||||||
|
}
|
||||||
|
if (num1.type === "literal" && num1.number === 0) {
|
||||||
|
// optimization: first operand is 0
|
||||||
|
return num2;
|
||||||
|
}
|
||||||
|
if (num2.type === "literal" && num1.type === "literal") {
|
||||||
|
// optimization: operands operand are literals
|
||||||
|
return new AstLiteral(num1.number + num2.number);
|
||||||
|
}
|
||||||
|
return new AstBinaryOperation(
|
||||||
|
"+",
|
||||||
|
num1,
|
||||||
|
num2,
|
||||||
|
num1.min + num2.min,
|
||||||
|
num1.max + num2.max
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildMulOperation(num1, num2) {
|
||||||
|
if (num2.type === "literal") {
|
||||||
|
// optimization: second operands is a literal...
|
||||||
|
if (num2.number === 0) {
|
||||||
|
return new AstLiteral(0); // and it's 0
|
||||||
|
} else if (num2.number === 1) {
|
||||||
|
return num1; // and it's 1
|
||||||
|
} else if (num1.type === "literal") {
|
||||||
|
// ... and first operands is a literal too
|
||||||
|
return new AstLiteral(num1.number * num2.number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (num1.type === "literal") {
|
||||||
|
// optimization: first operands is a literal...
|
||||||
|
if (num1.number === 0) {
|
||||||
|
return new AstLiteral(0); // and it's 0
|
||||||
|
} else if (num1.number === 1) {
|
||||||
|
return num2; // and it's 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const min = Math.min(
|
||||||
|
num1.min * num2.min,
|
||||||
|
num1.min * num2.max,
|
||||||
|
num1.max * num2.min,
|
||||||
|
num1.max * num2.max
|
||||||
|
);
|
||||||
|
const max = Math.max(
|
||||||
|
num1.min * num2.min,
|
||||||
|
num1.min * num2.max,
|
||||||
|
num1.max * num2.min,
|
||||||
|
num1.max * num2.max
|
||||||
|
);
|
||||||
|
return new AstBinaryOperation("*", num1, num2, min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSubOperation(num1, num2) {
|
||||||
|
if (num2.type === "literal") {
|
||||||
|
// optimization: second operands is a literal...
|
||||||
|
if (num2.number === 0) {
|
||||||
|
return num1; // ... and it's 0
|
||||||
|
} else if (num1.type === "literal") {
|
||||||
|
// ... and first operands is a literal too
|
||||||
|
return new AstLiteral(num1.number - num2.number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
num2.type === "binary" &&
|
||||||
|
num2.op === "-" &&
|
||||||
|
num1.type === "literal" &&
|
||||||
|
num1.number === 1 &&
|
||||||
|
num2.arg1.type === "literal" &&
|
||||||
|
num2.arg1.number === 1
|
||||||
|
) {
|
||||||
|
// optimization for case: 1 - (1 - x)
|
||||||
|
return num2.arg2;
|
||||||
|
}
|
||||||
|
return new AstBinaryOperation(
|
||||||
|
"-",
|
||||||
|
num1,
|
||||||
|
num2,
|
||||||
|
num1.min - num2.max,
|
||||||
|
num1.max - num2.min
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildMinOperation(num1, max) {
|
||||||
|
if (num1.min >= max) {
|
||||||
|
// optimization: num1 min value is not less than required max
|
||||||
|
return new AstLiteral(max); // just returning max
|
||||||
|
} else if (num1.max <= max) {
|
||||||
|
// optimization: num1 max value is not greater than required max
|
||||||
|
return num1; // just returning an argument
|
||||||
|
}
|
||||||
|
return new AstMin(num1, max);
|
||||||
|
}
|
||||||
|
|
||||||
// Most of the PDFs functions consist of simple operations such as:
|
// Most of the PDFs functions consist of simple operations such as:
|
||||||
// roll, exch, sub, cvr, pop, index, dup, mul, if, gt, add.
|
// roll, exch, sub, cvr, pop, index, dup, mul, if, gt, add.
|
||||||
//
|
//
|
||||||
// We can compile most of such programs, and at the same moment, we can
|
// We can compile most of such programs, and at the same moment, we can
|
||||||
// optimize some expressions using basic math properties. Keeping track of
|
// optimize some expressions using basic math properties. Keeping track of
|
||||||
// min/max values will allow us to avoid extra Math.min/Math.max calls.
|
// min/max values will allow us to avoid extra Math.min/Math.max calls.
|
||||||
const PostScriptCompiler = (function PostScriptCompilerClosure() {
|
class PostScriptCompiler {
|
||||||
class AstNode {
|
compile(code, domain, range) {
|
||||||
constructor(type) {
|
const stack = [];
|
||||||
this.type = type;
|
const instructions = [];
|
||||||
|
const inputSize = domain.length >> 1,
|
||||||
|
outputSize = range.length >> 1;
|
||||||
|
let lastRegister = 0;
|
||||||
|
let n, j;
|
||||||
|
let num1, num2, ast1, ast2, tmpVar, item;
|
||||||
|
for (let i = 0; i < inputSize; i++) {
|
||||||
|
stack.push(new AstArgument(i, domain[i * 2], domain[i * 2 + 1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
visit(visitor) {
|
for (let i = 0, ii = code.length; i < ii; i++) {
|
||||||
unreachable("abstract method");
|
item = code[i];
|
||||||
}
|
if (typeof item === "number") {
|
||||||
}
|
stack.push(new AstLiteral(item));
|
||||||
|
continue;
|
||||||
class AstArgument extends AstNode {
|
|
||||||
constructor(index, min, max) {
|
|
||||||
super("args");
|
|
||||||
this.index = index;
|
|
||||||
this.min = min;
|
|
||||||
this.max = max;
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
visitor.visitArgument(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AstLiteral extends AstNode {
|
|
||||||
constructor(number) {
|
|
||||||
super("literal");
|
|
||||||
this.number = number;
|
|
||||||
this.min = number;
|
|
||||||
this.max = number;
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
visitor.visitLiteral(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AstBinaryOperation extends AstNode {
|
|
||||||
constructor(op, arg1, arg2, min, max) {
|
|
||||||
super("binary");
|
|
||||||
this.op = op;
|
|
||||||
this.arg1 = arg1;
|
|
||||||
this.arg2 = arg2;
|
|
||||||
this.min = min;
|
|
||||||
this.max = max;
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
visitor.visitBinaryOperation(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AstMin extends AstNode {
|
|
||||||
constructor(arg, max) {
|
|
||||||
super("max");
|
|
||||||
this.arg = arg;
|
|
||||||
this.min = arg.min;
|
|
||||||
this.max = max;
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
visitor.visitMin(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AstVariable extends AstNode {
|
|
||||||
constructor(index, min, max) {
|
|
||||||
super("var");
|
|
||||||
this.index = index;
|
|
||||||
this.min = min;
|
|
||||||
this.max = max;
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
visitor.visitVariable(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AstVariableDefinition extends AstNode {
|
|
||||||
constructor(variable, arg) {
|
|
||||||
super("definition");
|
|
||||||
this.variable = variable;
|
|
||||||
this.arg = arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(visitor) {
|
|
||||||
visitor.visitVariableDefinition(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ExpressionBuilderVisitor {
|
|
||||||
constructor() {
|
|
||||||
this.parts = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
visitArgument(arg) {
|
|
||||||
this.parts.push(
|
|
||||||
"Math.max(",
|
|
||||||
arg.min,
|
|
||||||
", Math.min(",
|
|
||||||
arg.max,
|
|
||||||
", src[srcOffset + ",
|
|
||||||
arg.index,
|
|
||||||
"]))"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitVariable(variable) {
|
|
||||||
this.parts.push("v", variable.index);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitLiteral(literal) {
|
|
||||||
this.parts.push(literal.number);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitBinaryOperation(operation) {
|
|
||||||
this.parts.push("(");
|
|
||||||
operation.arg1.visit(this);
|
|
||||||
this.parts.push(" ", operation.op, " ");
|
|
||||||
operation.arg2.visit(this);
|
|
||||||
this.parts.push(")");
|
|
||||||
}
|
|
||||||
|
|
||||||
visitVariableDefinition(definition) {
|
|
||||||
this.parts.push("var ");
|
|
||||||
definition.variable.visit(this);
|
|
||||||
this.parts.push(" = ");
|
|
||||||
definition.arg.visit(this);
|
|
||||||
this.parts.push(";");
|
|
||||||
}
|
|
||||||
|
|
||||||
visitMin(max) {
|
|
||||||
this.parts.push("Math.min(");
|
|
||||||
max.arg.visit(this);
|
|
||||||
this.parts.push(", ", max.max, ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
toString() {
|
|
||||||
return this.parts.join("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildAddOperation(num1, num2) {
|
|
||||||
if (num2.type === "literal" && num2.number === 0) {
|
|
||||||
// optimization: second operand is 0
|
|
||||||
return num1;
|
|
||||||
}
|
|
||||||
if (num1.type === "literal" && num1.number === 0) {
|
|
||||||
// optimization: first operand is 0
|
|
||||||
return num2;
|
|
||||||
}
|
|
||||||
if (num2.type === "literal" && num1.type === "literal") {
|
|
||||||
// optimization: operands operand are literals
|
|
||||||
return new AstLiteral(num1.number + num2.number);
|
|
||||||
}
|
|
||||||
return new AstBinaryOperation(
|
|
||||||
"+",
|
|
||||||
num1,
|
|
||||||
num2,
|
|
||||||
num1.min + num2.min,
|
|
||||||
num1.max + num2.max
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildMulOperation(num1, num2) {
|
|
||||||
if (num2.type === "literal") {
|
|
||||||
// optimization: second operands is a literal...
|
|
||||||
if (num2.number === 0) {
|
|
||||||
return new AstLiteral(0); // and it's 0
|
|
||||||
} else if (num2.number === 1) {
|
|
||||||
return num1; // and it's 1
|
|
||||||
} else if (num1.type === "literal") {
|
|
||||||
// ... and first operands is a literal too
|
|
||||||
return new AstLiteral(num1.number * num2.number);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (num1.type === "literal") {
|
|
||||||
// optimization: first operands is a literal...
|
|
||||||
if (num1.number === 0) {
|
|
||||||
return new AstLiteral(0); // and it's 0
|
|
||||||
} else if (num1.number === 1) {
|
|
||||||
return num2; // and it's 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const min = Math.min(
|
|
||||||
num1.min * num2.min,
|
|
||||||
num1.min * num2.max,
|
|
||||||
num1.max * num2.min,
|
|
||||||
num1.max * num2.max
|
|
||||||
);
|
|
||||||
const max = Math.max(
|
|
||||||
num1.min * num2.min,
|
|
||||||
num1.min * num2.max,
|
|
||||||
num1.max * num2.min,
|
|
||||||
num1.max * num2.max
|
|
||||||
);
|
|
||||||
return new AstBinaryOperation("*", num1, num2, min, max);
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildSubOperation(num1, num2) {
|
|
||||||
if (num2.type === "literal") {
|
|
||||||
// optimization: second operands is a literal...
|
|
||||||
if (num2.number === 0) {
|
|
||||||
return num1; // ... and it's 0
|
|
||||||
} else if (num1.type === "literal") {
|
|
||||||
// ... and first operands is a literal too
|
|
||||||
return new AstLiteral(num1.number - num2.number);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
num2.type === "binary" &&
|
|
||||||
num2.op === "-" &&
|
|
||||||
num1.type === "literal" &&
|
|
||||||
num1.number === 1 &&
|
|
||||||
num2.arg1.type === "literal" &&
|
|
||||||
num2.arg1.number === 1
|
|
||||||
) {
|
|
||||||
// optimization for case: 1 - (1 - x)
|
|
||||||
return num2.arg2;
|
|
||||||
}
|
|
||||||
return new AstBinaryOperation(
|
|
||||||
"-",
|
|
||||||
num1,
|
|
||||||
num2,
|
|
||||||
num1.min - num2.max,
|
|
||||||
num1.max - num2.min
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildMinOperation(num1, max) {
|
|
||||||
if (num1.min >= max) {
|
|
||||||
// optimization: num1 min value is not less than required max
|
|
||||||
return new AstLiteral(max); // just returning max
|
|
||||||
} else if (num1.max <= max) {
|
|
||||||
// optimization: num1 max value is not greater than required max
|
|
||||||
return num1; // just returning an argument
|
|
||||||
}
|
|
||||||
return new AstMin(num1, max);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-shadow
|
|
||||||
class PostScriptCompiler {
|
|
||||||
compile(code, domain, range) {
|
|
||||||
const stack = [];
|
|
||||||
const instructions = [];
|
|
||||||
const inputSize = domain.length >> 1,
|
|
||||||
outputSize = range.length >> 1;
|
|
||||||
let lastRegister = 0;
|
|
||||||
let n, j;
|
|
||||||
let num1, num2, ast1, ast2, tmpVar, item;
|
|
||||||
for (let i = 0; i < inputSize; i++) {
|
|
||||||
stack.push(new AstArgument(i, domain[i * 2], domain[i * 2 + 1]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0, ii = code.length; i < ii; i++) {
|
switch (item) {
|
||||||
item = code[i];
|
case "add":
|
||||||
if (typeof item === "number") {
|
if (stack.length < 2) {
|
||||||
stack.push(new AstLiteral(item));
|
return null;
|
||||||
continue;
|
}
|
||||||
}
|
num2 = stack.pop();
|
||||||
|
num1 = stack.pop();
|
||||||
switch (item) {
|
stack.push(buildAddOperation(num1, num2));
|
||||||
case "add":
|
break;
|
||||||
if (stack.length < 2) {
|
case "cvr":
|
||||||
return null;
|
if (stack.length < 1) {
|
||||||
}
|
return null;
|
||||||
num2 = stack.pop();
|
}
|
||||||
|
break;
|
||||||
|
case "mul":
|
||||||
|
if (stack.length < 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
num2 = stack.pop();
|
||||||
|
num1 = stack.pop();
|
||||||
|
stack.push(buildMulOperation(num1, num2));
|
||||||
|
break;
|
||||||
|
case "sub":
|
||||||
|
if (stack.length < 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
num2 = stack.pop();
|
||||||
|
num1 = stack.pop();
|
||||||
|
stack.push(buildSubOperation(num1, num2));
|
||||||
|
break;
|
||||||
|
case "exch":
|
||||||
|
if (stack.length < 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ast1 = stack.pop();
|
||||||
|
ast2 = stack.pop();
|
||||||
|
stack.push(ast1, ast2);
|
||||||
|
break;
|
||||||
|
case "pop":
|
||||||
|
if (stack.length < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
stack.pop();
|
||||||
|
break;
|
||||||
|
case "index":
|
||||||
|
if (stack.length < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
num1 = stack.pop();
|
||||||
|
if (num1.type !== "literal") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
n = num1.number;
|
||||||
|
if (n < 0 || !Number.isInteger(n) || stack.length < n) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ast1 = stack[stack.length - n - 1];
|
||||||
|
if (ast1.type === "literal" || ast1.type === "var") {
|
||||||
|
stack.push(ast1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tmpVar = new AstVariable(lastRegister++, ast1.min, ast1.max);
|
||||||
|
stack[stack.length - n - 1] = tmpVar;
|
||||||
|
stack.push(tmpVar);
|
||||||
|
instructions.push(new AstVariableDefinition(tmpVar, ast1));
|
||||||
|
break;
|
||||||
|
case "dup":
|
||||||
|
if (stack.length < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
typeof code[i + 1] === "number" &&
|
||||||
|
code[i + 2] === "gt" &&
|
||||||
|
code[i + 3] === i + 7 &&
|
||||||
|
code[i + 4] === "jz" &&
|
||||||
|
code[i + 5] === "pop" &&
|
||||||
|
code[i + 6] === code[i + 1]
|
||||||
|
) {
|
||||||
|
// special case of the commands sequence for the min operation
|
||||||
num1 = stack.pop();
|
num1 = stack.pop();
|
||||||
stack.push(buildAddOperation(num1, num2));
|
stack.push(buildMinOperation(num1, code[i + 1]));
|
||||||
|
i += 6;
|
||||||
break;
|
break;
|
||||||
case "cvr":
|
}
|
||||||
if (stack.length < 1) {
|
ast1 = stack.at(-1);
|
||||||
return null;
|
if (ast1.type === "literal" || ast1.type === "var") {
|
||||||
}
|
// we don't have to save into intermediate variable a literal or
|
||||||
|
// variable.
|
||||||
|
stack.push(ast1);
|
||||||
break;
|
break;
|
||||||
case "mul":
|
}
|
||||||
if (stack.length < 2) {
|
tmpVar = new AstVariable(lastRegister++, ast1.min, ast1.max);
|
||||||
return null;
|
stack[stack.length - 1] = tmpVar;
|
||||||
}
|
stack.push(tmpVar);
|
||||||
num2 = stack.pop();
|
instructions.push(new AstVariableDefinition(tmpVar, ast1));
|
||||||
num1 = stack.pop();
|
break;
|
||||||
stack.push(buildMulOperation(num1, num2));
|
case "roll":
|
||||||
break;
|
if (stack.length < 2) {
|
||||||
case "sub":
|
return null;
|
||||||
if (stack.length < 2) {
|
}
|
||||||
return null;
|
num2 = stack.pop();
|
||||||
}
|
num1 = stack.pop();
|
||||||
num2 = stack.pop();
|
if (num2.type !== "literal" || num1.type !== "literal") {
|
||||||
num1 = stack.pop();
|
// both roll operands must be numbers
|
||||||
stack.push(buildSubOperation(num1, num2));
|
return null;
|
||||||
break;
|
}
|
||||||
case "exch":
|
j = num2.number;
|
||||||
if (stack.length < 2) {
|
n = num1.number;
|
||||||
return null;
|
if (
|
||||||
}
|
n <= 0 ||
|
||||||
ast1 = stack.pop();
|
!Number.isInteger(n) ||
|
||||||
ast2 = stack.pop();
|
!Number.isInteger(j) ||
|
||||||
stack.push(ast1, ast2);
|
stack.length < n
|
||||||
break;
|
) {
|
||||||
case "pop":
|
// ... and integers
|
||||||
if (stack.length < 1) {
|
return null;
|
||||||
return null;
|
}
|
||||||
}
|
j = ((j % n) + n) % n;
|
||||||
stack.pop();
|
if (j === 0) {
|
||||||
break;
|
break; // just skipping -- there are nothing to rotate
|
||||||
case "index":
|
}
|
||||||
if (stack.length < 1) {
|
Array.prototype.push.apply(
|
||||||
return null;
|
stack,
|
||||||
}
|
stack.splice(stack.length - n, n - j)
|
||||||
num1 = stack.pop();
|
);
|
||||||
if (num1.type !== "literal") {
|
break;
|
||||||
return null;
|
default:
|
||||||
}
|
return null; // unsupported operator
|
||||||
n = num1.number;
|
|
||||||
if (n < 0 || !Number.isInteger(n) || stack.length < n) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
ast1 = stack[stack.length - n - 1];
|
|
||||||
if (ast1.type === "literal" || ast1.type === "var") {
|
|
||||||
stack.push(ast1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
tmpVar = new AstVariable(lastRegister++, ast1.min, ast1.max);
|
|
||||||
stack[stack.length - n - 1] = tmpVar;
|
|
||||||
stack.push(tmpVar);
|
|
||||||
instructions.push(new AstVariableDefinition(tmpVar, ast1));
|
|
||||||
break;
|
|
||||||
case "dup":
|
|
||||||
if (stack.length < 1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
typeof code[i + 1] === "number" &&
|
|
||||||
code[i + 2] === "gt" &&
|
|
||||||
code[i + 3] === i + 7 &&
|
|
||||||
code[i + 4] === "jz" &&
|
|
||||||
code[i + 5] === "pop" &&
|
|
||||||
code[i + 6] === code[i + 1]
|
|
||||||
) {
|
|
||||||
// special case of the commands sequence for the min operation
|
|
||||||
num1 = stack.pop();
|
|
||||||
stack.push(buildMinOperation(num1, code[i + 1]));
|
|
||||||
i += 6;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ast1 = stack.at(-1);
|
|
||||||
if (ast1.type === "literal" || ast1.type === "var") {
|
|
||||||
// we don't have to save into intermediate variable a literal or
|
|
||||||
// variable.
|
|
||||||
stack.push(ast1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
tmpVar = new AstVariable(lastRegister++, ast1.min, ast1.max);
|
|
||||||
stack[stack.length - 1] = tmpVar;
|
|
||||||
stack.push(tmpVar);
|
|
||||||
instructions.push(new AstVariableDefinition(tmpVar, ast1));
|
|
||||||
break;
|
|
||||||
case "roll":
|
|
||||||
if (stack.length < 2) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
num2 = stack.pop();
|
|
||||||
num1 = stack.pop();
|
|
||||||
if (num2.type !== "literal" || num1.type !== "literal") {
|
|
||||||
// both roll operands must be numbers
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
j = num2.number;
|
|
||||||
n = num1.number;
|
|
||||||
if (
|
|
||||||
n <= 0 ||
|
|
||||||
!Number.isInteger(n) ||
|
|
||||||
!Number.isInteger(j) ||
|
|
||||||
stack.length < n
|
|
||||||
) {
|
|
||||||
// ... and integers
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
j = ((j % n) + n) % n;
|
|
||||||
if (j === 0) {
|
|
||||||
break; // just skipping -- there are nothing to rotate
|
|
||||||
}
|
|
||||||
Array.prototype.push.apply(
|
|
||||||
stack,
|
|
||||||
stack.splice(stack.length - n, n - j)
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return null; // unsupported operator
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stack.length !== outputSize) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = [];
|
|
||||||
for (const instruction of instructions) {
|
|
||||||
const statementBuilder = new ExpressionBuilderVisitor();
|
|
||||||
instruction.visit(statementBuilder);
|
|
||||||
result.push(statementBuilder.toString());
|
|
||||||
}
|
|
||||||
for (let i = 0, ii = stack.length; i < ii; i++) {
|
|
||||||
const expr = stack[i],
|
|
||||||
statementBuilder = new ExpressionBuilderVisitor();
|
|
||||||
expr.visit(statementBuilder);
|
|
||||||
const min = range[i * 2],
|
|
||||||
max = range[i * 2 + 1];
|
|
||||||
const out = [statementBuilder.toString()];
|
|
||||||
if (min > expr.min) {
|
|
||||||
out.unshift("Math.max(", min, ", ");
|
|
||||||
out.push(")");
|
|
||||||
}
|
|
||||||
if (max < expr.max) {
|
|
||||||
out.unshift("Math.min(", max, ", ");
|
|
||||||
out.push(")");
|
|
||||||
}
|
|
||||||
out.unshift("dest[destOffset + ", i, "] = ");
|
|
||||||
out.push(";");
|
|
||||||
result.push(out.join(""));
|
|
||||||
}
|
|
||||||
return result.join("\n");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return PostScriptCompiler;
|
if (stack.length !== outputSize) {
|
||||||
})();
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = [];
|
||||||
|
for (const instruction of instructions) {
|
||||||
|
const statementBuilder = new ExpressionBuilderVisitor();
|
||||||
|
instruction.visit(statementBuilder);
|
||||||
|
result.push(statementBuilder.toString());
|
||||||
|
}
|
||||||
|
for (let i = 0, ii = stack.length; i < ii; i++) {
|
||||||
|
const expr = stack[i],
|
||||||
|
statementBuilder = new ExpressionBuilderVisitor();
|
||||||
|
expr.visit(statementBuilder);
|
||||||
|
const min = range[i * 2],
|
||||||
|
max = range[i * 2 + 1];
|
||||||
|
const out = [statementBuilder.toString()];
|
||||||
|
if (min > expr.min) {
|
||||||
|
out.unshift("Math.max(", min, ", ");
|
||||||
|
out.push(")");
|
||||||
|
}
|
||||||
|
if (max < expr.max) {
|
||||||
|
out.unshift("Math.min(", max, ", ");
|
||||||
|
out.push(")");
|
||||||
|
}
|
||||||
|
out.unshift("dest[destOffset + ", i, "] = ");
|
||||||
|
out.push(";");
|
||||||
|
result.push(out.join(""));
|
||||||
|
}
|
||||||
|
return result.join("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
isPDFFunction,
|
isPDFFunction,
|
||||||
|
Loading…
Reference in New Issue
Block a user