musa-dsl 0.14.26 → 0.21.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/Gemfile +0 -1
- data/README.md +5 -1
- data/lib/musa-dsl.rb +54 -11
- data/lib/musa-dsl/core-ext.rb +7 -13
- data/lib/musa-dsl/core-ext/array-explode-ranges.rb +15 -23
- data/lib/musa-dsl/core-ext/arrayfy.rb +30 -12
- data/lib/musa-dsl/core-ext/attribute-builder.rb +194 -0
- data/lib/musa-dsl/core-ext/deep-copy.rb +185 -0
- data/lib/musa-dsl/core-ext/dynamic-proxy.rb +44 -40
- data/lib/musa-dsl/core-ext/inspect-nice.rb +40 -22
- data/lib/musa-dsl/core-ext/smart-proc-binder.rb +108 -0
- data/lib/musa-dsl/core-ext/with.rb +26 -0
- data/lib/musa-dsl/datasets.rb +8 -3
- data/lib/musa-dsl/datasets/dataset.rb +3 -0
- data/lib/musa-dsl/datasets/delta-d.rb +12 -0
- data/lib/musa-dsl/datasets/e.rb +61 -0
- data/lib/musa-dsl/datasets/gdv.rb +51 -411
- data/lib/musa-dsl/datasets/gdvd.rb +179 -0
- data/lib/musa-dsl/datasets/helper.rb +41 -0
- data/lib/musa-dsl/datasets/p.rb +68 -0
- data/lib/musa-dsl/datasets/packed-v.rb +19 -0
- data/lib/musa-dsl/datasets/pdv.rb +22 -15
- data/lib/musa-dsl/datasets/ps.rb +113 -0
- data/lib/musa-dsl/datasets/score.rb +210 -0
- data/lib/musa-dsl/datasets/score/queriable.rb +48 -0
- data/lib/musa-dsl/datasets/score/render.rb +31 -0
- data/lib/musa-dsl/datasets/score/to-mxml/process-pdv.rb +160 -0
- data/lib/musa-dsl/datasets/score/to-mxml/process-ps.rb +51 -0
- data/lib/musa-dsl/datasets/score/to-mxml/process-time.rb +153 -0
- data/lib/musa-dsl/datasets/score/to-mxml/to-mxml.rb +158 -0
- data/lib/musa-dsl/datasets/v.rb +23 -0
- data/lib/musa-dsl/generative.rb +5 -5
- data/lib/musa-dsl/generative/backboner.rb +274 -0
- data/lib/musa-dsl/generative/darwin.rb +102 -96
- data/lib/musa-dsl/generative/generative-grammar.rb +182 -187
- data/lib/musa-dsl/generative/markov.rb +56 -53
- data/lib/musa-dsl/generative/variatio.rb +234 -222
- data/lib/musa-dsl/logger.rb +1 -0
- data/lib/musa-dsl/logger/logger.rb +31 -0
- data/lib/musa-dsl/matrix.rb +1 -0
- data/lib/musa-dsl/matrix/matrix.rb +210 -0
- data/lib/musa-dsl/midi.rb +2 -2
- data/lib/musa-dsl/midi/midi-recorder.rb +54 -52
- data/lib/musa-dsl/midi/midi-voices.rb +183 -182
- data/lib/musa-dsl/music.rb +5 -5
- data/lib/musa-dsl/music/chord-definition.rb +54 -50
- data/lib/musa-dsl/music/chord-definitions.rb +13 -9
- data/lib/musa-dsl/music/chords.rb +236 -238
- data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +187 -183
- data/lib/musa-dsl/music/scales.rb +331 -332
- data/lib/musa-dsl/musicxml.rb +1 -0
- data/lib/musa-dsl/musicxml/builder/attributes.rb +155 -0
- data/lib/musa-dsl/musicxml/builder/backup-forward.rb +45 -0
- data/lib/musa-dsl/musicxml/builder/direction.rb +322 -0
- data/lib/musa-dsl/musicxml/builder/helper.rb +90 -0
- data/lib/musa-dsl/musicxml/builder/measure.rb +137 -0
- data/lib/musa-dsl/musicxml/builder/note-complexities.rb +152 -0
- data/lib/musa-dsl/musicxml/builder/note.rb +577 -0
- data/lib/musa-dsl/musicxml/builder/part-group.rb +44 -0
- data/lib/musa-dsl/musicxml/builder/part.rb +67 -0
- data/lib/musa-dsl/musicxml/builder/pitched-note.rb +126 -0
- data/lib/musa-dsl/musicxml/builder/rest.rb +117 -0
- data/lib/musa-dsl/musicxml/builder/score-partwise.rb +120 -0
- data/lib/musa-dsl/musicxml/builder/typed-text.rb +43 -0
- data/lib/musa-dsl/musicxml/builder/unpitched-note.rb +112 -0
- data/lib/musa-dsl/neumalang.rb +1 -1
- data/lib/musa-dsl/neumalang/datatypes.citrus +79 -0
- data/lib/musa-dsl/neumalang/neuma.citrus +165 -0
- data/lib/musa-dsl/neumalang/neumalang.citrus +32 -242
- data/lib/musa-dsl/neumalang/neumalang.rb +373 -142
- data/lib/musa-dsl/neumalang/process.citrus +21 -0
- data/lib/musa-dsl/neumalang/terminals.citrus +67 -0
- data/lib/musa-dsl/neumalang/vectors.citrus +23 -0
- data/lib/musa-dsl/neumas.rb +5 -0
- data/lib/musa-dsl/neumas/array-to-neumas.rb +34 -0
- data/lib/musa-dsl/neumas/neuma-decoder.rb +63 -0
- data/lib/musa-dsl/neumas/neuma-gdv-decoder.rb +57 -0
- data/lib/musa-dsl/neumas/neuma-gdvd-decoder.rb +15 -0
- data/lib/musa-dsl/neumas/neumas.rb +37 -0
- data/lib/musa-dsl/neumas/string-to-neumas.rb +34 -0
- data/lib/musa-dsl/repl.rb +1 -1
- data/lib/musa-dsl/repl/repl.rb +105 -105
- data/lib/musa-dsl/sequencer.rb +1 -1
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-control.rb +163 -136
- data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +301 -286
- data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +562 -270
- data/lib/musa-dsl/sequencer/base-sequencer-public.rb +199 -199
- data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +77 -0
- data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +75 -0
- data/lib/musa-dsl/sequencer/sequencer-dsl.rb +105 -85
- data/lib/musa-dsl/sequencer/timeslots.rb +34 -0
- data/lib/musa-dsl/series.rb +1 -1
- data/lib/musa-dsl/{core-ext → series}/array-to-serie.rb +1 -1
- data/lib/musa-dsl/series/base-series.rb +171 -168
- data/lib/musa-dsl/series/hash-serie-splitter.rb +134 -132
- data/lib/musa-dsl/series/holder-serie.rb +1 -1
- data/lib/musa-dsl/series/main-serie-constructors.rb +6 -1
- data/lib/musa-dsl/series/main-serie-operations.rb +807 -797
- data/lib/musa-dsl/series/proxy-serie.rb +6 -6
- data/lib/musa-dsl/series/queue-serie.rb +5 -5
- data/lib/musa-dsl/series/series.rb +2 -0
- data/lib/musa-dsl/transcription.rb +4 -0
- data/lib/musa-dsl/transcription/from-gdv-to-midi.rb +227 -0
- data/lib/musa-dsl/transcription/from-gdv-to-musicxml.rb +36 -0
- data/lib/musa-dsl/transcription/from-gdv.rb +17 -0
- data/lib/musa-dsl/transcription/transcription.rb +55 -0
- data/lib/musa-dsl/transport.rb +6 -6
- data/lib/musa-dsl/transport/clock.rb +26 -26
- data/lib/musa-dsl/transport/dummy-clock.rb +32 -30
- data/lib/musa-dsl/transport/external-tick-clock.rb +21 -20
- data/lib/musa-dsl/transport/input-midi-clock.rb +82 -80
- data/lib/musa-dsl/transport/timer-clock.rb +72 -71
- data/lib/musa-dsl/transport/timer.rb +28 -26
- data/lib/musa-dsl/transport/transport.rb +99 -95
- data/musa-dsl.gemspec +3 -3
- metadata +73 -24
- data/lib/musa-dsl/core-ext/array-apply-get.rb +0 -18
- data/lib/musa-dsl/core-ext/array-to-neumas.rb +0 -28
- data/lib/musa-dsl/core-ext/as-context-run.rb +0 -44
- data/lib/musa-dsl/core-ext/duplicate.rb +0 -134
- data/lib/musa-dsl/core-ext/key-parameters-procedure-binder.rb +0 -85
- data/lib/musa-dsl/core-ext/proc-nice.rb +0 -13
- data/lib/musa-dsl/core-ext/send-nice.rb +0 -21
- data/lib/musa-dsl/core-ext/string-to-neumas.rb +0 -27
- data/lib/musa-dsl/datasets/gdv-decorators.rb +0 -221
- data/lib/musa-dsl/generative/rules.rb +0 -282
- data/lib/musa-dsl/neuma.rb +0 -1
- data/lib/musa-dsl/neuma/neuma.rb +0 -181
@@ -0,0 +1,77 @@
|
|
1
|
+
module Musa
|
2
|
+
module Sequencer
|
3
|
+
class BaseSequencer
|
4
|
+
module TickBasedTiming
|
5
|
+
|
6
|
+
attr_reader :position, :ticks_per_bar, :tick_duration
|
7
|
+
|
8
|
+
def tick
|
9
|
+
if @hold_public_ticks
|
10
|
+
@hold_ticks += 1
|
11
|
+
else
|
12
|
+
_tick @position_mutex.synchronize { @position += @tick_duration }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def position=(new_position)
|
17
|
+
raise ArgumentError,
|
18
|
+
"Sequencer #{self}: cannot move back. current position: #{@position} new position: #{new_position}" \
|
19
|
+
if new_position < position
|
20
|
+
|
21
|
+
_hold_public_ticks
|
22
|
+
@on_fast_forward.each { |block| block.call(true) }
|
23
|
+
|
24
|
+
_tick(@position_mutex.synchronize { @position += @tick_duration }) while @position < new_position
|
25
|
+
|
26
|
+
@on_fast_forward.each { |block| block.call(false) }
|
27
|
+
_release_public_ticks
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def _init_timing
|
33
|
+
@ticks_per_bar = Rational(beats_per_bar * ticks_per_beat)
|
34
|
+
@tick_duration = Rational(1, @ticks_per_bar)
|
35
|
+
|
36
|
+
@hold_public_ticks = false
|
37
|
+
@hold_ticks = 0
|
38
|
+
end
|
39
|
+
|
40
|
+
def _reset_timing
|
41
|
+
@position = @position_mutex.synchronize { 1r - @tick_duration }
|
42
|
+
end
|
43
|
+
|
44
|
+
def _check_position(position)
|
45
|
+
ticks_position = position / @tick_duration
|
46
|
+
|
47
|
+
if ticks_position.round != ticks_position
|
48
|
+
original_position = position
|
49
|
+
position = ticks_position.round * @tick_duration
|
50
|
+
|
51
|
+
if @do_log
|
52
|
+
_log "BaseSequencer._numeric_at: warning: rounding "\
|
53
|
+
"position #{position} (#{original_position.to_f.round(5)}) "\
|
54
|
+
"to tick precision: #{position} (#{position.to_f.round(5)})"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
position
|
59
|
+
end
|
60
|
+
|
61
|
+
def _quantize(position)
|
62
|
+
(position / @tick_duration).round * @tick_duration
|
63
|
+
end
|
64
|
+
|
65
|
+
def _hold_public_ticks
|
66
|
+
@hold_public_ticks = true
|
67
|
+
end
|
68
|
+
|
69
|
+
def _release_public_ticks
|
70
|
+
@hold_ticks.times { _tick(@position_mutex.synchronize { @position += @tick_duration }) }
|
71
|
+
@hold_ticks = 0
|
72
|
+
@hold_public_ticks = false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Musa
|
2
|
+
module Sequencer
|
3
|
+
class BaseSequencer
|
4
|
+
module TicklessBasedTiming
|
5
|
+
|
6
|
+
attr_reader :position
|
7
|
+
|
8
|
+
def ticks_per_bar
|
9
|
+
Float::INFINITY
|
10
|
+
end
|
11
|
+
|
12
|
+
def tick_duration
|
13
|
+
0r
|
14
|
+
end
|
15
|
+
|
16
|
+
# TODO implementar hold_public_ticks adaptado a modo tickless para permitir que una operación de asignación de
|
17
|
+
# TODO posición con .position = X, finalice antes de comenzar a procesar el resto de ticks en un contexto multithread.
|
18
|
+
# TODO pero tiene sentido cuando el modo tickless se usa SOLO con .run?
|
19
|
+
# TODO tendría sentido sólo si también se usa con ticks temporizados, lo cual ocurriría si se reimplementa el modo tickbased
|
20
|
+
# TODO a partir del modo tickless.
|
21
|
+
|
22
|
+
def tick
|
23
|
+
_tick @position_mutex.synchronize { @position = @timeslots.first_after(@position) }
|
24
|
+
end
|
25
|
+
|
26
|
+
# TODO puede pensarse que un sequencer tickbased es como un ticklessbased en que cada tick se aavanza el position en 1 / ticks_per_bar
|
27
|
+
# TODO esto haría que los eventos que cayeran en posiciones no cuantizadas por la resolución en ticks_per_bar se ejecutarían en el siguiente
|
28
|
+
# TODO lo cual sería una ventaja porque eliminaría la necesidad de cuantizar en el _at.
|
29
|
+
# TODO Por otro lado cuando se cuantiza en _at se redondea al más cercano, mientras que el modelo basado en avanzar redondea siempre hacia arriba,
|
30
|
+
# TODO pero esto podría resolverse con una opción de cuantización opcional que en _at ajustara la posición a la más cercana.
|
31
|
+
|
32
|
+
def position=(new_position)
|
33
|
+
raise ArgumentError, "Sequencer #{self}: cannot move back. current position: #{@position} new position: #{new_position}" if new_position < @position
|
34
|
+
|
35
|
+
@on_fast_forward.each { |block| block.call(true) }
|
36
|
+
|
37
|
+
loop do
|
38
|
+
next_position = nil
|
39
|
+
|
40
|
+
@position_mutex.synchronize do
|
41
|
+
next_position = @timeslots.first_after(@position)
|
42
|
+
end
|
43
|
+
|
44
|
+
if next_position <= new_position
|
45
|
+
_tick next_position
|
46
|
+
else
|
47
|
+
break
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
@position = new_position
|
52
|
+
|
53
|
+
@on_fast_forward.each { |block| block.call(false) }
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def _init_timing
|
59
|
+
end
|
60
|
+
|
61
|
+
def _reset_timing
|
62
|
+
@position = nil
|
63
|
+
end
|
64
|
+
|
65
|
+
def _check_position(position)
|
66
|
+
position
|
67
|
+
end
|
68
|
+
|
69
|
+
def _quantize(position)
|
70
|
+
position
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -1,94 +1,114 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
end
|
43
|
-
|
44
|
-
def now(*value_parameters, **key_parameters, &block)
|
45
|
-
block ||= proc {}
|
46
|
-
|
47
|
-
@sequencer.now *value_parameters, **key_parameters do |*value_args, **key_args|
|
48
|
-
_as_context_run block, value_args, key_args
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def at(*value_parameters, **key_parameters, &block)
|
53
|
-
block ||= proc {}
|
54
|
-
|
55
|
-
@sequencer.at *value_parameters, **key_parameters do |*value_args, **key_args|
|
56
|
-
_as_context_run block, value_args, key_args
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def wait(*value_parameters, **key_parameters, &block)
|
61
|
-
block ||= proc {}
|
62
|
-
|
63
|
-
@sequencer.wait *value_parameters, **key_parameters do | *values, **key_values |
|
64
|
-
_as_context_run block, values, key_values
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def play(*value_parameters, **key_parameters, &block)
|
69
|
-
block ||= proc {}
|
70
|
-
|
71
|
-
@sequencer.play *value_parameters, **key_parameters do |*value_args, **key_args|
|
72
|
-
_as_context_run block, value_args, key_args
|
3
|
+
require_relative '../core-ext/with'
|
4
|
+
|
5
|
+
module Musa
|
6
|
+
module Sequencer
|
7
|
+
class Sequencer
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
def_delegators :@sequencer,
|
11
|
+
:beats_per_bar, :ticks_per_beat, :ticks_per_bar, :tick_duration,
|
12
|
+
:size, :empty?,
|
13
|
+
:on_debug_at, :on_error, :on_fast_forward, :before_tick,
|
14
|
+
:raw_at,
|
15
|
+
:tick,
|
16
|
+
:reset,
|
17
|
+
:position=,
|
18
|
+
:event_handler
|
19
|
+
|
20
|
+
def_delegators :@context, :position, :logger, :debug
|
21
|
+
def_delegators :@context, :with, :now, :at, :wait, :play, :every, :move
|
22
|
+
def_delegators :@context, :everying, :playing, :moving
|
23
|
+
def_delegators :@context, :launch, :on
|
24
|
+
def_delegators :@context, :run
|
25
|
+
|
26
|
+
def initialize(beats_per_bar, ticks_per_beat,
|
27
|
+
sequencer: nil,
|
28
|
+
logger: nil,
|
29
|
+
do_log: nil, do_error_log: nil, log_position_format: nil,
|
30
|
+
&block)
|
31
|
+
|
32
|
+
@sequencer = sequencer
|
33
|
+
@sequencer ||= BaseSequencer.new beats_per_bar, ticks_per_beat,
|
34
|
+
logger: logger,
|
35
|
+
do_log: do_log,
|
36
|
+
do_error_log: do_error_log,
|
37
|
+
log_position_format: log_position_format
|
38
|
+
|
39
|
+
@context = DSLContext.new @sequencer
|
40
|
+
|
41
|
+
with &block if block_given?
|
73
42
|
end
|
74
|
-
end
|
75
43
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
44
|
+
class DSLContext
|
45
|
+
extend Forwardable
|
46
|
+
include Musa::Extension::SmartProcBinder
|
47
|
+
include Musa::Extension::With
|
48
|
+
|
49
|
+
attr_reader :sequencer
|
50
|
+
|
51
|
+
def_delegators :@sequencer,
|
52
|
+
:launch, :on,
|
53
|
+
:position, :size, :everying, :playing, :moving,
|
54
|
+
:ticks_per_bar, :logger, :debug, :inspect,
|
55
|
+
:run
|
56
|
+
|
57
|
+
def initialize(sequencer)
|
58
|
+
@sequencer = sequencer
|
59
|
+
end
|
60
|
+
|
61
|
+
def now(*value_parameters, **key_parameters, &block)
|
62
|
+
block ||= proc {}
|
63
|
+
|
64
|
+
@sequencer.now *value_parameters, **key_parameters do |*value_args, **key_args|
|
65
|
+
with *value_args, **key_args, &block
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def at(*value_parameters, **key_parameters, &block)
|
70
|
+
block ||= proc {}
|
71
|
+
|
72
|
+
@sequencer.at *value_parameters, **key_parameters do |*value_args, **key_args|
|
73
|
+
with *value_args, **key_args, &block
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def wait(*value_parameters, **key_parameters, &block)
|
78
|
+
block ||= proc {}
|
79
|
+
@sequencer.wait *value_parameters, **key_parameters do | *values, **key_values |
|
80
|
+
with *values, **key_values, &block
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def play(*value_parameters, **key_parameters, &block)
|
85
|
+
block ||= proc {}
|
86
|
+
|
87
|
+
@sequencer.play *value_parameters, **key_parameters do |*value_args, **key_args|
|
88
|
+
with *value_args, **key_args, &block
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def every(*value_parameters, **key_parameters, &block)
|
93
|
+
block ||= proc {}
|
94
|
+
|
95
|
+
@sequencer.every *value_parameters, **key_parameters do |*value_args, **key_args|
|
96
|
+
args = SmartProcBinder.new(block)._apply(value_args, key_args)
|
97
|
+
with *args.first, **args.last, &block
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def move(*value_parameters, **key_parameters, &block)
|
102
|
+
block ||= proc {}
|
103
|
+
|
104
|
+
@sequencer.move *value_parameters, **key_parameters do |*value_args, **key_args|
|
105
|
+
with *value_args, **key_args, &block
|
106
|
+
end
|
107
|
+
end
|
81
108
|
end
|
82
|
-
end
|
83
109
|
|
84
|
-
|
85
|
-
block ||= proc {}
|
86
|
-
|
87
|
-
@sequencer.move *value_parameters, **key_parameters do |*value_args, **key_args|
|
88
|
-
_as_context_run block, value_args, key_args
|
89
|
-
end
|
110
|
+
private_constant :DSLContext
|
90
111
|
end
|
91
112
|
end
|
92
|
-
|
93
|
-
private_constant :DSLContext
|
94
113
|
end
|
114
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Musa
|
4
|
+
module Sequencer
|
5
|
+
class BaseSequencer
|
6
|
+
class Timeslots < Hash
|
7
|
+
|
8
|
+
def initialize(*several_variants)
|
9
|
+
super
|
10
|
+
@sorted_keys = SortedSet.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def []=(key, value)
|
14
|
+
super
|
15
|
+
@sorted_keys << key
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete(key)
|
19
|
+
super
|
20
|
+
@sorted_keys.delete key
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def first_after(position)
|
25
|
+
if position.nil?
|
26
|
+
@sorted_keys.first
|
27
|
+
else
|
28
|
+
@sorted_keys.find { |k| k >= position }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/musa-dsl/series.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
require_relative 'series/series'
|
@@ -1,245 +1,248 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require_relative '../core-ext/deep-copy'
|
2
|
+
require_relative '../generative/generative-grammar'
|
3
|
+
|
4
|
+
using Musa::Extension::DeepCopy
|
3
5
|
|
4
6
|
module Musa
|
5
|
-
module
|
7
|
+
module Series
|
8
|
+
module SerieOperations end
|
6
9
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
10
|
+
module SeriePrototyping
|
11
|
+
def prototype?
|
12
|
+
@is_instance ? false : true
|
13
|
+
end
|
11
14
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
+
def instance?
|
16
|
+
@is_instance ? true : false
|
17
|
+
end
|
15
18
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
def prototype
|
20
|
+
if @is_instance
|
21
|
+
@instance_of || (@instance_of = clone.tap(&:_prototype).mark_as_prototype!)
|
22
|
+
else
|
23
|
+
self
|
24
|
+
end
|
21
25
|
end
|
22
|
-
end
|
23
26
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
+
def _prototype
|
28
|
+
nil
|
29
|
+
end
|
27
30
|
|
28
|
-
|
31
|
+
alias_method :p, :prototype
|
29
32
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
33
|
+
def mark_as_prototype!
|
34
|
+
@is_instance = nil
|
35
|
+
freeze
|
36
|
+
end
|
34
37
|
|
35
|
-
|
38
|
+
protected :_prototype, :mark_as_prototype!
|
36
39
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
40
|
+
def mark_regarding!(source)
|
41
|
+
if source.prototype?
|
42
|
+
mark_as_prototype!
|
43
|
+
else
|
44
|
+
mark_as_instance!
|
45
|
+
end
|
42
46
|
end
|
43
|
-
end
|
44
47
|
|
45
|
-
|
48
|
+
protected :mark_regarding!
|
46
49
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
50
|
+
def instance
|
51
|
+
if @is_instance
|
52
|
+
self
|
53
|
+
else
|
54
|
+
clone(freeze: false).tap(&:_instance).mark_as_instance!(self)
|
55
|
+
end
|
52
56
|
end
|
53
|
-
end
|
54
57
|
|
55
|
-
|
58
|
+
alias_method :i, :instance
|
56
59
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
+
def _instance
|
61
|
+
nil
|
62
|
+
end
|
60
63
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
64
|
+
def mark_as_instance!(prototype = nil)
|
65
|
+
@instance_of = prototype
|
66
|
+
@is_instance = true
|
67
|
+
self
|
68
|
+
end
|
66
69
|
|
67
|
-
|
70
|
+
protected :_instance, :mark_as_instance!
|
68
71
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
72
|
+
class PrototypingSerieError < RuntimeError
|
73
|
+
def initialize(message = nil)
|
74
|
+
message ||= 'This serie is a prototype serie: cannot be consumed. To consume the serie use an instance serie via .instance method'
|
75
|
+
super message
|
76
|
+
end
|
73
77
|
end
|
74
78
|
end
|
75
|
-
end
|
76
79
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
+
module Serie
|
81
|
+
include SeriePrototyping
|
82
|
+
include SerieOperations
|
80
83
|
|
81
|
-
|
82
|
-
|
84
|
+
def restart
|
85
|
+
raise PrototypingSerieError unless @is_instance
|
83
86
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
87
|
+
@_have_peeked_next_value = false
|
88
|
+
@_peeked_next_value = nil
|
89
|
+
@_have_current_value = false
|
90
|
+
@_current_value = nil
|
88
91
|
|
89
|
-
|
92
|
+
_restart if respond_to? :_restart
|
90
93
|
|
91
|
-
|
92
|
-
|
94
|
+
self
|
95
|
+
end
|
93
96
|
|
94
|
-
|
95
|
-
|
97
|
+
def next_value
|
98
|
+
raise PrototypingSerieError unless @is_instance
|
96
99
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
100
|
+
unless @_have_current_value && @_current_value.nil?
|
101
|
+
if @_have_peeked_next_value
|
102
|
+
@_have_peeked_next_value = false
|
103
|
+
@_current_value = @_peeked_next_value
|
104
|
+
else
|
105
|
+
@_current_value = _next_value
|
106
|
+
end
|
103
107
|
end
|
104
|
-
end
|
105
108
|
|
106
|
-
|
109
|
+
propagate_value @_current_value
|
107
110
|
|
108
|
-
|
109
|
-
|
111
|
+
@_current_value
|
112
|
+
end
|
110
113
|
|
111
|
-
|
114
|
+
alias_method :v, :next_value
|
112
115
|
|
113
|
-
|
114
|
-
|
116
|
+
def peek_next_value
|
117
|
+
raise PrototypingSerieError unless @is_instance
|
115
118
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
+
unless @_have_peeked_next_value
|
120
|
+
@_have_peeked_next_value = true
|
121
|
+
@_peeked_next_value = _next_value
|
122
|
+
end
|
123
|
+
|
124
|
+
@_peeked_next_value
|
119
125
|
end
|
120
126
|
|
121
|
-
|
122
|
-
|
127
|
+
def current_value
|
128
|
+
raise PrototypingSerieError unless @is_instance
|
123
129
|
|
124
|
-
|
125
|
-
|
130
|
+
@_current_value
|
131
|
+
end
|
126
132
|
|
127
|
-
|
128
|
-
|
133
|
+
def infinite?
|
134
|
+
false
|
135
|
+
end
|
129
136
|
|
130
|
-
|
131
|
-
|
132
|
-
end
|
137
|
+
def to_a(recursive: nil, duplicate: nil, restart: nil, dr: nil)
|
138
|
+
recursive ||= false
|
133
139
|
|
134
|
-
|
135
|
-
recursive ||= false
|
140
|
+
dr ||= instance?
|
136
141
|
|
137
|
-
|
142
|
+
duplicate = dr if duplicate.nil?
|
143
|
+
restart = dr if restart.nil?
|
138
144
|
|
139
|
-
|
140
|
-
restart = dr if restart.nil?
|
145
|
+
raise 'Cannot convert to array an infinite serie' if infinite?
|
141
146
|
|
142
|
-
|
147
|
+
array = []
|
143
148
|
|
144
|
-
|
149
|
+
serie = instance
|
145
150
|
|
146
|
-
|
151
|
+
serie = serie.clone(deep: true) if duplicate
|
152
|
+
serie = serie.restart if restart
|
147
153
|
|
148
|
-
|
149
|
-
|
154
|
+
while value = serie.next_value
|
155
|
+
array << if recursive
|
156
|
+
process_for_to_a(value)
|
157
|
+
else
|
158
|
+
value
|
159
|
+
end
|
160
|
+
end
|
150
161
|
|
151
|
-
|
152
|
-
array << if recursive
|
153
|
-
process_for_to_a(value)
|
154
|
-
else
|
155
|
-
value
|
156
|
-
end
|
162
|
+
array
|
157
163
|
end
|
158
164
|
|
159
|
-
|
160
|
-
end
|
165
|
+
alias_method :a, :to_a
|
161
166
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
Nodificator.to_node(self, **attributes)
|
166
|
-
end
|
167
|
+
def to_node(**attributes)
|
168
|
+
Nodificator.to_node(self, **attributes)
|
169
|
+
end
|
167
170
|
|
168
|
-
|
171
|
+
alias_method :node, :to_node
|
169
172
|
|
170
|
-
|
171
|
-
|
173
|
+
class Nodificator
|
174
|
+
extend Musa::GenerativeGrammar
|
172
175
|
|
173
|
-
|
174
|
-
|
176
|
+
def self.to_node(serie, **attributes)
|
177
|
+
N(serie, **attributes)
|
178
|
+
end
|
175
179
|
end
|
176
|
-
end
|
177
180
|
|
178
|
-
|
181
|
+
private_constant :Nodificator
|
179
182
|
|
180
|
-
|
183
|
+
protected
|
181
184
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
+
def propagate_value(value)
|
186
|
+
@_slaves.each { |s| s.push_next_value value } if @_slaves
|
187
|
+
end
|
185
188
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
189
|
+
private
|
190
|
+
|
191
|
+
def process_for_to_a(value)
|
192
|
+
case value
|
193
|
+
when Serie
|
194
|
+
value.to_a(recursive: true, restart: false, duplicate: false)
|
195
|
+
when Array
|
196
|
+
a = value.clone
|
197
|
+
a.collect! { |v| v.is_a?(Serie) ? v.to_a(recursive: true, restart: false, duplicate: false) : process_for_to_a(v) }
|
198
|
+
when Hash
|
199
|
+
h = value.clone
|
200
|
+
h.transform_values! { |v| v.is_a?(Serie) ? v.to_a(recursive: true, restart: false, duplicate: false) : process_for_to_a(v) }
|
201
|
+
else
|
202
|
+
value
|
203
|
+
end
|
200
204
|
end
|
201
205
|
end
|
202
206
|
|
203
|
-
|
207
|
+
class Slave
|
208
|
+
include Serie
|
204
209
|
|
205
|
-
|
206
|
-
include Serie
|
210
|
+
attr_reader :master
|
207
211
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
@next_value = []
|
213
|
-
end
|
212
|
+
def initialize(master)
|
213
|
+
@master = master
|
214
|
+
@next_value = []
|
215
|
+
end
|
214
216
|
|
215
|
-
|
216
|
-
|
217
|
-
|
217
|
+
def _restart
|
218
|
+
throw OperationNotAllowedError, "SlaveSerie #{self}: slave series cannot be restarted"
|
219
|
+
end
|
218
220
|
|
219
|
-
|
220
|
-
|
221
|
+
def next_value
|
222
|
+
value = @next_value.shift
|
221
223
|
|
222
|
-
|
224
|
+
raise "Warning: slave serie #{self} has lost sync with his master serie #{@master}" if value.nil? && !@master.peek_next_value.nil?
|
223
225
|
|
224
|
-
|
226
|
+
propagate_value value
|
225
227
|
|
226
|
-
|
227
|
-
|
228
|
+
value
|
229
|
+
end
|
228
230
|
|
229
|
-
|
230
|
-
|
231
|
+
def peek_next_value
|
232
|
+
value = @next_value.first
|
231
233
|
|
232
|
-
|
234
|
+
raise "Warning: slave serie #{self} has lost sync with his master serie #{@master}" if value.nil? && !@master.peek_next_value.nil?
|
233
235
|
|
234
|
-
|
235
|
-
|
236
|
+
value
|
237
|
+
end
|
236
238
|
|
237
|
-
|
238
|
-
|
239
|
-
|
239
|
+
def infinite?
|
240
|
+
@master.infinite?
|
241
|
+
end
|
240
242
|
|
241
|
-
|
242
|
-
|
243
|
+
def push_next_value(value)
|
244
|
+
@next_value << value
|
245
|
+
end
|
243
246
|
end
|
244
247
|
end
|
245
248
|
end
|