lkml 0.1.0 → 0.2.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 +4 -4
- data/.github/workflows/ci.yml +28 -0
- data/.gitignore +57 -0
- data/.rubocop.yml +25 -0
- data/.ruby-version +1 -0
- data/CONTRIBUTING.md +56 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +76 -0
- data/LICENSE.md +1 -2
- data/README.md +81 -7
- data/Rakefile +12 -0
- data/bin/lkml +6 -0
- data/lib/lkml/cli.rb +44 -0
- data/lib/lkml/keys.rb +64 -114
- data/lib/lkml/lexer.rb +45 -68
- data/lib/lkml/parser.rb +296 -191
- data/lib/lkml/simple.rb +244 -238
- data/lib/lkml/tokens.rb +44 -129
- data/lib/lkml/tree.rb +362 -236
- data/lib/lkml/utils.rb +32 -0
- data/lib/lkml/version.rb +1 -1
- data/lib/lkml/visitors.rb +69 -64
- data/lib/lkml.rb +49 -14
- data/lkml.gemspec +35 -0
- data/script/benchmark.rb +36 -0
- data/script/download_lookml.rb +70 -0
- metadata +88 -13
data/lib/lkml/tree.rb
CHANGED
|
@@ -1,319 +1,445 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require_relative "keys"
|
|
4
4
|
|
|
5
5
|
module Lkml
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def initialize(value, line_number = nil, prefix: '', suffix: '')
|
|
10
|
-
@value = value
|
|
11
|
-
@line_number = line_number
|
|
12
|
-
@prefix = prefix
|
|
13
|
-
@suffix = suffix
|
|
14
|
-
end
|
|
6
|
+
module Tree
|
|
7
|
+
module_function
|
|
15
8
|
|
|
16
|
-
def
|
|
17
|
-
|
|
9
|
+
def items_to_str(*items)
|
|
10
|
+
items.join
|
|
18
11
|
end
|
|
19
12
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
13
|
+
module Visitor
|
|
14
|
+
def visit(_document)
|
|
15
|
+
raise NotImplementedError
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def visit_container(_node)
|
|
19
|
+
raise NotImplementedError
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def visit_block(_node)
|
|
23
|
+
raise NotImplementedError
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def visit_list(_node)
|
|
27
|
+
raise NotImplementedError
|
|
28
|
+
end
|
|
23
29
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
false
|
|
30
|
+
def visit_pair(_node)
|
|
31
|
+
raise NotImplementedError
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def visit_token(_token)
|
|
35
|
+
raise NotImplementedError
|
|
31
36
|
end
|
|
32
37
|
end
|
|
33
38
|
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
class SyntaxToken
|
|
40
|
+
attr_reader :value, :line_number, :prefix, :suffix
|
|
41
|
+
|
|
42
|
+
def initialize(value, line_number = nil, prefix = "", suffix = "", expr_suffix: nil)
|
|
43
|
+
@value = value
|
|
44
|
+
@line_number = line_number
|
|
45
|
+
@prefix = prefix
|
|
46
|
+
@suffix = suffix
|
|
47
|
+
@expr_suffix = expr_suffix unless expr_suffix.nil?
|
|
48
|
+
freeze
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def format_value
|
|
52
|
+
@value
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def accept(visitor)
|
|
56
|
+
visitor.visit_token(self)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def ==(other)
|
|
60
|
+
if instance_of?(other.class)
|
|
61
|
+
ivars_match?(other)
|
|
62
|
+
elsif other.is_a?(String)
|
|
63
|
+
@value == other
|
|
64
|
+
else
|
|
65
|
+
false
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def eql?(other)
|
|
70
|
+
self == other
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def hash
|
|
74
|
+
instance_variables.map { |v| instance_variable_get(v) }.hash
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def to_s
|
|
78
|
+
Tree.items_to_str(@prefix, format_value, @suffix)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def with(**changes)
|
|
82
|
+
self.class.new(
|
|
83
|
+
changes.fetch(:value, @value),
|
|
84
|
+
changes.fetch(:line_number, @line_number),
|
|
85
|
+
changes.fetch(:prefix, @prefix),
|
|
86
|
+
changes.fetch(:suffix, @suffix)
|
|
87
|
+
)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
protected
|
|
91
|
+
|
|
92
|
+
def ivars_match?(other)
|
|
93
|
+
instance_variables.all? { |v| instance_variable_get(v) == other.instance_variable_get(v) }
|
|
94
|
+
end
|
|
36
95
|
end
|
|
37
|
-
end
|
|
38
96
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
97
|
+
class LeftCurlyBrace < SyntaxToken
|
|
98
|
+
def initialize(value = "{", line_number = nil, prefix = "", suffix = "")
|
|
99
|
+
super
|
|
100
|
+
end
|
|
42
101
|
end
|
|
43
|
-
end
|
|
44
102
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
103
|
+
class RightCurlyBrace < SyntaxToken
|
|
104
|
+
def initialize(value = "}", line_number = nil, prefix = "", suffix = "")
|
|
105
|
+
super
|
|
106
|
+
end
|
|
48
107
|
end
|
|
49
|
-
end
|
|
50
108
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
109
|
+
class Colon < SyntaxToken
|
|
110
|
+
def initialize(value = ":", line_number = nil, prefix = "", suffix = "")
|
|
111
|
+
super
|
|
112
|
+
end
|
|
54
113
|
end
|
|
55
|
-
end
|
|
56
114
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
115
|
+
class LeftBracket < SyntaxToken
|
|
116
|
+
def initialize(value = "[", line_number = nil, prefix = "", suffix = "")
|
|
117
|
+
super
|
|
118
|
+
end
|
|
60
119
|
end
|
|
61
|
-
end
|
|
62
120
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
121
|
+
class RightBracket < SyntaxToken
|
|
122
|
+
def initialize(value = "]", line_number = nil, prefix = "", suffix = "")
|
|
123
|
+
super
|
|
124
|
+
end
|
|
66
125
|
end
|
|
67
|
-
end
|
|
68
126
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
127
|
+
class DoubleSemicolon < SyntaxToken
|
|
128
|
+
def initialize(value = ";;", line_number = nil, prefix = "", suffix = "")
|
|
129
|
+
super
|
|
130
|
+
end
|
|
72
131
|
end
|
|
73
|
-
end
|
|
74
132
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
133
|
+
class Comma < SyntaxToken
|
|
134
|
+
def initialize(value = ",", line_number = nil, prefix = "", suffix = "")
|
|
135
|
+
super
|
|
136
|
+
end
|
|
78
137
|
end
|
|
79
|
-
end
|
|
80
138
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
139
|
+
class QuotedSyntaxToken < SyntaxToken
|
|
140
|
+
def format_value
|
|
141
|
+
"\"#{@value.gsub('\\"', '"').gsub('"', '\\"')}\""
|
|
142
|
+
end
|
|
84
143
|
end
|
|
85
|
-
end
|
|
86
144
|
|
|
87
|
-
|
|
88
|
-
|
|
145
|
+
class ExpressionSyntaxToken < SyntaxToken
|
|
146
|
+
attr_reader :expr_suffix
|
|
89
147
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
end
|
|
148
|
+
def initialize(value, line_number = nil, prefix = " ", suffix = "", expr_suffix = " ")
|
|
149
|
+
super(value, line_number, prefix, suffix, expr_suffix: expr_suffix)
|
|
150
|
+
end
|
|
94
151
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
152
|
+
def with(**changes)
|
|
153
|
+
ExpressionSyntaxToken.new(
|
|
154
|
+
changes.fetch(:value, @value),
|
|
155
|
+
changes.fetch(:line_number, @line_number),
|
|
156
|
+
changes.fetch(:prefix, @prefix),
|
|
157
|
+
changes.fetch(:suffix, @suffix),
|
|
158
|
+
changes.fetch(:expr_suffix, @expr_suffix)
|
|
159
|
+
)
|
|
160
|
+
end
|
|
99
161
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
162
|
+
def to_s
|
|
163
|
+
Tree.items_to_str(@prefix, format_value, @expr_suffix, ";;", @suffix)
|
|
164
|
+
end
|
|
103
165
|
end
|
|
104
166
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
167
|
+
class SyntaxNode
|
|
168
|
+
def children
|
|
169
|
+
raise NotImplementedError
|
|
170
|
+
end
|
|
108
171
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
172
|
+
def line_number
|
|
173
|
+
raise NotImplementedError
|
|
174
|
+
end
|
|
112
175
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
instance_variables.all? { |var| instance_variable_get(var) == other.instance_variable_get(var) }
|
|
116
|
-
elsif other.is_a?(String)
|
|
117
|
-
@value == other
|
|
118
|
-
else
|
|
119
|
-
false
|
|
176
|
+
def accept(_visitor)
|
|
177
|
+
raise NotImplementedError
|
|
120
178
|
end
|
|
121
179
|
end
|
|
122
|
-
end
|
|
123
180
|
|
|
124
|
-
|
|
125
|
-
|
|
181
|
+
class PairNode < SyntaxNode
|
|
182
|
+
attr_reader :type, :value, :colon
|
|
126
183
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
184
|
+
def initialize(type:, value:, colon: nil)
|
|
185
|
+
super()
|
|
186
|
+
@type = type
|
|
187
|
+
@value = value
|
|
188
|
+
@colon = colon || Colon.new(":", nil, "", " ")
|
|
189
|
+
freeze
|
|
190
|
+
end
|
|
133
191
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
192
|
+
def inspect
|
|
193
|
+
"#{self.class.name.split('::').last}(type='#{@type.value}', value='#{@value.value}')"
|
|
194
|
+
end
|
|
137
195
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
196
|
+
def children
|
|
197
|
+
nil
|
|
198
|
+
end
|
|
141
199
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
200
|
+
def line_number
|
|
201
|
+
@type.line_number
|
|
202
|
+
end
|
|
145
203
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
end
|
|
204
|
+
def accept(visitor)
|
|
205
|
+
visitor.visit_pair(self)
|
|
206
|
+
end
|
|
150
207
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
def initialize(type, items:, left_bracket:, right_bracket:, colon: Colon.new(suffix: ' '), leading_comma: nil, trailing_comma: nil) # rubocop:disable Metrics/ParameterLists,Layout/LineLength
|
|
155
|
-
super()
|
|
156
|
-
@type = type
|
|
157
|
-
@items = items
|
|
158
|
-
@left_bracket = left_bracket
|
|
159
|
-
@right_bracket = right_bracket
|
|
160
|
-
@colon = colon
|
|
161
|
-
@leading_comma = leading_comma
|
|
162
|
-
@trailing_comma = trailing_comma
|
|
163
|
-
end
|
|
208
|
+
def to_s
|
|
209
|
+
Tree.items_to_str(@type, @colon, @value)
|
|
210
|
+
end
|
|
164
211
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
@trailing_comma && @items.any? ? @trailing_comma : '',
|
|
173
|
-
@right_bracket
|
|
174
|
-
].join
|
|
175
|
-
end
|
|
212
|
+
def with(**changes)
|
|
213
|
+
PairNode.new(
|
|
214
|
+
type: changes.fetch(:type, @type),
|
|
215
|
+
value: changes.fetch(:value, @value),
|
|
216
|
+
colon: changes.fetch(:colon, @colon)
|
|
217
|
+
)
|
|
218
|
+
end
|
|
176
219
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
220
|
+
def ==(other)
|
|
221
|
+
instance_of?(other.class) &&
|
|
222
|
+
@type == other.type &&
|
|
223
|
+
@value == other.value &&
|
|
224
|
+
@colon == other.colon
|
|
182
225
|
end
|
|
183
226
|
end
|
|
184
227
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
228
|
+
class ListNode < SyntaxNode
|
|
229
|
+
attr_reader :type, :items, :left_bracket, :right_bracket, :colon, :leading_comma, :trailing_comma
|
|
230
|
+
|
|
231
|
+
def initialize(type:, items:, left_bracket:, right_bracket:, colon: nil, leading_comma: nil,
|
|
232
|
+
trailing_comma: nil)
|
|
233
|
+
super()
|
|
234
|
+
@type = type
|
|
235
|
+
@items = items.freeze
|
|
236
|
+
@left_bracket = left_bracket
|
|
237
|
+
@right_bracket = right_bracket
|
|
238
|
+
@colon = colon || Colon.new(":", nil, "", " ")
|
|
239
|
+
@leading_comma = leading_comma
|
|
240
|
+
@trailing_comma = trailing_comma
|
|
241
|
+
freeze
|
|
242
|
+
end
|
|
188
243
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
244
|
+
def inspect
|
|
245
|
+
"#{self.class.name.split('::').last}(type='#{@type.value}')"
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def children
|
|
249
|
+
return [].freeze if @items.empty?
|
|
193
250
|
|
|
194
|
-
|
|
195
|
-
|
|
251
|
+
@items.first.is_a?(PairNode) ? @items : []
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def line_number
|
|
255
|
+
@type.line_number
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def accept(visitor)
|
|
259
|
+
visitor.visit_list(self)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def to_s
|
|
263
|
+
mid = @items.join(",")
|
|
264
|
+
lc = @leading_comma && !@items.empty? ? @leading_comma.to_s : ""
|
|
265
|
+
tc = @trailing_comma && !@items.empty? ? @trailing_comma.to_s : ""
|
|
266
|
+
Tree.items_to_str(@type, @colon, @left_bracket, lc, mid, tc, @right_bracket)
|
|
267
|
+
end
|
|
196
268
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
269
|
+
def with(**changes)
|
|
270
|
+
ListNode.new(
|
|
271
|
+
type: changes.fetch(:type, @type),
|
|
272
|
+
items: changes.fetch(:items, @items),
|
|
273
|
+
left_bracket: changes.fetch(:left_bracket, @left_bracket),
|
|
274
|
+
right_bracket: changes.fetch(:right_bracket, @right_bracket),
|
|
275
|
+
colon: changes.fetch(:colon, @colon),
|
|
276
|
+
leading_comma: changes.fetch(:leading_comma, @leading_comma),
|
|
277
|
+
trailing_comma: changes.fetch(:trailing_comma, @trailing_comma)
|
|
278
|
+
)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def ==(other)
|
|
282
|
+
instance_of?(other.class) &&
|
|
283
|
+
@type == other.type &&
|
|
284
|
+
@items == other.items &&
|
|
285
|
+
@left_bracket == other.left_bracket &&
|
|
286
|
+
@right_bracket == other.right_bracket &&
|
|
287
|
+
@colon == other.colon &&
|
|
288
|
+
@leading_comma == other.leading_comma &&
|
|
289
|
+
@trailing_comma == other.trailing_comma
|
|
290
|
+
end
|
|
202
291
|
end
|
|
203
292
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
293
|
+
class ContainerNode < SyntaxNode
|
|
294
|
+
attr_reader :items, :top_level
|
|
295
|
+
|
|
296
|
+
def initialize(items:, top_level: false)
|
|
297
|
+
super()
|
|
298
|
+
@top_level = top_level
|
|
299
|
+
@items = items.freeze
|
|
300
|
+
validate_duplicate_keys!
|
|
301
|
+
freeze
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def inspect
|
|
305
|
+
"#{self.class.name.split('::').last}()"
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def validate_duplicate_keys!
|
|
309
|
+
counts = Hash.new(0)
|
|
310
|
+
@items.each { |item| counts[item.type.value] += 1 }
|
|
311
|
+
counts.each do |key, count|
|
|
312
|
+
next if @top_level || count <= 1 || Keys::PLURAL_KEYS.include?(key)
|
|
313
|
+
|
|
314
|
+
raise KeyError,
|
|
315
|
+
"Key \"#{key}\" already exists in tree and would overwrite the " \
|
|
316
|
+
"existing value."
|
|
209
317
|
end
|
|
210
318
|
end
|
|
211
|
-
end
|
|
212
319
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
320
|
+
def children
|
|
321
|
+
@items
|
|
322
|
+
end
|
|
216
323
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
324
|
+
def line_number
|
|
325
|
+
@items[0]&.line_number
|
|
326
|
+
end
|
|
220
327
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
328
|
+
def accept(visitor)
|
|
329
|
+
visitor.visit_container(self)
|
|
330
|
+
end
|
|
224
331
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
end
|
|
332
|
+
def to_s
|
|
333
|
+
Tree.items_to_str(*@items)
|
|
334
|
+
end
|
|
229
335
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
@left_brace = left_brace
|
|
237
|
-
@right_brace = right_brace
|
|
238
|
-
@colon = colon
|
|
239
|
-
@name = name
|
|
240
|
-
@container = container
|
|
241
|
-
end
|
|
336
|
+
def with(**changes)
|
|
337
|
+
ContainerNode.new(
|
|
338
|
+
items: changes.fetch(:items, @items),
|
|
339
|
+
top_level: changes.fetch(:top_level, @top_level)
|
|
340
|
+
)
|
|
341
|
+
end
|
|
242
342
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
@left_brace,
|
|
249
|
-
@container || '',
|
|
250
|
-
@right_brace
|
|
251
|
-
].join
|
|
343
|
+
def ==(other)
|
|
344
|
+
instance_of?(other.class) &&
|
|
345
|
+
@top_level == other.top_level &&
|
|
346
|
+
@items == other.items
|
|
347
|
+
end
|
|
252
348
|
end
|
|
253
349
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
end
|
|
350
|
+
class BlockNode < SyntaxNode
|
|
351
|
+
attr_reader :type, :left_brace, :right_brace, :colon, :name, :container
|
|
257
352
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
353
|
+
def initialize(type:, left_brace: nil, right_brace: nil, colon: nil, name: nil, container: nil)
|
|
354
|
+
super()
|
|
355
|
+
@type = type
|
|
356
|
+
@left_brace = left_brace || LeftCurlyBrace.new("{", nil, "", "\n")
|
|
357
|
+
@right_brace = right_brace || RightCurlyBrace.new("}", nil, "\n", "")
|
|
358
|
+
@colon = colon || Colon.new(":", nil, "", " ")
|
|
359
|
+
@name = name
|
|
360
|
+
@container = container || ContainerNode.new(items: [])
|
|
361
|
+
freeze
|
|
362
|
+
end
|
|
261
363
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
364
|
+
def inspect
|
|
365
|
+
nm = @name ? "name='#{@name.value}'" : "None"
|
|
366
|
+
"#{self.class.name.split('::').last}(type='#{@type.value}', #{nm})"
|
|
367
|
+
end
|
|
266
368
|
|
|
267
|
-
|
|
268
|
-
|
|
369
|
+
def children
|
|
370
|
+
@container.children
|
|
371
|
+
end
|
|
269
372
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
@prefix = prefix
|
|
274
|
-
@suffix = suffix
|
|
275
|
-
end
|
|
373
|
+
def line_number
|
|
374
|
+
@type.line_number
|
|
375
|
+
end
|
|
276
376
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
377
|
+
def accept(visitor)
|
|
378
|
+
visitor.visit_block(self)
|
|
379
|
+
end
|
|
280
380
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
381
|
+
def to_s
|
|
382
|
+
nm = @name || ""
|
|
383
|
+
ctr = @container || ""
|
|
384
|
+
Tree.items_to_str(@type, @colon, nm, @left_brace, ctr, @right_brace)
|
|
385
|
+
end
|
|
284
386
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
387
|
+
def with(**changes)
|
|
388
|
+
BlockNode.new(
|
|
389
|
+
type: changes.fetch(:type, @type),
|
|
390
|
+
left_brace: changes.fetch(:left_brace, @left_brace),
|
|
391
|
+
right_brace: changes.fetch(:right_brace, @right_brace),
|
|
392
|
+
colon: changes.fetch(:colon, @colon),
|
|
393
|
+
name: changes.fetch(:name, @name),
|
|
394
|
+
container: changes.fetch(:container, @container)
|
|
395
|
+
)
|
|
396
|
+
end
|
|
288
397
|
|
|
289
|
-
|
|
290
|
-
|
|
398
|
+
def ==(other)
|
|
399
|
+
instance_of?(other.class) &&
|
|
400
|
+
@type == other.type &&
|
|
401
|
+
@left_brace == other.left_brace &&
|
|
402
|
+
@right_brace == other.right_brace &&
|
|
403
|
+
@colon == other.colon &&
|
|
404
|
+
@name == other.name &&
|
|
405
|
+
@container == other.container
|
|
406
|
+
end
|
|
291
407
|
end
|
|
292
|
-
end
|
|
293
408
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
raise NotImplementedError, 'Subclasses must implement the visit method'
|
|
297
|
-
end
|
|
409
|
+
class DocumentNode < SyntaxNode
|
|
410
|
+
attr_reader :container, :prefix, :suffix
|
|
298
411
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
412
|
+
def initialize(container, prefix = "", suffix = "")
|
|
413
|
+
super()
|
|
414
|
+
@container = container
|
|
415
|
+
@prefix = prefix
|
|
416
|
+
@suffix = suffix
|
|
417
|
+
freeze
|
|
418
|
+
end
|
|
302
419
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
420
|
+
def children
|
|
421
|
+
[@container]
|
|
422
|
+
end
|
|
306
423
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
424
|
+
def line_number
|
|
425
|
+
1
|
|
426
|
+
end
|
|
310
427
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
428
|
+
def accept(visitor)
|
|
429
|
+
visitor.visit(self)
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def to_s
|
|
433
|
+
Tree.items_to_str(@prefix, @container, @suffix)
|
|
434
|
+
end
|
|
314
435
|
|
|
315
|
-
|
|
316
|
-
|
|
436
|
+
def with(**changes)
|
|
437
|
+
DocumentNode.new(
|
|
438
|
+
changes.fetch(:container, @container),
|
|
439
|
+
changes.fetch(:prefix, @prefix),
|
|
440
|
+
changes.fetch(:suffix, @suffix)
|
|
441
|
+
)
|
|
442
|
+
end
|
|
317
443
|
end
|
|
318
444
|
end
|
|
319
445
|
end
|