1
1
package net.quickwrite.lexer
2
2
3
3
import net.quickwrite.JSONLexerException
4
+ import kotlin.jvm.Throws
4
5
5
6
class StringJSONLexer (private val content : CharSequence ) : JSONLexer {
6
7
private var position = 0
7
8
9
+ @Throws(JSONLexerException ::class )
8
10
override fun getNext (): JSONLexeme {
9
11
skipWhitespace()
10
12
11
13
if (position >= content.length) {
12
- return JSONLexeme (JSONLexemeType .EOF )
14
+ return JSONLexeme (JSONLexemeType .EOF , position )
13
15
}
14
16
15
- return when (content.getChar(position)) {
17
+ return when (val char = content.getChar(position)) {
16
18
' {' -> charLexeme(JSONLexemeType .CURLY_OPEN )
17
19
' }' -> charLexeme(JSONLexemeType .CURLY_CLOSE )
18
20
' [' -> charLexeme(JSONLexemeType .SQUARE_OPEN )
@@ -28,26 +30,45 @@ class StringJSONLexer(private val content: CharSequence) : JSONLexer {
28
30
' t' -> parseLiteral(" true" , JSONLexemeType .TRUE )
29
31
' f' -> parseLiteral(" false" , JSONLexemeType .FALSE )
30
32
' n' -> parseLiteral(" null" , JSONLexemeType .NULL )
31
- else -> throw JSONLexerException (" Invalid token" )
33
+ else -> throw JSONLexerException (" Did not recognize ' $char ' as a valid token character " , getPosition(position) )
32
34
}
33
35
}
34
36
35
37
private fun parseLiteral (input : String , type : JSONLexemeType ): JSONLexeme {
36
- for (i in input) {
37
- if (content.getChar(position) != i) {
38
- throw JSONLexerException (" Expected '$input ', but got malformed input" )
38
+ fun captureRestToken () {
39
+ // Try to capture the rest of the token
40
+ while (! content.getChar(position).isWhitespace() && content.getChar(position) != 0 .toChar()) {
41
+ position++
42
+ }
43
+ }
44
+
45
+ for (i in 0 .. < input.length) {
46
+ if (content.getChar(position) != input[i]) {
47
+ val start = position - i
48
+ captureRestToken()
49
+
50
+ throw JSONLexerException (" Expected '$input ', but got malformed input" , getPosition(start, position - start))
39
51
}
40
52
position++
41
53
}
42
- return JSONLexeme (type)
54
+
55
+ if (content.getChar(position).isLetter()) {
56
+ val start = position - input.length
57
+ captureRestToken()
58
+
59
+ throw JSONLexerException (" Expected '$input ', but got malformed input" , getPosition(start, position - start))
60
+ }
61
+
62
+ return JSONLexeme (type, position - input.length, input.length)
43
63
}
44
64
45
65
private fun charLexeme (type : JSONLexemeType ): JSONLexeme {
46
66
position++
47
- return JSONLexeme (type)
67
+ return JSONLexeme (type, position - 1 )
48
68
}
49
69
50
70
private fun parseString (): JSONLexeme {
71
+ val start = position
51
72
position++
52
73
53
74
val builder = StringBuilder ()
@@ -65,7 +86,8 @@ class StringJSONLexer(private val content: CharSequence) : JSONLexer {
65
86
' r' -> ' \r '
66
87
' t' -> ' \t '
67
88
' u' -> parseUDigitNumber()
68
- else -> throw JSONLexerException (" The character $char cannot be escaped" ) // TODO: Better error handling
89
+ 0 .toChar() -> throw JSONLexerException (" String wasn't correctly terminated" , getPosition(content.length - 1 ))
90
+ else -> throw JSONLexerException (" The character $char cannot be escaped" , getPosition(position - 1 , 2 ))
69
91
})
70
92
position++
71
93
continue
@@ -76,16 +98,17 @@ class StringJSONLexer(private val content: CharSequence) : JSONLexer {
76
98
}
77
99
78
100
if (position >= content.length) {
79
- throw JSONLexerException (" String wasn't terminated" ) // TODO: Better error handling
101
+ throw JSONLexerException (" String wasn't correctly terminated" , getPosition(content.length - 1 ))
80
102
}
81
103
82
104
position++
83
105
84
- return JSONLexeme (JSONLexemeType .STRING , builder.toString())
106
+ return JSONLexeme (JSONLexemeType .STRING , start, position - start, builder.toString())
85
107
}
86
108
87
109
private fun parseUDigitNumber (): Char {
88
110
var number = 0
111
+ val start = position
89
112
90
113
for (i in 3 downTo 0 ) {
91
114
position++
@@ -109,7 +132,10 @@ class StringJSONLexer(private val content: CharSequence) : JSONLexer {
109
132
continue
110
133
}
111
134
112
- throw JSONLexerException (" An invalid hex digit has been provided. Expected 0-9A-Fa-f, but got '$char '" ) // TODO: Better error handling
135
+ throw JSONLexerException (
136
+ " An invalid hex digit has been provided. Expected 0-9A-Fa-f, but got '$char '" ,
137
+ getPosition(start - 1 , position - start + 1 )
138
+ )
113
139
}
114
140
115
141
return number.toChar()
@@ -122,10 +148,13 @@ class StringJSONLexer(private val content: CharSequence) : JSONLexer {
122
148
123
149
if (content.getChar(position) == ' .' ) {
124
150
position++
125
- val start = position
151
+ val pStart = position
126
152
parseNumber()
127
- if (start == position)
128
- throw JSONLexerException (" Trailing dot is not allowed" ) // TODO: Better error handling
153
+ if (pStart == position)
154
+ throw JSONLexerException (
155
+ " Trailing dot is not allowed" ,
156
+ getPosition(start, position - start)
157
+ )
129
158
}
130
159
131
160
if (content.getChar(position) == ' e' || content.getChar(position) == ' E' ) {
@@ -134,13 +163,21 @@ class StringJSONLexer(private val content: CharSequence) : JSONLexer {
134
163
if (content.getChar(position) == ' -' || content.getChar(position) == ' +' )
135
164
position++
136
165
137
- val start = position
166
+ val pStart = position
138
167
parseNumber()
139
- if (start == position)
140
- throw JSONLexerException (" Trailing '${content.getChar(position)} ' is not allowed" ) // TODO: Better error handling
168
+ if (pStart == position)
169
+ throw JSONLexerException (
170
+ " Trailing '${content.getChar(position - 1 )} ' is not allowed" ,
171
+ getPosition(start, position - start)
172
+ )
141
173
}
142
174
143
- return JSONLexeme (JSONLexemeType .NUMBER , content.substring(if (negative) start - 1 else start, position))
175
+ return JSONLexeme (
176
+ JSONLexemeType .NUMBER ,
177
+ start,
178
+ position - start,
179
+ content.substring(if (negative) start - 1 else start, position)
180
+ )
144
181
}
145
182
146
183
private fun parseFirstPart () {
@@ -153,7 +190,7 @@ class StringJSONLexer(private val content: CharSequence) : JSONLexer {
153
190
position++
154
191
155
192
if (content.getChar(position) in ' 0' .. ' 9' ) {
156
- throw JSONLexerException (" A number cannot start with a zero" ) // TODO: Better error handling
193
+ throw JSONLexerException (" A number cannot start with a zero" , getPosition(position - 1 , 2 ))
157
194
}
158
195
return
159
196
}
@@ -172,6 +209,42 @@ class StringJSONLexer(private val content: CharSequence) : JSONLexer {
172
209
}
173
210
}
174
211
212
+ override fun getPosition (position : Int , length : Int ): JSONPositionData {
213
+ var pos = position
214
+
215
+ while (pos - 1 >= 0 && content[pos - 1 ] != ' \n ' && content[pos - 1 ] != ' \r ' ) {
216
+ pos--
217
+ }
218
+
219
+ val startLine = pos
220
+
221
+ pos = position
222
+ while (pos < content.length && content[pos] != ' \n ' && content[pos] != ' \r ' ) {
223
+ pos++
224
+ }
225
+
226
+ class Position : JSONPositionData {
227
+ override fun lineNumber (): Int {
228
+ return content.slice(0 .. position).lines().size
229
+ }
230
+
231
+ override fun linePosition (): Int {
232
+ return position - startLine
233
+ }
234
+
235
+ override fun getLength (): Int {
236
+ return length
237
+ }
238
+
239
+ override fun getLine (): String {
240
+ return content.substring(startLine, pos)
241
+ }
242
+
243
+ }
244
+
245
+ return Position ()
246
+ }
247
+
175
248
private fun CharSequence.getChar (index : Int ): Char {
176
249
if (index >= this .length) {
177
250
return 0 .toChar()
@@ -183,5 +256,5 @@ class StringJSONLexer(private val content: CharSequence) : JSONLexer {
183
256
private fun Char.isWhitespace (): Boolean {
184
257
return this == ' ' || this == ' \r ' || this == ' \n ' || this == ' \t '
185
258
}
259
+ return this [index]
186
260
}
187
-
0 commit comments