11# frozen_string_literal: true
22
33module SyntaxTree
4- # WithEnvironment is a module intended to be included in classes inheriting
5- # from Visitor. The module overrides a few visit methods to automatically keep
6- # track of local variables and arguments defined in the current environment .
4+ # WithScope is a module intended to be included in classes inheriting from
5+ # Visitor. The module overrides a few visit methods to automatically keep
6+ # track of local variables and arguments defined in the current scope .
77 # Example usage:
88 #
99 # class MyVisitor < Visitor
10- # include WithEnvironment
10+ # include WithScope
1111 #
1212 # def visit_ident(node)
1313 # # Check if we're visiting an identifier for an argument, a local
1414 # # variable or something else
15- # local = current_environment .find_local(node)
15+ # local = current_scope .find_local(node)
1616 #
1717 # if local.type == :argument
1818 # # handle identifiers for arguments
@@ -24,11 +24,11 @@ module SyntaxTree
2424 # end
2525 # end
2626 #
27- module WithEnvironment
28- # The environment class is used to keep track of local variables and
29- # arguments inside a particular scope
30- class Environment
31- # This class tracks the occurrences of a local variable or argument
27+ module WithScope
28+ # The scope class is used to keep track of local variables and arguments
29+ # inside a particular scope.
30+ class Scope
31+ # This class tracks the occurrences of a local variable or argument.
3232 class Local
3333 # [Symbol] The type of the local (e.g. :argument, :variable)
3434 attr_reader :type
@@ -55,20 +55,20 @@ def add_usage(location)
5555 end
5656 end
5757
58- # [Integer] a unique identifier for this environment
58+ # [Integer] a unique identifier for this scope
5959 attr_reader :id
6060
61+ # [scope | nil] The parent scope
62+ attr_reader :parent
63+
6164 # [Hash[String, Local]] The local variables and arguments defined in this
62- # environment
65+ # scope
6366 attr_reader :locals
6467
65- # [Environment | nil] The parent environment
66- attr_reader :parent
67-
6868 def initialize ( id , parent = nil )
6969 @id = id
70- @locals = { }
7170 @parent = parent
71+ @locals = { }
7272 end
7373
7474 # Adding a local definition will either insert a new entry in the locals
@@ -97,7 +97,7 @@ def add_local_usage(identifier, type)
9797 resolve_local ( name , type ) . add_usage ( identifier . location )
9898 end
9999
100- # Try to find the local given its name in this environment or any of its
100+ # Try to find the local given its name in this scope or any of its
101101 # parents.
102102 def find_local ( name )
103103 locals [ name ] || parent &.find_local ( name )
@@ -117,44 +117,35 @@ def resolve_local(name, type)
117117 end
118118 end
119119
120+ attr_reader :current_scope
121+
120122 def initialize ( *args , **kwargs , &block )
121123 super
122- @environment_id = 0
123- end
124-
125- def current_environment
126- @current_environment ||= Environment . new ( next_environment_id )
127- end
128124
129- def with_new_environment ( parent_environment = nil )
130- previous_environment = @current_environment
131- @current_environment =
132- Environment . new ( next_environment_id , parent_environment )
133- yield
134- ensure
135- @current_environment = previous_environment
125+ @current_scope = Scope . new ( 0 )
126+ @next_scope_id = 0
136127 end
137128
138- # Visits for nodes that create new environments , such as classes, modules
129+ # Visits for nodes that create new scopes , such as classes, modules
139130 # and method definitions.
140131 def visit_class ( node )
141- with_new_environment { super }
132+ with_scope { super }
142133 end
143134
144135 def visit_module ( node )
145- with_new_environment { super }
136+ with_scope { super }
146137 end
147138
148- # When we find a method invocation with a block, only the code that
149- # happens inside of the block needs a fresh environment . The method
150- # invocation itself happens in the same environment .
139+ # When we find a method invocation with a block, only the code that happens
140+ # inside of the block needs a fresh scope . The method invocation
141+ # itself happens in the same scope .
151142 def visit_method_add_block ( node )
152143 visit ( node . call )
153- with_new_environment ( current_environment ) { visit ( node . block ) }
144+ with_scope ( current_scope ) { visit ( node . block ) }
154145 end
155146
156147 def visit_def ( node )
157- with_new_environment { super }
148+ with_scope { super }
158149 end
159150
160151 # Visit for keeping track of local arguments, such as method and block
@@ -163,48 +154,45 @@ def visit_params(node)
163154 add_argument_definitions ( node . requireds )
164155
165156 node . posts . each do |param |
166- current_environment . add_local_definition ( param , :argument )
157+ current_scope . add_local_definition ( param , :argument )
167158 end
168159
169160 node . keywords . each do |param |
170- current_environment . add_local_definition ( param . first , :argument )
161+ current_scope . add_local_definition ( param . first , :argument )
171162 end
172163
173164 node . optionals . each do |param |
174- current_environment . add_local_definition ( param . first , :argument )
165+ current_scope . add_local_definition ( param . first , :argument )
175166 end
176167
177168 super
178169 end
179170
180171 def visit_rest_param ( node )
181172 name = node . name
182- current_environment . add_local_definition ( name , :argument ) if name
173+ current_scope . add_local_definition ( name , :argument ) if name
183174
184175 super
185176 end
186177
187178 def visit_kwrest_param ( node )
188179 name = node . name
189- current_environment . add_local_definition ( name , :argument ) if name
180+ current_scope . add_local_definition ( name , :argument ) if name
190181
191182 super
192183 end
193184
194185 def visit_blockarg ( node )
195186 name = node . name
196- current_environment . add_local_definition ( name , :argument ) if name
187+ current_scope . add_local_definition ( name , :argument ) if name
197188
198189 super
199190 end
200191
201192 # Visit for keeping track of local variable definitions
202193 def visit_var_field ( node )
203194 value = node . value
204-
205- if value . is_a? ( SyntaxTree ::Ident )
206- current_environment . add_local_definition ( value , :variable )
207- end
195+ current_scope . add_local_definition ( value , :variable ) if value . is_a? ( Ident )
208196
209197 super
210198 end
@@ -215,12 +203,9 @@ def visit_var_field(node)
215203 def visit_var_ref ( node )
216204 value = node . value
217205
218- if value . is_a? ( SyntaxTree ::Ident )
219- definition = current_environment . find_local ( value . value )
220-
221- if definition
222- current_environment . add_local_usage ( value , definition . type )
223- end
206+ if value . is_a? ( Ident )
207+ definition = current_scope . find_local ( value . value )
208+ current_scope . add_local_usage ( value , definition . type ) if definition
224209 end
225210
226211 super
@@ -233,13 +218,21 @@ def add_argument_definitions(list)
233218 if param . is_a? ( SyntaxTree ::MLHSParen )
234219 add_argument_definitions ( param . contents . parts )
235220 else
236- current_environment . add_local_definition ( param , :argument )
221+ current_scope . add_local_definition ( param , :argument )
237222 end
238223 end
239224 end
240225
241- def next_environment_id
242- @environment_id += 1
226+ def next_scope_id
227+ @next_scope_id += 1
228+ end
229+
230+ def with_scope ( parent_scope = nil )
231+ previous_scope = @current_scope
232+ @current_scope = Scope . new ( next_scope_id , parent_scope )
233+ yield
234+ ensure
235+ @current_scope = previous_scope
243236 end
244237 end
245238end
0 commit comments