Module: Kumi::Analyzer
- Defined in:
- lib/kumi/analyzer.rb
Defined Under Namespace
Classes: Result
Constant Summary collapse
- Passes =
Core::Analyzer::Passes
- ERROR_THRESHOLD_PASS =
Passes::NormalizeToNASTPass
- DEFAULT_PASSES =
[ Passes::NameIndexer, # 1. Finds all names and checks for duplicates. Passes::ImportAnalysisPass, # 2. Loads source schemas for imports (NEW). Passes::InputCollector, # 3. Collects field metadata from input declarations. Passes::InputFormSchemaPass, # 4. Builds minimal form schema from input metadata. Passes::DeclarationValidator, # 5. Checks the basic structure of each rule. Passes::SemanticConstraintValidator, # 6. Validates DSL semantic constraints at AST level. Passes::DependencyResolver, # 7. Builds the dependency graph with conditional dependencies. Passes::Toposorter, # 8. Creates the final evaluation order, allowing safe cycles. Passes::InputAccessPlannerPass # 9. Plans access strategies for input fields. ].freeze
- HIR_TO_LIR_PASSES =
Pipeline passes for the determinisitic NAST->LIR approach
[ Passes::NormalizeToNASTPass, # Normalizes AST to uniform NAST representation Passes::ConstantFoldingPass, # Folds constant expressions in NAST Passes::NASTDimensionalAnalyzerPass, # Extracts dimensional and type metadata from NAST Passes::SNASTPass, # Creates Semantic NAST with dimensional stamps and execution plans Passes::UnsatDetector, # Detects impossible constraints with resolved function IDs and SNAST metadata Passes::OutputSchemaPass, # Builds minimal output schema from SNAST Passes::AttachTerminalInfoPass, # Attaches key_chain info to InputRef nodes Passes::AttachAnchorsPass, Passes::PrecomputeAccessPathsPass, Passes::LIR::LowerPass, # Lowers the schema to LIR (LIR Structs) Passes::LIR::HoistScalarReferencesPass, Passes::LIR::InlineDeclarationsPass, # Inlines LoadDeclaration when site axes == decl axes Passes::LIR::LocalCSEPass, # Local CSE optimization for pure LIR operations Passes::LIR::InstructionSchedulingPass, Passes::LIR::LoopFusionPass, Passes::LIR::LocalCSEPass, # Local CSE optimization for pure LIR operations Passes::LIR::DeadCodeEliminationPass, # Removes dead code Passes::LIR::KernelBindingPass, # Binds kernels to LIR operations Passes::LIR::LoopInvariantCodeMotionPass # Passes::LIR::ValidationPass # Validates LIR structural and contextual correctness ].freeze
- RUBY_TARGET_PASSES =
[ Passes::LIR::ConstantPropagationPass, # Ruby uses this Intra-block constant propagation Passes::LIR::DeadCodeEliminationPass, # Removes dead code Passes::Codegen::RubyPass, # Generates ruby code from LIR Passes::Codegen::JsPass ]
Class Method Summary collapse
- .analyze!(schema, passes: DEFAULT_PASSES, registry: nil, **opts) ⇒ Object
- .create_analysis_result(state) ⇒ Object
-
.format_errors(errors) ⇒ Object
Handle both old and new error formats for backward compatibility.
- .handle_analysis_errors(errors) ⇒ Object
- .run_analysis_passes(schema, passes, state, errors) ⇒ Object
Class Method Details
.analyze!(schema, passes: DEFAULT_PASSES, registry: nil, **opts) ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/kumi/analyzer.rb', line 52 def self.analyze!(schema, passes: DEFAULT_PASSES, registry: nil, **opts) errors = [] schema_digest = schema.digest Core::Analyzer::Checkpoint.stop_after registry ||= Kumi::RegistryV2.load state = Core::Analyzer::AnalysisState.new(opts).with(:registry, registry).with(:schema_digest, schema_digest) state, stopped = run_analysis_passes(schema, passes, state, errors) return create_analysis_result(state) if stopped state, stopped = run_analysis_passes(schema, HIR_TO_LIR_PASSES, state, errors) return create_analysis_result(state) if stopped state, = run_analysis_passes(schema, RUBY_TARGET_PASSES, state, errors) handle_analysis_errors(errors) unless errors.empty? create_analysis_result(state) end |
.create_analysis_result(state) ⇒ Object
133 134 135 136 137 138 139 140 141 142 |
# File 'lib/kumi/analyzer.rb', line 133 def self.create_analysis_result(state) Result.new( definitions: state[:declarations], dependency_graph: state[:dependencies], leaf_map: state[:leaves], topo_order: state[:evaluation_order], decl_types: state[:inferred_types], state: state.to_h ) end |
.format_errors(errors) ⇒ Object
Handle both old and new error formats for backward compatibility
145 146 147 148 149 |
# File 'lib/kumi/analyzer.rb', line 145 def self.format_errors(errors) return "" if errors.empty? errors.map(&:to_s).join("\n") end |
.handle_analysis_errors(errors) ⇒ Object
122 123 124 125 126 127 128 129 130 131 |
# File 'lib/kumi/analyzer.rb', line 122 def self.handle_analysis_errors(errors) raise Kumi::Errors::AnalysisError, "\n" + errors.join("\n") if errors.first.is_a? String type_errors = errors.select { |e| e.type == :type } first_error_location = errors.first.location raise Errors::TypeError.new(format_errors(errors), first_error_location) if type_errors.any? raise Errors::SemanticError.new(format_errors(errors), first_error_location) end |
.run_analysis_passes(schema, passes, state, errors) ⇒ Object
71 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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/kumi/analyzer.rb', line 71 def self.run_analysis_passes(schema, passes, state, errors) # Resume from a saved state if configured state = Core::Analyzer::Checkpoint.load_initial_state(state) # Prepare options for PassManager debug_on = Core::Analyzer::Debug.enabled? resume_at = Core::Analyzer::Checkpoint.resume_at stop_after = Core::Analyzer::Checkpoint.stop_after # Filter passes based on checkpoint resume point filtered_passes = if resume_at passes.each_with_index do |pass_class, idx| pass_name = pass_class.name.split("::").last if pass_name == resume_at break passes[idx..] end end.flatten.compact else passes end # Check for error threshold pass if !errors.empty? && filtered_passes.include?(ERROR_THRESHOLD_PASS) raise handle_analysis_errors(errors) end # Use PassManager for orchestration manager = Core::Analyzer::PassManager.new(filtered_passes) = { checkpoint_enabled: true, debug_enabled: debug_on, profiling_enabled: true, stop_after: stop_after } result = manager.run(schema, state, errors, ) # Convert PassFailure errors back to ErrorEntry for consistency if result.failed? result.errors.each do |pass_failure| errors << Core::ErrorReporter.create_error( pass_failure., location: pass_failure.location, type: :semantic ) end end [result.final_state, result.stopped || false] end |