peruby 0.1.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.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +35 -0
  3. data/CHANGELOG.md +15 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +51 -0
  6. data/Rakefile +92 -0
  7. data/bin/peruby +9 -0
  8. data/doc/COMPAT.md +54 -0
  9. data/doc/CONTRIBUTING.md +21 -0
  10. data/doc/DESIGN.md +25 -0
  11. data/doc/INCOMPATIBILITIES.md +30 -0
  12. data/doc/PERF.md +51 -0
  13. data/doc/ROADMAP.md +18 -0
  14. data/examples/hello.pl +1 -0
  15. data/examples/json.pl +2 -0
  16. data/examples/object.pl +5 -0
  17. data/examples/word_count.pl +6 -0
  18. data/lib/peruby/cli.rb +224 -0
  19. data/lib/peruby/compile_unit.rb +103 -0
  20. data/lib/peruby/compiler.rb +224 -0
  21. data/lib/peruby/errors.rb +40 -0
  22. data/lib/peruby/lexer/heredoc.rb +8 -0
  23. data/lib/peruby/lexer/keywords.rb +63 -0
  24. data/lib/peruby/lexer/number.rb +32 -0
  25. data/lib/peruby/lexer/quote_like.rb +72 -0
  26. data/lib/peruby/lexer/source_scanner.rb +104 -0
  27. data/lib/peruby/lexer/state.rb +46 -0
  28. data/lib/peruby/lexer/structure_scanner.rb +149 -0
  29. data/lib/peruby/lexer/term_scanner.rb +301 -0
  30. data/lib/peruby/lexer/token.rb +16 -0
  31. data/lib/peruby/lexer.rb +123 -0
  32. data/lib/peruby/node.rb +88 -0
  33. data/lib/peruby/op/assign.rb +128 -0
  34. data/lib/peruby/op/builtin.rb +903 -0
  35. data/lib/peruby/op/call.rb +378 -0
  36. data/lib/peruby/op/control.rb +256 -0
  37. data/lib/peruby/op/element.rb +136 -0
  38. data/lib/peruby/op/expression.rb +342 -0
  39. data/lib/peruby/op/io.rb +113 -0
  40. data/lib/peruby/op/list.rb +102 -0
  41. data/lib/peruby/op/literal.rb +84 -0
  42. data/lib/peruby/op/loop.rb +158 -0
  43. data/lib/peruby/op/regexp.rb +288 -0
  44. data/lib/peruby/op/variable.rb +534 -0
  45. data/lib/peruby/op.rb +47 -0
  46. data/lib/peruby/parser/grammar.rb +5797 -0
  47. data/lib/peruby/parser/grammar.y +576 -0
  48. data/lib/peruby/parser.rb +14 -0
  49. data/lib/peruby/runtime/code.rb +21 -0
  50. data/lib/peruby/runtime/conv.rb +140 -0
  51. data/lib/peruby/runtime/directory_handle.rb +18 -0
  52. data/lib/peruby/runtime/env.rb +129 -0
  53. data/lib/peruby/runtime/glob.rb +24 -0
  54. data/lib/peruby/runtime/interpolation.rb +223 -0
  55. data/lib/peruby/runtime/io_handle.rb +37 -0
  56. data/lib/peruby/runtime/local_stack.rb +90 -0
  57. data/lib/peruby/runtime/match_state.rb +62 -0
  58. data/lib/peruby/runtime/module_loader.rb +133 -0
  59. data/lib/peruby/runtime/mro.rb +94 -0
  60. data/lib/peruby/runtime/perl_array.rb +81 -0
  61. data/lib/peruby/runtime/perl_hash.rb +57 -0
  62. data/lib/peruby/runtime/ref.rb +43 -0
  63. data/lib/peruby/runtime/regexp_compiler.rb +75 -0
  64. data/lib/peruby/runtime/scalar.rb +27 -0
  65. data/lib/peruby/runtime/sprintf.rb +54 -0
  66. data/lib/peruby/runtime/stash.rb +50 -0
  67. data/lib/peruby/runtime/test_builder.rb +47 -0
  68. data/lib/peruby/runtime.rb +325 -0
  69. data/lib/peruby/validator.rb +236 -0
  70. data/lib/peruby/version.rb +5 -0
  71. data/lib/peruby.rb +31 -0
  72. data/t/00-basic.t +5 -0
  73. data/t/lib/MiniTest.pm +22 -0
  74. metadata +130 -0
@@ -0,0 +1,534 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Peruby
4
+ module Op
5
+ # Variable read and lvalue lookup.
6
+ class Variable < Base
7
+ def initialize(sigil, name)
8
+ @sigil = sigil
9
+ @name = name
10
+ @key = [sigil, name].freeze
11
+ @dynamic_match = (sigil == :scalar && name.match?(/\A(?:\d+|[&`'+])\z/)) ||
12
+ (%i[array hash].include?(sigil) && %w[- +].include?(name))
13
+ end
14
+
15
+ def run(env, context)
16
+ value = lookup(env)
17
+ result(value, context)
18
+ end
19
+
20
+ def to_callable
21
+ lookup = lookup_callable
22
+ lambda do |env, context|
23
+ value = lookup.call(env)
24
+ return value.list if context == :list && value.is_a?(PerlHash)
25
+ return value.list if context == :list && value.is_a?(PerlArray)
26
+ return value.cells.dup if context == :list && value.respond_to?(:cells)
27
+ return [value] if context == :list
28
+ return value.size if @sigil == :array
29
+ return value.cells.size if @sigil == :hash
30
+
31
+ value.value
32
+ end
33
+ end
34
+
35
+ def scalar_callable
36
+ lookup = lookup_callable
37
+ sigil = @sigil
38
+ lambda do |env|
39
+ value = lookup.call(env)
40
+ next value.size if sigil == :array
41
+ next value.cells.size if sigil == :hash
42
+
43
+ value.value
44
+ end
45
+ end
46
+
47
+ def lvalue_callable = lookup_callable
48
+
49
+ def first_cell_callable
50
+ lookup = lookup_callable
51
+ ->(env) { (value = lookup.call(env)).respond_to?(:cells) ? value.cells.first : value }
52
+ end
53
+
54
+ def result(value, context)
55
+ return value.list if context == :list && value.is_a?(PerlHash)
56
+ return value.list if context == :list && value.is_a?(PerlArray)
57
+ return value.cells.dup if context == :list && value.respond_to?(:cells)
58
+ return [value] if context == :list
59
+ return value.size if @sigil == :array
60
+ return value.cells.size if @sigil == :hash
61
+
62
+ value.get
63
+ end
64
+
65
+ def lvalue(env, context)
66
+ value = lookup(env)
67
+ context == :list && value.respond_to?(:cells) ? value.cells : value
68
+ end
69
+
70
+ def argument_cells(env)
71
+ value = lookup(env)
72
+ return value.list if value.is_a?(PerlHash)
73
+
74
+ return value.list if value.is_a?(PerlArray)
75
+
76
+ value.respond_to?(:cells) ? value.cells.dup : [value]
77
+ end
78
+
79
+ def first_cell(env)
80
+ value = lookup(env)
81
+ value.respond_to?(:cells) ? value.cells.first : value
82
+ end
83
+
84
+ def aggregate_assignment? = %i[array hash].include?(@sigil)
85
+
86
+ def localize(env, stack)
87
+ value = lookup(env)
88
+ return stack.localize_scalar(value) if @sigil == :scalar
89
+ return stack.localize_array_value(value) if @sigil == :array
90
+ return stack.localize_hash_value(value) if @sigil == :hash
91
+ return stack.localize_entire_glob(value) if @sigil == :glob
92
+
93
+ raise PerlError, "Can't localize #{@sigil} variable"
94
+ end
95
+
96
+ def assign_values(env, values)
97
+ target = lookup(env)
98
+ if @sigil == :array
99
+ target.cells.replace(values.map { |value| Scalar.new(value) })
100
+ else
101
+ target.cells.clear
102
+ values.each_slice(2) { |key, value| target.slot(Conv.to_str(key)).set(value) }
103
+ end
104
+ target
105
+ end
106
+
107
+ def aggregate(env, kind, dereference: false)
108
+ return dereference_aggregate(env, kind) if dereference
109
+
110
+ kind == @sigil ? lookup(env) : env.fetch(kind, @name)
111
+ end
112
+
113
+ def aggregate_callable(kind, dereference: false)
114
+ return ->(env) { aggregate(env, kind, dereference:) } if dereference || kind != @sigil
115
+
116
+ lookup_callable
117
+ end
118
+
119
+ private
120
+
121
+ def lookup_callable
122
+ key = @key
123
+ return ->(env) { env.fetch_key(key) } if @dynamic_match
124
+
125
+ cached_env = cached_generation = cached_value = nil
126
+ lambda do |env|
127
+ generation = env.generation
128
+ unless cached_env.equal?(env) && cached_generation == generation
129
+ cached_env = env
130
+ cached_generation = generation
131
+ cached_value = env.fetch_key(key)
132
+ end
133
+ cached_value
134
+ end
135
+ end
136
+
137
+ def lookup(env)
138
+ return env.fetch_key(@key) if @dynamic_match
139
+ return @cached_value if @cached_env.equal?(env) && @cached_generation == env.generation
140
+
141
+ @cached_env = env
142
+ @cached_generation = env.generation
143
+ @cached_value = env.fetch_key(@key)
144
+ end
145
+
146
+ def dereference_aggregate(env, kind)
147
+ expected = kind == :array ? PerlArray : PerlHash
148
+ cell = lookup(env)
149
+ cell.set(Ref.new(expected.new)) if cell.is_a?(Scalar) && cell.get.nil?
150
+ value = cell.is_a?(Scalar) ? cell.get : cell
151
+ raise PerlError, "Not a #{kind.to_s.upcase} reference" unless value.is_a?(Ref) && value.target.is_a?(expected)
152
+
153
+ value.target
154
+ end
155
+ end
156
+
157
+ # Explicit scalar/array/hash/code dereference.
158
+ class Dereference < Base
159
+ def initialize(kind, name)
160
+ @kind = kind
161
+ @name = name
162
+ end
163
+
164
+ def run(env, context)
165
+ value = target(env)
166
+ return value.get if @kind == :scalar && context != :list
167
+ return [value] if @kind == :scalar
168
+ return value.list if context == :list && value.is_a?(PerlHash)
169
+ return value.list if context == :list && value.is_a?(PerlArray)
170
+ return value.cells.dup if context == :list && value.respond_to?(:cells)
171
+ return value.size if value.respond_to?(:size)
172
+
173
+ value
174
+ end
175
+
176
+ def lvalue(env, _context)
177
+ target(env)
178
+ end
179
+
180
+ def aggregate(env, kind, dereference: false)
181
+ raise PerlError, 'Invalid nested dereference' if dereference
182
+
183
+ value = @kind == :scalar ? referenced_target(env) : target(env)
184
+ expected = kind == :array ? PerlArray : PerlHash
185
+ raise PerlError, "Not a #{kind.to_s.upcase} reference" unless value.is_a?(expected)
186
+
187
+ value
188
+ end
189
+
190
+ private
191
+
192
+ def target(env)
193
+ target = referenced_target(env)
194
+ expected = { scalar: Scalar, array: PerlArray, hash: PerlHash, code: Code }.fetch(@kind)
195
+ raise PerlError, "Not a #{@kind.to_s.upcase} reference" unless target.is_a?(expected)
196
+
197
+ target
198
+ end
199
+
200
+ def referenced_target(env)
201
+ value = env.fetch(:scalar, @name).get
202
+ return value.target if value.is_a?(Ref)
203
+ return symbolic_target(env, value) if value.is_a?(String)
204
+
205
+ raise PerlError, 'Not a reference'
206
+ end
207
+
208
+ def symbolic_target(env, name)
209
+ raise PerlError, "Can't use string (#{name}) as a reference while strict refs in use" if env.strict_refs?
210
+
211
+ env.fetch(@kind, name)
212
+ end
213
+ end
214
+
215
+ # Dereference whose operand is an arbitrary expression, such as @{$h{list}}.
216
+ class DereferenceExpression < Base
217
+ def initialize(kind, expression)
218
+ @kind = kind
219
+ @expression = expression
220
+ end
221
+
222
+ def run(env, context)
223
+ value = target(env)
224
+ return value.get if @kind == :scalar && context != :list
225
+ return [value] if @kind == :scalar
226
+ return value.list if context == :list && value.is_a?(PerlHash)
227
+ return value.list if context == :list && value.is_a?(PerlArray)
228
+ return value.cells.dup if context == :list && value.respond_to?(:cells)
229
+ return value.size if value.respond_to?(:size)
230
+
231
+ value
232
+ end
233
+
234
+ def lvalue(env, _context)
235
+ target(env)
236
+ end
237
+
238
+ def aggregate(env, kind, dereference: false)
239
+ raise PerlError, 'Invalid nested dereference' if dereference
240
+
241
+ value = target(env)
242
+ expected = kind == :array ? PerlArray : PerlHash
243
+ raise PerlError, "Not a #{kind.to_s.upcase} reference" unless value.is_a?(expected)
244
+
245
+ value
246
+ end
247
+
248
+ private
249
+
250
+ def target(env)
251
+ expected = { scalar: Scalar, array: PerlArray, hash: PerlHash, code: Code }.fetch(@kind)
252
+ cell = begin
253
+ @expression.lvalue(env, :scalar)
254
+ rescue PerlError
255
+ nil
256
+ end
257
+ cell.set(Ref.new(expected.new)) if cell && cell.get.nil? && %i[array hash].include?(@kind)
258
+ value = cell ? cell.get : @expression.run(env, :scalar)
259
+
260
+ resolve_target(env, value, expected)
261
+ end
262
+
263
+ def resolve_target(env, value, expected)
264
+ target = value.target if value.is_a?(Ref)
265
+ if value.is_a?(String)
266
+ raise PerlError, "Can't use string (#{value}) as a reference while strict refs in use" if env.strict_refs?
267
+
268
+ target = env.fetch(@kind, value)
269
+ end
270
+ raise PerlError, 'Not a reference' unless target.is_a?(expected)
271
+
272
+ target
273
+ end
274
+ end
275
+
276
+ # Lexical variable declaration.
277
+ class Declare < Base
278
+ def initialize(sigil, name, initializer, predeclared = nil)
279
+ @sigil = sigil
280
+ @name = name
281
+ @initializer = initializer
282
+ @predeclared = predeclared
283
+ end
284
+
285
+ def run(env, context)
286
+ values = @initializer&.run(env, @sigil == :scalar ? :scalar : :list)
287
+ target = declaration_target(env)
288
+ assign(target, values) if @initializer
289
+ return Array(target.respond_to?(:cells) ? target.cells : target) if context == :list
290
+
291
+ declaration_value(target)
292
+ end
293
+
294
+ def to_callable
295
+ initializer = @initializer&.to_callable
296
+ scalar_initializer = @initializer.scalar_callable if @initializer.respond_to?(:scalar_callable)
297
+ lambda do |env, context|
298
+ values = if @sigil == :scalar && scalar_initializer
299
+ scalar_initializer.call(env)
300
+ else
301
+ initializer&.call(env, @sigil == :scalar ? :scalar : :list)
302
+ end
303
+ target = declaration_target(env)
304
+ assign(target, values) if initializer
305
+ next Array(target.respond_to?(:cells) ? target.cells : target) if context == :list
306
+
307
+ declaration_value(target)
308
+ end
309
+ end
310
+
311
+ def lvalue(env, _context)
312
+ env.fetch(@sigil, @name)
313
+ end
314
+
315
+ private
316
+
317
+ def declaration_target(env)
318
+ return env.declare(@sigil, @name) unless @predeclared
319
+
320
+ env.bind(@sigil, @name, @predeclared).tap { @predeclared = nil }
321
+ end
322
+
323
+ def assign(target, values)
324
+ return target.set(values) if target.is_a?(Scalar)
325
+ return target.cells.replace(values.map(&:copy)) if target.is_a?(PerlArray)
326
+
327
+ target.cells.clear
328
+ values.each_slice(2) { |key, value| target.slot(Conv.to_str(key&.get)).set(value&.get) }
329
+ end
330
+
331
+ def declaration_value(target)
332
+ target.is_a?(Scalar) ? target.get : target.cells.size
333
+ end
334
+ end
335
+
336
+ # Persistent lexical declaration, scoped independently for each closure.
337
+ class State < Base
338
+ def initialize(sigil, name, initializer, predeclared = nil)
339
+ @sigil = sigil
340
+ @name = name
341
+ @initializer = initializer
342
+ @predeclared = predeclared
343
+ @values = ObjectSpace::WeakMap.new
344
+ end
345
+
346
+ def run(env, context)
347
+ owner = env.state_owner || env.runtime
348
+ value = @values[owner]
349
+ unless value
350
+ value = initial_value(owner)
351
+ initialize_value(value, env)
352
+ @values[owner] = value
353
+ end
354
+ env.bind(@sigil, @name, value)
355
+ return Array(value.respond_to?(:cells) ? value.cells : value) if context == :list
356
+
357
+ scalar_value(value)
358
+ end
359
+
360
+ private
361
+
362
+ def initial_value(owner)
363
+ value = @predeclared
364
+ @predeclared = nil
365
+ # perlsub: cloned anonymous closures do not retain the compile-time state pad.
366
+ return value if value && (!owner.is_a?(Code) || owner.name)
367
+
368
+ empty_value
369
+ end
370
+
371
+ def empty_value
372
+ { scalar: -> { Scalar.new }, array: -> { PerlArray.new }, hash: -> { PerlHash.new } }.fetch(@sigil).call
373
+ end
374
+
375
+ def initialize_value(target, env)
376
+ return unless @initializer
377
+
378
+ values = @initializer.run(env, @sigil == :scalar ? :scalar : :list)
379
+ return target.set(values) if target.is_a?(Scalar)
380
+ return target.cells.replace(values.map(&:copy)) if target.is_a?(PerlArray)
381
+
382
+ target.cells.clear
383
+ values.each_slice(2) { |key, value| target.slot(Conv.to_str(key&.get)).set(value&.get) }
384
+ end
385
+
386
+ def scalar_value(value)
387
+ value.is_a?(Scalar) ? value.get : value.cells.size
388
+ end
389
+ end
390
+
391
+ # Destructuring lexical declaration.
392
+ class DeclareList < Base
393
+ def initialize(variables, initializer, predeclared = nil)
394
+ @variables = variables
395
+ @initializer = initializer
396
+ @predeclared = predeclared
397
+ end
398
+
399
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
400
+ def run(env, context)
401
+ return declare_scalar(env, context) if @variables.length == 1 && @variables.first.first == :scalar &&
402
+ @initializer.respond_to?(:first_cell)
403
+
404
+ values = @initializer&.run(env, :list) || []
405
+ targets = declaration_targets(env)
406
+ return context == :list ? targets : 0 unless @initializer
407
+
408
+ index = 0
409
+ targets.each do |target|
410
+ if target.is_a?(Scalar)
411
+ target.set(values[index]&.get)
412
+ index += 1
413
+ elsif target.is_a?(PerlArray)
414
+ target.cells.replace(values.drop(index).map(&:copy))
415
+ index = values.length
416
+ else
417
+ target.cells.clear
418
+ values.drop(index).each_slice(2) do |key, value|
419
+ target.slot(Conv.to_str(key&.get)).set(value&.get)
420
+ end
421
+ index = values.length
422
+ end
423
+ end
424
+ context == :list ? targets : values.size
425
+ end
426
+
427
+ def to_callable
428
+ return scalar_declaration_callable if @variables.length == 1 && @variables.first.first == :scalar &&
429
+ @initializer.respond_to?(:first_cell_callable)
430
+
431
+ initializer = @initializer&.to_callable
432
+ lambda do |env, context|
433
+ values = initializer ? initializer.call(env, :list) : []
434
+ declare_values(env, context, values)
435
+ end
436
+ end
437
+
438
+ private
439
+
440
+ def scalar_declaration_callable
441
+ source = @initializer.first_cell_callable
442
+ lambda do |env, context|
443
+ target = declaration_targets(env).first
444
+ target.set(source.call(env)&.value)
445
+ context == :list ? [target] : 1
446
+ end
447
+ end
448
+
449
+ def declare_values(env, context, values)
450
+ targets = declaration_targets(env)
451
+ return context == :list ? targets : 0 unless @initializer
452
+
453
+ index = 0
454
+ targets.each do |target|
455
+ if target.is_a?(Scalar)
456
+ target.set(values[index]&.value)
457
+ index += 1
458
+ elsif target.is_a?(PerlArray)
459
+ target.cells.replace(values.drop(index).map(&:copy))
460
+ index = values.length
461
+ else
462
+ target.cells.clear
463
+ values.drop(index).each_slice(2) { |key, value| target.slot(Conv.to_str(key&.value)).set(value&.value) }
464
+ index = values.length
465
+ end
466
+ end
467
+ context == :list ? targets : values.size
468
+ end
469
+
470
+ def declare_scalar(env, context)
471
+ source = @initializer.first_cell(env)
472
+ target = declaration_targets(env).first
473
+ target.set(source&.get)
474
+ context == :list ? [target] : 1
475
+ end
476
+
477
+ def declaration_targets(env)
478
+ return @variables.map { |sigil, name| env.declare(sigil, name) } unless @predeclared
479
+
480
+ targets = @variables.zip(@predeclared).map { |(sigil, name), value| env.bind(sigil, name, value) }
481
+ @predeclared = nil
482
+ targets
483
+ end
484
+ end
485
+
486
+ # Package variable declaration through our.
487
+ class GlobalDeclare < Base
488
+ def initialize(sigil, name, initializer)
489
+ @sigil = sigil
490
+ @name = name
491
+ @initializer = initializer
492
+ end
493
+
494
+ def run(env, context)
495
+ target = env.fetch(@sigil, @name)
496
+ bind_alias(env, target)
497
+ return target.get unless @initializer
498
+
499
+ values = @initializer.run(env, @sigil == :scalar ? :scalar : :list)
500
+ if target.is_a?(Scalar)
501
+ target.set(values)
502
+ elsif target.is_a?(PerlArray)
503
+ target.cells.replace(values.map(&:copy))
504
+ else
505
+ target.cells.clear
506
+ values.each_slice(2) { |key, value| target.slot(Conv.to_str(key&.get)).set(value&.get) }
507
+ end
508
+ context == :list && target.respond_to?(:list) ? target.list : values
509
+ end
510
+
511
+ private
512
+
513
+ def bind_alias(env, target)
514
+ name = @name.rpartition('::').last
515
+ env.bind(@sigil, name, target) unless name == @name
516
+ end
517
+ end
518
+
519
+ # Dynamically localizes a package variable or aggregate element.
520
+ class Local < Base
521
+ def initialize(target, assignment)
522
+ @target = target
523
+ @assignment = assignment
524
+ end
525
+
526
+ def run(env, context)
527
+ @target.localize(env, env.runtime.local_stack)
528
+ return @assignment.run(env, context) if @assignment
529
+
530
+ nil
531
+ end
532
+ end
533
+ end
534
+ end
data/lib/peruby/op.rb ADDED
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Peruby
4
+ # Executable operation tree.
5
+ module Op
6
+ # Base evaluation protocol. Context is one of :scalar, :list, or :void.
7
+ class Base
8
+ def run(_env, _context)
9
+ raise NotImplementedError
10
+ end
11
+
12
+ def lvalue(_env, _context)
13
+ raise PerlError, "Can't modify #{self.class} in assignment"
14
+ end
15
+
16
+ def local_slot(_env)
17
+ raise PerlError, "Can't localize #{self.class}"
18
+ end
19
+
20
+ def argument_cells(env)
21
+ run(env, :list)
22
+ end
23
+
24
+ def to_callable
25
+ ->(env, context) { run(env, context) }
26
+ end
27
+
28
+ def run_subroutine(env, context)
29
+ catch(:peruby_return) { run(env, context) }
30
+ end
31
+
32
+ def warning_directive? = false
33
+ end
34
+
35
+ # Lambda-composed executable with the original tree retained for fallback.
36
+ class Compiled < Base
37
+ def initialize(callable, subroutine = nil)
38
+ super()
39
+ @callable = callable
40
+ @subroutine = subroutine || ->(env, context) { catch(:peruby_return) { @callable.call(env, context) } }
41
+ end
42
+
43
+ def run(env, context) = @callable.call(env, context)
44
+ def run_subroutine(env, context) = @subroutine.call(env, context)
45
+ end
46
+ end
47
+ end