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.
- checksums.yaml +7 -0
- data/lib/errors/cant_distinguish_type_error.rb +17 -0
- data/lib/errors/index.rb +12 -0
- data/lib/errors/list_validation_error.rb +34 -0
- data/lib/errors/literal_union_validation_error.rb +18 -0
- data/lib/errors/literal_validation_error.rb +16 -0
- data/lib/errors/not_enough_discriminators.rb +7 -0
- data/lib/errors/object_validation_error.rb +56 -0
- data/lib/errors/required_field_error.rb +7 -0
- data/lib/errors/scalar_union_validation_error.rb +18 -0
- data/lib/errors/scalar_validation_error.rb +16 -0
- data/lib/errors/unexpected_field_error.rb +7 -0
- data/lib/errors/unexpected_value_error.rb +16 -0
- data/lib/errors/validation_error.rb +16 -0
- data/lib/language/ast/kind.rb +25 -0
- data/lib/language/lexer/lexer.rb +452 -0
- data/lib/language/lexer/location.rb +20 -0
- data/lib/language/lexer/syntax_error.rb +89 -0
- data/lib/language/lexer/token.rb +34 -0
- data/lib/language/lexer/token_kind.rb +37 -0
- data/lib/language/lexer/utils.rb +32 -0
- data/lib/language/parser/parser.rb +437 -0
- data/lib/language/source.rb +109 -0
- data/lib/schema/schema.rb +48 -0
- data/lib/schema/schema_builder.rb +148 -0
- data/lib/tsjson.rb +1 -0
- data/lib/types/any.rb +15 -0
- data/lib/types/base.rb +19 -0
- data/lib/types/boolean.rb +17 -0
- data/lib/types/discriminator_map.rb +116 -0
- data/lib/types/enum.rb +47 -0
- data/lib/types/float.rb +17 -0
- data/lib/types/index.rb +27 -0
- data/lib/types/int.rb +17 -0
- data/lib/types/intersection.rb +72 -0
- data/lib/types/list.rb +33 -0
- data/lib/types/literal.rb +25 -0
- data/lib/types/literal_union.rb +48 -0
- data/lib/types/merge.rb +21 -0
- data/lib/types/null.rb +17 -0
- data/lib/types/object.rb +87 -0
- data/lib/types/scalar.rb +24 -0
- data/lib/types/scalar_union.rb +25 -0
- data/lib/types/string.rb +17 -0
- data/lib/types/union.rb +61 -0
- metadata +85 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
module TSJSON
|
2
|
+
class Location
|
3
|
+
def self.get_location(source, position)
|
4
|
+
lineRegexp = /\r\n|[\n\r]/
|
5
|
+
line = 1
|
6
|
+
column = position + 1
|
7
|
+
body = source.body
|
8
|
+
new_lines_indexes = (0...body.length).find_all { |i| body[i, 1] == "\n" }
|
9
|
+
|
10
|
+
new_lines_indexes.each do |index|
|
11
|
+
break if index >= position
|
12
|
+
|
13
|
+
line += 1
|
14
|
+
column = position + 1 - (index + 1)
|
15
|
+
end
|
16
|
+
|
17
|
+
return { line: line, column: column }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require_relative './location.rb'
|
2
|
+
|
3
|
+
module TSJSON
|
4
|
+
class TSJSONSyntaxError < StandardError
|
5
|
+
attr_accessor :message,
|
6
|
+
:nodes,
|
7
|
+
:source,
|
8
|
+
:positions,
|
9
|
+
:path,
|
10
|
+
:locations,
|
11
|
+
:originalError
|
12
|
+
|
13
|
+
def initialize(
|
14
|
+
message, nodes, source, positions, path = nil, originalError = nil
|
15
|
+
)
|
16
|
+
self.message = message
|
17
|
+
|
18
|
+
_nodes =
|
19
|
+
if nodes.is_a?(Array)
|
20
|
+
nodes.length != 0 ? nodes : nil
|
21
|
+
else
|
22
|
+
nodes ? [nodes] : nil
|
23
|
+
end
|
24
|
+
|
25
|
+
# Compute locations in the source for the given nodes/positions.
|
26
|
+
_source = source
|
27
|
+
_source = _nodes[0].loc&.source if (!_source && _nodes)
|
28
|
+
|
29
|
+
_positions = positions
|
30
|
+
|
31
|
+
if (!_positions && _nodes)
|
32
|
+
_positions =
|
33
|
+
_nodes.reduce([]) do |list, node|
|
34
|
+
list.push(node.loc.start) if (node.loc)
|
35
|
+
return list
|
36
|
+
end
|
37
|
+
end
|
38
|
+
_positions = undefined if (_positions && _positions.length === 0)
|
39
|
+
|
40
|
+
if (positions && source)
|
41
|
+
_locations = positions.map { |pos| Location.get_location(source, pos) }
|
42
|
+
elsif (_nodes)
|
43
|
+
_locations =
|
44
|
+
_nodes.reduce([]) do |list, node|
|
45
|
+
if (node.loc)
|
46
|
+
list.push(
|
47
|
+
Location.get_location(node.loc.source, node.loc.start_pos)
|
48
|
+
)
|
49
|
+
end
|
50
|
+
return list
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
self.source = _source
|
55
|
+
self.positions = _positions
|
56
|
+
self.locations = _locations
|
57
|
+
self.nodes = _nodes
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.print_error(error)
|
61
|
+
output = error.message
|
62
|
+
|
63
|
+
if (error.nodes)
|
64
|
+
error.nodes.each do |node|
|
65
|
+
output += "\n\n" + printLocation(node.loc) if (node.loc)
|
66
|
+
end
|
67
|
+
elsif (error.source && error.locations)
|
68
|
+
error.locations.each do |location|
|
69
|
+
output +=
|
70
|
+
"\n\n" + Source.print_source_location(error.source, location)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
return output
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.syntax_error(source, position, description)
|
78
|
+
return new("Syntax Error: #{description}", nil, source, [position])
|
79
|
+
end
|
80
|
+
|
81
|
+
def toJSON
|
82
|
+
return { message: self.message, locations: self.locations }
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_s
|
86
|
+
return TSJSONSyntaxError.print_error(self)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module TSJSON
|
2
|
+
class Token
|
3
|
+
attr_accessor :kind,
|
4
|
+
:start_pos,
|
5
|
+
:end_pos,
|
6
|
+
:line,
|
7
|
+
:column,
|
8
|
+
:value,
|
9
|
+
:prev,
|
10
|
+
:next
|
11
|
+
|
12
|
+
def initialize(kind, start_pos, end_pos, line, column, prev, value = nil)
|
13
|
+
self.kind = kind
|
14
|
+
self.start_pos = start_pos
|
15
|
+
self.end_pos = end_pos
|
16
|
+
self.line = line
|
17
|
+
self.column = column
|
18
|
+
self.value = value
|
19
|
+
self.prev = prev
|
20
|
+
self.next = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def toJSON
|
24
|
+
{
|
25
|
+
kind: self.kind,
|
26
|
+
start: self.start_pos,
|
27
|
+
end: self.end_pos,
|
28
|
+
line: self.line,
|
29
|
+
column: self.column,
|
30
|
+
value: self.value
|
31
|
+
}.compact
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module TSJSON
|
2
|
+
module TokenKind
|
3
|
+
SOF = '<SOF>'
|
4
|
+
EOF = '<EOF>'
|
5
|
+
AMP = '&'
|
6
|
+
PIPE = '|'
|
7
|
+
PAREN_L = '('
|
8
|
+
PAREN_R = ')'
|
9
|
+
BRACKET_L = '['
|
10
|
+
BRACKET_R = ']'
|
11
|
+
BRACE_L = '{'
|
12
|
+
BRACE_R = '}'
|
13
|
+
CHEVRON_L = '<'
|
14
|
+
CHEVRON_R = '>'
|
15
|
+
COLON = ':'
|
16
|
+
SEMICOLON = ';'
|
17
|
+
EQUALS = '='
|
18
|
+
COMMA = ','
|
19
|
+
DOT = '.'
|
20
|
+
QUESTION_MARK = '?'
|
21
|
+
NAME = 'Name'
|
22
|
+
INT = 'Int'
|
23
|
+
FLOAT = 'Float'
|
24
|
+
STRING = 'String'
|
25
|
+
BLOCK_STRING = 'BlockString'
|
26
|
+
COMMENT = 'Comment'
|
27
|
+
|
28
|
+
def self.operation_precedence(kind)
|
29
|
+
case (kind)
|
30
|
+
when TokenKind::AMP
|
31
|
+
return 2
|
32
|
+
when TokenKind::PIPE
|
33
|
+
return 1
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module TSJSON
|
2
|
+
class LexerUtils
|
3
|
+
class << self
|
4
|
+
def is_punctuator_token_kind?(kind)
|
5
|
+
return(
|
6
|
+
kind === TokenKind::PIPE || kind === TokenKind::AMP ||
|
7
|
+
kind === TokenKind::COLON || kind === TokenKind::EQUALS ||
|
8
|
+
kind === TokenKind::CHEVRON_L || kind === TokenKind::CHEVRON_R ||
|
9
|
+
kind === TokenKind::PAREN_L || kind === TokenKind::PAREN_R ||
|
10
|
+
kind === TokenKind::BRACKET_L || kind === TokenKind::BRACKET_R ||
|
11
|
+
kind === TokenKind::BRACE_L || kind === TokenKind::BRACE_R
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
def is_operation_token(kind)
|
16
|
+
return kind == TokenKind::PIPE || kind == TokenKind::AMP
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_token_desc(token)
|
20
|
+
value = token.value
|
21
|
+
return(
|
22
|
+
get_token_kind_desc(token.kind) +
|
23
|
+
(value != nil ? " \"#{value}\"" : '')
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_token_kind_desc(kind)
|
28
|
+
return is_punctuator_token_kind?(kind) ? "\"#{kind}\"" : kind
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,437 @@
|
|
1
|
+
require_relative '../lexer/lexer.rb'
|
2
|
+
require_relative '../source.rb'
|
3
|
+
require_relative '../ast/kind.rb'
|
4
|
+
require_relative '../lexer/token_kind.rb'
|
5
|
+
|
6
|
+
module TSJSON
|
7
|
+
class Parser
|
8
|
+
def initialize(source)
|
9
|
+
source = Source.new(source) unless source.is_a?(Source)
|
10
|
+
@lexer = Lexer.new(source)
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse_document
|
14
|
+
return(
|
15
|
+
{
|
16
|
+
kind: AST::Kind::Document,
|
17
|
+
definitions:
|
18
|
+
parse_optional_many(
|
19
|
+
TokenKind::SOF,
|
20
|
+
:parse_definition,
|
21
|
+
TokenKind::EOF
|
22
|
+
)
|
23
|
+
}
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse_definition
|
28
|
+
if (peek(TokenKind::NAME))
|
29
|
+
case current_token.value
|
30
|
+
when 'type'
|
31
|
+
return parse_type_alias_definition
|
32
|
+
when 'enum'
|
33
|
+
return parse_enum_definition
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
raise unexpected
|
38
|
+
end
|
39
|
+
|
40
|
+
def parse_type_alias_definition
|
41
|
+
start = expect_token(TokenKind::NAME)
|
42
|
+
|
43
|
+
name = parse_name
|
44
|
+
parameters =
|
45
|
+
parse_optional_many(
|
46
|
+
TokenKind::CHEVRON_L,
|
47
|
+
:parse_type_parameter,
|
48
|
+
TokenKind::CHEVRON_R,
|
49
|
+
TokenKind::COMMA
|
50
|
+
)
|
51
|
+
|
52
|
+
expect_token(TokenKind::EQUALS)
|
53
|
+
definition = parse_operation
|
54
|
+
|
55
|
+
expect_optional_token(TokenKind::SEMICOLON)
|
56
|
+
|
57
|
+
return(
|
58
|
+
{
|
59
|
+
kind: AST::Kind::TypeAlias,
|
60
|
+
name: name,
|
61
|
+
parameters: parameters,
|
62
|
+
definition: definition,
|
63
|
+
loc: loc(start)
|
64
|
+
}
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
def parse_type_parameter
|
69
|
+
start = current_token
|
70
|
+
name = parse_name
|
71
|
+
|
72
|
+
return { kind: AST::Kind::TypeParameter, name: name, loc: loc(start) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def parse_name
|
76
|
+
token = expect_token(TokenKind::NAME)
|
77
|
+
return { kind: AST::Kind::Name, value: token.value, loc: loc(token) }
|
78
|
+
end
|
79
|
+
|
80
|
+
def parse_type_definition()
|
81
|
+
start = current_token
|
82
|
+
type = nil
|
83
|
+
if (peek(TokenKind::NAME))
|
84
|
+
type = parse_type_reference
|
85
|
+
elsif (peek(TokenKind::BRACE_L))
|
86
|
+
type = parse_type_literal
|
87
|
+
elsif (peek(TokenKind::PAREN_L))
|
88
|
+
type = parse_parenthesized_type
|
89
|
+
elsif (peek(TokenKind::BRACKET_L))
|
90
|
+
type = parse_tuple
|
91
|
+
elsif (peek(TokenKind::STRING))
|
92
|
+
type = parse_string_literal
|
93
|
+
elsif (peek(TokenKind::INT))
|
94
|
+
type = parse_int
|
95
|
+
elsif (peek(TokenKind::FLOAT))
|
96
|
+
type = parse_float
|
97
|
+
end
|
98
|
+
|
99
|
+
throw unexpected if (!type)
|
100
|
+
|
101
|
+
loop do
|
102
|
+
unless [TokenKind::BRACKET_L, TokenKind::DOT].include?(
|
103
|
+
current_token.kind
|
104
|
+
)
|
105
|
+
break
|
106
|
+
end
|
107
|
+
|
108
|
+
if (expect_optional_token(TokenKind::BRACKET_L))
|
109
|
+
if (expect_optional_token(TokenKind::BRACKET_R))
|
110
|
+
type = { kind: AST::Kind::ArrayType, type: type, loc: loc(start) }
|
111
|
+
else
|
112
|
+
index = parse_string_literal
|
113
|
+
expect_token(TokenKind::BRACKET_R)
|
114
|
+
type = {
|
115
|
+
kind: AST::Kind::IndexAccess,
|
116
|
+
target: type,
|
117
|
+
index: index,
|
118
|
+
loc: loc(start)
|
119
|
+
}
|
120
|
+
end
|
121
|
+
elsif (expect_optional_token(TokenKind::DOT))
|
122
|
+
property = parse_name
|
123
|
+
type = {
|
124
|
+
kind: AST::Kind::PropertyAccess,
|
125
|
+
target: type,
|
126
|
+
property: property,
|
127
|
+
loc: loc(start)
|
128
|
+
}
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
if (expect_optional_token(TokenKind::BRACKET_L))
|
133
|
+
expect_token(TokenKind::BRACKET_R)
|
134
|
+
type = { kind: AST::Kind::ArrayType, type: type, loc: loc(start) }
|
135
|
+
end
|
136
|
+
|
137
|
+
return type
|
138
|
+
end
|
139
|
+
|
140
|
+
def parse_operation
|
141
|
+
expect_optional_token(TokenKind::PIPE)
|
142
|
+
nodes = [parse_type_definition]
|
143
|
+
operations = []
|
144
|
+
|
145
|
+
execute_operation =
|
146
|
+
lambda do
|
147
|
+
operation = operations.shift
|
148
|
+
right = nodes.pop
|
149
|
+
left = nodes.pop
|
150
|
+
|
151
|
+
new_operation = nil
|
152
|
+
if (operation == TokenKind::AMP)
|
153
|
+
if (left[:kind] == AST::Kind::IntersectionType)
|
154
|
+
new_operation = {
|
155
|
+
kind: AST::Kind::IntersectionType,
|
156
|
+
types: left[:types].concat([right])
|
157
|
+
}
|
158
|
+
else
|
159
|
+
new_operation = {
|
160
|
+
kind: AST::Kind::IntersectionType,
|
161
|
+
types: [left, right]
|
162
|
+
}
|
163
|
+
end
|
164
|
+
elsif (operation == TokenKind::PIPE)
|
165
|
+
if (left[:kind] == AST::Kind::UnionType)
|
166
|
+
new_operation = {
|
167
|
+
kind: AST::Kind::UnionType,
|
168
|
+
types: left[:types].concat([right])
|
169
|
+
}
|
170
|
+
else
|
171
|
+
new_operation = {
|
172
|
+
kind: AST::Kind::UnionType,
|
173
|
+
types: [left, right]
|
174
|
+
}
|
175
|
+
end
|
176
|
+
end
|
177
|
+
nodes.push(new_operation)
|
178
|
+
end
|
179
|
+
|
180
|
+
operation_kind = nil
|
181
|
+
loop do
|
182
|
+
unless (
|
183
|
+
LexerUtils.is_operation_token(
|
184
|
+
(operation_kind = current_token.kind)
|
185
|
+
)
|
186
|
+
)
|
187
|
+
break
|
188
|
+
end
|
189
|
+
loop do
|
190
|
+
unless (
|
191
|
+
operations.length > 0 &&
|
192
|
+
TokenKind.operation_precedence(operations[0]) >=
|
193
|
+
TokenKind.operation_precedence(operation_kind)
|
194
|
+
)
|
195
|
+
break
|
196
|
+
end
|
197
|
+
execute_operation.call
|
198
|
+
end
|
199
|
+
|
200
|
+
operations.unshift(operation_kind)
|
201
|
+
@lexer.advance
|
202
|
+
nodes.push(parse_type_definition)
|
203
|
+
end
|
204
|
+
|
205
|
+
loop do
|
206
|
+
break unless (operations.length > 0)
|
207
|
+
execute_operation.call
|
208
|
+
end
|
209
|
+
|
210
|
+
return nodes[0]
|
211
|
+
end
|
212
|
+
|
213
|
+
def parse_string_literal()
|
214
|
+
token = expect_token(TokenKind::STRING)
|
215
|
+
return(
|
216
|
+
{ kind: AST::Kind::StringLiteral, value: token.value, loc: loc(token) }
|
217
|
+
)
|
218
|
+
end
|
219
|
+
|
220
|
+
def parse_int()
|
221
|
+
token = expect_token(TokenKind::INT)
|
222
|
+
return { kind: AST::Kind::Int, value: token.value.to_i, loc: loc(token) }
|
223
|
+
end
|
224
|
+
|
225
|
+
def parse_float()
|
226
|
+
token = expect_token(TokenKind::FLOAT)
|
227
|
+
return(
|
228
|
+
{ kind: AST::Kind::Float, value: token.value.to_f, loc: loc(token) }
|
229
|
+
)
|
230
|
+
end
|
231
|
+
|
232
|
+
def parse_type_reference
|
233
|
+
start = current_token
|
234
|
+
name = parse_name
|
235
|
+
args =
|
236
|
+
parse_optional_many(
|
237
|
+
TokenKind::CHEVRON_L,
|
238
|
+
:parse_operation,
|
239
|
+
TokenKind::CHEVRON_R,
|
240
|
+
TokenKind::COMMA
|
241
|
+
)
|
242
|
+
|
243
|
+
return(
|
244
|
+
{
|
245
|
+
kind: AST::Kind::TypeReference,
|
246
|
+
name: name,
|
247
|
+
args: args,
|
248
|
+
loc: loc(start)
|
249
|
+
}
|
250
|
+
)
|
251
|
+
end
|
252
|
+
|
253
|
+
def parse_tuple
|
254
|
+
start = current_token
|
255
|
+
types =
|
256
|
+
parse_many(
|
257
|
+
TokenKind::BRACKET_L,
|
258
|
+
:parse_type_definition,
|
259
|
+
TokenKind::BRACKET_R,
|
260
|
+
TokenKind::COMMA
|
261
|
+
)
|
262
|
+
return { kind: AST::Kind::Tuple, types: types, loc: loc(start) }
|
263
|
+
end
|
264
|
+
|
265
|
+
def parse_type_literal
|
266
|
+
start = current_token
|
267
|
+
properties =
|
268
|
+
parse_optional_many(
|
269
|
+
TokenKind::BRACE_L,
|
270
|
+
:parse_property_signature,
|
271
|
+
TokenKind::BRACE_R
|
272
|
+
)
|
273
|
+
return(
|
274
|
+
{
|
275
|
+
kind: AST::Kind::TypeLiteral,
|
276
|
+
properties: properties,
|
277
|
+
loc: loc(start)
|
278
|
+
}
|
279
|
+
)
|
280
|
+
end
|
281
|
+
|
282
|
+
def parse_property_signature()
|
283
|
+
start = current_token
|
284
|
+
name = parse_name
|
285
|
+
optional = expect_optional_token(TokenKind::QUESTION_MARK).nil?.!
|
286
|
+
expect_token(TokenKind::COLON)
|
287
|
+
type = parse_operation
|
288
|
+
expect_token(TokenKind::SEMICOLON)
|
289
|
+
|
290
|
+
return(
|
291
|
+
{
|
292
|
+
kind: AST::Kind::PropertySignature,
|
293
|
+
name: name,
|
294
|
+
type: type,
|
295
|
+
optional: optional,
|
296
|
+
loc: loc(start)
|
297
|
+
}
|
298
|
+
)
|
299
|
+
end
|
300
|
+
|
301
|
+
def parse_parenthesized_type()
|
302
|
+
start = expect_token(TokenKind::PAREN_L)
|
303
|
+
type = parse_operation
|
304
|
+
expect_token(TokenKind::PAREN_R)
|
305
|
+
return { kind: AST::Kind::ParenthesizedType, type: type, loc: loc(start) }
|
306
|
+
end
|
307
|
+
|
308
|
+
def parse_enum_definition
|
309
|
+
start = expect_token(TokenKind::NAME)
|
310
|
+
name = parse_name
|
311
|
+
members =
|
312
|
+
parse_many(
|
313
|
+
TokenKind::BRACE_L,
|
314
|
+
:parse_enum_member,
|
315
|
+
TokenKind::BRACE_R,
|
316
|
+
TokenKind::COMMA
|
317
|
+
)
|
318
|
+
expect_optional_token(TokenKind::SEMICOLON)
|
319
|
+
|
320
|
+
return(
|
321
|
+
{ kind: AST::Kind::Enum, name: name, members: members, loc: loc(start) }
|
322
|
+
)
|
323
|
+
end
|
324
|
+
|
325
|
+
def parse_enum_member
|
326
|
+
start = current_token
|
327
|
+
name = parse_name
|
328
|
+
|
329
|
+
value = nil
|
330
|
+
value = parse_string_literal if (expect_optional_token(TokenKind::EQUALS))
|
331
|
+
|
332
|
+
return(
|
333
|
+
{
|
334
|
+
kind: AST::Kind::EnumMember,
|
335
|
+
name: name,
|
336
|
+
value: value,
|
337
|
+
loc: loc(start)
|
338
|
+
}
|
339
|
+
)
|
340
|
+
end
|
341
|
+
|
342
|
+
#######
|
343
|
+
|
344
|
+
# Returns current token from lexer
|
345
|
+
def current_token
|
346
|
+
@lexer.token
|
347
|
+
end
|
348
|
+
|
349
|
+
# Helper function for creating an error when an unexpected lexed token is encountered.
|
350
|
+
def unexpected(at_token = nil)
|
351
|
+
token = at_token || current_token
|
352
|
+
return(
|
353
|
+
TSJSONSyntaxError.syntax_error(
|
354
|
+
@lexer.source,
|
355
|
+
token.start_pos,
|
356
|
+
"Unexpected #{LexerUtils.get_token_desc(token)}."
|
357
|
+
)
|
358
|
+
)
|
359
|
+
end
|
360
|
+
|
361
|
+
# Returns a location object, used to identify the place in the source that created a given parsed object.
|
362
|
+
def loc(start_token)
|
363
|
+
end_token = @lexer.last_token
|
364
|
+
return {} #Location.new(start_token, end_token, @lexer.source)
|
365
|
+
end
|
366
|
+
|
367
|
+
# Determines if the next token is of a given kind
|
368
|
+
def peek(kind)
|
369
|
+
current_token.kind == kind
|
370
|
+
end
|
371
|
+
|
372
|
+
# Returns a non-empty list of parse nodes, determined by the parseFn.
|
373
|
+
# This list begins with a lex token of openKind and ends with a lex token of closeKind.
|
374
|
+
# Advances the parser to the next lex token after the closing token.
|
375
|
+
def parse_many(open_kind, parse_fn_symbol, close_kind, delimeter_kind = nil)
|
376
|
+
expect_token(open_kind)
|
377
|
+
nodes = []
|
378
|
+
loop do
|
379
|
+
nodes.push(self.send(parse_fn_symbol))
|
380
|
+
expect_token(delimeter_kind) if (delimeter_kind && !peek(close_kind))
|
381
|
+
break if expect_optional_token(close_kind)
|
382
|
+
end
|
383
|
+
return nodes
|
384
|
+
end
|
385
|
+
|
386
|
+
# Returns a list of parse nodes, determined by the parseFn.
|
387
|
+
# It can be empty only if open token is missing otherwise it will always return non-empty list
|
388
|
+
# that begins with a lex token of openKind and ends with a lex token of closeKind.
|
389
|
+
# Advances the parser to the next lex token after the closing token.
|
390
|
+
def parse_optional_many(
|
391
|
+
open_kind,
|
392
|
+
parse_fn_symbol,
|
393
|
+
close_kind,
|
394
|
+
delimeter_kind = nil
|
395
|
+
)
|
396
|
+
if (expect_optional_token(open_kind))
|
397
|
+
nodes = []
|
398
|
+
loop do
|
399
|
+
break if expect_optional_token(close_kind)
|
400
|
+
nodes.push(self.send(parse_fn_symbol))
|
401
|
+
expect_token(delimeter_kind) if (delimeter_kind && !peek(close_kind))
|
402
|
+
end
|
403
|
+
return nodes
|
404
|
+
end
|
405
|
+
return []
|
406
|
+
end
|
407
|
+
|
408
|
+
# If the next token is of the given kind, return that token after advancing the lexer.
|
409
|
+
# Otherwise, do not change the parser state and throw an error.
|
410
|
+
def expect_token(kind)
|
411
|
+
token = current_token
|
412
|
+
if (token.kind == kind)
|
413
|
+
@lexer.advance
|
414
|
+
return token
|
415
|
+
end
|
416
|
+
|
417
|
+
raise TSJSONSyntaxError.syntax_error(
|
418
|
+
@lexer.source,
|
419
|
+
token.start_pos,
|
420
|
+
"Expected #{LexerUtils.get_token_kind_desc(kind)}, found #{
|
421
|
+
LexerUtils.get_token_desc(token)
|
422
|
+
}."
|
423
|
+
)
|
424
|
+
end
|
425
|
+
|
426
|
+
# If the next token is of the given kind, return that token after advancing the lexer.
|
427
|
+
# Otherwise, do not change the parser state and return undefined.
|
428
|
+
def expect_optional_token(kind)
|
429
|
+
token = current_token
|
430
|
+
if token.kind === kind
|
431
|
+
@lexer.advance
|
432
|
+
return token
|
433
|
+
end
|
434
|
+
return nil
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|