musa-dsl 0.30.2 → 0.41.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 (158) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -1
  3. data/.version +6 -0
  4. data/.yardopts +7 -0
  5. data/Gemfile +0 -1
  6. data/README.md +227 -6
  7. data/docs/README.md +83 -0
  8. data/docs/api-reference.md +86 -0
  9. data/docs/getting-started/quick-start.md +93 -0
  10. data/docs/getting-started/tutorial.md +58 -0
  11. data/docs/subsystems/core-extensions.md +316 -0
  12. data/docs/subsystems/datasets.md +465 -0
  13. data/docs/subsystems/generative.md +290 -0
  14. data/docs/subsystems/matrix.md +63 -0
  15. data/docs/subsystems/midi.md +123 -0
  16. data/docs/subsystems/music.md +544 -0
  17. data/docs/subsystems/musicxml-builder.md +264 -0
  18. data/docs/subsystems/neumas.md +71 -0
  19. data/docs/subsystems/repl.md +135 -0
  20. data/docs/subsystems/sequencer.md +98 -0
  21. data/docs/subsystems/series.md +302 -0
  22. data/docs/subsystems/transcription.md +152 -0
  23. data/docs/subsystems/transport.md +177 -0
  24. data/lib/musa-dsl/core-ext/array-explode-ranges.rb +68 -0
  25. data/lib/musa-dsl/core-ext/arrayfy.rb +110 -0
  26. data/lib/musa-dsl/core-ext/attribute-builder.rb +91 -30
  27. data/lib/musa-dsl/core-ext/deep-copy.rb +125 -2
  28. data/lib/musa-dsl/core-ext/dynamic-proxy.rb +78 -0
  29. data/lib/musa-dsl/core-ext/extension.rb +53 -0
  30. data/lib/musa-dsl/core-ext/hashify.rb +162 -1
  31. data/lib/musa-dsl/core-ext/inspect-nice.rb +154 -0
  32. data/lib/musa-dsl/core-ext/smart-proc-binder.rb +117 -0
  33. data/lib/musa-dsl/core-ext/with.rb +114 -0
  34. data/lib/musa-dsl/datasets/dataset.rb +109 -0
  35. data/lib/musa-dsl/datasets/delta-d.rb +78 -0
  36. data/lib/musa-dsl/datasets/e.rb +186 -2
  37. data/lib/musa-dsl/datasets/gdv.rb +279 -2
  38. data/lib/musa-dsl/datasets/gdvd.rb +201 -0
  39. data/lib/musa-dsl/datasets/helper.rb +75 -0
  40. data/lib/musa-dsl/datasets/p.rb +177 -2
  41. data/lib/musa-dsl/datasets/packed-v.rb +91 -0
  42. data/lib/musa-dsl/datasets/pdv.rb +136 -1
  43. data/lib/musa-dsl/datasets/ps.rb +134 -4
  44. data/lib/musa-dsl/datasets/score/queriable.rb +143 -1
  45. data/lib/musa-dsl/datasets/score/render.rb +105 -1
  46. data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +138 -1
  47. data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +111 -0
  48. data/lib/musa-dsl/datasets/score/to-mxml/process-time.rb +200 -1
  49. data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +145 -1
  50. data/lib/musa-dsl/datasets/score.rb +279 -0
  51. data/lib/musa-dsl/datasets/v.rb +88 -0
  52. data/lib/musa-dsl/generative/darwin.rb +215 -1
  53. data/lib/musa-dsl/generative/generative-grammar.rb +387 -0
  54. data/lib/musa-dsl/generative/markov.rb +135 -3
  55. data/lib/musa-dsl/generative/rules.rb +312 -4
  56. data/lib/musa-dsl/generative/variatio.rb +286 -2
  57. data/lib/musa-dsl/logger/logger.rb +267 -2
  58. data/lib/musa-dsl/matrix/matrix.rb +256 -10
  59. data/lib/musa-dsl/midi/midi-recorder.rb +113 -2
  60. data/lib/musa-dsl/midi/midi-voices.rb +275 -4
  61. data/lib/musa-dsl/music/chord-definition.rb +233 -1
  62. data/lib/musa-dsl/music/chord-definitions.rb +33 -6
  63. data/lib/musa-dsl/music/chords.rb +353 -2
  64. data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +70 -206
  65. data/lib/musa-dsl/music/scale_kinds/bebop/bebop_dominant_scale_kind.rb +110 -0
  66. data/lib/musa-dsl/music/scale_kinds/bebop/bebop_major_scale_kind.rb +110 -0
  67. data/lib/musa-dsl/music/scale_kinds/bebop/bebop_minor_scale_kind.rb +110 -0
  68. data/lib/musa-dsl/music/scale_kinds/blues/blues_major_scale_kind.rb +100 -0
  69. data/lib/musa-dsl/music/scale_kinds/blues/blues_scale_kind.rb +99 -0
  70. data/lib/musa-dsl/music/scale_kinds/chromatic_scale_kind.rb +79 -0
  71. data/lib/musa-dsl/music/scale_kinds/ethnic/double_harmonic_scale_kind.rb +102 -0
  72. data/lib/musa-dsl/music/scale_kinds/ethnic/hungarian_minor_scale_kind.rb +102 -0
  73. data/lib/musa-dsl/music/scale_kinds/ethnic/neapolitan_major_scale_kind.rb +102 -0
  74. data/lib/musa-dsl/music/scale_kinds/ethnic/neapolitan_minor_scale_kind.rb +101 -0
  75. data/lib/musa-dsl/music/scale_kinds/ethnic/phrygian_dominant_scale_kind.rb +103 -0
  76. data/lib/musa-dsl/music/scale_kinds/harmonic_major/harmonic_major_scale_kind.rb +104 -0
  77. data/lib/musa-dsl/music/scale_kinds/major_scale_kind.rb +110 -0
  78. data/lib/musa-dsl/music/scale_kinds/melodic_minor/altered_scale_kind.rb +106 -0
  79. data/lib/musa-dsl/music/scale_kinds/melodic_minor/dorian_b2_scale_kind.rb +104 -0
  80. data/lib/musa-dsl/music/scale_kinds/melodic_minor/locrian_sharp2_scale_kind.rb +103 -0
  81. data/lib/musa-dsl/music/scale_kinds/melodic_minor/lydian_augmented_scale_kind.rb +103 -0
  82. data/lib/musa-dsl/music/scale_kinds/melodic_minor/lydian_dominant_scale_kind.rb +106 -0
  83. data/lib/musa-dsl/music/scale_kinds/melodic_minor/melodic_minor_scale_kind.rb +104 -0
  84. data/lib/musa-dsl/music/scale_kinds/melodic_minor/mixolydian_b6_scale_kind.rb +103 -0
  85. data/lib/musa-dsl/music/scale_kinds/minor_harmonic_scale_kind.rb +125 -0
  86. data/lib/musa-dsl/music/scale_kinds/minor_natural_scale_kind.rb +123 -0
  87. data/lib/musa-dsl/music/scale_kinds/modes/dorian_scale_kind.rb +111 -0
  88. data/lib/musa-dsl/music/scale_kinds/modes/locrian_scale_kind.rb +114 -0
  89. data/lib/musa-dsl/music/scale_kinds/modes/lydian_scale_kind.rb +111 -0
  90. data/lib/musa-dsl/music/scale_kinds/modes/mixolydian_scale_kind.rb +111 -0
  91. data/lib/musa-dsl/music/scale_kinds/modes/phrygian_scale_kind.rb +111 -0
  92. data/lib/musa-dsl/music/scale_kinds/pentatonic/pentatonic_major_scale_kind.rb +93 -0
  93. data/lib/musa-dsl/music/scale_kinds/pentatonic/pentatonic_minor_scale_kind.rb +99 -0
  94. data/lib/musa-dsl/music/scale_kinds/symmetric/diminished_hw_scale_kind.rb +110 -0
  95. data/lib/musa-dsl/music/scale_kinds/symmetric/diminished_wh_scale_kind.rb +110 -0
  96. data/lib/musa-dsl/music/scale_kinds/symmetric/whole_tone_scale_kind.rb +99 -0
  97. data/lib/musa-dsl/music/scale_systems/equally_tempered_12_tone_scale_system.rb +80 -0
  98. data/lib/musa-dsl/music/scale_systems/twelve_semitones_scale_system.rb +60 -0
  99. data/lib/musa-dsl/music/scales.rb +1384 -40
  100. data/lib/musa-dsl/musicxml/builder/attributes.rb +483 -3
  101. data/lib/musa-dsl/musicxml/builder/backup-forward.rb +166 -1
  102. data/lib/musa-dsl/musicxml/builder/direction.rb +243 -0
  103. data/lib/musa-dsl/musicxml/builder/helper.rb +240 -0
  104. data/lib/musa-dsl/musicxml/builder/measure.rb +284 -0
  105. data/lib/musa-dsl/musicxml/builder/note-complexities.rb +324 -8
  106. data/lib/musa-dsl/musicxml/builder/note.rb +285 -0
  107. data/lib/musa-dsl/musicxml/builder/part-group.rb +108 -1
  108. data/lib/musa-dsl/musicxml/builder/part.rb +139 -0
  109. data/lib/musa-dsl/musicxml/builder/pitched-note.rb +124 -0
  110. data/lib/musa-dsl/musicxml/builder/rest.rb +93 -0
  111. data/lib/musa-dsl/musicxml/builder/score-partwise.rb +276 -0
  112. data/lib/musa-dsl/musicxml/builder/typed-text.rb +62 -1
  113. data/lib/musa-dsl/musicxml/builder/unpitched-note.rb +83 -0
  114. data/lib/musa-dsl/neumalang/neumalang.rb +675 -0
  115. data/lib/musa-dsl/neumas/array-to-neumas.rb +149 -0
  116. data/lib/musa-dsl/neumas/neuma-decoder.rb +253 -0
  117. data/lib/musa-dsl/neumas/neuma-gdv-decoder.rb +142 -2
  118. data/lib/musa-dsl/neumas/neuma-gdvd-decoder.rb +82 -0
  119. data/lib/musa-dsl/neumas/neumas.rb +67 -0
  120. data/lib/musa-dsl/neumas/string-to-neumas.rb +233 -1
  121. data/lib/musa-dsl/repl/repl.rb +550 -0
  122. data/lib/musa-dsl/sequencer/base-sequencer-implementation-every.rb +118 -2
  123. data/lib/musa-dsl/sequencer/base-sequencer-implementation-move.rb +149 -2
  124. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +296 -0
  125. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +88 -2
  126. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb +161 -0
  127. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +263 -0
  128. data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +173 -1
  129. data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +177 -0
  130. data/lib/musa-dsl/sequencer/base-sequencer.rb +710 -10
  131. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +210 -0
  132. data/lib/musa-dsl/sequencer/timeslots.rb +79 -0
  133. data/lib/musa-dsl/series/array-to-serie.rb +37 -1
  134. data/lib/musa-dsl/series/base-series.rb +843 -5
  135. data/lib/musa-dsl/series/buffer-serie.rb +54 -0
  136. data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +64 -0
  137. data/lib/musa-dsl/series/main-serie-constructors.rb +398 -2
  138. data/lib/musa-dsl/series/main-serie-operations.rb +538 -16
  139. data/lib/musa-dsl/series/proxy-serie.rb +67 -0
  140. data/lib/musa-dsl/series/quantizer-serie.rb +57 -7
  141. data/lib/musa-dsl/series/queue-serie.rb +78 -0
  142. data/lib/musa-dsl/series/series-composer.rb +701 -0
  143. data/lib/musa-dsl/series/timed-serie.rb +473 -28
  144. data/lib/musa-dsl/transcription/from-gdv-to-midi.rb +404 -1
  145. data/lib/musa-dsl/transcription/from-gdv-to-musicxml.rb +118 -0
  146. data/lib/musa-dsl/transcription/from-gdv.rb +84 -1
  147. data/lib/musa-dsl/transcription/transcription.rb +265 -0
  148. data/lib/musa-dsl/transport/clock.rb +125 -0
  149. data/lib/musa-dsl/transport/dummy-clock.rb +89 -2
  150. data/lib/musa-dsl/transport/external-tick-clock.rb +91 -0
  151. data/lib/musa-dsl/transport/input-midi-clock.rb +133 -1
  152. data/lib/musa-dsl/transport/timer-clock.rb +183 -1
  153. data/lib/musa-dsl/transport/timer.rb +83 -0
  154. data/lib/musa-dsl/transport/transport.rb +318 -0
  155. data/lib/musa-dsl/version.rb +2 -1
  156. data/lib/musa-dsl.rb +132 -25
  157. data/musa-dsl.gemspec +25 -18
  158. metadata +158 -16
@@ -1,9 +1,126 @@
1
1
  module Musa
2
+ # Generative grammar system for creating formal grammars with combinatorial generation.
3
+ #
4
+ # GenerativeGrammar provides a DSL for defining formal grammars that can generate
5
+ # all possible combinations of terminal and non-terminal symbols according to
6
+ # production rules. Similar to context-free grammars in formal language theory.
7
+ #
8
+ # ## Core Concepts
9
+ #
10
+ # - **Nodes (N)**: Terminal or block nodes with content and attributes
11
+ # - **Proxy Nodes (PN)**: Placeholder nodes for recursive grammar definitions
12
+ # - **Operators**: Combine nodes to form production rules
13
+ # - `|` (or): Alternative/choice between nodes
14
+ # - `+` (next): Concatenation/sequence of nodes
15
+ # - **Modifiers**:
16
+ # - `repeat(min:, max:)`: Repeat node multiple times
17
+ # - `limit(&block)`: Filter options by condition
18
+ # - **Options**: Generate all valid combinations matching the grammar
19
+ #
20
+ # ## Grammar Definition Process
21
+ #
22
+ # 1. Define terminal nodes with N(content, **attributes)
23
+ # 2. Combine nodes using operators (|, +)
24
+ # 3. Add repetition and limits as needed
25
+ # 4. Use PN() for recursive grammars
26
+ # 5. Call .options to generate all valid combinations
27
+ #
28
+ # ## Musical Applications
29
+ #
30
+ # - Generate melodic patterns with rhythmic constraints
31
+ # - Create harmonic progressions with voice leading rules
32
+ # - Produce variations of musical motifs
33
+ # - Build algorithmic composition structures
34
+ #
35
+ # @example Simple sequence with alternatives
36
+ # include Musa::GenerativeGrammar
37
+ #
38
+ # a = N('a', size: 1)
39
+ # b = N('b', size: 1)
40
+ # c = N('c', size: 1)
41
+ #
42
+ # # Grammar: (a or b) repeated 3 times, then c
43
+ # grammar = (a | b).repeat(3) + c
44
+ #
45
+ # # Generate all possibilities
46
+ # grammar.options(content: :join)
47
+ # # => ["aaac", "aabc", "abac", "abbc", "baac", "babc", "bbac", "bbbc"]
48
+ #
49
+ # @example Grammar with constraints
50
+ # a = N('a', size: 1)
51
+ # b = N('b', size: 1)
52
+ #
53
+ # # Limit: total size must equal 3
54
+ # grammar = (a | b).repeat.limit { |o| o.collect { |_| _.attributes[:size] }.sum == 3 }
55
+ #
56
+ # # Filter options where size <= 4
57
+ # grammar.options(content: :join) { |o| o.collect { |e| e.attributes[:size] }.sum <= 4 }
58
+ # # => ["aaa", "aab", "aba", "abb", "baa", "bab", "bba", "bbb"]
59
+ #
60
+ # @example Recursive grammar using proxy nodes
61
+ # a = N('a', size: 1)
62
+ # b = N('b', size: 1)
63
+ # c = N('c', size: 1)
64
+ #
65
+ # # Create proxy for recursion
66
+ # dp = PN()
67
+ #
68
+ # # Grammar: (c + dp) or (a or b), limit size to 3
69
+ # d = (c + dp | (a | b)).repeat.limit(:size, :sum, :==, 3)
70
+ #
71
+ # # Assign recursive reference
72
+ # dp.node = d
73
+ #
74
+ # d.options(:size, :sum, :<=, 4, content: :join)
75
+ # # => ["cca", "ccb", "caa", "cab", "cba", "cbb", "aca", "acb", ...]
76
+ #
77
+ # @example Block nodes for dynamic content
78
+ # a = N(color: :blue) { |parent| 'hola' }
79
+ # b = N(color: :red) { |parent, attributes|
80
+ # OptionElement.new('adios', final: true, **attributes)
81
+ # }
82
+ #
83
+ # grammar = (a | b).repeat(2)
84
+ # grammar.options
85
+ # # => [["hola", "hola"], ["hola", "adios"], ["adios", "hola"], ["adios", "adios"]]
86
+ #
87
+ # @see N Method to create terminal/block nodes
88
+ # @see PN Method to create proxy nodes for recursion
89
+ # @see Musa::Series::Serie Series conversion for grammar options
90
+ # @see https://en.wikipedia.org/wiki/Formal_grammar Formal grammar (Wikipedia)
91
+ # @see https://en.wikipedia.org/wiki/Context-free_grammar Context-free grammar (Wikipedia)
92
+ # @see https://en.wikipedia.org/wiki/Generative_grammar Generative grammar (Wikipedia)
2
93
  module GenerativeGrammar
3
94
  # TODO: refactor & reorganize regarding use of include Musa::GenerativeGrammar problems as default consumption mode (it forces the consumer to have new public methods -P, PN- and class names -OptionElement-)
4
95
 
5
96
  extend self
6
97
 
98
+ # Creates a terminal or block node.
99
+ #
100
+ # Nodes are the basic building blocks of grammars. They can be:
101
+ #
102
+ # - **Terminal nodes**: Fixed content with attributes
103
+ # - **Block nodes**: Dynamic content generated by block
104
+ #
105
+ # @param content [Object, nil] terminal content (if no block given)
106
+ # @param attributes [Hash] attributes attached to node
107
+ #
108
+ # @yieldparam parent [Array<OptionElement>] parent elements in generation
109
+ # @yieldparam attributes [Hash] node attributes
110
+ # @yieldreturn [Object, OptionElement] generated content or element
111
+ #
112
+ # @return [Implementation::FinalNode, Implementation::BlockNode] created node
113
+ #
114
+ # @example Terminal node
115
+ # n = N('a', size: 1)
116
+ #
117
+ # @example Block node
118
+ # n = N(color: :blue) { |parent| "hello" }
119
+ #
120
+ # @example Block returning OptionElement
121
+ # n = N(type: :special) { |parent, attrs|
122
+ # OptionElement.new('value', **attrs)
123
+ # }
7
124
  def N(content = nil, **attributes, &block)
8
125
  if block_given? && content.nil?
9
126
  Implementation::BlockNode.new(attributes, &block)
@@ -12,13 +129,45 @@ module Musa
12
129
  end
13
130
  end
14
131
 
132
+ # Creates a proxy node for recursive grammars.
133
+ #
134
+ # Proxy nodes act as placeholders that can be assigned later,
135
+ # enabling recursive and self-referential grammar definitions.
136
+ #
137
+ # @return [Implementation::ProxyNode] proxy node
138
+ #
139
+ # @example Recursive grammar
140
+ # a = N('a')
141
+ # b = N('b')
142
+ #
143
+ # # Create proxy
144
+ # proxy = PN()
145
+ #
146
+ # # Define grammar referencing itself through proxy
147
+ # grammar = a + proxy | b
148
+ #
149
+ # # Assign recursive reference
150
+ # proxy.node = grammar
15
151
  def PN
16
152
  Implementation::ProxyNode.new
17
153
  end
18
154
 
155
+ # Container for generated option elements.
156
+ #
157
+ # Wraps content and attributes for each element in a generated option.
158
+ # Used internally when generating grammar options.
159
+ #
160
+ # @attr_reader content [Object] element content
161
+ # @attr_reader attributes [Hash] element attributes
19
162
  class OptionElement
20
163
  attr_reader :content, :attributes
21
164
 
165
+ # Creates option element.
166
+ #
167
+ # @param content [Object] element content
168
+ # @param attributes [Hash, nil] element attributes
169
+ #
170
+ # @return [void]
22
171
  def initialize(content, attributes = nil)
23
172
  @content = content
24
173
  @attributes = attributes || {}
@@ -26,13 +175,54 @@ module Musa
26
175
  end
27
176
 
28
177
  module Implementation
178
+ # Base node class for grammar elements.
179
+ #
180
+ # Provides core operations for combining and transforming nodes.
181
+ # All node types (FinalNode, BlockNode, OrNode, etc.) inherit from this.
182
+ #
183
+ # @api private
29
184
  class Node
185
+ # Creates alternation between this node and another.
186
+ #
187
+ # Generates options where either this node or the other is chosen.
188
+ #
189
+ # @param other [Node] alternative node
190
+ #
191
+ # @return [OrNode] alternation node
192
+ #
193
+ # @example
194
+ # a = N('a')
195
+ # b = N('b')
196
+ # ab = a.or(b) # or a | b
30
197
  def or(other)
31
198
  OrNode.new(self, other)
32
199
  end
33
200
 
34
201
  alias_method :|, :or
35
202
 
203
+ # Repeats this node with optional min/max bounds.
204
+ #
205
+ # Generates options with different repetition counts of this node.
206
+ # Without bounds, generates infinite options (use with limit).
207
+ #
208
+ # @param exactly [Integer, nil] exact repetition count
209
+ # @param min [Integer, nil] minimum repetitions
210
+ # @param max [Integer, nil] maximum repetitions
211
+ #
212
+ # @return [Node] node with repetition
213
+ #
214
+ # @raise [ArgumentError] if both exactly and min/max are specified
215
+ #
216
+ # @example Fixed repetition
217
+ # a = N('a')
218
+ # aaa = a.repeat(3) # exactly 3 times
219
+ # aaa = a.repeat(exactly: 3) # same
220
+ #
221
+ # @example Bounded repetition
222
+ # a_range = a.repeat(min: 2, max: 4) # 2, 3, or 4 times
223
+ #
224
+ # @example Unbounded (use with limit)
225
+ # a_any = a.repeat.limit { |o| o.size <= 5 }
36
226
  def repeat(exactly = nil, min: nil, max: nil)
37
227
  raise ArgumentError, 'Only exactly value or min/max values are allowed' if exactly && (min || max)
38
228
 
@@ -55,6 +245,30 @@ module Musa
55
245
  end
56
246
  end
57
247
 
248
+ # Limits generated options by condition.
249
+ #
250
+ # Filters options to only those satisfying the given condition.
251
+ # Can use simplified arguments or custom block.
252
+ #
253
+ # @param attribute [Symbol, nil] attribute to check (simplified form)
254
+ # @param after_collect_operation [Symbol, nil] operation on collected values
255
+ # @param comparison_method [Symbol, nil] comparison method to apply
256
+ # @param comparison_value [Object, nil] value to compare against
257
+ #
258
+ # @yieldparam option [Array<OptionElement>] option to evaluate
259
+ # @yieldreturn [Boolean] true if option should be included
260
+ #
261
+ # @return [ConditionNode] limited node
262
+ #
263
+ # @raise [ArgumentError] if both simplified arguments and block given
264
+ #
265
+ # @example Block form
266
+ # grammar = (a | b).repeat.limit { |o|
267
+ # o.collect { |e| e.attributes[:size] }.sum == 3
268
+ # }
269
+ #
270
+ # @example Simplified form
271
+ # grammar = (a | b).repeat.limit(:size, :sum, :==, 3)
58
272
  def limit(attribute = nil, after_collect_operation = nil, comparison_method = nil, comparison_value = nil, &block)
59
273
  raise ArgumentError, 'Cannot use simplified arguments and yield block at the same time' if (attribute || after_collect_operation || comparison_method || comparison_value) && @block
60
274
 
@@ -63,12 +277,61 @@ module Musa
63
277
  ConditionNode.new(self, &block)
64
278
  end
65
279
 
280
+ # Creates sequence of this node followed by another.
281
+ #
282
+ # Generates options with this node's content followed by the other's.
283
+ #
284
+ # @param other [Node] node to follow
285
+ #
286
+ # @return [NextNode] sequence node
287
+ #
288
+ # @example
289
+ # a = N('a')
290
+ # b = N('b')
291
+ # ab = a.next(b) # or a + b
66
292
  def next(other)
67
293
  NextNode.new(self, other)
68
294
  end
69
295
 
70
296
  alias_method :+, :next
71
297
 
298
+ # Generates all options from this grammar node.
299
+ #
300
+ # Produces all valid combinations matching the grammar definition.
301
+ # Can filter with condition and control output format.
302
+ #
303
+ # @param attribute [Symbol, nil] attribute for filtering (simplified)
304
+ # @param after_collect_operation [Symbol, nil] operation on collected values
305
+ # @param comparison_method [Symbol, nil] comparison method
306
+ # @param comparison_value [Object, nil] value to compare against
307
+ # @param raw [Boolean] if true, return raw OptionElement arrays
308
+ # @param content [Symbol] how to extract content (:itself, :join, etc.)
309
+ #
310
+ # @yieldparam option [Array<OptionElement>] option to evaluate
311
+ # @yieldreturn [Boolean] true if option should be included
312
+ #
313
+ # @return [Array] generated options
314
+ #
315
+ # @raise [ArgumentError] if simplified arguments and block both given
316
+ # @raise [ArgumentError] if raw and content both specified
317
+ #
318
+ # @example Basic usage
319
+ # grammar = (a | b).repeat(2)
320
+ # grammar.options
321
+ # # => [["a", "a"], ["a", "b"], ["b", "a"], ["b", "b"]]
322
+ #
323
+ # @example With content formatting
324
+ # grammar.options(content: :join)
325
+ # # => ["aa", "ab", "ba", "bb"]
326
+ #
327
+ # @example With filtering
328
+ # grammar.options { |o| o.size == 2 }
329
+ #
330
+ # @example Simplified filtering
331
+ # grammar.options(:size, :sum, :<=, 4, content: :join)
332
+ #
333
+ # @example Raw OptionElements
334
+ # grammar.options(raw: true)
72
335
  def options(attribute = nil,
73
336
  after_collect_operation = nil,
74
337
  comparison_method = nil,
@@ -92,16 +355,34 @@ module Musa
92
355
  end
93
356
  end
94
357
 
358
+ # Counts number of options generated by this node.
359
+ #
360
+ # @return [Integer] option count
95
361
  def size
96
362
  options.size
97
363
  end
98
364
 
99
365
  alias_method :length, :size
100
366
 
367
+ # Gets option at index as series node.
368
+ #
369
+ # @param index [Integer] option index
370
+ #
371
+ # @return [Node] option converted to node
101
372
  def [](index)
102
373
  options[index].to_serie.to_node
103
374
  end
104
375
 
376
+ # Converts options to series.
377
+ #
378
+ # Generates options and converts them to a {Musa::Series::Serie}.
379
+ #
380
+ # @param flatten [Boolean] flatten nested series
381
+ #
382
+ # @yieldparam option [Array<OptionElement>] option to evaluate
383
+ # @yieldreturn [Boolean] true if option should be included
384
+ #
385
+ # @return [Musa::Series::Serie] series of options
105
386
  def to_serie(flatten: true, &condition)
106
387
  serie = _options(&condition).collect { |o| o.collect(&:content) }.to_serie(of_series: true).merge
107
388
  serie = serie.flatten if flatten
@@ -111,12 +392,30 @@ module Musa
111
392
 
112
393
  alias_method :s, :to_serie
113
394
 
395
+ # Internal method to generate option arrays.
396
+ #
397
+ # @param parent [Array<OptionElement>, nil] parent elements
398
+ #
399
+ # @yieldparam option [Array<OptionElement>] option to evaluate
400
+ # @yieldreturn [Boolean] true if option should be included
401
+ #
402
+ # @return [Array<Array<OptionElement>>] option arrays
403
+ #
404
+ # @api private
114
405
  def _options(parent: nil, &condition)
115
406
  raise NotImplementedError
116
407
  end
117
408
 
118
409
  protected
119
410
 
411
+ # Generates condition block from simplified arguments.
412
+ #
413
+ # @param attribute [Symbol, nil] attribute to check
414
+ # @param after_collect_operation [Symbol, nil] operation on collected values
415
+ # @param comparison_method [Symbol, nil] comparison method to apply
416
+ # @param comparison_value [Object, nil] value to compare against
417
+ #
418
+ # @return [Proc, nil] condition block or nil if arguments incomplete
120
419
  def generate_simple_condition_block(attribute = nil,
121
420
  after_collect_operation = nil,
122
421
  comparison_method = nil,
@@ -132,23 +431,46 @@ module Musa
132
431
  end
133
432
  end
134
433
 
434
+ # Proxy node for recursive grammar references.
435
+ #
436
+ # Acts as placeholder that delegates to assigned node.
437
+ # Enables recursive and self-referential grammars.
438
+ #
439
+ # @api private
135
440
  class ProxyNode < Node
441
+ # @return [Node, nil] assigned node
136
442
  attr_accessor :node
137
443
 
444
+ # @api private
138
445
  def _options(parent: nil, &condition)
139
446
  @node._options parent: parent, &condition
140
447
  end
141
448
  end
142
449
 
450
+ # Terminal node with fixed content.
451
+ #
452
+ # Represents a leaf node in the grammar tree with constant content.
453
+ #
454
+ # @attr_reader content [Object] node content
455
+ # @attr_reader attributes [Hash] node attributes
456
+ #
457
+ # @api private
143
458
  class FinalNode < Node
144
459
  attr_reader :content
145
460
  attr_reader :attributes
146
461
 
462
+ # @param content [Object] node content
463
+ # @param attributes [Hash] node attributes
464
+ #
465
+ # @return [void]
466
+ #
467
+ # @api private
147
468
  def initialize(content, attributes)
148
469
  super()
149
470
  @element = OptionElement.new(content, attributes)
150
471
  end
151
472
 
473
+ # @api private
152
474
  def _options(parent: nil, &condition)
153
475
  parent ||= []
154
476
 
@@ -164,12 +486,25 @@ module Musa
164
486
  end
165
487
  end
166
488
 
489
+ # Node with dynamic content generated by block.
490
+ #
491
+ # Executes block at generation time to produce content.
492
+ # Block receives parent elements and attributes.
493
+ #
494
+ # @api private
167
495
  class BlockNode < Node
496
+ # @param attributes [Hash] node attributes
497
+ # @yield [parent, attributes] block to generate content
498
+ #
499
+ # @return [void]
500
+ #
501
+ # @api private
168
502
  def initialize(attributes, &block)
169
503
  @attributes = attributes
170
504
  @block = block
171
505
  end
172
506
 
507
+ # @api private
173
508
  def _options(parent: nil, &condition)
174
509
  parent ||= []
175
510
 
@@ -188,12 +523,25 @@ module Musa
188
523
  end
189
524
  end
190
525
 
526
+ # Node that filters child node options by condition.
527
+ #
528
+ # Applies condition block to filter generated options.
529
+ # Created by {Node#limit}.
530
+ #
531
+ # @api private
191
532
  class ConditionNode < Node
533
+ # @param node [Node] node to filter
534
+ # @yield [option] condition block
535
+ #
536
+ # @return [void]
537
+ #
538
+ # @api private
192
539
  def initialize(node, &block)
193
540
  @node = node
194
541
  @block = block
195
542
  end
196
543
 
544
+ # @api private
197
545
  def _options(parent: nil, &condition)
198
546
  parent ||= []
199
547
 
@@ -207,13 +555,26 @@ module Musa
207
555
  end
208
556
  end
209
557
 
558
+ # Node representing alternation between two nodes.
559
+ #
560
+ # Generates options from either node1 or node2.
561
+ # Created by {Node#or} or `|` operator.
562
+ #
563
+ # @api private
210
564
  class OrNode < Node
565
+ # @param node1 [Node] first alternative
566
+ # @param node2 [Node] second alternative
567
+ #
568
+ # @return [void]
569
+ #
570
+ # @api private
211
571
  def initialize(node1, node2)
212
572
  @node1 = node1
213
573
  @node2 = node2
214
574
  super()
215
575
  end
216
576
 
577
+ # @api private
217
578
  def _options(parent: nil, &condition)
218
579
  parent ||= []
219
580
 
@@ -231,13 +592,26 @@ module Musa
231
592
  end
232
593
  end
233
594
 
595
+ # Node representing sequence of two nodes.
596
+ #
597
+ # Generates options with node followed by after.
598
+ # Created by {Node#next} or `+` operator.
599
+ #
600
+ # @api private
234
601
  class NextNode < Node
602
+ # @param node [Node] first node in sequence
603
+ # @param after [Node] node to follow
604
+ #
605
+ # @return [void]
606
+ #
607
+ # @api private
235
608
  def initialize(node, after)
236
609
  @node = node
237
610
  @after = after
238
611
  super()
239
612
  end
240
613
 
614
+ # @api private
241
615
  def _options(parent: nil, &condition)
242
616
  parent ||= []
243
617
 
@@ -251,7 +625,19 @@ module Musa
251
625
  end
252
626
  end
253
627
 
628
+ # Node representing repetition of a node.
629
+ #
630
+ # Generates options with different repetition counts.
631
+ # Created by {Node#repeat}.
632
+ #
633
+ # @api private
254
634
  class RepeatNode < Node
635
+ # @param node [Node] node to repeat
636
+ # @param max [Integer, nil] maximum repetitions (nil = infinite)
637
+ #
638
+ # @return [void]
639
+ #
640
+ # @api private
255
641
  def initialize(node, max = nil)
256
642
  @node = node
257
643
  @max = max
@@ -259,6 +645,7 @@ module Musa
259
645
  super()
260
646
  end
261
647
 
648
+ # @api private
262
649
  def _options(parent: nil, depth: nil, &condition)
263
650
  parent ||= []
264
651
  depth ||= 0