# -*- Ruby -*- grammar MethodName rule upcase_letter [A-Z] end rule downcase_letter [a-z] end rule suffix_letter [=!?] end rule letter upcase_letter | downcase_letter end rule id_symbol letter | '_' | [0-9] end # Examples: # var1 # my_var? rule variable_identifier ((downcase_letter | '_') id_symbol* suffix_letter?) { SymbolEntry = Struct.new(:type, :name, :chain) def value SymbolEntry.new(:variable, self.to_s) end } end # Examples: # MY_CONSTANT # MyConstant_01 rule constant_identifier (upcase_letter id_symbol*) { def value SymbolEntry.new(:constant, self.to_s) end } end # Examples: # $global_variable # We won't try for funny global names like $$, $? $:, $', etc rule global_identifier ('$' (constant_identifier | variable_identifier)) { def value SymbolEntry.new(:global, self.to_s) end } end # Examples: # Foo # foo rule local_identifier (constant_identifier | variable_identifier) { def value SymbolEntry.new(:instance, self.to_s) end } end # Example: @foo rule instance_identifier ('@' local_identifier ) { def value SymbolEntry.new(:instance, self.to_s) end } end # Example: @@foo rule classvar_identifier ('@@' local_identifier ) { def value SymbolEntry.new(:classvar, self.to_s) end } end rule identifier global_identifier | instance_identifier | classvar_identifier | local_identifier end # I think strict Ruby rules are that once one goes from :: to . # There is no going back. That is, A.B::C is invalid. # # Also I think method names can't be constants. But such # subtleties we'll handle in semantic analysis. rule class_module_chain ( ( parent:(local_identifier) symbol:('::'|'.') child:(class_module_chain) ) | (parent:(local_identifier) ) ) { def value let = self.to_s[0..0] type = (let.capitalize == let) ? :constant : :variable SymbolEntry.new(type, self.to_s, [parent, child, symbol.to_s]) end } end ############################################################## # Location-specific things. This is used in conjunction with # method-like things above. rule sp [ \t]+ end rule file_pos_sep sp | ':' end rule integer [0-9]+ { to_i } end rule vm_offset ('@' integer) { Position = Struct.new(:type, :value) unless defined?(Position) Position.new(:offset, integer.value) } end rule line_number ([0-9]+) { Position = Struct.new(:type, :value) unless defined?(Position) Position.new(:line, to_i) } end # Examples: # @43 # 5 rule position (vm_offset | line_number) end rule location (class_module_chain? file_pos_sep position | position | meth2:(class_module_chain)) { Location = Struct.new(:method_name, :position) unless defined?(Location) pos_val = position ? position.value : nil meth_val = if class_module_chain class_module_chain.value else # p ['++++2', meth2, meth2.class] meth2 ? meth2.value : nil end Location.new(meth_val, pos_val) } end end