Class: Ibex::Codegen::ActionLocations
- Inherits:
-
Object
- Object
- Ibex::Codegen::ActionLocations
- Defined in:
- lib/ibex/codegen/action_locations.rb,
sig/ibex/codegen/action_locations.rbs
Overview
Rewrites semantic-location expressions without touching Ruby literals, comments, heredoc bodies, or ordinary instance/class variables.
Constant Summary collapse
- SEMANTIC_HELPERS =
%w[loc result_loc].freeze
Instance Method Summary collapse
-
#initialize(source, maximum:, location:) ⇒ ActionLocations
constructor
A new instance of ActionLocations.
-
#lexable_source ⇒ String
Give every Ripper implementation syntactically valid input while preserving byte offsets and lexical string/comment boundaries.
- #line_offsets ⇒ Array[Integer]
- #mutable_binary_source_copy ⇒ String
- #references? ⇒ Boolean
- #replacement_for(spelling) ⇒ String
- #rewrite ⇒ String
- #semantic_references ⇒ Array[[ Integer, String ]]
Constructor Details
#initialize(source, maximum:, location:) ⇒ ActionLocations
Returns a new instance of ActionLocations.
17 18 19 20 21 |
# File 'lib/ibex/codegen/action_locations.rb', line 17 def initialize(source, maximum:, location:) @source = source @maximum = maximum @location = location end |
Instance Method Details
#lexable_source ⇒ String
Give every Ripper implementation syntactically valid input while preserving byte offsets and lexical string/comment boundaries.
74 75 76 |
# File 'lib/ibex/codegen/action_locations.rb', line 74 def lexable_source @source.gsub(/@(?:\$|\d+)/) { |spelling| "_" * spelling.bytesize } end |
#line_offsets ⇒ Array[Integer]
79 80 81 82 83 84 85 86 |
# File 'lib/ibex/codegen/action_locations.rb', line 79 def line_offsets offset = 0 @source.lines.map do |line| current = offset offset += line.bytesize current end end |
#mutable_binary_source_copy ⇒ String
50 51 52 |
# File 'lib/ibex/codegen/action_locations.rb', line 50 def mutable_binary_source_copy String.new(encoding: Encoding::BINARY).replace(@source.b) end |
#references? ⇒ Boolean
36 37 38 39 40 41 42 43 44 45 |
# File 'lib/ibex/codegen/action_locations.rb', line 36 def references? return true if @source.match?(/@(?:\$|\d+)/) && !semantic_references.empty? return false unless @source.match?(/\b(?:loc|result_loc)\b/) require "ripper" tokens = Object.const_get(:Ripper).__send__(:lex, lexable_source) tokens.any? do |_position, type, token, _state| type == :on_ident && SEMANTIC_HELPERS.include?(token) end end |
#replacement_for(spelling) ⇒ String
89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/ibex/codegen/action_locations.rb', line 89 def replacement_for(spelling) return "_ibex_location" if spelling == "@$" index = spelling.delete_prefix("@").to_i if index.zero? || index > @maximum raise Ibex::Error, "#{@location[:file]}:#{@location[:line]}:#{@location[:column]}: " \ "semantic location #{spelling} is outside 1..#{@maximum}" end "_ibex_locations[#{index - 1}]" end |
#rewrite ⇒ String
24 25 26 27 28 29 30 31 32 33 |
# File 'lib/ibex/codegen/action_locations.rb', line 24 def rewrite rewritten = mutable_binary_source_copy return rewritten.force_encoding(@source.encoding) unless @source.match?(/@(?:\$|\d+)/) replacements = semantic_references.map do |offset, spelling| [offset, spelling.bytesize, replacement_for(spelling)] end #: Array[[Integer, Integer, String]] replacements.reverse_each { |offset, length, replacement| rewritten[offset, length] = replacement } rewritten.force_encoding(@source.encoding) end |
#semantic_references ⇒ Array[[ Integer, String ]]
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/ibex/codegen/action_locations.rb', line 55 def semantic_references require "ripper" offsets = line_offsets tokens = Object.const_get(:Ripper).__send__(:lex, lexable_source) # @type var tokens: Array[[[Integer, Integer], Symbol, String, untyped]] tokens.filter_map do |position, type, _token, _state| next unless type == :on_ident line, column = position offset = offsets.fetch(line - 1) + column spelling = @source.b.byteslice(offset..)&.match(/\A@(?:\$|\d+)/)&.[](0) [offset, spelling] if spelling end end |