/** * 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 org.antlr.v4.runtime.ANTLRInputStream; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.ParseTreeWalker; import com.rapidminer.tools.expression.Expression; import com.rapidminer.tools.expression.ExpressionContext; import com.rapidminer.tools.expression.ExpressionEvaluator; import com.rapidminer.tools.expression.ExpressionException; import com.rapidminer.tools.expression.ExpressionParser; import com.rapidminer.tools.expression.ExpressionParsingException; import com.rapidminer.tools.expression.internal.antlr.FunctionExpressionLexer; import com.rapidminer.tools.expression.internal.antlr.FunctionExpressionParser; /** * Parser using antlr. * * @author Gisa Schaefer * */ public class AntlrParser implements ExpressionParser { private ExpressionContext lookup; /** * Creates a Parser that parses using antlr. * * @param lookUp * the {@link ExpressionContext} for looking up functions, variables and macros */ public AntlrParser(ExpressionContext lookup) { this.lookup = lookup; } /** * Parses the expression using antlr, aborts the parsing on the first error. * * @param expression * an expression, not {@code null} * @return a {@link ParseTree} for further processing * @throws ExpressionException */ ParseTree parseExpression(String expression) throws ExpressionException { if (expression == null) { throw new IllegalArgumentException("expression must not be null"); } ANTLRInputStream in = new ANTLRInputStream(expression); FunctionExpressionLexer lexer = new CapitulatingFunctionExpressionLexer(in); CommonTokenStream tokens = new CommonTokenStream(lexer); FunctionExpressionParser parser = new FunctionExpressionParser(tokens); parser.removeErrorListeners(); lexer.removeErrorListeners(); ExpressionErrorListener listener = new ExpressionErrorListener(); parser.addErrorListener(listener); lexer.addErrorListener(listener); parser.setErrorHandler(new CapitulatingErrorStrategy()); try { ParseTree tree = parser.operationExp(); if (listener.containsError()) { throw new ExpressionException(listener.getErrorMessage(), listener.getErrorLine()); } else { return tree; } } catch (CapitulatingRuntimeException e) { if (listener.containsError()) { throw new ExpressionException(listener.getErrorMessage(), listener.getErrorLine()); } else { // cannot happen since the parser and lexer always register the error before trying // to recover throw new ExpressionException("Unknown error"); } } } @Override public void checkSyntax(String expression) throws ExpressionException { ParseTree tree = parseExpression(expression); ParseTreeWalker walker = new ParseTreeWalker(); FunctionListener listener = new FunctionListener(lookup); try { walker.walk(listener, tree); } catch (ExpressionParsingException e) { throw new ExpressionException(e); } } @Override public Expression parse(String expression) throws ExpressionException { try { ExpressionEvaluator evaluator = parseToEvaluator(expression); return new SimpleExpression(evaluator); } catch (ExpressionParsingException e) { throw new ExpressionException(e); } } /** * Parses the expression to a tree and creates an {@link ExpressionEvaluator} out of it. * * @param expression * the expression to parse * @return the ExpressionEvaluator for the result * @throws ExpressionParsingException * if the creation of the ExpressionEvaluator failed * @throws ExpressionException * if the parsing failed */ public ExpressionEvaluator parseToEvaluator(String expression) throws ExpressionParsingException, ExpressionException { ParseTree tree = parseExpression(expression); return new EvaluatorCreationVisitor(lookup).visit(tree); } @Override public ExpressionContext getExpressionContext() { return lookup; } }