Class: Reflexive::ReflexiveRipper
- Inherits:
-
Ripper
- Object
- Ripper
- Reflexive::ReflexiveRipper
- Defined in:
- lib/reflexive/reflexive_ripper.rb
Overview
Records all scanner events, injects meta-events when scope changes (required to implement constant/method look-up)
Constant Summary collapse
- META_EVENT =
[ :meta_scope ].freeze
Instance Attribute Summary collapse
-
#scanner_events ⇒ Object
Returns the value of attribute scanner_events.
Class Method Summary collapse
-
.destruct_scanner_event(scanner_event) ⇒ Object
Returns array in format: [event_value, event_name, tags].
- .is_meta_event?(event) ⇒ Boolean
- .resolve_constant_ref(tokens) ⇒ Object
Instance Method Summary collapse
-
#initialize(*args) ⇒ ReflexiveRipper
constructor
A new instance of ReflexiveRipper.
- #inject_current_scope ⇒ Object
-
#on_class(*args) ⇒ Object
Two parser events which fire when class/module definition ends.
-
#on_const(const_name) ⇒ Object
cname : tIDENTIFIER { /*%%%*/ yyerror(“class/module name must be CONSTANT”); /*% $$ = dispatch1(class_name_error, $1); %*/ } | tCONSTANT ;.
-
#on_kw(kw) ⇒ Object
parse.y:.
- #on_module(*args) ⇒ Object
-
#on_op(token) ⇒ Object
matches “::” in two cases 1.
- #on_parse_error(*args) ⇒ Object
- #on_sp(*args) ⇒ Object
- #parse ⇒ Object
- #resolve_constant_ref(tokens) ⇒ Object
- #stop_await_scope_change ⇒ Object
-
#token_index(token) ⇒ Object
def on_call(*args) # puts “PARSE: on_call: #{ args.inspect }” if args.size == 3 && (constant = resolve_constant_ref(args)) && args == :“.” && args[1] == :ident.
Constructor Details
#initialize(*args) ⇒ ReflexiveRipper
Returns a new instance of ReflexiveRipper.
12 13 14 15 16 17 18 |
# File 'lib/reflexive/reflexive_ripper.rb', line 12 def initialize(*args) super @scanner_events = [] @await_scope_change = false @scope = [] @new_scope = nil end |
Instance Attribute Details
#scanner_events ⇒ Object
Returns the value of attribute scanner_events.
8 9 10 |
# File 'lib/reflexive/reflexive_ripper.rb', line 8 def scanner_events @scanner_events end |
Class Method Details
.destruct_scanner_event(scanner_event) ⇒ Object
Returns array in format: [event_value, event_name, tags]
33 34 35 36 |
# File 'lib/reflexive/reflexive_ripper.rb', line 33 def self.destruct_scanner_event(scanner_event) = scanner_event.delete(:tags) [ scanner_event.values.first, scanner_event.keys.first, ] end |
.is_meta_event?(event) ⇒ Boolean
38 39 40 |
# File 'lib/reflexive/reflexive_ripper.rb', line 38 def self.(event) META_EVENT.include?(event) end |
.resolve_constant_ref(tokens) ⇒ Object
158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/reflexive/reflexive_ripper.rb', line 158 def self.resolve_constant_ref(tokens) if tokens[0] == :var_ref && tokens[1][1] == :const # [:var_ref, ["B", :const]] tokens[1][0] elsif tokens[0] == :top_const_ref && tokens[1][1] == :const # [:top_const_ref, ["A", :const]] "::" + tokens[1][0] # [:const_path_ref, [:top_const_ref, ["A", :const]], ["B", :const]] # [:const_path_ref, [:var_ref, ["A", :const]], ["B", :const]] elsif tokens[0] == :const_path_ref && (constant = resolve_constant_ref(tokens[1])) "#{ constant }::#{ tokens[2][0] }" end end |
Instance Method Details
#inject_current_scope ⇒ Object
64 65 66 |
# File 'lib/reflexive/reflexive_ripper.rb', line 64 def inject_current_scope @scanner_events << { :meta_scope => @scope.dup } end |
#on_class(*args) ⇒ Object
Two parser events which fire when class/module definition ends
92 93 94 95 96 97 |
# File 'lib/reflexive/reflexive_ripper.rb', line 92 def on_class(*args) @scope.pop inject_current_scope [:class, *args] end |
#on_const(const_name) ⇒ Object
cname : tIDENTIFIER
{
/*%%%*/
yyerror("class/module name must be CONSTANT");
/*%
$$ = dispatch1(class_name_error, $1);
%*/
}
| tCONSTANT
;
“ClassName” or “ClassName::NestedClassName”
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
# File 'lib/reflexive/reflexive_ripper.rb', line 240 def on_const(const_name) if @await_scope_change == "k_class tCOLON3" # "class ::TopClass" @new_scope = "::#{ const_name }" elsif @await_scope_change == "k_class" # "class NormalClass" # | cname # { # /*%%%*/ # $$ = NEW_COLON2(0, $$); # /*% # $$ = dispatch1(const_ref, $1); # %*/ # } @new_scope = "#{ const_name }" @await_scope_change = "k_class primary_value tCOLON2" # "class Class::NestedClass" elsif @await_scope_change == "k_class primary_value tCOLON2" # # | primary_value tCOLON2 cname # { # /*%%%*/ # $$ = NEW_COLON2($1, $3); # /*% # $$ = dispatch2(const_path_ref, $1, $3); # %*/ # } # ; # # cname : tIDENTIFIER @new_scope << "#{ const_name }" # append to the scope else stop_await_scope_change end @scanner_events << { :const => const_name } @scanner_events[-1] end |
#on_kw(kw) ⇒ Object
parse.y:
| k_class cpath superclass
| k_module cpath
matches “class”
186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/reflexive/reflexive_ripper.rb', line 186 def on_kw(kw) if %w(class module).include?(kw) @await_scope_change = "k_class" else stop_await_scope_change end @scanner_events << { :kw => kw } @scanner_events[-1] end |
#on_module(*args) ⇒ Object
99 100 101 102 103 104 |
# File 'lib/reflexive/reflexive_ripper.rb', line 99 def on_module(*args) @scope.pop inject_current_scope [:module, *args] end |
#on_op(token) ⇒ Object
matches “::” in two cases
1. "class ::TopLevel",
2. "class Nested::Class"
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/reflexive/reflexive_ripper.rb', line 201 def on_op(token) if @await_scope_change == "k_class" && token == "::" # tCOLON2, tCOLON3 # cpath : tCOLON3 cname # { # /*%%%*/ # $$ = NEW_COLON3($2); # /*% # $$ = dispatch1(top_const_ref, $2); # %*/ # } @await_scope_change = "k_class tCOLON3" elsif @await_scope_change == "k_class primary_value tCOLON2" && token == "::" # tCOLON2, tCOLON3 @new_scope << "::" # append to the last defined scope else stop_await_scope_change end @scanner_events << { :op => token } @scanner_events[-1] end |
#on_parse_error(*args) ⇒ Object
106 107 108 |
# File 'lib/reflexive/reflexive_ripper.rb', line 106 def on_parse_error(*args) raise SyntaxError, "#{ lineno }: #{ args[0] }" end |
#on_sp(*args) ⇒ Object
172 173 174 175 176 177 178 |
# File 'lib/reflexive/reflexive_ripper.rb', line 172 def on_sp(*args) @scanner_events << { :sp => args[0] } # ignore space tokens when waiting for scope changes: # stop_await_scope_change @scanner_events[-1] end |
#parse ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/reflexive/reflexive_ripper.rb', line 20 def parse parse_tree = super if ENV["DEBUG"] require 'pp' pp parse_tree end ParseTreeTopDownWalker.new(parse_tree).walk parse_tree end |
#resolve_constant_ref(tokens) ⇒ Object
154 155 156 |
# File 'lib/reflexive/reflexive_ripper.rb', line 154 def resolve_constant_ref(tokens) self.class.resolve_constant_ref(tokens) end |
#stop_await_scope_change ⇒ Object
51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/reflexive/reflexive_ripper.rb', line 51 def stop_await_scope_change # get here when any of 3 possible constant reference ends, # which means we have new scope in effect if @await_scope_change if @new_scope @scope << @new_scope.dup inject_current_scope @new_scope = nil end @await_scope_change = false end end |
#token_index(token) ⇒ Object
def on_call(*args)
# puts "PARSE: on_call: #{ args.inspect }"
if args.size == 3 &&
(constant = resolve_constant_ref(args[0])) &&
args[1] == :"." &&
args[2][1] == :ident
meth_ident_token = args[2]
index = token_index(meth_ident_token)
tags = { :constant => constant, :method => args[2][0] }
# wrap the method identifier token in :method_call meta tokens
@scanner_events[index .. index] = [
# [ :method_call, :open, tags ],
meth_ident_token,
# [ :method_call, :close ]
]
end
[:call, *args]
end
def on_var_ref(*args)
# puts "PARSER: var_ref: #{ args.inspect }"
if args.size == 1 &&
args[0].size == 2 &&
args[0][1] == :ident
meth_ident_token = args[0]
index = token_index(meth_ident_token)
tags = { :instance_method => args[0][0] }
@scanner_events[index .. index] = [
[ :method_call, :open, tags ],
meth_ident_token,
[ :method_call, :close ]
]
# r @scanner_events[177]
end
[:var_ref, *args]
end
148 149 150 151 152 |
# File 'lib/reflexive/reflexive_ripper.rb', line 148 def token_index(token) @scanner_events.index do |t| t.object_id == token.object_id end end |