Module: Pushdown::Automaton
- Extended by:
- Loggability
- Defined in:
- lib/pushdown/automaton.rb
Overview
A mixin that adds pushdown-automaton functionality to another module/class.
Defined Under Namespace
Modules: InstanceMethods
Class Method Summary collapse
-
.extended(object) ⇒ Object
Extension callback – add some stuff to extending objects.
-
.generate_event_method(name, object) ⇒ Object
Generate the external event handler method for the pushdown state named
nameon the specifiedobject. -
.generate_initial_state_method(name) ⇒ Object
Generate the method that returns the initial state class for a pushdown state named
name. -
.generate_shadow_update_method(name, object) ⇒ Object
Generate the timed update method for every pushdown state named
nameon the specifiedobject. -
.generate_state_method(name, object) ⇒ Object
Generate the method used to access the current state object.
-
.generate_update_method(name, object) ⇒ Object
Generate the timed update method for the active pushdown state named
nameon the specifiedobject. -
.install_state_methods(name, object) ⇒ Object
Generate the pushdown API methods for the pushdown automaton with the given
nameand install them in the extendingobject.
Instance Method Summary collapse
-
#pushdown_inferred_state_class(class_name) ⇒ Object
Return the state class with the name inferred from the given
class_name. -
#pushdown_pluggable_state_class(state_base_class, class_name) ⇒ Object
Derive a state class object named
class_namevia the (pluggable)state_base_class. -
#pushdown_state(name, initial_state:, states: nil) ⇒ Object
Declare a attribute
namewhich is a pushdown state. -
#pushdown_state_class(state_name, class_name) ⇒ Object
Return the Class object for the class named
class_nameof the pushdown statestate_name.
Class Method Details
.extended(object) ⇒ Object
Extension callback – add some stuff to extending objects.
18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/pushdown/automaton.rb', line 18 def self::extended( object ) super unless object.respond_to?( :log ) object.extend( Loggability ) object.log_to( :pushdown ) end object.instance_variable_set( :@pushdown_states, {} ) object.singleton_class.attr_reader( :pushdown_states ) object.include( Pushdown::Automaton::InstanceMethods ) end |
.generate_event_method(name, object) ⇒ Object
Generate the external event handler method for the pushdown state named name on the specified object.
119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/pushdown/automaton.rb', line 119 def self::generate_event_method( name, object ) self.log.debug "Generating event method for %p: handle_%s_event" % [ object, name ] stack_method = object.instance_method( "#{name}_stack" ) meth = lambda do |event, *args| stack = stack_method.bind( self ).call current_state = stack.last result = current_state.on_event( event, *args ) return self.handle_pushdown_result( stack, result, name ) end end |
.generate_initial_state_method(name) ⇒ Object
Generate the method that returns the initial state class for a pushdown state named name.
171 172 173 174 175 176 177 178 |
# File 'lib/pushdown/automaton.rb', line 171 def self::generate_initial_state_method( name ) self.log.debug "Generating initial state method for %p" % [ name ] return lambda do config = self.pushdown_states[ name ] class_name = config[ :initial_state ] return self.pushdown_state_class( name, class_name ) end end |
.generate_shadow_update_method(name, object) ⇒ Object
Generate the timed update method for every pushdown state named name on the specified object.
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/pushdown/automaton.rb', line 151 def self::generate_shadow_update_method( name, object ) self.log.debug "Generating shadow update method for %p: shadow_update_%s" % [ object, name ] stack_method = object.instance_method( "#{name}_stack" ) meth = lambda do |*args| stack = stack_method.bind( self ).call stack.each do |state| state.shadow_update( *args ) end # :TODO: Calling/return convention? Could do something like #flat_map the # results? Or map to a hash keyed by state object? Is it useful enough to justify # the object churn of a method that might potentionally be in a hot loop? return nil end end |
.generate_state_method(name, object) ⇒ Object
Generate the method used to access the current state object.
108 109 110 111 112 113 114 |
# File 'lib/pushdown/automaton.rb', line 108 def self::generate_state_method( name, object ) self.log.debug "Generating current state method for %p: %p" % [ object, name ] stack_method = object.instance_method( "#{name}_stack" ) meth = lambda { stack_method.bind(self).call&.last } return meth end |
.generate_update_method(name, object) ⇒ Object
Generate the timed update method for the active pushdown state named name on the specified object.
135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/pushdown/automaton.rb', line 135 def self::generate_update_method( name, object ) self.log.debug "Generating update method for %p: update_%s" % [ object, name ] stack_method = object.instance_method( "#{name}_stack" ) meth = lambda do |*args| stack = stack_method.bind( self ).call current_state = stack.last result = current_state.update( *args ) return self.handle_pushdown_result( stack, result, name ) end end |
.install_state_methods(name, object) ⇒ Object
Generate the pushdown API methods for the pushdown automaton with the given name and install them in the extending object.
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/pushdown/automaton.rb', line 85 def self::install_state_methods( name, object ) self.log.debug "Installing pushdown methods for %p in %p" % [ name, object ] object.attr_reader( "#{name}_stack" ) # Relies on the above method having already been declared state_method = self.generate_state_method( name, object ) object.define_method( name, &state_method ) event_method = self.generate_event_method( name, object ) object.define_method( "handle_#{name}_event", &event_method ) update_method = self.generate_update_method( name, object ) object.define_method( "update_#{name}", &update_method ) update_method = self.generate_shadow_update_method( name, object ) object.define_method( "shadow_update_#{name}", &update_method ) initial_state_method = self.generate_initial_state_method( name ) object.define_singleton_method( "initial_#{name}", &initial_state_method ) end |
Instance Method Details
#pushdown_inferred_state_class(class_name) ⇒ Object
Return the state class with the name inferred from the given class_name.
210 211 212 213 214 215 216 217 |
# File 'lib/pushdown/automaton.rb', line 210 def pushdown_inferred_state_class( class_name ) constant_name = class_name.to_s.capitalize.gsub( /_(\p{Alnum})/ ) do |match| match[ 1 ].capitalize end self.log.debug "Inferred state class for %p is: %s" % [ class_name, constant_name ] return self.const_get( constant_name ) end |
#pushdown_pluggable_state_class(state_base_class, class_name) ⇒ Object
Derive a state class object named class_name via the (pluggable) state_base_class.
222 223 224 |
# File 'lib/pushdown/automaton.rb', line 222 def pushdown_pluggable_state_class( state_base_class, class_name ) return state_base_class.get_subclass( class_name ) end |
#pushdown_state(name, initial_state:, states: nil) ⇒ Object
Declare a attribute name which is a pushdown state.
182 183 184 185 186 |
# File 'lib/pushdown/automaton.rb', line 182 def pushdown_state( name, initial_state:, states: nil ) @pushdown_states[ name ] = { initial_state: initial_state, states: states } Pushdown::Automaton.install_state_methods( name, self ) end |
#pushdown_state_class(state_name, class_name) ⇒ Object
Return the Class object for the class named class_name of the pushdown state state_name.
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/pushdown/automaton.rb', line 191 def pushdown_state_class( state_name, class_name ) config = self.pushdown_states[ state_name ] or raise "No pushdown state named %p" % [ state_name ] states = config[ :states ] case states when NilClass return self.pushdown_inferred_state_class( class_name ) when Class return self.pushdown_pluggable_state_class( states, class_name ) when Hash return states[ class_name ] else raise "don't know how to derive a state class from %p (%p)" % [ states, states.class ] end end |