Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions src/main/java/graphql/language/IgnoredChar.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
import java.io.Serializable;
import java.util.Objects;

/**
* Graphql syntax has a series of characters, such as spaces, new lines and commas that are not considered relevant
* to the syntax. However they can be captured and associated with the AST elements they belong to.
*
* This costs more memory but for certain use cases (like editors) this maybe be useful
*/
@PublicApi
public class IgnoredChar implements Serializable {

Expand Down Expand Up @@ -38,10 +44,10 @@ public SourceLocation getSourceLocation() {
@Override
public String toString() {
return "IgnoredChar{" +
"value='" + value + '\'' +
", kind=" + kind +
", sourceLocation=" + sourceLocation +
'}';
"value='" + value + '\'' +
", kind=" + kind +
", sourceLocation=" + sourceLocation +
'}';
}

@Override
Expand All @@ -54,8 +60,8 @@ public boolean equals(Object o) {
}
IgnoredChar that = (IgnoredChar) o;
return Objects.equals(value, that.value) &&
kind == that.kind &&
Objects.equals(sourceLocation, that.sourceLocation);
kind == that.kind &&
Objects.equals(sourceLocation, that.sourceLocation);
}

@Override
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/graphql/language/IgnoredChars.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@

import static graphql.collect.ImmutableKit.emptyList;

/**
* Graphql syntax has a series of characters, such as spaces, new lines and commas that are not considered relevant
* to the syntax. However they can be captured and associated with the AST elements they belong to.
*
* This costs more memory but for certain use cases (like editors) this maybe be useful
*/
@PublicApi
public class IgnoredChars implements Serializable {

Expand Down
9 changes: 9 additions & 0 deletions src/main/java/graphql/parser/GraphqlAntlrToLanguage.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,17 @@ public class GraphqlAntlrToLanguage {
private static final int CHANNEL_IGNORED_CHARS = 3;
private final CommonTokenStream tokens;
private final MultiSourceReader multiSourceReader;
private final boolean captureIgnoredChars;


public GraphqlAntlrToLanguage(CommonTokenStream tokens, MultiSourceReader multiSourceReader) {
this(tokens, multiSourceReader, null);
}

public GraphqlAntlrToLanguage(CommonTokenStream tokens, MultiSourceReader multiSourceReader, Boolean captureIgnoredChars) {
this.tokens = tokens;
this.multiSourceReader = multiSourceReader;
this.captureIgnoredChars = captureIgnoredChars == null ? Parser.getCaptureIgnoredChars() : captureIgnoredChars;
}

//MARKER START: Here GraphqlOperation.g4 specific methods begin
Expand Down Expand Up @@ -774,6 +780,9 @@ protected void addCommonData(NodeBuilder nodeBuilder, ParserRuleContext parserRu
}

private void addIgnoredChars(ParserRuleContext ctx, NodeBuilder nodeBuilder) {
if (!captureIgnoredChars) {
return;
}
Token start = ctx.getStart();
int tokenStartIndex = start.getTokenIndex();
List<Token> leftChannel = tokens.getHiddenTokensToLeft(tokenStartIndex, CHANNEL_IGNORED_CHARS);
Expand Down
7 changes: 5 additions & 2 deletions src/main/java/graphql/parser/InvalidSyntaxException.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@


import graphql.GraphQLException;
import graphql.Internal;
import graphql.InvalidSyntaxError;
import graphql.PublicApi;
import graphql.language.SourceLocation;

import java.util.Collections;
import java.util.List;

@Internal
/**
* This exception is thrown by the {@link Parser} if the graphql syntax is not valid
*/
@PublicApi
public class InvalidSyntaxException extends GraphQLException {

private final String message;
Expand Down
173 changes: 163 additions & 10 deletions src/main/java/graphql/parser/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,108 @@
import java.util.List;
import java.util.function.BiFunction;

/**
* This can parse graphql syntax, both Query syntax and Schema Definition Language (SDL) syntax, into an
* Abstract Syntax Tree (AST) represented by a {@link Document}
* <p>
* You should not generally need to call this class as the {@link graphql.GraphQL} code sets this up for you
* but if you are doing specific graphql utilities this class is essential.
*
* Graphql syntax has a series of characters, such as spaces, new lines and commas that are not considered relevant
* to the syntax. However they can be captured and associated with the AST elements they belong to.
*
* This costs more memory but for certain use cases (like editors) this maybe be useful. We have chosen to no capture
* ignored characters by default but you can turn this on, either per parse or statically for the whole JVM
* via {@link #setCaptureIgnoredChars(boolean)}
*
* @see graphql.language.IgnoredChar
*/
@PublicApi
public class Parser {

private static boolean captureIgnoredChars = false;

public static Document parse(String input) {
/**
* By default the Parser will not capture ignored characters. A static holds this default
* value in a JVM wide basis.
*
* Significant memory savings can be made if we do NOT capture ignored characters,
* especially in SDL parsing.
*
* @return the static default value on whether to capture ignored chars
*
* @see graphql.language.IgnoredChar
*/
public static boolean getCaptureIgnoredChars() {
return captureIgnoredChars;
}

/**
* By default the Parser will not capture ignored characters. A static holds this default
* value in a JVM wide basis.
*
* Significant memory savings can be made if we do NOT capture ignored characters,
* especially in SDL parsing. So we have set this to false by default.
*
* This static can be set to true to allow the behavior of version 16.x or before.
*
* @param flag - whether to capture ignored characters in AST elements or not
*
* @see graphql.language.IgnoredChar
*/
public static void setCaptureIgnoredChars(boolean flag) {
captureIgnoredChars = flag;
}

/**
* Parses a string input into a graphql AST {@link Document}
*
* @param input the input to parse
*
* @return an AST {@link Document}
*
* @throws InvalidSyntaxException if the input is not valid graphql syntax
*/
public static Document parse(String input) throws InvalidSyntaxException {
return new Parser().parseDocument(input);
}

public static Value<?> parseValue(String input) {
/**
* Parses a string input into a graphql AST {@link Value}
*
* @param input the input to parse
*
* @return an AST {@link Value}
*
* @throws InvalidSyntaxException if the input is not valid graphql syntax
*/
public static Value<?> parseValue(String input) throws InvalidSyntaxException {
return new Parser().parseValueImpl(input);
}

/**
* Parses a string input into a graphql AST {@link Document}
*
* @param input the input to parse
*
* @return an AST {@link Document}
*
* @throws InvalidSyntaxException if the input is not valid graphql syntax
*/
public Document parseDocument(String input) throws InvalidSyntaxException {
return parseDocument(input, null);
}

/**
* Parses a string input into a graphql AST {@link Document}
*
* @param input the input to parse
* @param sourceName - the name to attribute to the input text in {@link SourceLocation#getSourceName()}
*
* @return an AST {@link Document}
*
* @throws InvalidSyntaxException if the input is not valid graphql syntax
*/
public Document parseDocument(String input, String sourceName) throws InvalidSyntaxException {
MultiSourceReader multiSourceReader = MultiSourceReader.newMultiSourceReader()
.string(input, sourceName)
Expand All @@ -47,29 +133,76 @@ public Document parseDocument(String input, String sourceName) throws InvalidSyn
return parseDocument(multiSourceReader);
}

public Document parseDocument(Reader reader) {
/**
* Parses a string input into a graphql AST {@link Document}
*
* @param input the input to parse
* @param captureIgnoredChars whether to capture characters that are otherwise ignored in graphql syntax against
* each AST element
*
* @return an AST {@link Document}
*
* @throws InvalidSyntaxException if the input is not valid graphql syntax
*/
public Document parseDocument(String input, boolean captureIgnoredChars) throws InvalidSyntaxException {
MultiSourceReader multiSourceReader = MultiSourceReader.newMultiSourceReader()
.string(input, null)
.trackData(true)
.build();
return parseDocument(multiSourceReader, captureIgnoredChars);
}

/**
* Parses reader input into a graphql AST {@link Document}
*
* @param reader the reader input to parse
*
* @return an AST {@link Document}
*
* @throws InvalidSyntaxException if the input is not valid graphql syntax
*/
public Document parseDocument(Reader reader) throws InvalidSyntaxException {
return parseDocumentImpl(reader, null);
}

/**
* Parses reader input into a graphql AST {@link Document}
*
* @param reader the reader input to parse
* @param captureIgnoredChars whether to capture characters that are otherwise ignored in graphql syntax against
* each AST element
*
* @return an AST {@link Document}
*
* @throws InvalidSyntaxException if the input is not valid graphql syntax
*/
public Document parseDocument(Reader reader, boolean captureIgnoredChars) throws InvalidSyntaxException {
return parseDocumentImpl(reader, captureIgnoredChars);
}

private Document parseDocumentImpl(Reader reader, Boolean captureIgnoredChars) throws InvalidSyntaxException {
BiFunction<GraphqlParser, GraphqlAntlrToLanguage, Object[]> nodeFunction = (parser, toLanguage) -> {
GraphqlParser.DocumentContext documentContext = parser.document();
Document doc = toLanguage.createDocument(documentContext);
return new Object[]{documentContext, doc};
};
return (Document) parseImpl(reader, nodeFunction);
return (Document) parseImpl(reader, nodeFunction, captureIgnoredChars);
}

private Value<?> parseValueImpl(String input) {
private Value<?> parseValueImpl(String input) throws InvalidSyntaxException {
BiFunction<GraphqlParser, GraphqlAntlrToLanguage, Object[]> nodeFunction = (parser, toLanguage) -> {
GraphqlParser.ValueContext documentContext = parser.value();
Value value = toLanguage.createValue(documentContext);
Value<?> value = toLanguage.createValue(documentContext);
return new Object[]{documentContext, value};
};
MultiSourceReader multiSourceReader = MultiSourceReader.newMultiSourceReader()
.string(input, null)
.trackData(true)
.build();
return (Value<?>) parseImpl(multiSourceReader, nodeFunction);
return (Value<?>) parseImpl(multiSourceReader, nodeFunction, null);
}

private Node parseImpl(Reader reader, BiFunction<GraphqlParser, GraphqlAntlrToLanguage, Object[]> nodeFunction) throws InvalidSyntaxException {
private Node<?> parseImpl(Reader reader, BiFunction<GraphqlParser, GraphqlAntlrToLanguage, Object[]> nodeFunction, Boolean captureIgnoredChars) throws InvalidSyntaxException {
MultiSourceReader multiSourceReader;
if (reader instanceof MultiSourceReader) {
multiSourceReader = (MultiSourceReader) reader;
Expand Down Expand Up @@ -104,10 +237,14 @@ public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int
ExtendedBailStrategy bailStrategy = new ExtendedBailStrategy(multiSourceReader);
parser.setErrorHandler(bailStrategy);

// preserve old protected call semantics - remove at some point
GraphqlAntlrToLanguage toLanguage = getAntlrToLanguage(tokens, multiSourceReader);
if (toLanguage == null) {
toLanguage = getAntlrToLanguage(tokens, multiSourceReader, captureIgnoredChars);
}
Object[] contextAndNode = nodeFunction.apply(parser, toLanguage);
ParserRuleContext parserRuleContext = (ParserRuleContext) contextAndNode[0];
Node node = (Node) contextAndNode[1];
Node<?> node = (Node<?>) contextAndNode[1];

Token stop = parserRuleContext.getStop();
List<Token> allTokens = tokens.getTokens();
Expand All @@ -133,8 +270,24 @@ public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int
* @param multiSourceReader the source of the query document
*
* @return a new GraphqlAntlrToLanguage instance
*
* @deprecated - really should use {@link #getAntlrToLanguage(CommonTokenStream, MultiSourceReader, Boolean)}
*/
@Deprecated
protected GraphqlAntlrToLanguage getAntlrToLanguage(CommonTokenStream tokens, MultiSourceReader multiSourceReader) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was left to preserve old behavior - if some one overrides this then they can.

return new GraphqlAntlrToLanguage(tokens, multiSourceReader);
return null;
}

/**
* Allows you to override the ANTLR to AST code.
*
* @param tokens the token stream
* @param multiSourceReader the source of the query document
* @param captureIgnoredChars - whether ignored characters should be captured in the AST elements
*
* @return a new GraphqlAntlrToLanguage instance
*/
protected GraphqlAntlrToLanguage getAntlrToLanguage(CommonTokenStream tokens, MultiSourceReader multiSourceReader, Boolean captureIgnoredChars) {
return new GraphqlAntlrToLanguage(tokens, multiSourceReader, captureIgnoredChars);
}
}
Loading