syntax_tree 5.0.1 → 5.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -84,10 +84,10 @@ module SyntaxTree
84
84
  @target_ruby_version = options.target_ruby_version
85
85
  end
86
86
 
87
- def self.format(source, node)
87
+ def self.format(source, node, base_indentation = 0)
88
88
  q = new(source, [])
89
89
  q.format(node)
90
- q.flush
90
+ q.flush(base_indentation)
91
91
  q.output.join
92
92
  end
93
93
 
@@ -111,6 +111,8 @@ module SyntaxTree
111
111
  write(id: request[:id], result: PP.pp(SyntaxTree.parse(store[uri]), +""))
112
112
  when Request[method: %r{\$/.+}]
113
113
  # ignored
114
+ when Request[method: "textDocument/documentColor", params: { textDocument: { uri: :any } }]
115
+ # ignored
114
116
  else
115
117
  raise ArgumentError, "Unhandled: #{request}"
116
118
  end
@@ -1604,7 +1604,7 @@ module SyntaxTree
1604
1604
  # { **pairs }
1605
1605
  #
1606
1606
  class AssocSplat < Node
1607
- # [untyped] the expression that is being splatted
1607
+ # [nil | untyped] the expression that is being splatted
1608
1608
  attr_reader :value
1609
1609
 
1610
1610
  # [Array[ Comment | EmbDoc ]] the comments attached to this node
@@ -1643,7 +1643,7 @@ module SyntaxTree
1643
1643
 
1644
1644
  def format(q)
1645
1645
  q.text("**")
1646
- q.format(value)
1646
+ q.format(value) if value
1647
1647
  end
1648
1648
 
1649
1649
  def ===(other)
@@ -6160,7 +6160,7 @@ module SyntaxTree
6160
6160
  # want to force it to not be a ternary, like if the predicate is an
6161
6161
  # assignment because it's hard to read.
6162
6162
  case node.predicate
6163
- when Assign, Command, CommandCall, MAssign, OpAssign
6163
+ when Assign, Binary, Command, CommandCall, MAssign, OpAssign
6164
6164
  return false
6165
6165
  when Not
6166
6166
  return false unless node.predicate.parentheses?
@@ -6183,10 +6183,10 @@ module SyntaxTree
6183
6183
  # and default instead to breaking them into multiple lines.
6184
6184
  def ternaryable?(statement)
6185
6185
  case statement
6186
- when AliasNode, Assign, Break, Command, CommandCall, Heredoc, IfNode,
6187
- IfOp, Lambda, MAssign, Next, OpAssign, RescueMod, ReturnNode,
6188
- Super, Undef, UnlessNode, UntilNode, VoidStmt, WhileNode,
6189
- YieldNode, ZSuper
6186
+ when AliasNode, Assign, Break, Command, CommandCall, Defined, Heredoc,
6187
+ IfNode, IfOp, Lambda, MAssign, Next, OpAssign, RescueMod,
6188
+ ReturnNode, Super, Undef, UnlessNode, UntilNode, VoidStmt,
6189
+ WhileNode, YieldNode, ZSuper
6190
6190
  # This is a list of nodes that should not be allowed to be a part of a
6191
6191
  # ternary clause.
6192
6192
  false
@@ -744,7 +744,7 @@ module SyntaxTree
744
744
 
745
745
  AssocSplat.new(
746
746
  value: value,
747
- location: operator.location.to(value.location)
747
+ location: operator.location.to((value || operator).location)
748
748
  )
749
749
  end
750
750
 
@@ -820,13 +820,13 @@ module SyntaxTree
820
820
  end
821
821
 
822
822
  bodystmt.bind(
823
- keyword.location.end_char,
823
+ find_next_statement_start(keyword.location.end_char),
824
824
  keyword.location.end_column,
825
825
  end_location.end_char,
826
826
  end_location.end_column
827
827
  )
828
- location = keyword.location.to(bodystmt.location)
829
828
 
829
+ location = keyword.location.to(end_location)
830
830
  Begin.new(bodystmt: bodystmt, location: location)
831
831
  end
832
832
  end
@@ -905,14 +905,15 @@ module SyntaxTree
905
905
  # (nil | Ensure) ensure_clause
906
906
  # ) -> BodyStmt
907
907
  def on_bodystmt(statements, rescue_clause, else_clause, ensure_clause)
908
+ parts = [statements, rescue_clause, else_clause, ensure_clause].compact
909
+
908
910
  BodyStmt.new(
909
911
  statements: statements,
910
912
  rescue_clause: rescue_clause,
911
913
  else_keyword: else_clause && consume_keyword(:else),
912
914
  else_clause: else_clause,
913
915
  ensure_clause: ensure_clause,
914
- location:
915
- Location.fixed(line: lineno, char: char_pos, column: current_column)
916
+ location: parts.first.location.to(parts.last.location)
916
917
  )
917
918
  end
918
919
 
@@ -994,22 +995,11 @@ module SyntaxTree
994
995
  # :call-seq:
995
996
  # on_case: (untyped value, untyped consequent) -> Case | RAssign
996
997
  def on_case(value, consequent)
997
- if (keyword = find_keyword(:case))
998
- tokens.delete(keyword)
999
-
1000
- Case.new(
1001
- keyword: keyword,
1002
- value: value,
1003
- consequent: consequent,
1004
- location: keyword.location.to(consequent.location)
1005
- )
1006
- else
1007
- operator =
1008
- if (keyword = find_keyword(:in))
1009
- tokens.delete(keyword)
1010
- else
1011
- consume_operator(:"=>")
1012
- end
998
+ if value && (operator = find_keyword(:in) || find_operator(:"=>")) &&
999
+ (value.location.end_char...consequent.location.start_char).cover?(
1000
+ operator.location.start_char
1001
+ )
1002
+ tokens.delete(operator)
1013
1003
 
1014
1004
  node =
1015
1005
  RAssign.new(
@@ -1021,6 +1011,15 @@ module SyntaxTree
1021
1011
 
1022
1012
  PinVisitor.visit(node, tokens)
1023
1013
  node
1014
+ else
1015
+ keyword = consume_keyword(:case)
1016
+
1017
+ Case.new(
1018
+ keyword: keyword,
1019
+ value: value,
1020
+ consequent: consequent,
1021
+ location: keyword.location.to(consequent.location)
1022
+ )
1024
1023
  end
1025
1024
  end
1026
1025
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SyntaxTree
4
- VERSION = "5.0.1"
4
+ VERSION = "5.1.0"
5
5
  end
@@ -0,0 +1,459 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SyntaxTree
4
+ module YARV
5
+ class Assembler
6
+ class ObjectVisitor < Compiler::RubyVisitor
7
+ def visit_dyna_symbol(node)
8
+ if node.parts.empty?
9
+ :""
10
+ else
11
+ raise CompilationError
12
+ end
13
+ end
14
+
15
+ def visit_string_literal(node)
16
+ case node.parts.length
17
+ when 0
18
+ ""
19
+ when 1
20
+ raise CompilationError unless node.parts.first.is_a?(TStringContent)
21
+ node.parts.first.value
22
+ else
23
+ raise CompilationError
24
+ end
25
+ end
26
+ end
27
+
28
+ CALLDATA_FLAGS = {
29
+ "ARGS_SPLAT" => CallData::CALL_ARGS_SPLAT,
30
+ "ARGS_BLOCKARG" => CallData::CALL_ARGS_BLOCKARG,
31
+ "FCALL" => CallData::CALL_FCALL,
32
+ "VCALL" => CallData::CALL_VCALL,
33
+ "ARGS_SIMPLE" => CallData::CALL_ARGS_SIMPLE,
34
+ "BLOCKISEQ" => CallData::CALL_BLOCKISEQ,
35
+ "KWARG" => CallData::CALL_KWARG,
36
+ "KW_SPLAT" => CallData::CALL_KW_SPLAT,
37
+ "TAILCALL" => CallData::CALL_TAILCALL,
38
+ "SUPER" => CallData::CALL_SUPER,
39
+ "ZSUPER" => CallData::CALL_ZSUPER,
40
+ "OPT_SEND" => CallData::CALL_OPT_SEND,
41
+ "KW_SPLAT_MUT" => CallData::CALL_KW_SPLAT_MUT
42
+ }.freeze
43
+
44
+ DEFINED_TYPES = [
45
+ nil,
46
+ "nil",
47
+ "instance-variable",
48
+ "local-variable",
49
+ "global-variable",
50
+ "class variable",
51
+ "constant",
52
+ "method",
53
+ "yield",
54
+ "super",
55
+ "self",
56
+ "true",
57
+ "false",
58
+ "assignment",
59
+ "expression",
60
+ "ref",
61
+ "func",
62
+ "constant-from"
63
+ ].freeze
64
+
65
+ attr_reader :filepath
66
+
67
+ def initialize(filepath)
68
+ @filepath = filepath
69
+ end
70
+
71
+ def assemble
72
+ iseq = InstructionSequence.new(:top, "<main>", nil, Location.default)
73
+ assemble_iseq(iseq, File.readlines(filepath, chomp: true))
74
+
75
+ iseq.compile!
76
+ iseq
77
+ end
78
+
79
+ def self.assemble(filepath)
80
+ new(filepath).assemble
81
+ end
82
+
83
+ private
84
+
85
+ def assemble_iseq(iseq, lines)
86
+ labels = Hash.new { |hash, name| hash[name] = iseq.label }
87
+ line_index = 0
88
+
89
+ while line_index < lines.length
90
+ line = lines[line_index]
91
+ line_index += 1
92
+
93
+ case line.strip
94
+ when "", /^;/
95
+ # skip over blank lines and comments
96
+ next
97
+ when /^(\w+):$/
98
+ # create labels
99
+ iseq.push(labels[$1])
100
+ next
101
+ when /^__END__/
102
+ # skip over the rest of the file when we hit __END__
103
+ return
104
+ end
105
+
106
+ insn, operands = line.split(" ", 2)
107
+
108
+ case insn
109
+ when "adjuststack"
110
+ iseq.adjuststack(parse_number(operands))
111
+ when "anytostring"
112
+ iseq.anytostring
113
+ when "branchif"
114
+ iseq.branchif(labels[operands])
115
+ when "branchnil"
116
+ iseq.branchnil(labels[operands])
117
+ when "branchunless"
118
+ iseq.branchunless(labels[operands])
119
+ when "checkkeyword"
120
+ kwbits_index, keyword_index = operands.split(/,\s*/)
121
+ iseq.checkkeyword(
122
+ parse_number(kwbits_index),
123
+ parse_number(keyword_index)
124
+ )
125
+ when "checkmatch"
126
+ iseq.checkmatch(parse_number(operands))
127
+ when "checktype"
128
+ iseq.checktype(parse_number(operands))
129
+ when "concatarray"
130
+ iseq.concatarray
131
+ when "concatstrings"
132
+ iseq.concatstrings(parse_number(operands))
133
+ when "defineclass"
134
+ body = parse_nested(lines[line_index..])
135
+ line_index += body.length
136
+
137
+ name_value, flags_value = operands.split(/,\s*/)
138
+ name = parse_symbol(name_value)
139
+ flags = parse_number(flags_value)
140
+
141
+ class_iseq = iseq.class_child_iseq(name.to_s, Location.default)
142
+ assemble_iseq(class_iseq, body)
143
+ iseq.defineclass(name, class_iseq, flags)
144
+ when "defined"
145
+ type, object, message = operands.split(/,\s*/)
146
+ iseq.defined(
147
+ DEFINED_TYPES.index(type),
148
+ parse_symbol(object),
149
+ parse_string(message)
150
+ )
151
+ when "definemethod"
152
+ body = parse_nested(lines[line_index..])
153
+ line_index += body.length
154
+
155
+ name = parse_symbol(operands)
156
+ method_iseq = iseq.method_child_iseq(name.to_s, Location.default)
157
+ assemble_iseq(method_iseq, body)
158
+
159
+ iseq.definemethod(name, method_iseq)
160
+ when "definesmethod"
161
+ body = parse_nested(lines[line_index..])
162
+ line_index += body.length
163
+
164
+ name = parse_symbol(operands)
165
+ method_iseq = iseq.method_child_iseq(name.to_s, Location.default)
166
+
167
+ assemble_iseq(method_iseq, body)
168
+ iseq.definesmethod(name, method_iseq)
169
+ when "dup"
170
+ iseq.dup
171
+ when "dupn"
172
+ iseq.dupn(parse_number(operands))
173
+ when "duparray"
174
+ iseq.duparray(parse_type(operands, Array))
175
+ when "duphash"
176
+ iseq.duphash(parse_type(operands, Hash))
177
+ when "expandarray"
178
+ number, flags = operands.split(/,\s*/)
179
+ iseq.expandarray(parse_number(number), parse_number(flags))
180
+ when "getblockparam"
181
+ lookup = find_local(iseq, operands)
182
+ iseq.getblockparam(lookup.index, lookup.level)
183
+ when "getblockparamproxy"
184
+ lookup = find_local(iseq, operands)
185
+ iseq.getblockparamproxy(lookup.index, lookup.level)
186
+ when "getclassvariable"
187
+ iseq.getclassvariable(parse_symbol(operands))
188
+ when "getconstant"
189
+ iseq.getconstant(parse_symbol(operands))
190
+ when "getglobal"
191
+ iseq.getglobal(parse_symbol(operands))
192
+ when "getinstancevariable"
193
+ iseq.getinstancevariable(parse_symbol(operands))
194
+ when "getlocal"
195
+ lookup = find_local(iseq, operands)
196
+ iseq.getlocal(lookup.index, lookup.level)
197
+ when "getspecial"
198
+ key, type = operands.split(/,\s*/)
199
+ iseq.getspecial(parse_number(key), parse_number(type))
200
+ when "intern"
201
+ iseq.intern
202
+ when "invokeblock"
203
+ iseq.invokeblock(
204
+ operands ? parse_calldata(operands) : YARV.calldata(nil, 0)
205
+ )
206
+ when "invokesuper"
207
+ calldata =
208
+ if operands
209
+ parse_calldata(operands)
210
+ else
211
+ YARV.calldata(
212
+ nil,
213
+ 0,
214
+ CallData::CALL_FCALL | CallData::CALL_ARGS_SIMPLE |
215
+ CallData::CALL_SUPER
216
+ )
217
+ end
218
+
219
+ block_iseq =
220
+ if lines[line_index].start_with?(" ")
221
+ body = parse_nested(lines[line_index..])
222
+ line_index += body.length
223
+
224
+ block_iseq = iseq.block_child_iseq(Location.default)
225
+ assemble_iseq(block_iseq, body)
226
+ block_iseq
227
+ end
228
+
229
+ iseq.invokesuper(calldata, block_iseq)
230
+ when "jump"
231
+ iseq.jump(labels[operands])
232
+ when "leave"
233
+ iseq.leave
234
+ when "newarray"
235
+ iseq.newarray(parse_number(operands))
236
+ when "newarraykwsplat"
237
+ iseq.newarraykwsplat(parse_number(operands))
238
+ when "newhash"
239
+ iseq.newhash(parse_number(operands))
240
+ when "newrange"
241
+ iseq.newrange(parse_options(operands, [0, 1]))
242
+ when "nop"
243
+ iseq.nop
244
+ when "objtostring"
245
+ iseq.objtostring(YARV.calldata(:to_s))
246
+ when "once"
247
+ block_iseq =
248
+ if lines[line_index].start_with?(" ")
249
+ body = parse_nested(lines[line_index..])
250
+ line_index += body.length
251
+
252
+ block_iseq = iseq.block_child_iseq(Location.default)
253
+ assemble_iseq(block_iseq, body)
254
+ block_iseq
255
+ end
256
+
257
+ iseq.once(block_iseq, iseq.inline_storage)
258
+ when "opt_and"
259
+ iseq.send(YARV.calldata(:&, 1))
260
+ when "opt_aref"
261
+ iseq.send(YARV.calldata(:[], 1))
262
+ when "opt_aref_with"
263
+ iseq.opt_aref_with(parse_string(operands), YARV.calldata(:[], 1))
264
+ when "opt_aset"
265
+ iseq.send(YARV.calldata(:[]=, 2))
266
+ when "opt_aset_with"
267
+ iseq.opt_aset_with(parse_string(operands), YARV.calldata(:[]=, 2))
268
+ when "opt_case_dispatch"
269
+ cdhash_value, else_label_value = operands.split(/\s*\},\s*/)
270
+ cdhash_value.sub!(/\A\{/, "")
271
+
272
+ pairs =
273
+ cdhash_value
274
+ .split(/\s*,\s*/)
275
+ .map! { |pair| pair.split(/\s*=>\s*/) }
276
+
277
+ cdhash = pairs.to_h { |value, nm| [parse(value), labels[nm]] }
278
+ else_label = labels[else_label_value]
279
+
280
+ iseq.opt_case_dispatch(cdhash, else_label)
281
+ when "opt_div"
282
+ iseq.send(YARV.calldata(:/, 1))
283
+ when "opt_empty_p"
284
+ iseq.send(YARV.calldata(:empty?))
285
+ when "opt_eq"
286
+ iseq.send(YARV.calldata(:==, 1))
287
+ when "opt_ge"
288
+ iseq.send(YARV.calldata(:>=, 1))
289
+ when "opt_gt"
290
+ iseq.send(YARV.calldata(:>, 1))
291
+ when "opt_getconstant_path"
292
+ iseq.opt_getconstant_path(parse_type(operands, Array))
293
+ when "opt_le"
294
+ iseq.send(YARV.calldata(:<=, 1))
295
+ when "opt_length"
296
+ iseq.send(YARV.calldata(:length))
297
+ when "opt_lt"
298
+ iseq.send(YARV.calldata(:<, 1))
299
+ when "opt_ltlt"
300
+ iseq.send(YARV.calldata(:<<, 1))
301
+ when "opt_minus"
302
+ iseq.send(YARV.calldata(:-, 1))
303
+ when "opt_mod"
304
+ iseq.send(YARV.calldata(:%, 1))
305
+ when "opt_mult"
306
+ iseq.send(YARV.calldata(:*, 1))
307
+ when "opt_neq"
308
+ iseq.send(YARV.calldata(:!=, 1))
309
+ when "opt_newarray_max"
310
+ iseq.newarray(parse_number(operands))
311
+ iseq.send(YARV.calldata(:max))
312
+ when "opt_newarray_min"
313
+ iseq.newarray(parse_number(operands))
314
+ iseq.send(YARV.calldata(:min))
315
+ when "opt_nil_p"
316
+ iseq.send(YARV.calldata(:nil?))
317
+ when "opt_not"
318
+ iseq.send(YARV.calldata(:!))
319
+ when "opt_or"
320
+ iseq.send(YARV.calldata(:|, 1))
321
+ when "opt_plus"
322
+ iseq.send(YARV.calldata(:+, 1))
323
+ when "opt_regexpmatch2"
324
+ iseq.send(YARV.calldata(:=~, 1))
325
+ when "opt_reverse"
326
+ iseq.send(YARV.calldata(:reverse))
327
+ when "opt_send_without_block"
328
+ iseq.send(parse_calldata(operands))
329
+ when "opt_size"
330
+ iseq.send(YARV.calldata(:size))
331
+ when "opt_str_freeze"
332
+ iseq.putstring(parse_string(operands))
333
+ iseq.send(YARV.calldata(:freeze))
334
+ when "opt_str_uminus"
335
+ iseq.putstring(parse_string(operands))
336
+ iseq.send(YARV.calldata(:-@))
337
+ when "opt_succ"
338
+ iseq.send(YARV.calldata(:succ))
339
+ when "pop"
340
+ iseq.pop
341
+ when "putnil"
342
+ iseq.putnil
343
+ when "putobject"
344
+ iseq.putobject(parse(operands))
345
+ when "putself"
346
+ iseq.putself
347
+ when "putspecialobject"
348
+ iseq.putspecialobject(parse_options(operands, [1, 2, 3]))
349
+ when "putstring"
350
+ iseq.putstring(parse_string(operands))
351
+ when "send"
352
+ block_iseq =
353
+ if lines[line_index].start_with?(" ")
354
+ body = parse_nested(lines[line_index..])
355
+ line_index += body.length
356
+
357
+ block_iseq = iseq.block_child_iseq(Location.default)
358
+ assemble_iseq(block_iseq, body)
359
+ block_iseq
360
+ end
361
+
362
+ iseq.send(parse_calldata(operands), block_iseq)
363
+ when "setblockparam"
364
+ lookup = find_local(iseq, operands)
365
+ iseq.setblockparam(lookup.index, lookup.level)
366
+ when "setconstant"
367
+ iseq.setconstant(parse_symbol(operands))
368
+ when "setglobal"
369
+ iseq.setglobal(parse_symbol(operands))
370
+ when "setlocal"
371
+ lookup = find_local(iseq, operands)
372
+ iseq.setlocal(lookup.index, lookup.level)
373
+ when "setn"
374
+ iseq.setn(parse_number(operands))
375
+ when "setclassvariable"
376
+ iseq.setclassvariable(parse_symbol(operands))
377
+ when "setinstancevariable"
378
+ iseq.setinstancevariable(parse_symbol(operands))
379
+ when "setspecial"
380
+ iseq.setspecial(parse_number(operands))
381
+ when "splatarray"
382
+ iseq.splatarray(parse_options(operands, [true, false]))
383
+ when "swap"
384
+ iseq.swap
385
+ when "throw"
386
+ iseq.throw(parse_number(operands))
387
+ when "topn"
388
+ iseq.topn(parse_number(operands))
389
+ when "toregexp"
390
+ options, length = operands.split(", ")
391
+ iseq.toregexp(parse_number(options), parse_number(length))
392
+ when "ARG_REQ"
393
+ iseq.argument_size += 1
394
+ iseq.local_table.plain(operands.to_sym)
395
+ when "ARG_BLOCK"
396
+ iseq.argument_options[:block_start] = iseq.argument_size
397
+ iseq.local_table.block(operands.to_sym)
398
+ iseq.argument_size += 1
399
+ else
400
+ raise "Could not understand: #{line}"
401
+ end
402
+ end
403
+ end
404
+
405
+ def find_local(iseq, operands)
406
+ name_string, level_string = operands.split(/,\s*/)
407
+ name = name_string.to_sym
408
+ level = level_string&.to_i || 0
409
+
410
+ iseq.local_table.plain(name)
411
+ iseq.local_table.find(name, level)
412
+ end
413
+
414
+ def parse(value)
415
+ program = SyntaxTree.parse(value)
416
+ raise if program.statements.body.length != 1
417
+
418
+ program.statements.body.first.accept(ObjectVisitor.new)
419
+ end
420
+
421
+ def parse_options(value, options)
422
+ parse(value).tap { raise unless options.include?(_1) }
423
+ end
424
+
425
+ def parse_type(value, type)
426
+ parse(value).tap { raise unless _1.is_a?(type) }
427
+ end
428
+
429
+ def parse_number(value)
430
+ parse_type(value, Integer)
431
+ end
432
+
433
+ def parse_string(value)
434
+ parse_type(value, String)
435
+ end
436
+
437
+ def parse_symbol(value)
438
+ parse_type(value, Symbol)
439
+ end
440
+
441
+ def parse_nested(lines)
442
+ body = lines.take_while { |line| line.match?(/^($|;| )/) }
443
+ body.map! { |line| line.delete_prefix!(" ") || +"" }
444
+ end
445
+
446
+ def parse_calldata(value)
447
+ message, argc_value, flags_value = value.split
448
+ flags =
449
+ if flags_value
450
+ flags_value.split("|").map(&CALLDATA_FLAGS).inject(:|)
451
+ else
452
+ CallData::CALL_ARGS_SIMPLE
453
+ end
454
+
455
+ YARV.calldata(message.to_sym, argc_value&.to_i || 0, flags)
456
+ end
457
+ end
458
+ end
459
+ end