Class: Ibex::Location
- Inherits:
-
Object
- Object
- Ibex::Location
- Defined in:
- lib/ibex/location.rb,
sig/ibex/location.rbs
Overview
Immutable source range suitable for lexer tokens and parser diagnostics.
Coordinates are one-based. Byte offsets are optional, zero-based, and half-open. Keeping both forms lets Unicode-aware lexers report human columns without losing an exact source slice.
Instance Attribute Summary collapse
- #column ⇒ Integer readonly
- #end_byte ⇒ Integer? readonly
- #end_column ⇒ Integer readonly
- #end_line ⇒ Integer readonly
- #file ⇒ String? readonly
- #line ⇒ Integer readonly
- #source_line ⇒ String? readonly
- #start_byte ⇒ Integer? readonly
Class Method Summary collapse
-
.join(locations) ⇒ Location
Fold a nonempty collection into one covering range.
Instance Method Summary collapse
- #before_or_equal?(left, right) ⇒ Boolean
- #before_or_equal_end?(left, right) ⇒ Boolean
- #empty? ⇒ Boolean
-
#initialize(line:, column:, file: nil, end_line: nil, end_column: nil, start_byte: nil, end_byte: nil, source_line: nil) ⇒ Location
constructor
A new instance of Location.
-
#join(other) ⇒ Location
Return the smallest range covering both locations.
- #joined_end_byte(other) ⇒ Integer?
- #joined_start_byte(other) ⇒ Integer?
- #optional_offset(name, value) ⇒ Integer?
- #positive_coordinate(name, value) ⇒ Integer
- #to_h ⇒ Hash[Symbol, untyped]
- #validate_order! ⇒ void
Constructor Details
#initialize(line:, column:, file: nil, end_line: nil, end_column: nil, start_byte: nil, end_byte: nil, source_line: nil) ⇒ Location
Returns a new instance of Location.
22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/ibex/location.rb', line 22 def initialize(line:, column:, file: nil, end_line: nil, end_column: nil, start_byte: nil, end_byte: nil, source_line: nil) @file = file&.dup&.freeze @line = positive_coordinate(:line, line) @column = positive_coordinate(:column, column) @end_line = positive_coordinate(:end_line, end_line || line) @end_column = positive_coordinate(:end_column, end_column || column) @start_byte = optional_offset(:start_byte, start_byte) @end_byte = optional_offset(:end_byte, end_byte) @source_line = source_line&.dup&.freeze validate_order! freeze end |
Instance Attribute Details
#column ⇒ Integer (readonly)
13 14 15 |
# File 'lib/ibex/location.rb', line 13 def column @column end |
#end_byte ⇒ Integer? (readonly)
17 18 19 |
# File 'lib/ibex/location.rb', line 17 def end_byte @end_byte end |
#end_column ⇒ Integer (readonly)
15 16 17 |
# File 'lib/ibex/location.rb', line 15 def end_column @end_column end |
#end_line ⇒ Integer (readonly)
14 15 16 |
# File 'lib/ibex/location.rb', line 14 def end_line @end_line end |
#file ⇒ String? (readonly)
11 12 13 |
# File 'lib/ibex/location.rb', line 11 def file @file end |
#line ⇒ Integer (readonly)
12 13 14 |
# File 'lib/ibex/location.rb', line 12 def line @line end |
#source_line ⇒ String? (readonly)
18 19 20 |
# File 'lib/ibex/location.rb', line 18 def source_line @source_line end |
#start_byte ⇒ Integer? (readonly)
16 17 18 |
# File 'lib/ibex/location.rb', line 16 def start_byte @start_byte end |
Class Method Details
.join(locations) ⇒ Location
Fold a nonempty collection into one covering range.
53 54 55 56 57 |
# File 'lib/ibex/location.rb', line 53 def self.join(locations) values = locations.to_a first = values.shift || raise(ArgumentError, "locations must not be empty") values.reduce(first) { |combined, location| combined.join(location) } end |
Instance Method Details
#before_or_equal?(left, right) ⇒ Boolean
105 106 107 |
# File 'lib/ibex/location.rb', line 105 def before_or_equal?(left, right) left.line < right.line || (left.line == right.line && left.column <= right.column) end |
#before_or_equal_end?(left, right) ⇒ Boolean
110 111 112 113 |
# File 'lib/ibex/location.rb', line 110 def before_or_equal_end?(left, right) left.end_line < right.end_line || (left.end_line == right.end_line && left.end_column <= right.end_column) end |
#empty? ⇒ Boolean
60 61 62 63 |
# File 'lib/ibex/location.rb', line 60 def empty? @line == @end_line && @column == @end_column && (@start_byte.nil? || @end_byte.nil? || @start_byte == @end_byte) end |
#join(other) ⇒ Location
Return the smallest range covering both locations.
38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/ibex/location.rb', line 38 def join(other) raise ArgumentError, "location files differ" unless file == other.file first = before_or_equal?(self, other) ? self : other ending = before_or_equal_end?(self, other) ? other : self self.class.new( file: file, line: first.line, column: first.column, end_line: ending.end_line, end_column: ending.end_column, start_byte: joined_start_byte(other), end_byte: joined_end_byte(other), source_line: first.source_line ) end |
#joined_end_byte(other) ⇒ Integer?
123 124 125 126 127 |
# File 'lib/ibex/location.rb', line 123 def joined_end_byte(other) return unless @end_byte && other.end_byte [@end_byte, other.end_byte].max end |
#joined_start_byte(other) ⇒ Integer?
116 117 118 119 120 |
# File 'lib/ibex/location.rb', line 116 def joined_start_byte(other) return unless @start_byte && other.start_byte [@start_byte, other.start_byte].min end |
#optional_offset(name, value) ⇒ Integer?
84 85 86 87 88 89 |
# File 'lib/ibex/location.rb', line 84 def optional_offset(name, value) return if value.nil? return value if value.is_a?(Integer) && value >= 0 raise ArgumentError, "#{name} must be a nonnegative Integer or nil" end |
#positive_coordinate(name, value) ⇒ Integer
77 78 79 80 81 |
# File 'lib/ibex/location.rb', line 77 def positive_coordinate(name, value) return value if value.is_a?(Integer) && value.positive? raise ArgumentError, "#{name} must be a positive Integer" end |
#to_h ⇒ Hash[Symbol, untyped]
66 67 68 69 70 71 72 |
# File 'lib/ibex/location.rb', line 66 def to_h { file: @file, line: @line, column: @column, end_line: @end_line, end_column: @end_column, start_byte: @start_byte, end_byte: @end_byte }.compact end |
#validate_order! ⇒ void
This method returns an undefined value.
92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/ibex/location.rb', line 92 def validate_order! if @end_line < @line || (@end_line == @line && @end_column < @column) raise ArgumentError, "end position precedes start position" end start_byte = @start_byte end_byte = @end_byte return unless start_byte && end_byte && end_byte < start_byte raise ArgumentError, "end_byte precedes start_byte" end |