| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.tinkerpop.gremlin.language.grammar; |
| |
| import org.antlr.v4.runtime.CharStream; |
| import org.antlr.v4.runtime.CharStreams; |
| import org.antlr.v4.runtime.CommonTokenStream; |
| import org.antlr.v4.runtime.atn.PredictionMode; |
| import org.apache.tinkerpop.gremlin.process.traversal.Traversal; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.util.Map; |
| |
| /** |
| * Parses Gremlin strings to an {@code Object}, typically to a {@link Traversal}. |
| */ |
| public class GremlinQueryParser { |
| private static final Logger log = LoggerFactory.getLogger(GremlinQueryParser.class); |
| private static final GremlinErrorListener errorListener = new GremlinErrorListener(); |
| |
| /** |
| * Parse Gremlin string using a default {@link GremlinAntlrToJava} object. |
| */ |
| public static Object parse(final String query) { |
| return parse(query, new GremlinAntlrToJava()); |
| } |
| |
| /** |
| * Parse Gremlin string using a specified {@link GremlinAntlrToJava} object. |
| */ |
| public static Object parse(final String query, final GremlinVisitor<Object> visitor) { |
| final CharStream in = CharStreams.fromString(query); |
| final GremlinLexer lexer = new GremlinLexer(in); |
| lexer.removeErrorListeners(); |
| lexer.addErrorListener(errorListener); |
| |
| final CommonTokenStream tokens = new CommonTokenStream(lexer); |
| |
| // Setup error handler on parser |
| final GremlinParser parser = new GremlinParser(tokens); |
| // SLL prediction mode is faster than the LL prediction mode when parsing the grammar, |
| // but it does not cover parsing all types of input. We use the SLL by default, and fallback |
| // to LL mode if fails to parse the query. |
| parser.getInterpreter().setPredictionMode(PredictionMode.SLL); |
| parser.removeErrorListeners(); |
| parser.addErrorListener(errorListener); |
| |
| GremlinParser.QueryListContext queryContext; |
| try { |
| queryContext = parser.queryList(); |
| } catch (Exception ex) { |
| // Retry parsing the query again with using LL prediction mode. LL parsing mode is more powerful |
| // so retrying the parsing would help parsing the rare edge cases. |
| try { |
| tokens.seek(0); // rewind input stream |
| lexer.reset(); |
| parser.reset(); |
| parser.getInterpreter().setPredictionMode(PredictionMode.LL); |
| log.debug("Query parsed with using LL prediction mode: {}", query); |
| queryContext = parser.queryList(); |
| } catch (Exception e) { |
| log.debug("Query parsing failed in retry with exception" + e); |
| throw new GremlinParserException("Failed to interpret Gremlin query: " + e.getMessage()); |
| } |
| } |
| |
| try { |
| return visitor.visit(queryContext); |
| } catch (ClassCastException ex) { |
| // Special case that can be interpreted as a (semantic) parse error. |
| // Do not include ex as the cause - org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor.eval() |
| // in Tinkerpop v 3.3.0, strips root causes off their outer exception objects. |
| // We just log ex here for debugging purposes. |
| log.debug("Converting a java.lang.ClassCastException to GremlinParserException," + |
| " assuming that it indicates a semantic parse error.", ex); |
| throw new GremlinParserException("Failed to interpret Gremlin query: " + ex.getMessage()); |
| } |
| } |
| } |