Skip to content

Commit a2513f3

Browse files
author
Brian Maher
committed
Initial take on adding GraphQL Schema parsing.
The definitions come from this pending pull request: graphql/graphql-spec#90 Specifically the grammar comes from: https://github.com/facebook/graphql/blob/de5433206643b2ec9e87e18e142f2e630bfa05b9/spec/Appendix%20B%20--%20Grammar%20Summary.md Note that the grammar is still not solidified, but tooling is already being created that is based on this grammar. For example: https://github.com/apollostack/graphql-tools My hope is to enable schema-first development for Java by creating tooling similar to the above, but for Java. Note that I had to change the 'NAME' token to be a 'name' rule. This was necessary to support "keywords" throughout the grammar. For example, without this change there are tests that define "type" and "scalar" as field names in various queries, and these would fail to parse.
1 parent 0bd261d commit a2513f3

14 files changed

Lines changed: 1171 additions & 29 deletions

src/main/antlr/Graphql.g4

Lines changed: 77 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,28 @@ grammar Graphql;
33
@header {
44
package graphql.parser.antlr;
55
}
6-
// Document
6+
7+
// Document
78

89
document : definition+;
910

1011
definition:
1112
operationDefinition |
12-
fragmentDefinition
13+
fragmentDefinition |
14+
typeSystemDefinition
1315
;
1416

1517
operationDefinition:
1618
selectionSet |
17-
operationType NAME? variableDefinitions? directives? selectionSet;
19+
operationType name? variableDefinitions? directives? selectionSet;
1820

1921
operationType : NAME;
2022

2123
variableDefinitions : '(' variableDefinition+ ')';
2224

2325
variableDefinition : variable ':' type defaultValue?;
2426

25-
variable : '$' NAME;
27+
variable : '$' name;
2628

2729
defaultValue : '=' value;
2830

@@ -35,13 +37,13 @@ field |
3537
fragmentSpread |
3638
inlineFragment;
3739

38-
field : alias? NAME arguments? directives? selectionSet?;
40+
field : alias? name arguments? directives? selectionSet?;
3941

40-
alias : NAME ':';
42+
alias : name ':';
4143

4244
arguments : '(' argument+ ')';
4345

44-
argument : NAME ':' valueWithVariable;
46+
argument : name ':' valueWithVariable;
4547

4648
// Fragments
4749

@@ -51,12 +53,13 @@ inlineFragment : '...' 'on' typeCondition directives? selectionSet;
5153

5254
fragmentDefinition : 'fragment' fragmentName 'on' typeCondition directives? selectionSet;
5355

54-
fragmentName : NAME;
56+
fragmentName : name;
5557

5658
typeCondition : typeName;
5759

5860
// Value
5961

62+
name: NAME | SCALAR | TYPE | INTERFACE | IMPLEMENTS | ENUM | UNION | INPUT | EXTEND | DIRECTIVE;
6063

6164
value :
6265
IntValue |
@@ -78,7 +81,7 @@ arrayValueWithVariable |
7881
objectValueWithVariable;
7982

8083

81-
enumValue : NAME ;
84+
enumValue : name ;
8285

8386
// Array Value
8487

@@ -91,30 +94,89 @@ arrayValueWithVariable: '[' valueWithVariable* ']';
9194

9295
objectValue: '{' objectField* '}';
9396
objectValueWithVariable: '{' objectFieldWithVariable* '}';
94-
objectField : NAME ':' value;
95-
objectFieldWithVariable : NAME ':' valueWithVariable;
97+
objectField : name ':' value;
98+
objectFieldWithVariable : name ':' valueWithVariable;
9699

97100
// Directives
98101

99102
directives : directive+;
100103

101-
directive :'@' NAME arguments?;
104+
directive :'@' name arguments?;
102105

103106
// Types
104107

105108
type : typeName | listType | nonNullType;
106109

107-
typeName : NAME;
110+
typeName : name;
108111
listType : '[' type ']';
109112
nonNullType: typeName '!' | listType '!';
110113

111114

115+
// Type System
116+
typeSystemDefinition:
117+
scalarTypeDefinition |
118+
objectTypeDefinition |
119+
interfaceTypeDefinition |
120+
unionTypeDefinition |
121+
enumTypeDefinition |
122+
inputObjectTypeDefinition
123+
;
124+
125+
namedType : name;
126+
127+
scalarTypeDefinition : SCALAR name directives?;
128+
129+
130+
objectTypeDefinition : TYPE name implementsInterfaces? directives? '{' fieldDefinition+ '}';
131+
132+
implementsInterfaces : IMPLEMENTS namedType+;
133+
134+
fieldDefinition : name argumentsDefinition? ':' type directives?;
135+
136+
argumentsDefinition : '(' inputValueDefinition+ ')';
137+
138+
inputValueDefinition : name ':' type defaultValue? directives?;
139+
140+
interfaceTypeDefinition : INTERFACE name directives? '{' fieldDefinition+ '}';
141+
142+
unionTypeDefinition : UNION name directives? '=' unionMembers;
143+
144+
unionMembers:
145+
namedType |
146+
unionMembers '|' namedType
147+
;
148+
149+
enumTypeDefinition : ENUM name directives? '{' enumValueDefinition+ '}';
150+
151+
enumValueDefinition : enumValue directives?;
152+
153+
inputObjectTypeDefinition : INPUT name directives? '{' inputValueDefinition+ '}';
154+
155+
typeExtensionDefinition : EXTEND objectTypeDefinition;
156+
157+
directiveDefinition : DIRECTIVE '@' name argumentsDefinition? 'on' directiveLocations;
158+
159+
directiveLocations :
160+
name |
161+
directiveLocations '|' name
162+
;
163+
112164

113165
// Token
114166

115167
BooleanValue: 'true' | 'false';
116168

117-
NAME: [_A-Za-z][_0-9A-Za-z]* ;
169+
SCALAR: 'scalar';
170+
TYPE: 'type';
171+
INTERFACE: 'interface';
172+
IMPLEMENTS: 'implements';
173+
ENUM: 'enum';
174+
UNION: 'union';
175+
INPUT: 'input';
176+
EXTEND: 'extend';
177+
DIRECTIVE: 'directive';
178+
NAME: [_A-Za-z][_0-9A-Za-z]*;
179+
118180

119181
IntValue : Sign? IntegerPart;
120182

@@ -144,4 +206,4 @@ fragment Comment: '#' ~[\n\r\u2028\u2029]*;
144206
fragment LineTerminator: [\n\r\u2028\u2029];
145207
146208
fragment Whitespace : [\t\u000b\f\u0020\u00a0];
147-
fragment Comma : ',';
209+
fragment Comma : ',';

src/main/java/graphql/language/AstComparator.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33

44
import java.util.List;
5+
import java.util.Iterator;
56

67
public class AstComparator {
78

89

910
public boolean isEqual(Node node1, Node node2) {
11+
if (null == node1) return null == node2;
1012
if (!node1.isEqualTo(node2)) return false;
1113
List<Node> childs1 = node1.getChildren();
1214
List<Node> childs2 = node2.getChildren();
@@ -16,4 +18,14 @@ public boolean isEqual(Node node1, Node node2) {
1618
}
1719
return true;
1820
}
21+
22+
public boolean isEqual(List<Node> nodes1, List<Node> nodes2) {
23+
if ( nodes1.size() != nodes2.size() ) return false;
24+
Iterator<Node> iter1 = nodes1.iterator();
25+
Iterator<Node> iter2 = nodes2.iterator();
26+
while (iter1.hasNext()) {
27+
if (!isEqual(iter1.next(), iter2.next())) return false;
28+
}
29+
return true;
30+
}
1931
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package graphql.language;
2+
3+
import java.util.ArrayList;
4+
import java.util.Iterator;
5+
import java.util.List;
6+
7+
public class EnumTypeDefinition extends AbstractNode implements TypeDefinition {
8+
private String name;
9+
private List<EnumValueDefinition> enumValueDefinitions;
10+
private List<Directive> directives;
11+
12+
public EnumTypeDefinition(String name) {
13+
this(name, null);
14+
}
15+
16+
public EnumTypeDefinition(String name, List<Directive> directives) {
17+
this.name = name;
18+
this.directives = ( null == directives ) ? new ArrayList<Directive>() : directives;
19+
this.enumValueDefinitions = new ArrayList<EnumValueDefinition>();
20+
}
21+
22+
public List<EnumValueDefinition> getEnumValueDefinitions() {
23+
return enumValueDefinitions;
24+
}
25+
26+
public List<Directive> getDirectives() {
27+
return directives;
28+
}
29+
30+
@Override
31+
public String getName() {
32+
return name;
33+
}
34+
35+
@Override
36+
public List<Node> getChildren() {
37+
List<Node> result = new ArrayList<Node>();
38+
result.addAll(enumValueDefinitions);
39+
result.addAll(directives);
40+
return result;
41+
}
42+
43+
@Override
44+
public boolean isEqualTo(Node o) {
45+
if (this == o) return true;
46+
if (o == null || getClass() != o.getClass()) return false;
47+
48+
EnumTypeDefinition that = (EnumTypeDefinition) o;
49+
50+
if ( null == name ) {
51+
if ( null != that.name ) return false;
52+
} else if ( !name.equals(that.name) ) {
53+
return false;
54+
}
55+
return true;
56+
57+
}
58+
59+
60+
@Override
61+
public String toString() {
62+
return "EnumTypeDefinition{" +
63+
"name='" + name + '\'' +
64+
", enumValueDefinitions=" + enumValueDefinitions +
65+
", directives=" + directives +
66+
'}';
67+
}
68+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package graphql.language;
2+
3+
4+
import java.util.ArrayList;
5+
import java.util.List;
6+
7+
public class EnumValueDefinition extends AbstractNode {
8+
private String name;
9+
private List<Directive> directives;
10+
11+
public EnumValueDefinition(String name) {
12+
this(name, null);
13+
}
14+
15+
public EnumValueDefinition(String name, List<Directive> directives) {
16+
this.name = name;
17+
this.directives = (null == directives) ? new ArrayList<Directive>() : directives;
18+
}
19+
20+
public String getName() {
21+
return name;
22+
}
23+
24+
public List<Directive> getDirectives() {
25+
return directives;
26+
}
27+
28+
@Override
29+
public List<Node> getChildren() {
30+
List<Node> result = new ArrayList<Node>();
31+
result.addAll(directives);
32+
return result;
33+
}
34+
35+
@Override
36+
public boolean isEqualTo(Node o) {
37+
if (this == o) return true;
38+
if (o == null || getClass() != o.getClass()) return false;
39+
40+
EnumValueDefinition that = (EnumValueDefinition) o;
41+
42+
if ( null == name ) {
43+
if ( null != that.name ) return false;
44+
} else if ( !name.equals(that.name) ) {
45+
return false;
46+
}
47+
48+
return true;
49+
50+
}
51+
52+
53+
@Override
54+
public String toString() {
55+
return "EnumValueDefinition{" +
56+
"name='" + name + '\'' +
57+
", directives=" + directives +
58+
'}';
59+
}
60+
}

0 commit comments

Comments
 (0)