musa-dsl 0.30.2 → 0.40.0

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.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.version +6 -0
  4. data/.yardopts +7 -0
  5. data/README.md +227 -6
  6. data/docs/README.md +83 -0
  7. data/docs/api-reference.md +86 -0
  8. data/docs/getting-started/quick-start.md +93 -0
  9. data/docs/getting-started/tutorial.md +58 -0
  10. data/docs/subsystems/core-extensions.md +316 -0
  11. data/docs/subsystems/datasets.md +465 -0
  12. data/docs/subsystems/generative.md +290 -0
  13. data/docs/subsystems/matrix.md +63 -0
  14. data/docs/subsystems/midi.md +123 -0
  15. data/docs/subsystems/music.md +233 -0
  16. data/docs/subsystems/musicxml-builder.md +264 -0
  17. data/docs/subsystems/neumas.md +71 -0
  18. data/docs/subsystems/repl.md +135 -0
  19. data/docs/subsystems/sequencer.md +98 -0
  20. data/docs/subsystems/series.md +302 -0
  21. data/docs/subsystems/transcription.md +152 -0
  22. data/docs/subsystems/transport.md +177 -0
  23. data/lib/musa-dsl/core-ext/array-explode-ranges.rb +68 -0
  24. data/lib/musa-dsl/core-ext/arrayfy.rb +110 -0
  25. data/lib/musa-dsl/core-ext/attribute-builder.rb +91 -30
  26. data/lib/musa-dsl/core-ext/deep-copy.rb +125 -2
  27. data/lib/musa-dsl/core-ext/dynamic-proxy.rb +78 -0
  28. data/lib/musa-dsl/core-ext/extension.rb +53 -0
  29. data/lib/musa-dsl/core-ext/hashify.rb +162 -1
  30. data/lib/musa-dsl/core-ext/inspect-nice.rb +154 -0
  31. data/lib/musa-dsl/core-ext/smart-proc-binder.rb +117 -0
  32. data/lib/musa-dsl/core-ext/with.rb +114 -0
  33. data/lib/musa-dsl/datasets/dataset.rb +109 -0
  34. data/lib/musa-dsl/datasets/delta-d.rb +78 -0
  35. data/lib/musa-dsl/datasets/e.rb +186 -2
  36. data/lib/musa-dsl/datasets/gdv.rb +279 -2
  37. data/lib/musa-dsl/datasets/gdvd.rb +201 -0
  38. data/lib/musa-dsl/datasets/helper.rb +75 -0
  39. data/lib/musa-dsl/datasets/p.rb +177 -2
  40. data/lib/musa-dsl/datasets/packed-v.rb +91 -0
  41. data/lib/musa-dsl/datasets/pdv.rb +136 -1
  42. data/lib/musa-dsl/datasets/ps.rb +134 -4
  43. data/lib/musa-dsl/datasets/score/queriable.rb +143 -1
  44. data/lib/musa-dsl/datasets/score/render.rb +105 -1
  45. data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +138 -1
  46. data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +111 -0
  47. data/lib/musa-dsl/datasets/score/to-mxml/process-time.rb +200 -1
  48. data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +145 -1
  49. data/lib/musa-dsl/datasets/score.rb +279 -0
  50. data/lib/musa-dsl/datasets/v.rb +88 -0
  51. data/lib/musa-dsl/generative/darwin.rb +180 -1
  52. data/lib/musa-dsl/generative/generative-grammar.rb +359 -0
  53. data/lib/musa-dsl/generative/markov.rb +133 -3
  54. data/lib/musa-dsl/generative/rules.rb +258 -4
  55. data/lib/musa-dsl/generative/variatio.rb +217 -2
  56. data/lib/musa-dsl/logger/logger.rb +267 -2
  57. data/lib/musa-dsl/matrix/matrix.rb +256 -10
  58. data/lib/musa-dsl/midi/midi-recorder.rb +108 -1
  59. data/lib/musa-dsl/midi/midi-voices.rb +265 -4
  60. data/lib/musa-dsl/music/chord-definition.rb +233 -1
  61. data/lib/musa-dsl/music/chord-definitions.rb +33 -6
  62. data/lib/musa-dsl/music/chords.rb +308 -2
  63. data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +315 -0
  64. data/lib/musa-dsl/music/scales.rb +957 -40
  65. data/lib/musa-dsl/musicxml/builder/attributes.rb +483 -3
  66. data/lib/musa-dsl/musicxml/builder/backup-forward.rb +166 -1
  67. data/lib/musa-dsl/musicxml/builder/direction.rb +243 -0
  68. data/lib/musa-dsl/musicxml/builder/helper.rb +240 -0
  69. data/lib/musa-dsl/musicxml/builder/measure.rb +284 -0
  70. data/lib/musa-dsl/musicxml/builder/note-complexities.rb +324 -8
  71. data/lib/musa-dsl/musicxml/builder/note.rb +285 -0
  72. data/lib/musa-dsl/musicxml/builder/part-group.rb +108 -1
  73. data/lib/musa-dsl/musicxml/builder/part.rb +139 -0
  74. data/lib/musa-dsl/musicxml/builder/pitched-note.rb +124 -0
  75. data/lib/musa-dsl/musicxml/builder/rest.rb +93 -0
  76. data/lib/musa-dsl/musicxml/builder/score-partwise.rb +276 -0
  77. data/lib/musa-dsl/musicxml/builder/typed-text.rb +62 -1
  78. data/lib/musa-dsl/musicxml/builder/unpitched-note.rb +83 -0
  79. data/lib/musa-dsl/neumalang/neumalang.rb +675 -0
  80. data/lib/musa-dsl/neumas/array-to-neumas.rb +149 -0
  81. data/lib/musa-dsl/neumas/neuma-decoder.rb +253 -0
  82. data/lib/musa-dsl/neumas/neuma-gdv-decoder.rb +142 -2
  83. data/lib/musa-dsl/neumas/neuma-gdvd-decoder.rb +82 -0
  84. data/lib/musa-dsl/neumas/neumas.rb +67 -0
  85. data/lib/musa-dsl/neumas/string-to-neumas.rb +233 -1
  86. data/lib/musa-dsl/repl/repl.rb +550 -0
  87. data/lib/musa-dsl/sequencer/base-sequencer-implementation-every.rb +118 -2
  88. data/lib/musa-dsl/sequencer/base-sequencer-implementation-move.rb +149 -2
  89. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +296 -0
  90. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +88 -2
  91. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb +161 -0
  92. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +263 -0
  93. data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +173 -1
  94. data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +177 -0
  95. data/lib/musa-dsl/sequencer/base-sequencer.rb +710 -10
  96. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +210 -0
  97. data/lib/musa-dsl/sequencer/timeslots.rb +79 -0
  98. data/lib/musa-dsl/series/array-to-serie.rb +37 -1
  99. data/lib/musa-dsl/series/base-series.rb +843 -5
  100. data/lib/musa-dsl/series/buffer-serie.rb +48 -0
  101. data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +41 -0
  102. data/lib/musa-dsl/series/main-serie-constructors.rb +398 -2
  103. data/lib/musa-dsl/series/main-serie-operations.rb +538 -16
  104. data/lib/musa-dsl/series/proxy-serie.rb +67 -0
  105. data/lib/musa-dsl/series/quantizer-serie.rb +45 -7
  106. data/lib/musa-dsl/series/queue-serie.rb +65 -0
  107. data/lib/musa-dsl/series/series-composer.rb +701 -0
  108. data/lib/musa-dsl/series/timed-serie.rb +473 -28
  109. data/lib/musa-dsl/transcription/from-gdv-to-midi.rb +404 -1
  110. data/lib/musa-dsl/transcription/from-gdv-to-musicxml.rb +118 -0
  111. data/lib/musa-dsl/transcription/from-gdv.rb +84 -1
  112. data/lib/musa-dsl/transcription/transcription.rb +265 -0
  113. data/lib/musa-dsl/transport/clock.rb +125 -0
  114. data/lib/musa-dsl/transport/dummy-clock.rb +89 -2
  115. data/lib/musa-dsl/transport/external-tick-clock.rb +91 -0
  116. data/lib/musa-dsl/transport/input-midi-clock.rb +133 -1
  117. data/lib/musa-dsl/transport/timer-clock.rb +183 -1
  118. data/lib/musa-dsl/transport/timer.rb +83 -0
  119. data/lib/musa-dsl/transport/transport.rb +318 -0
  120. data/lib/musa-dsl/version.rb +1 -1
  121. data/lib/musa-dsl.rb +132 -25
  122. data/musa-dsl.gemspec +12 -10
  123. metadata +87 -8
@@ -2,13 +2,243 @@ require_relative '../core-ext/deep-copy'
2
2
  require_relative '../generative/generative-grammar'
3
3
 
4
4
  module Musa
5
+ # Core Serie infrastructure providing prototype/instance system and base implementation.
6
+ #
7
+ # This module defines the fundamental architecture for all Series in Musa DSL:
8
+ #
9
+ # ## Core Concepts
10
+ #
11
+ # ### Prototype/Instance Pattern
12
+ #
13
+ # Series use a **prototype/instance pattern** to enable reusable series definitions:
14
+ #
15
+ # - **Prototype**: Template/blueprint for series (cannot be consumed)
16
+ # - **Instance**: Cloned copy ready for consumption (can call next_value)
17
+ # - **Undefined**: Series with unresolved dependencies
18
+ #
19
+ # ### Serie States
20
+ #
21
+ # Every serie exists in one of three states:
22
+ #
23
+ # - **:prototype** - Template state, cannot consume, can create instances
24
+ # - **:instance** - Active state, can consume values, has independent state
25
+ # - **:undefined** - Unresolved state, cannot use until dependencies resolve
26
+ #
27
+ # ### Why Prototype/Instance?
28
+ #
29
+ # Enables **reusable series definitions** without re-evaluating constructors:
30
+ #
31
+ # ```ruby
32
+ # # Define once (prototype)
33
+ # melody = S(60, 64, 67, 72)
34
+ #
35
+ # # Use multiple times (instances)
36
+ # voice1 = melody.instance # Independent playback
37
+ # voice2 = melody.instance # Separate playback
38
+ # ```
39
+ #
40
+ # ## Integration
41
+ #
42
+ # Series integrate with:
43
+ #
44
+ # - **Sequencer**: Play series over time via play() method
45
+ # - **Generative**: Use with generative grammars
46
+ # - **MIDI**: Generate MIDI events from series
47
+ # - **Datasets**: Use AbsTimed, AbsD for timing
48
+ #
49
+ # ## Module Architecture
50
+ #
51
+ # ### Serie Module
52
+ #
53
+ # Factory module providing:
54
+ #
55
+ # - **Serie.base**: Base module without source/sources
56
+ # - **Serie.with**: Configurable module with source/sources/block
57
+ #
58
+ # ### Prototyping Module
59
+ #
60
+ # Implements prototype/instance lifecycle:
61
+ #
62
+ # - State management (:prototype, :instance, :undefined)
63
+ # - Cloning (prototype -> instance)
64
+ # - State resolution from sources
65
+ # - Validation and permissions
66
+ #
67
+ # ### SerieImplementation Module
68
+ #
69
+ # Core iteration protocol:
70
+ #
71
+ # - **init**: Initialize instance state
72
+ # - **restart**: Reset to beginning
73
+ # - **next_value**: Consume next element
74
+ # - **peek_next_value**: Look ahead without consuming
75
+ # - **current_value**: Last consumed value
76
+ #
77
+ # ## Serie Protocol
78
+ #
79
+ # All series must implement:
80
+ #
81
+ # ```ruby
82
+ # def _next_value
83
+ # # Return next value or nil when finished
84
+ # end
85
+ #
86
+ # def _init
87
+ # # Initialize instance state (optional)
88
+ # end
89
+ #
90
+ # def _restart
91
+ # # Reset to beginning (optional)
92
+ # end
93
+ # ```
94
+ #
95
+ # ## Usage Patterns
96
+ #
97
+ # ### Basic Prototype/Instance
98
+ #
99
+ # ```ruby
100
+ # # Create prototype
101
+ # proto = S(1, 2, 3)
102
+ # proto.prototype? # => true
103
+ #
104
+ # # Create instance
105
+ # inst = proto.instance # or proto.i
106
+ # inst.instance? # => true
107
+ #
108
+ # # Consume values
109
+ # inst.next_value # => 1
110
+ # inst.next_value # => 2
111
+ # ```
112
+ #
113
+ # ### Multiple Instances
114
+ #
115
+ # ```ruby
116
+ # proto = S(1, 2, 3)
117
+ #
118
+ # a = proto.i
119
+ # b = proto.i # Independent instance
120
+ #
121
+ # a.next_value # => 1
122
+ # b.next_value # => 1 (independent)
123
+ # ```
124
+ #
125
+ # ### State Resolution
126
+ #
127
+ # ```ruby
128
+ # proxy = PROXY() # Undefined - no source yet
129
+ # proxy.undefined? # => true
130
+ #
131
+ # proxy.source = S(1, 2, 3) # Becomes prototype
132
+ # proxy.prototype? # => true
133
+ # ```
134
+ #
135
+ # ## Technical Details
136
+ #
137
+ # ### Peek Mechanism
138
+ #
139
+ # `peek_next_value` uses internal buffering to look ahead without state change:
140
+ #
141
+ # ```ruby
142
+ # s = S(1, 2, 3).i
143
+ # s.peek_next_value # => 1 (buffered)
144
+ # s.peek_next_value # => 1 (same)
145
+ # s.next_value # => 1 (consumes buffered)
146
+ # ```
147
+ #
148
+ # ### Source/Sources Pattern
149
+ #
150
+ # Series can depend on:
151
+ #
152
+ # - **source**: Single upstream serie
153
+ # - **sources**: Multiple upstream series (Array or Hash)
154
+ #
155
+ # State automatically resolves based on dependencies:
156
+ #
157
+ # - All sources :prototype → :prototype
158
+ # - All sources :instance → :instance
159
+ # - Mixed or undefined → :undefined
160
+ #
161
+ # ### Deep Copy Support
162
+ #
163
+ # Uses Musa::Extension::DeepCopy for proper cloning including nested structures.
164
+ #
165
+ # ## Applications
166
+ #
167
+ # - Reusable melodic/harmonic patterns
168
+ # - Multiple voices from single definition
169
+ # - Lazy evaluation of algorithmic sequences
170
+ # - Composable transformations
171
+ # - Memory-efficient sequence playback
172
+ #
173
+ # @see Musa::Series::Constructors Serie constructor methods
174
+ # @see Musa::Series::Operations Serie transformation operations
175
+ #
176
+ # @api public
5
177
  module Series
178
+ # Series constructor methods.
179
+ #
180
+ # @api public
6
181
  module Constructors; extend self; end
182
+
183
+ # Serie transformation operations.
184
+ #
185
+ # @api public
7
186
  module Operations; end
8
187
 
9
188
  include Constructors
10
189
 
190
+ # Serie module factory providing configurable serie modules.
191
+ #
192
+ # Creates modules dynamically based on requirements:
193
+ #
194
+ # - **Serie.base**: Minimal module without dependencies
195
+ # - **Serie.with**: Configured module with source/sources/block
196
+ #
197
+ # ## Factory Pattern
198
+ #
199
+ # Serie.with generates modules at runtime with specific features:
200
+ #
201
+ # ```ruby
202
+ # # Generate module with source support
203
+ # include Serie.with(source: true, source_as: :upstream)
204
+ #
205
+ # # Now has @source, #upstream, #upstream= methods
206
+ # ```
207
+ #
208
+ # ## Configuration Options
209
+ #
210
+ # - **source**: Single upstream serie dependency
211
+ # - **sources**: Multiple upstream serie dependencies
212
+ # - **block**: Block/proc attribute
213
+ # - **smart_block**: SmartProcBinder-wrapped block
214
+ #
215
+ # ## Musical Applications
216
+ #
217
+ # Used internally by serie implementations to declare dependencies
218
+ # and automatically handle prototype/instance propagation.
219
+ #
220
+ # @see SerieImplementation Core serie protocol
221
+ # @see Prototyping Prototype/instance state management
222
+ #
223
+ # @api private
11
224
  module Serie
225
+ # Creates base serie module without dependencies.
226
+ #
227
+ # Minimal module for series that generate values without upstream
228
+ # sources (e.g., array-backed series, value generators).
229
+ #
230
+ # @return [Module] base module with SerieImplementation
231
+ #
232
+ # @example Base serie
233
+ # class SimpleSerie
234
+ # include Serie.base
235
+ #
236
+ # def _next_value
237
+ # # Generate value
238
+ # end
239
+ # end
240
+ #
241
+ # @api private
12
242
  def self.base
13
243
  Module.new do
14
244
  include SerieImplementation
@@ -21,6 +251,78 @@ module Musa
21
251
  end
22
252
  end
23
253
 
254
+ # Creates configurable serie module with specified features.
255
+ #
256
+ # Factory method generating modules with:
257
+ #
258
+ # - Single source dependency (source: true)
259
+ # - Multiple sources dependency (sources: true)
260
+ # - Block attribute (block: true, smart_block: true)
261
+ #
262
+ # ## Source Support
263
+ #
264
+ # **source: true** adds:
265
+ #
266
+ # - `@source` instance variable
267
+ # - `#source` getter (or custom name via source_as:)
268
+ # - `#source=` setter with state validation
269
+ # - Automatic prototype/instance propagation
270
+ #
271
+ # ## Sources Support
272
+ #
273
+ # **sources: true** adds:
274
+ #
275
+ # - `@sources` instance variable (Hash or Array)
276
+ # - `#sources` getter/setter
277
+ # - Automatic state resolution from all sources
278
+ #
279
+ # ## Block Support
280
+ #
281
+ # **block: true** - Simple proc attribute
282
+ # **smart_block: true** - SmartProcBinder-wrapped block
283
+ #
284
+ # ## State Propagation
285
+ #
286
+ # Sources automatically propagate state:
287
+ #
288
+ # - Setting source to :prototype → marks self as :prototype
289
+ # - Setting source to :instance → marks self as :instance
290
+ # - Cloning propagates through source/sources
291
+ #
292
+ # @param source [Boolean] add single source dependency
293
+ # @param source_as [Symbol, nil] custom name for source attribute
294
+ # @param private_source [Boolean, nil] make source methods private
295
+ # @param mandatory_source [Boolean, nil] require source to be set
296
+ # @param sources [Boolean] add multiple sources dependency
297
+ # @param sources_as [Symbol, nil] custom name for sources attribute
298
+ # @param private_sources [Boolean, nil] make sources methods private
299
+ # @param mandatory_sources [Boolean, nil] require sources to be set
300
+ # @param smart_block [Boolean] add SmartProcBinder block support
301
+ # @param block [Boolean] add simple block support
302
+ # @param block_as [Symbol, nil] custom name for block attribute
303
+ #
304
+ # @return [Module] configured module with SerieImplementation
305
+ #
306
+ # @example Serie with single source
307
+ # class ReverseSerie
308
+ # include Serie.with(source: true)
309
+ #
310
+ # def _next_value
311
+ # # Process source.next_value
312
+ # end
313
+ # end
314
+ #
315
+ # @example Serie with block
316
+ # class MapSerie
317
+ # include Serie.with(source: true, smart_block: true)
318
+ #
319
+ # def _next_value
320
+ # value = source.next_value
321
+ # value ? @block.call(value) : nil
322
+ # end
323
+ # end
324
+ #
325
+ # @api private
24
326
  def self.with(source: false,
25
327
  source_as: nil,
26
328
  private_source: nil,
@@ -130,31 +432,148 @@ module Musa
130
432
  end
131
433
  end
132
434
 
435
+ # Prototype/instance state management for Series.
436
+ #
437
+ # Implements the prototype/instance pattern enabling reusable serie
438
+ # definitions. Every serie exists in one of three states:
439
+ #
440
+ # ## States
441
+ #
442
+ # - **:prototype** - Template state, cannot consume, can create instances
443
+ # - **:instance** - Active state, can consume values, has independent state
444
+ # - **:undefined** - Unresolved state, dependencies not yet resolved
445
+ #
446
+ # ## State Queries
447
+ #
448
+ # ```ruby
449
+ # serie.state # => :prototype | :instance | :undefined
450
+ # serie.prototype? # => true if prototype
451
+ # serie.instance? # => true if instance
452
+ # serie.undefined? # => true if undefined
453
+ # ```
454
+ #
455
+ # ## State Transitions
456
+ #
457
+ # ### Prototype Creation
458
+ #
459
+ # Created by constructors (S, E, RND, etc.):
460
+ #
461
+ # ```ruby
462
+ # proto = S(1, 2, 3)
463
+ # proto.prototype? # => true
464
+ # ```
465
+ #
466
+ # ### Instance Creation
467
+ #
468
+ # Via `.instance` (or `.i` alias):
469
+ #
470
+ # ```ruby
471
+ # inst = proto.instance
472
+ # inst.instance? # => true
473
+ # ```
474
+ #
475
+ # ### State Resolution
476
+ #
477
+ # Undefined series resolve state from sources:
478
+ #
479
+ # ```ruby
480
+ # proxy = PROXY() # Undefined
481
+ # proxy.source = S(1, 2, 3) # Becomes prototype
482
+ # ```
483
+ #
484
+ # ## Cloning Behavior
485
+ #
486
+ # Creating instance clones the serie and all dependencies:
487
+ #
488
+ # ```ruby
489
+ # proto = S(1, 2, 3).map { |x| x * 2 }
490
+ # inst = proto.instance # Clones both map and S
491
+ # ```
492
+ #
493
+ # ## Validation
494
+ #
495
+ # Operations check state permissions:
496
+ # - `next_value`, `restart` require :instance
497
+ # - `infinite?`, `to_a` allow :prototype
498
+ # - Undefined state raises PrototypingError
499
+ #
500
+ # ## Musical Applications
501
+ #
502
+ # - Reusable melodic patterns
503
+ # - Multiple independent playbacks
504
+ # - Lazy definition of transformations
505
+ # - Memory-efficient pattern libraries
506
+ #
507
+ # @api private
133
508
  module Prototyping
509
+ # Returns current state of serie.
510
+ #
511
+ # Attempts to resolve undefined state from sources before returning.
512
+ # State is one of: :prototype, :instance, or :undefined.
513
+ #
514
+ # @return [Symbol] current state (:prototype, :instance, :undefined)
515
+ #
516
+ # @api public
134
517
  def state
135
518
  try_to_resolve_undefined_state_if_needed
136
519
  @state || :undefined
137
520
  end
138
521
 
522
+ # Checks if serie is in prototype state.
523
+ #
524
+ # @return [Boolean] true if prototype, false otherwise
525
+ #
526
+ # @api public
139
527
  def prototype?
140
528
  try_to_resolve_undefined_state_if_needed
141
529
  @state&.==(:prototype)
142
530
  end
143
531
 
532
+ # Checks if serie is in instance state.
533
+ #
534
+ # @return [Boolean] true if instance, false otherwise
535
+ #
536
+ # @api public
144
537
  def instance?
145
538
  try_to_resolve_undefined_state_if_needed
146
539
  @state&.==(:instance)
147
540
  end
148
541
 
542
+ # Checks if serie is in undefined state.
543
+ #
544
+ # @return [Boolean] true if undefined, false otherwise
545
+ #
546
+ # @api public
149
547
  def undefined?
150
548
  try_to_resolve_undefined_state_if_needed
151
549
  @state.nil? || @state == :undefined
152
550
  end
153
551
 
552
+ # Checks if serie state is defined (not undefined).
553
+ #
554
+ # @return [Boolean] true if prototype or instance, false if undefined
555
+ #
556
+ # @api public
154
557
  def defined?
155
558
  !undefined?
156
559
  end
157
560
 
561
+ # Returns prototype of serie.
562
+ #
563
+ # - If already prototype, returns self
564
+ # - If instance, returns original prototype (if available)
565
+ # - If undefined, raises PrototypingError
566
+ #
567
+ # @return [Serie] prototype serie
568
+ #
569
+ # @raise [PrototypingError] if serie is undefined
570
+ #
571
+ # @example Get prototype
572
+ # proto = S(1, 2, 3)
573
+ # inst = proto.instance
574
+ # inst.prototype # => proto
575
+ #
576
+ # @api public
158
577
  def prototype
159
578
  try_to_resolve_undefined_state_if_needed
160
579
 
@@ -170,8 +589,41 @@ module Musa
170
589
  end
171
590
  end
172
591
 
592
+ # Short alias for {#prototype}.
593
+ #
594
+ # @return [Serie] prototype serie
595
+ #
596
+ # @api public
173
597
  alias_method :p, :prototype
174
598
 
599
+ # Creates or returns instance of serie.
600
+ #
601
+ # - If already instance, returns self
602
+ # - If prototype, creates new instance by cloning
603
+ # - If undefined, raises PrototypingError
604
+ #
605
+ # ## Cloning Process
606
+ #
607
+ # 1. Clones serie structure
608
+ # 2. Marks clone as :instance
609
+ # 3. Propagates instance creation to sources
610
+ # 4. Calls init if defined
611
+ #
612
+ # Each call creates independent instance with separate state.
613
+ #
614
+ # @return [Serie] instance serie
615
+ #
616
+ # @raise [PrototypingError] if serie is undefined
617
+ #
618
+ # @example Create instances
619
+ # proto = S(1, 2, 3)
620
+ # a = proto.instance
621
+ # b = proto.instance # Different instance
622
+ #
623
+ # a.next_value # => 1
624
+ # b.next_value # => 1 (independent)
625
+ #
626
+ # @api public
175
627
  def instance
176
628
  try_to_resolve_undefined_state_if_needed
177
629
 
@@ -190,14 +642,27 @@ module Musa
190
642
  end
191
643
  end
192
644
 
645
+ # Short alias for {#instance}.
646
+ #
647
+ # @return [Serie] instance serie
648
+ #
649
+ # @api public
193
650
  alias_method :i, :instance
194
651
 
195
- # By default, if there is a @source attribute that contains the source of the serie, SeriePrototyping will
196
- # handle prototyping/instancing automatically.
197
- # If there is a @sources attribute with the eventual several sources, SeriePrototyping will handle them by
198
- # default.
199
- # If needed the subclasses can override this behaviour to accommodate to real subclass specificities.
652
+ # Converts serie and dependencies to prototype state.
653
+ #
654
+ # Called automatically during cloning. By default, handles @source and
655
+ # @sources attributes automatically. Subclasses can override to add
656
+ # custom prototyping logic.
657
+ #
658
+ # ## Default Behavior
659
+ #
660
+ # - Calls `.prototype` on @source if present
661
+ # - Calls `.prototype` on all @sources elements (Array or Hash)
200
662
  #
663
+ # @return [void]
664
+ #
665
+ # @api private
201
666
  protected def _prototype!
202
667
  @source = @source.prototype if @source
203
668
 
@@ -209,6 +674,20 @@ module Musa
209
674
  end
210
675
  end
211
676
 
677
+ # Converts serie and dependencies to instance state.
678
+ #
679
+ # Called automatically during instance creation. By default, handles
680
+ # @source and @sources attributes automatically. Subclasses can
681
+ # override to add custom instancing logic.
682
+ #
683
+ # ## Default Behavior
684
+ #
685
+ # - Calls `.instance` on @source if present
686
+ # - Calls `.instance` on all @sources elements (Array or Hash)
687
+ #
688
+ # @return [void]
689
+ #
690
+ # @api private
212
691
  protected def _instance!
213
692
  @source = @source.instance if @source
214
693
 
@@ -220,6 +699,15 @@ module Musa
220
699
  end
221
700
  end
222
701
 
702
+ # Marks serie with specified state.
703
+ #
704
+ # @param state [Symbol, nil] desired state (:prototype, :instance, :undefined, nil)
705
+ #
706
+ # @return [void]
707
+ #
708
+ # @raise [ArgumentError] if state is not recognized
709
+ #
710
+ # @api private
223
711
  protected def mark_as!(state)
224
712
  case state
225
713
  when nil, :undefined
@@ -233,6 +721,18 @@ module Musa
233
721
  end
234
722
  end
235
723
 
724
+ # Marks serie state based on source state.
725
+ #
726
+ # Propagates state from source:
727
+ # - Source nil/undefined → mark as undefined
728
+ # - Source prototype → mark as prototype
729
+ # - Source instance → mark as instance
730
+ #
731
+ # @param source [Serie, nil] source serie
732
+ #
733
+ # @return [void]
734
+ #
735
+ # @api private
236
736
  protected def mark_regarding!(source)
237
737
  if source.nil? || source.undefined?
238
738
  mark_as_undefined!
@@ -243,11 +743,23 @@ module Musa
243
743
  end
244
744
  end
245
745
 
746
+ # Marks serie as undefined.
747
+ #
748
+ # @return [Serie] self
749
+ #
750
+ # @api private
246
751
  protected def mark_as_undefined!
247
752
  @state = :undefined
248
753
  self
249
754
  end
250
755
 
756
+ # Marks serie as prototype.
757
+ #
758
+ # Calls _sources_resolved if state changed.
759
+ #
760
+ # @return [Serie] self
761
+ #
762
+ # @api private
251
763
  protected def mark_as_prototype!
252
764
  notify = @state != :prototype
253
765
 
@@ -257,6 +769,15 @@ module Musa
257
769
  self
258
770
  end
259
771
 
772
+ # Marks serie as instance.
773
+ #
774
+ # Calls _sources_resolved if state changed.
775
+ #
776
+ # @param prototype [Serie, nil] original prototype
777
+ #
778
+ # @return [Serie] self
779
+ #
780
+ # @api private
260
781
  protected def mark_as_instance!(prototype = nil)
261
782
  notify = @state != :instance
262
783
 
@@ -267,8 +788,28 @@ module Musa
267
788
  self
268
789
  end
269
790
 
791
+ # Hook called when sources are resolved to defined state.
792
+ #
793
+ # Subclasses can override to perform actions when state becomes
794
+ # defined (e.g., validate configuration, initialize caches).
795
+ #
796
+ # @return [void]
797
+ #
798
+ # @api private
270
799
  protected def _sources_resolved; end
271
800
 
801
+ # Attempts to resolve undefined state from sources.
802
+ #
803
+ # Called automatically before state queries. Resolves state based on
804
+ # @source and @sources dependencies:
805
+ #
806
+ # - All sources :prototype → :prototype
807
+ # - All sources :instance → :instance
808
+ # - Mixed or any undefined → :undefined
809
+ #
810
+ # @return [void]
811
+ #
812
+ # @api private
272
813
  private def try_to_resolve_undefined_state_if_needed
273
814
 
274
815
  return unless @state.nil? || @state == :undefined
@@ -323,7 +864,36 @@ module Musa
323
864
  mark_as!(new_state)
324
865
  end
325
866
 
867
+ # Error raised when serie is used in wrong state.
868
+ #
869
+ # Raised when attempting to consume a prototype serie or perform
870
+ # operations on undefined serie.
871
+ #
872
+ # ## Common Scenarios
873
+ #
874
+ # - Calling `next_value` on prototype
875
+ # - Calling `restart` on prototype
876
+ # - Using undefined serie
877
+ #
878
+ # ## Solution
879
+ #
880
+ # Call `.instance` (or `.i`) to create consumable instance:
881
+ #
882
+ # ```ruby
883
+ # proto = S(1, 2, 3)
884
+ # proto.next_value # => PrototypingError
885
+ #
886
+ # inst = proto.instance
887
+ # inst.next_value # => 1
888
+ # ```
889
+ #
890
+ # @api public
326
891
  class PrototypingError < RuntimeError
892
+ # Creates prototyping error with message.
893
+ #
894
+ # @param message [String, nil] custom error message
895
+ #
896
+ # @api public
327
897
  def initialize(message = nil)
328
898
  message ||= 'This serie is a prototype serie: cannot be consumed. To consume the serie use an instance serie via .instance method'
329
899
  super message
@@ -331,6 +901,56 @@ module Musa
331
901
  end
332
902
  end
333
903
 
904
+ # Core serie implementation providing iteration protocol.
905
+ #
906
+ # Includes all serie functionality:
907
+ # - Serie module (marker interface)
908
+ # - Prototyping (prototype/instance pattern)
909
+ # - Operations (transformations)
910
+ #
911
+ # ## Iteration Protocol
912
+ #
913
+ # Implements standard protocol for all series:
914
+ #
915
+ # ```ruby
916
+ # serie.init # Initialize/reset state
917
+ # serie.restart # Reset to beginning
918
+ # serie.next_value # Get next value
919
+ # serie.peek_next_value # Look ahead
920
+ # serie.current_value # Last value
921
+ # ```
922
+ #
923
+ # ## Subclass Requirements
924
+ #
925
+ # Subclasses must implement:
926
+ #
927
+ # ```ruby
928
+ # def _next_value
929
+ # # Return next value or nil when finished
930
+ # end
931
+ # ```
932
+ #
933
+ # Optional hooks:
934
+ #
935
+ # ```ruby
936
+ # def _init
937
+ # # Initialize instance state
938
+ # end
939
+ #
940
+ # def _restart
941
+ # # Reset to beginning
942
+ # end
943
+ # ```
944
+ #
945
+ # ## Peek Buffering
946
+ #
947
+ # `peek_next_value` buffers one value ahead without advancing state:
948
+ #
949
+ # - First peek: calls _next_value and buffers result
950
+ # - Subsequent peeks: returns buffered value
951
+ # - Next next_value: consumes buffered value
952
+ #
953
+ # @api private
334
954
  module SerieImplementation
335
955
  include Serie
336
956
  include Prototyping
@@ -338,6 +958,19 @@ module Musa
338
958
 
339
959
  using Musa::Extension::DeepCopy
340
960
 
961
+ # Initializes instance state.
962
+ #
963
+ # Called automatically when creating instance. Resets:
964
+ # - Peek buffer
965
+ # - Current value cache
966
+ # - Calls subclass _init hook
967
+ #
968
+ # @return [Serie] self
969
+ #
970
+ # @example Automatic initialization
971
+ # inst = S(1, 2, 3).instance # init called
972
+ #
973
+ # @api public
341
974
  def init
342
975
  @_have_peeked_next_value = false
343
976
  @_peeked_next_value = nil
@@ -349,8 +982,33 @@ module Musa
349
982
  self
350
983
  end
351
984
 
985
+ # Subclass hook for custom initialization.
986
+ #
987
+ # Override to initialize instance-specific state (e.g., counters,
988
+ # buffers, internal series).
989
+ #
990
+ # @return [void]
991
+ #
992
+ # @api private
352
993
  private def _init; end
353
994
 
995
+ # Restarts serie to beginning.
996
+ #
997
+ # Resets serie to initial state as if freshly created. Calls init
998
+ # and subclass _restart hook.
999
+ #
1000
+ # @return [Serie] self
1001
+ #
1002
+ # @raise [PrototypingError] if serie is not instance
1003
+ #
1004
+ # @example Restart series
1005
+ # s = S(1, 2, 3).i
1006
+ # s.next_value # => 1
1007
+ # s.next_value # => 2
1008
+ # s.restart
1009
+ # s.next_value # => 1
1010
+ #
1011
+ # @api public
354
1012
  def restart(...)
355
1013
  check_state_permissions
356
1014
  init
@@ -359,8 +1017,38 @@ module Musa
359
1017
  self
360
1018
  end
361
1019
 
1020
+ # Subclass hook for custom restart logic.
1021
+ #
1022
+ # Override to reset subclass-specific state beyond what init handles.
1023
+ #
1024
+ # @return [void]
1025
+ #
1026
+ # @api private
362
1027
  private def _restart; end
363
1028
 
1029
+ # Gets next value from serie.
1030
+ #
1031
+ # Advances serie to next element and returns it. Returns nil when
1032
+ # serie is exhausted. Once nil is returned, subsequent calls continue
1033
+ # returning nil.
1034
+ #
1035
+ # ## Peek Integration
1036
+ #
1037
+ # If peek_next_value was called, consumes peeked value instead of
1038
+ # calling _next_value again.
1039
+ #
1040
+ # @return [Object, nil] next value or nil if exhausted
1041
+ #
1042
+ # @raise [PrototypingError] if serie is not instance
1043
+ #
1044
+ # @example Basic iteration
1045
+ # s = S(1, 2, 3).i
1046
+ # s.next_value # => 1
1047
+ # s.next_value # => 2
1048
+ # s.next_value # => 3
1049
+ # s.next_value # => nil
1050
+ #
1051
+ # @api public
364
1052
  def next_value
365
1053
  check_state_permissions
366
1054
 
@@ -376,10 +1064,41 @@ module Musa
376
1064
  @_current_value
377
1065
  end
378
1066
 
1067
+ # Subclass implementation of value generation.
1068
+ #
1069
+ # Must be implemented by subclasses. Should return next value or nil
1070
+ # when serie is exhausted.
1071
+ #
1072
+ # @return [Object, nil] next value or nil if finished
1073
+ #
1074
+ # @api private
379
1075
  private def _next_value; end
380
1076
 
1077
+ # Short alias for {#next_value}.
1078
+ #
1079
+ # @return [Object, nil] next value
1080
+ #
1081
+ # @api public
381
1082
  alias_method :v, :next_value
382
1083
 
1084
+ # Peeks at next value without consuming it.
1085
+ #
1086
+ # Looks ahead to see what next_value will return, but doesn't advance
1087
+ # serie state. Multiple peeks return same value. Next next_value call
1088
+ # will consume peeked value.
1089
+ #
1090
+ # @return [Object, nil] next value that will be returned
1091
+ #
1092
+ # @raise [PrototypingError] if serie is not instance
1093
+ #
1094
+ # @example Peek ahead
1095
+ # s = S(1, 2, 3).i
1096
+ # s.peek_next_value # => 1
1097
+ # s.peek_next_value # => 1 (same)
1098
+ # s.next_value # => 1
1099
+ # s.peek_next_value # => 2
1100
+ #
1101
+ # @api public
383
1102
  def peek_next_value
384
1103
  check_state_permissions
385
1104
 
@@ -391,17 +1110,76 @@ module Musa
391
1110
  @_peeked_next_value
392
1111
  end
393
1112
 
1113
+ # Returns last consumed value.
1114
+ #
1115
+ # Returns value from most recent next_value call, or nil if
1116
+ # next_value hasn't been called yet.
1117
+ #
1118
+ # @return [Object, nil] last consumed value
1119
+ #
1120
+ # @raise [PrototypingError] if serie is not instance
1121
+ #
1122
+ # @example Track current
1123
+ # s = S(1, 2, 3).i
1124
+ # s.current_value # => nil
1125
+ # s.next_value # => 1
1126
+ # s.current_value # => 1
1127
+ #
1128
+ # @api public
394
1129
  def current_value
395
1130
  check_state_permissions
396
1131
 
397
1132
  @_current_value
398
1133
  end
399
1134
 
1135
+ # Checks if serie is infinite.
1136
+ #
1137
+ # Returns true if serie never exhausts (e.g., generators, cycles).
1138
+ # Prototypes allowed for this query.
1139
+ #
1140
+ # @return [Boolean] true if infinite, false if finite
1141
+ #
1142
+ # @api public
400
1143
  def infinite?
401
1144
  check_state_permissions(allows_prototype: true)
402
1145
  @source&.infinite? || false
403
1146
  end
404
1147
 
1148
+ # Converts serie to array by consuming all values.
1149
+ #
1150
+ # Creates instance, optionally duplicates/restarts it, consumes all
1151
+ # values, and returns them as array. Raises error if serie is infinite.
1152
+ #
1153
+ # ## Options
1154
+ #
1155
+ # - **duplicate**: Clone serie before consuming (default: based on dr)
1156
+ # - **recursive**: Convert nested Series to arrays (default: false)
1157
+ # - **restart**: Restart before consuming (default: based on dr)
1158
+ # - **dr**: Shorthand for duplicate+restart (default: true if instance)
1159
+ #
1160
+ # @param duplicate [Boolean, nil] clone before consuming
1161
+ # @param recursive [Boolean, nil] convert nested Series
1162
+ # @param restart [Boolean, nil] restart before consuming
1163
+ # @param dr [Boolean, nil] duplicate and restart shorthand
1164
+ #
1165
+ # @return [Array] array of all values
1166
+ #
1167
+ # @raise [RuntimeError] if serie is infinite
1168
+ # @raise [PrototypingError] if prototype and allows_prototype: false
1169
+ #
1170
+ # @example Basic conversion
1171
+ # proto = S(1, 2, 3)
1172
+ # proto.to_a # => [1, 2, 3]
1173
+ #
1174
+ # @example Preserve instance
1175
+ # inst = S(1, 2, 3).i
1176
+ # inst.to_a(duplicate: true) # Consumes copy, inst unchanged
1177
+ #
1178
+ # @example Recursive conversion
1179
+ # s = S(S(1, 2), S(3, 4))
1180
+ # s.to_a(recursive: true) # => [[1, 2], [3, 4]]
1181
+ #
1182
+ # @api public
405
1183
  def to_a(duplicate: nil, recursive: nil, restart: nil, dr: nil)
406
1184
  check_state_permissions(allows_prototype: true)
407
1185
  raise 'Cannot convert to array an infinite serie' if infinite?
@@ -431,8 +1209,23 @@ module Musa
431
1209
  array
432
1210
  end
433
1211
 
1212
+ # Short alias for {#to_a}.
1213
+ #
1214
+ # @return [Array] array of all values
1215
+ #
1216
+ # @api public
434
1217
  alias_method :a, :to_a
435
1218
 
1219
+ # Recursively converts series in value to arrays.
1220
+ #
1221
+ # Helper for to_a with recursive: true. Converts Serie values to
1222
+ # arrays and recursively processes Arrays and Hashes.
1223
+ #
1224
+ # @param value [Object] value to process
1225
+ #
1226
+ # @return [Object] processed value with Series converted to arrays
1227
+ #
1228
+ # @api private
436
1229
  private def process_for_to_a(value)
437
1230
  case value
438
1231
  when Serie
@@ -448,12 +1241,43 @@ module Musa
448
1241
  end
449
1242
  end
450
1243
 
1244
+ # Converts serie to generative grammar node.
1245
+ #
1246
+ # Creates Node wrapper for use in generative grammar system. Nodes
1247
+ # can be used in generative rules and substitutions.
1248
+ #
1249
+ # @param attributes [Hash] additional node attributes
1250
+ #
1251
+ # @return [Node] generative grammar node wrapping serie
1252
+ #
1253
+ # @see Musa::GenerativeGrammar
1254
+ #
1255
+ # @api public
451
1256
  def to_node(**attributes)
452
1257
  Nodificator.to_node(self, **attributes)
453
1258
  end
454
1259
 
1260
+ # Short alias for {#to_node}.
1261
+ #
1262
+ # @return [Node] generative grammar node
1263
+ #
1264
+ # @api public
455
1265
  alias_method :node, :to_node
456
1266
 
1267
+ # Validates serie state before operation.
1268
+ #
1269
+ # Checks if serie is in valid state for requested operation. Raises
1270
+ # PrototypingError if:
1271
+ # - Serie is undefined
1272
+ # - Serie is prototype and allows_prototype is false
1273
+ #
1274
+ # @param allows_prototype [Boolean, nil] allow prototype state
1275
+ #
1276
+ # @return [void]
1277
+ #
1278
+ # @raise [PrototypingError] if state is invalid for operation
1279
+ #
1280
+ # @api private
457
1281
  private def check_state_permissions(allows_prototype: nil)
458
1282
  try_to_resolve_undefined_state_if_needed
459
1283
 
@@ -464,9 +1288,23 @@ module Musa
464
1288
  end
465
1289
  end
466
1290
 
1291
+ # Helper class for converting Series to generative grammar nodes.
1292
+ #
1293
+ # Extends GenerativeGrammar to provide N() constructor for creating
1294
+ # nodes from series.
1295
+ #
1296
+ # @api private
467
1297
  class Nodificator
468
1298
  extend Musa::GenerativeGrammar
469
1299
 
1300
+ # Converts serie to node using GenerativeGrammar#N.
1301
+ #
1302
+ # @param serie [Serie] serie to wrap
1303
+ # @param attributes [Hash] node attributes
1304
+ #
1305
+ # @return [Node] generative grammar node
1306
+ #
1307
+ # @api private
470
1308
  def self.to_node(serie, **attributes)
471
1309
  N(serie, **attributes)
472
1310
  end