syntax_tree 5.2.0 → 5.3.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/auto-merge.yml +1 -1
- data/.github/workflows/gh-pages.yml +1 -1
- data/.gitmodules +3 -0
- data/.rubocop.yml +4 -0
- data/CHANGELOG.md +18 -1
- data/Gemfile.lock +6 -6
- data/README.md +1 -0
- data/lib/syntax_tree/cli.rb +3 -2
- data/lib/syntax_tree/formatter.rb +23 -2
- data/lib/syntax_tree/index.rb +374 -0
- data/lib/syntax_tree/node.rb +127 -97
- data/lib/syntax_tree/parser.rb +19 -2
- data/lib/syntax_tree/plugin/disable_ternary.rb +7 -0
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree/yarv/assembler.rb +10 -6
- data/lib/syntax_tree/yarv/compiler.rb +1 -1
- data/lib/syntax_tree/yarv/decompiler.rb +1 -1
- data/lib/syntax_tree/yarv/instruction_sequence.rb +1 -1
- data/lib/syntax_tree/yarv/instructions.rb +800 -0
- data/lib/syntax_tree/yarv/legacy.rb +33 -0
- data/lib/syntax_tree/yarv/vm.rb +4 -0
- data/lib/syntax_tree.rb +15 -0
- metadata +4 -2
data/lib/syntax_tree/node.rb
CHANGED
@@ -775,6 +775,10 @@ module SyntaxTree
|
|
775
775
|
other.is_a?(ArgParen) && arguments === other.arguments
|
776
776
|
end
|
777
777
|
|
778
|
+
def arity
|
779
|
+
arguments&.arity || 0
|
780
|
+
end
|
781
|
+
|
778
782
|
private
|
779
783
|
|
780
784
|
def trailing_comma?
|
@@ -848,6 +852,21 @@ module SyntaxTree
|
|
848
852
|
def ===(other)
|
849
853
|
other.is_a?(Args) && ArrayMatch.call(parts, other.parts)
|
850
854
|
end
|
855
|
+
|
856
|
+
def arity
|
857
|
+
parts.sum do |part|
|
858
|
+
case part
|
859
|
+
when ArgStar, ArgsForward
|
860
|
+
Float::INFINITY
|
861
|
+
when BareAssocHash
|
862
|
+
part.assocs.sum do |assoc|
|
863
|
+
assoc.is_a?(AssocSplat) ? Float::INFINITY : 1
|
864
|
+
end
|
865
|
+
else
|
866
|
+
1
|
867
|
+
end
|
868
|
+
end
|
869
|
+
end
|
851
870
|
end
|
852
871
|
|
853
872
|
# ArgBlock represents using a block operator on an expression.
|
@@ -1008,6 +1027,10 @@ module SyntaxTree
|
|
1008
1027
|
def ===(other)
|
1009
1028
|
other.is_a?(ArgsForward)
|
1010
1029
|
end
|
1030
|
+
|
1031
|
+
def arity
|
1032
|
+
Float::INFINITY
|
1033
|
+
end
|
1011
1034
|
end
|
1012
1035
|
|
1013
1036
|
# ArrayLiteral represents an array literal, which can optionally contain
|
@@ -1080,58 +1103,6 @@ module SyntaxTree
|
|
1080
1103
|
end
|
1081
1104
|
end
|
1082
1105
|
|
1083
|
-
# Formats an array that contains only a list of variable references. To make
|
1084
|
-
# things simpler, if there are a bunch, we format them all using the "fill"
|
1085
|
-
# algorithm as opposed to breaking them into a ton of lines. For example,
|
1086
|
-
#
|
1087
|
-
# [foo, bar, baz]
|
1088
|
-
#
|
1089
|
-
# instead of becoming:
|
1090
|
-
#
|
1091
|
-
# [
|
1092
|
-
# foo,
|
1093
|
-
# bar,
|
1094
|
-
# baz
|
1095
|
-
# ]
|
1096
|
-
#
|
1097
|
-
# would instead become:
|
1098
|
-
#
|
1099
|
-
# [
|
1100
|
-
# foo, bar,
|
1101
|
-
# baz
|
1102
|
-
# ]
|
1103
|
-
#
|
1104
|
-
# provided the line length was hit between `bar` and `baz`.
|
1105
|
-
class VarRefsFormatter
|
1106
|
-
# The separator for the fill algorithm.
|
1107
|
-
class Separator
|
1108
|
-
def call(q)
|
1109
|
-
q.text(",")
|
1110
|
-
q.fill_breakable
|
1111
|
-
end
|
1112
|
-
end
|
1113
|
-
|
1114
|
-
# [Args] the contents of the array
|
1115
|
-
attr_reader :contents
|
1116
|
-
|
1117
|
-
def initialize(contents)
|
1118
|
-
@contents = contents
|
1119
|
-
end
|
1120
|
-
|
1121
|
-
def format(q)
|
1122
|
-
q.text("[")
|
1123
|
-
q.group do
|
1124
|
-
q.indent do
|
1125
|
-
q.breakable_empty
|
1126
|
-
q.seplist(contents.parts, Separator.new) { |part| q.format(part) }
|
1127
|
-
q.if_break { q.text(",") } if q.trailing_comma?
|
1128
|
-
end
|
1129
|
-
q.breakable_empty
|
1130
|
-
end
|
1131
|
-
q.text("]")
|
1132
|
-
end
|
1133
|
-
end
|
1134
|
-
|
1135
1106
|
# This is a special formatter used if the array literal contains no values
|
1136
1107
|
# but _does_ contain comments. In this case we do some special formatting to
|
1137
1108
|
# make sure the comments gets indented properly.
|
@@ -1206,19 +1177,17 @@ module SyntaxTree
|
|
1206
1177
|
end
|
1207
1178
|
|
1208
1179
|
def format(q)
|
1209
|
-
if
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
QSymbolsFormatter.new(contents).format(q)
|
1216
|
-
return
|
1217
|
-
end
|
1180
|
+
if lbracket.comments.empty? && contents && contents.comments.empty? &&
|
1181
|
+
contents.parts.length > 1
|
1182
|
+
if qwords?
|
1183
|
+
QWordsFormatter.new(contents).format(q)
|
1184
|
+
return
|
1185
|
+
end
|
1218
1186
|
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1187
|
+
if qsymbols?
|
1188
|
+
QSymbolsFormatter.new(contents).format(q)
|
1189
|
+
return
|
1190
|
+
end
|
1222
1191
|
end
|
1223
1192
|
|
1224
1193
|
if empty_with_comments?
|
@@ -1250,39 +1219,24 @@ module SyntaxTree
|
|
1250
1219
|
private
|
1251
1220
|
|
1252
1221
|
def qwords?
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
part.
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
else
|
1264
|
-
false
|
1265
|
-
end
|
1222
|
+
contents.parts.all? do |part|
|
1223
|
+
case part
|
1224
|
+
when StringLiteral
|
1225
|
+
part.comments.empty? && part.parts.length == 1 &&
|
1226
|
+
part.parts.first.is_a?(TStringContent) &&
|
1227
|
+
!part.parts.first.value.match?(/[\s\[\]\\]/)
|
1228
|
+
when CHAR
|
1229
|
+
!part.value.match?(/[\[\]\\]/)
|
1230
|
+
else
|
1231
|
+
false
|
1266
1232
|
end
|
1233
|
+
end
|
1267
1234
|
end
|
1268
1235
|
|
1269
1236
|
def qsymbols?
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
part.is_a?(SymbolLiteral) && part.comments.empty?
|
1274
|
-
end
|
1275
|
-
end
|
1276
|
-
|
1277
|
-
def var_refs?(q)
|
1278
|
-
lbracket.comments.empty? && contents && contents.comments.empty? &&
|
1279
|
-
contents.parts.all? do |part|
|
1280
|
-
part.is_a?(VarRef) && part.comments.empty?
|
1281
|
-
end &&
|
1282
|
-
(
|
1283
|
-
contents.parts.sum { |part| part.value.value.length + 2 } >
|
1284
|
-
q.maxwidth * 2
|
1285
|
-
)
|
1237
|
+
contents.parts.all? do |part|
|
1238
|
+
part.is_a?(SymbolLiteral) && part.comments.empty?
|
1239
|
+
end
|
1286
1240
|
end
|
1287
1241
|
|
1288
1242
|
# If we have an empty array that contains only comments, then we're going
|
@@ -3068,6 +3022,10 @@ module SyntaxTree
|
|
3068
3022
|
end
|
3069
3023
|
end
|
3070
3024
|
end
|
3025
|
+
|
3026
|
+
def arity
|
3027
|
+
arguments&.arity || 0
|
3028
|
+
end
|
3071
3029
|
end
|
3072
3030
|
|
3073
3031
|
# Case represents the beginning of a case chain.
|
@@ -3481,6 +3439,10 @@ module SyntaxTree
|
|
3481
3439
|
arguments === other.arguments && block === other.block
|
3482
3440
|
end
|
3483
3441
|
|
3442
|
+
def arity
|
3443
|
+
arguments.arity
|
3444
|
+
end
|
3445
|
+
|
3484
3446
|
private
|
3485
3447
|
|
3486
3448
|
def align(q, node, &block)
|
@@ -3646,6 +3608,10 @@ module SyntaxTree
|
|
3646
3608
|
arguments === other.arguments && block === other.block
|
3647
3609
|
end
|
3648
3610
|
|
3611
|
+
def arity
|
3612
|
+
arguments&.arity || 0
|
3613
|
+
end
|
3614
|
+
|
3649
3615
|
private
|
3650
3616
|
|
3651
3617
|
def argument_alignment(q, doc)
|
@@ -4175,6 +4141,17 @@ module SyntaxTree
|
|
4175
4141
|
def endless?
|
4176
4142
|
!bodystmt.is_a?(BodyStmt)
|
4177
4143
|
end
|
4144
|
+
|
4145
|
+
def arity
|
4146
|
+
case params
|
4147
|
+
when Params
|
4148
|
+
params.arity
|
4149
|
+
when Paren
|
4150
|
+
params.contents.arity
|
4151
|
+
else
|
4152
|
+
0..0
|
4153
|
+
end
|
4154
|
+
end
|
4178
4155
|
end
|
4179
4156
|
|
4180
4157
|
# Defined represents the use of the +defined?+ operator. It can be used with
|
@@ -4362,6 +4339,15 @@ module SyntaxTree
|
|
4362
4339
|
opening.is_a?(Kw)
|
4363
4340
|
end
|
4364
4341
|
|
4342
|
+
def arity
|
4343
|
+
case block_var
|
4344
|
+
when BlockVar
|
4345
|
+
block_var.params.arity
|
4346
|
+
else
|
4347
|
+
0..0
|
4348
|
+
end
|
4349
|
+
end
|
4350
|
+
|
4365
4351
|
private
|
4366
4352
|
|
4367
4353
|
# If this is nested anywhere inside certain nodes, then we can't change
|
@@ -6153,7 +6139,7 @@ module SyntaxTree
|
|
6153
6139
|
module Ternaryable
|
6154
6140
|
class << self
|
6155
6141
|
def call(q, node)
|
6156
|
-
return false if ENV["STREE_FAST_FORMAT"]
|
6142
|
+
return false if ENV["STREE_FAST_FORMAT"] || q.disable_auto_ternary?
|
6157
6143
|
|
6158
6144
|
# If this is a conditional inside of a parentheses as the only content,
|
6159
6145
|
# then we don't want to transform it into a ternary. Presumably the user
|
@@ -6496,9 +6482,26 @@ module SyntaxTree
|
|
6496
6482
|
|
6497
6483
|
def format(q)
|
6498
6484
|
force_flat = [
|
6499
|
-
AliasNode,
|
6500
|
-
|
6501
|
-
|
6485
|
+
AliasNode,
|
6486
|
+
Assign,
|
6487
|
+
Break,
|
6488
|
+
Command,
|
6489
|
+
CommandCall,
|
6490
|
+
Heredoc,
|
6491
|
+
IfNode,
|
6492
|
+
IfOp,
|
6493
|
+
Lambda,
|
6494
|
+
MAssign,
|
6495
|
+
Next,
|
6496
|
+
OpAssign,
|
6497
|
+
RescueMod,
|
6498
|
+
ReturnNode,
|
6499
|
+
Super,
|
6500
|
+
Undef,
|
6501
|
+
UnlessNode,
|
6502
|
+
VoidStmt,
|
6503
|
+
YieldNode,
|
6504
|
+
ZSuper
|
6502
6505
|
]
|
6503
6506
|
|
6504
6507
|
if q.parent.is_a?(Paren) || force_flat.include?(truthy.class) ||
|
@@ -8325,6 +8328,29 @@ module SyntaxTree
|
|
8325
8328
|
keyword_rest === other.keyword_rest && block === other.block
|
8326
8329
|
end
|
8327
8330
|
|
8331
|
+
# Returns a range representing the possible number of arguments accepted
|
8332
|
+
# by this params node not including the block. For example:
|
8333
|
+
#
|
8334
|
+
# def foo(a, b = 1, c:, d: 2, &block)
|
8335
|
+
# ...
|
8336
|
+
# end
|
8337
|
+
#
|
8338
|
+
# has arity 2..4.
|
8339
|
+
#
|
8340
|
+
def arity
|
8341
|
+
optional_keywords = keywords.count { |_label, value| value }
|
8342
|
+
|
8343
|
+
lower_bound =
|
8344
|
+
requireds.length + posts.length + keywords.length - optional_keywords
|
8345
|
+
|
8346
|
+
upper_bound =
|
8347
|
+
if keyword_rest.nil? && rest.nil?
|
8348
|
+
lower_bound + optionals.length + optional_keywords
|
8349
|
+
end
|
8350
|
+
|
8351
|
+
lower_bound..upper_bound
|
8352
|
+
end
|
8353
|
+
|
8328
8354
|
private
|
8329
8355
|
|
8330
8356
|
def format_contents(q, parts)
|
@@ -11594,6 +11620,10 @@ module SyntaxTree
|
|
11594
11620
|
def access_control?
|
11595
11621
|
@access_control ||= %w[private protected public].include?(value.value)
|
11596
11622
|
end
|
11623
|
+
|
11624
|
+
def arity
|
11625
|
+
0
|
11626
|
+
end
|
11597
11627
|
end
|
11598
11628
|
|
11599
11629
|
# VoidStmt represents an empty lexical block of code.
|
data/lib/syntax_tree/parser.rb
CHANGED
@@ -1103,6 +1103,7 @@ module SyntaxTree
|
|
1103
1103
|
# :call-seq:
|
1104
1104
|
# on_comment: (String value) -> Comment
|
1105
1105
|
def on_comment(value)
|
1106
|
+
# char is the index of the # character in the source.
|
1106
1107
|
char = char_pos
|
1107
1108
|
location =
|
1108
1109
|
Location.token(
|
@@ -1112,8 +1113,24 @@ module SyntaxTree
|
|
1112
1113
|
size: value.size - 1
|
1113
1114
|
)
|
1114
1115
|
|
1115
|
-
|
1116
|
-
|
1116
|
+
# Loop backward in the source string, starting from the beginning of the
|
1117
|
+
# comment, and find the first character that is not a space or a tab. If
|
1118
|
+
# index is -1, this indicates that we've checked all of the characters
|
1119
|
+
# back to the start of the source, so this comment must be at the
|
1120
|
+
# beginning of the file.
|
1121
|
+
#
|
1122
|
+
# We are purposefully not using rindex or regular expressions here because
|
1123
|
+
# they check if there are invalid characters, which is actually possible
|
1124
|
+
# with the use of __END__ syntax.
|
1125
|
+
index = char - 1
|
1126
|
+
while index > -1 && (source[index] == "\t" || source[index] == " ")
|
1127
|
+
index -= 1
|
1128
|
+
end
|
1129
|
+
|
1130
|
+
# If we found a character that was not a space or a tab before the comment
|
1131
|
+
# and it's a newline, then this comment is inline. Otherwise, it stands on
|
1132
|
+
# its own and can be attached as its own node in the tree.
|
1133
|
+
inline = index != -1 && source[index] != "\n"
|
1117
1134
|
comment =
|
1118
1135
|
Comment.new(value: value.chomp, inline: inline, location: location)
|
1119
1136
|
|
data/lib/syntax_tree/version.rb
CHANGED
@@ -62,22 +62,26 @@ module SyntaxTree
|
|
62
62
|
"constant-from"
|
63
63
|
].freeze
|
64
64
|
|
65
|
-
attr_reader :
|
65
|
+
attr_reader :lines
|
66
66
|
|
67
|
-
def initialize(
|
68
|
-
@
|
67
|
+
def initialize(lines)
|
68
|
+
@lines = lines
|
69
69
|
end
|
70
70
|
|
71
71
|
def assemble
|
72
72
|
iseq = InstructionSequence.new("<main>", "<compiled>", 1, :top)
|
73
|
-
assemble_iseq(iseq,
|
73
|
+
assemble_iseq(iseq, lines)
|
74
74
|
|
75
75
|
iseq.compile!
|
76
76
|
iseq
|
77
77
|
end
|
78
78
|
|
79
|
-
def self.assemble(
|
80
|
-
new(
|
79
|
+
def self.assemble(source)
|
80
|
+
new(source.lines(chomp: true)).assemble
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.assemble_file(filepath)
|
84
|
+
new(File.readlines(filepath, chomp: true)).assemble
|
81
85
|
end
|
82
86
|
|
83
87
|
private
|
@@ -97,7 +97,7 @@ module SyntaxTree
|
|
97
97
|
clause << Next(Args([]))
|
98
98
|
when Leave
|
99
99
|
value = Args([clause.pop])
|
100
|
-
clause << (iseq.type
|
100
|
+
clause << (iseq.type != :top ? Break(value) : ReturnNode(value))
|
101
101
|
when OptAnd, OptDiv, OptEq, OptGE, OptGT, OptLE, OptLT, OptLTLT,
|
102
102
|
OptMinus, OptMod, OptMult, OptOr, OptPlus
|
103
103
|
left, right = clause.pop(2)
|