Class: Ibex::Frontend::SourceCursor
- Inherits:
-
Object
- Object
- Ibex::Frontend::SourceCursor
- Defined in:
- lib/ibex/frontend/source_cursor.rb,
sig/ibex/frontend/source_cursor.rbs
Overview
Advances through source text while maintaining one-based coordinates.
Instance Attribute Summary collapse
- #byte_offset ⇒ Integer readonly
- #column ⇒ Integer readonly
- #file ⇒ String readonly
- #index ⇒ Integer readonly
- #line ⇒ Integer readonly
- #source ⇒ String readonly
Instance Method Summary collapse
- #advance(count = 1) ⇒ void
- #advance_ascii(count) ⇒ void
- #advance_utf8(count) ⇒ void
- #byte_offset_for_character(character_index) ⇒ Integer
- #continuation_byte?(byte) ⇒ Boolean
- #eof? ⇒ Boolean
-
#initialize(source, file) ⇒ SourceCursor
constructor
A new instance of SourceCursor.
- #location ⇒ Location
- #peek(offset = 0) ⇒ String?
- #position ⇒ SourcePosition
- #previous_character_byte_offset(byte_offset) ⇒ Integer
- #rest ⇒ String
- #span_from(start) ⇒ SourceSpan
- #utf8_character_width(byte_offset) ⇒ Integer
- #walk_to_character(byte_offset, from_index, to_index) ⇒ Integer
Constructor Details
#initialize(source, file) ⇒ SourceCursor
Returns a new instance of SourceCursor.
51 52 53 54 55 56 57 58 59 60 |
# File 'lib/ibex/frontend/source_cursor.rb', line 51 def initialize(source, file) @source = SourceEncoding.validated_utf8(source, file) @file = file.dup.freeze @ascii_only = @source.ascii_only? @character_length = @ascii_only ? @source.bytesize : @source.length @index = 0 @byte_offset = 0 @line = 1 @column = 1 end |
Instance Attribute Details
#byte_offset ⇒ Integer (readonly)
46 47 48 |
# File 'lib/ibex/frontend/source_cursor.rb', line 46 def byte_offset @byte_offset end |
#column ⇒ Integer (readonly)
48 49 50 |
# File 'lib/ibex/frontend/source_cursor.rb', line 48 def column @column end |
#file ⇒ String (readonly)
44 45 46 |
# File 'lib/ibex/frontend/source_cursor.rb', line 44 def file @file end |
#index ⇒ Integer (readonly)
45 46 47 |
# File 'lib/ibex/frontend/source_cursor.rb', line 45 def index @index end |
#line ⇒ Integer (readonly)
47 48 49 |
# File 'lib/ibex/frontend/source_cursor.rb', line 47 def line @line end |
#source ⇒ String (readonly)
43 44 45 |
# File 'lib/ibex/frontend/source_cursor.rb', line 43 def source @source end |
Instance Method Details
#advance(count = 1) ⇒ void
This method returns an undefined value.
111 112 113 114 115 |
# File 'lib/ibex/frontend/source_cursor.rb', line 111 def advance(count = 1) return advance_utf8(count) unless @ascii_only advance_ascii(count) end |
#advance_ascii(count) ⇒ void
This method returns an undefined value.
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/ibex/frontend/source_cursor.rb', line 120 def advance_ascii(count) count.times do break if @index >= @character_length newline = @source.getbyte(@index) == 10 @index += 1 @byte_offset = @index if newline @line += 1 @column = 1 else @column += 1 end end end |
#advance_utf8(count) ⇒ void
This method returns an undefined value.
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/ibex/frontend/source_cursor.rb', line 137 def advance_utf8(count) count.times do break if @index >= @character_length leading_byte = @source.getbyte(@byte_offset) raise Ibex::Error, "#{@file}: input must be valid UTF-8" unless leading_byte @index += 1 @byte_offset += if leading_byte < 0x80 1 elsif leading_byte < 0xE0 2 elsif leading_byte < 0xF0 3 else 4 end if leading_byte == 10 @line += 1 @column = 1 else @column += 1 end end end |
#byte_offset_for_character(character_index) ⇒ Integer
164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/ibex/frontend/source_cursor.rb', line 164 def byte_offset_for_character(character_index) start_distance = character_index current_distance = (character_index - @index).abs finish_distance = @character_length - character_index if current_distance <= start_distance && current_distance <= finish_distance walk_to_character(@byte_offset, @index, character_index) elsif start_distance <= finish_distance walk_to_character(0, 0, character_index) else walk_to_character(@source.bytesize, @character_length, character_index) end end |
#continuation_byte?(byte) ⇒ Boolean
213 214 215 |
# File 'lib/ibex/frontend/source_cursor.rb', line 213 def continuation_byte?(byte) byte >= 0x80 && byte < 0xC0 end |
#eof? ⇒ Boolean
63 64 65 |
# File 'lib/ibex/frontend/source_cursor.rb', line 63 def eof? @index >= @character_length end |
#location ⇒ Location
96 97 98 |
# File 'lib/ibex/frontend/source_cursor.rb', line 96 def location Location.new(file: @file, line: @line, column: @column) end |
#peek(offset = 0) ⇒ String?
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/ibex/frontend/source_cursor.rb', line 68 def peek(offset = 0) character_index = @index + offset if @ascii_only return if character_index >= @character_length return @source.byteslice(character_index, 1) end character_index += @character_length if character_index.negative? return if character_index.negative? || character_index >= @character_length start_byte = if character_index == @index @byte_offset elsif character_index == @index + 1 @byte_offset + utf8_character_width(@byte_offset) else byte_offset_for_character(character_index) end @source.byteslice(start_byte, utf8_character_width(start_byte)) end |
#position ⇒ SourcePosition
101 102 103 |
# File 'lib/ibex/frontend/source_cursor.rb', line 101 def position SourcePosition.new(byte_offset: @byte_offset, line: @line, column: @column) end |
#previous_character_byte_offset(byte_offset) ⇒ Integer
192 193 194 195 196 197 198 |
# File 'lib/ibex/frontend/source_cursor.rb', line 192 def previous_character_byte_offset(byte_offset) byte_offset -= 1 while (byte = @source.getbyte(byte_offset)) && continuation_byte?(byte) byte_offset -= 1 end byte_offset end |
#rest ⇒ String
90 91 92 93 |
# File 'lib/ibex/frontend/source_cursor.rb', line 90 def rest @source.byteslice(@byte_offset, @source.bytesize - @byte_offset) || String.new(encoding: Encoding::UTF_8) end |
#span_from(start) ⇒ SourceSpan
106 107 108 |
# File 'lib/ibex/frontend/source_cursor.rb', line 106 def span_from(start) SourceSpan.new(file: @file, start: start, finish: position) end |
#utf8_character_width(byte_offset) ⇒ Integer
201 202 203 204 205 206 207 208 209 210 |
# File 'lib/ibex/frontend/source_cursor.rb', line 201 def utf8_character_width(byte_offset) leading_byte = @source.getbyte(byte_offset) raise Ibex::Error, "#{@file}: input must be valid UTF-8" unless leading_byte return 1 if leading_byte < 0x80 return 2 if leading_byte < 0xE0 return 3 if leading_byte < 0xF0 4 end |
#walk_to_character(byte_offset, from_index, to_index) ⇒ Integer
179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/ibex/frontend/source_cursor.rb', line 179 def walk_to_character(byte_offset, from_index, to_index) while from_index < to_index byte_offset += utf8_character_width(byte_offset) from_index += 1 end while from_index > to_index byte_offset = previous_character_byte_offset(byte_offset) from_index -= 1 end byte_offset end |