-- Copyright (c) 2014 Contributors as noted in the AUTHORS file -- -- This file is part of frp-arduino. -- -- frp-arduino is free software: you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- (at your option) any later version. -- -- frp-arduino is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with frp-arduino. If not, see . module Arduino.DSL ( -- * Core Action , Stream , Output , LLI , compileProgram , def , (=:) , foo -- * Expressions , Expression -- ** Bits , DAG.Bit , bitHigh , bitLow , flipBit , isHigh , boolToBit -- ** Bytes , DAG.Byte -- ** Words , DAG.Word , isEven , greater -- ** Byte arrays , formatString , formatNumber -- ** Tuples , pack2 , pack6 , unpack2 , unpack6 , pack2Output , pack6Output -- ** Conditionals , if_ -- * Streams -- ** Mapping , mapS , mapSMany , mapS2 -- ** Filtering , filterS -- ** Folding , foldpS -- ** Flattering , flattenS -- ** Delaying , delay -- ** Syntactic sugar , (~>) -- * Low Level Instructions (LLI) -- | The glue between streams and harware. , 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 pack2Output :: Output a1 -> Output a2 -> Output (a1, a2) pack2Output 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) pack6Output :: Output a1 -> Output a2 -> Output a3 -> Output a4 -> Output a5 -> Output a6 -> Output (a1, a2, a3, a4, a5, a6) pack6Output 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 -- | Contrast with 'flattenS'. 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) -- | Needs a tuple created with 'pack2'. 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 -- | Contrast with 'mapSMany'. 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