Procedure: complex operators for RPN expressions

This commit is contained in:
Yaski
2012-07-06 03:22:23 +06:00
parent a53dba6d39
commit b5bf529a2c
2 changed files with 140 additions and 42 deletions

View File

@@ -7,8 +7,7 @@
* */
package alternativa.engine3d.materials.compiler {
/**
/**
* @private
*/
public class CommandType {
@@ -51,7 +50,68 @@ package alternativa.engine3d.materials.compiler {
["mov", "add", "sub", "mul", "div", "rcp", "min", "max", "frc", "sqt", "rsq", "pow", "log", "exp",
"nrm", "sin", "cos", "crs", "dp3", "dp4", "abs", "neg", "sat", "m33", "m44", "m34","1a","1b","1c","1d","1e","1f","20","21","22","23","24","25","26", "kil", "tex", "sge", "slt", "2b", "seq", "sne"]
);
public function CommandType() {
public static const commands:Object = formCommandsList();
private static function formCommandsList():Object {
// dp3( ,) +- */
var result:Object = {};
result[","] = new CommandType(0, 1);
result[")"] = new CommandType(0, 1);
result["+"] = new CommandType(2, 2);
result["-"] = new CommandType(2, 2);
result["*"] = new CommandType(2, 3);
result["/"] = new CommandType(2, 3);
result["mov"] = new CommandType(1);
result["add"] = new CommandType();
result["sub"] = new CommandType();
result["mul"] = new CommandType();
result["div"] = new CommandType();
result["rcp"] = new CommandType(1);
result["min"] = new CommandType();
result["max"] = new CommandType();
result["frc"] = new CommandType(1);
result["sqt"] = new CommandType(1);
result["rsq"] = new CommandType(1);
result["pow"] = new CommandType();
result["log"] = new CommandType(1);
result["exp"] = new CommandType(1);
result["nrm"] = new CommandType(1);
result["sin"] = new CommandType(1);
result["cos"] = new CommandType(1);
result["crs"] = new CommandType();
result["dp3"] = new CommandType();
result["dp4"] = new CommandType();
result["abs"] = new CommandType(1);
result["neg"] = new CommandType(1);
result["sat"] = new CommandType(1);
result["m33"] = new CommandType();
result["m44"] = new CommandType();
result["m34"] = new CommandType();
// result["kil"] = new CommandType();
// result["tex"] = new CommandType();
result["sge"] = new CommandType();
result["slt"] = new CommandType();
result["seq"] = new CommandType();
result["sne"] = new CommandType();
// setting ids
for (var s:String in result) {
result[s].id = s;
}
result["+"].id = "add";
result["-"].id = "sub";
result["*"].id = "mul";
result["/"].id = "div";
return result;
}
public var id:String;
public var priority:int;
public var numInputs:int;
public function CommandType(numInputs:int = 2, priority:int = 0) {
this.numInputs = numInputs;
this.priority = priority;
}
}
}
}

View File

@@ -8,13 +8,13 @@
package alternativa.engine3d.materials.compiler {
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.alternativa3d;
import flash.display3D.Context3DProgramType;
import flash.utils.ByteArray;
import flash.utils.Endian;
import flash.display3D.Context3DProgramType;
import flash.utils.ByteArray;
import flash.utils.Endian;
use namespace alternativa3d;
use namespace alternativa3d;
/**
* @private
@@ -188,7 +188,9 @@ package alternativa.engine3d.materials.compiler {
// 7) swizzle handled like in AGAL compiler
// TODO: handle swizzle smartly (.zw -> .zwzw)
// TODO: implement complex operators (dp3, nrm, sat)
// TODO: implement operators inputs size check
// TODO: implement operators auto output size
// TODO: implement operators output swizzle
// TODO: minimize output temporaries count (sort operators by priority)
// TODO: write to ByteArray directly
// TODO: implement negate unary operator (-x)
@@ -203,7 +205,7 @@ package alternativa.engine3d.materials.compiler {
if (commentIndex >= 0) {
source = source.substr(0, commentIndex);
}
var operands:Array = source.match(/([activo]((\[.+\])|(\d+))(\.[xyzw]{1,4})?|[+\-*\/=])/g);
var operands:Array = source.match(/[a-z]+(((\[.+\])|(\d+))(\.[xyzw]{1,4})?)?|[+\-*\/=(),]/g);
var numOperands:int = operands.length;
if (numOperands < 3) return;
if (operands[1] != "=") {
@@ -226,19 +228,22 @@ package alternativa.engine3d.materials.compiler {
var outputVar:String = (maskIndex >= 0) ? output.substr(0, maskIndex) : output;
if (outputMaskLen == 4) output = outputVar;
var operators:Vector.<String> = new Vector.<String>();
var operators:Vector.<CommandType> = new Vector.<CommandType>();
var variables:Vector.<String> = new Vector.<String>();
function getPriority(command:String):int {
return ((command == "+" || command == "-") ? 1 : 2);
function getPriority(command:CommandType):int {
return command.priority;
}
function getSwizzleLen(value:String):uint {
var i:int = value.lastIndexOf(".");
return (i < 0 ? 4 : value.length - i - 1);
}
function writeCommand(command:String, operandIndex:int, isLastOperator:Boolean):void {
var b:String = variables.pop();
function writeCommand(command:CommandType, numInputs:int, operandIndex:int, isLastOperator:Boolean):void {
if (numInputs != command.numInputs) {
throw new Error("Syntax error. Operator " + command.id + " inputs count wrong. Expected " + command.numInputs + ".");
}
var b:String = (numInputs > 1) ? variables.pop() : null;
var a:String = variables.pop();
if (a == null || b == null) throw new Error("Syntax error. Variable expected after " + command + ".");
if (a == null || (numInputs > 1 && b == null)) throw new Error("Syntax error. Variable expected after " + command + ".");
// Check can we use output for writing
var i:int;
for (i = 0; i < variables.length; i++) {
@@ -253,12 +258,17 @@ package alternativa.engine3d.materials.compiler {
throw new Error("Expression is too complex. Output used as source.");
}
}
var aSwizzleLen:uint = getSwizzleLen(a);
var bSwizzleLen:uint = getSwizzleLen(b);
if (aSwizzleLen != bSwizzleLen && aSwizzleLen != 1 && bSwizzleLen != 1) {
throw new Error("Variables size mistmatch " + a + " and " + b + ".");
}
var maxSwizzle:uint = (aSwizzleLen > bSwizzleLen) ? aSwizzleLen : bSwizzleLen;
if (numInputs <= 1) {
maxSwizzle = getSwizzleLen(a);
} else {
var aSwizzleLen:uint = getSwizzleLen(a);
var bSwizzleLen:uint = getSwizzleLen(b);
if (aSwizzleLen != bSwizzleLen && aSwizzleLen != 1 && bSwizzleLen != 1) {
throw new Error("Variables size mistmatch " + a + " and " + b + ".");
}
maxSwizzle = (aSwizzleLen > bSwizzleLen) ? aSwizzleLen : bSwizzleLen;
}
if (maxSwizzle > outputMaskLen || (isLastOperator && maxSwizzle != outputMaskLen && maxSwizzle != 1)) {
throw new Error("Expression differs in size with output " + output + ".");
}
@@ -273,19 +283,10 @@ package alternativa.engine3d.materials.compiler {
out = outputVar + ".xyz";
}
}
switch (command) {
case "+":
writeAGALExpression("add " + out + " " + a + " " + b);
break;
case "-":
writeAGALExpression("sub " + out + " " + a + " " + b);
break;
case "*":
writeAGALExpression("mul " + out + " " + a + " " + b);
break;
case "/":
writeAGALExpression("div " + out + " " + a + " " + b);
break;
if (numInputs > 1) {
writeAGALExpression(command.id + " " + out + " " + a + " " + b);
} else {
writeAGALExpression(command.id + " " + out + " " + a);
}
variables.push(out);
}
@@ -297,6 +298,7 @@ package alternativa.engine3d.materials.compiler {
}
writeAGALExpression("mov " + output + " " + operand);
}
var command:CommandType;
var wasVariable:Boolean = false;
for (i = 2; i < numOperands; i++) {
operand = operands[i];
@@ -306,23 +308,59 @@ package alternativa.engine3d.materials.compiler {
case "*":
case "/":
if (!wasVariable) throw new Error("Syntax error. Variable expected before " + operand + ".");
command = CommandType.commands[operand];
// process operators from stack while their priority is higher or equal
while (operators.length > 0 && getPriority(operators[operators.length - 1]) >= getPriority(operand)) {
writeCommand(operators.pop(), i, false);
while (operators.length > 0 && getPriority(operators[operators.length - 1]) >= getPriority(command)) {
writeCommand(operators.pop(), 2, i, false);
}
operators.push(operand);
operators.push(command);
wasVariable = false;
break;
case ")":
case ",":
if (!wasVariable) throw new Error("Syntax error. Variable expected before " + operand + ".");
command = CommandType.commands[operand];
// process all commands before until comma or left bracket
while (operators.length > 0 && getPriority(operators[operators.length - 1]) > getPriority(command)) {
writeCommand(operators.pop(), 2, i, false);
}
if (operand == ",") {
operators.push(command);
wasVariable = false;
} else {
// count all commas until function
var numParams:int = 1;
while ((command = operators.pop()) != null && command.priority != 0) {
numParams++;
}
writeCommand(command, numParams, i, i == numOperands - 1);
wasVariable = true;
}
break;
default:
if (wasVariable) throw new Error("Syntax error. Operator expected before " + operand + ".");
variables.push(operand);
wasVariable = true;
command = CommandType.commands[operand];
if (command != null) {
// is command
// test bracket
if (i + 1 >= numOperands || operands[i + 1] != "(") {
throw new Error("Syntax error. Expected bracket after " + operand + ".");
}
operators.push(command);
i++; // skip bracket
// wasVariable = false;
} else {
// is variable
variables.push(operand);
wasVariable = true;
}
break;
}
}
// process remained operators
while ((operand = operators.pop()) != null) {
writeCommand(operand, numOperands, operators.length == 0);
while ((command = operators.pop()) != null) {
writeCommand(command, 2, numOperands, operators.length == 0);
}
if (variables.length > 1) throw new Error("Syntax error. Unknown novel error.");
}