Skip to content

Commit bb00a76

Browse files
committed
finish block string support implementation with many tests
this commit has the debugging code still in it. next one will remove that.
1 parent ac824d1 commit bb00a76

File tree

2 files changed

+242
-82
lines changed

2 files changed

+242
-82
lines changed

Sources/GraphQL/Language/Lexer.swift

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -748,8 +748,96 @@ func readRawString(source: Source, start: Int, line: Int, col: Int, prev: Token)
748748
*/
749749

750750
func blockStringValue(rawValue: String) -> String {
751-
print("\n\n **** blockStringValue Not Yet Implemented **** \n\n")
752-
return rawValue
751+
752+
print("inputString: \n>>>\(rawValue)<<<\n") // debug
753+
754+
var commonIndent: Int = 0
755+
var lines = rawValue.utf8.split { (code) -> Bool in
756+
return code == 0x000A || code == 0x000D
757+
}
758+
759+
for line in lines { print(String(line)) } // debug
760+
761+
for idx in lines.indices {
762+
let line = lines[idx]
763+
// we already drop this before we get here..
764+
if idx == lines.startIndex { continue }
765+
if let indentIndex = line.firstIndex(where: { $0 != 0x0009 && $0 != 0x0020 }) {
766+
let indent = line.distance(from: line.startIndex, to: indentIndex)
767+
if commonIndent == 0 || indent < commonIndent {
768+
commonIndent = indent
769+
}
770+
}
771+
}
772+
773+
print("\ncommonIndent: \(commonIndent)\n") // debug
774+
775+
var newLines: [String.UTF8View.SubSequence] = []
776+
if commonIndent != 0 {
777+
for idx in lines.indices {
778+
let line = lines[idx]
779+
// pretty sure they are dropping thinking about """\n which we already drop
780+
if idx == lines.startIndex {
781+
newLines.append(line)
782+
continue
783+
}
784+
newLines.append(line.dropFirst(commonIndent))
785+
}
786+
787+
for line in lines { print(String(line)) } // debug
788+
print()
789+
for line in newLines { print(String(line)) } // debug
790+
791+
lines = newLines
792+
newLines.removeAll()
793+
}
794+
795+
for idx in lines.indices {
796+
let line = lines[idx]
797+
if newLines.count == 0,
798+
line.firstIndex(where: { $0 != 0x0009 && $0 != 0x0020 }) == nil {
799+
continue
800+
}
801+
newLines.append(line)
802+
}
803+
804+
for line in newLines { print(String(line)) } // debug
805+
806+
lines = newLines
807+
newLines.removeAll()
808+
print()
809+
for line in lines { print(String(line)) } // debug
810+
811+
for idx in lines.indices.reversed() {
812+
let line = lines[idx]
813+
if newLines.count == 0,
814+
line.firstIndex(where: { $0 != 0x0009 && $0 != 0x0020 }) == nil {
815+
continue
816+
}
817+
newLines.insert(line, at: newLines.startIndex)
818+
}
819+
820+
for line in newLines { print(String(line)) } // debug
821+
822+
lines = newLines
823+
newLines.removeAll()
824+
print()
825+
for line in lines { print(String(line)) } // debug
826+
827+
var result: Substring = Substring()
828+
829+
for idx in lines.indices {
830+
if idx == lines.startIndex {
831+
result.append(contentsOf: Substring(lines[idx]))
832+
} else {
833+
result.append(contentsOf: Substring("\u{000A}"))
834+
result.append(contentsOf: Substring(lines[idx]))
835+
}
836+
}
837+
838+
print( "\n>>>\(result)<<<\n" ) // debug
839+
840+
return String(result)
753841
}
754842

755843
/**

Tests/GraphQLTests/LanguageTests/LexerTests.swift

Lines changed: 152 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -208,86 +208,6 @@ class LexerTests : XCTestCase {
208208
}
209209
}
210210

211-
func testMultiLineStrings() throws {
212-
let token = try lexOne(#" """ Multi-line string\n With Inner "foo" \n should be Valid """ "#)
213-
let expected = Token(
214-
kind: .blockString,
215-
start: 1,
216-
end: 64,
217-
line: 1,
218-
column: 2,
219-
value: " Multi-line string\n With Inner \"foo\" \n should be Valid "
220-
)
221-
222-
XCTAssertEqual(token, expected, "\nexpected: \n \(dump(expected))\n\ngot: \n\(dump(token))\n")
223-
}
224-
225-
func testMultiLineStringsUnescapedReturns() throws {
226-
let token = try lexOne(#"""
227-
"""
228-
Multi-line string
229-
with Inner "foo"
230-
should be valid
231-
"""
232-
"""#)
233-
234-
let expected = Token(
235-
kind: .blockString,
236-
start: 0,
237-
end: 59,
238-
line: 1,
239-
column: 1,
240-
value: " Multi-line string\nwith Inner \"foo\"\nshould be valid\n"
241-
)
242-
243-
XCTAssertEqual(token, expected, "expected: \n \(dump(expected))\ngot: \n\(dump(token))\n")
244-
}
245-
246-
func fails_testMultiLineStringsUnescapedReturnsIndentationTest() throws {
247-
let token = try lexOne(#"""
248-
"""
249-
Multi-line string {
250-
with Inner "foo"
251-
should be valid indented
252-
}
253-
"""
254-
"""#)
255-
256-
let expected = Token(
257-
kind: .string,
258-
start: 0,
259-
end: 71,
260-
line: 1,
261-
column: 1,
262-
value: "Multi-line string {\nwith Inner \"foo\"\nshould be valid indented\n}"
263-
)
264-
265-
XCTAssertEqual(token, expected, "expected: \n \(dump(expected))\ngot: \n\(dump(token))\n")
266-
}
267-
268-
269-
func testEmptyQuote() throws {
270-
let token = try lexOne(#" "" "#)
271-
let expected = Token(kind: .string, start: 1, end: 3, line: 1, column: 2, value: "")
272-
XCTAssertEqual(token, expected, "\n\(dump(expected))\n\(dump(token))\n")
273-
}
274-
275-
func testEmptySimpleMultilineBlockQuote() throws {
276-
let token = try lexOne(#" """""" "#)
277-
let expected = Token(kind: .blockString, start: 1, end: 7, line: 1, column: 2, value: "")
278-
XCTAssertEqual(token, expected, "\n\(dump(expected))\n\(dump(token))\n")
279-
}
280-
281-
func testEmptyTrimmedCharactersMultilineBlockQuote() throws {
282-
let token = try lexOne(#"""
283-
"""
284-
"""
285-
"""#)
286-
let expected = Token(kind: .blockString, start: 0, end: 7, line: 1, column: 1, value: "")
287-
XCTAssertEqual(token, expected, "\n\(dump(expected))\n\(dump(token))\n")
288-
}
289-
290-
291211
func testStringErrors() throws {
292212
XCTAssertThrowsError(try lexOne("\""))
293213
// "Syntax Error GraphQL (1:2) Unterminated string"
@@ -821,4 +741,156 @@ class LexerTests : XCTestCase {
821741

822742
XCTAssertEqual(tokens.map({ $0.kind }), expectedKinds)
823743
}
744+
745+
//
746+
// Tests for Blockstring support
747+
//
748+
749+
// Tests for sub-routines of `readString` for Blockstring handling
750+
751+
func testReadRawString() throws {
752+
let sourceStr = #"""
753+
"""
754+
TopLevel {
755+
indented
756+
alsoIndented
757+
}
758+
"""
759+
"""#
760+
761+
let expected = Token(kind: .blockString,
762+
start: 0,
763+
end: 66,
764+
line: 1,
765+
column: 1,
766+
value: " TopLevel {\n indented\n alsoIndented\n }\n",
767+
prev: nil, next: nil)
768+
769+
let source = Source(body: sourceStr, name: "TestSource")
770+
let token = try readRawString(source: source,
771+
start: 0,
772+
line: 1,
773+
col: 1,
774+
prev: Token(kind: .sof, start: 0, end: 0, line: 1, column: 1))
775+
776+
XCTAssertEqual(token, expected, "\n\(dump(expected))\n\(dump(token))\n")
777+
print(String(describing: token.value))
778+
}
779+
780+
func testBlockStringIndentationAndBlankLine() throws {
781+
let rawString = "\n\n\n TopLevel {\n indented\n alsoIndented\n }\n\n\n\t\t\n" // from testReadRawString() above
782+
let cleanedString = blockStringValue(rawValue: rawString)
783+
784+
XCTAssertEqual(cleanedString, " TopLevel {\n indented\n alsoIndented\n}")
785+
}
786+
787+
func testBlockStringDoubleIndentationAndBlankLine() throws {
788+
let rawString = "\n\n\n TopLevel {\n indented: {\n foo: String\n }\n alsoIndented\n }\n\n\n\t\t\n" // from testReadRawString() above
789+
let cleanedString = blockStringValue(rawValue: rawString)
790+
791+
XCTAssertEqual(cleanedString, " TopLevel {\n indented: {\n foo: String\n }\n alsoIndented\n}")
792+
}
793+
794+
func testBlockStringIndentationAndBlankLineFirstLineNotIndentedWeird() throws {
795+
let rawString = "\n\n\nTopLevel {\n indented\n alsoIndented\n }\n\n\n\t\t\n" // from testReadRawString() above
796+
let cleanedString = blockStringValue(rawValue: rawString)
797+
798+
XCTAssertEqual(cleanedString, "TopLevel {\n indented\n alsoIndented\n}")
799+
}
800+
801+
// Lexer tests for multi-line string token parsing
802+
803+
func testMultiLineStrings() throws {
804+
let token = try lexOne(#" """ Multi-line string\n With Inner "foo" \nshould be Valid """ "#)
805+
let expected = Token(
806+
kind: .blockString,
807+
start: 1,
808+
end: 63,
809+
line: 1,
810+
column: 2,
811+
value: " Multi-line string\n With Inner \"foo\" \nshould be Valid "
812+
)
813+
814+
XCTAssertEqual(token, expected, "\nexpected: \n \(dump(expected))\n\ngot: \n\(dump(token))\n")
815+
}
816+
817+
func testMultiLineStringsSingleSpaceIndent() throws {
818+
let token = try lexOne(#" """ Multi-line string\n With Inner "foo" \n should be Valid """ "#)
819+
let expected = Token(
820+
kind: .blockString,
821+
start: 1,
822+
end: 64,
823+
line: 1,
824+
column: 2,
825+
value: " Multi-line string\nWith Inner \"foo\" \nshould be Valid "
826+
)
827+
828+
XCTAssertEqual(token, expected, "\nexpected: \n \(dump(expected))\n\ngot: \n\(dump(token))\n")
829+
}
830+
831+
func testMultiLineStringsUnescapedReturns() throws {
832+
let token = try lexOne(#"""
833+
"""
834+
Multi-line string
835+
with Inner "foo"
836+
should be valid
837+
"""
838+
"""#)
839+
840+
let expected = Token(
841+
kind: .blockString,
842+
start: 0,
843+
end: 59,
844+
line: 1,
845+
column: 1,
846+
value: " Multi-line string\nwith Inner \"foo\"\nshould be valid"
847+
)
848+
849+
XCTAssertEqual(token, expected, "expected: \n \(dump(expected))\ngot: \n\(dump(token))\n")
850+
}
851+
852+
func testMultiLineStringsUnescapedReturnsIndentationTest() throws {
853+
let token = try lexOne(#"""
854+
"""
855+
Multi-line string {
856+
with Inner "foo"
857+
should be valid indented
858+
}
859+
"""
860+
"""#)
861+
862+
let expected = Token(
863+
kind: .blockString,
864+
start: 0,
865+
end: 79,
866+
line: 1,
867+
column: 1,
868+
value: "Multi-line string {\n with Inner \"foo\"\n should be valid indented\n}"
869+
)
870+
871+
XCTAssertEqual(token, expected, "expected: \n \(dump(expected))\ngot: \n\(dump(token))\n")
872+
}
873+
874+
// Test empty strings & multi-line string lexer token parsing
875+
876+
func testEmptyQuote() throws {
877+
let token = try lexOne(#" "" "#)
878+
let expected = Token(kind: .string, start: 1, end: 3, line: 1, column: 2, value: "")
879+
XCTAssertEqual(token, expected, "\n\(dump(expected))\n\(dump(token))\n")
880+
}
881+
882+
func testEmptySimpleMultilineBlockQuote() throws {
883+
let token = try lexOne(#" """""" "#)
884+
let expected = Token(kind: .blockString, start: 1, end: 7, line: 1, column: 2, value: "")
885+
XCTAssertEqual(token, expected, "\n\(dump(expected))\n\(dump(token))\n")
886+
}
887+
888+
func testEmptyTrimmedCharactersMultilineBlockQuote() throws {
889+
let token = try lexOne(#"""
890+
"""
891+
"""
892+
"""#)
893+
let expected = Token(kind: .blockString, start: 0, end: 7, line: 1, column: 1, value: "")
894+
XCTAssertEqual(token, expected, "\n\(dump(expected))\n\(dump(token))\n")
895+
}
824896
}

0 commit comments

Comments
 (0)