blob: 7ab7392af2f9d42868747693086d29339db57fff [file] [log] [blame]
/*
* 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());
}
}
}