Class: Ibex::Runtime::SyntaxSession
- Inherits:
-
Object
- Object
- Ibex::Runtime::SyntaxSession
- Defined in:
- lib/ibex/runtime/syntax_session.rb,
sig/ibex/runtime/syntax_session.rbs
Overview
Generated-language syntax boundary backed by the existing Red/Green CST incremental engine. It does not execute parser production actions.
Constant Summary collapse
- TRUSTED_PROFILE =
:trusted_application_code
Instance Attribute Summary collapse
- #execution_profile ⇒ Symbol readonly
- #limits ⇒ SyntaxSessionLimits readonly
Instance Method Summary collapse
-
#apply_edits(edits) ⇒ SyntaxSessionResult
Apply byte-oriented edits against the current source.
- #apply_edits_locked(edits) ⇒ SyntaxSessionResult
- #cancellation_checkpoint! ⇒ void
- #enforce_limit!(resource, limit, observed) ⇒ void
- #handle_runtime_event(event, parser) ⇒ void
- #immutable_diagnostics(diagnostics) ⇒ Array[SyntaxSessionDiagnostic]
-
#initialize(parser_class, source, execution_profile:, resource_limits: nil, limits: nil, cancellation: nil, blender: true) ⇒ SyntaxSession
constructor
A new instance of SyntaxSession.
- #normalize_source(source) ⇒ CST::SourceText
-
#repair(policy: RepairPolicy.new, token_text: {}) ⇒ SyntaxRepairResult
Propose byte edits through the existing bounded repair search, then validate them with a fresh syntax-only parse.
- #reset_operation_evidence ⇒ void
- #result ⇒ SyntaxSessionResult
- #snapshot(parsed) ⇒ SyntaxSessionResult
- #source_text ⇒ CST::SourceText
- #validate_execution_profile(parser_class, acknowledged) ⇒ Symbol
Constructor Details
#initialize(parser_class, source, execution_profile:, resource_limits: nil, limits: nil, cancellation: nil, blender: true) ⇒ SyntaxSession
Returns a new instance of SyntaxSession.
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/ibex/runtime/syntax_session.rb', line 214 def initialize( parser_class, source, execution_profile:, resource_limits: nil, limits: nil, cancellation: nil, blender: true ) @execution_profile = validate_execution_profile(parser_class, execution_profile) @limits = limits || SyntaxSessionLimits.new #: SyntaxSessionLimits unless @limits.is_a?(SyntaxSessionLimits) raise ArgumentError, "limits must be an Ibex::Runtime::SyntaxSessionLimits" end unless cancellation.nil? || cancellation.is_a?(CancellationToken) raise ArgumentError, "cancellation must be an Ibex::Runtime::CancellationToken or nil" end @cancellation = cancellation #: CancellationToken? @parser_class = parser_class #: Class @resource_limits = resource_limits || ResourceLimits.new #: ResourceLimits @mutex = Mutex.new #: Mutex @revision = 0 #: Integer @operation_expected_tokens = [] #: Array[String] @operation_fallback_reasons = [] #: Array[Symbol] source_text = normalize_source(source) enforce_limit!(:source_bytes, @limits.max_source_bytes, source_text.bytesize) cancellation_checkpoint! event_observer = ->(event, parser) { handle_runtime_event(event, parser) } #: Proc @incremental = CST::IncrementalParseSession.new( parser_class, source_text, resource_limits: @resource_limits, blender: blender, event_observer: event_observer ) #: CST::IncrementalParseSession @result = snapshot(@incremental.result) #: SyntaxSessionResult rescue ResourceLimitError => e raise SyntaxSessionResourceLimitError.new(resource: e.resource, limit: e.limit, observed: e.observed), cause: e end |
Instance Attribute Details
#execution_profile ⇒ Symbol (readonly)
208 209 210 |
# File 'lib/ibex/runtime/syntax_session.rb', line 208 def execution_profile @execution_profile end |
#limits ⇒ SyntaxSessionLimits (readonly)
209 210 211 |
# File 'lib/ibex/runtime/syntax_session.rb', line 209 def limits @limits end |
Instance Method Details
#apply_edits(edits) ⇒ SyntaxSessionResult
Apply byte-oriented edits against the current source.
268 269 270 |
# File 'lib/ibex/runtime/syntax_session.rb', line 268 def apply_edits(edits) @mutex.synchronize { apply_edits_locked(edits) } end |
#apply_edits_locked(edits) ⇒ SyntaxSessionResult
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/ibex/runtime/syntax_session.rb', line 288 def apply_edits_locked(edits) raise ArgumentError, "edits must be an Array" unless edits.is_a?(Array) cancellation_checkpoint! enforce_limit!(:edits_per_operation, @limits.max_edits_per_operation, edits.length) normalized = CST::TextEdit.normalize(edits) inserted_bytes = normalized.sum { |edit| edit.insert_text.bytesize } enforce_limit!(:inserted_bytes, @limits.max_inserted_bytes, inserted_bytes) next_source = @incremental.source_text.apply(normalized) enforce_limit!(:source_bytes, @limits.max_source_bytes, next_source.bytesize) return @result if normalized.empty? reset_operation_evidence parsed = @incremental.edit(normalized) @revision += 1 @result = snapshot(parsed) rescue ResourceLimitError => e raise SyntaxSessionResourceLimitError.new(resource: e.resource, limit: e.limit, observed: e.observed), cause: e end |
#cancellation_checkpoint! ⇒ void
This method returns an undefined value.
345 346 347 348 349 |
# File 'lib/ibex/runtime/syntax_session.rb', line 345 def cancellation_checkpoint! return unless @cancellation&.cancelled? raise SyntaxSessionCancelled, "syntax session operation was cancelled" end |
#enforce_limit!(resource, limit, observed) ⇒ void
This method returns an undefined value.
338 339 340 341 342 |
# File 'lib/ibex/runtime/syntax_session.rb', line 338 def enforce_limit!(resource, limit, observed) return unless observed > limit raise SyntaxSessionResourceLimitError.new(resource: resource, limit: limit, observed: observed) end |
#handle_runtime_event(event, parser) ⇒ void
This method returns an undefined value.
352 353 354 355 356 357 358 359 360 361 362 |
# File 'lib/ibex/runtime/syntax_session.rb', line 352 def handle_runtime_event(event, parser) cancellation_checkpoint! case event.type when :error parser.expected_tokens.each do |token| @operation_expected_tokens << token unless @operation_expected_tokens.include?(token) end when :cst_fallback @operation_fallback_reasons << event.data.fetch("reason").to_s.to_sym end end |
#immutable_diagnostics(diagnostics) ⇒ Array[SyntaxSessionDiagnostic]
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 |
# File 'lib/ibex/runtime/syntax_session.rb', line 395 def immutable_diagnostics(diagnostics) diagnostics.map do |diagnostic| if diagnostic.is_a?(ParseError) SyntaxSessionDiagnostic.new( kind: :parse_error, data: { message: diagnostic., token_id: diagnostic.token_id, token_name: diagnostic.token_name, expected_tokens: diagnostic.expected_tokens, location: EventSanitizer.location(diagnostic.location) } ) else data = diagnostic.is_a?(Hash) ? diagnostic : { message: diagnostic.to_s } SyntaxSessionDiagnostic.new(kind: :syntax_error, data: data) end end.freeze end |
#normalize_source(source) ⇒ CST::SourceText
327 328 329 330 331 332 333 334 335 |
# File 'lib/ibex/runtime/syntax_session.rb', line 327 def normalize_source(source) return source if source.is_a?(CST::SourceText) raise ArgumentError, "source must be a String or Ibex::Runtime::CST::SourceText" unless source.is_a?(String) unless source.encoding.ascii_compatible? raise Encoding::CompatibilityError, "syntax session source must use an ASCII-compatible encoding" end CST::SourceText.new(source) end |
#repair(policy: RepairPolicy.new, token_text: {}) ⇒ SyntaxRepairResult
Propose byte edits through the existing bounded repair search, then validate them with a fresh syntax-only parse. The session is unchanged.
275 276 277 278 279 280 281 282 283 |
# File 'lib/ibex/runtime/syntax_session.rb', line 275 def repair(policy: RepairPolicy.new, token_text: {}) @mutex.synchronize do SyntaxRepairer.new( @parser_class, @incremental.source_text, @result, execution_profile: @execution_profile, resource_limits: @resource_limits, limits: @limits, cancellation: @cancellation, policy: policy, token_text: token_text ).call end end |
#reset_operation_evidence ⇒ void
This method returns an undefined value.
365 366 367 368 |
# File 'lib/ibex/runtime/syntax_session.rb', line 365 def reset_operation_evidence @operation_expected_tokens = [] #: Array[String] @operation_fallback_reasons = [] #: Array[Symbol] end |
#result ⇒ SyntaxSessionResult
262 263 264 |
# File 'lib/ibex/runtime/syntax_session.rb', line 262 def result @mutex.synchronize { @result } end |
#snapshot(parsed) ⇒ SyntaxSessionResult
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 |
# File 'lib/ibex/runtime/syntax_session.rb', line 371 def snapshot(parsed) token_count = @incremental.token_memo.tokens.length reused_tokens = if @incremental.last_full_fallback? 0 else @incremental.last_relex_result&.reused_count.to_i end metrics = SyntaxSessionMetrics.new( reused_ratio: parsed.reused_ratio, reused_tokens: reused_tokens, token_count: token_count, fallback_reasons: @operation_fallback_reasons ) SyntaxSessionResult.new( revision: @revision, syntax_root: parsed.syntax_root, diagnostics: immutable_diagnostics(parsed.diagnostics), expected_tokens: @operation_expected_tokens, metrics: metrics, status: parsed.diagnostics.empty? ? :ok : :syntax_error ) end |
#source_text ⇒ CST::SourceText
257 258 259 |
# File 'lib/ibex/runtime/syntax_session.rb', line 257 def source_text @mutex.synchronize { @incremental.source_text } end |
#validate_execution_profile(parser_class, acknowledged) ⇒ Symbol
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
# File 'lib/ibex/runtime/syntax_session.rb', line 309 def validate_execution_profile(parser_class, acknowledged) unless parser_class.is_a?(Class) && parser_class <= Parser raise ArgumentError, "parser_class must inherit from Ibex::Runtime::Parser" end profile = parser_class.syntax_execution_profile unless profile == TRUSTED_PROFILE raise SyntaxSessionTrustError, "declarative syntax artifacts are not available; generated parser profile was #{profile.inspect}" end return profile if acknowledged == profile raise SyntaxSessionTrustError, "syntax_session executes generated lexer actions; " \ "pass execution_profile: #{profile.inspect} to acknowledge it" end |