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) ⇒ IncrementalParseSession
constructor
A new instance of IncrementalParseSession.
-
#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) ⇒ IncrementalParseSession
Returns a new instance of IncrementalParseSession.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 26 def initialize(parser_class, source_text, resource_limits: nil, blender: true) 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! @cache = NodeCache.new #: NodeCache @source_text = source_text @last_relex_result = nil #: RelexResult? @last_blender = nil #: Blender? @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
118 119 120 121 122 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 118 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
87 88 89 90 91 92 93 94 95 96 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 87 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.
46 47 48 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 46 def edit(edits) @mutex.synchronize { edit_locked(TextEdit.normalize(edits)) } end |
#edit_locked(edits) ⇒ SyntaxResult
64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 64 def edit_locked(edits) return @result if edits.empty? previous_memo = @token_memo previous_root = @result.syntax_root previous_parse_memo = @parse_memo @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) end |
#emit_reuse_event(blender, relexed, reused_ratio) ⇒ void
This method returns an undefined value.
125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 125 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.
170 171 172 173 174 175 176 177 178 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 170 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.
213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 213 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
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 99 def finish_blended_edit(relexed, blender) @last_relex_result = relexed @last_blender = blender 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
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 196 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 @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 |
#observe {|arg0| ... } ⇒ Observation::Subscription
Observe parser and incremental runtime events.
52 53 54 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 52 def observe(&observer) @parser.observe(&observer) end |
#parse_current(reused_ratio) ⇒ [ SyntaxResult, TokenMemo, ParseMemo ]
155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 155 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.
181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 181 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?
80 81 82 83 84 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 80 def scan_current @parser.__send__(:scan_syntax_with_cache, @source_text, @cache) rescue ParseError nil end |
#unobserve(subscription) ⇒ Boolean
57 58 59 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 57 def unobserve(subscription) @parser.unobserve(subscription) end |
#validate_parser! ⇒ void
This method returns an undefined value.
139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/ibex/runtime/cst/incremental/session.rb', line 139 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 |