Skip to content

Instantly share code, notes, and snippets.

@Arneball
Last active August 29, 2015 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Arneball/9349865 to your computer and use it in GitHub Desktop.
Save Arneball/9349865 to your computer and use it in GitHub Desktop.
import java.util.concurrent.LinkedBlockingDeque
import scala.annotation.tailrec
object `MyOwnFsm;)` extends App{
// val musta = new Musta
// musta ! "to5"
// musta ! "to6"
// musta ! "should crash"
// println
val pc = new PorcheCounter
1 to 3 foreach { _ => pc ! Car("porsche") }
pc ! "status"
}
case class Car(brand: String, year: Int=util.Random.nextInt)
sealed trait GuyStatus
case object CountingMode extends GuyStatus
case object AwakeGuy extends GuyStatus
class PorcheCounter extends FSM[GuyStatus, List[Car]]{
when(AwakeGuy){
case Event(car @ Car("porsche", _), state) => stay using car::state
case Event("count", _) => goto(CountingMode)
}
when(CountingMode){
case Event("work", _) => goto(AwakeGuy)
}
onUnhandled{
case Event("status", sd) =>
println(sd.mkString(","))
stay
}
startWith(AwakeGuy -> Nil)
}
class Musta extends FSM[Int, String]{
onTransition{
case 3 -> _ => println("Going from three")
case 5 -> 6 => println("In transition 5->6")
}
onUnhandled{
case t =>
println(s"Got unhandled message $t")
stay
}
when(3) {
case Event(m @ "to5", "runken") =>
println(s"in 3 $m, going to 5")
goto(5) using "batiken"
}
when(5) {
case Event(m @ "to6", _) =>
println(s"in 5 $m")
goto(6)
case Event(m, _) =>
println(s"in 5 $m")
stay
}
startWith(3 -> "runken")
}
trait FSM[State, Data] {
private type SD = (State, Data)
private type MessageParser = PartialFunction[Any, Transition]
case class Transition private[FSM](state: ->, data: Data) {
def using(data: Data) = copy(data=data)
}
case class Event private[FSM](message: Any, data: Data)
case class -> private[FSM](from: State, to: State)
private val queue = new LinkedBlockingDeque[Any]
private val functions = collection.mutable.HashMap[State, MessageParser]()
private val thread = new Thread{
@tailrec override final def run() = {
val mess = Event(queue.take(), currentData)
val ordinaryFun = functions.get(currentState)
// do transition execution
def transit(transission: ->) = transitions.foreach{
case pf if pf.isDefinedAt(transission) => pf(transission)
case _ => // noop
}
// apply the message to the Messageparser
def handle(pf: MessageParser) = {
val Transition(t @ _ -> newstate, newdata) = pf(mess)
transit(t)
leState = (newstate, newdata)
}
(ordinaryFun, unhandler) match {
case (Some(fun), _) if fun.isDefinedAt(mess) => handle(fun)
case (_, Some(unhand)) if unhand.isDefinedAt(mess) => handle(unhand)
case _ => throw new RuntimeException(s"No function matching $mess")
}
run() // tailcall ;)
}
}
thread.start()
implicit private def stateArrow(states: (State, State)): -> = ->(states._1, states._2)
private var leState: SD = _
private type Trans = PartialFunction[->, Any]
private var transitions = Nil: List[Trans]
def onTransition(pf: Trans) = transitions = pf::transitions
private var unhandler: Option[MessageParser] = None
def onUnhandled(pf: MessageParser) = unhandler = Some(pf)
def stay: Transition = Transition(currentState -> currentState, currentData)
def goto(state: State): Transition = {
Transition(currentState -> state, currentData)
}
def !(a: Any) = queue.offer(a)
/** Setter for state handling shit */
def when(state: State)(function: MessageParser) = {
functions(state) = function
}
def currentState: State = leState._1
def currentData: Data = leState._2
def startWith(state: SD) = leState = state
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment