/*
* JAME 6.2.1
* http://jame.sourceforge.net
*
* Copyright 2001, 2016 Andrea Medeghini
*
* This file is part of JAME.
*
* JAME is an application for creating fractals and other graphics artifacts.
*
* JAME is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JAME is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with JAME. If not, see <http://www.gnu.org/licenses/>.
*
*/
package net.sf.jame.contextfree.parser;
import org.antlr.v4.runtime.Token;
class ASTOperator extends ASTExpression {
private char op;
private ASTExpression left;
private ASTExpression right;
public ASTOperator(char op, ASTExpression left, ASTExpression right, Token location) {
super(location);
this.op = op;
this.left = left;
this.right = right;
int index = "NP!+-*/^_<>LG=n&|X".indexOf(""+op);
if (index == -1) {
error("Unknown operator");
} else if (index < 3) {
if (right != null) {
error("Operator takes only one operand");
}
} else {
if (right != null) {
error("Operator takes two operands");
}
}
}
public ASTOperator(char op, ASTExpression left, Token location) {
this(op, left, null, location);
}
public char getOp() {
return op;
}
public ASTExpression getLeft() {
return left;
}
public ASTExpression getRight() {
return right;
}
// public static ASTExpression makeCanonical(List<ASTExpression> temp) {
// // Receive a vector of modification terms and return an ASTexpression
// // with those terms rearranged into TRSSF canonical order.
// // Duplicate terms are left in the input vector.
// List<ASTExpression> dropped = new ArrayList<ASTExpression>();
//
// try {
// ASTModTerm[] result = new ASTModTerm[1];
//
// ASTModTerm[] x = new ASTModTerm[1];
// ASTModTerm[] y = new ASTModTerm[1];
// ASTModTerm[] z = new ASTModTerm[1];
// ASTModTerm[] rot = new ASTModTerm[1];
// ASTModTerm[] skew = new ASTModTerm[1];
// ASTModTerm[] size = new ASTModTerm[1];
// ASTModTerm[] zsize = new ASTModTerm[1];
// ASTModTerm[] flip = new ASTModTerm[1];
// ASTModTerm[] transform = new ASTModTerm[1];
// ASTModTerm[] var = new ASTModTerm[1];
//
// for (ASTExpression exp : temp) {
// if (exp.type != EExpType.ModType) {
// throw new RuntimeException("Non-adjustment in shape adjustment context");
// }
//
// ASTModTerm mod = (ASTModTerm) exp;
//
// int argcount = 0;
// if (mod.getArguments() != null && mod.getArguments().type == EExpType.NumericType) {
// argcount = mod.getArguments().evaluate((double[])null, 0, null);
// }
//
// switch (mod.getModType()) {
// case x:
// setmod(x, mod, dropped);
// if (argcount > 1) {
// y[0] = null;
// }
// break;
// case y:
// setmod(y, mod, dropped);
// break;
// case z:
// setmod(z, mod, dropped);
// break;
// case modification:
// case transform:
// setmod(transform, mod, dropped);
// break;
// case rot:
// setmod(rot, mod, dropped);
// break;
// case size:
// setmod(size, mod, dropped);
// break;
// case zsize:
// setmod(zsize, mod, dropped);
// break;
// case skew:
// setmod(skew, mod, dropped);
// break;
// case flip:
// setmod(flip, mod, dropped);
// break;
// default:
// addmod(var, mod);
// break;
// }
// }
//
// temp.clear();
// temp.addAll(dropped);
//
// // If x and y are provided then merge them into a single (x,y) modification
// if (x[0] != null && y[0] != null && x[0].getArguments().evaluate((double[])null, 0, null) == 1 && y[0].getArguments().evaluate((double[])null, 0, null) == 1) {
// x[0].setArguments(new ASTCons(x[0].getArguments(), y[0].getArguments()));
// y[0].setArguments(null);
// y[0] = null;
// }
//
// addmod(result, x[0]);
// addmod(result, y[0]);
// addmod(result, z[0]);
// addmod(result, rot[0]);
// addmod(result, size[0]);
// addmod(result, zsize[0]);
// addmod(result, skew[0]);
// addmod(result, flip[0]);
// addmod(result, transform[0]);
// addmod(result, var[0]);
//
// return result[0];
// } catch (RuntimeException e) {
// temp.clear();
// dropped.clear();
// throw e;
// }
// }
// private static void addmod(ASTExpression[] var, ASTExpression mod) {
// if (mod == null)
// return;
// if (var[0] != null) {
// var[0] = new ASTOperator('+', var[0], mod);
// } else {
// var[0] = mod;
// }
// }
//
// private static void setmod(ASTModTerm[] mod, ASTModTerm newmod, List<ASTExpression> dropped) {
// if (mod[0] != null)
// dropped.add(mod[0]);
// mod[0] = newmod;
// }
@Override
public int evaluate(double[] result, int length, RTI rti) {
double[] l = new double[] { 0.0 };
double[] r = new double[] { 0.0 };
if (result != null && length < 1)
return -1;
if (type == EExpType.FlagType && op == '+') {
if (left.evaluate(result != null ? l : null, 1, rti) != 1)
return -1;
if (right == null || right.evaluate(result != null ? r : null, 1, rti) != 1)
return -1;
int f = (int) l[0] | (int) r[0];
if (result != null)
result[0] = f;
return 1;
}
if (type != EExpType.NumericType) {
error("Non-numeric expression in a numeric context");
return -1;
}
if (left.evaluate(result != null ? l : null, 1, rti) != 1) {
error("illegal operand");
return -1;
}
// short-circuit evaluate && and ||
if (result != null && (op == '&' || op == '|')) {
if (l[0] != 0.0 && op == '|') {
result[0] = l[0];
return 1;
}
if (l[0] == 0.0 && op == '&') {
result[0] = 0.0;
return 1;
}
}
int rightnum = right != null ? right.evaluate(result != null ? r : null, 1, rti) : 0;
if (rightnum == 0 && (op == 'N' || op == 'P' || op == '!')) {
if (result != null) {
switch (op) {
case 'P':
result[0] = +l[0];
break;
case 'N':
result[0] = -l[0];
break;
case '!':
result[0] = l[0] == 0.0 ? 1.0 : 0.0;
break;
default:
return -1;
}
}
return 1;
}
if (rightnum != 1) {
error("illegal operand");
return -1;
}
if (result != null) {
switch (op) {
case '+':
result[0] = l[0] + r[0];
break;
case '-':
result[0] = l[0] - r[0];
break;
case '_':
result[0] = l[0] - r[0] > 0.0 ? l[0] - r[0] : 0.0;
break;
case '*':
result[0] = l[0] * r[0];
break;
case '/':
result[0] = l[0] / r[0];
break;
case '<':
result[0] = (l[0] < r[0]) ? 1.0 : 0.0;
break;
case 'L':
result[0] = (l[0] <= r[0]) ? 1.0 : 0.0;
break;
case '>':
result[0] = (l[0] > r[0]) ? 1.0 : 0.0;
break;
case 'G':
result[0] = (l[0] >= r[0]) ? 1.0 : 0.0;
break;
case '=':
result[0] = (l[0] == r[0]) ? 1.0 : 0.0;
break;
case 'n':
result[0] = (l[0] != r[0]) ? 1.0 : 0.0;
break;
case '&':
result[0] = r[0];
break;
case '|':
result[0] = r[0];
break;
case 'X':
result[0] = (l[0] != 0 && r[0] == 0 || l[0] == 0 && r[0] != 0) ? 1.0 : 0.0;
break;
case '^':
result[0] = Math.pow(l[0], r[0]);
if (isNatural && result[0] < 9007199254740992.0) {
long pow = 1;
long il = (long)l[0];
long ir = (long)r[0];
while (ir != 0) {
if ((ir & 1) != 0) pow *= il;
il *= il;
ir >>= 1;
}
result[0] = pow;
}
break;
default:
return -1;
}
} else {
if ("+-*/^_<>LG=n&|X".indexOf(op) == -1)
return -1;
}
return 1;
}
@Override
public void entropy(StringBuilder e) {
left.entropy(e);
if (right != null)
right.entropy(e);
switch (op) {
case '*':
e.append("\u002E\u0032\u00D9\u002C\u0041\u00FE");
break;
case '/':
e.append("\u006B\u0015\u0023\u0041\u009E\u00EB");
break;
case '+':
e.append("\u00D7\u00B1\u00B0\u0039\u0033\u00C8");
break;
case '-':
e.append("\u005D\u00E7\u00F0\u0094\u00C4\u0013");
break;
case '^':
e.append("\u0002\u003C\u0068\u0036\u00C5\u00A0");
break;
case 'N':
e.append("\u0055\u0089\u0051\u0046\u00DB\u0084");
break;
case 'P':
e.append("\u008E\u00AC\u0029\u004B\u000E\u00DC");
break;
case '!':
e.append("\u0019\u003A\u003E\u0053\u0014\u00EA");
break;
case '<':
e.append("\u00BE\u00DB\u00C4\u00A6\u004E\u00AD");
break;
case '>':
e.append("\u00C7\u00D9\u0057\u0032\u00D6\u0087");
break;
case 'L':
e.append("\u00E3\u0056\u007E\u0044\u0057\u0080");
break;
case 'G':
e.append("\u00B1\u002D\u002A\u00CC\u002C\u0040");
break;
case '=':
e.append("\u0078\u0048\u00C2\u0095\u00A9\u00E2");
break;
case 'n':
e.append("\u0036\u00CC\u0001\u003B\u002F\u00AD");
break;
case '&':
e.append("\u0028\u009B\u00FB\u007F\u00DB\u009C");
break;
case '|':
e.append("\u002E\u0040\u001B\u0044\u0015\u007C");
break;
case 'X':
e.append("\u00A7\u002B\u0092\u00FA\u00FC\u00F9");
break;
case '_':
e.append("\u0060\u002F\u0010\u00AD\u0010\u00FF");
break;
default:
break;
}
}
@Override
public ASTExpression simplify() {
left = left.simplify();
if (right != null)
right = right.simplify();
if (isConstant && (type == EExpType.NumericType || type == EExpType.FlagType)) {
double[] result = new double[1];
if (evaluate(result, 1, null) != 1) {
return null;
}
ASTReal r = new ASTReal(result[0], location);
r.setType(type);
r.setIsNatural(isNatural);
return r;
}
return this;
}
@Override
public ASTExpression compile(ECompilePhase ph) {
if (left != null)
left = left.compile(ph);
if (right != null)
right = right.compile(ph);
switch (ph) {
case TypeCheck:
{
isConstant = left.isConstant() && (right == null || right.isConstant());
locality = right != null ? combineLocality(left.getLocality(), right.getLocality()) : left.getLocality();
type = right != null ? EExpType.expTypeByOrdinal(left.getType().ordinal() | right.getType().ordinal()) : left.getType();
if ("+_*<>LG=n&|X^!".indexOf(""+op) != -1) {
isNatural = left.isNatural() && (right == null || right.isNatural());
}
if (op == '+') {
if (type == EExpType.FlagType && type != EExpType.NumericType) {
error("Operands must be numeric or flags");
}
} else {
if (type != EExpType.NumericType) {
error("Operand(s) must be numeric");
}
}
if (op == '_' && !isNatural()) {
error("Proper subtraction operands must be natural");
}
}
break;
case Simplify:
break;
default:
break;
}
return null;
}
}