ruby_json_parser 0.1.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 (47) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +35 -0
  3. data/CHANGELOG.md +5 -0
  4. data/LICENSE +21 -0
  5. data/README.md +143 -0
  6. data/Rakefile +12 -0
  7. data/lib/ruby_json_parser/ast.rb +312 -0
  8. data/lib/ruby_json_parser/evaluator.rb +81 -0
  9. data/lib/ruby_json_parser/lexer.rb +358 -0
  10. data/lib/ruby_json_parser/parser.rb +205 -0
  11. data/lib/ruby_json_parser/result.rb +43 -0
  12. data/lib/ruby_json_parser/token.rb +171 -0
  13. data/lib/ruby_json_parser/version.rb +6 -0
  14. data/lib/ruby_json_parser.rb +77 -0
  15. data/sorbet/config +4 -0
  16. data/sorbet/rbi/annotations/.gitattributes +1 -0
  17. data/sorbet/rbi/annotations/minitest.rbi +119 -0
  18. data/sorbet/rbi/annotations/rainbow.rbi +269 -0
  19. data/sorbet/rbi/gems/.gitattributes +1 -0
  20. data/sorbet/rbi/gems/ast@2.4.2.rbi +585 -0
  21. data/sorbet/rbi/gems/erubi@1.13.0.rbi +150 -0
  22. data/sorbet/rbi/gems/json@2.7.2.rbi +1562 -0
  23. data/sorbet/rbi/gems/language_server-protocol@3.17.0.3.rbi +14238 -0
  24. data/sorbet/rbi/gems/minitest@5.24.1.rbi +1563 -0
  25. data/sorbet/rbi/gems/netrc@0.11.0.rbi +159 -0
  26. data/sorbet/rbi/gems/parallel@1.25.1.rbi +287 -0
  27. data/sorbet/rbi/gems/parser@3.3.4.0.rbi +5519 -0
  28. data/sorbet/rbi/gems/prism@0.30.0.rbi +39212 -0
  29. data/sorbet/rbi/gems/racc@1.8.0.rbi +162 -0
  30. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +403 -0
  31. data/sorbet/rbi/gems/rake@13.2.1.rbi +3028 -0
  32. data/sorbet/rbi/gems/rbi@0.1.13.rbi +3078 -0
  33. data/sorbet/rbi/gems/regexp_parser@2.9.2.rbi +3772 -0
  34. data/sorbet/rbi/gems/rexml@3.3.1.rbi +4813 -0
  35. data/sorbet/rbi/gems/rubocop-ast@1.31.3.rbi +7015 -0
  36. data/sorbet/rbi/gems/rubocop@1.65.0.rbi +58191 -0
  37. data/sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi +1318 -0
  38. data/sorbet/rbi/gems/spoom@1.3.3.rbi +4926 -0
  39. data/sorbet/rbi/gems/strscan@3.1.0.rbi +9 -0
  40. data/sorbet/rbi/gems/tapioca@0.15.1.rbi +3566 -0
  41. data/sorbet/rbi/gems/thor@1.3.1.rbi +4352 -0
  42. data/sorbet/rbi/gems/unicode-display_width@2.5.0.rbi +66 -0
  43. data/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +435 -0
  44. data/sorbet/rbi/gems/yard@0.9.36.rbi +18221 -0
  45. data/sorbet/tapioca/config.yml +13 -0
  46. data/sorbet/tapioca/require.rb +4 -0
  47. metadata +105 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 642bf9c4ccd18cfadf48b5ebfa77838c2b27f85e22f3ad87b800709b94697344
4
+ data.tar.gz: e56908db728c244bd31105f6feb8093d5f5f2b04f56d4d0d04d43c140fdc5077
5
+ SHA512:
6
+ metadata.gz: 3488592ef9a18ab9e2dbc155c7e9f7c00fab7e523b41de5b9a0624c9462285a0d1b5239f2fc38b7d1723e2945ec4735731ffc52dae554d5146dcc09f54b76e56
7
+ data.tar.gz: 39da06576d98f82b3d4f023b10e815c4f2f79684b13264414ba62f762d184327dcc0eca7327e8523fa379a957dc275c22b1186ad87134955e4e3b9ce8a31e41c
data/.rubocop.yml ADDED
@@ -0,0 +1,35 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.1
3
+
4
+ inherit_gem:
5
+ rubocop-espago: rubocop.yml
6
+
7
+ Lint/BooleanSymbol:
8
+ Enabled: false
9
+
10
+ Lint/MissingSuper:
11
+ Enabled: false
12
+
13
+ Style/NumericPredicate:
14
+ Enabled: false
15
+
16
+ Metrics/MethodLength:
17
+ Enabled: false
18
+
19
+ Metrics/AbcSize:
20
+ Enabled: false
21
+
22
+ Style/YodaExpression:
23
+ Enabled: false
24
+
25
+ Metrics/CyclomaticComplexity:
26
+ Enabled: false
27
+
28
+ Metrics/BlockLength:
29
+ Enabled: false
30
+
31
+ Metrics/ClassLength:
32
+ Enabled: false
33
+
34
+ Style/RaiseArgs:
35
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2024-11-26
4
+
5
+ - Initial release
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Mateusz Drewniak
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # RubyJsonParser
2
+
3
+ This library implements a JSON lexer, parser and evaluator in pure Ruby 💎.
4
+
5
+ It has been built for educational purposes, to serve as a simple example of what makes parsers tick.
6
+
7
+ ## Installation
8
+
9
+ Install the gem and add to the application's Gemfile by executing:
10
+
11
+ $ bundle add ruby_json_parser
12
+
13
+ If bundler is not being used to manage dependencies, install the gem by executing:
14
+
15
+ $ gem install ruby_json_parser
16
+
17
+ ## Usage
18
+
19
+ ### Lexer
20
+
21
+ This library implements a streaming JSON lexer.
22
+ You can use it by creating an instance of `RubyJsonParser::Lexer` passing in a string
23
+ with JSON source.
24
+
25
+ You can call the `next` method to receive the next token.
26
+ Once the lexing is complete a token of type `:end_of_file` gets returned.
27
+
28
+ ```rb
29
+ require 'ruby_json_parser'
30
+
31
+ lexer = RubyJsonParser::Lexer.new('{ "some": ["json", 2e-29, "text"] }')
32
+ lexer.next #=> Token(:lbrace)
33
+ lexer.next #=> Token(:string, "some")
34
+ lexer.next #=> Token(:colon)
35
+ lexer.next #=> Token(:lbracket)
36
+ lexer.next #=> Token(:string, "json")
37
+ # ...
38
+ lexer.next #=> Token(:end_of_file)
39
+ ```
40
+
41
+ There is a simplified API that lets you generate an array of all tokens.
42
+
43
+ ```rb
44
+ require 'ruby_json_parser'
45
+
46
+ RubyJsonParser.lex('{ "some": ["json", 2e-29, "text"] }')
47
+ #=> [Token(:lbrace), Token(:string, "some"), Token(:colon), Token(:lbracket), Token(:string, "json"), Token(:comma), Token(:number, "2e-29"), Token(:comma), Token(:string, "text"), Token(:rbracket), Token(:rbrace)]
48
+ ```
49
+
50
+ ### Parser
51
+
52
+ This library implements a JSON parser.
53
+ You can use it by calling `RubyJsonParser.parse` passing in a string
54
+ with JSON source.
55
+
56
+ It returns `RubyJsonParser::Result` which contains the produced AST (Abstract Syntax Tree) and the list of encountered errors.
57
+
58
+ ```rb
59
+ require 'ruby_json_parser'
60
+
61
+ RubyJsonParser.parse('{ "some": ["json", 2e-29, "text"] }')
62
+ #=> <RubyJsonParser::Result>
63
+ # AST:
64
+ # (object
65
+ # (pair
66
+ # "some"
67
+ # (array
68
+ # "json"
69
+ # 2e-29
70
+ # "text")))
71
+
72
+ result = RubyJsonParser.parse('[1, 2')
73
+ #=> <RubyJsonParser::Result>
74
+ # !Errors!
75
+ # - unexpected `END_OF_FILE`, expected `]`
76
+ #
77
+ # AST:
78
+ # (array
79
+ # 1
80
+ # 2)
81
+
82
+ result.ast # get the AST
83
+ result.err? # check if there are any errors
84
+ result.errors # get the list of errors
85
+ ```
86
+
87
+ All AST nodes are implemented as classes under the `RubyJsonParser::AST` module.
88
+ AST nodes have an `inspect` method that presents their structure in the [S-expression](https://en.wikipedia.org/wiki/S-expression) format.
89
+ You can also use `#to_s` to convert them to a JSON-like human readable format.
90
+
91
+ ```rb
92
+ result = RubyJsonParser.parse('{"some" :[ "json",2e-29 , "text" ]}')
93
+ ast = result.ast
94
+
95
+ puts ast.inspect # S-expression format
96
+ # (object
97
+ # (pair
98
+ # "some"
99
+ # (array
100
+ # "json"
101
+ # 2e-29
102
+ # "text")))
103
+
104
+ puts ast.to_s # JSON-like format
105
+ # {"some": ["json", 2e-29, "text"]}
106
+
107
+ ast.class #=> RubyJsonParser::AST::ObjectLiteralNode
108
+
109
+ ast.pairs[0].key #=> RubyJsonParser::AST::StringLiteralNode("some")
110
+ ast.pairs[0].value.elements[2] #=> RubyJsonParser::AST::NumberLiteralNode("2e-29")
111
+ ```
112
+
113
+ ### Evaluator
114
+
115
+ This library implements a JSON evaluator.
116
+ It interprets a JSON source string as builtin Ruby data structures.
117
+
118
+ You can use it by calling `RubyJsonParser.eval` passing in a string
119
+ with JSON source.
120
+
121
+ It throws `RubyJsonParser::SyntaxError` when the string cannot be parsed.
122
+
123
+ ```rb
124
+ RubyJsonParser.eval('{ "some": ["json", 2e-29, "text"] }')
125
+ #=> {"some"=>["json", 2.0e-29, "text"]}
126
+
127
+ RubyJsonParser.eval('{ "some" }')
128
+ #! RubyJsonParser::SyntaxError: missing key in object literal for value: `"some"`
129
+ ```
130
+
131
+ ## Development
132
+
133
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
134
+
135
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
136
+
137
+ ## Contributing
138
+
139
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Verseth/ruby_json_parser.
140
+
141
+ ## License
142
+
143
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'minitest/test_task'
5
+
6
+ Minitest::TestTask.create
7
+
8
+ require 'rubocop/rake_task'
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[test rubocop]
@@ -0,0 +1,312 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ class RubyJsonParser
5
+ # Contains the definitions of all AST (Abstract Syntax Tree) nodes.
6
+ # AST is the data structure that is returned by the parser.
7
+ module AST
8
+ # A string that represents a single level of indentation
9
+ # in S-expressions
10
+ INDENT_UNIT = ' '
11
+
12
+ # Abstract class representing an AST node.
13
+ class Node
14
+ extend T::Sig
15
+ extend T::Helpers
16
+
17
+ abstract!
18
+
19
+ # Get the JSON-like representation of the AST
20
+ sig { abstract.returns(String) }
21
+ def to_s; end
22
+
23
+ # Inspect the AST in the S-expression format
24
+ sig { abstract.params(indent: Integer).returns(String) }
25
+ def inspect(indent = 0); end
26
+ end
27
+
28
+ # Represents an invalid node
29
+ class InvalidNode < Node
30
+ sig { returns(Token) }
31
+ attr_reader :token
32
+
33
+ sig { params(token: Token).void }
34
+ def initialize(token)
35
+ @token = token
36
+ end
37
+
38
+ sig { params(other: Object).returns(T::Boolean) }
39
+ def ==(other)
40
+ return false unless other.is_a?(InvalidNode)
41
+
42
+ token == other.token
43
+ end
44
+
45
+ sig { override.returns(String) }
46
+ def to_s
47
+ "<invalid: `#{token}`>"
48
+ end
49
+
50
+ sig { override.params(indent: Integer).returns(String) }
51
+ def inspect(indent = 0)
52
+ "#{INDENT_UNIT * indent}(invalid `#{token}`)"
53
+ end
54
+ end
55
+
56
+ # Represents a false literal eg. `false`
57
+ class FalseLiteralNode < Node
58
+ sig { params(other: Object).returns(T::Boolean) }
59
+ def ==(other)
60
+ other.is_a?(FalseLiteralNode)
61
+ end
62
+
63
+ sig { override.returns(String) }
64
+ def to_s
65
+ 'false'
66
+ end
67
+
68
+ sig { override.params(indent: Integer).returns(String) }
69
+ def inspect(indent = 0)
70
+ "#{INDENT_UNIT * indent}false"
71
+ end
72
+ end
73
+
74
+ # Represents a true literal eg. `true`
75
+ class TrueLiteralNode < Node
76
+ sig { params(other: Object).returns(T::Boolean) }
77
+ def ==(other)
78
+ other.is_a?(TrueLiteralNode)
79
+ end
80
+
81
+ sig { override.returns(String) }
82
+ def to_s
83
+ 'true'
84
+ end
85
+
86
+ sig { override.params(indent: Integer).returns(String) }
87
+ def inspect(indent = 0)
88
+ "#{INDENT_UNIT * indent}true"
89
+ end
90
+ end
91
+
92
+ # Represents a true literal eg. `null`
93
+ class NullLiteralNode < Node
94
+ sig { params(other: Object).returns(T::Boolean) }
95
+ def ==(other)
96
+ other.is_a?(NullLiteralNode)
97
+ end
98
+
99
+ sig { override.returns(String) }
100
+ def to_s
101
+ 'null'
102
+ end
103
+
104
+ sig { override.params(indent: Integer).returns(String) }
105
+ def inspect(indent = 0)
106
+ "#{INDENT_UNIT * indent}null"
107
+ end
108
+ end
109
+
110
+ # Represents a number literal eg. `123.5`
111
+ class NumberLiteralNode < Node
112
+ sig { returns(String) }
113
+ attr_reader :value
114
+
115
+ sig { params(value: String).void }
116
+ def initialize(value)
117
+ @value = value
118
+ end
119
+
120
+ sig { params(other: Object).returns(T::Boolean) }
121
+ def ==(other)
122
+ return false unless other.is_a?(NumberLiteralNode)
123
+
124
+ value == other.value
125
+ end
126
+
127
+ sig { override.returns(String) }
128
+ def to_s
129
+ value
130
+ end
131
+
132
+ sig { override.params(indent: Integer).returns(String) }
133
+ def inspect(indent = 0)
134
+ "#{INDENT_UNIT * indent}#{value}"
135
+ end
136
+ end
137
+
138
+ # Represents a string literal eg. `"foo"`
139
+ class StringLiteralNode < Node
140
+ sig { returns(String) }
141
+ attr_reader :value
142
+
143
+ sig { params(value: String).void }
144
+ def initialize(value)
145
+ @value = value
146
+ end
147
+
148
+ sig { params(other: Object).returns(T::Boolean) }
149
+ def ==(other)
150
+ return false unless other.is_a?(StringLiteralNode)
151
+
152
+ value == other.value
153
+ end
154
+
155
+ sig { override.returns(String) }
156
+ def to_s
157
+ value.inspect
158
+ end
159
+
160
+ sig { override.params(indent: Integer).returns(String) }
161
+ def inspect(indent = 0)
162
+ "#{INDENT_UNIT * indent}#{value.inspect}"
163
+ end
164
+ end
165
+
166
+ # Represents an object literal eg. `{ "foo": 123 }`
167
+ class ObjectLiteralNode < Node
168
+ sig { returns(T::Array[KeyValuePairNode]) }
169
+ attr_reader :pairs
170
+
171
+ sig { params(pairs: T::Array[KeyValuePairNode]).void }
172
+ def initialize(pairs)
173
+ @pairs = pairs
174
+ end
175
+
176
+ sig { params(other: Object).returns(T::Boolean) }
177
+ def ==(other)
178
+ return false unless other.is_a?(ObjectLiteralNode)
179
+
180
+ pairs == other.pairs
181
+ end
182
+
183
+ sig { override.returns(String) }
184
+ def to_s
185
+ buff = String.new
186
+ buff << '{'
187
+
188
+ @pairs.each.with_index do |pair, i|
189
+ buff << ', ' if i > 0
190
+ buff << pair.to_s
191
+ end
192
+
193
+ buff << '}'
194
+ buff
195
+ end
196
+
197
+ sig { override.params(indent: Integer).returns(String) }
198
+ def inspect(indent = 0)
199
+ buff = String.new
200
+
201
+ buff << "#{INDENT_UNIT * indent}(object"
202
+ @pairs.each do |pair|
203
+ buff << "\n"
204
+ buff << pair.inspect(indent + 1)
205
+ end
206
+ buff << ')'
207
+ buff
208
+ end
209
+ end
210
+
211
+ # Represents a key-value pair eg. `"foo": 123`
212
+ class KeyValuePairNode < Node
213
+ sig { returns(T.nilable(Node)) }
214
+ attr_reader :key
215
+
216
+ sig { returns(T.nilable(Node)) }
217
+ attr_reader :value
218
+
219
+ sig { params(key: T.nilable(Node), value: T.nilable(Node)).void }
220
+ def initialize(key, value)
221
+ @key = key
222
+ @value = value
223
+ end
224
+
225
+ sig { params(other: Object).returns(T::Boolean) }
226
+ def ==(other)
227
+ return false unless other.is_a?(KeyValuePairNode)
228
+
229
+ key == other.key && value == other.value
230
+ end
231
+
232
+ sig { override.returns(String) }
233
+ def to_s
234
+ return value.to_s unless key
235
+
236
+ "#{key}: #{value}"
237
+ end
238
+
239
+ sig { override.params(indent: Integer).returns(String) }
240
+ def inspect(indent = 0)
241
+ buff = String.new
242
+ buff << "#{INDENT_UNIT * indent}(pair"
243
+
244
+ k = key
245
+ buff << "\n"
246
+ buff <<
247
+ if k
248
+ k.inspect(indent + 1)
249
+ else
250
+ "#{INDENT_UNIT * (indent + 1)}<nil>"
251
+ end
252
+
253
+ v = value
254
+ buff << "\n"
255
+ buff <<
256
+ if v
257
+ v.inspect(indent + 1)
258
+ else
259
+ "#{INDENT_UNIT * (indent + 1)}<nil>"
260
+ end
261
+
262
+ buff << ')'
263
+ buff
264
+ end
265
+ end
266
+
267
+ # Represents an object literal eg. `[1, "foo"]`
268
+ class ArrayLiteralNode < Node
269
+ sig { returns(T::Array[Node]) }
270
+ attr_reader :elements
271
+
272
+ sig { params(elements: T::Array[Node]).void }
273
+ def initialize(elements)
274
+ @elements = elements
275
+ end
276
+
277
+ sig { params(other: Object).returns(T::Boolean) }
278
+ def ==(other)
279
+ return false unless other.is_a?(ArrayLiteralNode)
280
+
281
+ elements == other.elements
282
+ end
283
+
284
+ sig { override.returns(String) }
285
+ def to_s
286
+ buff = String.new
287
+ buff << '['
288
+
289
+ @elements.each.with_index do |element, i|
290
+ buff << ', ' if i > 0
291
+ buff << element.to_s
292
+ end
293
+
294
+ buff << ']'
295
+ buff
296
+ end
297
+
298
+ sig { override.params(indent: Integer).returns(String) }
299
+ def inspect(indent = 0)
300
+ buff = String.new
301
+
302
+ buff << "#{INDENT_UNIT * indent}(array"
303
+ @elements.each do |element|
304
+ buff << "\n"
305
+ buff << element.inspect(indent + 1)
306
+ end
307
+ buff << ')'
308
+ buff
309
+ end
310
+ end
311
+ end
312
+ end
@@ -0,0 +1,81 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'token'
5
+
6
+ class RubyJsonParser
7
+ # An evaluator for JSON.
8
+ # Creates Ruby structures based on an JSON AST.
9
+ module Evaluator
10
+ # Eval error
11
+ class Error < StandardError; end
12
+
13
+ class << self
14
+ extend T::Sig
15
+
16
+ sig { params(source: String).returns(Object) }
17
+ def eval(source)
18
+ result = RubyJsonParser.parse(source)
19
+ raise SyntaxError.new(result.errors) if result.err?
20
+
21
+ eval_node(result.ast)
22
+ end
23
+
24
+ sig { params(node: AST::Node).returns(Object) }
25
+ def eval_node(node)
26
+ case node
27
+ when AST::NullLiteralNode
28
+ nil
29
+ when AST::FalseLiteralNode
30
+ false
31
+ when AST::TrueLiteralNode
32
+ true
33
+ when AST::StringLiteralNode
34
+ eval_string(node)
35
+ when AST::NumberLiteralNode
36
+ eval_number(node)
37
+ when AST::ArrayLiteralNode
38
+ eval_array(node)
39
+ when AST::ObjectLiteralNode
40
+ eval_object(node)
41
+ else
42
+ raise Error, "invalid AST node: #{node.class}"
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ sig { params(node: AST::StringLiteralNode).returns(String) }
49
+ def eval_string(node)
50
+ node.value
51
+ end
52
+
53
+ sig { params(node: AST::NumberLiteralNode).returns(T.any(Integer, Float)) }
54
+ def eval_number(node)
55
+ Integer(node.value)
56
+ rescue ArgumentError
57
+ node.value.to_f
58
+ end
59
+
60
+ sig { params(node: AST::ArrayLiteralNode).returns(T::Array[Object]) }
61
+ def eval_array(node)
62
+ node.elements.map do |element|
63
+ eval_node(element)
64
+ end
65
+ end
66
+
67
+ sig { params(node: AST::ObjectLiteralNode).returns(T::Hash[String, Object]) }
68
+ def eval_object(node)
69
+ result = {}
70
+ node.pairs.each do |pair|
71
+ key = T.cast(pair.key, AST::StringLiteralNode)
72
+ val = T.must pair.value
73
+ result[eval_string(key)] = eval_node(val)
74
+ end
75
+
76
+ result
77
+ end
78
+ end
79
+
80
+ end
81
+ end