module Blockchain.JCommand ( JCommand(..), Storage(..), Word(..), JBool(..), jcompile ) where import Prelude hiding (LT, GT, EQ) import Control.Applicative import Control.Monad import Blockchain.Util import Blockchain.VM.Opcodes import Blockchain.VM.Code import Blockchain.ExtWord data Storage = PermStorage Word | MemStorage Word deriving (Show) data Word = Number Word256 | TheAddress | Origin | Caller | CallDataSize | Input Word | PermVal Word | MemVal Word | Abs Word | Word :+: Word | Word :-: Word | Word :*: Word | Neg Word | Signum Word deriving (Show) data JBool = JTrue | JFalse | Word :==: Word | Word :>: Word | Word :<: Word | Word :>=: Word | Word :<=: Word deriving (Show) instance Num Word where fromInteger x = Number $ fromInteger x Number x + Number y = Number $ x+y x + y = x :+: y x - y = x :-: y Number x * Number y = Number $ x*y x * y = x :*: y abs (Number x) = Number $ abs x abs x = Abs x negate (Number x) = Number (-x) negate x = Neg x signum (Number x) = Number (signum x) signum x = Signum x data JCommand = Storage :=: Word | If JBool [JCommand] | While JBool [JCommand] | ReturnCode Code deriving (Show) infixl 6 :+: infixl 5 :-: infixl 4 :=: j::[JCommand]->Unique [Operation] j x = fmap concat $ sequence $ jCommand2Op <$> x jcompile::[JCommand]->(Int, [Operation]) jcompile x = runUnique (j x) 0 data Unique a = Unique { runUnique::Int->(Int, a) } instance Functor Unique where fmap = liftM instance Applicative Unique where pure = return (<*>) = ap instance Monad Unique where (Unique runner) >>= f = Unique $ \val -> let (val', x') = runner val Unique g = f x' in g val' return x = Unique $ \val -> (val, x) getUnique::String->Unique String getUnique s = Unique $ \val -> (val+1, s ++ show val) pushVal::Word->[Operation] pushVal (Number x) = [PUSH $ integer2Bytes1 $ toInteger x] pushVal TheAddress = [ADDRESS] pushVal Caller = [CALLER] pushVal CallDataSize = [CALLDATASIZE] pushVal Origin = [ORIGIN] pushVal (Input x) = pushVal x ++ [CALLDATALOAD] pushVal (PermVal x) = pushVal x ++ [SLOAD] pushVal (MemVal x) = pushVal x ++ [MLOAD] pushVal (x :+: y) = pushVal y ++ pushVal x ++ [ADD] pushVal (x :-: y) = pushVal y ++ pushVal x ++ [SUB] pushVal (x :*: y) = pushVal y ++ pushVal x ++ [MUL] pushVal (Abs x) = pushVal x ++ pushVal (Signum x) ++ [MUL] pushVal (Signum x) = pushVal x ++ pushVal (Number 0) ++ [GT] ++ pushVal x ++ pushVal (Number 0) ++ [LT, SUB] pushVal (Neg x) = pushVal x ++ [NEG] pushBoolVal::JBool->[Operation] pushBoolVal (x :==: y) = pushVal y ++ pushVal x ++ [EQ] pushBoolVal (x :>: y) = pushVal y ++ pushVal x ++ [GT] pushBoolVal (x :<: y) = pushVal y ++ pushVal x ++ [LT] pushBoolVal (x :>=: y) = pushVal y ++ pushVal x ++ [ISZERO, LT] pushBoolVal (x :<=: y) = pushVal y ++ pushVal x ++ [ISZERO, GT] pushBoolVal JTrue = [PUSH [1]] pushBoolVal JFalse = [PUSH [0]] jCommand2Op::JCommand->Unique [Operation] jCommand2Op (PermStorage sPosition :=: val) = return $ pushVal val ++ pushVal sPosition ++ [SSTORE] jCommand2Op (MemStorage sPosition :=: val) = return $ pushVal val ++ pushVal sPosition ++ [MSTORE] jCommand2Op (If cond code) = do after <- getUnique "after" compiledCode <- j code return $ pushBoolVal cond ++ [ISZERO, PUSHLABEL after, JUMPI] ++ compiledCode ++ [LABEL after] jCommand2Op (While cond code) = do after <- getUnique "after" before <- getUnique "before" compiledCode <- j code return $ [LABEL before] ++ pushBoolVal cond ++ [ISZERO, PUSHLABEL after, JUMPI] ++ compiledCode ++ [PUSHLABEL before, JUMP] ++ [LABEL after] jCommand2Op (ReturnCode (Code codeBytes)) = do codeBegin <- getUnique "begin" codeEnd <- getUnique "end" return $ [ PUSHDIFF codeBegin codeEnd, PUSHLABEL codeBegin, PUSH [0], CODECOPY, PUSHDIFF codeBegin codeEnd, PUSH [0], RETURN ] ++ [LABEL codeBegin, DATA codeBytes, LABEL codeEnd]