module Arduino.DSL
(
Action
, Stream
, Output
, LLI
, compileProgram
, def
, (=:)
, foo
, Expression
, DAG.Bit
, bitHigh
, bitLow
, flipBit
, isHigh
, boolToBit
, DAG.Byte
, DAG.Word
, isEven
, greater
, formatString
, formatNumber
, pack2
, pack6
, unpack2
, unpack6
, output2
, output6
, if_
, mapS
, mapSMany
, mapS2
, filterS
, foldpS
, flattenS
, delay
, (~>)
, createOutput
, createInput
, setBit
, clearBit
, writeBit
, writeByte
, writeWord
, readBit
, readWord
, waitBitSet
, byteConstant
, wordConstant
, end
) where
import Arduino.Internal.CodeGen.C (streamsToC)
import Arduino.Internal.CodeGen.Dot(streamsToDot)
import Control.Monad.State
import Data.Char (ord)
import qualified Arduino.Internal.DAG as DAG
data DAGState = DAGState
{ idCounter :: Int
, dag :: DAG.Streams
}
type Action a = State DAGState a
newtype Stream a = Stream { unStream :: Action DAG.Identifier }
newtype Expression a = Expression { unExpression :: DAG.Expression }
newtype Output a = Output { unOutput :: Stream a -> Action () }
newtype LLI a = LLI { unLLI :: DAG.LLI }
instance Num (Expression a) where
(+) left right = Expression $ DAG.Add (unExpression left) (unExpression right)
() left right = Expression $ DAG.Sub (unExpression left) (unExpression right)
(*) = error "* not yet implemented"
abs = error "abs not yet implemented"
signum = error "signum not yet implemented"
fromInteger value = Expression $ DAG.WordConstant $ fromIntegral value
compileProgram :: Action a -> IO ()
compileProgram action = do
let dagState = execState action (DAGState 1 DAG.emptyStreams)
writeFile "main.c" $ streamsToC (dag dagState)
writeFile "dag.dot" $ streamsToDot (dag dagState)
def :: Stream a -> Action (Stream a)
def stream = do
name <- unStream stream
return $ Stream $ return name
(=:) :: Output a -> Stream a -> Action ()
(=:) = unOutput
infixr 0 =:
foo :: Output a -> (Stream b -> Stream a) -> Output b
foo output fn = Output $ \stream -> do
output =: fn stream
output2 :: Output a1
-> Output a2
-> Output (a1, a2)
output2 output1 output2 =
Output $ \stream -> do
x <- def stream
output1 =: x ~> mapS (\x -> let (a, _) = unpack2 x in a)
output2 =: x ~> mapS (\x -> let (_, a) = unpack2 x in a)
output6 :: Output a1
-> Output a2
-> Output a3
-> Output a4
-> Output a5
-> Output a6
-> Output (a1, a2, a3, a4, a5, a6)
output6 output1 output2 output3 output4 output5 output6 =
Output $ \stream -> do
x <- def stream
output1 =: x ~> mapS (\x -> let (a, _, _, _, _, _) = unpack6 x in a)
output2 =: x ~> mapS (\x -> let (_, a, _, _, _, _) = unpack6 x in a)
output3 =: x ~> mapS (\x -> let (_, _, a, _, _, _) = unpack6 x in a)
output4 =: x ~> mapS (\x -> let (_, _, _, a, _, _) = unpack6 x in a)
output5 =: x ~> mapS (\x -> let (_, _, _, _, a, _) = unpack6 x in a)
output6 =: x ~> mapS (\x -> let (_, _, _, _, _, a) = unpack6 x in a)
(~>) :: Stream a -> (Stream a -> Stream b) -> Stream b
(~>) stream fn = Stream $ do
streamName <- unStream stream
let outputStream = fn (Stream (return streamName))
unStream $ outputStream
mapS :: (Expression a -> Expression b) -> Stream a -> Stream b
mapS fn stream = Stream $ do
streamName <- unStream stream
expressionStreamName <- addAnonymousStream (DAG.Map expression)
addDependency streamName expressionStreamName
where
expression = unExpression $ fn $ Expression $ DAG.Input 0
mapSMany :: (Expression a -> [Expression b]) -> Stream a -> Stream b
mapSMany fn stream = Stream $ do
streamName <- unStream stream
expressionStreamName <- addAnonymousStream (DAG.MapMany expression)
addDependency streamName expressionStreamName
where
expression = map unExpression $ fn $ Expression $ DAG.Input 0
mapS2 :: (Expression a -> Expression b -> Expression c)
-> Stream a
-> Stream b
-> Stream c
mapS2 fn left right = Stream $ do
leftName <- unStream left
rightName <- unStream right
expressionStreamName <- addAnonymousStream (DAG.Map expression)
addDependency leftName expressionStreamName
addDependency rightName expressionStreamName
where
expression = unExpression $ fn (Expression $ DAG.Input 0)
(Expression $ DAG.Input 1)
delay :: Stream (a, DAG.Word) -> Stream a
delay stream = Stream $ do
streamName <- unStream stream
expressionStreamName <- addAnonymousStream expression
addDependency streamName expressionStreamName
where
expression = DAG.DelayMicroseconds (DAG.TupleValue 1 (DAG.Input 0))
(DAG.TupleValue 0 (DAG.Input 0))
filterS :: (Expression a -> Expression Bool) -> Stream a -> Stream a
filterS fn stream = Stream $ do
streamName <- unStream stream
expressionStreamName <- addAnonymousStream filterTransform
addDependency streamName expressionStreamName
where
filterTransform = DAG.Filter expression
expression = unExpression $ fn $ Expression $ DAG.Input 0
foldpS :: (Expression a -> Expression b -> Expression b)
-> Expression b
-> Stream a
-> Stream b
foldpS fn startValue stream = Stream $ do
streamName <- unStream stream
expressionStreamName <- addAnonymousStream foldTransform
addDependency streamName expressionStreamName
where
foldTransform = DAG.Fold expression startExpression
expression = unExpression $ fn (Expression $ DAG.Input 0)
(Expression $ DAG.Input 1)
startExpression = unExpression startValue
flattenS :: Stream [a] -> Stream a
flattenS stream = Stream $ do
streamName <- unStream stream
expressionStreamName <- addAnonymousStream expression
addDependency streamName expressionStreamName
where
expression = DAG.Flatten $ DAG.Input 0
if_ :: Expression Bool -> Expression a -> Expression a -> Expression a
if_ condition trueExpression falseExpression =
Expression (DAG.If (unExpression condition)
(unExpression trueExpression)
(unExpression falseExpression))
greater :: Expression DAG.Word -> Expression DAG.Word -> Expression Bool
greater left right = Expression $ DAG.Greater (unExpression left) (unExpression right)
flipBit :: Expression DAG.Bit -> Expression DAG.Bit
flipBit = Expression . DAG.Not . unExpression
isEven :: Expression DAG.Word -> Expression Bool
isEven = Expression . DAG.Even . unExpression
boolToBit :: Expression Bool -> Expression DAG.Bit
boolToBit = Expression . DAG.BoolToBit . unExpression
isHigh :: Expression DAG.Bit -> Expression Bool
isHigh = Expression . DAG.IsHigh . unExpression
formatString :: String -> Expression [DAG.Byte]
formatString = Expression . DAG.ListConstant . map (DAG.ByteConstant . fromIntegral . ord)
formatNumber :: Expression DAG.Word -> Expression [DAG.Byte]
formatNumber = Expression . DAG.NumberToByteArray . unExpression
pack2 :: (Expression a1, Expression a2) -> Expression (a1, a2)
pack2 (a1, a2) = Expression $ DAG.TupleConstant $
[ unExpression a1
, unExpression a2
]
pack6 :: (Expression a1, Expression a2, Expression a3, Expression a4, Expression a5, Expression a6)
-> Expression (a1, a2, a3, a4, a5, a6)
pack6 (a1, a2, a3, a4, a5, a6) = Expression $ DAG.TupleConstant $
[ unExpression a1
, unExpression a2
, unExpression a3
, unExpression a4
, unExpression a5
, unExpression a6
]
unpack2 :: Expression (a1, a2) -> (Expression a1, Expression a2)
unpack2 expression =
( Expression $ DAG.TupleValue 0 (unExpression expression)
, Expression $ DAG.TupleValue 1 (unExpression expression)
)
unpack6 :: Expression (a1, a2, a3, a4, a5, a6)
-> (Expression a1, Expression a2, Expression a3, Expression a4, Expression a5, Expression a6)
unpack6 expression =
( Expression $ DAG.TupleValue 0 (unExpression expression)
, Expression $ DAG.TupleValue 1 (unExpression expression)
, Expression $ DAG.TupleValue 2 (unExpression expression)
, Expression $ DAG.TupleValue 3 (unExpression expression)
, Expression $ DAG.TupleValue 4 (unExpression expression)
, Expression $ DAG.TupleValue 5 (unExpression expression)
)
bitLow :: Expression DAG.Bit
bitLow = Expression $ DAG.BitConstant DAG.Low
bitHigh :: Expression DAG.Bit
bitHigh = Expression $ DAG.BitConstant DAG.High
addAnonymousStream :: DAG.Body -> Action DAG.Identifier
addAnonymousStream body = do
name <- buildUniqIdentifier "stream"
addStream name body
buildUniqIdentifier :: String -> Action DAG.Identifier
buildUniqIdentifier baseName = do
dag <- get
let id = idCounter dag
modify inc
return $ baseName ++ "_" ++ show id
where
inc dag = dag { idCounter = idCounter dag + 1 }
addStream :: DAG.Identifier -> DAG.Body -> Action DAG.Identifier
addStream name body = do
streamTreeState <- get
unless (DAG.hasStream (dag streamTreeState) name) $ do
modify $ insertStream $ DAG.Stream name [] body []
return name
where
insertStream :: DAG.Stream -> DAGState -> DAGState
insertStream stream x = x { dag = DAG.addStream (dag x) stream }
addDependency :: DAG.Identifier -> DAG.Identifier -> Action DAG.Identifier
addDependency source destination = do
modify (\x -> x { dag = DAG.addDependency source destination (dag x) })
return destination
createInput :: String -> LLI () -> LLI a -> Stream a
createInput name initLLI bodyLLI =
Stream $ addStream ("input_" ++ name) body
where
body = DAG.Driver (unLLI initLLI) (unLLI bodyLLI)
createOutput :: String -> LLI () -> (LLI a -> LLI ()) -> Output a
createOutput name initLLI bodyLLI = Output $ \stream -> do
streamName <- unStream stream
outputName <- addAnonymousStream $ DAG.Driver (unLLI initLLI) (unLLI (bodyLLI (LLI DAG.InputValue)))
addDependency streamName outputName
return ()
setBit :: String -> String -> LLI a -> LLI a
setBit register bit next = writeBit register bit (constBit DAG.High) next
clearBit :: String -> String -> LLI a -> LLI a
clearBit register bit next = writeBit register bit (constBit DAG.Low) next
writeByte :: String -> LLI DAG.Byte -> LLI a -> LLI a
writeByte register value next = LLI $ DAG.WriteByte register (unLLI value) (unLLI next)
writeWord :: String -> LLI DAG.Word -> LLI a -> LLI a
writeWord register value next = LLI $ DAG.WriteWord register (unLLI value) (unLLI next)
readBit :: String -> String -> LLI DAG.Bit
readBit register bit = LLI $ DAG.ReadBit register bit
readWord :: String -> LLI a -> LLI DAG.Word
readWord register next = LLI $ DAG.ReadWord register (unLLI next)
waitBitSet :: String -> String -> LLI a -> LLI a
waitBitSet register bit next = waitBit register bit DAG.High next
waitBit :: String -> String -> DAG.Bit -> LLI a -> LLI a
waitBit register bit value next = LLI $ DAG.WaitBit register bit value (unLLI next)
writeBit :: String -> String -> LLI a -> LLI b -> LLI b
writeBit register bit var next = LLI $ DAG.WriteBit register bit (unLLI var) (unLLI next)
byteConstant :: DAG.Byte -> LLI DAG.Byte
byteConstant = LLI . DAG.Const . show
wordConstant :: DAG.Word -> LLI DAG.Word
wordConstant = LLI . DAG.Const . show
constBit :: DAG.Bit -> LLI DAG.Bit
constBit = LLI . DAG.ConstBit
end :: LLI ()
end = LLI $ DAG.End