26
26
27
27
import Foundation
28
28
29
+ public struct TokenPosition {
30
+ let line : Int
31
+ let column : Int
32
+ let source : String
33
+
34
+ var sourceLine : String {
35
+ return String ( source. split ( separator: " \n " ) [ line] )
36
+ }
37
+
38
+ var tokenMarker : String {
39
+ // Creates a "^" marker underneath the source line pointing to the token
40
+ var output = " "
41
+
42
+ // Add marker at appropriate column
43
+ for _ in 0 ..< ( column - 1 ) {
44
+ output += " "
45
+ }
46
+
47
+ output += " ^ "
48
+ return output
49
+ }
50
+ }
29
51
30
- public enum TokenType : Equatable {
31
- case lParen
32
- case rParen
33
- case lBrace
34
- case rBrace
35
- case symbol( String )
36
- case float( Double )
37
- case integer( Int )
38
- case string( String )
52
+ public enum TokenType {
53
+ case lParen( TokenPosition )
54
+ case rParen( TokenPosition )
55
+ case lBrace( TokenPosition )
56
+ case rBrace( TokenPosition )
57
+ case symbol( TokenPosition , String )
58
+ case float( TokenPosition , Double )
59
+ case integer( TokenPosition , Int )
60
+ case string( TokenPosition , String )
39
61
}
40
62
41
63
public func == ( a: TokenType , b: TokenType ) -> Bool {
42
64
switch ( a, b) {
43
65
case ( . lParen, . lParen) : return true
44
66
case ( . rParen, . rParen) : return true
45
- case ( . symbol( let a) , . symbol( let b) ) where a == b: return true
46
- case ( . float( let a) , . float( let b) ) where a == b: return true
47
- case ( . integer( let a) , . integer( let b) ) where a == b: return true
48
- case ( . string( let a) , . string( let b) ) where a == b: return true
67
+ case ( . symbol( _ , let a) , . symbol( _ , let b) ) where a == b: return true
68
+ case ( . float( _ , let a) , . float( _ , let b) ) where a == b: return true
69
+ case ( . integer( _ , let a) , . integer( _ , let b) ) where a == b: return true
70
+ case ( . string( _ , let a) , . string( _ , let b) ) where a == b: return true
49
71
default : return false
50
72
}
51
73
}
@@ -77,7 +99,7 @@ class LParenTokenMatcher: TokenMatcher {
77
99
static func getToken( _ stream: StringStream ) -> TokenType ? {
78
100
if isMatch ( stream) {
79
101
stream. advanceCharacter ( )
80
- return TokenType . lParen
102
+ return TokenType . lParen ( TokenPosition ( line : stream . currentLine , column : stream . currentColumn , source : stream . str ) )
81
103
}
82
104
return nil
83
105
}
@@ -91,7 +113,7 @@ class RParenTokenMatcher: TokenMatcher {
91
113
static func getToken( _ stream: StringStream ) -> TokenType ? {
92
114
if isMatch ( stream) {
93
115
stream. advanceCharacter ( )
94
- return TokenType . rParen
116
+ return TokenType . rParen ( TokenPosition ( line : stream . currentLine , column : stream . currentColumn , source : stream . str ) )
95
117
}
96
118
return nil
97
119
}
@@ -105,7 +127,7 @@ class LBraceTokenMatcher: TokenMatcher {
105
127
static func getToken( _ stream: StringStream ) -> TokenType ? {
106
128
if isMatch ( stream) {
107
129
stream. advanceCharacter ( )
108
- return TokenType . lBrace
130
+ return TokenType . lBrace ( TokenPosition ( line : stream . currentLine , column : stream . currentColumn , source : stream . str ) )
109
131
}
110
132
return nil
111
133
}
@@ -119,7 +141,7 @@ class RBraceTokenMatcher: TokenMatcher {
119
141
static func getToken( _ stream: StringStream ) -> TokenType ? {
120
142
if isMatch ( stream) {
121
143
stream. advanceCharacter ( )
122
- return TokenType . rBrace
144
+ return TokenType . rBrace ( TokenPosition ( line : stream . currentLine , column : stream . currentColumn , source : stream . str ) )
123
145
}
124
146
return nil
125
147
}
@@ -145,7 +167,7 @@ class SymbolMatcher: TokenMatcher {
145
167
}
146
168
}
147
169
148
- return TokenType . symbol ( tok)
170
+ return TokenType . symbol ( TokenPosition ( line : stream . currentLine , column : stream . currentColumn , source : stream . str ) , tok)
149
171
}
150
172
return nil
151
173
}
@@ -193,7 +215,13 @@ class StringMatcher: TokenMatcher {
193
215
stream. advanceCharacter ( )
194
216
195
217
guard let escapeChar = stream. currentCharacter else {
196
- throw LispError . lexer ( msg: " Error in string: expected escape character " )
218
+ let pos = TokenPosition ( line: stream. currentLine, column: stream. currentColumn + 1 , source: stream. str)
219
+ let msg = """
220
+ \( pos. line) : \( pos. column) : Expected escape character:
221
+ \( pos. sourceLine)
222
+ \( pos. tokenMarker)
223
+ """
224
+ throw LispError . lexer ( msg: msg)
197
225
}
198
226
199
227
let escapeResult : String
@@ -218,10 +246,17 @@ class StringMatcher: TokenMatcher {
218
246
throw LispError . lexer ( msg: " Error in string: invalid hex escape sequence: \( String ( [ h1, h2] ) ) " )
219
247
}
220
248
escapeResult = String ( Character ( UnicodeScalar ( hexValue) ) )
221
-
249
+ case " \" " :
250
+ escapeResult = " \" "
222
251
223
252
default :
224
- throw LispError . lexer ( msg: " Unknown escape character in string: \\ \( escapeChar) " )
253
+ let pos = TokenPosition ( line: stream. currentLine, column: stream. currentColumn + 1 , source: stream. str)
254
+ let msg = """
255
+ \( pos. line) : \( pos. column) : Unknown escape character in string: \\ \( escapeChar)
256
+ \( pos. sourceLine)
257
+ \( pos. tokenMarker)
258
+ """
259
+ throw LispError . lexer ( msg: msg)
225
260
}
226
261
227
262
tok += escapeResult
@@ -239,7 +274,7 @@ class StringMatcher: TokenMatcher {
239
274
240
275
stream. advanceCharacter ( )
241
276
242
- return TokenType . string ( tok)
277
+ return TokenType . string ( TokenPosition ( line : stream . currentLine , column : stream . currentColumn , source : stream . str ) , tok)
243
278
}
244
279
245
280
return nil
@@ -294,12 +329,12 @@ class NumberMatcher: TokenMatcher {
294
329
guard let num = Double ( tok) else {
295
330
throw LispError . lexer ( msg: " \( tok) is not a valid floating point number. " )
296
331
}
297
- return TokenType . float ( num)
332
+ return TokenType . float ( TokenPosition ( line : stream . currentLine , column : stream . currentColumn , source : stream . str ) , num)
298
333
} else {
299
334
guard let num = Int ( tok) else {
300
335
throw LispError . lexer ( msg: " \( tok) is not a valid number. " )
301
336
}
302
- return TokenType . integer ( num)
337
+ return TokenType . integer ( TokenPosition ( line : stream . currentLine , column : stream . currentColumn , source : stream . str ) , num)
303
338
}
304
339
305
340
}
@@ -341,6 +376,9 @@ class StringStream {
341
376
var currentCharacterIdx : String . Index
342
377
var nextCharacterIdx : String . Index ?
343
378
var characterCount : Int
379
+
380
+ var currentLine : Int
381
+ var currentColumn : Int
344
382
345
383
init ( source: String ) {
346
384
str = source
@@ -350,12 +388,22 @@ class StringStream {
350
388
nextCharacterIdx = str. index ( after: currentCharacterIdx)
351
389
currentCharacter = str. characters [ currentCharacterIdx]
352
390
391
+ currentLine = 0
392
+ currentColumn = 0
393
+
353
394
if str. count > 1 {
354
395
nextCharacter = str [ nextCharacterIdx!]
355
396
}
356
397
}
357
398
358
399
func advanceCharacter( ) {
400
+ if currentCharacter != nil && currentCharacter! == " \n " {
401
+ currentLine += 1
402
+ currentColumn = 0
403
+ } else {
404
+ currentColumn += 1
405
+ }
406
+
359
407
position += 1
360
408
361
409
if position >= characterCount
@@ -453,11 +501,16 @@ class Tokenizer {
453
501
}
454
502
} else {
455
503
if count == 0 {
456
- throw LispError . lexer ( msg: " Unrecognized character ' \( stream. currentCharacter ?? " " . first!) ' " )
504
+ let pos = TokenPosition ( line: stream. currentLine, column: stream. currentColumn + 1 , source: stream. str)
505
+ let msg = """
506
+ \( pos. line) : \( pos. column) : Unrecognized character ' \( stream. currentCharacter ?? " " . first!) ':
507
+ \t \( pos. sourceLine)
508
+ \t \( pos. tokenMarker)
509
+ """
510
+ throw LispError . lexer ( msg: msg)
457
511
}
458
512
}
459
513
460
514
return try getNextToken ( )
461
515
}
462
-
463
516
}
0 commit comments