Class: Ibex::Frontend::SourceDocument
- Inherits:
-
Object
- Object
- Ibex::Frontend::SourceDocument
- Defined in:
- lib/ibex/frontend/source_document.rb,
sig/ibex/frontend/source_document.rbs
Overview
Lossless grammar source plus its semantic parse and lexical concrete syntax tree.
Instance Attribute Summary collapse
- #ast ⇒ AST::Root, ... readonly
- #cst ⇒ CST::Document readonly
- #file ⇒ String readonly
- #source ⇒ String readonly
- #tokens ⇒ Array[Token] readonly
Instance Method Summary collapse
- #build_line_starts ⇒ Array[Integer]
- #byte_offset_at(line, column) ⇒ Integer
- #full_source_coverage? ⇒ Boolean
-
#initialize(source:, file:, tokens:, cst:, ast: nil) ⇒ SourceDocument
constructor
A new instance of SourceDocument.
- #position_at(byte_offset) ⇒ SourcePosition
- #render ⇒ String
- #segment_slices_match? ⇒ Boolean
- #slice(span) ⇒ String
- #span(start_byte, end_byte) ⇒ SourceSpan
- #token_for(segment) ⇒ Token?
- #validate_byte_offset(byte_offset) ⇒ void
- #validate_render ⇒ void
- #validate_span(span) ⇒ void
- #with_ast(ast) ⇒ SourceDocument
Constructor Details
#initialize(source:, file:, tokens:, cst:, ast: nil) ⇒ SourceDocument
Returns a new instance of SourceDocument.
106 107 108 109 110 111 112 113 114 115 |
# File 'lib/ibex/frontend/source_document.rb', line 106 def initialize(source:, file:, tokens:, cst:, ast: nil) @source = SourceEncoding.validated_utf8(source, file) @file = file.dup.freeze @tokens = tokens.dup.freeze @cst = cst @ast = ast @line_starts = build_line_starts.freeze #: Array[Integer] validate_render freeze end |
Instance Attribute Details
#ast ⇒ AST::Root, ... (readonly)
102 103 104 |
# File 'lib/ibex/frontend/source_document.rb', line 102 def ast @ast end |
#cst ⇒ CST::Document (readonly)
101 102 103 |
# File 'lib/ibex/frontend/source_document.rb', line 101 def cst @cst end |
#file ⇒ String (readonly)
99 100 101 |
# File 'lib/ibex/frontend/source_document.rb', line 99 def file @file end |
#source ⇒ String (readonly)
98 99 100 |
# File 'lib/ibex/frontend/source_document.rb', line 98 def source @source end |
#tokens ⇒ Array[Token] (readonly)
100 101 102 |
# File 'lib/ibex/frontend/source_document.rb', line 100 def tokens @tokens end |
Instance Method Details
#build_line_starts ⇒ Array[Integer]
175 176 177 178 179 180 181 182 183 184 |
# File 'lib/ibex/frontend/source_document.rb', line 175 def build_line_starts starts = [0] offset = 0 binary_source = source.b while (newline = binary_source.index("\n", offset)) offset = newline + 1 starts << offset end starts end |
#byte_offset_at(line, column) ⇒ Integer
141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/ibex/frontend/source_document.rb', line 141 def byte_offset_at(line, column) raise ArgumentError, "line and column must be positive" unless line.positive? && column.positive? line_start = @line_starts[line - 1] raise ArgumentError, "line is outside the source" unless line_start line_end = @line_starts[line] || source.bytesize line_text = source.byteslice(line_start, line_end - line_start) || "" line_text = line_text.delete_suffix("\n") character_count = column - 1 raise ArgumentError, "column is outside the source line" if character_count > line_text.length line_start + line_text.each_char.take(character_count).join.bytesize end |
#full_source_coverage? ⇒ Boolean
194 195 196 197 198 199 200 201 202 203 |
# File 'lib/ibex/frontend/source_document.rb', line 194 def full_source_coverage? segments = cst.segments return source.empty? if segments.empty? first = segments.first last = segments.last return false unless first && last first.span.start_byte.zero? && last.span.end_byte == source.bytesize end |
#position_at(byte_offset) ⇒ SourcePosition
129 130 131 132 133 134 135 136 137 138 |
# File 'lib/ibex/frontend/source_document.rb', line 129 def position_at(byte_offset) validate_byte_offset(byte_offset) prefix = source.byteslice(0, byte_offset) || "" raise ArgumentError, "byte offset must be on a UTF-8 character boundary" unless prefix.valid_encoding? line_index = (@line_starts.bsearch_index { |start| start > byte_offset } || @line_starts.length) - 1 line_start = @line_starts.fetch(line_index) line_prefix = source.byteslice(line_start, byte_offset - line_start) || "" SourcePosition.new(byte_offset: byte_offset, line: line_index + 1, column: line_prefix.length + 1) end |
#render ⇒ String
118 119 120 |
# File 'lib/ibex/frontend/source_document.rb', line 118 def render cst.render end |
#segment_slices_match? ⇒ Boolean
206 207 208 209 210 211 |
# File 'lib/ibex/frontend/source_document.rb', line 206 def segment_slices_match? cst.segments.all? do |segment| segment.span.file == file && source.byteslice(segment.span.start_byte, segment.span.length) == segment.text end end |
#slice(span) ⇒ String
123 124 125 126 |
# File 'lib/ibex/frontend/source_document.rb', line 123 def slice(span) validate_span(span) source.byteslice(span.start_byte, span.length) || "" end |
#span(start_byte, end_byte) ⇒ SourceSpan
157 158 159 |
# File 'lib/ibex/frontend/source_document.rb', line 157 def span(start_byte, end_byte) SourceSpan.new(file: file, start: position_at(start_byte), finish: position_at(end_byte)) end |
#token_for(segment) ⇒ Token?
162 163 164 165 |
# File 'lib/ibex/frontend/source_document.rb', line 162 def token_for(segment) index = segment.token_index index && tokens[index] end |
#validate_byte_offset(byte_offset) ⇒ void
This method returns an undefined value.
214 215 216 217 218 |
# File 'lib/ibex/frontend/source_document.rb', line 214 def validate_byte_offset(byte_offset) return if byte_offset.between?(0, source.bytesize) raise ArgumentError, "byte offset is outside the source" end |
#validate_render ⇒ void
This method returns an undefined value.
187 188 189 190 191 |
# File 'lib/ibex/frontend/source_document.rb', line 187 def validate_render return if full_source_coverage? && segment_slices_match? && render == source raise ArgumentError, "concrete syntax tree does not reproduce its source" end |
#validate_span(span) ⇒ void
This method returns an undefined value.
221 222 223 224 225 226 |
# File 'lib/ibex/frontend/source_document.rb', line 221 def validate_span(span) raise ArgumentError, "span belongs to another source" unless span.file == file validate_byte_offset(span.start_byte) validate_byte_offset(span.end_byte) end |
#with_ast(ast) ⇒ SourceDocument
168 169 170 |
# File 'lib/ibex/frontend/source_document.rb', line 168 def with_ast(ast) self.class.new(source: source, file: file, tokens: tokens, cst: cst, ast: ast) end |