Created
July 9, 2015 16:56
-
-
Save dsferruzza/955bf8075256b114dcc8 to your computer and use it in GitHub Desktop.
Arduino Internal DSL in Scala, following the lecture by @petitroll at EJCP2015
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
case class ArduinoML(transitions: Seq[Transition]) { | |
def genCode: String = { | |
val statePrefix = "state_" | |
val init = transitions.head.from | |
val bricks = transitions.foldLeft(Set.empty[Brick])((acc, cur) => acc ++ cur.from.actuators ++ cur.to.actuators ++ cur.cond.sensors) | |
val br = bricks | |
.map(" " + _.genCode) | |
.mkString("\n") | |
val tr = transitions | |
.map { transition => | |
val from = transition.from | |
val to = transition.to | |
val actions = from.genActionCode | |
val cond = transition.cond.genCondCode | |
s""" | |
void $statePrefix${from.name}() { | |
$actions | |
if ($cond) { $statePrefix${to.name}(); } else { $statePrefix${from.name}(); } | |
} | |
""" | |
} | |
.mkString("\n") | |
s""" | |
// Code generated by ArduinoML | |
void setup() { | |
// Here comes brick declaration | |
$br | |
} | |
void loop() { | |
$statePrefix${init.name}(); | |
} | |
$tr | |
""" | |
} | |
} | |
sealed trait Brick { | |
def pin: Int | |
def genCode: String = { | |
val direction = this match { | |
case a: Actuator => "OUTPUT" | |
case s: Sensor => "INPUT" | |
} | |
s"pinMode($pin, $direction);" | |
} | |
} | |
case class Actuator(pin: Int) extends Brick | |
case class Sensor(pin: Int) extends Brick with Condition { | |
def genCondCode: String = s"digitalRead($pin) == HIGH" | |
def sensors: Set[Sensor] = Set(this) | |
} | |
//------------------------------------------------------------------ | |
case class State(n: Symbol, actions: (Actuator, Boolean)*) { | |
def name: String = n.name | |
lazy val actuators: Set[Actuator] = actions.map(_._1).toSet | |
def genActionState(s: Boolean): String = s match { | |
case true => "HIGH" | |
case false => "LOW" | |
} | |
def genActionCode: String = { | |
actions | |
.map { | |
case (Actuator(pin), state) => s" digitalWrite($pin, ${genActionState(state)});" | |
} | |
.mkString("\n") | |
} | |
} | |
case class Transition(s: (State, State), cond: Condition) { | |
lazy val from: State = s._1 | |
lazy val to: State = s._2 | |
} | |
sealed trait Condition { | |
def genCondCode: String | |
def sensors: Set[Sensor] | |
} | |
case class Not(c: Condition) extends Condition { | |
def genCondCode: String = c match { | |
case Sensor(pin) => s"digitalRead($pin) == LOW" | |
case cond@And(_) => Or(cond.c.map(con => Not(con)): _*).genCondCode | |
case cond@Or(_) => And(cond.c.map(con => Not(con)): _*).genCondCode | |
case Not(c) => c.genCondCode | |
case cond@_ => s"!(${cond.genCondCode})" | |
} | |
def sensors: Set[Sensor] = c.sensors | |
} | |
case class And(c: Condition*) extends Condition { | |
def genCondCode: String = { | |
val conds = c.map(_.genCondCode).mkString(" && ") | |
s"($conds)" | |
} | |
def sensors: Set[Sensor] = c.flatMap(_.sensors).toSet | |
} | |
case class Or(c: Condition*) extends Condition { | |
def genCondCode: String = { | |
val conds = c.map(_.genCondCode).mkString(" || ") | |
s"($conds)" | |
} | |
def sensors: Set[Sensor] = c.flatMap(_.sensors).toSet | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object Hello extends App { | |
val light = Actuator(12) | |
val button = Sensor(9) | |
val button2 = Sensor(8) | |
val stateOn = State('light_on, light -> true) | |
val stateOn2 = State('light_on2, light -> true) | |
val stateOff = State('light_off, light -> false) | |
val stateOff2 = State('light_off2, light -> false) | |
val t1 = And(Or(button, button2), Not(And(button, button2))) | |
val t2 = Not(Or(button, button2, Not(button2))) | |
val app = ArduinoML( | |
/*Seq( | |
Transition(stateOff -> stateOn, t2), | |
Transition(stateOn -> stateOff, Not(t2)) | |
)*/ | |
Seq( | |
Transition(stateOff2 -> stateOn, button), | |
Transition(stateOn -> stateOn2, Not(button)), | |
Transition(stateOn2 -> stateOff, button), | |
Transition(stateOff -> stateOff2, Not(button)) | |
) | |
) | |
println | |
println | |
println(app) | |
println | |
println(app.genCode) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment