/*
* Copyright 2014 Igor Maznitsa (http://www.igormaznitsa.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.igormaznitsa.prol.data;
import com.igormaznitsa.prol.exceptions.ProlCriticalError;
import java.io.PrintWriter;
/**
* This class represents a prolog operator
*
* @author Igor Maznitsa (igor.maznitsa@igormaznitsa.com)
*/
public final class Operator extends Term {
/**
* The max operator priority constant
*/
public static final int PRIORITY_MAX = 0;
/**
* The min operator priority constant
*/
public static final int PRIORITY_MIN = 1200;
/**
* The constant represents the 'xf' type
*/
public static final int OPTYPE_XF = 0;
/**
* The constant represents the 'yf' type
*/
public static final int OPTYPE_YF = 1;
/**
* The constant represents the 'fx' type
*/
public static final int OPTYPE_FX = 2;
/**
* The constant represents the 'fy' type
*/
public static final int OPTYPE_FY = 3;
/**
* The constant represents the 'xfx' type
*/
public static final int OPTYPE_XFX = 4;
/**
* The constant represents the 'xfy' type
*/
public static final int OPTYPE_XFY = 5;
/**
* The constant represents the 'yfx' type
*/
public static final int OPTYPE_YFX = 6;
/**
* The variable contains the operator type value
*/
private final int opType;
/**
* The variable contains the operator priority value
*/
private final int opPriority;
/**
* The variable contains the precalculated hash code for the operator
*/
private final int precalculatedHashCode;
/**
* The variable contains the operator signature
*/
private final String signature;
/**
* This auxulary function allows to generate a lot of similar operators from a
* string array
*
* @param priority the priority of all created operators
* @param type the type of all created operators
* @param names a string array contains names of created operators, must not
* be null
* @return an array of Operator objects which were generated from the
* arguments
*/
public static Operator[] makeOperators(final int priority, final int type, final String[] names) {
final Operator[] result = new Operator[names.length];
for (int li = 0; li < names.length; li++) {
result[li] = new Operator(priority, type, names[li]);
}
return result;
}
/**
* The constructor
*
* @param priority the operator priority
* @param type the operator type
* @param name the operator name, must not be null
* @throws java.lang.IllegalArgumentException will be thrown if there is some
* incompatible value at arguments
*/
public Operator(final int priority, final int type, final String name) {
super(name);
if (priority < PRIORITY_MAX || priority > PRIORITY_MIN) {
throw new IllegalArgumentException("Wrong priority value");
}
switch (type) {
case OPTYPE_FX:
case OPTYPE_FY:
case OPTYPE_XF:
case OPTYPE_YF: {
signature = name + "/1";
}
break;
case OPTYPE_YFX:
case OPTYPE_XFY:
case OPTYPE_XFX: {
signature = name + "/2";
}
break;
default:
throw new IllegalArgumentException("Wrong operator type");
}
opType = type;
opPriority = priority;
int hash = name.hashCode();
hash = 89 * hash + this.opType;
hash = 89 * hash + this.opPriority;
precalculatedHashCode = hash;
}
@Override
public int getTermType() {
return TYPE_OPERATOR;
}
/**
* Get the type of the operator
*
* @return the operator type as integer
*/
public int getOperatorType() {
return opType;
}
@Override
public int getPriority() {
return opPriority;
}
/**
* Check that the operator can be functor for the structure
*
* @param struct the checked structure, must not be null
* @return true if the operator is compatible with the structure else false
*/
public boolean validateStructureForOperator(final TermStruct struct) {
boolean result = false;
if (struct != null) {
switch (struct.getArity()) {
case 1: {
switch (opType) {
case OPTYPE_XFY:
case OPTYPE_XFX:
case OPTYPE_YFX: {
result = false;
}
break;
case OPTYPE_XF:
case OPTYPE_FX: {
final Term atom = struct.getElement(0);
if (atom == null) {
result = false;
}
else {
result = atom.getPriority() < getPriority();
}
}
break;
case OPTYPE_YF:
case OPTYPE_FY: {
final Term atom = struct.getElement(0);
if (atom == null) {
result = false;
}
else {
result = atom.getPriority() <= getPriority();
}
}
break;
default:
throw new ProlCriticalError("Unknown type");
}
}
break;
case 2: {
switch (opType) {
case OPTYPE_XFY:
case OPTYPE_XFX:
case OPTYPE_YFX: {
final Term elementLeft = struct.getElement(0);
final Term elementRight = struct.getElement(1);
if (elementLeft == null || elementRight == null) {
result = false;
}
else {
switch (opType) {
case OPTYPE_XFX: {
result = elementLeft.getPriority() < getPriority() && elementRight.getPriority() < getPriority();
}
break;
case OPTYPE_YFX: {
result = elementLeft.getPriority() <= getPriority() && elementRight.getPriority() < getPriority();
}
break;
case OPTYPE_XFY: {
result = elementLeft.getPriority() < getPriority() && elementRight.getPriority() <= getPriority();
}
break;
default: {
result = false;
}
break;
}
}
}
break;
case OPTYPE_XF:
case OPTYPE_FX: {
final Term atom = struct.getElement(opType == OPTYPE_XF ? 0 : 1);
if (atom == null) {
result = false;
}
else {
result = atom.getPriority() < getPriority();
}
}
break;
case OPTYPE_YF:
case OPTYPE_FY: {
final Term atom = struct.getElement(opType == OPTYPE_YF ? 0 : 1);
if (atom == null) {
result = false;
}
else {
result = atom.getPriority() <= getPriority();
}
}
break;
default: {
throw new ProlCriticalError("Unknown type");
}
}
}
break;
default: {
result = false;
}
}
}
return result;
}
@Override
public int hashCode() {
return precalculatedHashCode;
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Operator other = (Operator) obj;
if (this.precalculatedHashCode != other.precalculatedHashCode) {
return false;
}
if (this.opType != other.opType) {
return false;
}
if (this.opPriority != other.opPriority) {
return false;
}
return this.getText().equals(other.getText());
}
@Override
public String getSourceLikeRepresentation() {
return getText();
}
@Override
public String toString() {
return getText();
}
/**
* Decode the operator type to the string representation
*
* @return the operator type as String
*/
public String getTypeAsString() {
return Operator.getTypeFromIndex(opType);
}
/**
* Write the operator into a writter like prolog source format
*
* @param writer the writer, must not be null
* @throws NullPointerException if the writter is null
*/
public void write(final PrintWriter writer) {
if (writer == null) {
throw new NullPointerException("Writer is null");
}
writer.print(":- op(");
writer.print(opPriority);
writer.print(',');
writer.print(getTypeAsString());
writer.print(",\'");
writer.print(getText());
writer.println("\').");
}
@Override
public String getSignature() {
return signature;
}
@Override
public String forWrite() {
return getText();
}
@Override
@SuppressWarnings("unchecked")
public boolean Equ(final Term atom) {
if (this == atom) {
return true;
}
switch (atom.getTermType()) {
case Term.TYPE_ATOM: {
return getText().equals(atom.getText());
}
case Term.TYPE_OPERATOR: {
return this == atom;
}
case Term.TYPE_VAR: {
final Var var = (Var) atom;
final Term value = var.getValue();
if (value == null) {
return ((Var) atom).setValue(this);
}
else {
return Equ(value);
}
}
}
return false;
}
/**
* Decode a type index into its string representation
*
* @param index the index to be decoded
* @return the string representation of the index
*/
public static final String getTypeFromIndex(final int index) {
switch (index) {
case OPTYPE_FX:
return "fx";
case OPTYPE_FY:
return "fy";
case OPTYPE_XF:
return "xf";
case OPTYPE_YF:
return "yf";
case OPTYPE_XFX:
return "xfx";
case OPTYPE_XFY:
return "xfy";
case OPTYPE_YFX:
return "yfx";
default:
return "<UNKNOWN>";
}
}
/**
* Decode the string operator type representation into its numeric analogue
*
* @param op_type the string operator type representation
* @return numeric value equals the string value or -1 if it's not an operator
* type
*/
public static final int getTypeFromString(final String op_type) {
switch (op_type.length()) {
case 2: {
if ("xf".equals(op_type)) {
return OPTYPE_XF;
}
if ("fx".equals(op_type)) {
return OPTYPE_FX;
}
if ("fy".equals(op_type)) {
return OPTYPE_FY;
}
if ("yf".equals(op_type)) {
return OPTYPE_YF;
}
return -1;
}
case 3: {
if ("xfx".equals(op_type)) {
return OPTYPE_XFX;
}
if ("yfx".equals(op_type)) {
return OPTYPE_YFX;
}
if ("xfy".equals(op_type)) {
return OPTYPE_XFY;
}
return -1;
}
default:
return -1;
}
}
}