/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* This program 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
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.tools.expression.internal.antlr;
import com.rapidminer.tools.expression.ExpressionContext;
import com.rapidminer.tools.expression.ExpressionEvaluator;
import com.rapidminer.tools.expression.ExpressionType;
import com.rapidminer.tools.expression.Function;
import com.rapidminer.tools.expression.internal.SimpleExpressionEvaluator;
import com.rapidminer.tools.expression.internal.antlr.FunctionExpressionParser.AttributeContext;
import com.rapidminer.tools.expression.internal.antlr.FunctionExpressionParser.FunctionContext;
import com.rapidminer.tools.expression.internal.antlr.FunctionExpressionParser.IndirectScopeConstantContext;
import com.rapidminer.tools.expression.internal.antlr.FunctionExpressionParser.IntegerContext;
import com.rapidminer.tools.expression.internal.antlr.FunctionExpressionParser.LowerExpContext;
import com.rapidminer.tools.expression.internal.antlr.FunctionExpressionParser.OperationExpContext;
import com.rapidminer.tools.expression.internal.antlr.FunctionExpressionParser.RealContext;
import com.rapidminer.tools.expression.internal.antlr.FunctionExpressionParser.ScopeConstantContext;
import com.rapidminer.tools.expression.internal.antlr.FunctionExpressionParser.StringContext;
import com.rapidminer.tools.expression.internal.antlr.FunctionExpressionParser.VariableContext;
/**
* Visitor that recursively builds an {@link ExpressionEvaluator}. Specifies what should happen at
* every node of the {@link ParseTree}.
*
* @author Gisa Schaefer
*
*/
class EvaluatorCreationVisitor extends FunctionExpressionParserBaseVisitor<ExpressionEvaluator> {
private final ExpressionContext lookUp;
/**
* Creates a Visitor that recursively builds an {@link ExpressionEvaluator}.
*
* @param lookUp
* the {@link ExpressionContext} for looking up functions, variables and scope
* constants
*/
EvaluatorCreationVisitor(ExpressionContext lookUp) {
this.lookUp = lookUp;
}
@Override
public ExpressionEvaluator visitOperationExp(OperationExpContext ctx) {
if (ctx.op == null) {
return visit(ctx.atomExp());
} else {
if (ctx.operationExp().size() == 1) {
ExpressionEvaluator right = visit(ctx.operationExp(0));
String operatorName = ctx.op.getText();
Function function = lookUp.getFunction(ctx.op.getText());
if (function == null) {
throw new UnknownFunctionException(ctx, "expression_parser.unknown_operator", operatorName);
}
return function.compute(right);
} else {
ExpressionEvaluator left = visit(ctx.operationExp(0));
ExpressionEvaluator right = visit(ctx.operationExp(1));
String operatorName = ctx.op.getText();
Function function = lookUp.getFunction(ctx.op.getText());
if (function == null) {
throw new UnknownFunctionException(ctx, "expression_parser.unknown_operator", operatorName);
}
return function.compute(left, right);
}
}
}
@Override
public ExpressionEvaluator visitLowerExp(LowerExpContext ctx) {
return visit(ctx.operationExp());
}
@Override
public ExpressionEvaluator visitFunction(FunctionContext ctx) {
int numberOfInner = ctx.operationExp().size();
ExpressionEvaluator[] innerEvaluators = new ExpressionEvaluator[numberOfInner];
for (int i = 0; i < numberOfInner; i++) {
innerEvaluators[i] = visit(ctx.operationExp(i));
}
String functionName = ctx.NAME().getText();
Function function = lookUp.getFunction(functionName);
if (function == null) {
throw new UnknownFunctionException(ctx, "expression_parser.unknown_function", functionName);
}
return function.compute(innerEvaluators);
}
@Override
public ExpressionEvaluator visitAttribute(AttributeContext ctx) {
String attributeName = getAttributeName(ctx.getText());
ExpressionEvaluator attributeEvaluator = lookUp.getDynamicVariable(attributeName);
if (attributeEvaluator == null) {
throw new UnknownDynamicVariableException(ctx, "expression_parser.unknown_attribute", attributeName);
}
return attributeEvaluator;
}
/**
* Deletes the enclosing [ ], unescapes [ ] and \
*
* @param text
* a ATTRIBUTE as defined in FunctionExpressionLexer.g4
* @return
*/
private String getAttributeName(String text) {
String attributeName = text.substring(1, text.length() - 1);
return attributeName.replace("\\[", "[").replace("\\]", "]").replace("\\\\", "\\");
}
@Override
public ExpressionEvaluator visitVariable(VariableContext ctx) {
String name = ctx.getText();
ExpressionEvaluator variableEvaluator = lookUp.getVariable(name);
if (variableEvaluator == null) {
throw new UnknownVariableException(ctx, "expression_parser.unknown_variable", name);
}
return variableEvaluator;
}
@Override
public ExpressionEvaluator visitScopeConstant(ScopeConstantContext ctx) {
String scopeConstantName = getScopeConstantName(ctx.getText());
ExpressionEvaluator scopeConstantEvaluator = lookUp.getScopeConstant(scopeConstantName);
if (scopeConstantEvaluator == null) {
throw new UnknownScopeConstantException(ctx, "expression_parser.unknown_scope", scopeConstantName);
}
return scopeConstantEvaluator;
}
/**
* Deletes the enclosing %{ } or #{ }, unescapes {,} and \
*
* @param text
* a SCOPE_CONSTANT or INDIRECT_SCOPE_CONSTANT as defined in
* FunctionExpressionLexer.g4
* @return
*/
private String getScopeConstantName(String text) {
String scopeName = text.substring(2, text.length() - 1);
return scopeName.replace("\\{", "{").replace("\\}", "}").replace("\\\\", "\\");
}
@Override
public ExpressionEvaluator visitIndirectScopeConstant(IndirectScopeConstantContext ctx) {
String scopeConstantName = getScopeConstantName(ctx.getText());
String attributeName = lookUp.getScopeString(scopeConstantName);
if (attributeName == null) {
throw new UnknownScopeConstantException(ctx, "expression_parser.unknown_scope", scopeConstantName);
}
ExpressionEvaluator attributeEvaluator = lookUp.getDynamicVariable(attributeName);
if (attributeEvaluator == null) {
throw new UnknownDynamicVariableException(ctx, "expression_parser.unknown_attribute_in_scope", attributeName,
scopeConstantName);
}
return attributeEvaluator;
}
@Override
public ExpressionEvaluator visitString(StringContext ctx) {
String stringValue = getStringValue(ctx.getText());
return new SimpleExpressionEvaluator(stringValue, ExpressionType.STRING);
}
/**
* Unescapes escaped sequences and replaces tabs and newlines by spaces.
*
* @param text
* a STRING as defined in FunctionExpressionLexer.g4
* @return
*/
private String getStringValue(String text) {
// delete leading and trailing "
text = text.substring(1, text.length() - 1);
// unescape
text = text.replace("\\\"", "\"").replace("\\\\", "\\");
// replace \\u**** by associated unicode character
int pos = text.indexOf("\\u");
while (pos >= 0) {
text = text.substring(0, pos) + (char) Integer.parseInt(text.substring(pos + 2, pos + 6), 16)
+ text.substring(pos + 6, text.length());
pos = text.indexOf("\\u");
}
// replace tabs and line breaks by spaces
text = text.replace("\t", " ").replace("\r\n", " ").replace("\n", " ");
return text;
}
@Override
public ExpressionEvaluator visitReal(RealContext ctx) {
double doubleValue = Double.parseDouble(ctx.getText());
return new SimpleExpressionEvaluator(doubleValue, ExpressionType.DOUBLE);
}
@Override
public ExpressionEvaluator visitInteger(IntegerContext ctx) {
double doubleValue = Double.parseDouble(ctx.getText());
return new SimpleExpressionEvaluator(doubleValue, ExpressionType.INTEGER);
}
}