syntax_tree 5.1.0 → 5.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|