rubocop-ast 0.5.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/rubocop/ast.rb +17 -0
- data/lib/rubocop/ast/builder.rb +1 -0
- data/lib/rubocop/ast/node.rb +44 -125
- data/lib/rubocop/ast/node/array_node.rb +1 -0
- data/lib/rubocop/ast/node/block_node.rb +1 -0
- data/lib/rubocop/ast/node/def_node.rb +5 -0
- data/lib/rubocop/ast/node/keyword_splat_node.rb +1 -0
- data/lib/rubocop/ast/node/mixin/collection_node.rb +1 -0
- data/lib/rubocop/ast/node/mixin/descendence.rb +116 -0
- data/lib/rubocop/ast/node/mixin/method_dispatch_node.rb +2 -0
- data/lib/rubocop/ast/node/mixin/method_identifier_predicates.rb +9 -0
- data/lib/rubocop/ast/node/mixin/numeric_node.rb +1 -0
- data/lib/rubocop/ast/node/mixin/predicate_operator_node.rb +7 -3
- data/lib/rubocop/ast/node/pair_node.rb +4 -0
- data/lib/rubocop/ast/node/regexp_node.rb +9 -4
- data/lib/rubocop/ast/node_pattern.rb +44 -870
- data/lib/rubocop/ast/node_pattern/builder.rb +72 -0
- data/lib/rubocop/ast/node_pattern/comment.rb +45 -0
- data/lib/rubocop/ast/node_pattern/compiler.rb +104 -0
- data/lib/rubocop/ast/node_pattern/compiler/atom_subcompiler.rb +56 -0
- data/lib/rubocop/ast/node_pattern/compiler/binding.rb +78 -0
- data/lib/rubocop/ast/node_pattern/compiler/debug.rb +168 -0
- data/lib/rubocop/ast/node_pattern/compiler/node_pattern_subcompiler.rb +146 -0
- data/lib/rubocop/ast/node_pattern/compiler/sequence_subcompiler.rb +420 -0
- data/lib/rubocop/ast/node_pattern/compiler/subcompiler.rb +57 -0
- data/lib/rubocop/ast/node_pattern/lexer.rb +70 -0
- data/lib/rubocop/ast/node_pattern/lexer.rex +39 -0
- data/lib/rubocop/ast/node_pattern/lexer.rex.rb +182 -0
- data/lib/rubocop/ast/node_pattern/method_definer.rb +143 -0
- data/lib/rubocop/ast/node_pattern/node.rb +275 -0
- data/lib/rubocop/ast/node_pattern/parser.racc.rb +470 -0
- data/lib/rubocop/ast/node_pattern/parser.rb +66 -0
- data/lib/rubocop/ast/node_pattern/parser.y +103 -0
- data/lib/rubocop/ast/node_pattern/sets.rb +37 -0
- data/lib/rubocop/ast/node_pattern/with_meta.rb +111 -0
- data/lib/rubocop/ast/processed_source.rb +5 -1
- data/lib/rubocop/ast/traversal.rb +149 -172
- data/lib/rubocop/ast/version.rb +1 -1
- metadata +37 -3
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module AST
|
5
|
+
class NodePattern
|
6
|
+
class Compiler
|
7
|
+
# Base class for subcompilers
|
8
|
+
# Implements visitor pattern
|
9
|
+
#
|
10
|
+
# Doc on how this fits in the compiling process:
|
11
|
+
# /doc/modules/ROOT/pages/node_pattern.md
|
12
|
+
class Subcompiler
|
13
|
+
attr_reader :compiler
|
14
|
+
|
15
|
+
def initialize(compiler)
|
16
|
+
@compiler = compiler
|
17
|
+
@node = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def compile(node)
|
21
|
+
prev = @node
|
22
|
+
@node = node
|
23
|
+
do_compile
|
24
|
+
ensure
|
25
|
+
@node = prev
|
26
|
+
end
|
27
|
+
|
28
|
+
# @api private
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :node
|
33
|
+
|
34
|
+
def do_compile
|
35
|
+
send(self.class.registry.fetch(node.type, :visit_other_type))
|
36
|
+
end
|
37
|
+
|
38
|
+
@registry = {}
|
39
|
+
class << self
|
40
|
+
attr_reader :registry
|
41
|
+
|
42
|
+
def method_added(method)
|
43
|
+
@registry[Regexp.last_match(1).to_sym] = method if method =~ /^visit_(.*)/
|
44
|
+
super
|
45
|
+
end
|
46
|
+
|
47
|
+
def inherited(base)
|
48
|
+
us = self
|
49
|
+
base.class_eval { @registry = us.registry.dup }
|
50
|
+
super
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
require_relative 'lexer.rex'
|
5
|
+
rescue LoadError
|
6
|
+
msg = '*** You must run `rake generate` to generate the lexer and the parser ***'
|
7
|
+
puts '*' * msg.length, msg, '*' * msg.length
|
8
|
+
raise
|
9
|
+
end
|
10
|
+
|
11
|
+
module RuboCop
|
12
|
+
module AST
|
13
|
+
class NodePattern
|
14
|
+
# Lexer class for `NodePattern`
|
15
|
+
#
|
16
|
+
# Doc on how this fits in the compiling process:
|
17
|
+
# /doc/modules/ROOT/pages/node_pattern.md
|
18
|
+
class Lexer < LexerRex
|
19
|
+
Error = ScanError
|
20
|
+
|
21
|
+
REGEXP_OPTIONS = {
|
22
|
+
'i' => ::Regexp::IGNORECASE,
|
23
|
+
'm' => ::Regexp::MULTILINE,
|
24
|
+
'x' => ::Regexp::EXTENDED,
|
25
|
+
'o' => 0
|
26
|
+
}.freeze
|
27
|
+
private_constant :REGEXP_OPTIONS
|
28
|
+
|
29
|
+
attr_reader :source_buffer, :comments, :tokens
|
30
|
+
|
31
|
+
def initialize(source)
|
32
|
+
@tokens = []
|
33
|
+
super()
|
34
|
+
parse(source)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# @return [token]
|
40
|
+
def emit(type)
|
41
|
+
value = ss[1] || ss.matched
|
42
|
+
value = yield value if block_given?
|
43
|
+
token = token(type, value)
|
44
|
+
@tokens << token
|
45
|
+
token
|
46
|
+
end
|
47
|
+
|
48
|
+
def emit_comment
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def emit_regexp
|
53
|
+
body = ss[1]
|
54
|
+
options = ss[2]
|
55
|
+
flag = options.each_char.map { |c| REGEXP_OPTIONS[c] }.sum
|
56
|
+
|
57
|
+
emit(:tREGEXP) { Regexp.new(body, flag) }
|
58
|
+
end
|
59
|
+
|
60
|
+
def do_parse
|
61
|
+
# Called by the generated `parse` method, do nothing here.
|
62
|
+
end
|
63
|
+
|
64
|
+
def token(type, value)
|
65
|
+
[type, value]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# The only difficulty is to distinguish: `fn(argument)` from `fn (sequence)`.
|
2
|
+
# The presence of the whitespace determines if it is an _argument_ to the
|
3
|
+
# function call `fn` or if a _sequence_ follows the function call.
|
4
|
+
#
|
5
|
+
# If there is the potential for an argument list, the lexer enters the state `:ARG`.
|
6
|
+
# The rest of the times, the state is `nil`.
|
7
|
+
#
|
8
|
+
# In case of an argument list, :tARG_LIST is emitted instead of a '('.
|
9
|
+
# Therefore, the token '(' always signals the beginning of a sequence.
|
10
|
+
|
11
|
+
class RuboCop::AST::NodePattern::LexerRex
|
12
|
+
|
13
|
+
macros
|
14
|
+
SYMBOL_NAME /[\w+@*\/?!<>=~|%^-]+|\[\]=?/
|
15
|
+
IDENTIFIER /[a-zA-Z_][a-zA-Z0-9_-]*/
|
16
|
+
REGEXP_BODY /(?:[^\/]|\\\/)*/
|
17
|
+
REGEXP /\/(#{REGEXP_BODY})(?<!\\)\/([imxo]*)/
|
18
|
+
rules
|
19
|
+
/\s+/
|
20
|
+
/:(#{SYMBOL_NAME})/o { emit :tSYMBOL, &:to_sym }
|
21
|
+
/"(.+?)"/ { emit :tSTRING }
|
22
|
+
/[-+]?\d+\.\d+/ { emit :tNUMBER, &:to_f }
|
23
|
+
/[-+]?\d+/ { emit :tNUMBER, &:to_i }
|
24
|
+
/#{Regexp.union(
|
25
|
+
%w"( ) { | } [ ] < > $ ! ^ ` ... + * ? ,"
|
26
|
+
)}/o { emit ss.matched, &:to_sym }
|
27
|
+
/#{REGEXP}/o { emit_regexp }
|
28
|
+
/%([A-Z:][a-zA-Z_:]+)/ { emit :tPARAM_CONST }
|
29
|
+
/%([a-z_]+)/ { emit :tPARAM_NAMED }
|
30
|
+
/%(\d*)/ { emit(:tPARAM_NUMBER) { |s| s.empty? ? 1 : s.to_i } } # Map `%` to `%1`
|
31
|
+
/_(#{IDENTIFIER})/o { emit :tUNIFY }
|
32
|
+
/_/o { emit :tWILDCARD }
|
33
|
+
/\#(#{IDENTIFIER}[!?]?)/o { @state = :ARG; emit :tFUNCTION_CALL, &:to_sym }
|
34
|
+
/#{IDENTIFIER}\?/o { @state = :ARG; emit :tPREDICATE, &:to_sym }
|
35
|
+
/#{IDENTIFIER}/o { emit :tNODE_TYPE, &:to_sym }
|
36
|
+
:ARG /\(/ { @state = nil; emit :tARG_LIST }
|
37
|
+
:ARG // { @state = nil }
|
38
|
+
/\#.*/ { emit_comment }
|
39
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: UTF-8
|
3
|
+
#--
|
4
|
+
# This file is automatically generated. Do not modify it.
|
5
|
+
# Generated by: oedipus_lex version 2.5.2.
|
6
|
+
# Source: lib/rubocop/ast/node_pattern/lexer.rex
|
7
|
+
#++
|
8
|
+
|
9
|
+
# The only difficulty is to distinguish: `fn(argument)` from `fn (sequence)`.
|
10
|
+
# The presence of the whitespace determines if it is an _argument_ to the
|
11
|
+
# function call `fn` or if a _sequence_ follows the function call.
|
12
|
+
#
|
13
|
+
# If there is the potential for an argument list, the lexer enters the state `:ARG`.
|
14
|
+
# The rest of the times, the state is `nil`.
|
15
|
+
#
|
16
|
+
# In case of an argument list, :tARG_LIST is emitted instead of a '('.
|
17
|
+
# Therefore, the token '(' always signals the beginning of a sequence.
|
18
|
+
|
19
|
+
|
20
|
+
##
|
21
|
+
# The generated lexer RuboCop::AST::NodePattern::LexerRex
|
22
|
+
|
23
|
+
class RuboCop::AST::NodePattern::LexerRex
|
24
|
+
require 'strscan'
|
25
|
+
|
26
|
+
# :stopdoc:
|
27
|
+
SYMBOL_NAME = /[\w+@*\/?!<>=~|%^-]+|\[\]=?/
|
28
|
+
IDENTIFIER = /[a-zA-Z_][a-zA-Z0-9_-]*/
|
29
|
+
REGEXP_BODY = /(?:[^\/]|\\\/)*/
|
30
|
+
REGEXP = /\/(#{REGEXP_BODY})(?<!\\)\/([imxo]*)/
|
31
|
+
# :startdoc:
|
32
|
+
# :stopdoc:
|
33
|
+
class LexerError < StandardError ; end
|
34
|
+
class ScanError < LexerError ; end
|
35
|
+
# :startdoc:
|
36
|
+
|
37
|
+
##
|
38
|
+
# The file name / path
|
39
|
+
|
40
|
+
attr_accessor :filename
|
41
|
+
|
42
|
+
##
|
43
|
+
# The StringScanner for this lexer.
|
44
|
+
|
45
|
+
attr_accessor :ss
|
46
|
+
|
47
|
+
##
|
48
|
+
# The current lexical state.
|
49
|
+
|
50
|
+
attr_accessor :state
|
51
|
+
|
52
|
+
alias :match :ss
|
53
|
+
|
54
|
+
##
|
55
|
+
# The match groups for the current scan.
|
56
|
+
|
57
|
+
def matches
|
58
|
+
m = (1..9).map { |i| ss[i] }
|
59
|
+
m.pop until m[-1] or m.empty?
|
60
|
+
m
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Yields on the current action.
|
65
|
+
|
66
|
+
def action
|
67
|
+
yield
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
##
|
72
|
+
# The current scanner class. Must be overridden in subclasses.
|
73
|
+
|
74
|
+
def scanner_class
|
75
|
+
StringScanner
|
76
|
+
end unless instance_methods(false).map(&:to_s).include?("scanner_class")
|
77
|
+
|
78
|
+
##
|
79
|
+
# Parse the given string.
|
80
|
+
|
81
|
+
def parse str
|
82
|
+
self.ss = scanner_class.new str
|
83
|
+
self.state ||= nil
|
84
|
+
|
85
|
+
do_parse
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Read in and parse the file at +path+.
|
90
|
+
|
91
|
+
def parse_file path
|
92
|
+
self.filename = path
|
93
|
+
open path do |f|
|
94
|
+
parse f.read
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# The current location in the parse.
|
100
|
+
|
101
|
+
def location
|
102
|
+
[
|
103
|
+
(filename || "<input>"),
|
104
|
+
].compact.join(":")
|
105
|
+
end
|
106
|
+
|
107
|
+
##
|
108
|
+
# Lex the next token.
|
109
|
+
|
110
|
+
def next_token
|
111
|
+
|
112
|
+
token = nil
|
113
|
+
|
114
|
+
until ss.eos? or token do
|
115
|
+
token =
|
116
|
+
case state
|
117
|
+
when nil then
|
118
|
+
case
|
119
|
+
when ss.skip(/\s+/) then
|
120
|
+
# do nothing
|
121
|
+
when ss.skip(/:(#{SYMBOL_NAME})/o) then
|
122
|
+
action { emit :tSYMBOL, &:to_sym }
|
123
|
+
when ss.skip(/"(.+?)"/) then
|
124
|
+
action { emit :tSTRING }
|
125
|
+
when ss.skip(/[-+]?\d+\.\d+/) then
|
126
|
+
action { emit :tNUMBER, &:to_f }
|
127
|
+
when ss.skip(/[-+]?\d+/) then
|
128
|
+
action { emit :tNUMBER, &:to_i }
|
129
|
+
when ss.skip(/#{Regexp.union(
|
130
|
+
%w"( ) { | } [ ] < > $ ! ^ ` ... + * ? ,"
|
131
|
+
)}/o) then
|
132
|
+
action { emit ss.matched, &:to_sym }
|
133
|
+
when ss.skip(/#{REGEXP}/o) then
|
134
|
+
action { emit_regexp }
|
135
|
+
when ss.skip(/%([A-Z:][a-zA-Z_:]+)/) then
|
136
|
+
action { emit :tPARAM_CONST }
|
137
|
+
when ss.skip(/%([a-z_]+)/) then
|
138
|
+
action { emit :tPARAM_NAMED }
|
139
|
+
when ss.skip(/%(\d*)/) then
|
140
|
+
action { emit(:tPARAM_NUMBER) { |s| s.empty? ? 1 : s.to_i } } # Map `%` to `%1`
|
141
|
+
when ss.skip(/_(#{IDENTIFIER})/o) then
|
142
|
+
action { emit :tUNIFY }
|
143
|
+
when ss.skip(/_/o) then
|
144
|
+
action { emit :tWILDCARD }
|
145
|
+
when ss.skip(/\#(#{IDENTIFIER}[!?]?)/o) then
|
146
|
+
action { @state = :ARG; emit :tFUNCTION_CALL, &:to_sym }
|
147
|
+
when ss.skip(/#{IDENTIFIER}\?/o) then
|
148
|
+
action { @state = :ARG; emit :tPREDICATE, &:to_sym }
|
149
|
+
when ss.skip(/#{IDENTIFIER}/o) then
|
150
|
+
action { emit :tNODE_TYPE, &:to_sym }
|
151
|
+
when ss.skip(/\#.*/) then
|
152
|
+
action { emit_comment }
|
153
|
+
else
|
154
|
+
text = ss.string[ss.pos .. -1]
|
155
|
+
raise ScanError, "can not match (#{state.inspect}) at #{location}: '#{text}'"
|
156
|
+
end
|
157
|
+
when :ARG then
|
158
|
+
case
|
159
|
+
when ss.skip(/\(/) then
|
160
|
+
action { @state = nil; emit :tARG_LIST }
|
161
|
+
when ss.skip(//) then
|
162
|
+
action { @state = nil }
|
163
|
+
else
|
164
|
+
text = ss.string[ss.pos .. -1]
|
165
|
+
raise ScanError, "can not match (#{state.inspect}) at #{location}: '#{text}'"
|
166
|
+
end
|
167
|
+
else
|
168
|
+
raise ScanError, "undefined state at #{location}: '#{state}'"
|
169
|
+
end # token = case state
|
170
|
+
|
171
|
+
next unless token # allow functions to trigger redo w/ nil
|
172
|
+
end # while
|
173
|
+
|
174
|
+
raise LexerError, "bad lexical result at #{location}: #{token.inspect}" unless
|
175
|
+
token.nil? || (Array === token && token.size >= 2)
|
176
|
+
|
177
|
+
# auto-switch state
|
178
|
+
self.state = token.last if token && token.first == :state
|
179
|
+
|
180
|
+
token
|
181
|
+
end # def next_token
|
182
|
+
end # class
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module AST
|
5
|
+
class NodePattern
|
6
|
+
# Functionality to turn `match_code` into methods/lambda
|
7
|
+
module MethodDefiner
|
8
|
+
def def_node_matcher(base, method_name, **defaults)
|
9
|
+
def_helper(base, method_name, **defaults) do |name|
|
10
|
+
params = emit_params('param0 = self')
|
11
|
+
<<~RUBY
|
12
|
+
def #{name}(#{params})
|
13
|
+
#{VAR} = param0
|
14
|
+
#{compile_init}
|
15
|
+
#{emit_method_code}
|
16
|
+
end
|
17
|
+
RUBY
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def def_node_search(base, method_name, **defaults)
|
22
|
+
def_helper(base, method_name, **defaults) do |name|
|
23
|
+
emit_node_search(name)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def compile_as_lambda
|
28
|
+
<<~RUBY
|
29
|
+
->(#{emit_params('param0')}, block: nil) do
|
30
|
+
#{VAR} = param0
|
31
|
+
#{compile_init}
|
32
|
+
#{emit_lambda_code}
|
33
|
+
end
|
34
|
+
RUBY
|
35
|
+
end
|
36
|
+
|
37
|
+
def as_lambda
|
38
|
+
eval(compile_as_lambda) # rubocop:disable Security/Eval
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# This method minimizes the closure for our method
|
44
|
+
def wrapping_block(method_name, **defaults)
|
45
|
+
proc do |*args, **values|
|
46
|
+
send method_name, *args, **defaults, **values
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def def_helper(base, method_name, **defaults)
|
51
|
+
location = caller_locations(3, 1).first
|
52
|
+
unless defaults.empty?
|
53
|
+
call = :"without_defaults_#{method_name}"
|
54
|
+
base.send :define_method, method_name, &wrapping_block(call, **defaults)
|
55
|
+
method_name = call
|
56
|
+
end
|
57
|
+
src = yield method_name
|
58
|
+
base.class_eval(src, location.path, location.lineno)
|
59
|
+
end
|
60
|
+
|
61
|
+
def emit_node_search(method_name)
|
62
|
+
if method_name.to_s.end_with?('?')
|
63
|
+
on_match = 'return true'
|
64
|
+
else
|
65
|
+
args = emit_params(":#{method_name}", 'param0', forwarding: true)
|
66
|
+
prelude = "return enum_for(#{args}) unless block_given?\n"
|
67
|
+
on_match = emit_yield_capture(VAR)
|
68
|
+
end
|
69
|
+
emit_node_search_body(method_name, prelude: prelude, on_match: on_match)
|
70
|
+
end
|
71
|
+
|
72
|
+
def emit_node_search_body(method_name, prelude:, on_match:)
|
73
|
+
<<~RUBY
|
74
|
+
def #{method_name}(#{emit_params('param0')})
|
75
|
+
#{compile_init}
|
76
|
+
#{prelude}
|
77
|
+
param0.each_node do |#{VAR}|
|
78
|
+
if #{match_code}
|
79
|
+
#{on_match}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
RUBY
|
85
|
+
end
|
86
|
+
|
87
|
+
def emit_yield_capture(when_no_capture = '', yield_with: 'yield')
|
88
|
+
yield_val = if captures.zero?
|
89
|
+
when_no_capture
|
90
|
+
elsif captures == 1
|
91
|
+
'captures[0]' # Circumvent https://github.com/jruby/jruby/issues/5710
|
92
|
+
else
|
93
|
+
'*captures'
|
94
|
+
end
|
95
|
+
"#{yield_with}(#{yield_val})"
|
96
|
+
end
|
97
|
+
|
98
|
+
def emit_retval
|
99
|
+
if captures.zero?
|
100
|
+
'true'
|
101
|
+
elsif captures == 1
|
102
|
+
'captures[0]'
|
103
|
+
else
|
104
|
+
'captures'
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def emit_param_list
|
109
|
+
(1..positional_parameters).map { |n| "param#{n}" }.join(',')
|
110
|
+
end
|
111
|
+
|
112
|
+
def emit_keyword_list(forwarding: false)
|
113
|
+
pattern = "%<keyword>s: #{'%<keyword>s' if forwarding}"
|
114
|
+
named_parameters.map { |k| format(pattern, keyword: k) }.join(',')
|
115
|
+
end
|
116
|
+
|
117
|
+
def emit_params(*first, forwarding: false)
|
118
|
+
params = emit_param_list
|
119
|
+
keywords = emit_keyword_list(forwarding: forwarding)
|
120
|
+
[*first, params, keywords].reject(&:empty?).join(',')
|
121
|
+
end
|
122
|
+
|
123
|
+
def emit_method_code
|
124
|
+
<<~RUBY
|
125
|
+
return unless #{match_code}
|
126
|
+
block_given? ? #{emit_yield_capture} : (return #{emit_retval})
|
127
|
+
RUBY
|
128
|
+
end
|
129
|
+
|
130
|
+
def emit_lambda_code
|
131
|
+
<<~RUBY
|
132
|
+
return unless #{match_code}
|
133
|
+
block ? #{emit_yield_capture(yield_with: 'block.call')} : (return #{emit_retval})
|
134
|
+
RUBY
|
135
|
+
end
|
136
|
+
|
137
|
+
def compile_init
|
138
|
+
"captures = Array.new(#{captures})" if captures.positive?
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|