Skip to content

Commit b26b365

Browse files
committed
blob literal
1 parent a993c12 commit b26b365

4 files changed

Lines changed: 25 additions & 5 deletions

File tree

Sources/Compiler/Parse/Lexer.swift

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ struct Lexer {
8282

8383
let peek = peek
8484

85-
if current.isLetter {
85+
// The x' is actually the start of a blob literal which
86+
// will be handled later in the switch
87+
if current.isLetter, !(current == "x" && peek == "'") {
8688
return parseWord()
8789
}
8890

@@ -140,7 +142,14 @@ struct Lexer {
140142
case ("|", _): return consumeSingle(of: .pipe)
141143
case ("^", _): return consumeSingle(of: .carrot)
142144
case ("~", _): return consumeSingle(of: .tilde)
143-
case ("'", _): return parseString()
145+
case ("'", _):
146+
let (contents, location) = parseStringContents()
147+
return Token(kind: .string(contents), location: location)
148+
case ("x", "'"):
149+
let start = startLocation()
150+
advance() // past the x
151+
let (contents, _) = parseStringContents()
152+
return Token(kind: .blob(contents), location: location(from: start))
144153
case ("\"", _): return parseEscapedIdentifier(closing: "\"")
145154
case ("[", _): return parseEscapedIdentifier(closing: "]")
146155
case ("`", _): return parseEscapedIdentifier(closing: "`")
@@ -331,7 +340,9 @@ struct Lexer {
331340
return Token(kind: .hex(value), location: location)
332341
}
333342

334-
private mutating func parseString() -> Token {
343+
/// Gets the raw string between two `'`. Assumes that the starting char
344+
/// is a quote and will consume both the starting and end.
345+
private mutating func parseStringContents() -> (Substring, SourceLocation) {
335346
let tokenStart = startLocation()
336347
advance()
337348
let stringStart = currentIndex
@@ -348,7 +359,7 @@ struct Lexer {
348359
diagnostics.add(.init("Unterminated string", at: location(from: tokenStart)))
349360
}
350361

351-
return Token(kind: .string(source[stringRange]), location: location(from: tokenStart))
362+
return (source[stringRange], location(from: tokenStart))
352363
}
353364

354365
private mutating func skipWhitespace() {

Sources/Compiler/Parse/Parsers.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2058,7 +2058,7 @@ enum Parsers {
20582058
precedence: Operator.Precedence
20592059
) throws -> any ExprSyntax {
20602060
switch state.current.kind {
2061-
case .double, .string, .int, .hex, .currentDate, .currentTime, .currentTimestamp, .true, .false:
2061+
case .double, .string, .int, .hex, .currentDate, .currentTime, .currentTimestamp, .true, .false, .blob:
20622062
return literal(state: &state)
20632063
case .identifier, .star:
20642064
return try columnExpr(state: &state, schema: nil, table: nil)
@@ -2528,6 +2528,8 @@ enum Parsers {
25282528
kind = .numeric(Double(value), isInt: true)
25292529
case let .string(value):
25302530
kind = .string(value)
2531+
case let .blob(value):
2532+
kind = .blob(value)
25312533
case .true:
25322534
kind = .true
25332535
case .false:

Sources/Compiler/Parse/Token.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ struct Token {
176176
case double(Double)
177177
case int(Int)
178178
case hex(Int)
179+
case blob(Substring)
179180

180181
case abort
181182
case action
@@ -387,6 +388,7 @@ struct Token {
387388
case let .double(value): value.description
388389
case let .int(value): value.description
389390
case let .hex(value): value.description
391+
case let .blob(value): value.description
390392
case .abort: "ABORT"
391393
case .action: "ACTION"
392394
case .add: "ADD"

Tests/CompilerTests/LexerTests.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ class LexerTests: XCTestCase {
3131
XCTAssertEqual(tokens, [.int(100), .double(20.2), .int(123), .double(123.4), .double(1e2), .double(3.2e-3), .hex(0xFF), .eof])
3232
}
3333

34+
func testBlob() throws {
35+
let tokens = tokens(of: "x'some words' x'select'")
36+
XCTAssertEqual(tokens, [.blob("some words"), .blob("select"), .eof])
37+
}
38+
3439
func testOperators() throws {
3540
let tokens = tokens(of: "<< <= >> >= || == != <> -> ->> * . ( ) , + - / % < > & | ^ ~")
3641

0 commit comments

Comments
 (0)