lib/alda-rb.rb in alda-rb-0.1.4 vs lib/alda-rb.rb in alda-rb-0.2.0
- old
+ new
@@ -1,1075 +1,9 @@
-require 'readline'
-require 'set'
-require 'stringio'
-require 'irb/ruby-lex'
-require 'alda-rb/version'
-require 'colorize'
+# frozen_string_literal: true
-{
- Array => -> { "[#{map(&:to_alda_code).join ' '}]" },
- Hash => -> { "{#{to_a.reduce(:+).map(&:to_alda_code).join ' '}}" },
- String => -> { dump },
- Symbol => -> { ?: + to_s },
- Numeric => -> { inspect },
- Range => -> { "#{first}-#{last}" },
- TrueClass => -> { 'true' },
- FalseClass => -> { 'false' },
- NilClass => -> { 'nil' }
-}.each { |klass, block| klass.define_method :to_alda_code, &block }
-
-class Proc
- # Runs +self+ for +n+ times.
- def * n
- if !lambda? || arity == 1
- n.times &self
- else
- n.times { self.() }
- end
- end
-end
-
-class StringIO
- # Equivalent to #string.
- def to_s
- string
- end
-end
-
-module Kernel
- # Runs the alda command.
- # Does not capture output.
- # @example
- # alda 'version'
- # alda 'play', '-c', 'piano: a'
- # alda 'repl'
- def alda *args
- system Alda.executable, *args
- end
-end
-
-# The module serving as a namespace.
-module Alda
-
- # The array of available subcommands of alda executable.
- #
- # Alda# is able to invoke +alda+ at the command line.
- # The subcommand is the name of the method invoked upon Alda#.
- #
- # The first argument (a hash) is interpreted as the options.
- # The keyword arguments are interpreted as the subcommand options.
- #
- # The return value is the string output by the command in STDOUT.
- #
- # If the exit code is nonzero, a CommandLineError# is raised.
- # @example
- # Alda.version
- # # => "Client version: 1.4.0\nServer version: [27713] 1.4.0\n"
- # Alda.parse code: 'bassoon: o3 c'
- # # => "{\"chord-mode\":false,\"current-instruments\":...}\n"
- COMMANDS = %i[
- help update repl up start_server init down stop_server
- downup restart_server list status version play stop parse
- instruments export
- ].freeze
-
- COMMANDS.each do |command|
- define_method command do |*args, **opts|
- block = ->key, val do
- next unless val
- args.push "--#{key.to_s.tr ?_, ?-}"
- args.push val.to_s unless val == true
- end
- # executable
- args.unshift Alda.executable
- args.map! &:to_s
- # options
- Alda.options.each &block
- # subcommand
- args.push command.to_s
- # subcommand options
- opts.each &block
- # subprocess
- IO.popen(args, &:read).tap do
- raise CommandLineError.new $?, _1 if $?.exitstatus.nonzero?
- end
- end
- end
-
- # The path to the +alda+ executable.
- #
- # The default value is <tt>"alda"</tt>,
- # which will depend on your PATH.
- singleton_class.attr_accessor :executable
- @executable = 'alda'
-
- singleton_class.attr_reader :options
- @options = {}
-
- # @return Whether the alda server is up.
- def up?
- status.include? 'up'
- end
-
- # @return Whether the alda server is down.
- def down?
- status.include? 'down'
- end
-
- module_function :up?, :down?, *COMMANDS
-
- # Start a REPL session.
- def self.repl
- REPL.new.run
- end
-
- # Sets the options of alda command.
- # Not the subcommand options.
- def self.[] **opts
- @options.merge! opts
- self
- end
-
- # Clears the command line options.
- def self.clear_options
- @options.clear
- end
-
- # Including this module can make your class have the ability
- # to have an event list.
- # See docs below to get an overview of its functions.
- module EventList
-
- # The array containing the events (Event# objects),
- # most of which are EventContainer# objects.
- attr_accessor :events
-
- # The set containing the available variable names.
- attr_accessor :variables
-
- def on_contained
- instance_eval &@block if @block
- end
-
- # Make the object have the ability to appending its +events+
- # conveniently.
- #
- # Here is a list of sugar. When the name of a method meets certain
- # condition, the method is regarded as an event appended to +events+.
- #
- # 1. Ending with 2 underlines: set variable. See SetVariable#.
- #
- # 2. Starting with 2 lowercase letters and
- # ending with underline character: instrument. See Part#.
- #
- # 3. Starting with 2 lowercase letters: inline lisp code,
- # set variable, or get variable.
- # One of the above three is chosen intelligently.
- # See InlineLisp#, SetVariable#, GetVariable#.
- #
- # 4. Starting with "t": CRAM. See Cram#.
- #
- # 5. Starting with one of "a", "b", ..., "g": note. See Note#.
- #
- # 6. Starting with "r": rest. See Rest#.
- #
- # 7. "x": chord. See Chord#.
- #
- # 8. "s": sequence. See Sequence#.
- #
- # 9. Starting with "o": octave. See Octave#.
- #
- # 10. Starting with "v": voice. See Voice#.
- #
- # 11. Starting with "__" (2 underlines): at marker. See AtMarker#.
- #
- # 12. Starting with "_" (underline): marker. See Marker#.
- #
- # Notes cannot have dots.
- # To tie multiple durations, +_+ is used instead of +~+.
- #
- # All the appended events are contained in an EventContainer# object,
- # which is to be returned.
- #
- # These sugars forms a DSL.
- # @see #initialize.
- # @return an EventContainer# object.
- def method_missing name, *args, &block
- if @parent&.respond_to? name, true
- return @parent.__send__ name, *args, &block
- end
- sequence_sugar = ->event do
- if args.size == 1
- joined = args.first
- unless (got = @events.pop) == (expected = joined)
- raise OrderError.new expected, got
- end
- Sequence.join event, joined
- else
- event
- end
- end
- case
- when /\A(?<head>[a-z][a-z].*)__\z/ =~ name
- SetVariable.new head, *args, &block
- when /\A(?<part>[a-z][a-z].*)_\z/ =~ name
- if args.first.is_a? String
- Part.new [part], args.first
- else
- sequence_sugar.(Part.new [part])
- end
- when /\A[a-z][a-z].*\z/ =~ name
- if block
- SetVariable.new name, *args, &block
- elsif has_variable?(name) && (args.empty? || args.size == 1 && args.first.is_a?(Event))
- sequence_sugar.(GetVariable.new name)
- else
- InlineLisp.new name, *args
- end
- when /\At(?<duration>.*)\z/ =~ name
- Cram.new duration, &block
- when /\A(?<pitch>[a-g])(?<duration>.*)\z/ =~ name
- sequence_sugar.(Note.new pitch, duration)
- when /\Ar(?<duration>.*)\z/ =~ name
- sequence_sugar.(Rest.new duration)
- when /\Ax\z/ =~ name
- Chord.new &block
- when /\As\z/ =~ name
- Sequence.new *args, &block
- when /\Ao!\z/ =~ name
- sequence_sugar.(Octave.new('').tap { _1.up_or_down = 1})
- when /\Ao\?\z/ =~ name
- sequence_sugar.(Octave.new('').tap { _1.up_or_down = -1})
- when /\Ao(?<num>\d*)\z/ =~ name
- sequence_sugar.(Octave.new num)
- when /\Av(?<num>\d+)\z/ =~ name
- sequence_sugar.(Voice.new num)
- when /\A__(?<head>.+)\z/ =~ name
- sequence_sugar.(AtMarker.new head)
- when /\A_(?<head>.+)\z/ =~ name
- sequence_sugar.(Marker.new head)
- else
- super
- end.then do |event|
- EventContainer.new event, self
- end.tap &@events.method(:push)
- end
-
- def has_variable? name
- @variables.include?(name) || !!@parent&.has_variable?(name)
- end
-
- # Append the events of another EventList# object here.
- # This method covers the disadvantage of alda's being unable to
- # import scores from other files.
- # See https://github.com/alda-lang/alda-core/issues/8.
- def import event_list
- @events.concat event_list.events
- end
-
- # @param block to be passed with the EventList# object as +self+.
- # @example
- # Alda::Score.new do
- # tempo! 108 # inline lisp
- # piano_ # piano part
- # o4 # octave 4
- # c8; d; e; f # notes
- # g4 g a f g e f d e c # a sequence
- # d4_8 # cannot have '~', use '_' instead
- # o3 b8 o4 c2 # a sequence
- # end
- # # => #<Alda::Score:0x... @events=[...]>
- def initialize &block
- @events ||= []
- @variables ||= Set.new
- @block ||= block
- end
-
- # Same as #events
- def to_a
- @events
- end
-
- # Join the alda codes of #events with a specified delimiter.
- # Returns a string representing the result.
- def events_alda_codes delimiter = ' '
- @events.map(&:to_alda_code).join delimiter
- end
- end
-
- # The class mixes in EventList# and provides methods to play or parse.
- class Score
- include EventList
-
- # Plays the score.
- # @return The command line output of the +alda+ command.
- # @example
- # Alda::Score.new { piano_; c; d; e }.play
- # # => "[27713] Parsing/evaluating...\n[27713] Playing...\n"
- # # (and plays the sound)
- # Alda::Score.new { piano_; c; d; e }.play from: 1
- # # (plays only an E note)
- def play **opts
- Alda.play code: self, **opts
- end
-
- # Parses the score.
- # @return The JSON string of the parse result.
- # @example
- # Alda::Score.new { piano_; c }.parse output: :events
- # # => "[{\"event-type\":...}]\n"
- def parse **opts
- Alda.parse code: self, **opts
- end
-
- # Exports the score.
- # @return The command line output of the +alda+ command.
- # @example
- # Alda::Score.new { piano_; c }.export output: 'temp.mid'
- # # (outputs a midi file called temp.mid)
- def export **opts
- Alda.export code: self, **opts
- end
-
- # Saves the alda codes into a file.
- def save filename
- File.open(filename, 'w') { _1.puts to_s }
- end
-
- # Loads alda codes from a file.
- def load filename
- event = InlineLisp.new :alda_code, File.read(filename)
- @events.push event
- event
- end
-
- # @return Alda codes.
- def to_s
- events_alda_codes
- end
-
- # The initialization.
- def initialize(...)
- super
- on_contained
- end
-
- # Clears all the events and variables.
- def clear
- @events.clear
- @variables.clear
- self
- end
- end
-
- # An encapsulation for the REPL session for alda-rb.
- class REPL
-
- # The score object used in REPL.
- # Includes Alda#, so it can refer to alda commandline.
- class TempScore < Score
- include Alda
-
- Score.instance_methods(false).each do |meth|
- define_method meth, Score.instance_method(meth)
- end
-
- def initialize session
- super()
- @session = session
- end
-
- def to_s
- history
- end
-
- def history
- @session.history.to_s
- end
-
- def clear_history
- @session.clear_history
- end
-
- def get_binding
- binding
- end
-
- alias quit exit
- end
-
- # The history.
- attr_reader :history
-
- # Initialization.
- def initialize
- @score = TempScore.new self
- @binding = @score.get_binding
- @lex = RubyLex.new
- @history = StringIO.new
- end
-
- # Runs the session. Includes the start, the main loop, and the termination.
- def run
- start
- while code = rb_code
- break unless process_rb_code code
- end
- terminate
- end
-
- # Starts the session.
- def start
- end
-
- # Reads the next Ruby codes input in the REPL session.
- # It can intelligently continue reading if the code is not complete yet.
- def rb_code
- result = ''
- begin
- buf = Readline.readline '> '.green, true
- return unless buf
- result.concat buf, ?\n
- ltype, indent, continue, block_open = @lex.check_state result
- rescue Interrupt
- $stdout.puts
- retry
- end while ltype || indent.nonzero? || continue || block_open
- result
- end
-
- # Processes the Ruby codes read.
- # Sending it to a score and sending the result to alda.
- # @return +true+ for continue looping, +false+ for breaking the loop.
- def process_rb_code code
- @score.clear
- begin
- @binding.eval code
- rescue StandardError, ScriptError => e
- $stderr.print e.full_message
- return true
- rescue Interrupt
- return true
- rescue SystemExit
- return false
- end
- code = @score.events_alda_codes
- unless code.empty?
- $stdout.puts code.yellow
- play_score code
- end
- true
- end
-
- # Tries to run the block and rescue CommandLineError#.
- def try_command # :block:
- begin
- yield
- rescue CommandLineError => e
- puts e.message.red
- end
- end
-
- # Plays the score.
- def play_score code
- try_command do
- Alda.play code: code, history: @history
- @history.puts code
- end
- end
-
- # Terminates the REPL session.
- def terminate
- clear_history
- end
-
- # Clears the history.
- def clear_history
- @history = StringIO.new
- end
- end
-
- # The error is raised when one tries to
- # run a non-existing subcommand of +alda+.
- class CommandLineError < StandardError
-
- # The <tt>Process::Status</tt> object representing the status of
- # the process that runs +alda+ command.
- attr_reader :status
-
- # The port on which the problematic Alda server runs.
- # @example
- # begin
- # Alda.play({port: 1108}, code: "y")
- # rescue CommandLineError => e
- # e.port # => 1108
- # end
- attr_reader :port
-
- # Create a CommandLineError# object.
- # @param status The status of the process running +alda+ command.
- # @param msg The exception message.
- def initialize status, msg = nil
- if match = msg&.match(/^\[(?<port>\d+)\]\sERROR\s(?<message>.*)$/)
- super match[:message]
- @port = match[:port].to_i
- else
- super msg
- @port = nil
- end
- @status = status
- end
- end
-
- # This error is raised when one tries to
- # append events in an EventList# in a wrong order.
- # @example
- # Alda::Score.new do
- # motif = f4 f e e d d c2
- # g4 f e d c2 # It commented out, error will not occur
- # c4 c g g a a g2 motif # (OrderError)
- # end
- class OrderError < StandardError
-
- # The expected element gotten if it is of the correct order.
- # @see #got
- # @example
- # Alda::Score.new do
- # motif = f4 f e e d d c2
- # g4 f e d c2
- # p @events.size # => 2
- # c4 c g g a a g2 motif
- # rescue OrderError => e
- # p @events.size # => 1
- # p e.expected # => #<Alda::EventContainer:...>
- # p e.got # => #<Alda::EventContainer:...>
- # end
- attr_reader :expected
-
- # The actually gotten element.
- # For an example, see #expected.
- # @see #expected
- attr_reader :got
-
- def initialize expected, got
- super 'events are out of order'
- @expected = expected
- @got = got
- end
- end
-
- # The class of elements of EventList#events.
- class Event
-
- # The EventList# object that contains it.
- # Note that it may not be directly contained, but with an EventContainer#
- # object in the middle.
- attr_accessor :parent
-
- # The EventContainer# object that contains it.
- # It may be +nil+, especially probably when
- # it itself is an EventContainer#.
- attr_accessor :container
-
- # The callback invoked when it is contained in an EventContainer#.
- # It is overridden in InlineLisp# and EventList#.
- # @example
- # class Alda::Note
- # def on_contained
- # puts 'a note contained'
- # end
- # end
- # Alda::Score.new { c } # => outputs "a note contained"
- def on_contained
- end
-
- # Converts to alda code. To be overridden.
- def to_alda_code
- ''
- end
- end
-
- # The class for objects containing an event.
- class EventContainer < Event
-
- # The contained Event# object.
- attr_accessor :event
-
- # The repetition counts. +nil+ if none.
- attr_accessor :count
-
- # The repetition labels. Empty if none.
- attr_accessor :labels
-
- # @param event The Event# object to be contained.
- # @param parent The EventList# object containing the event.
- def initialize event, parent
- @event = event
- @parent = parent
- @labels = []
- on_containing
- end
-
- # Make #event a Chord# object.
- # @example
- # Alda::Score.new { piano_; c/-e/g }.play
- # # (plays the chord Cm)
- #
- # If the contained event is a Part# object,
- # make #event a new Part# object.
- # @example
- # Alda::Score.new { violin_/viola_/cello_; e; f; g}.play
- # # (plays notes E, F, G with three instruments simultaneously)
- def / other
- unless (expected = other) == (got = @parent.events.pop)
- raise OrderError.new expected, got
- end
- @event =
- if @event.is_a? Part
- Part.new @event.names + other.event.names, other.event.arg
- else
- Chord.new @event, other.event
- end
- self
- end
-
- def to_alda_code
- result = @event.to_alda_code
- unless @labels.empty?
- result.concat ?', @labels.map(&:to_alda_code).join(?,)
- end
- result.concat ?*, @count.to_alda_code if @count
- result
- end
-
- # Marks repetition.
- def * num
- @count = (@count || 1) * num
- self
- end
-
- # Marks alternative repetition.
- def % labels
- labels = [labels] unless labels.is_a? Array
- @labels.replace labels.to_a
- self
- end
-
- def event= event
- @event = event
- on_containing
- @event
- end
-
- def on_containing
- if @event
- @event.container = self
- @event.parent = @parent
- @event.on_contained
- end
- end
-
- def method_missing name, *args
- result = @event.__send__ name, *args
- result = self if result == @event
- result
- end
- end
-
- # Inline lisp event.
- class InlineLisp < Event
-
- # The function name of the lisp function
- attr_accessor :head
-
- # The arguments passed to the lisp function.
- # Its elements can be
- # Array#, Hash#, Numeric#, String#, Symbol#, or Event#.
- attr_accessor :args
-
- # The underlines in +head+ will be converted to hyphens.
- def initialize head, *args
- @head = head.to_s.gsub ?_, ?-
- @args = args
- end
-
- def to_alda_code
- "(#{head} #{args.map(&:to_alda_code).join ' '})"
- end
-
- def on_contained
- super
- @args.reverse_each do |event|
- if event.is_a?(Event) && (expected = event) != (got = @parent.events.pop)
- raise OrderError.new expected, got
- end
- end
- end
- end
-
- # A note event.
- class Note < Event
-
- # The string representing the pitch
- attr_accessor :pitch
-
- # The string representing the duration.
- # It ends with +~+ if the note slurs.
- attr_accessor :duration
-
- # The underlines in +duration+ will be converted to +~+.
- # Exclamation mark and question mark in +duration+
- # will be interpreted as accidentals in #pitch.
- #
- # The number of underlines at the end of +duration+ means:
- # neither natural nor slur if 0,
- # natural if 1,
- # slur if 2,
- # both natural and slur if 3.
- def initialize pitch, duration
- @pitch = pitch.to_s
- @duration = duration.to_s.tr ?_, ?~
- case @duration[-1]
- when ?! # sharp
- @pitch.concat ?+
- @duration[-1] = ''
- when ?? # flat
- @pitch.concat ?-
- @duration[-1] = ''
- end
- waves = /(?<str>~+)\z/ =~ @duration ? str.size : return
- @duration[@duration.length - waves..] = ''
- if waves >= 2
- waves -= 2
- @duration.concat ?~
- end
- @pitch.concat ?_ * waves
- end
-
- # Append a sharp sign after #pitch.
- # @example
- # Alda::Score.new { piano_; +c }.play
- # # (plays a C\# note)
- def +@
- @pitch.concat ?+
- self
- end
-
- # Append a flat sign after #pitch.
- # @example
- # Alda::Score.new { piano_; -d }.play
- # # (plays a Db note)
- def -@
- @pitch.concat ?-
- self
- end
-
- # Append a natural sign after #pitch
- # @example
- # Alda::Score.new { piano_; key_sig 'f+'; ~f }.play
- # # (plays a F note)
- def ~
- @pitch.concat ?_
- self
- end
-
- def to_alda_code
- result = @pitch + @duration
- result.concat ?*, @count.to_alda_code if @count
- result
- end
- end
-
- # A rest event.
- class Rest < Event
-
- # The string representing a duration.
- attr_accessor :duration
-
- # Underlines in +duration+ will be converted to +~+.
- def initialize duration
- @duration = duration.to_s.tr ?_, ?~
- end
-
- def to_alda_code
- ?r + @duration
- end
- end
-
- # An octave event.
- class Octave < Event
-
- # The string representing the octave's number.
- # It can be empty, serving for #+@ and #-@.
- attr_accessor :num
-
- # Positive for up, negative for down, and 0 as default.
- attr_accessor :up_or_down
-
- def initialize num
- @num = num.to_s
- @up_or_down = 0
- end
-
- # Octave up.
- # @example
- # Alda::Score.new { piano_; c; +o; c }.play
- # # (plays C4, then C5)
- # @see #-@
- def +@
- @up_or_down += 1
- self
- end
-
- # Octave down.
- # @see #+@.
- def -@
- @up_or_down -= 1
- self
- end
-
- def to_alda_code
- case @up_or_down <=> 0
- when 0
- ?o + @num
- when 1
- ?> * @up_or_down
- when -1
- ?< * -@up_or_down
- end
- end
- end
-
- # A chord event.
- # Includes EventList#.
- class Chord < Event
- include EventList
-
- # EventList#x invokes this method.
- # @see EventList#method_missing
- # @param events In most cases, should not be used.
- # @param block To be passed with the Chord# object as +self+.
- # @example
- # Alda::Score.new { piano_; x { c; -e; g } }.play
- # # (plays chord Cm)
- def initialize *events, &block
- @events = events
- super &block
- end
-
- def to_alda_code
- events_alda_codes ?/
- end
- end
-
- # A part event.
- class Part < Event
-
- # The names of the part. To be joined with +/+ as delimiter.
- attr_accessor :names
-
- # The nickname of the part. +nil+ if none.
- attr_accessor :arg
-
- def initialize names, arg = nil
- @names = names.map { |name| name.to_s.tr ?_, ?- }
- @arg = arg
- end
-
- def to_alda_code
- result = @names.join ?/
- result.concat " \"#{@arg}\"" if @arg
- result.concat ?:
- end
-
- # Enables dot accessor.
- # @example
- # Alda::Score.new do
- # violin_/viola_/cello_('strings'); g1_1_1
- # strings_.cello_; -o; c1_1_1
- # end.play
- def method_missing name, *args
- str = name.to_s
- return super unless str[-1] == ?_
- str[-1] = ''
- @names.last.concat ?., str
- if args.size == 1
- joined = args.first
- unless (got = @parent.events.pop) == (expected = joined)
- raise OrderError.new expected, got
- end
- unless @container
- @container = EventContainer.new nil, @parent
- @parent.events.delete self
- @parent.push @container
- end
- @container.event = Sequence.join self, joined
- end
- end
- end
-
- # A voice event.
- class Voice < Event
-
- # The string representing the voice's number.
- attr_accessor :num
-
- def initialize num
- @num = num
- end
-
- def to_alda_code
- ?V + num + ?:
- end
- end
-
- # A CRAM event. Includes EventList#.
- class Cram < Event
- include EventList
-
- # The string representing the duration of the CRAM.
- attr_accessor :duration
-
- # EventList#t invokes this method.
- # @see EventList#method_missing
- # @param block To be passed with the CRAM as +self+.
- # @example
- # Alda::Score.new { piano_; t8 { x; y; }}
- def initialize duration, &block
- @duration = duration
- super &block
- end
-
- def to_alda_code
- "{#{events_alda_codes}}#@duration"
- end
- end
-
- # A marker event.
- # @see AtMarker#
- class Marker < Event
-
- # The marker's name
- attr_accessor :name
-
- # Underlines in +name+ is converted to hyphens.
- def initialize name
- @name = name.to_s.tr ?_, ?-
- end
-
- def to_alda_code
- ?% + @name
- end
- end
-
- # An at-marker event.
- # @see Marker#
- class AtMarker < Event
-
- # The corresponding marker's name
- attr_accessor :name
-
- # Underlines in +name+ is converted to hyphens.
- def initialize name
- @name = name.to_s.tr ?_, ?-
- end
-
- def to_alda_code
- ?@ + @name
- end
- end
-
- # A sequence event. Includes EventList#.
- class Sequence < Event
- include EventList
-
- # Using this module can fix a bug of Array#flatten.
- # @example
- # def (a = Object.new).method_missing(...)
- # Object.new
- # end
- # [a].flatten rescue $! # => #<TypeError:...>
- # using Alda::Sequence::RefineFlatten
- # [a].flatten # => [#<Object:...>]
- module RefineFlatten
- refine Array do
- def flatten
- each_with_object [] do |element, result|
- if element.is_a? Array
- result.push *element.flatten
- else
- result.push element
- end
- end
- end
- end
- end
- using RefineFlatten
-
- def to_alda_code
- @events.to_alda_code
- end
-
- # Creates a Sequence# object by joining +events+.
- # The EventContainer# objects are extracted,
- # and the Sequence# objects are flattened.
- def self.join *events
- new do
- @events = events.map do |event|
- while event.is_a?(EventContainer) && !event.count && event.labels.empty?
- event = event.event
- end
- event.is_a?(Sequence) ? event.events : event
- end.flatten
- end
- end
- end
-
- # A set-variable event.
- # Includes EventList#.
- class SetVariable < Event
- include EventList
-
- # The name of the variable.
- attr_accessor :name
-
- # The events passed to it using arguments instead of a block.
- attr_reader :original_events
-
- def initialize name, *events, &block
- @name = name.to_sym
- @original_events = events
- @events = events.clone
- super &block
- end
-
- # Specially, the result ends with a newline.
- def to_alda_code
- "#@name = #{events_alda_codes}\n"
- end
-
- def on_contained
- super
- @parent.variables.add @name
- @original_events.reverse_each do |event|
- unless (expected = event) == (got = @parent.events.pop)
- raise OrderError.new expected, got
- end
- end
- end
- end
-
- # A get-variable event
- class GetVariable < Event
-
- # The name of the variable
- attr_accessor :name
-
- def initialize name
- @name = name
- end
-
- def to_alda_code
- @name.to_s
- end
- end
-end
+require 'alda-rb/version'
+require 'alda-rb/patches'
+require 'alda-rb/error'
+require 'alda-rb/commandline'
+require 'alda-rb/event_list'
+require 'alda-rb/repl'
+require 'alda-rb/event'