Skip to content

Commit 58788b8

Browse files
authored
Merge pull request #74 from youfoodz/add-null-value-literal-support
Added support for parsing null value literals
2 parents 1b11819 + d3695ad commit 58788b8

File tree

4 files changed

+167
-10
lines changed

4 files changed

+167
-10
lines changed

Sources/GraphQL/Language/AST.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,7 @@ extension IntValue : Value {}
728728
extension FloatValue : Value {}
729729
extension StringValue : Value {}
730730
extension BooleanValue : Value {}
731+
extension NullValue : Value {}
731732
extension EnumValue : Value {}
732733
extension ListValue : Value {}
733734
extension ObjectValue : Value {}
@@ -754,6 +755,10 @@ public func == (lhs: Value, rhs: Value) -> Bool {
754755
if let r = rhs as? BooleanValue {
755756
return l == r
756757
}
758+
case let l as NullValue:
759+
if let r = rhs as? NullValue {
760+
return l == r
761+
}
757762
case let l as EnumValue:
758763
if let r = rhs as? EnumValue {
759764
return l == r
@@ -843,6 +848,21 @@ extension BooleanValue : Equatable {
843848
}
844849
}
845850

851+
public final class NullValue {
852+
public let kind: Kind = .nullValue
853+
public let loc: Location?
854+
855+
init(loc: Location? = nil) {
856+
self.loc = loc
857+
}
858+
}
859+
860+
extension NullValue : Equatable {
861+
public static func == (lhs: NullValue, rhs: NullValue) -> Bool {
862+
return true
863+
}
864+
}
865+
846866
public final class EnumValue {
847867
public let kind: Kind = .enumValue
848868
public let loc: Location?

Sources/GraphQL/Language/Kinds.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public enum Kind {
1414
case floatValue
1515
case stringValue
1616
case booleanValue
17+
case nullValue
1718
case enumValue
1819
case listValue
1920
case objectValue

Sources/GraphQL/Language/Parser.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,12 @@ func parseValueLiteral(lexer: Lexer, isConst: Bool) throws -> Value {
477477
loc: loc(lexer: lexer, startToken: token),
478478
value: token.value == "true"
479479
)
480-
} else if token.value != "null" {
480+
} else if token.value == "null" {
481+
try lexer.advance()
482+
return NullValue(
483+
loc: loc(lexer: lexer, startToken: token)
484+
)
485+
} else {
481486
try lexer.advance()
482487
return EnumValue(
483488
loc: loc(lexer: lexer, startToken: token),

Tests/GraphQLTests/LanguageTests/ParserTests.swift

Lines changed: 140 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -103,21 +103,122 @@ class ParserTests : XCTestCase {
103103
))
104104
}
105105

106-
XCTAssertThrowsError(try parse(source: "{ fieldWithNullableStringInput(input: null) }")) { error in
107-
guard let error = error as? GraphQLError else {
108-
return XCTFail()
109-
}
110-
111-
XCTAssert(error.message.contains(
112-
"Syntax Error GraphQL (1:39) Unexpected Name \"null\""
113-
))
114-
}
115106
}
116107

117108
func testVariableInlineValues() throws {
118109
_ = try parse(source: "{ field(complex: { a: { b: [ $var ] } }) }")
119110
}
120111

112+
func testFieldWithArguments() throws {
113+
let query = """
114+
{
115+
stringArgField(stringArg: "Hello World")
116+
intArgField(intArg: 1)
117+
floatArgField(floatArg: 3.14)
118+
falseArgField(boolArg: false)
119+
trueArgField(boolArg: true)
120+
nullArgField(value: null)
121+
enumArgField(enumArg: VALUE)
122+
multipleArgs(arg1: 1, arg2: false, arg3: THIRD)
123+
}
124+
"""
125+
126+
let expected = Document(
127+
definitions: [
128+
OperationDefinition(
129+
operation: .query,
130+
selectionSet: SelectionSet(
131+
selections: [
132+
Field(
133+
name: Name(value: "stringArgField"),
134+
arguments: [
135+
Argument(
136+
name: Name(value: "stringArg"),
137+
value: StringValue(value: "Hello World", block: false)
138+
)
139+
]
140+
),
141+
Field(
142+
name: Name(value: "intArgField"),
143+
arguments: [
144+
Argument(
145+
name: Name(value: "intArg"),
146+
value: IntValue(value: "1")
147+
)
148+
]
149+
),
150+
Field(
151+
name: Name(value: "floatArgField"),
152+
arguments: [
153+
Argument(
154+
name: Name(value: "floatArg"),
155+
value: FloatValue(value: "3.14")
156+
)
157+
]
158+
),
159+
Field(
160+
name: Name(value: "falseArgField"),
161+
arguments: [
162+
Argument(
163+
name: Name(value: "boolArg"),
164+
value: BooleanValue(value: false)
165+
)
166+
]
167+
),
168+
Field(
169+
name: Name(value: "trueArgField"),
170+
arguments: [
171+
Argument(
172+
name: Name(value: "boolArg"),
173+
value: BooleanValue(value: true)
174+
)
175+
]
176+
),
177+
Field(
178+
name: Name(value: "nullArgField"),
179+
arguments: [
180+
Argument(
181+
name: Name(value: "value"),
182+
value: NullValue()
183+
)
184+
]
185+
),
186+
Field(
187+
name: Name(value: "enumArgField"),
188+
arguments: [
189+
Argument(
190+
name: Name(value: "enumArg"),
191+
value: EnumValue(value: "VALUE")
192+
)
193+
]
194+
),
195+
Field(
196+
name: Name(value: "multipleArgs"),
197+
arguments: [
198+
Argument(
199+
name: Name(value: "arg1"),
200+
value: IntValue(value: "1")
201+
),
202+
Argument(
203+
name: Name(value: "arg2"),
204+
value: BooleanValue(value: false)
205+
),
206+
Argument(
207+
name: Name(value: "arg3"),
208+
value: EnumValue(value: "THIRD")
209+
),
210+
]
211+
),
212+
]
213+
)
214+
)
215+
]
216+
)
217+
218+
let document = try parse(source: query)
219+
XCTAssert(document == expected)
220+
}
221+
121222
// it('parses multi-byte characters', async () => {
122223
// // Note: \u0A0A could be naively interpretted as two line-feed chars.
123224
// expect(
@@ -325,4 +426,34 @@ class ParserTests : XCTestCase {
325426

326427
XCTAssert(try parseType(source: source) == expected)
327428
}
429+
430+
func testParseDirective() throws {
431+
let source = #"""
432+
directive @restricted(
433+
"""The reason for this restriction"""
434+
reason: String = null
435+
) on FIELD_DEFINITION
436+
"""#
437+
438+
let expected = Document(definitions: [
439+
DirectiveDefinition(
440+
description: nil,
441+
name: Name(value: "restricted"),
442+
arguments: [
443+
InputValueDefinition(
444+
description: StringValue(value: "The reason for this restriction", block: true),
445+
name: Name(value: "reason"),
446+
type: NamedType(name: Name(value: "String")),
447+
defaultValue: NullValue()
448+
)
449+
],
450+
locations: [
451+
Name(value: "FIELD_DEFINITION")
452+
]
453+
)
454+
])
455+
456+
let document = try parse(source: source)
457+
XCTAssert(document == expected)
458+
}
328459
}

0 commit comments

Comments
 (0)