tsjson 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/lib/errors/cant_distinguish_type_error.rb +17 -0
  3. data/lib/errors/index.rb +12 -0
  4. data/lib/errors/list_validation_error.rb +34 -0
  5. data/lib/errors/literal_union_validation_error.rb +18 -0
  6. data/lib/errors/literal_validation_error.rb +16 -0
  7. data/lib/errors/not_enough_discriminators.rb +7 -0
  8. data/lib/errors/object_validation_error.rb +56 -0
  9. data/lib/errors/required_field_error.rb +7 -0
  10. data/lib/errors/scalar_union_validation_error.rb +18 -0
  11. data/lib/errors/scalar_validation_error.rb +16 -0
  12. data/lib/errors/unexpected_field_error.rb +7 -0
  13. data/lib/errors/unexpected_value_error.rb +16 -0
  14. data/lib/errors/validation_error.rb +16 -0
  15. data/lib/language/ast/kind.rb +25 -0
  16. data/lib/language/lexer/lexer.rb +452 -0
  17. data/lib/language/lexer/location.rb +20 -0
  18. data/lib/language/lexer/syntax_error.rb +89 -0
  19. data/lib/language/lexer/token.rb +34 -0
  20. data/lib/language/lexer/token_kind.rb +37 -0
  21. data/lib/language/lexer/utils.rb +32 -0
  22. data/lib/language/parser/parser.rb +437 -0
  23. data/lib/language/source.rb +109 -0
  24. data/lib/schema/schema.rb +48 -0
  25. data/lib/schema/schema_builder.rb +148 -0
  26. data/lib/tsjson.rb +1 -0
  27. data/lib/types/any.rb +15 -0
  28. data/lib/types/base.rb +19 -0
  29. data/lib/types/boolean.rb +17 -0
  30. data/lib/types/discriminator_map.rb +116 -0
  31. data/lib/types/enum.rb +47 -0
  32. data/lib/types/float.rb +17 -0
  33. data/lib/types/index.rb +27 -0
  34. data/lib/types/int.rb +17 -0
  35. data/lib/types/intersection.rb +72 -0
  36. data/lib/types/list.rb +33 -0
  37. data/lib/types/literal.rb +25 -0
  38. data/lib/types/literal_union.rb +48 -0
  39. data/lib/types/merge.rb +21 -0
  40. data/lib/types/null.rb +17 -0
  41. data/lib/types/object.rb +87 -0
  42. data/lib/types/scalar.rb +24 -0
  43. data/lib/types/scalar_union.rb +25 -0
  44. data/lib/types/string.rb +17 -0
  45. data/lib/types/union.rb +61 -0
  46. metadata +85 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 78840d40b3eb87c1d769fc9fbf0abe0ed430aec3d133f08853dadbdec8320bc1
4
+ data.tar.gz: 79ec134234bd8258785c415dd0d7399eff8bf4570feaa3ba66f606d5e10544fa
5
+ SHA512:
6
+ metadata.gz: 7746982f586ee257f27776394568361e179c141b8c4bad6f1b49bf578c0a3672f3deea1307646170c5c314e11107a9dd3ecbdb3b9a3542f2beb62aa872f5da10
7
+ data.tar.gz: f41b5427d03b4f66c0d0a3d08b8d2f4dab4c3dfbc1be99bcf2d3e7eeac6206d4db1ce61422bf999c3b9e65f1bd4c41868f2837b54260569decdeb0ea0cf16409
@@ -0,0 +1,17 @@
1
+ module TSJSON
2
+ class CantDistinguishTypeError < ValidationError
3
+ def initialize(discriminators:)
4
+ super
5
+ end
6
+
7
+ def to_human_json
8
+ {
9
+ message: "Can't distinguish type. Need more discriminators",
10
+ details:
11
+ @details[:discriminators].map do |d|
12
+ { message: "#{d[:field]}: #{d[:values].join(', ')}" }
13
+ end
14
+ }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ require_relative './validation_error.rb'
2
+ require_relative './unexpected_value_error.rb'
3
+ require_relative './cant_distinguish_type_error.rb'
4
+ require_relative './scalar_validation_error.rb'
5
+ require_relative './scalar_union_validation_error.rb'
6
+ require_relative './literal_validation_error.rb'
7
+ require_relative './list_validation_error.rb'
8
+ require_relative './object_validation_error.rb'
9
+ require_relative './unexpected_field_error.rb'
10
+ require_relative './not_enough_discriminators.rb'
11
+ require_relative './required_field_error.rb'
12
+ require_relative './literal_union_validation_error.rb'
@@ -0,0 +1,34 @@
1
+ module TSJSON
2
+ class ListValidationError < ValidationError
3
+ def initialize(errors:)
4
+ super
5
+ end
6
+
7
+ def to_json
8
+ {
9
+ code: self.class.name.split('::').last,
10
+ details: {
11
+ errors:
12
+ @details[:errors].map do |e|
13
+ { index: e[:index], error: e[:error].to_json }
14
+ end
15
+ }
16
+ }
17
+ end
18
+
19
+ def to_human_json
20
+ details =
21
+ @details[:errors].map do |err|
22
+ index = err[:index]
23
+ err_human_json = err[:error].to_human_json
24
+
25
+ {
26
+ message: "#{index}: #{err_human_json[:message]}",
27
+ details: err_human_json[:details]
28
+ }.compact
29
+ end
30
+
31
+ return { message: 'One or more items are invalid', details: details }
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,18 @@
1
+ module TSJSON
2
+ class LiteralUnionValidationError < ValidationError
3
+ def initialize(expected_values:, received_value:)
4
+ super
5
+ end
6
+
7
+ def to_human_json
8
+ {
9
+ message:
10
+ "Expected values: `#{
11
+ @details[:expected_values].join(', ')
12
+ }`. Received value `#{@details[:received_value]}` of type `#{
13
+ @details[:received_type].class
14
+ }`"
15
+ }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ module TSJSON
2
+ class LiteralValidationError < ValidationError
3
+ def initialize(expected_value:, received_value:)
4
+ super
5
+ end
6
+
7
+ def to_human_json
8
+ {
9
+ message:
10
+ "Expected value `#{@details[:expected_value]}`. Received value `#{
11
+ @details[:received_value]
12
+ }`"
13
+ }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ module TSJSON
2
+ class NotEnoughDiscriminators < ValidationError
3
+ def initialize(types:)
4
+ super
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,56 @@
1
+ module TSJSON
2
+ class ObjectValidationError < ValidationError
3
+ def initialize(errors:)
4
+ super
5
+ end
6
+
7
+ def to_json
8
+ {
9
+ code: self.class.name.split('::').last,
10
+ details: {
11
+ errors:
12
+ @details[:errors].map do |e|
13
+ { field: e[:field], error: e[:error].to_json }
14
+ end
15
+ }
16
+ }
17
+ end
18
+
19
+ def to_human_json
20
+ required_fields = []
21
+ unexpected_fields = []
22
+ other_errors = []
23
+
24
+ @details[:errors].each do |err|
25
+ if (err[:error].is_a?(RequiredFieldError))
26
+ required_fields.push(err[:field])
27
+ elsif (err[:error].is_a?(UnexpectedFieldError))
28
+ unexpected_fields.push(err[:field])
29
+ else
30
+ err_human_json = err[:error].to_human_json
31
+ other_errors.push(
32
+ {
33
+ message: "#{err[:field]}: #{err_human_json[:message]}",
34
+ details: err_human_json[:details]
35
+ }.compact
36
+ )
37
+ end
38
+ end
39
+
40
+ details = []
41
+ if required_fields.length > 0
42
+ details.push(
43
+ { message: "Required fields: #{required_fields.join(', ')}" }
44
+ )
45
+ end
46
+ if unexpected_fields.length > 0
47
+ details.push(
48
+ { message: "Unexpected fields: #{unexpected_fields.join(', ')}" }
49
+ )
50
+ end
51
+ details.concat(other_errors)
52
+
53
+ { message: 'Invalid object', details: details }
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,7 @@
1
+ module TSJSON
2
+ class RequiredFieldError < ValidationError
3
+ def initialize
4
+ super
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,18 @@
1
+ module TSJSON
2
+ class ScalarUnionValidationError < ValidationError
3
+ def initialize(expected_types:, received_type:, received_value:)
4
+ super
5
+ end
6
+
7
+ def to_human_json
8
+ {
9
+ message:
10
+ "Expected types: `#{
11
+ @details[:expected_types].join(', ')
12
+ }`. Received value `#{@details[:received_value]}` of type `#{
13
+ @details[:received_type]
14
+ }`"
15
+ }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ module TSJSON
2
+ class ScalarValidationError < ValidationError
3
+ def initialize(expected_type:, received_type:, received_value:)
4
+ super
5
+ end
6
+
7
+ def to_human_json
8
+ {
9
+ message:
10
+ "Expected type `#{@details[:expected_type]}`. Received value `#{
11
+ @details[:received_value]
12
+ }` of type `#{@details[:received_type]}`"
13
+ }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ module TSJSON
2
+ class UnexpectedFieldError < ValidationError
3
+ def initialize
4
+ super
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ module TSJSON
2
+ class UnexpectedValueError < ValidationError
3
+ def initialize(field:, expected_values:, received:)
4
+ super
5
+ end
6
+
7
+ def to_human_json
8
+ {
9
+ message:
10
+ "Field '#{@details[:field]}' received unexpected value '#{
11
+ @details[:received]
12
+ }'. Expected values are: #{@details[:expected_values].join(', ')}"
13
+ }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module TSJSON
2
+ class ValidationError < StandardError
3
+ def initialize(**details)
4
+ @details = details
5
+ super(to_json.to_s)
6
+ end
7
+
8
+ def to_json
9
+ { code: self.class.name.split('::').last, details: @details }
10
+ end
11
+
12
+ def to_human_json
13
+ { message: self.class.name.split('::').last }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+ module TSJSON
2
+ module AST
3
+ module Kind
4
+ Name = 'Name'
5
+ StringLiteral = 'StringLiteral'
6
+ Int = 'Int'
7
+ Float = 'Float'
8
+ Document = 'Document'
9
+ TypeAlias = 'TypeAlias'
10
+ TypeParameter = 'TypeParameter'
11
+ TypeLiteral = 'TypeLiteral'
12
+ PropertySignature = 'PropertySignature'
13
+ UnionType = 'UnionType'
14
+ IntersectionType = 'IntersectionType'
15
+ ParenthesizedType = 'ParenthesizedType'
16
+ ArrayType = 'ArrayType'
17
+ Tuple = 'Tuple'
18
+ TypeReference = 'TypeReference'
19
+ Enum = 'Enum'
20
+ EnumMember = 'EnumMember'
21
+ IndexAccess = 'IndexAccess'
22
+ PropertyAccess = 'PropertyAccess'
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,452 @@
1
+ require 'json'
2
+ require_relative './token.rb'
3
+ require_relative './token_kind.rb'
4
+ require_relative './syntax_error.rb'
5
+ require_relative './utils.rb'
6
+
7
+ module TSJSON
8
+ class Lexer
9
+ attr_accessor :source, :last_token, :token, :line, :line_start
10
+
11
+ def initialize(source)
12
+ startOfFileToken = Token.new(TokenKind::SOF, 0, 0, 0, 0, nil)
13
+
14
+ self.source = source
15
+ self.last_token = startOfFileToken
16
+ self.token = startOfFileToken
17
+ self.line = 1
18
+ self.line_start = 0
19
+ end
20
+
21
+ def advance
22
+ self.last_token = self.token
23
+ self.token = self.lookahead
24
+ end
25
+
26
+ def lookahead
27
+ token = self.token
28
+
29
+ if token.kind != TokenKind::EOF
30
+ loop do
31
+ # Note: next is only mutable during parsing, so we cast to allow this.
32
+ token = token.next || (token.next = readToken(token))
33
+ break if (token.kind != TokenKind::COMMENT)
34
+ end
35
+ end
36
+ return token
37
+ end
38
+
39
+ def readToken(prev)
40
+ lexer = self
41
+ source = lexer.source
42
+ body = source.body
43
+ body_length = body.length
44
+
45
+ pos = prev.end_pos
46
+ while (pos < body_length)
47
+ code = char_code_at(body, pos)
48
+
49
+ line = lexer.line
50
+ col = 1 + pos - lexer.line_start
51
+
52
+ #SourceCharacter
53
+ case (code)
54
+ when 0xfeff, 9, 32
55
+ pos += 1
56
+ next
57
+ when 10
58
+ pos += 1
59
+ lexer.line += 1
60
+ lexer.line_start = pos
61
+ next
62
+ when 13
63
+ if (char_code_at(body, pos + 1) == 10)
64
+ pos += 2
65
+ else
66
+ pos += 1
67
+ end
68
+ lexer.line += 1
69
+ lexer.line_start = pos
70
+ next
71
+ when char_code('/')
72
+ if (char_code_at(body, pos + 1) == char_code('/'))
73
+ return read_comment(source, pos, line, col, prev)
74
+ end
75
+ break
76
+ when char_code(',')
77
+ return Token.new(TokenKind::COMMA, pos, pos + 1, line, col, prev)
78
+ when char_code('&')
79
+ return Token.new(TokenKind::AMP, pos, pos + 1, line, col, prev)
80
+ when char_code('(')
81
+ return Token.new(TokenKind::PAREN_L, pos, pos + 1, line, col, prev)
82
+ when char_code(')')
83
+ return Token.new(TokenKind::PAREN_R, pos, pos + 1, line, col, prev)
84
+ when char_code(':')
85
+ return Token.new(TokenKind::COLON, pos, pos + 1, line, col, prev)
86
+ when char_code(';')
87
+ return Token.new(TokenKind::SEMICOLON, pos, pos + 1, line, col, prev)
88
+ when char_code('=')
89
+ return Token.new(TokenKind::EQUALS, pos, pos + 1, line, col, prev)
90
+ when char_code('<')
91
+ return Token.new(TokenKind::CHEVRON_L, pos, pos + 1, line, col, prev)
92
+ when char_code('>')
93
+ return Token.new(TokenKind::CHEVRON_R, pos, pos + 1, line, col, prev)
94
+ when char_code('[')
95
+ return Token.new(TokenKind::BRACKET_L, pos, pos + 1, line, col, prev)
96
+ when char_code(']')
97
+ return Token.new(TokenKind::BRACKET_R, pos, pos + 1, line, col, prev)
98
+ when char_code('{')
99
+ return Token.new(TokenKind::BRACE_L, pos, pos + 1, line, col, prev)
100
+ when char_code('|')
101
+ return Token.new(TokenKind::PIPE, pos, pos + 1, line, col, prev)
102
+ when char_code('}')
103
+ return Token.new(TokenKind::BRACE_R, pos, pos + 1, line, col, prev)
104
+ when char_code('.')
105
+ return Token.new(TokenKind::DOT, pos, pos + 1, line, col, prev)
106
+ when char_code('?')
107
+ return(
108
+ Token.new(TokenKind::QUESTION_MARK, pos, pos + 1, line, col, prev)
109
+ )
110
+ when char_code('"')
111
+ return read_string(source, pos, line, col, prev)
112
+ when char_code('-'), char_code('0'), char_code('1'), char_code('2'),
113
+ char_code('3'), char_code('4'), char_code('5'), char_code('6'),
114
+ char_code('7'), char_code('8'), char_code('9')
115
+ return read_number(source, pos, code, line, col, prev)
116
+ when char_code('A'), char_code('B'), char_code('C'), char_code('D'),
117
+ char_code('E'), char_code('F'), char_code('G'), char_code('H'),
118
+ char_code('I'), char_code('J'), char_code('K'), char_code('L'),
119
+ char_code('M'), char_code('N'), char_code('O'), char_code('P'),
120
+ char_code('Q'), char_code('R'), char_code('S'), char_code('T'),
121
+ char_code('U'), char_code('V'), char_code('W'), char_code('X'),
122
+ char_code('Y'), char_code('Z'), char_code('_'), char_code('a'),
123
+ char_code('b'), char_code('c'), char_code('d'), char_code('e'),
124
+ char_code('f'), char_code('g'), char_code('h'), char_code('i'),
125
+ char_code('j'), char_code('k'), char_code('l'), char_code('m'),
126
+ char_code('n'), char_code('o'), char_code('p'), char_code('q'),
127
+ char_code('r'), char_code('s'), char_code('t'), char_code('u'),
128
+ char_code('v'), char_code('w'), char_code('x'), char_code('y'),
129
+ char_code('z')
130
+ return read_name(source, pos, line, col, prev)
131
+ end
132
+
133
+ raise TSJSONSyntaxError.syntax_error(
134
+ source,
135
+ pos,
136
+ unexpectedCharacterMessage(code)
137
+ )
138
+ end
139
+
140
+ line = lexer.line
141
+ col = 1 + pos - lexer.line_start
142
+ return(
143
+ Token.new(TokenKind::EOF, body_length, body_length, line, col, prev)
144
+ )
145
+ end
146
+
147
+ def char_code_at(str, pos)
148
+ str[pos || 0].ord
149
+ rescue StandardError
150
+ Float::NAN
151
+ end
152
+
153
+ def char_code(str)
154
+ char_code_at(str, 0)
155
+ end
156
+
157
+ def unexpectedCharacterMessage(code)
158
+ if (code < 0x0020 && code != 0x0009 && code != 0x000a && code != 0x000d)
159
+ return "Cannot contain the invalid character #{print_char_code(code)}."
160
+ end
161
+
162
+ if (code == 39)
163
+ return(
164
+ 'Unexpected single quote character (\'), did you mean to use a double quote (")?'
165
+ )
166
+ end
167
+
168
+ return "Cannot parse the unexpected character #{print_char_code(code)}."
169
+ end
170
+
171
+ def print_char_code(code)
172
+ return(
173
+ if is_nan?(code)
174
+ TokenKind::EOF
175
+ else
176
+ if code < 0x007f
177
+ code.chr.to_json
178
+ else
179
+ utf_str = '00' + code.to_s(16).upcase
180
+ "\"\\u#{utf_str.slice(utf_str.length - 4, 4)}\""
181
+ end
182
+ end
183
+ )
184
+ end
185
+
186
+ def read_name(source, start, line, col, prev)
187
+ body = source.body
188
+ bodyLength = body.length
189
+ position = start + 1
190
+ code = 0
191
+ while (
192
+ position != bodyLength &&
193
+ (!is_nan?(code = char_code_at(body, position))) &&
194
+ (
195
+ code == 95 || (code >= 48 && code <= 57) ||
196
+ (code >= 65 && code <= 90) || (code >= 97 && code <= 122)
197
+ )
198
+ )
199
+ position += 1
200
+ end
201
+ return(
202
+ Token.new(
203
+ TokenKind::NAME,
204
+ start,
205
+ position,
206
+ line,
207
+ col,
208
+ prev,
209
+ body[start..position - 1]
210
+ )
211
+ )
212
+ end
213
+
214
+ def read_comment(source, start, line, col, prev)
215
+ body = source.body
216
+
217
+ position = start
218
+
219
+ loop do
220
+ code = char_code_at(body, position += 1)
221
+ break unless !is_nan?(code) && (code > 0x001f || code == 0x0009)
222
+ end
223
+
224
+ return(
225
+ Token.new(
226
+ TokenKind::COMMENT,
227
+ start,
228
+ position,
229
+ line,
230
+ col,
231
+ prev,
232
+ body[start + 2..position - 1]
233
+ )
234
+ )
235
+ end
236
+
237
+ def read_string(source, start, line, col, prev)
238
+ body = source.body
239
+ position = start + 1
240
+ chunkStart = position
241
+ code = 0
242
+ value = ''
243
+
244
+ while (
245
+ position < body.length && (code = char_code_at(body, position)) &&
246
+ !is_nan?(code) && code != 0x000a && code != 0x000d
247
+ )
248
+ # Closing Quote (")
249
+ if (code == 34)
250
+ value += body[chunkStart..position - 1]
251
+ return(
252
+ Token.new(
253
+ TokenKind::STRING,
254
+ start,
255
+ position + 1,
256
+ line,
257
+ col,
258
+ prev,
259
+ value
260
+ )
261
+ )
262
+ end
263
+
264
+ # SourceCharacter
265
+ if (code < 0x0020 && code != 0x0009)
266
+ raise TSJSONSyntaxError.syntax_error(
267
+ source,
268
+ position,
269
+ "Invalid character within String: #{print_char_code(code)}."
270
+ )
271
+ end
272
+
273
+ position += 1
274
+ if (code == 92)
275
+ # \
276
+ value += body[chunkStart..position - 2]
277
+ code = char_code_at(body, position)
278
+ case (code)
279
+ when 34
280
+ value += '"'
281
+ when 47
282
+ value += '/'
283
+ when 92
284
+ value += '\\'
285
+ when 98
286
+ value += '\b'
287
+ when 102
288
+ value += '\f'
289
+ when 110
290
+ value += '\n'
291
+ when 114
292
+ value += '\r'
293
+ when 116
294
+ value += '\t'
295
+ when 117
296
+ charCode =
297
+ uniCharCode(
298
+ char_code_at(body, position + 1),
299
+ char_code_at(body, position + 2),
300
+ char_code_at(body, position + 3),
301
+ char_code_at(body, position + 4)
302
+ )
303
+ if (charCode < 0)
304
+ invalid_sequence = body[position + 1..position + 4]
305
+ raise TSJSONSyntaxError.syntax_error(
306
+ source,
307
+ position,
308
+ "Invalid character escape sequence: \\u#{
309
+ invalid_sequence
310
+ }."
311
+ )
312
+ end
313
+ value += charCode.chr(Encoding::UTF_8)
314
+ position += 4
315
+ else
316
+ raise TSJSONSyntaxError.syntax_error(
317
+ source,
318
+ position,
319
+ "Invalid character escape sequence: \\#{code.chr}."
320
+ )
321
+ end
322
+ position += 1
323
+ chunkStart = position
324
+ end
325
+ end
326
+
327
+ raise TSJSONSyntaxError.syntax_error(
328
+ source,
329
+ position,
330
+ 'Unterminated string.'
331
+ )
332
+ end
333
+
334
+ def read_number(source, start, firstCode, line, col, prev)
335
+ body = source.body
336
+ code = firstCode
337
+ position = start
338
+ isFloat = false
339
+
340
+ code = char_code_at(body, position += 1) if (code === 45) # -
341
+
342
+ if (code === 48)
343
+ # 0
344
+ code = char_code_at(body, position += 1)
345
+ if (code >= 48 && code <= 57)
346
+ raise TSJSONSyntaxError.syntax_error(
347
+ source,
348
+ position,
349
+ "Invalid number, unexpected digit after 0: #{
350
+ print_char_code(code)
351
+ }."
352
+ )
353
+ end
354
+ else
355
+ position = read_digits(source, position, code)
356
+ code = char_code_at(body, position)
357
+ end
358
+
359
+ if (code === 46)
360
+ # .
361
+ isFloat = true
362
+
363
+ code = char_code_at(body, position += 1)
364
+ position = read_digits(source, position, code)
365
+ code = char_code_at(body, position)
366
+ end
367
+
368
+ if (code === 69 || code === 101)
369
+ # E e
370
+ isFloat = true
371
+
372
+ code = char_code_at(body, position += 1)
373
+ code = char_code_at(body, position += 1) if (code === 43 || code === 45) # + -
374
+ position = read_digits(source, position, code)
375
+ code = char_code_at(body, position)
376
+ end
377
+
378
+ # Numbers cannot be followed by . or NameStart
379
+ if (code === 46 || is_name_start(code))
380
+ raise TSJSONSyntaxError.syntax_error(
381
+ source,
382
+ position,
383
+ "Invalid number, expected digit but got: #{
384
+ print_char_code(code)
385
+ }."
386
+ )
387
+ end
388
+
389
+ return(
390
+ Token.new(
391
+ isFloat ? TokenKind::FLOAT : TokenKind::INT,
392
+ start,
393
+ position,
394
+ line,
395
+ col,
396
+ prev,
397
+ body[start..position - 1]
398
+ )
399
+ )
400
+ end
401
+
402
+ def read_digits(source, start, firstCode)
403
+ body = source.body
404
+ position = start
405
+ code = firstCode
406
+ if (code >= 48 && code <= 57)
407
+ # 0 - 9
408
+ loop do
409
+ code = char_code_at(body, position += 1)
410
+ break unless (code >= 48 && code <= 57) # 0 - 9
411
+ end
412
+ return position
413
+ end
414
+ raise TSJSONSyntaxError.syntax_error(
415
+ source,
416
+ position,
417
+ "Invalid number, expected digit but got: #{
418
+ print_char_code(code)
419
+ }."
420
+ )
421
+ end
422
+
423
+ def is_name_start(code)
424
+ return(
425
+ code === 95 || (code >= 65 && code <= 90) || (code >= 97 && code <= 122)
426
+ )
427
+ end
428
+
429
+ def uniCharCode(a, b, c, d)
430
+ return(
431
+ (char2hex(a) << 12) | (char2hex(b) << 8) | (char2hex(c) << 4) |
432
+ char2hex(d)
433
+ )
434
+ end
435
+
436
+ def char2hex(a)
437
+ if a >= 48 && a <= 57
438
+ a - 48 # 0-9
439
+ elsif a >= 65 && a <= 70
440
+ a - 55 # A-F
441
+ elsif a >= 97 && a <= 102
442
+ a - 87 # a-f
443
+ else
444
+ -1
445
+ end
446
+ end
447
+
448
+ def is_nan?(val)
449
+ val.is_a?(Float) && val.nan?
450
+ end
451
+ end
452
+ end