This is the mail archive of the guile@cygnus.com mailing list for the guile project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: finite state machines in guile


Klaus Schilling wrote:
> 
> What is a good way to model finite state machines in guile?
> Does it make sense to wrap a R.Smith's libfsm , or can guile do it better
> on its own?
> 
> Klaus Schilling

Klaus, I have modified the previous code that I sent you.  I
have changed the sea table to be seperate from the fsm to
allow reuse of essentially global table.  Also the action
procedures now take the first argument as self.



;; Finite State Machine by lookup table, each state and event has a
;; numerical value.  The FSM is a tagged vector, with current-state,
;; and state/event/action SEA table.
;;
;; An entry in the SEA table is a pair with the first being the next
;; state to move to and then a procedure (with one argument) to
;; execute.
;;
;; A call to process-event! causes the machine to change state to the
;; next state for the current state and event.  This allows the
;; possibility of the FSM to be reentrant.  Then the action procedure
;; is called with a reference to itself and any event specific data
;; there may be.
;;
;; This implementation is derived from lookup tables I have used in
;; implementing communication protocols.  The original machines were
;; written in C, were very simple and very efficient.

(define *fsm-tag* "fsm")

(define (fsm? obj)
  (and (vector? obj) (eq? (vector-ref obj 0) *fsm-tag*)))

(define (assert-fsm obj)
  (if (fsm? obj)
      #t
      (error "Object is not a FSM: " obj)))

(define (current-state obj)
  (assert-fsm obj)
  (vector-ref obj 1))

(define (sea-table obj)
  (assert-fsm obj)
  (vector-ref obj 2))
  
(define (set-current-state! obj new-state)
  (assert-fsm obj)
  (vector-set! obj 1 new-state))

(define (make-fsm initial-state sea-table)
  (vector
   *fsm-tag*
   initial-state ;; Initial State set to 0
   sea-table))

(define (make-sea-table num-states num-events)
  (make-array #f num-states num-events))

(define (make-sea-entry next-state action-proc) ;; sea
  (cons next-state action-proc))

(define (attach-action! sea-table for-state for-event next-state
action-proc)
  (array-set! sea-table (make-sea-entry next-state action-proc)
for-state for-event))

(define (process-event! fsm event . data)
  (let ((sea-entry (array-ref (sea-table fsm) (current-state fsm)
event)))
    (if sea-entry ;; Do Nothing if #f, allow for sparsely populated
tables
	(begin
	  (set-current-state! fsm (car sea-entry))
	  ((cdr sea-entry) fsm (if (null? data) #f (car data)))))))
    

;; Example - Salutation FSM

;; Events
(define hello 0)
(define goodbye 1)
(define number-salutation-events 2)

;; States
(define alone 0)
(define together 1)
(define number-group-states 2)

(define salutor-table (make-sea-table number-group-states
number-salutation-events))
(attach-action! salutor-table alone hello together
		(lambda (self . foo) (display "Hello!") (newline)))
(attach-action! salutor-table alone goodbye alone
		(lambda (self . foo) (display "Who said that!") (newline)))
(attach-action! salutor-table together hello together
		(lambda (self . foo) (display "Hello AGAIN!") (newline)))
(attach-action! salutor-table together goodbye alone
		(lambda (self . foo) (display "Goodbye!") (newline)))

(define george (make-fsm alone salutor-table))

(process-event! george hello)
(process-event! george hello "How is it going?")
(process-event! george goodbye)
(process-event! george goodbye)