@@ -100,18 +100,35 @@ extension Source {
100
100
}
101
101
}
102
102
103
- mutating func tryEatNonEmpty( _ c: Char ) throws -> Bool {
104
- guard !isEmpty else { throw ParseError . expected ( String ( c) ) }
105
- return tryEat ( c)
106
- }
107
-
108
103
mutating func tryEatNonEmpty< C: Collection > ( sequence c: C ) throws -> Bool
109
104
where C. Element == Char
110
105
{
111
- guard !isEmpty else { throw ParseError . expected ( String ( c) ) }
106
+ _ = try recordLoc { src in
107
+ guard !src. isEmpty else { throw ParseError . expected ( String ( c) ) }
108
+ }
112
109
return tryEat ( sequence: c)
113
110
}
114
111
112
+ mutating func tryEatNonEmpty( _ c: Char ) throws -> Bool {
113
+ try tryEatNonEmpty ( sequence: String ( c) )
114
+ }
115
+
116
+ /// Attempt to make a series of lexing steps in `body`, returning `nil` if
117
+ /// unsuccesful, which will revert the source back to its previous state. If
118
+ /// an error is thrown, the source will not be reverted.
119
+ mutating func tryEating< T> (
120
+ _ body: ( inout Source ) throws -> T ?
121
+ ) rethrows -> T ? {
122
+ // We don't revert the source if an error is thrown, as it's useful to
123
+ // maintain the source location in that case.
124
+ let current = self
125
+ guard let result = try body ( & self ) else {
126
+ self = current
127
+ return nil
128
+ }
129
+ return result
130
+ }
131
+
115
132
/// Throws an expected ASCII character error if not matched
116
133
mutating func expectASCII( ) throws -> Located < Character > {
117
134
try recordLoc { src in
@@ -277,7 +294,7 @@ extension Source {
277
294
return try Source . validateUnicodeScalar ( str, . octal)
278
295
279
296
default :
280
- throw ParseError . misc ( " TODO: Or is this an assert? " )
297
+ fatalError ( " Unexpected scalar start " )
281
298
}
282
299
}
283
300
}
@@ -295,16 +312,11 @@ extension Source {
295
312
if src. tryEat ( " + " ) { return . oneOrMore }
296
313
if src. tryEat ( " ? " ) { return . zeroOrOne }
297
314
298
- // FIXME: Actually, PCRE treats empty as literal `{}`...
299
- // But Java 8 errors out?
300
- if src. tryEat ( " { " ) {
301
- // FIXME: Erm, PCRE parses as literal if no lowerbound...
302
- let amt = try src. expectRange ( )
303
- try src. expect ( " } " )
304
- return amt. value // FIXME: track actual range...
315
+ return try src. tryEating { src in
316
+ guard src. tryEat ( " { " ) , let range = try src. lexRange ( ) , src. tryEat ( " } " )
317
+ else { return nil }
318
+ return range. value
305
319
}
306
-
307
- return nil
308
320
}
309
321
guard let amt = amt else { return nil }
310
322
@@ -317,56 +329,56 @@ extension Source {
317
329
return ( amt, kind)
318
330
}
319
331
320
- /// Consume a range
332
+ /// Try to consume a range, returning `nil` if unsuccessful.
321
333
///
322
334
/// Range -> ',' <Int> | <Int> ',' <Int>? | <Int>
323
335
/// | ExpRange
324
336
/// ExpRange -> '..<' <Int> | '...' <Int>
325
337
/// | <Int> '..<' <Int> | <Int> '...' <Int>?
326
- mutating func expectRange ( ) throws -> Located < Quant . Amount > {
338
+ mutating func lexRange ( ) throws -> Located < Quant . Amount > ? {
327
339
try recordLoc { src in
328
- // TODO: lex positive numbers, more specifically...
329
-
330
- let lowerOpt = try src. lexNumber ( )
331
-
332
- // ',' or '...' or '..<' or nothing
333
- let closedRange : Bool ?
334
- if src. tryEat ( " , " ) {
335
- closedRange = true
336
- } else if src. experimentalRanges && src. tryEat ( " . " ) {
337
- try src. expect ( " . " )
338
- if src. tryEat ( " . " ) {
340
+ try src. tryEating { src in
341
+ let lowerOpt = try src. lexNumber ( )
342
+
343
+ // ',' or '...' or '..<' or nothing
344
+ // TODO: We ought to try and consume whitespace here and emit a
345
+ // diagnostic for the user warning them that it would cause the range to
346
+ // be treated as literal.
347
+ let closedRange : Bool ?
348
+ if src. tryEat ( " , " ) {
339
349
closedRange = true
350
+ } else if src. experimentalRanges && src. tryEat ( " . " ) {
351
+ try src. expect ( " . " )
352
+ if src. tryEat ( " . " ) {
353
+ closedRange = true
354
+ } else {
355
+ try src. expect ( " < " )
356
+ closedRange = false
357
+ }
340
358
} else {
341
- try src. expect ( " < " )
342
- closedRange = false
359
+ closedRange = nil
343
360
}
344
- } else {
345
- closedRange = nil
346
- }
347
- // FIXME: wait, why `try!` ?
348
- let upperOpt : Located < Int > ?
349
- if let u = try ! src. lexNumber ( ) {
350
- upperOpt = ( closedRange == true ) ? u : Located ( u. value- 1 , u. location)
351
- } else {
352
- upperOpt = nil
353
- }
354
361
355
- switch ( lowerOpt, closedRange, upperOpt) {
356
- case let ( l? , nil , nil ) :
357
- return . exactly( l)
358
- case let ( l? , true , nil ) :
359
- return . nOrMore( l)
360
- case let ( nil , _, u? ) :
361
- return . upToN( u)
362
- case let ( l? , _, u? ) :
363
- // FIXME: source location tracking
364
- return . range( l, u)
365
-
366
- case let ( nil , nil , u) where u != nil :
367
- fatalError ( " Not possible " )
368
- default :
369
- throw ParseError . misc ( " Invalid range " )
362
+ let upperOpt = try src. lexNumber ( ) ? . map { upper in
363
+ // If we have an open range, the upper bound should be adjusted down.
364
+ closedRange == true ? upper : upper - 1
365
+ }
366
+
367
+ switch ( lowerOpt, closedRange, upperOpt) {
368
+ case let ( l? , nil , nil ) :
369
+ return . exactly( l)
370
+ case let ( l? , true , nil ) :
371
+ return . nOrMore( l)
372
+ case let ( nil , _? , u? ) :
373
+ return . upToN( u)
374
+ case let ( l? , _? , u? ) :
375
+ return . range( l, u)
376
+
377
+ case ( nil , nil , _? ) :
378
+ fatalError ( " Didn't lex lower bound, but lexed upper bound? " )
379
+ default :
380
+ return nil
381
+ }
370
382
}
371
383
}
372
384
}
@@ -393,12 +405,24 @@ extension Source {
393
405
try lexUntil ( eating: String ( end) )
394
406
}
395
407
396
- /// Expect a linear run of non-nested non-empty content
408
+ /// Expect a linear run of non-nested non-empty content ending with a given
409
+ /// delimiter. If `ignoreEscaped` is true, escaped characters will not be
410
+ /// considered for the ending delimiter.
397
411
private mutating func expectQuoted(
398
- endingWith end: String
412
+ endingWith end: String , ignoreEscaped : Bool = false
399
413
) throws -> Located < String > {
400
414
try recordLoc { src in
401
- let result = try src. lexUntil ( eating: end) . value
415
+ let result = try src. lexUntil { src in
416
+ if try src. tryEatNonEmpty ( sequence: end) {
417
+ return true
418
+ }
419
+ // Ignore escapes if we're allowed to. lexUntil will consume the next
420
+ // character.
421
+ if ignoreEscaped, src. tryEat ( " \\ " ) {
422
+ try src. expectNonEmpty ( )
423
+ }
424
+ return false
425
+ } . value
402
426
guard !result. isEmpty else {
403
427
throw ParseError . misc ( " Expected non-empty contents " )
404
428
}
@@ -412,7 +436,7 @@ extension Source {
412
436
///
413
437
/// With `SyntaxOptions.experimentalQuotes`, also accepts
414
438
///
415
- /// ExpQuote -> '"' [^"]* '"'
439
+ /// ExpQuote -> '"' ('\"' | [^"]) * '"'
416
440
///
417
441
/// Future: Experimental quotes are full fledged Swift string literals
418
442
///
@@ -424,8 +448,7 @@ extension Source {
424
448
return try src. expectQuoted ( endingWith: #"\E"# ) . value
425
449
}
426
450
if src. experimentalQuotes, src. tryEat ( " \" " ) {
427
- // TODO: escaped `"`, etc...
428
- return try src. expectQuoted ( endingWith: " \" " ) . value
451
+ return try src. expectQuoted ( endingWith: " \" " , ignoreEscaped: true ) . value
429
452
}
430
453
return nil
431
454
}
0 commit comments