tryouts 3.5.1 → 3.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab0eb9c341b10dd5e505a3f62cc33a19a393cb294aecd756809b19c2484784f1
4
- data.tar.gz: cddfd1a827b0ef37a9e69224124f2f696bbd8c2f098edbfce79b563b3017fbcd
3
+ metadata.gz: 958254585920c850e17a18e13699040fad97ec8610f3c2141b32899016dc4194
4
+ data.tar.gz: 3666350beb13d17cf24bf184350fd88746cebbf7b7efc109c3e5af7f8d5c6c3b
5
5
  SHA512:
6
- metadata.gz: 066b7697b04b3501f01a0994e9d10a66c5a94d593bdfa5cb1b618bb1264f228425bd7d8b4d98cf9a555a5e23bbe002f137c943d5195ad4ce972de30f03dfac9c
7
- data.tar.gz: 82390327e8da8e35d0c9eaa4557800b09bba76e065704f9d704acab45ab24dd62e97edc7aa54d8104ee50cab31a9dab321c75449386f70243674c8f2d838716a
6
+ metadata.gz: 1a1091a28256b97c210f8a0a970db3a78d715e1b195e95a5a844e8f32b7d65f7e6d5cb49209baaf2c061b02500ad6bf2d71968cbcb76c16206108eabfa8039f3
7
+ data.tar.gz: 9613b25213146c5da6dbd5153fa101e8139e205f06fb5bcddb8fa172a82fc76dffff56f79d69609c4764623ac9bf0682a0ba8ef4e76fd49517920a9b3e1141af
data/README.md CHANGED
@@ -132,18 +132,15 @@ try --agent --agent-limit 1000 # limit output to 1000 tokens
132
132
 
133
133
  #### Why Not Pipe Test Output Directly to AI?
134
134
 
135
- Raw test output creates several problems when working with AI assistants:
135
+ I mean, you could. If that already works well, you could probably still benefit from an agent that is able to focus on the critical information for the task. And the extra context window space.
136
136
 
137
- - **Token bloat**: Verbose formatting wastes 60-80% of your context window on styling
138
- - **Signal vs noise**: Important failures get buried in passing test details and framework boilerplate
139
- - **Inconsistent parsing**: AI struggles with varying output formats across different test runs
140
- - **Context overflow**: Large test suites exceed AI token limits, truncating critical information
137
+ Raw test output creates problems when working with AI assistants: high token usage with inconsistent parsing across different runs, where the same logical failure might be interpreted differently, making it difficult to reliably produce and analyze results consistently.
141
138
 
142
- #### TOPA: A Better Approach
139
+ #### TOPAZ: A Better Approach
143
140
 
144
- Tryouts' `--agent` mode inspired the development of **TOPA (Test Output Protocol for AI)** - a standardized format optimized for AI analysis. The [tpane](https://github.com/delano/tpane) tool implements this protocol, transforming any test framework's output into structured, token-efficient formats.
141
+ Tryouts' `--agent` mode inspired the development of **TOPAZ (Test Output Protocol for AI Zealots)** - a standardized format optimized for AI analysis. The [tpane](https://github.com/delano/tpane) tool implements this protocol, transforming any test framework's output into structured, token-efficient formats.
145
142
 
146
- Instead of overwhelming AI with raw output, TOPA provides clean semantic data focusing on what actually needs attention - failures, errors, and actionable context.
143
+ Instead of overwhelming AI with raw output, TOPAZ provides clean semantic data focusing on what actually needs attention - failures, errors, and actionable context.
147
144
 
148
145
  ### Exit Codes
149
146
 
@@ -4,13 +4,13 @@ require_relative 'token_budget'
4
4
 
5
5
  class Tryouts
6
6
  class CLI
7
- # TOPA (Test Output Protocol for AI) Formatter
7
+ # TOPAZ (Test Output Protocol for AI Zealots) Formatter
8
8
  #
9
9
  # Language-agnostic test output format designed for LLM context management.
10
- # This formatter implements the TOPA v1.0 specification for structured,
10
+ # This formatter implements the TOPAZ v1.0 specification for structured,
11
11
  # token-efficient test result communication.
12
12
  #
13
- # TOPA Features:
13
+ # TOPAZ Features:
14
14
  # - Language-agnostic field naming (snake_case, hierarchical)
15
15
  # - Standardized execution context (runtime, environment, VCS)
16
16
  # - Token budget awareness with smart truncation
@@ -27,7 +27,7 @@ class Tryouts
27
27
  # - environment: Normalized env vars (ci_system, app_env, etc.)
28
28
  # - test_framework: Framework name, isolation mode, parser
29
29
  # - execution_flags: Runtime flags in normalized form
30
- # - protocol: TOPA version and configuration
30
+ # - protocol: TOPAZ version and configuration
31
31
  # - project: Auto-detected project type
32
32
  # - test_discovery: File pattern matching rules
33
33
  #
@@ -49,7 +49,7 @@ class Tryouts
49
49
  @focus_mode = options[:agent_focus] || :failures
50
50
  @collected_files = []
51
51
  @current_file_data = nil
52
- @total_stats = { files: 0, tests: 0, failures: 0, errors: 0, elapsed: 0 }
52
+ @total_stats = { files: 0, tests: 0, failures: 0, errors: 0, elapsed_time: 0 }
53
53
  @output_rendered = false
54
54
  @options = options # Store all options for execution context display
55
55
  @all_warnings = [] # Store warnings globally for execution details
@@ -120,7 +120,7 @@ class Tryouts
120
120
  # Always update global totals
121
121
  @total_stats[:failures] += failed_count
122
122
  @total_stats[:errors] += error_count
123
- @total_stats[:elapsed] += elapsed_time if elapsed_time
123
+ @total_stats[:elapsed_time] += elapsed_time if elapsed_time
124
124
 
125
125
  # Update per-file data - file_result is called AFTER file_end, so data is in @collected_files
126
126
  relative_file_path = relative_path(file_path)
@@ -175,7 +175,7 @@ class Tryouts
175
175
  error_count: @collected_files.sum { |f| f[:errors].size },
176
176
  successful_files: @collected_files.size - @collected_files.count { |f| f[:failures].any? || f[:errors].any? },
177
177
  total_files: @collected_files.size,
178
- elapsed_time: @total_stats[:elapsed]
178
+ elapsed_time: @total_stats[:elapsed_time]
179
179
  ) unless @output_rendered
180
180
  end
181
181
 
@@ -188,7 +188,7 @@ class Tryouts
188
188
  errors: error_count,
189
189
  successful_files: successful_files,
190
190
  total_files: total_files,
191
- elapsed: elapsed_time
191
+ elapsed_time: elapsed_time,
192
192
  )
193
193
 
194
194
  # Now render all collected data
@@ -278,6 +278,12 @@ class Tryouts
278
278
  def render_summary_only
279
279
  output = []
280
280
 
281
+ time_str = if @total_stats[:elapsed_time] < 2.0
282
+ " (#{(@total_stats[:elapsed_time] * 1000).round}ms)"
283
+ else
284
+ " (#{@total_stats[:elapsed_time].round(2)}s)"
285
+ end
286
+
281
287
  # Add execution context header for agent clarity
282
288
  output << render_execution_context
283
289
  output << ""
@@ -293,13 +299,13 @@ class Tryouts
293
299
  details = []
294
300
  details << "#{failed_count} failed" if failed_count > 0
295
301
  details << "#{error_count} errors" if error_count > 0
296
- status_parts << "FAIL: #{issues_count}/#{@total_stats[:tests]} tests (#{details.join(', ')}, #{passed_count} passed)"
302
+ status_parts << "FAIL: #{issues_count}/#{@total_stats[:tests]} tests (#{details.join(', ')}, #{passed_count} passed#{time_str})"
297
303
  else
298
304
  # Agent doesn't need output in the positive case (i.e. for passing
299
305
  # tests). It just fills out the context window.
300
306
  end
301
307
 
302
- status_parts << "(#{format_time(@total_stats[:elapsed])})" if @total_stats[:elapsed]
308
+ status_parts << "(#{format_time(@total_stats[:elapsed_time])})" if @total_stats[:elapsed_time]
303
309
 
304
310
  output << status_parts.join(" ")
305
311
 
@@ -331,6 +337,12 @@ class Tryouts
331
337
  # Only show errors (exceptions), skip assertion failures
332
338
  critical_files = @collected_files.select { |f| f[:errors].any? }
333
339
 
340
+ time_str = if @total_stats[:elapsed_time] < 2.0
341
+ " (#{(@total_stats[:elapsed_time] * 1000).round}ms)"
342
+ else
343
+ " (#{@total_stats[:elapsed_time].round(2)}s)"
344
+ end
345
+
334
346
  output = []
335
347
 
336
348
  # Add execution context header for agent clarity
@@ -343,7 +355,7 @@ class Tryouts
343
355
  return
344
356
  end
345
357
 
346
- output << "CRITICAL: #{critical_files.size} file#{'s' if critical_files.size != 1} with errors"
358
+ output << "CRITICAL: #{critical_files.size} file#{'s' if critical_files.size != 1} with errors#{time_str}"
347
359
  output << ""
348
360
 
349
361
  critical_files.each do |file_data|
@@ -373,6 +385,12 @@ class Tryouts
373
385
  def render_full_structured
374
386
  output = []
375
387
 
388
+ time_str = if @total_stats[:elapsed_time] < 2.0
389
+ " (#{(@total_stats[:elapsed_time] * 1000).round}ms)"
390
+ else
391
+ " (#{@total_stats[:elapsed_time].round(2)}s)"
392
+ end
393
+
376
394
  # Add execution context header for agent clarity
377
395
  output << render_execution_context
378
396
  output << ""
@@ -408,7 +426,7 @@ class Tryouts
408
426
  summary = "Summary: \n"
409
427
  summary += "#{passed_count} testcases passed, #{failed_count} failed"
410
428
  summary += ", #{error_count} errors" if error_count > 0
411
- summary += " in #{@total_stats[:files]} files"
429
+ summary += " in #{@total_stats[:files]} files#{time_str}"
412
430
 
413
431
  output << summary
414
432
 
@@ -548,7 +566,7 @@ class Tryouts
548
566
 
549
567
  # Runtime - compact format
550
568
  platform = RUBY_PLATFORM.gsub(/darwin\d+/, 'darwin') # Simplify darwin25 -> darwin
551
- context_lines << " runtime: ruby #{RUBY_VERSION} (#{platform})"
569
+ context_lines << " runtime: ruby #{RUBY_VERSION} (#{platform}); tryouts #{Tryouts::VERSION}"
552
570
 
553
571
  # Package manager - only if present, compact format
554
572
  if defined?(Bundler)
@@ -592,8 +610,8 @@ class Tryouts
592
610
  context_lines << " flags: #{flags.join(', ')}"
593
611
  end
594
612
 
595
- # TOPA protocol - compact
596
- context_lines << " protocol: TOPA v1.0 | focus: #{@focus_mode} | limit: #{@budget.limit}"
613
+ # TOPAZ protocol - compact
614
+ context_lines << " protocol: TOPAZ v0.3 | focus: #{@focus_mode} | limit: #{@budget.limit}"
597
615
 
598
616
  # File count being tested
599
617
  if @collected_files && @collected_files.any?
@@ -10,20 +10,20 @@ class Tryouts
10
10
  Minitest: Fresh context (each test isolated)
11
11
 
12
12
  Examples:
13
- try test_try.rb # Tryouts test runner with shared context
14
- try --rspec test_try.rb # RSpec with fresh context
15
- try --direct --shared-context test_try.rb # Explicit shared context
16
- try --generate-rspec test_try.rb # Output RSpec code only
17
- try --inspect test_try.rb # Inspect file structure and validation
18
- try --agent test_try.rb # Agent-optimized structured output
19
- try --agent --agent-limit 10000 tests/ # Agent mode with 10K token limit
13
+ try test_try.rb # Tryouts test runner with shared context
14
+ try --rspec test_try.rb # RSpec with fresh context
15
+ try --direct --shared-context test_try.rb # Explicit shared context
16
+ try --generate-rspec test_try.rb # Output RSpec code only
17
+ try --inspect test_try.rb # Inspect file structure and validation
18
+ try --agent test_try.rb # Agent-optimized structured output
19
+ try --agent --agent-limit 10000 tests/ # Agent mode with 10K token limit
20
20
 
21
21
  Agent Output Modes:
22
- --agent # Structured, token-efficient output
23
- --agent-focus summary # Show counts and problem files only
24
- --agent-focus first-failure # Show first failure per file
25
- --agent-focus critical # Show errors/exceptions only
26
- --agent-limit 1000 # Limit output to 1000 tokens
22
+ --agent # Structured, token-efficient output
23
+ --agent-focus summary # Show counts and problem files only
24
+ --agent-focus first-failure # Show first failure per file
25
+ --agent-focus critical # Show errors/exceptions only
26
+ --agent-limit 1000 # Limit output to 1000 tokens
27
27
 
28
28
  File Naming & Organization:
29
29
  Files must end with '_try.rb' or '.try.rb' (e.g., auth_service_try.rb, user_model.try.rb)
@@ -37,7 +37,7 @@ class Tryouts
37
37
  #=> true # this is the expected result
38
38
 
39
39
  File Structure (3 sections):
40
- # Setup section (optional) - runs once before all tests
40
+ # Setup section (optional) - code before first testcase runs once before all tests
41
41
  @shared_var = "available to all test cases"
42
42
 
43
43
  ## TEST: Feature description
@@ -45,9 +45,9 @@ class Tryouts
45
45
  result = some_operation()
46
46
  #=> expected_value
47
47
 
48
- # Teardown section (optional) - runs once after all tests
48
+ # Teardown section (optional) - code after last testcase runs once after all tests
49
49
 
50
- Context Guidelines:
50
+ Execution Context:
51
51
  Shared Context (default): Instance variables persist across test cases
52
52
  - Use for: Integration testing, stateful scenarios, realistic workflows
53
53
  - Caution: Test order matters, state accumulates
@@ -1,6 +1,6 @@
1
1
  # lib/tryouts/file_processor.rb
2
2
 
3
- require_relative 'parsers/prism_parser'
3
+ require_relative 'parsers/legacy_parser'
4
4
  require_relative 'parsers/enhanced_parser'
5
5
  require_relative 'test_executor'
6
6
  require_relative 'cli/modes/inspect'
@@ -84,7 +84,7 @@ class Tryouts
84
84
  when :enhanced
85
85
  EnhancedParser.new(file, options)
86
86
  when :prism
87
- PrismParser.new(file, options)
87
+ LegacyParser.new(file, options)
88
88
  end
89
89
  end
90
90
 
@@ -22,5 +22,15 @@ class Tryouts
22
22
  suggestion: "Use explicit '## Description' to clarify test structure"
23
23
  )
24
24
  end
25
+
26
+ def self.malformed_expectation(line_number:, syntax:, context:)
27
+ new(
28
+ type: :malformed_expectation,
29
+ message: "Malformed expectation syntax '#=#{syntax}>' at line #{line_number}",
30
+ line_number: line_number,
31
+ context: context,
32
+ suggestion: "Use valid expectation syntax like #=>, #==>, #=:>, #=!>, etc."
33
+ )
34
+ end
25
35
  end
26
36
  end
@@ -0,0 +1,178 @@
1
+ In Ruby 3.4+, `case/when` and `case/in` represent fundamentally different approaches to conditional logic:
2
+
3
+ ## `case/when` - Traditional Equality Matching
4
+
5
+ Uses the `===` operator for comparison. Simple and straightforward:
6
+
7
+ ```ruby
8
+ def classify_response(status)
9
+ case status
10
+ when 200..299
11
+ "success"
12
+ when 400..499
13
+ "client_error"
14
+ when 500..599
15
+ "server_error"
16
+ when String
17
+ "string_status"
18
+ else
19
+ "unknown"
20
+ end
21
+ end
22
+ ```
23
+
24
+ ## `case/in` - Pattern Matching with Destructuring
25
+
26
+ Matches structure and binds variables. Much more powerful:
27
+
28
+ ```ruby
29
+ def process_api_response(response)
30
+ case response
31
+ in { status: 200, data: { user: { name: String => name, age: Integer => age } } }
32
+ "User #{name} is #{age} years old"
33
+
34
+ in { status: 200, data: Array => items } if items.length > 10
35
+ "Got #{items.length} items"
36
+
37
+ in { status: 400..499, error: { message: msg } }
38
+ "Client error: #{msg}"
39
+
40
+ in { status: 500.. }
41
+ "Server error occurred"
42
+
43
+ in nil | {}
44
+ "Empty response"
45
+ else
46
+ "Unexpected response format"
47
+ end
48
+ end
49
+ ```
50
+
51
+ ## Key Differences
52
+
53
+ ### 1. **Variable Binding**
54
+
55
+ ```ruby
56
+ # case/when - no binding
57
+ case user
58
+ when Hash
59
+ puts user[:name] # Must access manually
60
+ end
61
+
62
+ # case/in - automatic binding
63
+ case user
64
+ in { name: String => username, age: } # 'age' variable created automatically
65
+ puts username # Bound variable available
66
+ end
67
+ ```
68
+
69
+ ### 2. **Structural Matching**
70
+
71
+ ```ruby
72
+ # case/when - only surface comparison
73
+ case data
74
+ when Array
75
+ # Know it's an array, but not its contents
76
+ end
77
+
78
+ # case/in - deep structure matching
79
+ case data
80
+ in [first, *middle, last] if middle.length > 2
81
+ # Automatically destructured with guard condition
82
+ end
83
+ ```
84
+
85
+ ### 3. **Guard Conditions**
86
+
87
+ ```ruby
88
+ # case/when - separate if needed
89
+ case number
90
+ when Integer
91
+ if number > 100
92
+ "big integer"
93
+ else
94
+ "small integer"
95
+ end
96
+ end
97
+
98
+ # case/in - integrated guards
99
+ case number
100
+ in Integer => n if n > 100
101
+ "big integer"
102
+ in Integer
103
+ "small integer"
104
+ end
105
+ ```
106
+
107
+ ## Practical Example for Tryouts
108
+
109
+ For parsing tryout lines, here's the difference:
110
+
111
+ ### Traditional `case/when`
112
+ ```ruby
113
+ def parse_line(line)
114
+ case line
115
+ when /^##\s*(.+)/
116
+ [:description, $1.strip]
117
+ when /^#=>\s*(.+)/
118
+ [:expectation, $1.strip]
119
+ when /^#=\?>\s*(.+)/
120
+ [:debug_info, $1.strip]
121
+ else
122
+ [:code, line]
123
+ end
124
+ end
125
+ ```
126
+
127
+ ### Pattern Matching `case/in`
128
+ ```ruby
129
+ def parse_line(line)
130
+ case line
131
+ in /^##\s*(.+)/ => description
132
+ [:description, description.strip]
133
+ in /^#=>\s*(.+)/ => expectation
134
+ [:expectation, expectation.strip]
135
+ in /^#=\?>\s*(.+)/ => debug_expr
136
+ [:debug_info, debug_expr.strip]
137
+ in /^\s*$/
138
+ [:blank]
139
+ else
140
+ [:code, line]
141
+ end
142
+ end
143
+ ```
144
+
145
+ ## When to Use Which
146
+
147
+ ### Use `case/when` for:
148
+ - Simple value comparisons
149
+ - Class/type checking
150
+ - Range matching
151
+ - Traditional switch-like logic
152
+
153
+ ### Use `case/in` for:
154
+ - Complex data structure matching
155
+ - When you need variable binding
156
+ - Guard conditions
157
+ - Destructuring arrays/hashes
158
+ - Multiple conditions per branch
159
+
160
+ ## Ruby 3.4+ Enhancements
161
+
162
+ Ruby 3.4 added several pattern matching improvements:
163
+
164
+ ```ruby
165
+ # Variable binding in array patterns
166
+ case data
167
+ in [String => first, *String => middle, String => last]
168
+ # All string array with bound variables
169
+ end
170
+
171
+ # Hash patterns with rest
172
+ case config
173
+ in { required: true, **rest } if rest.keys.all? { |k| k.is_a?(Symbol) }
174
+ # Required config with symbol keys only
175
+ end
176
+ ```
177
+
178
+ For the Tryouts modernization, `case/in` provides cleaner syntax for parsing complex comment patterns while binding the captured content directly to variables, eliminating the need for global match variables like `$1`.
@@ -6,11 +6,89 @@ require_relative 'shared_methods'
6
6
  require_relative '../parser_warning'
7
7
 
8
8
  class Tryouts
9
- # Fixed PrismParser with pattern matching for robust token filtering
10
9
  module Parsers
10
+ # Base class for all tryout parsers providing common functionality
11
+ #
12
+ # BaseParser establishes the foundation for parsing tryout files by handling
13
+ # file loading, Prism integration, and providing shared parsing infrastructure.
14
+ # All concrete parser implementations (EnhancedParser, LegacyParser) inherit
15
+ # from this class.
16
+ #
17
+ # @abstract Subclass and implement {#parse} to create a concrete parser
18
+ # @example Implementing a custom parser
19
+ # class MyCustomParser < Tryouts::Parsers::BaseParser
20
+ # def parse
21
+ # # Your parsing logic here
22
+ # # Must return a Tryouts::Testrun object
23
+ # end
24
+ #
25
+ # private
26
+ #
27
+ # def parser_type
28
+ # :custom
29
+ # end
30
+ # end
31
+ #
32
+ # @!attribute [r] source_path
33
+ # @return [String] Path to the source file being parsed
34
+ # @!attribute [r] source
35
+ # @return [String] Raw source code content
36
+ # @!attribute [r] lines
37
+ # @return [Array<String>] Source lines with line endings removed
38
+ # @!attribute [r] prism_result
39
+ # @return [Prism::ParseResult] Result of parsing source with Prism
40
+ # @!attribute [r] parsed_at
41
+ # @return [Time] Timestamp when parsing was initiated
42
+ # @!attribute [r] options
43
+ # @return [Hash] Parser configuration options
44
+ # @!attribute [r] warnings
45
+ # @return [Array<Tryouts::ParserWarning>] Collection of parsing warnings
46
+ #
47
+ # ## Shared Functionality
48
+ #
49
+ # ### 1. File and Source Management
50
+ # - Automatic file reading and line splitting
51
+ # - UTF-8 encoding handling
52
+ # - Path normalization and validation
53
+ #
54
+ # ### 2. Prism Integration
55
+ # - Automatic Prism parsing of source code
56
+ # - Syntax error detection and handling
57
+ # - AST access for advanced parsing needs
58
+ #
59
+ # ### 3. Warning System
60
+ # - Centralized warning collection and management
61
+ # - Type-safe warning objects with context
62
+ # - Integration with output formatters
63
+ #
64
+ # ### 4. Shared Methods
65
+ # - Token grouping and classification logic
66
+ # - Test case boundary detection
67
+ # - Common utility methods for all parsers
68
+ #
69
+ # ## Parser Requirements
70
+ #
71
+ # Concrete parser implementations must:
72
+ # 1. Implement the abstract `parse` method
73
+ # 2. Return a `Tryouts::Testrun` object
74
+ # 3. Handle syntax errors appropriately
75
+ # 4. Provide a unique `parser_type` identifier
76
+ #
77
+ # @see EnhancedParser For Prism-based comment extraction
78
+ # @see LegacyParser For line-by-line parsing approach
79
+ # @see SharedMethods For common parsing utilities
80
+ # @since 3.0.0
11
81
  class BaseParser
12
82
  include Tryouts::Parsers::SharedMethods
13
83
 
84
+ # Initialize a new parser instance
85
+ #
86
+ # @param source_path [String] Absolute path to the tryout source file
87
+ # @param options [Hash] Configuration options for parsing behavior
88
+ # @option options [Boolean] :strict Enable strict mode validation
89
+ # @option options [Boolean] :warnings Enable warning collection (default: true)
90
+ # @raise [Errno::ENOENT] If source file doesn't exist
91
+ # @raise [Errno::EACCES] If source file isn't readable
14
92
  def initialize(source_path, options = {})
15
93
  @source_path = source_path
16
94
  @source = File.read(source_path)
@@ -21,6 +99,28 @@ class Tryouts
21
99
  @warnings = []
22
100
  end
23
101
 
102
+ # Parse the source file into structured test data
103
+ #
104
+ # @abstract Subclasses must implement this method
105
+ # @return [Tryouts::Testrun] Parsed test structure with setup, tests, teardown, and warnings
106
+ # @raise [NotImplementedError] If called directly on BaseParser
107
+ def parse
108
+ raise NotImplementedError, "Subclasses must implement #parse"
109
+ end
110
+
111
+ protected
112
+
113
+ # Get the parser type identifier
114
+ #
115
+ # @abstract Subclasses should override to provide unique identifier
116
+ # @return [Symbol] Parser type identifier
117
+ def parser_type
118
+ :base
119
+ end
120
+
121
+ # Access to instance variables for subclasses
122
+ attr_reader :source_path, :source, :lines, :prism_result, :parsed_at, :options
123
+
24
124
  end
25
125
  end
26
126
  end