tsjson 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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