syntax_tree 5.1.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/dependabot.yml +4 -0
- data/.github/workflows/auto-merge.yml +1 -1
- data/.github/workflows/gh-pages.yml +1 -1
- data/.github/workflows/main.yml +5 -2
- data/.gitmodules +9 -0
- data/.rubocop.yml +11 -1
- data/CHANGELOG.md +29 -1
- data/Gemfile.lock +10 -10
- data/README.md +1 -0
- data/Rakefile +7 -0
- data/exe/yarv +63 -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 +146 -107
- data/lib/syntax_tree/parser.rb +20 -3
- 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 +17 -13
- data/lib/syntax_tree/yarv/bf.rb +13 -16
- data/lib/syntax_tree/yarv/compiler.rb +25 -14
- data/lib/syntax_tree/yarv/decompiler.rb +10 -1
- data/lib/syntax_tree/yarv/disassembler.rb +3 -2
- data/lib/syntax_tree/yarv/instruction_sequence.rb +191 -87
- data/lib/syntax_tree/yarv/instructions.rb +1011 -42
- data/lib/syntax_tree/yarv/legacy.rb +59 -3
- data/lib/syntax_tree/yarv/vm.rb +628 -0
- data/lib/syntax_tree/yarv.rb +0 -269
- data/lib/syntax_tree.rb +16 -0
- metadata +9 -3
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
|
@@ -3001,16 +2955,25 @@ module SyntaxTree
|
|
3001
2955
|
else
|
3002
2956
|
q.format(message)
|
3003
2957
|
|
3004
|
-
|
3005
|
-
|
3006
|
-
|
3007
|
-
|
3008
|
-
|
3009
|
-
|
3010
|
-
|
3011
|
-
|
3012
|
-
|
3013
|
-
|
2958
|
+
# Note that this explicitly leaves parentheses in place even if they are
|
2959
|
+
# empty. There are two reasons we would need to do this. The first is if
|
2960
|
+
# we're calling something that looks like a constant, as in:
|
2961
|
+
#
|
2962
|
+
# Foo()
|
2963
|
+
#
|
2964
|
+
# In this case if we remove the parentheses then this becomes a constant
|
2965
|
+
# reference and not a method call. The second is if we're calling a
|
2966
|
+
# method that is the same name as a local variable that is in scope, as
|
2967
|
+
# in:
|
2968
|
+
#
|
2969
|
+
# foo = foo()
|
2970
|
+
#
|
2971
|
+
# In this case we have to keep the parentheses or else it treats this
|
2972
|
+
# like assigning nil to the local variable. Note that we could attempt
|
2973
|
+
# to be smarter about this by tracking the local variables that are in
|
2974
|
+
# scope, but for now it's simpler and more efficient to just leave the
|
2975
|
+
# parentheses in place.
|
2976
|
+
q.format(arguments) if arguments
|
3014
2977
|
end
|
3015
2978
|
end
|
3016
2979
|
|
@@ -3059,6 +3022,10 @@ module SyntaxTree
|
|
3059
3022
|
end
|
3060
3023
|
end
|
3061
3024
|
end
|
3025
|
+
|
3026
|
+
def arity
|
3027
|
+
arguments&.arity || 0
|
3028
|
+
end
|
3062
3029
|
end
|
3063
3030
|
|
3064
3031
|
# Case represents the beginning of a case chain.
|
@@ -3472,6 +3439,10 @@ module SyntaxTree
|
|
3472
3439
|
arguments === other.arguments && block === other.block
|
3473
3440
|
end
|
3474
3441
|
|
3442
|
+
def arity
|
3443
|
+
arguments.arity
|
3444
|
+
end
|
3445
|
+
|
3475
3446
|
private
|
3476
3447
|
|
3477
3448
|
def align(q, node, &block)
|
@@ -3637,6 +3608,10 @@ module SyntaxTree
|
|
3637
3608
|
arguments === other.arguments && block === other.block
|
3638
3609
|
end
|
3639
3610
|
|
3611
|
+
def arity
|
3612
|
+
arguments&.arity || 0
|
3613
|
+
end
|
3614
|
+
|
3640
3615
|
private
|
3641
3616
|
|
3642
3617
|
def argument_alignment(q, doc)
|
@@ -4166,6 +4141,17 @@ module SyntaxTree
|
|
4166
4141
|
def endless?
|
4167
4142
|
!bodystmt.is_a?(BodyStmt)
|
4168
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
|
4169
4155
|
end
|
4170
4156
|
|
4171
4157
|
# Defined represents the use of the +defined?+ operator. It can be used with
|
@@ -4353,6 +4339,15 @@ module SyntaxTree
|
|
4353
4339
|
opening.is_a?(Kw)
|
4354
4340
|
end
|
4355
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
|
+
|
4356
4351
|
private
|
4357
4352
|
|
4358
4353
|
# If this is nested anywhere inside certain nodes, then we can't change
|
@@ -6144,7 +6139,7 @@ module SyntaxTree
|
|
6144
6139
|
module Ternaryable
|
6145
6140
|
class << self
|
6146
6141
|
def call(q, node)
|
6147
|
-
return false if ENV["STREE_FAST_FORMAT"]
|
6142
|
+
return false if ENV["STREE_FAST_FORMAT"] || q.disable_auto_ternary?
|
6148
6143
|
|
6149
6144
|
# If this is a conditional inside of a parentheses as the only content,
|
6150
6145
|
# then we don't want to transform it into a ternary. Presumably the user
|
@@ -6487,9 +6482,26 @@ module SyntaxTree
|
|
6487
6482
|
|
6488
6483
|
def format(q)
|
6489
6484
|
force_flat = [
|
6490
|
-
AliasNode,
|
6491
|
-
|
6492
|
-
|
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
|
6493
6505
|
]
|
6494
6506
|
|
6495
6507
|
if q.parent.is_a?(Paren) || force_flat.include?(truthy.class) ||
|
@@ -8316,6 +8328,29 @@ module SyntaxTree
|
|
8316
8328
|
keyword_rest === other.keyword_rest && block === other.block
|
8317
8329
|
end
|
8318
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
|
+
|
8319
8354
|
private
|
8320
8355
|
|
8321
8356
|
def format_contents(q, parts)
|
@@ -11585,6 +11620,10 @@ module SyntaxTree
|
|
11585
11620
|
def access_control?
|
11586
11621
|
@access_control ||= %w[private protected public].include?(value.value)
|
11587
11622
|
end
|
11623
|
+
|
11624
|
+
def arity
|
11625
|
+
0
|
11626
|
+
end
|
11588
11627
|
end
|
11589
11628
|
|
11590
11629
|
# VoidStmt represents an empty lexical block of code.
|
data/lib/syntax_tree/parser.rb
CHANGED
@@ -53,7 +53,7 @@ module SyntaxTree
|
|
53
53
|
# there's a BOM at the beginning of the file, which is the reason we need
|
54
54
|
# to compare it to 0 here.
|
55
55
|
def [](byteindex)
|
56
|
-
indices[byteindex
|
56
|
+
indices[[byteindex, 0].max]
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
@@ -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
|
-
iseq = InstructionSequence.new(
|
73
|
-
assemble_iseq(iseq,
|
72
|
+
iseq = InstructionSequence.new("<main>", "<compiled>", 1, :top)
|
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
|
@@ -138,7 +142,7 @@ module SyntaxTree
|
|
138
142
|
name = parse_symbol(name_value)
|
139
143
|
flags = parse_number(flags_value)
|
140
144
|
|
141
|
-
class_iseq = iseq.class_child_iseq(name.to_s,
|
145
|
+
class_iseq = iseq.class_child_iseq(name.to_s, 1)
|
142
146
|
assemble_iseq(class_iseq, body)
|
143
147
|
iseq.defineclass(name, class_iseq, flags)
|
144
148
|
when "defined"
|
@@ -153,7 +157,7 @@ module SyntaxTree
|
|
153
157
|
line_index += body.length
|
154
158
|
|
155
159
|
name = parse_symbol(operands)
|
156
|
-
method_iseq = iseq.method_child_iseq(name.to_s,
|
160
|
+
method_iseq = iseq.method_child_iseq(name.to_s, 1)
|
157
161
|
assemble_iseq(method_iseq, body)
|
158
162
|
|
159
163
|
iseq.definemethod(name, method_iseq)
|
@@ -162,7 +166,7 @@ module SyntaxTree
|
|
162
166
|
line_index += body.length
|
163
167
|
|
164
168
|
name = parse_symbol(operands)
|
165
|
-
method_iseq = iseq.method_child_iseq(name.to_s,
|
169
|
+
method_iseq = iseq.method_child_iseq(name.to_s, 1)
|
166
170
|
|
167
171
|
assemble_iseq(method_iseq, body)
|
168
172
|
iseq.definesmethod(name, method_iseq)
|
@@ -221,7 +225,7 @@ module SyntaxTree
|
|
221
225
|
body = parse_nested(lines[line_index..])
|
222
226
|
line_index += body.length
|
223
227
|
|
224
|
-
block_iseq = iseq.block_child_iseq(
|
228
|
+
block_iseq = iseq.block_child_iseq(1)
|
225
229
|
assemble_iseq(block_iseq, body)
|
226
230
|
block_iseq
|
227
231
|
end
|
@@ -249,7 +253,7 @@ module SyntaxTree
|
|
249
253
|
body = parse_nested(lines[line_index..])
|
250
254
|
line_index += body.length
|
251
255
|
|
252
|
-
block_iseq = iseq.block_child_iseq(
|
256
|
+
block_iseq = iseq.block_child_iseq(1)
|
253
257
|
assemble_iseq(block_iseq, body)
|
254
258
|
block_iseq
|
255
259
|
end
|
@@ -354,7 +358,7 @@ module SyntaxTree
|
|
354
358
|
body = parse_nested(lines[line_index..])
|
355
359
|
line_index += body.length
|
356
360
|
|
357
|
-
block_iseq = iseq.block_child_iseq(
|
361
|
+
block_iseq = iseq.block_child_iseq(1)
|
358
362
|
assemble_iseq(block_iseq, body)
|
359
363
|
block_iseq
|
360
364
|
end
|
data/lib/syntax_tree/yarv/bf.rb
CHANGED
@@ -13,7 +13,7 @@ module SyntaxTree
|
|
13
13
|
|
14
14
|
def compile
|
15
15
|
# Set up the top-level instruction sequence that will be returned.
|
16
|
-
iseq = InstructionSequence.new(
|
16
|
+
iseq = InstructionSequence.new("<compiled>", "<compiled>", 1, :top)
|
17
17
|
|
18
18
|
# Set up the $tape global variable that will hold our state.
|
19
19
|
iseq.duphash({ 0 => 0 })
|
@@ -80,19 +80,6 @@ module SyntaxTree
|
|
80
80
|
|
81
81
|
private
|
82
82
|
|
83
|
-
# This is the location of the top instruction sequence, derived from the
|
84
|
-
# source string.
|
85
|
-
def location
|
86
|
-
Location.new(
|
87
|
-
start_line: 1,
|
88
|
-
start_char: 0,
|
89
|
-
start_column: 0,
|
90
|
-
end_line: source.count("\n") + 1,
|
91
|
-
end_char: source.size,
|
92
|
-
end_column: source.size - (source.rindex("\n") || 0) - 1
|
93
|
-
)
|
94
|
-
end
|
95
|
-
|
96
83
|
# $tape[$cursor] += value
|
97
84
|
def change_by(iseq, value)
|
98
85
|
iseq.getglobal(:$tape)
|
@@ -111,6 +98,7 @@ module SyntaxTree
|
|
111
98
|
end
|
112
99
|
|
113
100
|
iseq.send(YARV.calldata(:[]=, 2))
|
101
|
+
iseq.pop
|
114
102
|
end
|
115
103
|
|
116
104
|
# $cursor += value
|
@@ -138,6 +126,7 @@ module SyntaxTree
|
|
138
126
|
iseq.send(YARV.calldata(:chr))
|
139
127
|
|
140
128
|
iseq.send(YARV.calldata(:putc, 1))
|
129
|
+
iseq.pop
|
141
130
|
end
|
142
131
|
|
143
132
|
# $tape[$cursor] = $stdin.getc.ord
|
@@ -150,6 +139,7 @@ module SyntaxTree
|
|
150
139
|
iseq.send(YARV.calldata(:ord))
|
151
140
|
|
152
141
|
iseq.send(YARV.calldata(:[]=, 2))
|
142
|
+
iseq.pop
|
153
143
|
end
|
154
144
|
|
155
145
|
# unless $tape[$cursor] == 0
|
@@ -164,14 +154,21 @@ module SyntaxTree
|
|
164
154
|
|
165
155
|
iseq.putobject(0)
|
166
156
|
iseq.send(YARV.calldata(:==, 1))
|
167
|
-
iseq.
|
157
|
+
iseq.branchif(end_label)
|
168
158
|
|
169
159
|
[start_label, end_label]
|
170
160
|
end
|
171
161
|
|
172
162
|
# Jump back to the start of the loop.
|
173
163
|
def loop_end(iseq, start_label, end_label)
|
174
|
-
iseq.
|
164
|
+
iseq.getglobal(:$tape)
|
165
|
+
iseq.getglobal(:$cursor)
|
166
|
+
iseq.send(YARV.calldata(:[], 1))
|
167
|
+
|
168
|
+
iseq.putobject(0)
|
169
|
+
iseq.send(YARV.calldata(:==, 1))
|
170
|
+
iseq.branchunless(start_label)
|
171
|
+
|
175
172
|
iseq.push(end_label)
|
176
173
|
end
|
177
174
|
end
|