Giter Site home page Giter Site logo

cl-simple-fsm's Introduction

finite-state-machine

An intuitive implementation of a finite state machine.

Operation

Creating a state machine

First, create your state machine, and store it somewhere.

(defvar *state*
  (make-instance 'info.isoraqathedh.finite-state-machine:finite-state-machine
                 :states (list :start
                               :sign
                               :pre-decimal-point-digit
                               :decimal-point
                               :post-decimal-point-digit
                               :reject)
                 :accepting-states (list :pre-decimal-point-digit
                                         :post-decimal-point-digit)))

(All unqualified symbols are in common-lisp or the package that this system uses, info.isoraqathedh.finite-state-machine.)

Alternatively, you can subclass finite-state-machine if you wish to make many of the same machine and give them an identity:

(defclass decimal-recogniser (finite-state-machine)
  ()
  (:default-initargs
   :states (list :start
                 :sign
                 :pre-decimal-point-digit
                 :decimal-point
                 :post-decimal-point-digit
                 :reject)
   :accepting-states (list :pre-decimal-point-digit
                           :post-decimal-point-digit)))

(defvar *state* (make-instance 'decimal-recogniser))

You must provide make-instance a list of states, or it will complain. If you don’t provide a starting state via :start-state, then the first one is automatically selected as the start state. If you don’t provide any :accepting-states, this is acceptable but a little bit silly.

The states can be anything you feel is appropriate, but if the default comparison function #'eql is inadequate, you may want to set :test to compare them with each other. For simplicity, choose keywords.

Transitions

The next step is to define some transitions. This is done by adding methods to next-state, which takes in the state machine (with its current state) and an event.

What that event is can again be anything you desire, as long as you can specify it as a specialiser on the method. If you do use a one-off state machine as above, then you should use an eql specialiser for your methods.

(defmethod next-state ((machine decimal-recogniser) (event character))
  (ecase (state machine)
    (:start
     (case event
       ((#\+ #\-) :sign)
       ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) :pre-decimal-point-digit)
       (#\. :decimal-point)
       (t :reject)))
    (:sign
     (case event
       ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) :pre-decimal-point-digit)
       (#\. :decimal-point)
       (t :reject)))
    (:pre-decimal-point-digit
     (case event
       ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) :pre-decimal-point-digit)
       (#\. :decimal-point)
       (t :reject)))
    ((:decimal-point :post-decimal-point-digit)
     (case event
       ((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) :post-decimal-point-digit)
       (t :reject)))
    (:reject :reject)))

After that, you can run the machine on a particular sequence (here, a string). To check if the machine is in an accepting state, use acceptingp:

(defun decimal-number-p (to-check)
  (loop with recogniser = (make-instance 'decimal-recogniser)
        for c across to-check
        do (next-state! recogniser c)
        finally (return (acceptingp recogniser))))

(decimal-number-p "123.45")
(decimal-number-p "-123")
(decimal-number-p "bogus")

Note we have used next-state! here, which automatically sets the next state on the original object.

For best results, consider a token class that lists out all objects that can change the state of the machine as the event.

Things to do [0/4]

Transition table

There will be a way to more succinctly represent the transition table so that code like state-transition-method don’t have to be written.

Encompassing macros

All of these should have some way to wrap them all around as one coherent whole. Candidates are:

  • define-state-machine, which defines a state machine and its transitions at once; and
  • with-state-machine, which creates a state machine lasting for the body of the macro.

Reconsider history

Finite state machines don’t have history. It may be better to remove them.

Built-in tokens

Consider creating tokenised-finite-state-machine, which contains within it the list of tokens that it recognises.

License

MIT

cl-simple-fsm's People

Contributors

isoraqathedh avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.