tock-mirror/backends/GenerateCTest.hs
Neil Brown c8b724d2be Merged the latest set of changes from the trunk into the Polyplate branch
I also added the import list to all the Data.Generics imports in the tests (as I did for the other modules recently)
2009-04-10 20:38:29 +00:00

1267 lines
69 KiB
Haskell

{-
Tock: a compiler for parallel languages
Copyright (C) 2007 University of Kent
This program 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 2 of the License, or (at your
option) any later version.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
-}
-- #ignore-exports
-- | Tests for the C and C++ backends
--
-- The testing strategy is as follows. The way we have implemented the C and C++ backends is
-- to have a dictionary of functions 'GenerateC.GenOps' that is used for (mutually) recursive
-- calls. We can take advantage of this during testing. For example, we have a test that
-- tests genArraySubscript directly. When we test genVariableChecked, we don't want to have
-- to effectively check parts of genArraySubscript again. So we can \"override\" the
-- genArraySubscript to return a dummy value, and then we are effectively testing
-- that genVariableChecked calls genArraySubscript at the appropriate point. This is similar
-- to a testing technique in OOP where one might take a class and override some methods to
-- do a similar trick.
module GenerateCTest (tests) where
import Control.Monad.Error
import Control.Monad.State
import Control.Monad.Reader
import Control.Monad.Writer hiding (tell)
import Data.Generics (Data)
import Data.List (isInfixOf, intersperse)
import Data.Maybe (fromMaybe)
import Test.HUnit hiding (State)
import Text.Regex
import qualified AST as A
import CompState
import Errors
import GenerateC
import GenerateCBased
import GenerateCPPCSP
import Metadata
import Pass
import TestUtils
import TypeSizes
import Utils
-- | A few helper functions for writing certain characters (that won't appear in our generated C/C++ source)
-- to the WriterT monad. Useful as simple placeholders/special values during testers.
at :: CGen ()
at = tell ["@"]
dollar :: CGen ()
dollar = tell ["$"]
caret :: CGen ()
caret = tell ["^"]
hash :: CGen ()
hash = tell ["#"]
backq :: CGen ()
backq = tell ["`"]
-- | A few easy helpers for name variables for testing.
foo :: A.Name
foo = simpleName "foo"
bar:: A.Name
bar = simpleName "bar"
bar2 :: A.Name
bar2 = simpleName "bar2"
-- | Asserts that the given output of a CGen pass matches the expected value.
assertGen :: String -> String -> IO (Either Errors.ErrorReport [String]) -> Assertion
assertGen n exp act
= do r <- act
case r of
Left (_,err) -> assertFailure $ n ++ " pass failed, error: " ++ err
Right ss -> assertEqual n exp (subRegex (mkRegex "/\\*\\*/") (concat ss) "")
-- | Asserts that the given output of a CGen pass matches the expected regex, and returns the matched groups.
assertGenR :: String -> String -> IO (Either Errors.ErrorReport [String]) -> IO [String]
assertGenR n exp act
= do r <- act
case r of
Left (_,err) -> (assertFailure $ n ++ " pass failed, error: " ++ err) >> return []
Right ss ->
case matchRegex (mkRegex exp) (subRegex (mkRegex "/\\*\\*/") (concat ss) "") of
Just matches -> return matches
Nothing -> (assertFailure $ n ++ " regex match failed, regex: \"" ++ exp ++ "\" text: " ++ (concat ss)) >> return []
-- | Asserts that the given output of a CGen pass is a failure
assertGenFail :: String -> IO (Either Errors.ErrorReport [String]) -> Assertion
assertGenFail n act
= do r <- act
case r of
Left _ -> return ()
Right ss -> if isInfixOf "#error" (concat ss)
then return ()
else assertFailure $ n ++ " pass succeeded when expected to fail, output: " ++ (subRegex (mkRegex "/\\*\\*/") (concat ss) "")
evalCGen :: CGen () -> GenOps -> CompState -> IO (Either Errors.ErrorReport [String])
evalCGen act ops state = evalCGen' (runReaderT act ops) state
evalCGen' :: CGen' () -> CompState -> IO (Either Errors.ErrorReport [String])
evalCGen' act state = runPassM state pass >>* fst
where
pass = execStateT act (CGenOutputs (Left []) (Left []))
>>* (\(CGenOutputs (Left x) _) -> x)
-- | Checks that running the test for the C and C++ backends produces the right output for each.
testBothS ::
String -- ^ Test Name
-> String -- ^ C expected
-> String -- ^ C++ expected
-> CGen () -- ^ Actual
-> (State CompState ()) -- ^ State transformation
-> Test
testBothS testName expC expCPP act startState = TestList
[TestCase $ assertGen (testName ++ "/C") expC $ evalCGen act cgenOps state
,TestCase $ assertGen (testName ++ "/C++") expCPP $ evalCGen act cppgenOps state]
where
state = execState startState emptyState
-- | Checks that both the C and C++ backends fail on the given input.
testBothFailS :: String -> CGen () -> (State CompState ()) -> Test
testBothFailS testName act startState = TestList
[TestCase $ assertGenFail (testName ++ "/C") (evalCGen act cgenOps state)
,TestCase $ assertGenFail (testName ++ "/C++") (evalCGen act cppgenOps state) ]
where
state = execState startState emptyState
-- | Checks that the given output of a backend satisfies the given regex, and returns the matched groups.
testRS :: String -> String -> CGen' () -> State CompState () -> IO [String]
testRS testName exp act startState = assertGenR testName exp (evalCGen' act state)
where
state = execState startState emptyState
-- | Like testBothS, but with the output of the C and C++ backends the same.
testBothSameS ::
String -- ^ Test Name
-> String -- ^ C and C++ expected
-> CGen () -- ^ Actual
-> (State CompState ()) -- ^ State transformation
-> Test
testBothSameS n e a s = testBothS n e e a s
-- | Checks that the output of the test matches the given regexes for C and C++
testBothR ::
String -- ^ Test Name
-> String -- ^ C expected
-> String -- ^ C++ expected
-> CGen () -- ^ Actual
-> Test
testBothR n eC eCPP a = TestList
[TestCase $ (testRS n eC (runReaderT a cgenOps) (return ())) >> return ()
,TestCase $ (testRS n eCPP (runReaderT a cppgenOps) (return ())) >> (return ())]
-- | Like testBothR, but where the output of the C and C++ passes is expected to be the same.
testBothSameR :: String -> String -> CGen () -> Test
testBothSameR n e a = testBothR n e e a
-- | Like testBothFailS, but with the default beginning state.
testBothFail :: String -> CGen () -> Test
testBothFail a b = testBothFailS a b (return ())
-- | Like testBothS, but with the default beginning state.
testBoth :: String -> String -> String -> CGen () -> Test
testBoth a b c d = testBothS a b c d (return ())
-- | Like testBothSameS, but with the default beginning state.
testBothSame :: String -> String -> CGen () -> Test
testBothSame a b c = testBothSameS a b c (return ())
-- | These functions are here for a historical reason, and are all defined
-- to be call.
tcall, tcall2, tcall3, tcall4, tcall5 :: CGenCall a => (GenOps -> a) -> a
tcall = call
tcall2 = call
tcall3 = call
tcall4 = call
tcall5 = call
type Override = CGen () -> CGen ()
-- | Overrides a specified function in GenOps to return the given value
override1 ::
b -- ^ The value to return for the overridden function
-> (a -> b) -- ^ The resulting overriden function
override1 val = (\_ -> val)
override2 :: b -> (a0 -> a1 -> b)
override2 val = (\_ _ -> val)
override3 :: b -> (a0 -> a1 -> a2 -> b)
override3 val = (\_ _ _ -> val)
override4 :: b -> (a0 -> a1 -> a2 -> a3 -> b)
override4 val = (\_ _ _ _ -> val)
override5 :: b -> (a0 -> a1 -> a2 -> a3 -> a4 -> b)
override5 val = (\_ _ _ _ _ -> val)
testGenType :: Test
testGenType = TestList
[
testBothSame "GenType 0" "uint8_t" (gt A.Byte)
,testBothSame "GenType 1" "uint16_t" (gt A.UInt16)
,testBothSame "GenType 2" "uint32_t" (gt A.UInt32)
,testBothSame "GenType 3" "uint64_t" (gt A.UInt64)
,testBothSame "GenType 4" "int8_t" (gt A.Int8)
,testBothSame "GenType 5" "int16_t" (gt A.Int16)
,testBothSame "GenType 6" "int32_t" (gt A.Int32)
,testBothSame "GenType 7" "int64_t" (gt A.Int64)
,testBoth "GenType 8"
(case cIntSize of
2 -> "int16_t"
4 -> "int32_t"
8 -> "int64_t")
(case cxxIntSize of
2 -> "int16_t"
4 -> "int32_t"
8 -> "int64_t")
(gt A.Int)
,testBothSame "GenType 9" "bool" (gt A.Bool)
,testBothSame "GenType 10" "float" (gt A.Real32)
,testBothSame "GenType 11" "double" (gt A.Real64)
,testBothSame "GenType 20" "uint8_t*" (gt $ A.Mobile A.Byte)
,testBothSame "GenType 21" "bool*" (gt $ A.Mobile A.Bool)
,testBothSame "GenType 22" "float*" (gt $ A.Mobile A.Real32)
,testBothSame "GenType 100" "int32_t*" (gt $ A.Array [dimension 5] A.Int32)
,testBothSame "GenType 101" "int32_t*" (gt $ A.Array [dimension 5, dimension 2, dimension 9] A.Int32)
,testBothSame "GenType 102" "int32_t*" (gt $ A.Array [dimension 5, A.UnknownDimension] A.Int32)
,testBothSame "GenType 103" "foo" (gt $ A.Record (simpleName "foo"))
,testBoth "GenType 200" "Time" "csp::Time" (gt A.Time)
,testBoth "GenType 201" "Time" "csp::Time" (gt $ A.Timer A.OccamTimer)
,testBothSame "GenType 250" "mt_array_t*" (gt $ A.Mobile $ A.Array [dimension 5, dimension 2, dimension 9] A.Int32)
,testBothSame "GenType 251" "mt_array_t*" (gt $ A.Mobile $ A.Array [dimension 5, A.UnknownDimension] A.Int32)
,testBothSame "GenType 251" "mt_array_t*" (gt $ A.Mobile $ A.Array [A.UnknownDimension] A.Int32)
,testBothSame "GenType 252" "foo*" (gt $ A.Mobile $ A.Record (simpleName "foo"))
,testBoth "GenType 253" "Time*" "csp::Time*" (gt $ A.Mobile A.Time)
,testBoth "GenType 300" "Channel" "csp::One2OneChannel<int32_t>" (gt $ A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Int32)
,testBoth "GenType 301" "mt_cb_t*" "csp::One2AnyChannel<int32_t>" (gt $ A.Chan (A.ChanAttributes A.Unshared A.Shared) A.Int32)
,testBoth "GenType 302" "mt_cb_t*" "csp::Any2OneChannel<int32_t>" (gt $ A.Chan (A.ChanAttributes A.Shared A.Unshared) A.Int32)
,testBoth "GenType 303" "mt_cb_t*" "csp::Any2AnyChannel<int32_t>" (gt $ A.Chan (A.ChanAttributes A.Shared A.Shared) A.Int32)
,testBoth "GenType 310" "Channel" "csp::One2OneChannel<int32_t*>" (gt $ A.Chan (A.ChanAttributes A.Unshared A.Unshared) (A.Mobile A.Int32))
,testBoth "GenType 400" "Channel*" "csp::AltChanin<int32_t>" (gt $ A.ChanEnd A.DirInput A.Unshared A.Int32)
,testBoth "GenType 401" "Channel*" "csp::AltChanin<int32_t>" (gt $ A.ChanEnd A.DirInput A.Shared A.Int32)
,testBoth "GenType 402" "Channel*" "csp::Chanout<int32_t>" (gt $ A.ChanEnd A.DirOutput A.Unshared A.Int32)
,testBoth "GenType 403" "Channel*" "csp::Chanout<int32_t>" (gt $ A.ChanEnd A.DirOutput A.Shared A.Int32)
--ANY and protocols cannot occur outside channels in C++ or C, they are tested here:
,testBothFail "GenType 500" (gt $ A.Any)
,testBothFail "GenType 600" (gt $ A.UserProtocol (simpleName "foo"))
,testBothFail "GenType 650" (gt $ A.Counted A.Int32 A.Int32)
,testBoth "GenType 700" "Channel**" "csp::One2OneChannel<int32_t>**" (gt $ A.Array [dimension 5] $ A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Int32)
,testBoth "GenType 701" "Channel**" "csp::AltChanin<int32_t>*" (gt $ A.Array [dimension 5] $ A.ChanEnd A.DirInput A.Unshared A.Int32)
--Test types that can only occur inside channels:
--ANY:
,testBoth "GenType 800" "Channel" "csp::One2OneChannel<tockSendableArrayOfBytes>" (gt $ A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Any)
--Protocol:
,testBoth "GenType 900" "Channel" "csp::One2OneChannel<tockSendableArrayOfBytes>" (gt $ A.Chan (A.ChanAttributes A.Unshared A.Unshared) $ A.UserProtocol (simpleName "foo"))
--Counted:
,testBoth "GenType 1000" "Channel" "csp::One2OneChannel<tockSendableArrayOfBytes>" (gt $ A.Chan (A.ChanAttributes A.Unshared A.Unshared) $ A.Counted A.Int32 A.Int32)
--Channels of arrays are special in C++:
,testBoth "GenType 1100" "Channel" "csp::One2OneChannel<tockSendableArray<int32_t,6>>"
(gt $ A.Chan (A.ChanAttributes A.Unshared A.Unshared) $ A.Array [dimension 6] A.Int32)
,testBoth "GenType 1101" "Channel" "csp::One2OneChannel<tockSendableArray<int32_t,6*7*8>>"
(gt $ A.Chan (A.ChanAttributes A.Unshared A.Unshared) $ A.Array [dimension 6,dimension 7,dimension 8] A.Int32)
-- List types:
,testBothS "GenType 2000" "GQueue*" "tockList<int16_t>" (gt $ A.List A.Int16) markRainTest
,testBothS "GenType 2001" "GQueue*" "tockList<tockList<int16_t>>" (gt $ A.List $ A.List A.Int16) markRainTest
,testBothS "GenType 2010" "GQueue**" "tockList<int16_t>" (gt $ A.Mobile $ A.List A.Int16) markRainTest
,testBothS "GenType 2011" "GQueue**" "tockList<tockList<int16_t>>" (gt $ A.Mobile $ A.List $ A.List A.Int16) markRainTest
,testBothS "GenType 2012" "GQueue**" "tockList<tockList<int16_t>>" (gt $ A.Mobile $ A.List $ A.Mobile $ A.List A.Int16) markRainTest
]
where
gt t = genType t
testStop :: Test
testStop =
testBoth "Stop" "occam_stop(\"foo:4:9\",1,\"bar\");" "throw StopException(\"foo:4:9\" \"bar\");" (tcall2 genStop (Meta (Just "foo") 4 9) "bar")
testArraySizes :: Test
testArraySizes = TestList
[
testBothSame "genArrayLiteralElems 0" "$" $ unfolded (tcall genArrayLiteralElems $
A.Only emptyMeta undefined)
,testBothSame "genArrayLiteralElems 1" "$,$,$" $ unfolded (tcall genArrayLiteralElems $
A.Several emptyMeta [A.Only emptyMeta undefined, A.Only emptyMeta undefined, A.Only emptyMeta undefined])
,testBothSame "genArrayLiteralElems 2" "$,$,$" $ unfolded (tcall genArrayLiteralElems $
A.Several emptyMeta [A.Only emptyMeta undefined, A.Several emptyMeta [A.Only emptyMeta undefined, A.Only emptyMeta undefined]])
]
where
unfolded :: Override
unfolded = local (\ops -> ops {genUnfoldedExpression = override1 dollar})
testActuals :: Test
testActuals = TestList
[
-- C adds a prefix comma (to follow Process* me) but C++ does not:
testBoth "genActuals 0" "@,@" "@,@" $ overActual (tcall genActuals [undefined, undefined] [undefined, undefined])
,testBothSame "genActuals 1" "" $ (tcall genActuals [] [])
--For expressions, genExpression should be called:
,testBothSame "genActual 0" "$" $ over (tcall genActual valFormal $ A.ActualExpression (A.True undefined))
,testBothSame "genActual 1" "$" $ over (tcall genActual valFormal $ A.ActualExpression (A.Literal undefined undefined undefined))
--The abbreviation mode used when generating an actual should come from the
--corresponding formal, not from the variable:
,testBothSameS "genActual 10" "@" (over (tcall genActual valFormal $ A.ActualVariable (A.Variable undefined foo)))
(defineVariable "foo" A.Int)
,testBothSameS "genActual 11" "&@" (over (tcall genActual refFormal $ A.ActualVariable (A.Variable undefined foo)))
(defineVariable "foo" A.Int)
,testBothSameS "genActual 12" "@" (over (tcall genActual valFormal $ A.ActualVariable (A.Variable undefined foo)))
(do defineVariable "bar" A.Int
defineIs "foo" A.Int (variable "bar"))
,testBothSameS "genActual 13" "&@" (over (tcall genActual refFormal $ A.ActualVariable (A.Variable undefined foo)))
(do defineVariable "bar" A.Int
defineIs "foo" A.Int (variable "bar"))
]
where
valFormal :: A.Formal
valFormal = A.Formal A.ValAbbrev undefined undefined
refFormal :: A.Formal
refFormal = A.Formal A.Abbrev undefined undefined
overActual :: Override
overActual = local (\ops -> ops {genActual = override2 at})
over :: Override
over = local (\ops -> ops {genVariable' = override3 at, genExpression = override1 dollar})
-- TODO test the other two array checking methods
testArraySubscript :: Test
testArraySubscript = TestList
[
testBothSameS "genArraySubscript 0" "[5*8*9]"
(tcall3 genArraySubscript A.NoCheck (A.Variable emptyMeta foo) [lit 5]) stateTrans
,testBothSameS "genArraySubscript 1" "[5*8*9+6*9]"
(tcall3 genArraySubscript A.NoCheck (A.Variable emptyMeta foo) [lit 5, lit 6]) stateTrans
,testBothSameS "genArraySubscript 2" "[5*8*9+6*9+7]"
(tcall3 genArraySubscript A.NoCheck (A.Variable emptyMeta foo) [lit 5, lit 6, lit 7]) stateTrans
,testBothSameS "genArraySubscript 3" ("[occam_check_index(5,7," ++ m ++ ")*8*9]")
(tcall3 genArraySubscript A.CheckBoth (A.Variable emptyMeta foo) [lit 5]) stateTrans
,testBothSameS "genArraySubscript 4"
("[occam_check_index(5,7," ++ m ++ ")*8*9+occam_check_index(6,8," ++ m ++ ")*9]")
(tcall3 genArraySubscript A.CheckBoth (A.Variable emptyMeta foo) [lit 5, lit 6]) stateTrans
,testBothSameS "genArraySubscript 5"
("[occam_check_index(5,7," ++ m ++ ")*8*9+occam_check_index(6,8," ++ m ++ ")*9+occam_check_index(7,9," ++ m ++ ")]")
(tcall3 genArraySubscript A.CheckBoth (A.Variable emptyMeta foo) [lit 5, lit 6, lit 7]) stateTrans
]
where
stateTrans :: CSM m => m ()
stateTrans = defineName (simpleName "foo") $ simpleDefDecl "foo" (A.Array [dimension 7,dimension 8,dimension 9] A.Int)
m = "\"" ++ show emptyMeta ++ "\""
lit :: Int -> (Meta, CGen ())
lit n = (emptyMeta, tell [show n])
testArraySlice :: Test
testArraySlice = TestList
[
-- Slice from a one-dimensional array:
testSlice 0 ("(&arr[" ++ checkSlice "4" "5" "12" ++ "])") "arr" 4 5 [dimension 12]
-- Slice from a two-dimensional array:
,testSlice 1 ("(&arr[" ++ checkSlice "4" "5" "12" ++ "*arr_sizes[1]])") "arr" 4 5 [dimension 12,dimension 12]
-- Slice from a three-dimensional array:
,testSlice 2 ("(&arr[" ++ checkSlice "4" "5" "12" ++ "*arr_sizes[1]*arr_sizes[2]])") "arr" 4 5 [dimension 12,dimension 12,dimension 12]
-- TODO test with unknown dimensions
]
where
testSlice :: Int -> String -> String -> Integer -> Integer -> [A.Dimension] -> Test
testSlice index exp nm start count ds
= testBothSameS ("genSlice " ++ show index) exp
(tcall genVariable
(A.SubscriptedVariable emptyMeta (A.SubscriptFromFor emptyMeta A.CheckBoth (intLiteral start) (intLiteral count)) (variable nm))
A.Original
)
(defineName (simpleName nm) $ simpleDefDecl nm (A.Array ds A.Int))
m = "\"" ++ show emptyMeta ++ "\""
checkSlice s e sub = "occam_check_slice(" ++ s ++ "," ++ e ++ "," ++ sub ++ "," ++ m ++ ")"
-- TODO fix this test so that it tests fixed dimensions properly
testOverArray :: Test
testOverArray = TestList $ map testOverArray'
[(cSize,cIndex,"", cgenOps)
,(cSize,cIndex,"", cppgenOps)
]
where
cSize n = "_sizes\\[" ++ show n ++ "\\]"
cIndex x = "\\[" ++ concat (intersperse "\\+" $ map cIndex' x) ++ "\\]"
cIndex' :: (String,[Int]) -> String
cIndex' (s,ns) = s ++ concat (map (\n -> "\\*foo" ++ cSize n) ns)
testOverArray' :: ((Int -> String),[(String,[Int])] -> String,String, GenOps) -> Test
testOverArray' (sz,f',suff,ops) = TestCase $
do testRS "testOverArray'" rx1Static (flip runReaderT ops $ tcall3 genOverArray emptyMeta (A.Variable emptyMeta foo) func) state1Static
testRS "testOverArray'" rx1Dynamic (flip runReaderT ops $ tcall3 genOverArray emptyMeta (A.Variable emptyMeta foo) func) state1Dynamic
testRS "testOverArray'" rx3Static (flip runReaderT ops $ tcall3 genOverArray emptyMeta (A.Variable emptyMeta foo) func) state3Static
testRS "testOverArray'" rx3Dynamic (flip runReaderT ops $ tcall3 genOverArray emptyMeta (A.Variable emptyMeta foo) func) state3Dynamic
return ()
where
func f = Just $ call genVariableUnchecked (f $ A.Variable emptyMeta foo) A.Original >> tell [";"]
rx1Static = "^for\\(int ([[:alnum:]_]+)=0;\\1<7;\\1\\+\\+)\\{foo\\[\\1\\]" ++ suff ++ ";\\}$"
rx1Dynamic = "^for\\(int ([[:alnum:]_]+)=0;\\1<foo" ++ sz 0 ++ ";\\1\\+\\+)\\{foo\\[\\1\\]" ++ suff ++ ";\\}$"
rx3Static
= "^for\\(int ([[:alnum:]_]+)=0;\\1<7;\\1\\+\\+)\\{" ++
"for\\(int ([[:alnum:]_]+)=0;\\2<8;\\2\\+\\+)\\{" ++
"for\\(int ([[:alnum:]_]+)=0;\\3<9;\\3\\+\\+)\\{" ++
"foo" ++ (f' [("\\1",[1,2]),("\\2",[2]),("\\3",[])]) ++ suff ++ ";\\}\\}\\}$"
rx3Dynamic
= "^for\\(int ([[:alnum:]_]+)=0;\\1<foo" ++ sz 0 ++ ";\\1\\+\\+)\\{" ++
"for\\(int ([[:alnum:]_]+)=0;\\2<8;\\2\\+\\+)\\{" ++
"for\\(int ([[:alnum:]_]+)=0;\\3<foo" ++ sz 2 ++ ";\\3\\+\\+)\\{" ++
"foo" ++ (f' [("\\1",[1,2]),("\\2",[2]),("\\3",[])]) ++ suff ++ ";\\}\\}\\}$"
state1Static :: CSM m => m ()
state1Static = defineName (simpleName "foo") $ simpleDefDecl "foo" (A.Array [dimension 7] A.Int)
state1Dynamic :: CSM m => m ()
state1Dynamic = defineName (simpleName "foo") $ simpleDefDecl "foo" (A.Array [A.UnknownDimension] A.Int)
state3Static :: CSM m => m ()
state3Static = defineName (simpleName "foo") $ simpleDefDecl "foo" (A.Array [dimension 7, dimension 8, dimension 9] A.Int)
state3Dynamic :: CSM m => m ()
state3Dynamic = defineName (simpleName "foo") $ simpleDefDecl "foo" (A.Array [A.UnknownDimension, dimension 8, A.UnknownDimension] A.Int)
testReplicator :: Test
testReplicator = TestList
[
testBothSameR "testReplicator 1" "for\\(int ([[:alnum:]_]+)=10,foo=1;\\1>0;\\1--,foo\\+=3\\)\\{" (tcall2 genReplicatorStart
foo (A.For emptyMeta (intLiteral 1) (intLiteral 10) (intLiteral 3)))
]
testDeclaration :: Test
testDeclaration = TestList
[
--Simple:
testBothSame "genDeclaration 0" "int32_t foo;" (tcall3 genDeclaration NotTopLevel A.Int32 foo False)
--Channels and channel-ends:
,testBoth "genDeclaration 1" "Channel foo;" "csp::One2OneChannel<int32_t> foo;" (tcall3 genDeclaration NotTopLevel (A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Int32) foo False)
,testBoth "genDeclaration 2" "Channel foo;" "csp::Any2OneChannel<int32_t> foo;" (tcall3 genDeclaration NotTopLevel (A.Chan (A.ChanAttributes A.Shared A.Unshared) A.Int32) foo False)
,testBoth "genDeclaration 3" "Channel foo;" "csp::One2AnyChannel<int32_t> foo;" (tcall3 genDeclaration NotTopLevel (A.Chan (A.ChanAttributes A.Unshared A.Shared) A.Int32) foo False)
,testBoth "genDeclaration 4" "mt_cb_t* foo;" "csp::Any2AnyChannel<int32_t> foo;" (tcall3 genDeclaration NotTopLevel (A.Chan (A.ChanAttributes A.Shared A.Shared) A.Int32) foo False)
,testBoth "genDeclaration 5" "Channel* foo;" "csp::AltChanin<int32_t> foo;" (tcall3 genDeclaration NotTopLevel (A.ChanEnd A.DirInput A.Unshared A.Int32) foo False)
,testBoth "genDeclaration 6" "mt_cb_t* foo;" "csp::AltChanin<int32_t> foo;" (tcall3 genDeclaration NotTopLevel (A.ChanEnd A.DirInput A.Shared A.Int32) foo False)
,testBoth "genDeclaration 7" "Channel* foo;" "csp::Chanout<int32_t> foo;" (tcall3 genDeclaration NotTopLevel (A.ChanEnd A.DirOutput A.Unshared A.Int32) foo False)
,testBoth "genDeclaration 8" "mt_cb_t* foo;" "csp::Chanout<int32_t> foo;" (tcall3 genDeclaration NotTopLevel (A.ChanEnd A.DirOutput A.Shared A.Int32) foo False)
--Arrays (of simple):
,testBothSame "genDeclaration 100" "int32_t foo[8];"
(tcall3 genDeclaration NotTopLevel (A.Array [dimension 8] A.Int32) foo False)
,testBothSame "genDeclaration 101" "int32_t foo[8*9];"
(tcall3 genDeclaration NotTopLevel (A.Array [dimension 8,dimension 9] A.Int32) foo False)
,testBothSame "genDeclaration 102" "int32_t foo[8*9*10];"
(tcall3 genDeclaration NotTopLevel (A.Array [dimension 8,dimension 9,dimension 10] A.Int32) foo False)
--Arrays (of simple) inside records:
,testBothSame "genDeclaration 110" "int32_t foo[8];"
(tcall3 genDeclaration NotTopLevel (A.Array [dimension 8] A.Int32) foo True)
,testBothSame "genDeclaration 111" "int32_t foo[8*9];"
(tcall3 genDeclaration NotTopLevel (A.Array [dimension 8,dimension 9] A.Int32) foo True)
,testBothSame "genDeclaration 112" "int32_t foo[8*9*10];"
(tcall3 genDeclaration NotTopLevel (A.Array [dimension 8,dimension 9,dimension 10] A.Int32) foo True)
--Arrays of channels and channel-ends:
,testBoth "genDeclaration 200" "Channel foo_storage[8];Channel* foo[8];"
"csp::One2OneChannel<int32_t> foo_storage[8];csp::One2OneChannel<int32_t>* foo[8];"
(tcall3 genDeclaration NotTopLevel (A.Array [dimension 8] $ A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Int32) foo False)
,testBoth "genDeclaration 201" "Channel foo_storage[8*9];Channel* foo[8*9];"
"csp::One2OneChannel<int32_t> foo_storage[8*9];csp::One2OneChannel<int32_t>* foo[8*9];"
(tcall3 genDeclaration NotTopLevel (A.Array [dimension 8, dimension 9] $ A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Int32) foo False)
,testBoth "genDeclaration 202" "Channel* foo[8];"
"csp::AltChanin<int32_t> foo[8];"
(tcall3 genDeclaration NotTopLevel (A.Array [dimension 8] $ A.ChanEnd A.DirInput A.Unshared A.Int32) foo False)
,testBoth "genDeclaration 203" "Channel* foo[8*9];"
"csp::Chanout<int32_t> foo[8*9];"
(tcall3 genDeclaration NotTopLevel (A.Array [dimension 8, dimension 9] $ A.ChanEnd A.DirOutput A.Unshared A.Int32) foo False)
--Records of simple:
,testBothSameS "genDeclaration 300" "REC foo;" (tcall3 genDeclaration NotTopLevel (A.Record $ simpleName "REC") foo False) (stateR A.Int32)
--Records of arrays of int32_t (the sizes are set by declareInit):
,testBothSameS "genDeclaration 400" "REC foo;" (tcall3 genDeclaration NotTopLevel (A.Record $ simpleName "REC") foo False) (stateR $ A.Array [dimension 8] A.Int32)
--Timers:
,testBoth "genDeclaration 500" "Time foo;" "csp::Time foo;"
(tcall3 genDeclaration NotTopLevel (A.Timer A.OccamTimer) foo False)
,testBoth "genDeclaration 501" "Time foo[20];" "csp::Time foo[20];"
(tcall3 genDeclaration NotTopLevel (A.Array [dimension 20] (A.Timer A.OccamTimer)) foo False)
]
where
stateR t = defRecord "REC" "bar" t
testDeclareInitFree :: Test
testDeclareInitFree = TestLabel "testDeclareInitFree" $ TestList
[
-- Plain type:
testAllSame 0 ("","") A.Int
-- Channel types:
,testAll 1 ("ChanInit(wptr,&foo);","") ("","") $ A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Int
,testAllSame 2 ("","") $ A.ChanEnd A.DirInput A.Unshared A.Int
-- Plain arrays:
,testAllSame 3 ("","") $ A.Array [dimension 4] A.Int
-- Channel arrays:
,testAll 4 ("tock_init_chan_array(foo_storage,foo,4);^ChanInit(wptr,(foo)[0]);^","") ("tockInitChanArray(foo_storage,foo,4);","") $ A.Array [dimension 4] $ A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Int
,testAllSame 6 ("","") $ A.Array [dimension 4] $ A.ChanEnd A.DirInput A.Unshared A.Int
-- Plain records:
,testAllR 100 ("","") ("","") A.Int id
-- Records containing an array:
,testAllR 101 ("","") ("","") (A.Array [dimension 4,dimension 5] A.Int) id
-- Arrays of records containing an array:
,testAllRA 200 ("^^","") ("","") (A.Array [dimension 4,dimension 5] A.Int) id
-- Mobile versions
,testAllSame 1003 ("if(foo!=NULL){MTRelease(wptr,(void*)foo);foo=NULL;}","") $ A.Mobile $ A.Array [dimension 4] A.Int
,testAllSame 1004 ("if(foo!=NULL){MTRelease(wptr,(void*)foo);foo=NULL;}","") $ A.Mobile $ A.Array [dimension 4] $ A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Int
,testAllR 1100 ("","") ("","") A.Int A.Mobile
-- Records containing an array:
,testAllR 1101 ("","") ("","") (A.Array [dimension 4,dimension 5] A.Int) A.Mobile
-- Arrays of records containing an array:
,testAllRA 1200 ("","") ("","") (A.Array [dimension 4,dimension 5] A.Int) A.Mobile
]
where
testAll :: Int -> (String,String) -> (String,String) -> A.Type -> Test
testAll n eC eCPP t = testAll' n eC eCPP t (defineName (simpleName "foo") $ simpleDefDecl "foo" t)
testAllR :: Int -> (String,String) -> (String,String) -> A.Type -> (A.Type -> A.Type) -> Test
testAllR n eC eCPP t f = testAll' n eC eCPP (f $ A.Record $ simpleName "REC") ((defRecord "REC" "bar" t) >> (defineName (simpleName "foo") $ simpleDefDecl "foo" $ A.Record (simpleName "REC")))
testAllRA :: Int -> (String,String) -> (String,String) -> A.Type -> (A.Type -> A.Type) -> Test
testAllRA n eC eCPP t f = testAll' n eC eCPP (A.Array [dimension 5] $ f $ A.Record $ simpleName "REC") ((defRecord "REC" "bar" t) >> (defineName (simpleName "foo") $ simpleDefDecl "foo" $ A.Array [dimension 5] $ A.Record (simpleName "REC")))
testAll' :: Int -> (String,String) -> (String,String) -> A.Type -> State CompState () -> Test
testAll' n (iC,fC) (iCPP,fCPP) t state = TestList
[
testBothS ("testDeclareInitFree/a" ++ show n) ("@" ++ iC) ("@" ++ iCPP) (over (tcall introduceSpec NotTopLevel $ A.Specification emptyMeta foo (A.Declaration emptyMeta t))) state
,testBothS ("testDeclareInitFree/b" ++ show n) iC iCPP (over $ ask >>= \ops -> (fromMaybe (return ())) (declareInit ops emptyMeta t (A.Variable emptyMeta foo))) state
,testBothS ("testDeclareInitFree/c" ++ show n) fC fCPP (over (tcall removeSpec $ A.Specification emptyMeta foo (A.Declaration emptyMeta t))) state
,testBothS ("testDeclareInitFree/d" ++ show n) fC fCPP (over $ ask >>= \ops -> (fromMaybe (return ())) (declareFree ops emptyMeta t (A.Variable emptyMeta foo))) state
]
where
overArray _ v f = case f (\v -> A.SubscriptedVariable emptyMeta (A.Subscript emptyMeta A.NoCheck $ intLiteral 0) v) of
Just p -> caret >> p >> caret
Nothing -> return ()
over :: Override
over = local $ \ops -> ops {genDeclaration = override4 at, genOverArray = overArray}
testAllSame :: Int -> (String,String) -> A.Type -> Test
testAllSame n e t = testAll n e e t
testRecord :: Test
testRecord = TestList
[
--Record types:
testAllSame 400 ("typedef struct{#ATION_True}foo;","") foo
(A.RecordAttr False False) [(bar,A.Int)]
,testAllSame 401 ("typedef struct{#ATION_True#ATION_True} occam_struct_packed foo;","") foo
(A.RecordAttr True False) [(bar,A.Int),(bar,A.Int)]
,testAllSame 402 ("typedef struct{#ATION_True}foo;","") foo
(A.RecordAttr False False) [(bar,A.Array [dimension 6, dimension 7] A.Int)]
]
where
testAll :: Int -> (String,String) -> (String,String) -> A.Name -> A.RecordAttr -> [(A.Name, A.Type)] -> Test
testAll a b c0 c1 c2 d = testAllS a b c0 c1 c2 d (return ()) over
testAllS :: Int -> (String,String) -> (String,String) -> A.Name -> A.RecordAttr -> [(A.Name, A.Type)] -> State CompState () -> (GenOps -> GenOps) -> Test
testAllS n (eCI,eCR) (eCPPI,eCPPR) rn rb rts st overFunc
= testBothS ("testRecord " ++ show n) eCI eCPPI (local overFunc (tcall genRecordTypeSpec rn rb rts)) st
testAllSame n e s0 s1 s2 = testAll n e e s0 s1 s2
over ops = ops {genDeclaration = override3 (tell . (\x -> ["#ATION_",show x]))
,declareInit = (override3 (Just $ tell ["#INIT"])), declareFree = override3 (Just $ tell ["#FREE"])
,getCType = (\_ x _ -> return $ Plain $ "$(" ++ show x ++ ")")
,genVariable' = override3 at
}
testSpec :: Test
testSpec = TestList
[
--Declaration:
testAllSame 0 ("#ATION_False#INIT","#FREE") $ A.Declaration emptyMeta A.Int
,testAllSame 1 ("#ATION_False#INIT","#FREE") $ A.Declaration emptyMeta (A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Int)
,testAllSame 2 ("#ATION_False#INIT","#FREE") $ A.Declaration emptyMeta (A.Array [dimension 3] A.Int)
,testAllSame 3 ("#ATION_False#INIT","#FREE") $ A.Declaration emptyMeta (A.Array [dimension 3] $ A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Int)
-- TODO test declarations with initialisers
--Empty/failure cases:
,testAllSame 100 ("","") $ A.DataType undefined undefined
,testBothFail "testAllSame 200" (tcall introduceSpec NotTopLevel $ A.Specification emptyMeta foo $ A.RetypesExpr emptyMeta A.Original A.Int (A.True emptyMeta))
,testBothFail "testAllSame 300" (tcall introduceSpec NotTopLevel $ A.Specification emptyMeta foo $ A.Place emptyMeta (A.True emptyMeta))
,testAllSame 350 ("","") $ A.Protocol emptyMeta undefined
--IsChannelArray:
,testAllSame 500
("$(" ++ show chanInt ++ ")*foo[]={@,@};","")
$ A.Is emptyMeta A.Abbrev (A.Array [dimension 2] chanInt)
$ A.ActualChannelArray [A.Variable undefined undefined,A.Variable undefined undefined]
--Is:
-- Plain types require you to take an address to get the pointer:
,testAllSameForTypes 600 (\t -> ("$(" ++ show t ++ ")* const foo=&bar;",""))
(\t -> A.Is emptyMeta A.Abbrev t $ A.ActualVariable (variable "bar")) [A.Int,A.Time]
,testAllSameForTypes 610 (\t -> ("$(" ++ show t ++ ")* const foo=(&bar);",""))
(\t -> A.Is emptyMeta A.Abbrev t $ A.ActualVariable (variable "bar")) [chanInt,A.Record foo]
--Abbreviations of channel-ends in C++ should just copy the channel-end, rather than trying to take the address of the temporary returned by writer()/reader()
--C abbreviations will be of type Channel*, so they can just copy the channel address.
,testAllForTypes 620 (\t -> ("$(" ++ show t ++ ") foo=bar;","")) (\t -> ("$(" ++ show t ++ ") foo=bar;",""))
(\t -> A.Is emptyMeta A.Abbrev t $ A.ActualVariable (variable "bar")) [chanIntIn,chanIntOut]
,testAllSameForTypes 700 (\t -> ("$(" ++ show t ++ ") const foo=bar;",""))
(\t -> A.Is emptyMeta A.ValAbbrev t $ A.ActualVariable (variable "bar")) [A.Int,A.Time]
,testAllSameForTypes 710 (\t -> ("$(" ++ show t ++ ") const* const foo=(&bar);",""))
(\t -> A.Is emptyMeta A.ValAbbrev t $ A.ActualVariable (variable "bar")) [A.Record foo]
-- I don't think ValAbbrev of channels/channel-ends makes much sense (occam doesn't support it, certainly) so they are not tested here.
--TODO test Is more (involving subscripts, arrays and slices)
--ProtocolCase:
,testAllSame 800 ("typedef enum{empty_protocol_foo}foo;","") $ A.ProtocolCase emptyMeta []
,testAllSame 801 ("typedef enum{bar_foo}foo;","") $ A.ProtocolCase emptyMeta [(bar,[])]
,testAllSame 802 ("typedef enum{bar_foo,wibble_foo}foo;","") $ A.ProtocolCase emptyMeta [(bar,[]),(simpleName "wibble",[])]
--Retypes:
-- Normal abbreviation:
,testAllSameS 900 ("int32_t* const foo=(int32_t* const)&y;@","") (A.Retypes emptyMeta A.Abbrev A.Int32 (variable "y"))
(defineName (simpleName "y") (simpleDefDecl "y" A.Real32)) (\ops -> ops {genRetypeSizes = override5 at})
-- Val abbreviation:
,testAllSameS 901 ("int32_t const foo=*(int32_t const*)&y;@","") (A.Retypes emptyMeta A.ValAbbrev A.Int32 (variable "y"))
(defineName (simpleName "y") (simpleDefDecl "y" A.Real32)) (\ops -> ops {genRetypeSizes = override5 at})
--Abbreviations of records as records:
,testAllSameS 910 ("bar* const foo=(bar* const)(&y);@","") (A.Retypes emptyMeta A.Abbrev (A.Record bar) (variable "y"))
(defineName (simpleName "y") (simpleDefDecl "y" (A.Record bar2))) (\ops -> ops {genRetypeSizes = override5 at})
-- Val abbreviation of records as records:
,testAllSameS 911 ("const bar* const foo=(const bar* const)(&y);@","") (A.Retypes emptyMeta A.ValAbbrev (A.Record bar) (variable "y"))
(defineName (simpleName "y") (simpleDefDecl "y" (A.Record bar2))) (\ops -> ops {genRetypeSizes = override5 at})
-- Channel retyping doesn't require size checking:
,testAllS 1000 ("Channel* const foo=(Channel* const)(&y);","") ("csp::One2OneChannel<tockSendableArrayOfBytes>* const foo=(csp::One2OneChannel<tockSendableArrayOfBytes>* const)(&y);","")
(A.Retypes emptyMeta A.Abbrev (A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Any) (variable "y"))
(defineName (simpleName "y") (simpleDefDecl "y" (A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Any))) id
-- Plain-to-array retyping:
-- single (unknown) dimension:
,testAllSameS 1100 ("uint8_t* foo=(uint8_t*)&y;@","")
(A.Retypes emptyMeta A.Abbrev (A.Array [A.UnknownDimension] A.Byte) (variable "y"))
(defineName (simpleName "y") (simpleDefDecl "y" A.Int32)) (\ops -> ops {genRetypeSizes = override5 at})
-- single (known) dimension:
,testAllSameS 1101 ("uint8_t* foo=(uint8_t*)&y;@","")
(A.Retypes emptyMeta A.Abbrev (A.Array [dimension 4] A.Byte) (variable "y"))
(defineName (simpleName "y") (simpleDefDecl "y" A.Int32)) (\ops -> ops {genRetypeSizes = override5 at})
-- single (unknown) dimension, VAL:
,testAllSameS 1102 ("uint8_t const* foo=(uint8_t const*)&y;@","")
(A.Retypes emptyMeta A.ValAbbrev (A.Array [A.UnknownDimension] A.Byte) (variable "y"))
(defineName (simpleName "y") (simpleDefDecl "y" A.Int32)) (\ops -> ops {genRetypeSizes = override5 at})
-- single (known) dimension, VAL:
,testAllSameS 1103 ("uint8_t const* foo=(uint8_t const*)&y;@","")
(A.Retypes emptyMeta A.ValAbbrev (A.Array [dimension 4] A.Byte) (variable "y"))
(defineName (simpleName "y") (simpleDefDecl "y" A.Int32)) (\ops -> ops {genRetypeSizes = override5 at})
-- TODO test multiple dimensions plain-to-array (mainly for C++)
-- Array-to-plain retyping:
,testAllSameS 1200 ("int32_t* const foo=(int32_t* const)y;@","")
(A.Retypes emptyMeta A.Abbrev A.Int32 (variable "y"))
(defineName (simpleName "y") (simpleDefDecl "y" (A.Array [A.UnknownDimension] A.Byte))) (\ops -> ops {genRetypeSizes = override5 at})
,testAllSameS 1201 ("int32_t const foo=*(int32_t const*)y;@","")
(A.Retypes emptyMeta A.ValAbbrev A.Int32 (variable "y"))
(defineName (simpleName "y") (simpleDefDecl "y" (A.Array [A.UnknownDimension] A.Byte))) (\ops -> ops {genRetypeSizes = override5 at})
--TODO test array-to-array retyping
--TODO IsExpr
--TODO Proc
]
where
testAllSameForTypes :: Int -> (A.Type -> (String, String)) -> (A.Type -> A.SpecType) -> [A.Type] -> Test
testAllSameForTypes n te spec ts = testAllForTypes n te te spec ts
testAllForTypes :: Int -> (A.Type -> (String, String)) -> (A.Type -> (String, String)) -> (A.Type -> A.SpecType) -> [A.Type] -> Test
testAllForTypes n teC teCPP spec ts = TestList [testAllS (n+i) (teC t) (teCPP t) (spec t) (defineName (simpleName "bar") $ simpleDefDecl "bar" t) over' | (i,t) <- zip [0..] ts]
chanInt = A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Int
chanIntIn = A.ChanEnd A.DirInput A.Unshared A.Int
chanIntOut = A.ChanEnd A.DirOutput A.Unshared A.Int
testAll :: Int -> (String,String) -> (String,String) -> A.SpecType -> Test
testAll a b c d = testAllS a b c d (return ()) over
testAllS :: Int -> (String,String) -> (String,String) -> A.SpecType -> State CompState () -> (GenOps -> GenOps) -> Test
testAllS n (eCI,eCR) (eCPPI,eCPPR) spec st overFunc = TestList
[
testBothS ("testSpec " ++ show n) eCI eCPPI (local overFunc (tcall introduceSpec NotTopLevel $ A.Specification emptyMeta foo spec)) st
,testBothS ("testSpec " ++ show n) eCR eCPPR (local overFunc (tcall removeSpec $ A.Specification emptyMeta foo spec)) st
]
testAllSame n e s = testAll n e e s
testAllSameS n e s st o = testAllS n e e s st o
over' ops = ops {genDeclaration = override3 (tell . (\x -> ["#ATION_",show x]))
,declareInit = (override3 (Just $ tell ["#INIT"])), declareFree = override3 (Just $ tell ["#FREE"])
,getScalarType = (\x -> Just $ "$(" ++ show x ++ ")")
}
over ops = (over' ops) { genVariable' = override3 at }
testRetypeSizes :: Test
testRetypeSizes = TestList
[
-- Channel retyping doesn't need size check:
test 0 "" (A.Chan undefined undefined) (A.Chan undefined undefined)
-- Plain types just need to check the return of occam_check_retype:
,test 1 "if(occam_check_retype(#S,#D,#M)!=1){@}"
A.Int A.Int32
,test 2 "if(occam_check_retype(#S,#D,#M)!=1){@}"
(A.Record foo) (A.Record bar)
-- Array types where both sizes are fixed should act like the plain types:
,test 3 "if(occam_check_retype(#S,#D,#M)!=1){@}"
(A.Array [dimension 2] A.Int) (A.Array [dimension 8] A.Byte)
,test 4 "if(occam_check_retype(#S,#D,#M)!=1){@}"
(A.Array [dimension 2,dimension 3,dimension 4] A.Int) (A.Array [A.UnknownDimension] A.Byte)
-- Array types with a free dimension should not check the return:
,test 100 "occam_check_retype(#S,#D,#M);"
(A.Array [A.UnknownDimension] A.Int) (A.Array [dimension 8] A.Byte)
,test 101 "occam_check_retype(#S,#D,#M);"
(A.Array [dimension 2,A.UnknownDimension,dimension 4] A.Int) (A.Array [A.UnknownDimension] A.Byte)
]
where
test :: Int -> String -> A.Type -> A.Type -> Test
test n exp destT srcT = testBothSame ("testRetypeSizes " ++ show n) (repAll exp)
(over (tcall5 genRetypeSizes emptyMeta destT undefined srcT undefined))
where
repAll = (rep "#S" ("$(" ++ show srcT ++ " Right)")) .
(rep "#D" ("$(" ++ show destT ++ " Left True)")) .
(rep "#M" ("\"" ++ show emptyMeta ++ "\""))
rep search replace str = subRegex (mkRegex search) str replace
showBytesInParams _ t (Right _) = tell ["$(" ++ show t ++ " Right)"]
showBytesInParams _ t v = tell ["$(" ++ show t ++ " " ++ show v ++ ")"]
over :: Override
over = local $ \ops -> ops {genBytesIn = showBytesInParams, genStop = override2 at}
defRecord :: String -> String -> A.Type -> State CompState ()
defRecord rec mem t = defineName (simpleName rec) $
A.NameDef emptyMeta rec rec
(A.RecordType emptyMeta (A.RecordAttr False False) [(simpleName mem,t)])
A.Original A.NameUser A.Unplaced
testGenVariable :: Test
testGenVariable = TestList
[
-- Various types, unsubscripted:
testSameA 0 ("foo","*foo","foo") id A.Int
,testSameA 10 ("foo","*foo","*foo") id (A.Record bar)
,testSameA2 20 ("foo","*foo") id (A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Int)
,testSameA2 30 ("foo","foo") id (A.ChanEnd A.DirInput A.Unshared A.Int)
-- Mobile versions of the above:
,testSameA2 40 ("foo","*foo") id (A.Mobile A.Int)
,testSameA2 45 ("*foo","**foo") deref (A.Mobile A.Int)
,testSameA2 50 ("foo","*foo") id (A.Mobile $ A.Record bar)
,testSameA2 55 ("*foo","**foo") deref (A.Mobile $ A.Record bar)
-- Arrays of the previous types, unsubscripted:
,testSameA 100 ("foo","foo","foo") id (A.Array [dimension 8] A.Int)
,testSameA 110 ("foo","foo","foo") id (A.Array [dimension 8] $ A.Record bar)
,testSameA2 120 ("foo","foo") id (A.Array [dimension 8] $ A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Int)
,testSameA2 130 ("foo","foo") id (A.Array [dimension 8] $ A.ChanEnd A.DirInput A.Unshared A.Int)
-- Mobile arrays of the previous types:
,testSameA2 140 ("foo","*foo") id (A.Mobile $ A.Array [dimension 8] A.Int)
,testSameA2 145 ("((int32_t*)((foo)->data))","((int32_t*)((*foo)->data))") deref (A.Mobile $ A.Array [dimension 8] A.Int)
,testSameA2 150 ("foo","*foo") id (A.Mobile $ A.Array [dimension 8] $ A.Record bar)
,testSameA2 155 ("((bar*)((foo)->data))","((bar*)((*foo)->data))") deref (A.Mobile $ A.Array [dimension 8] $ A.Record bar)
-- Subscripted record:
,testSameA 200 ("(foo).x","(*foo).x","(*foo).x") fieldX (A.Record bar)
,testSameA2 210 ("(*foo).x","(**foo).x") (fieldX . deref) (A.Mobile $ A.Record bar)
,testSameA 220 ("(foo).y","(*foo).y","(*foo).y") fieldY (A.Record $ simpleName "barbar")
,testSameA 230 ("((foo).y).x","((*foo).y).x","((*foo).y).x") (fieldX . fieldY) (A.Record $ simpleName "barbar")
-- Fully subscripted array:
,testAC 300 ("(foo)@C4","(foo)@U4") (sub 4) (A.Array [dimension 8] A.Int)
,testAC 305 ("(foo)@C4,5,6","(foo)@U4,5,6") ((sub 6) . (sub 5) . (sub 4)) (A.Array [dimension 8,dimension 9,dimension 10] A.Int)
,testAC 310 ("(foo)@C4","(foo)@U4") (sub 4) (A.Array [dimension 8] $ A.Record bar)
-- Original channel arrays are Channel*[], abbreviated channel arrays are Channel*[]:
,testAC2 320 ("(foo)@C4","(foo)@U4") ("(foo)@C4","(foo)@U4") (sub 4) (A.Array [dimension 8] $ A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Int)
,testAC 330 ("(foo)@C4","(foo)@U4") (sub 4) (A.Array [dimension 8] $ A.ChanEnd A.DirInput A.Unshared A.Int)
-- Fully subscripted array, and record field reference:
,testAC 400 ("((foo)@C4).x","((foo)@U4).x") (fieldX . (sub 4)) (A.Array [dimension 8] $ A.Record bar)
-- As above, but then with an index too:
,testAC 410 ("((foo)@C4).x@C4","((foo)@U4).x)@U4") ((sub 4) . fieldX . (sub 4)) (A.Array [dimension 8] $ A.Record bar)
--TODO come back to slices later
-- Directed variables (incl. members of arrays, deref mobiles):
,testSameA2 500 ("$(&foo)$","$foo$") dir (A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Int)
-- Test for mobile channels (in future)
--,testSameA2 510 ("$foo$","$(*foo)$") (dir . deref) (A.Mobile $ A.Chan A.DirUnknown (A.ChanAttributes A.Unshared A.Unshared) A.Int)
,testAC2 520 ("$foo@C4$","$foo@U4$") ("$foo@C4$","$foo@U4$") (dir . (sub 4)) (A.Array [dimension 8] $ A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Int)
]
where
deref = A.DerefVariable emptyMeta
dir = A.DirectedVariable emptyMeta A.DirInput
fieldX = A.SubscriptedVariable emptyMeta (A.SubscriptField emptyMeta $ simpleName "x")
fieldY = A.SubscriptedVariable emptyMeta (A.SubscriptField emptyMeta $ simpleName "y")
sub n = A.SubscriptedVariable emptyMeta (A.Subscript emptyMeta A.CheckBoth $ intLiteral n)
test :: Int -> (String,String) -> (String,String) -> (A.Variable -> A.Variable) -> A.AbbrevMode -> A.Type -> Test
test n (eC,eUC) (eCPP,eUCPP) sub am t = TestList
[
testBothS ("testGenVariable/checked" ++ show n) eC eCPP
(over (tcall genVariable (sub $ A.Variable emptyMeta foo) A.Original)) state
,testBothS ("testGenVariable/unchecked" ++ show n) eUC eUCPP
(over (tcall genVariableUnchecked (sub $ A.Variable emptyMeta foo) A.Original)) state
]
where
state = do defineName (simpleName "foo") $
A.NameDef emptyMeta "foo" "foo"
(A.Declaration emptyMeta t) am A.NameUser A.Unplaced
defRecord "bar" "x" $ A.Array [dimension 7] A.Int
defRecord "barbar" "y" $ A.Record bar
over :: Override
over = local $ \ops -> ops {genArraySubscript = (\c _ subs -> at >> (tell [if c /= A.NoCheck then "C" else "U"]) >> (seqComma $ map snd subs))
,genDirectedVariable = (\_ _ cg _ -> dollar >> cg >> dollar)}
testA :: Int -> (String,String) -> (String,String) -> (A.Variable -> A.Variable) -> A.Type -> Test
testA n eC eCPP sub t = TestList [test n eC eCPP sub A.Original t, test (n+1) eC eCPP sub A.Abbrev t, test (n+2) eC eCPP sub A.ValAbbrev t]
-- | Tests that the given (checked,unchecked) expected values occur in both C and C++
testAC :: Int -> (String,String) -> (A.Variable -> A.Variable) -> A.Type -> Test
testAC n e sub t = testA n e e sub t
-- | Tests that the given (checked,unchecked) expected values (for Original and Abbrev modes) occur in both C and C++
testAC2 :: Int -> (String,String) -> (String,String) -> (A.Variable -> A.Variable) -> A.Type -> Test
testAC2 n e e' sub t = TestList [test n e e sub A.Original t, test (n+1) e' e' sub A.Abbrev t]
testSame :: Int -> String -> (A.Variable -> A.Variable) -> A.AbbrevMode -> A.Type -> Test
testSame n e sub am t = test n (e,e) (e,e) sub am t
testSameA :: Int -> (String,String,String) -> (A.Variable -> A.Variable) -> A.Type -> Test
testSameA n (eO,eA,eVA) sub t = TestList [testSame n eO sub A.Original t,testSame (n+1) eA sub A.Abbrev t,testSame (n+2) eVA sub A.ValAbbrev t]
testSameA2 :: Int -> (String,String) -> (A.Variable -> A.Variable) -> A.Type -> Test
testSameA2 n (eO,eA) sub t = TestList [testSame n eO sub A.Original t,testSame (n+1) eA sub A.Abbrev t]
testAssign :: Test
testAssign = TestList
[
testBothSameS "testAssign 0" "@=$;" (over (tcall3 genAssign emptyMeta [A.Variable emptyMeta foo] (A.ExpressionList emptyMeta [e]))) (state A.Int)
,testBothSameS "testAssign 1" "@=$;" (over (tcall3 genAssign emptyMeta [A.Variable emptyMeta foo] (A.ExpressionList emptyMeta [e]))) (state A.Time)
,testBothSameS "testAssign 2" "@=$;" (over (tcall3 genAssign emptyMeta [A.Variable emptyMeta foo] (A.ExpressionList emptyMeta [e])))
(state $ A.ChanEnd A.DirInput A.Unshared A.Int)
-- Fail because genAssign only handles one destination and one source:
,testBothFail "testAssign 100" (tcall3 genAssign emptyMeta [A.Variable emptyMeta foo,A.Variable emptyMeta foo] (A.ExpressionList emptyMeta [e]))
,testBothFail "testAssign 101" (tcall3 genAssign emptyMeta [A.Variable emptyMeta foo] (A.ExpressionList emptyMeta [e,e]))
,testBothFail "testAssign 102" (tcall3 genAssign emptyMeta [A.Variable emptyMeta foo,A.Variable emptyMeta foo] (A.ExpressionList emptyMeta [e, e]))
-- Fail because assignment can't be done with these types (should have already been transformed away):
,testBothFailS "testAssign 200" (over (tcall3 genAssign emptyMeta [A.Variable emptyMeta foo] (A.ExpressionList emptyMeta [e])))
(state $ A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Int)
,testBothFailS "testAssign 201" (over (tcall3 genAssign emptyMeta [A.Variable emptyMeta foo] (A.ExpressionList emptyMeta [e])))
(state $ A.Record bar)
]
where
--The expression won't be examined so we can use what we like:
e = A.True emptyMeta
state t = defineName (simpleName "foo") $ simpleDefDecl "foo" t
over :: Override
over = local $ \ops -> ops {genVariable' = override3 at, genExpression = override1 dollar}
testCase :: Test
testCase = TestList
[
testBothSame "testCase 0" "switch($){default:^}" (over (tcall3 genCase emptyMeta e (A.Several emptyMeta [])))
,testBothSame "testCase 1" "switch($){default:{@}break;}" (over (tcall3 genCase emptyMeta e (A.Only emptyMeta $ A.Else emptyMeta p)))
,testBothSame "testCase 2" "switch($){default:{#@}break;}" (over (tcall3 genCase emptyMeta e (spec $ A.Only emptyMeta $ A.Else emptyMeta p)))
,testBothSame "testCase 10" "switch($){case $:{@}break;default:^}" (over (tcall3 genCase emptyMeta e (A.Only emptyMeta $ A.Option emptyMeta [intLiteral 0] p)))
,testBothSame "testCase 20" "switch($){case $:case $:{#@}break;default:{@}break;case $:{@}break;}" (over (tcall3 genCase emptyMeta e $ A.Several emptyMeta
[spec $ A.Only emptyMeta $ A.Option emptyMeta [e, e] p
,A.Only emptyMeta $ A.Else emptyMeta p
,A.Only emptyMeta $ A.Option emptyMeta [e] p]
))
]
where
--The expression and process won't be used so we can use what we like:
e = A.True emptyMeta
p = A.Skip emptyMeta
spec :: Data a => A.Structured a -> A.Structured a
spec = A.Spec emptyMeta undefined
over :: Override
over = local $ \ops -> ops {genExpression = override1 dollar, genProcess = override1 at
, genStop = override2 caret, genSpec = override3 (hash >> return undefined)}
testIf :: Test
testIf = TestList
[
testBothR "testIf 0" "\\{\\^\\}" "\\{\\^\\}"
(over (tcall2 genIf emptyMeta (A.Several emptyMeta [])))
,testBothR "testIf 1" "if\\(\\$\\)\\{@\\}else \\{\\^\\}" "if\\(\\$\\)\\{@\\}else \\{\\^\\}"
(over (tcall2 genIf emptyMeta (A.Only emptyMeta $ A.Choice emptyMeta e p)))
,testBothR "testIf 2" "/\\*([[:alnum:]_]+)\\*/`if\\(\\$\\)\\{@goto \\1;\\}#\\^\\1:;"
"class ([[:alnum:]_]+)\\{\\};try\\{`if\\(\\$\\)\\{@throw \\1\\(\\);\\}#\\^\\}catch\\(\\1\\)\\{\\}"
(over (tcall2 genIf emptyMeta (A.Spec emptyMeta undefined $ A.Only emptyMeta $ A.Choice emptyMeta e p)))
]
where
e :: A.Expression
e = undefined
p :: A.Process
p = undefined
over :: Override
over = local $ \ops -> ops { genExpression = override1 dollar
, genProcess = override1 at
, genStop = override2 caret
, introduceSpec = override2 backq
, removeSpec = override1 hash}
testWhile :: Test
testWhile = testBothSame "testWhile 0" "while($){@}" (over (tcall2 genWhile undefined undefined))
where
over :: Override
over = local $ \ops -> ops {genExpression = override1 dollar, genProcess = override1 at}
testInput :: Test
testInput = TestList
[
-- Test that genInput passes on the calls properly:
testBothSame "testInput 0" "" (overInputItemCase (tcall2 genInput undefined $ A.InputSimple undefined []))
,testBothSame "testInput 1" "^" (overInputItemCase (tcall2 genInput undefined $ A.InputSimple undefined [undefined]))
,testBothSame "testInput 2" "^^^" (overInputItemCase (tcall2 genInput undefined $ A.InputSimple undefined [undefined, undefined, undefined]))
-- Reading an integer (special case in the C backend):
,testInputItem 100 "ChanInInt(wptr,#,&x);" "tockRecvArrayOfBytes(#,tockSendableArrayOfBytes(^(Int),&x));"
(A.InVariable emptyMeta $ variable "x") A.Int
-- Reading a other plain types:
,testInputItem 101 "ChanIn(wptr,#,&x,^(Int8));" "tockRecvArrayOfBytes(#,tockSendableArrayOfBytes(^(Int8),&x));"
(A.InVariable emptyMeta $ variable "x") A.Int8
,testInputItem 102 ("ChanIn(wptr,#,(&x),^(" ++ show (A.Record foo) ++ "));")
("tockRecvArrayOfBytes(#,tockSendableArrayOfBytes(^(" ++ show (A.Record foo) ++ "),(&x)));")
(A.InVariable emptyMeta $ variable "x") (A.Record foo)
-- Reading into a fixed size array:
,testInputItem 103 "ChanIn(wptr,#,x,^(Array [Dimension 8] Int));"
"tockRecvArrayOfBytes(#,tockSendableArrayOfBytes(^(Array [Dimension 8] Int),x));"
(A.InVariable emptyMeta $ variable "x") $ A.Array [dimension 8] A.Int
-- Reading into subscripted variables:
,testInputItem 110 "ChanInInt(wptr,#,&xs$);" "tockRecvArrayOfBytes(#,tockSendableArrayOfBytes(^(Int),&xs$));"
(A.InVariable emptyMeta $ sub0 $ variable "xs") A.Int
-- Reading a other plain types:
,testInputItem 111 "ChanIn(wptr,#,&xs$,^(Int8));" "tockRecvArrayOfBytes(#,tockSendableArrayOfBytes(^(Int8),&xs$));"
(A.InVariable emptyMeta $ sub0 $ variable "xs") A.Int8
,testInputItem 112 ("ChanIn(wptr,#,(&xs$),^(" ++ show (A.Record foo) ++ "));")
("tockRecvArrayOfBytes(#,tockSendableArrayOfBytes(^(" ++ show (A.Record foo) ++ "),(&xs$)));")
(A.InVariable emptyMeta $ sub0 $ variable "xs") (A.Record foo)
-- A counted array of Int:
,testInputItem 200 "ChanInInt(wptr,#,&x);ChanIn(wptr,#,xs,x*^(Int));"
"tockRecvArrayOfBytes(#,tockSendableArrayOfBytes(^(Int),&x));tockRecvArrayOfBytes(#,tockSendableArrayOfBytes(x*^(Int),xs));"
(A.InCounted emptyMeta (variable "x") (variable "xs")) (A.Counted A.Int A.Int)
-- A counted array, counted by Int8:
,testInputItem 201 "ChanIn(wptr,#,&x,^(Int8));ChanIn(wptr,#,xs,x*^(Int));"
"tockRecvArrayOfBytes(#,tockSendableArrayOfBytes(^(Int8),&x));tockRecvArrayOfBytes(#,tockSendableArrayOfBytes(x*^(Int),xs));"
(A.InCounted emptyMeta (variable "x") (variable "xs")) (A.Counted A.Int8 A.Int)
-- TODO reading in a counted/fixed-size array into an array of arrays (or will that have already been sliced?)
-- inputs as part of protocols/any:
,testInputItemProt 300 "ChanInInt(wptr,#,&x);" "tockRecvArrayOfBytes(#,tockSendableArrayOfBytes(^(Int),&x));"
(A.InVariable emptyMeta $ variable "x") A.Int
,testInputItemProt 301 "ChanIn(wptr,#,&x,^(Int8));" "tockRecvArrayOfBytes(#,tockSendableArrayOfBytes(^(Int8),&x));"
(A.InVariable emptyMeta $ variable "x") A.Int8
,testInputItemProt 302 ("ChanIn(wptr,#,(&x),^(" ++ show (A.Record foo) ++ "));") ("tockRecvArrayOfBytes(#,tockSendableArrayOfBytes(^(" ++ show (A.Record foo) ++ "),(&x)));")
(A.InVariable emptyMeta $ variable "x") (A.Record foo)
,testInputItemProt 303 "ChanIn(wptr,#,x,^(Array [Dimension 8] Int));" "tockRecvArrayOfBytes(#,tockSendableArrayOfBytes(^(Array [Dimension 8] Int),x));"
(A.InVariable emptyMeta $ variable "x") $ A.Array [dimension 8] A.Int
,testInputItemProt 400 "ChanInInt(wptr,#,&x);ChanIn(wptr,#,xs,x*^(Int));"
"tockRecvArrayOfBytes(#,tockSendableArrayOfBytes(^(Int),&x));tockRecvArrayOfBytes(#,tockSendableArrayOfBytes(x*^(Int),xs));"
(A.InCounted emptyMeta (variable "x") (variable "xs")) (A.Counted A.Int A.Int)
,testInputItemProt 401 "ChanIn(wptr,#,&x,^(Int8));ChanIn(wptr,#,xs,x*^(Int8));"
"tockRecvArrayOfBytes(#,tockSendableArrayOfBytes(^(Int8),&x));tockRecvArrayOfBytes(#,tockSendableArrayOfBytes(x*^(Int8),xs));"
(A.InCounted emptyMeta (variable "x") (variable "xs")) (A.Counted A.Int8 A.Int8)
]
where
sub0 = A.SubscriptedVariable emptyMeta (A.Subscript emptyMeta A.NoCheck (intLiteral 0))
testInputItem :: Int -> String -> String -> A.InputItem -> A.Type -> Test
testInputItem n eC eCPP oi t = testInputItem' n eC eCPP oi t t
-- Tests sending things over channels of protocol or ANY
testInputItemProt :: Int -> String -> String -> A.InputItem -> A.Type -> Test
testInputItemProt n eC eCPP oi t = TestList [testInputItem' n eC eCPP oi t (A.UserProtocol foo),testInputItem' n eC eCPP oi t A.Any]
testInputItem' :: Int -> String -> String -> A.InputItem -> A.Type -> A.Type -> Test
testInputItem' n eC eCPP ii t ct = TestList
[
testBothS ("testInput " ++ show n) (hashIs "&c" eC) (hashIs "(c).reader()" eCPP) (over (tcall2 genInputItem (A.Variable emptyMeta $ simpleName "c") ii))
(state $ A.Chan (A.ChanAttributes A.Unshared A.Unshared))
,testBothS ("testInput [in] " ++ show n) (hashIs "c" eC) (hashIs "c" eCPP) (over (tcall2 genInputItem (A.Variable emptyMeta $ simpleName "c") ii))
(state $ A.ChanEnd A.DirInput A.Unshared)
]
where
hashIs x y = subRegex (mkRegex "#") y x
state ch = do defineName (simpleName "c") $ simpleDefDecl "c" (ch ct)
case t of
A.Counted t t' -> do defineName (simpleName "x") $ simpleDefDecl "x" t
defineName (simpleName "xs") $ simpleDefDecl "xs" (mkArray t')
_ -> do defineName (simpleName "x") $ simpleDefDecl "x" t
defineName (simpleName "xs") $ simpleDefDecl "xs" (mkArray t)
mkArray (A.Array ds t) = A.Array (dimension 6:ds) t
mkArray t = A.Array [dimension 6] t
-- chan = simpleName "c"
-- chanIn = simpleName "cIn"
-- state = do defineName chan $ simpleDefDecl "c" (A.Chan A.DirUnknown (A.ChanAttributes False False) $ A.UserProtocol foo)
-- defineName chanOut $ simpleDefDecl "cIn" (A.Chan A.DirInput (A.ChanAttributes False False) $ A.UserProtocol foo)
overInputItemCase, over :: Override
overInputItemCase = local $ \ops -> ops {genInputItem = override2 caret}
over = local $ \ops -> ops {genBytesIn = (\_ t _ -> tell ["^(", showSimplerType t, ")"]) , genArraySubscript = override3 dollar}
-- | Show a type, simplifying how Dimensions are show.
showSimplerType :: A.Type -> String
showSimplerType t = subRegex re (show t) "Dimension \\1"
where re = mkRegex "Dimension [^\"]*\"([^\"]*)\"\\)\\)"
testOutput :: Test
testOutput = TestList
[
-- testBothSame "testOutput 0" "" (overOutputItem (tcall2 genOutput undefined []))
-- ,testBothSame "testOutput 1" "^" (overOutputItem (tcall2 genOutput undefined [undefined]))
-- ,testBothSame "testOutput 2" "^^^" (overOutputItem (tcall2 genOutput undefined [undefined,undefined,undefined]))
testBothS "testOutput 100" "ChanOutInt(wptr,(&c),bar_foo);^" "tockSendInt((&c)->writer(),bar_foo);^" (overOutput (tcall3 genOutputCase (A.Variable emptyMeta chan) bar [])) state
,testBothS "testOutput 101" "ChanOutInt(wptr,cOut,bar_foo);^" "tockSendInt(cOut,bar_foo);^" (overOutput (tcall3 genOutputCase (A.Variable emptyMeta chanOut) bar [])) state
--Integers are a special case in the C backend:
,testOutputItem 201 "ChanOutInt(wptr,#,x);"
"tockSendArrayOfBytes(#,tockSendableArrayOfBytes(&x));"
(A.OutExpression emptyMeta $ exprVariable "x") A.Int
--A plain type on the channel of the right type:
,testOutputItem 202 "ChanOut(wptr,#,&x,^);"
"tockSendArrayOfBytes(#,tockSendableArrayOfBytes(&x));"
(A.OutExpression emptyMeta $ exprVariable "x") A.Int64
--A record type on the channel of the right type (because records are always referenced by pointer):
,testOutputItem 203 "ChanOut(wptr,#,(&x),^);"
"tockSendArrayOfBytes(#,tockSendableArrayOfBytes((&x)));"
(A.OutExpression emptyMeta $ exprVariable "x") (A.Record foo)
--A fixed size array on the channel of the right type:
,testOutputItem 204 "ChanOut(wptr,#,x,^);"
"tockSendArrayOfBytes(#,tockSendableArrayOfBytes(x));"
(A.OutExpression emptyMeta $ exprVariable "x") (A.Array [dimension 6] A.Int)
,testOutputItem 205 "ChanOut(wptr,#,x,^);"
"tockSendArrayOfBytes(#,tockSendableArrayOfBytes(x));"
(A.OutExpression emptyMeta $ exprVariable "x") (A.Array [dimension 6, dimension 7, dimension 8] A.Int)
--A counted array:
,testOutputItem 206 "ChanOutInt(wptr,#,x);ChanOut(wptr,#,xs,x*^);"
"tockSendArrayOfBytes(#,tockSendableArrayOfBytes(&x));tockSendArrayOfBytes(#,tockSendableArrayOfBytes(xs));"
(A.OutCounted emptyMeta (exprVariable "x") (exprVariable "xs")) (A.Counted A.Int A.Int)
--A counted array of arrays:
,testOutputItem 207 "ChanOutInt(wptr,#,x);ChanOut(wptr,#,xs,x*^);"
"tockSendArrayOfBytes(#,tockSendableArrayOfBytes(&x));tockSendArrayOfBytes(#,tockSendableArrayOfBytes(xs));"
(A.OutCounted emptyMeta (exprVariable "x") (exprVariable "xs")) (A.Counted A.Int (A.Array [dimension 5] A.Int))
,testOutputItem 208 "ChanOutInt(wptr,#,x);ChanOut(wptr,#,xs,x*^);"
"tockSendArrayOfBytes(#,tockSendableArrayOfBytes(&x));tockSendArrayOfBytes(#,tockSendableArrayOfBytes(xs));"
(A.OutCounted emptyMeta (exprVariable "x") (exprVariable "xs")) (A.Counted A.Int (A.Array [dimension 4,dimension 5] A.Int))
-- Test counted arrays that do not have Int as the count type:
,testOutputItem 209 "ChanOut(wptr,#,&x,^);ChanOut(wptr,#,xs,x*^);"
"tockSendArrayOfBytes(#,tockSendableArrayOfBytes(&x));tockSendArrayOfBytes(#,tockSendableArrayOfBytes(xs));"
(A.OutCounted emptyMeta (exprVariable "x") (exprVariable "xs")) (A.Counted A.Int8 A.Int8)
--TODO add a pass that makes sure all outputs are variables. Including count for counted items
--Test sending things that are part of protocols (this will require different code in the C++ backend)
,testOutputItemProt 301 "ChanOutInt(wptr,#,x);"
"tockSendArrayOfBytes(#,tockSendableArrayOfBytes(&x));"
(A.OutExpression emptyMeta $ exprVariable "x") A.Int
,testOutputItemProt 302 "ChanOut(wptr,#,&x,^);"
"tockSendArrayOfBytes(#,tockSendableArrayOfBytes(&x));"
(A.OutExpression emptyMeta $ exprVariable "x") A.Int64
,testOutputItemProt 303 "ChanOut(wptr,#,&x,^);"
"tockSendArrayOfBytes(#,tockSendableArrayOfBytes((&x)));"
(A.OutExpression emptyMeta $ exprVariable "x") (A.Record foo)
,testOutputItemProt 304 "ChanOut(wptr,#,x,^);"
"tockSendArrayOfBytes(#,tockSendableArrayOfBytes(x));"
(A.OutExpression emptyMeta $ exprVariable "x") (A.Array [dimension 6] A.Int)
,testOutputItemProt 305 "ChanOut(wptr,#,x,^);"
"tockSendArrayOfBytes(#,tockSendableArrayOfBytes(x));"
(A.OutExpression emptyMeta $ exprVariable "x") (A.Array [dimension 6, dimension 7, dimension 8] A.Int)
,testOutputItemProt 306 "ChanOutInt(wptr,#,x);ChanOut(wptr,#,xs,x*^);"
"tockSendArrayOfBytes(#,tockSendableArrayOfBytes(&x));tockSendArrayOfBytes(#,tockSendableArrayOfBytes(xs));"
(A.OutCounted emptyMeta (exprVariable "x") (exprVariable "xs")) (A.Counted A.Int A.Int)
,testOutputItemProt 307 "ChanOutInt(wptr,#,x);ChanOut(wptr,#,xs,x*^);"
"tockSendArrayOfBytes(#,tockSendableArrayOfBytes(&x));tockSendArrayOfBytes(#,tockSendableArrayOfBytes(xs));"
(A.OutCounted emptyMeta (exprVariable "x") (exprVariable "xs")) (A.Counted A.Int (A.Array [dimension 5] A.Int))
,testOutputItemProt 308 "ChanOutInt(wptr,#,x);ChanOut(wptr,#,xs,x*^);"
"tockSendArrayOfBytes(#,tockSendableArrayOfBytes(&x));tockSendArrayOfBytes(#,tockSendableArrayOfBytes(xs));"
(A.OutCounted emptyMeta (exprVariable "x") (exprVariable "xs")) (A.Counted A.Int (A.Array [dimension 4,dimension 5] A.Int))
--TODO add tests for sending on channels that are part of (normal, and abbreviated) channel arrays.
]
where
testOutputItem :: Int -> String -> String -> A.OutputItem -> A.Type -> Test
testOutputItem n eC eCPP oi t = testOutputItem' n eC eCPP oi t t
-- Tests sending things over channels of protocol or ANY
testOutputItemProt :: Int -> String -> String -> A.OutputItem -> A.Type -> Test
testOutputItemProt n eC eCPP oi t = TestList [testOutputItem' n eC eCPP oi t (A.UserProtocol foo),testOutputItem' n eC eCPP oi t A.Any]
testOutputItem' :: Int -> String -> String -> A.OutputItem -> A.Type -> A.Type -> Test
testOutputItem' n eC eCPP oi t ct = TestList
[
testBothS ("testOutput " ++ show n) (hashIs "&c" eC) (hashIs "(c).writer()" eCPP)
(over (tcall3 genOutputItem A.Int64 (A.Variable emptyMeta $ simpleName "c") oi))
(state $ A.Chan (A.ChanAttributes A.Unshared A.Unshared))
,testBothS ("testOutput [out] " ++ show n) (hashIs "c" eC) (hashIs "c" eCPP)
(over (tcall3 genOutputItem A.Int64 (A.Variable emptyMeta $ simpleName "c") oi))
(state $ A.ChanEnd A.DirOutput A.Unshared)
]
where
hashIs x y = subRegex (mkRegex "#") y x
state ch = do defineName (simpleName "c") $ simpleDefDecl "c" (ch ct)
case t of
A.Counted t t' -> do defineName (simpleName "x") $ simpleDefDecl "x" t
defineName (simpleName "xs") $ simpleDefDecl "xs" (mkArray t')
_ -> defineName (simpleName "x") $ simpleDefDecl "x" t
mkArray (A.Array ds t) = A.Array (dimension 6:ds) t
mkArray t = A.Array [dimension 6] t
chan = simpleName "c"
chanOut = simpleName "cOut"
state :: CSM m => m ()
state = do defineName chan $ simpleDefDecl "c" (A.Chan (A.ChanAttributes A.Unshared A.Unshared) $ A.UserProtocol foo)
defineName chanOut $ simpleDefDecl "cOut" (A.ChanEnd A.DirOutput A.Unshared $ A.UserProtocol foo)
defineName foo $ simpleDef "foo" $ A.ProtocolCase emptyMeta [(simpleName "bar", [])]
overOutput, overOutputItem, over :: Override
overOutput = local $ \ops -> ops {genOutput = override2 caret}
overOutputItem = local $ \ops -> ops {genOutputItem = override3 caret}
over = local $ \ops -> ops {genBytesIn = override3 caret}
testBytesIn :: Test
testBytesIn = TestList
[
testBothSame "testBytesIn 0" "sizeof(int8_t)" (tcall3 genBytesIn undefined A.Int8 undefined)
,testBothSame "testBytesIn 1" "sizeof(foo)" (tcall3 genBytesIn undefined (A.Record foo) undefined)
,testBoth "testBytesIn 2" "sizeof(Channel)" "sizeof(csp::One2OneChannel<int32_t>)" (tcall3 genBytesIn undefined (A.Chan (A.ChanAttributes A.Unshared A.Unshared) A.Int32) undefined)
,testBoth "testBytesIn 3" "sizeof(Channel*)" "sizeof(csp::AltChanin<int64_t>)" (tcall3 genBytesIn undefined (A.ChanEnd A.DirInput A.Unshared A.Int64) undefined)
--Array with a single known dimension:
,testBothSame "testBytesIn 100" "5*sizeof(int16_t)" (tcall3 genBytesIn undefined (A.Array [dimension 5] A.Int16) (Left False))
--single unknown dimension, no variable, no free dimension allowed:
,testBothFail "testBytesIn 101a" (tcall3 genBytesIn undefined (A.Array [A.UnknownDimension] A.Int) (Left False))
--single unknown dimension, no variable, free dimension allowed:
,testBothSame "testBytesIn 101b" "sizeof(int16_t)" (tcall3 genBytesIn undefined (A.Array [A.UnknownDimension] A.Int16) (Left True))
--single unknown dimension, with variable:
,testBothSame "testBytesIn 102" "$(@0)*sizeof(int32_t)" (over (tcall3 genBytesIn undefined (A.Array [A.UnknownDimension] A.Int32) (Right undefined)))
--Array with all known dimensions:
,testBothSame "testBytesIn 200" "7*6*5*sizeof(int16_t)" (tcall3 genBytesIn undefined (A.Array [dimension 5,dimension 6, dimension 7] A.Int16) (Left False))
--single unknown dimension, no variable, no free dimension allowed:
,testBothFail "testBytesIn 201a" (tcall3 genBytesIn undefined (A.Array [dimension 5,dimension 6,A.UnknownDimension] A.Int) (Left False))
--single unknown dimension, no variable, free dimension allowed:
,testBothSame "testBytesIn 201b" "6*5*sizeof(int64_t)" (tcall3 genBytesIn undefined (A.Array [dimension 5,dimension 6,A.UnknownDimension] A.Int64) (Left True))
--single unknown dimension, with variable:
,testBothSame "testBytesIn 202" "$(@2)*6*5*sizeof(int8_t)" (over (tcall3 genBytesIn undefined (A.Array [dimension 5,dimension 6,A.UnknownDimension] A.Int8) (Right undefined)))
]
where
over :: Override
over = local $ \ops -> ops {genVariable' = override3 dollar}
testMobile :: Test
testMobile = TestList
[
testBoth "testMobile 0" "malloc(#(Int Left False))" "new Int" (local over (tcall3 genAllocMobile emptyMeta (A.Mobile A.Int) Nothing))
,TestCase $ assertGen "testMobile 1/C++" "new Int($)" $ (evalCGen (call genAllocMobile emptyMeta (A.Mobile A.Int) (Just undefined)) (over cppgenOps) emptyState)
,testBoth "testMobile 100" "if(@!=NULL){free(@);@=NULL;}" "if(@!=NULL){delete @;@=NULL;}"
(local over (tcall2 genClearMobile emptyMeta undefined))
]
where
showBytesInParams _ t (Right _) = tell ["#(" ++ show t ++ " Right)"]
showBytesInParams _ t v = tell ["#(" ++ show t ++ " " ++ show v ++ ")"]
over ops = ops { genBytesIn = showBytesInParams
, getCType = (\_ t _ -> return $ Plain $ show t)
, genExpression = override1 dollar
, genVariable' = override3 at
}
---Returns the list of tests:
tests :: Test
tests = TestLabel "GenerateCTest" $ TestList
[
testActuals
,testArraySizes
,testArraySlice
,testArraySubscript
,testAssign
,testBytesIn
,testCase
,testDeclaration
,testDeclareInitFree
,testGenType
,testGenVariable
,testIf
,testInput
,testMobile
,testOutput
,testOverArray
,testRecord
,testReplicator
,testRetypeSizes
,testSpec
,testStop
,testWhile
]