Class: Ibex::Runtime::CST::IncrementalParseSession
- Inherits:
-
Object
- Object
- Ibex::Runtime::CST::IncrementalParseSession
- Defined in:
- lib/ibex/runtime/cst/incremental/session.rb,
sig/ibex/runtime/cst/incremental/session.rbs
Overview
A mutable, single-owner session around immutable source and syntax results.
Instance Attribute Summary collapse
- #last_blender ⇒ Blender? readonly
- #last_relex_result ⇒ RelexResult? readonly
- #parse_memo ⇒ ParseMemo readonly
- #result ⇒ SyntaxResult readonly
- #source_text ⇒ SourceText readonly
- #token_memo ⇒ TokenMemo readonly
Instance Method Summary collapse
- #blender_reused_ratio(blender, root, relexed) ⇒ Float
- #build_blender(old_root, old_memo, lexed, edits) ⇒ Blender
-
#edit(edits) ⇒ SyntaxResult
Apply edits expressed against the current source and return syntax only.
- #edit_locked(edits) ⇒ SyntaxResult
- #emit_reuse_event(blender, relexed, reused_ratio) ⇒ void
-
#empty_parse_memo(root) ⇒ ParseMemo
Error trees cannot be reused, but retain position-aligned disposable memo storage.
- #enforce_memo_budget! ⇒ void
- #finish_blended_edit(relexed, blender) ⇒ SyntaxResult
- #finish_full_fallback(previous_memo, edits, reason) ⇒ SyntaxResult
-
#initialize(parser_class, source_text, resource_limits: nil, blender: true, event_observer: nil) ⇒ IncrementalParseSession
constructor
A new instance of IncrementalParseSession.
-
#last_full_fallback? ⇒ Boolean
Whether the last completed edit discarded all incremental reuse and reparsed the current source from a fresh token stream.
-
#observe {|arg0| ... } ⇒ Observation::Subscription
Observe parser and incremental runtime events.
- #parse_current(reused_ratio) ⇒ [ SyntaxResult, TokenMemo, ParseMemo ]
- #report_blender_fallback(blender) ⇒ void
- #scan_current ⇒ LexedSyntax?
- #unobserve(subscription) ⇒ Boolean
- #validate_parser! ⇒ void
Constructor Details
#initialize(parser_class, source_text, resource_limits: nil, blender: true, event_observer: nil) ⇒ IncrementalParseSession
Returns a new instance of IncrementalParseSession.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 27 def initialize(parser_class, source_text, resource_limits: nil, blender: true, event_observer: nil) unless source_text.is_a?(SourceText) raise ArgumentError, "incremental_session requires an Ibex::Runtime::CST::SourceText" end @resource_limits = resource_limits || ResourceLimits.new #: ResourceLimits @parser = parser_class.__send__(:new, resource_limits: @resource_limits) #: Parser validate_parser! @parser.observe { |event| event_observer.call(event, @parser) } if event_observer @cache = NodeCache.new #: NodeCache @source_text = source_text @last_relex_result = nil #: RelexResult? @last_blender = nil #: Blender? @last_full_fallback = false #: bool @blender_enabled = blender @mutex = Mutex.new @result, @token_memo, @parse_memo = parse_current(0.0) enforce_memo_budget! end |
Instance Attribute Details
#last_blender ⇒ Blender? (readonly)
17 18 19 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 17 def last_blender @last_blender end |
#last_relex_result ⇒ RelexResult? (readonly)
16 17 18 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 16 def last_relex_result @last_relex_result end |
#parse_memo ⇒ ParseMemo (readonly)
15 16 17 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 15 def parse_memo @parse_memo end |
#result ⇒ SyntaxResult (readonly)
13 14 15 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 13 def result @result end |
#source_text ⇒ SourceText (readonly)
12 13 14 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 12 def source_text @source_text end |
#token_memo ⇒ TokenMemo (readonly)
14 15 16 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 14 def token_memo @token_memo end |
Instance Method Details
#blender_reused_ratio(blender, root, relexed) ⇒ Float
141 142 143 144 145 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 141 def blender_reused_ratio(blender, root, relexed) return relexed.reused_ratio unless @blender_enabled blender.reused_descendants.fdiv(root.descendant_count) end |
#build_blender(old_root, old_memo, lexed, edits) ⇒ Blender
109 110 111 112 113 114 115 116 117 118 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 109 def build_blender(old_root, old_memo, lexed, edits) Blender.new( old_root: old_root, parse_memo: old_memo, lexed: lexed, edits: edits, max_decomposed_nodes: @resource_limits.max_incremental_decomposed_nodes, enabled: @blender_enabled ) end |
#edit(edits) ⇒ SyntaxResult
Apply edits expressed against the current source and return syntax only.
49 50 51 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 49 def edit(edits) @mutex.synchronize { edit_locked(TextEdit.normalize(edits)) } end |
#edit_locked(edits) ⇒ SyntaxResult
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 72 def edit_locked(edits) return @result if edits.empty? previous_source = @source_text previous_memo = @token_memo previous_root = @result.syntax_root previous_parse_memo = @parse_memo previous_result = @result previous_relex_result = @last_relex_result previous_blender = @last_blender previous_full_fallback = @last_full_fallback @source_text = @source_text.apply(edits) lexed = scan_current return finish_full_fallback(previous_memo, edits, :lexical_error) unless lexed relexed = Relexer.reconcile(previous_memo, lexed.memo, edits) blender = build_blender(previous_root, previous_parse_memo, lexed.with_memo(relexed.memo), edits) finish_blended_edit(relexed, blender) rescue StandardError @source_text = previous_source @token_memo = previous_memo @parse_memo = previous_parse_memo @result = previous_result @last_relex_result = previous_relex_result @last_blender = previous_blender @last_full_fallback = previous_full_fallback raise end |
#emit_reuse_event(blender, relexed, reused_ratio) ⇒ void
This method returns an undefined value.
148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 148 def emit_reuse_event(blender, relexed, reused_ratio) @parser.__send__( :emit_incremental_event, :cst_reuse, { "stage" => blender.reused_descendants.positive? ? "subtree" : "lexical", "reused_tokens" => relexed.reused_count, "token_count" => relexed.memo.tokens.length, "reused_ratio" => reused_ratio } ) end |
#empty_parse_memo(root) ⇒ ParseMemo
Error trees cannot be reused, but retain position-aligned disposable memo storage.
193 194 195 196 197 198 199 200 201 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 193 def empty_parse_memo(root) tables = @parser.__send__(:parser_tables) #: Hash[Symbol, untyped] ParseMemo.new( left_states: Array.new(root.descendant_count), grammar_digest: tables.fetch(:grammar_digest), state_count: tables.fetch(:state_count), production_count: tables.fetch(:production_count) ) end |
#enforce_memo_budget! ⇒ void
This method returns an undefined value.
237 238 239 240 241 242 243 244 245 246 247 248 249 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 237 def enforce_memo_budget! observed = @token_memo.estimated_bytes + @parse_memo.estimated_bytes limit = @resource_limits.max_session_memo_bytes return unless observed > limit @parser.__send__( :emit_incremental_event, :cst_fallback, { "reason" => "memo_budget", "limit" => limit, "observed" => observed } ) @cache.clear @token_memo = TokenMemo.from_root(@result.syntax_root) end |
#finish_blended_edit(relexed, blender) ⇒ SyntaxResult
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 121 def finish_blended_edit(relexed, blender) @last_relex_result = relexed @last_blender = blender @last_full_fallback = false parsed = @parser.__send__(:parse_syntax_token_source, blender, @cache) reused_ratio = blender_reused_ratio(blender, parsed.syntax_root.green, relexed) @token_memo = relexed.memo @parse_memo = @parser.__send__(:syntax_parse_memo) || empty_parse_memo(parsed.syntax_root.green) @result = SyntaxResult.new( syntax_root: parsed.syntax_root, diagnostics: parsed.diagnostics, reused_ratio: reused_ratio ) report_blender_fallback(blender) enforce_memo_budget! emit_reuse_event(blender, relexed, reused_ratio) @result end |
#finish_full_fallback(previous_memo, edits, reason) ⇒ SyntaxResult
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 219 def finish_full_fallback(previous_memo, edits, reason) fresh_result, fresh_memo, fresh_parse_memo = parse_current(0.0) @last_relex_result = Relexer.reconcile(previous_memo, fresh_memo, edits) @last_blender = nil @last_full_fallback = true @token_memo = fresh_memo @parse_memo = fresh_parse_memo @result = fresh_result @parser.__send__( :emit_incremental_event, :cst_fallback, { "reason" => reason.to_s, "limit" => 0, "observed" => 0 } ) enforce_memo_budget! @result end |
#last_full_fallback? ⇒ Boolean
Whether the last completed edit discarded all incremental reuse and reparsed the current source from a fresh token stream.
67 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 67 def last_full_fallback? = @last_full_fallback |
#observe {|arg0| ... } ⇒ Observation::Subscription
Observe parser and incremental runtime events.
55 56 57 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 55 def observe(&observer) @parser.observe(&observer) end |
#parse_current(reused_ratio) ⇒ [ SyntaxResult, TokenMemo, ParseMemo ]
178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 178 def parse_current(reused_ratio) parsed = @parser.__send__(:parse_syntax_with_cache, @source_text, @cache) result = SyntaxResult.new( syntax_root: parsed.syntax_root, diagnostics: parsed.diagnostics, reused_ratio: reused_ratio ) states = @parser.__send__(:syntax_token_states) parse_memo = @parser.__send__(:syntax_parse_memo) || empty_parse_memo(result.syntax_root.green) [result, TokenMemo.from_root(result.syntax_root, states: states), parse_memo] end |
#report_blender_fallback(blender) ⇒ void
This method returns an undefined value.
204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 204 def report_blender_fallback(blender) return unless blender.fallback_reason @parser.__send__( :emit_incremental_event, :cst_fallback, { "reason" => blender.fallback_reason.to_s, "limit" => @resource_limits.max_incremental_decomposed_nodes, "observed" => blender.decomposed_nodes } ) end |
#scan_current ⇒ LexedSyntax?
102 103 104 105 106 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 102 def scan_current @parser.__send__(:scan_syntax_with_cache, @source_text, @cache) rescue ParseError nil end |
#unobserve(subscription) ⇒ Boolean
60 61 62 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 60 def unobserve(subscription) @parser.unobserve(subscription) end |
#validate_parser! ⇒ void
This method returns an undefined value.
162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 162 def validate_parser! unless @parser.is_a?(GeneratedLexer) raise IncrementalUnsupportedError, "incremental parsing requires a generated lexer" end tables = @parser.__send__(:parser_tables) #: Hash[Symbol, untyped] config = tables[:cst] unless tables.fetch(:format_version) >= 6 && config.is_a?(Hash) raise IncrementalUnsupportedError, "incremental parsing requires a format-v6 Red/Green CST parser" end return unless config.fetch(:trivia_policy) == :drop raise IncrementalUnsupportedError, "incremental parsing does not support drop trivia policy" end |