Skip to content

Commit 13d1f73

Browse files
committed
add ampersand support
1 parent d11320e commit 13d1f73

File tree

4 files changed

+50
-2
lines changed

4 files changed

+50
-2
lines changed

Sources/GraphQL/Language/AST.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public final class Token {
3939
case eof = "<EOF>"
4040
case bang = "!"
4141
case dollar = "$"
42+
case amp = "&"
4243
case openingParenthesis = "("
4344
case closingParenthesis = ")"
4445
case spread = "..."

Sources/GraphQL/Language/Lexer.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,16 @@ func readToken(lexer: Lexer, prev: Token) throws -> Token {
244244
column: col,
245245
prev: prev
246246
)
247+
// &
248+
case 38:
249+
return Token(
250+
kind: .amp,
251+
start: position,
252+
end: position + 1,
253+
line: line,
254+
column: col,
255+
prev: prev
256+
)
247257
// (
248258
case 40:
249259
return Token(

Sources/GraphQL/Language/Parser.swift

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -796,17 +796,20 @@ func parseObjectTypeDefinition(lexer: Lexer) throws -> ObjectTypeDefinition {
796796
}
797797

798798
/**
799-
* ImplementsInterfaces : implements NamedType+
799+
* ImplementsInterfaces :
800+
* - implements &? NamedType
801+
* - ImplementsInterfaces & NamedType
800802
*/
801803
func parseImplementsInterfaces(lexer: Lexer) throws -> [NamedType] {
802804
var types: [NamedType] = []
803805

804806
if lexer.token.value == "implements" {
805807
try lexer.advance()
806808

809+
try expectOptional(lexer: lexer, kind: .amp)
807810
repeat {
808811
types.append(try parseNamedType(lexer: lexer))
809-
} while peek(lexer: lexer, kind: .name)
812+
} while try expectOptional(lexer: lexer, kind: .amp) != nil || peek(lexer: lexer, kind: .name)
810813
}
811814

812815
return types
@@ -1148,6 +1151,20 @@ func expect(lexer: Lexer, kind: Token.Kind) throws -> Token {
11481151
return token
11491152
}
11501153

1154+
/**
1155+
* If the next token is of the given kind, return that token after advancing
1156+
* the lexer. Otherwise, do not change the parser state and return nil.
1157+
*/
1158+
@discardableResult
1159+
func expectOptional(lexer: Lexer, kind: Token.Kind) throws -> Token? {
1160+
let token = lexer.token
1161+
if token.kind == kind {
1162+
try lexer.advance()
1163+
return token
1164+
}
1165+
return nil
1166+
}
1167+
11511168
/**
11521169
* If the next token is a keyword with the given value, return that token after
11531170
* advancing the lexer. Otherwise, do not change the parser state and return

Tests/GraphQLTests/LanguageTests/ParserTests.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,26 @@ class ParserTests: XCTestCase {
110110
"Syntax Error GraphQL (1:9) Expected Name, found }"
111111
))
112112
}
113+
114+
XCTAssertThrowsError(try parse(source: "type WithImplementsButNoTypes implements {}")) { error in
115+
guard let error = error as? GraphQLError else {
116+
return XCTFail()
117+
}
118+
119+
XCTAssert(error.message.contains(
120+
"Syntax Error GraphQL (1:42) Expected Name, found {"
121+
))
122+
}
123+
124+
XCTAssertThrowsError(try parse(source: "type WithImplementsWithTrailingAmp implements AInterface & {}")) { error in
125+
guard let error = error as? GraphQLError else {
126+
return XCTFail()
127+
}
128+
129+
XCTAssert(error.message.contains(
130+
"Syntax Error GraphQL (1:60) Expected Name, found {"
131+
))
132+
}
113133
}
114134

115135
func testVariableInlineValues() throws {

0 commit comments

Comments
 (0)