Compare commits
3 Commits
feature/la
...
generate_c
| Author | SHA1 | Date | |
|---|---|---|---|
| 84f6925f60 | |||
| 4ae957aac6 | |||
| f4a3cf69ad |
@@ -18,13 +18,14 @@ extra-source-files: CHANGELOG.md
|
|||||||
|
|
||||||
executable generation
|
executable generation
|
||||||
main-is: Main.hs
|
main-is: Main.hs
|
||||||
other-modules: Base, BinaryOps, Compare, Conversions, CryptoNum, File, Gen
|
other-modules: Base, BinaryOps, Compare, Conversions, CryptoNum, File, Gen, Testing
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
build-depends: base ^>=4.12.0.0,
|
build-depends: base ^>=4.12.0.0,
|
||||||
containers,
|
containers,
|
||||||
directory,
|
directory,
|
||||||
filepath,
|
filepath,
|
||||||
mtl
|
mtl,
|
||||||
|
random
|
||||||
hs-source-dirs: src
|
hs-source-dirs: src
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
ghc-options: -Wall
|
ghc-options: -Wall
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ base :: File
|
|||||||
base = File {
|
base = File {
|
||||||
predicate = \ _ _ -> True,
|
predicate = \ _ _ -> True,
|
||||||
outputName = "base",
|
outputName = "base",
|
||||||
generator = declareBaseStructure
|
generator = declareBaseStructure,
|
||||||
|
testGenerator = Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
declareBaseStructure :: Word -> Gen ()
|
declareBaseStructure :: Word -> Gen ()
|
||||||
|
|||||||
@@ -3,15 +3,20 @@ module BinaryOps(
|
|||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Control.Monad(forM_)
|
import Control.Monad(forM_,replicateM_)
|
||||||
|
import Data.Bits((.&.), (.|.), shiftL, xor)
|
||||||
import File
|
import File
|
||||||
import Gen
|
import Gen
|
||||||
|
|
||||||
|
binaryTestCount :: Int
|
||||||
|
binaryTestCount = 3000
|
||||||
|
|
||||||
binaryOps :: File
|
binaryOps :: File
|
||||||
binaryOps = File {
|
binaryOps = File {
|
||||||
predicate = \ _ _ -> True,
|
predicate = \ _ _ -> True,
|
||||||
outputName = "binary",
|
outputName = "binary",
|
||||||
generator = declareBinaryOperators
|
generator = declareBinaryOperators,
|
||||||
|
testGenerator = Just testVectors
|
||||||
}
|
}
|
||||||
|
|
||||||
declareBinaryOperators :: Word -> Gen ()
|
declareBinaryOperators :: Word -> Gen ()
|
||||||
@@ -51,7 +56,7 @@ declareBinaryOperators bitsize =
|
|||||||
out ("output.value[" ++ show i ++ "] = !self.value[" ++ show i ++ "];")
|
out ("output.value[" ++ show i ++ "] = !self.value[" ++ show i ++ "];")
|
||||||
out "output"
|
out "output"
|
||||||
blank
|
blank
|
||||||
addBinaryLaws name
|
addBinaryLaws name entries
|
||||||
|
|
||||||
generateBinOps :: String -> String -> String -> String -> Word -> Gen ()
|
generateBinOps :: String -> String -> String -> String -> Word -> Gen ()
|
||||||
generateBinOps trait name fun op entries =
|
generateBinOps trait name fun op entries =
|
||||||
@@ -98,8 +103,8 @@ generateBinOpsFromAssigns trait name fun op =
|
|||||||
out ("output " ++ op ++ " rhs;")
|
out ("output " ++ op ++ " rhs;")
|
||||||
out "output"
|
out "output"
|
||||||
|
|
||||||
addBinaryLaws :: String -> Gen ()
|
addBinaryLaws :: String -> Word -> Gen ()
|
||||||
addBinaryLaws name =
|
addBinaryLaws name entries =
|
||||||
do let args3 = "(a: " ++ name ++ ", b: " ++ name ++ ", c: " ++ name ++ ")"
|
do let args3 = "(a: " ++ name ++ ", b: " ++ name ++ ", c: " ++ name ++ ")"
|
||||||
args2 = "(a: " ++ name ++ ", b: " ++ name ++ ")"
|
args2 = "(a: " ++ name ++ ", b: " ++ name ++ ")"
|
||||||
out "#[cfg(test)]"
|
out "#[cfg(test)]"
|
||||||
@@ -143,3 +148,24 @@ addBinaryLaws name =
|
|||||||
blank
|
blank
|
||||||
wrapIndent ("fn or_ident(a: " ++ name ++ ") -> bool") $
|
wrapIndent ("fn or_ident(a: " ++ name ++ ") -> bool") $
|
||||||
out ("(&a | " ++ name ++ "::zero()) == a")
|
out ("(&a | " ++ name ++ "::zero()) == a")
|
||||||
|
wrapIndent ("fn neg_as_xor(a: " ++ name ++ ") -> bool") $
|
||||||
|
do out ("let ones = " ++ name ++ "{ value: [0xFFFFFFFFFFFFFFFFu64; "
|
||||||
|
++ show entries ++ "] };")
|
||||||
|
out ("!&a == (&ones ^ &a)")
|
||||||
|
|
||||||
|
|
||||||
|
testVectors :: Word -> Gen ()
|
||||||
|
testVectors bitsize = replicateM_ binaryTestCount $
|
||||||
|
do a <- newNum False bitsize
|
||||||
|
b <- newNum False bitsize
|
||||||
|
let o = a .|. b
|
||||||
|
c = a .&. b
|
||||||
|
n = a `xor` ((1 `shiftL` fromIntegral bitsize) - 1)
|
||||||
|
x = a `xor` b
|
||||||
|
emitTestVariable 'a' a
|
||||||
|
emitTestVariable 'b' b
|
||||||
|
emitTestVariable 'c' c
|
||||||
|
emitTestVariable 'o' o
|
||||||
|
emitTestVariable 'n' n
|
||||||
|
emitTestVariable 'x' x
|
||||||
|
|
||||||
@@ -9,7 +9,8 @@ comparisons :: File
|
|||||||
comparisons = File {
|
comparisons = File {
|
||||||
predicate = \ _ _ -> True,
|
predicate = \ _ _ -> True,
|
||||||
outputName = "compare",
|
outputName = "compare",
|
||||||
generator = declareComparators
|
generator = declareComparators,
|
||||||
|
testGenerator = Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
declareComparators :: Word -> Gen ()
|
declareComparators :: Word -> Gen ()
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ conversions :: File
|
|||||||
conversions = File {
|
conversions = File {
|
||||||
predicate = \ _ _ -> True,
|
predicate = \ _ _ -> True,
|
||||||
outputName = "conversions",
|
outputName = "conversions",
|
||||||
generator = declareConversions
|
generator = declareConversions,
|
||||||
|
testGenerator = Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
declareConversions :: Word -> Gen ()
|
declareConversions :: Word -> Gen ()
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ cryptoNum :: File
|
|||||||
cryptoNum = File {
|
cryptoNum = File {
|
||||||
predicate = \ _ _ -> True,
|
predicate = \ _ _ -> True,
|
||||||
outputName = "cryptonum",
|
outputName = "cryptonum",
|
||||||
generator = declareCryptoNumInstance
|
generator = declareCryptoNumInstance,
|
||||||
|
testGenerator = Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
declareCryptoNumInstance :: Word -> Gen ()
|
declareCryptoNumInstance :: Word -> Gen ()
|
||||||
@@ -24,7 +25,9 @@ declareCryptoNumInstance bitsize =
|
|||||||
out "#[cfg(test)]"
|
out "#[cfg(test)]"
|
||||||
out "use crate::testing::{build_test_path,run_test};"
|
out "use crate::testing::{build_test_path,run_test};"
|
||||||
out "#[cfg(test)]"
|
out "#[cfg(test)]"
|
||||||
out "use quickcheck::quickcheck;"
|
out "use quickcheck::{Arbitrary,Gen,quickcheck};"
|
||||||
|
out "#[cfg(test)]"
|
||||||
|
out "use std::fmt;"
|
||||||
out ("use super::" ++ name ++ ";")
|
out ("use super::" ++ name ++ ";")
|
||||||
blank
|
blank
|
||||||
implFor "CryptoNum" name $
|
implFor "CryptoNum" name $
|
||||||
@@ -96,14 +99,44 @@ declareCryptoNumInstance bitsize =
|
|||||||
out ("idx -= 1;")
|
out ("idx -= 1;")
|
||||||
out ("bytes[idx] = byte" ++ show (bytes-1) ++ ";")
|
out ("bytes[idx] = byte" ++ show (bytes-1) ++ ";")
|
||||||
blank
|
blank
|
||||||
|
let bytes = bitsize `div` 8
|
||||||
|
struct = "Bytes" ++ show bytes
|
||||||
|
out "#[cfg(test)]"
|
||||||
|
out "#[derive(Clone)]"
|
||||||
|
wrapIndent ("struct " ++ struct) $
|
||||||
|
out ("value: [u8; " ++ show bytes ++ "]")
|
||||||
|
blank
|
||||||
|
out "#[cfg(test)]"
|
||||||
|
implFor "PartialEq" struct $
|
||||||
|
wrapIndent ("fn eq(&self, other: &Self) -> bool") $
|
||||||
|
out "self.value.iter().zip(other.value.iter()).all(|(a,b)| a == b)"
|
||||||
|
blank
|
||||||
|
out "#[cfg(test)]"
|
||||||
|
implFor "fmt::Debug" struct $
|
||||||
|
wrapIndent ("fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result") $
|
||||||
|
out "f.debug_list().entries(self.value.iter()).finish()"
|
||||||
|
blank
|
||||||
|
out "#[cfg(test)]"
|
||||||
|
implFor "Arbitrary" struct $
|
||||||
|
wrapIndent ("fn arbitrary<G: Gen>(g: &mut G) -> Self") $
|
||||||
|
do out ("let mut res = " ++ struct ++ "{ value: [0; " ++ show bytes ++ "] };")
|
||||||
|
out ("g.fill_bytes(&mut res.value);")
|
||||||
|
out ("res")
|
||||||
|
blank
|
||||||
out "#[cfg(test)]"
|
out "#[cfg(test)]"
|
||||||
wrapIndent "quickcheck!" $
|
wrapIndent "quickcheck!" $
|
||||||
do wrapIndent ("fn to_from_ident(x: " ++ name ++ ") -> bool") $
|
do wrapIndent ("fn to_from_ident(x: " ++ name ++ ") -> bool") $
|
||||||
do out ("let mut buffer = [0; " ++ show (bitsize `div` 8) ++ "];")
|
do out ("let mut buffer = [0; " ++ show bytes ++ "];")
|
||||||
out ("x.to_bytes(&mut buffer);");
|
out ("x.to_bytes(&mut buffer);");
|
||||||
out ("let y = " ++ name ++ "::from_bytes(&buffer);")
|
out ("let y = " ++ name ++ "::from_bytes(&buffer);")
|
||||||
out ("x == y")
|
out ("x == y")
|
||||||
blank
|
blank
|
||||||
|
wrapIndent ("fn from_to_ident(x: " ++ struct ++ ") -> bool") $
|
||||||
|
do out ("let val = " ++ name ++ "::from_bytes(&x.value);")
|
||||||
|
out ("let mut buffer = [0; " ++ show bytes ++ "];")
|
||||||
|
out ("val.to_bytes(&mut buffer);")
|
||||||
|
out ("buffer.iter().zip(x.value.iter()).all(|(a,b)| a == b)")
|
||||||
|
blank
|
||||||
out "#[cfg(test)]"
|
out "#[cfg(test)]"
|
||||||
out "#[allow(non_snake_case)]"
|
out "#[allow(non_snake_case)]"
|
||||||
out "#[test]"
|
out "#[test]"
|
||||||
|
|||||||
@@ -2,12 +2,13 @@ module File(
|
|||||||
File(..),
|
File(..),
|
||||||
Task(..),
|
Task(..),
|
||||||
addModuleTasks,
|
addModuleTasks,
|
||||||
makeTask
|
makeTasks
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Control.Monad(forM_)
|
import Control.Monad(forM_)
|
||||||
import Data.Char(toUpper)
|
import Data.Char(toUpper)
|
||||||
|
import Data.List(isPrefixOf)
|
||||||
import qualified Data.Map.Strict as Map
|
import qualified Data.Map.Strict as Map
|
||||||
import Gen(Gen,blank,out)
|
import Gen(Gen,blank,out)
|
||||||
import System.FilePath(takeBaseName,takeDirectory,takeFileName,(</>))
|
import System.FilePath(takeBaseName,takeDirectory,takeFileName,(</>))
|
||||||
@@ -15,7 +16,8 @@ import System.FilePath(takeBaseName,takeDirectory,takeFileName,(</>))
|
|||||||
data File = File {
|
data File = File {
|
||||||
predicate :: Word -> [Word] -> Bool,
|
predicate :: Word -> [Word] -> Bool,
|
||||||
outputName :: FilePath,
|
outputName :: FilePath,
|
||||||
generator :: Word -> Gen ()
|
generator :: Word -> Gen (),
|
||||||
|
testGenerator :: Maybe (Word -> Gen ())
|
||||||
}
|
}
|
||||||
|
|
||||||
data Task = Task {
|
data Task = Task {
|
||||||
@@ -23,26 +25,29 @@ data Task = Task {
|
|||||||
fileGenerator :: Gen ()
|
fileGenerator :: Gen ()
|
||||||
}
|
}
|
||||||
|
|
||||||
makeTask :: FilePath ->
|
makeTasks :: FilePath -> FilePath ->
|
||||||
Word -> [Word] ->
|
Word -> [Word] ->
|
||||||
File ->
|
File ->
|
||||||
Maybe Task
|
[Task]
|
||||||
makeTask base size allSizes file
|
makeTasks srcBase testBase size allSizes file
|
||||||
| predicate file size allSizes =
|
| predicate file size allSizes =
|
||||||
Just Task {
|
let base = Task (srcBase </> ("u" ++ show size) </> outputName file <> ".rs") (generator file size)
|
||||||
outputFile = base </> ("u" ++ show size) </> outputName file <> ".rs",
|
in case testGenerator file of
|
||||||
fileGenerator = generator file size
|
Nothing -> [base]
|
||||||
}
|
Just x ->
|
||||||
| otherwise =
|
[base, Task (testBase </> outputName file </> ("U" ++ show size ++ ".test")) (x size)]
|
||||||
Nothing
|
| otherwise = []
|
||||||
|
|
||||||
addModuleTasks :: FilePath -> [Task] -> [Task]
|
addModuleTasks :: FilePath -> [Task] -> [Task]
|
||||||
addModuleTasks base baseTasks = unsignedTask : (baseTasks ++ moduleTasks)
|
addModuleTasks base baseTasks = unsignedTask : (baseTasks ++ moduleTasks)
|
||||||
where
|
where
|
||||||
moduleMap = foldr addModuleInfo Map.empty baseTasks
|
moduleMap = foldr addModuleInfo Map.empty baseTasks
|
||||||
addModuleInfo task =
|
addModuleInfo task map
|
||||||
|
| base `isPrefixOf` outputFile task =
|
||||||
Map.insertWith (++) (takeDirectory (outputFile task))
|
Map.insertWith (++) (takeDirectory (outputFile task))
|
||||||
[takeBaseName (outputFile task)]
|
[takeBaseName (outputFile task)]
|
||||||
|
map
|
||||||
|
| otherwise = map
|
||||||
moduleTasks = Map.foldrWithKey generateModuleTask [] moduleMap
|
moduleTasks = Map.foldrWithKey generateModuleTask [] moduleMap
|
||||||
generateModuleTask directory mods acc = acc ++ [Task {
|
generateModuleTask directory mods acc = acc ++ [Task {
|
||||||
outputFile = directory </> "mod.rs",
|
outputFile = directory </> "mod.rs",
|
||||||
|
|||||||
@@ -10,14 +10,19 @@ module Gen(
|
|||||||
implFor,
|
implFor,
|
||||||
implFor',
|
implFor',
|
||||||
implFor'',
|
implFor'',
|
||||||
|
newNum,
|
||||||
|
TestVariable(..),
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Control.Monad.RWS.Strict(RWS,evalRWS)
|
import Control.Monad.RWS.Strict(RWS,evalRWS)
|
||||||
import Control.Monad.State.Class(MonadState,get,put)
|
import Control.Monad.State.Class(MonadState,get,put)
|
||||||
import Control.Monad.Writer.Class(MonadWriter,tell)
|
import Control.Monad.Writer.Class(MonadWriter,tell)
|
||||||
|
import Data.Bits(shiftL)
|
||||||
import Data.List(replicate)
|
import Data.List(replicate)
|
||||||
import Data.Word(Word)
|
import Data.Word(Word)
|
||||||
|
import Numeric(showHex)
|
||||||
|
import System.Random(StdGen, newStdGen, random, randomR)
|
||||||
|
|
||||||
newtype Gen a = Gen { unGen :: RWS () String GenState a}
|
newtype Gen a = Gen { unGen :: RWS () String GenState a}
|
||||||
deriving (Applicative, Functor, Monad, MonadState GenState, MonadWriter String)
|
deriving (Applicative, Functor, Monad, MonadState GenState, MonadWriter String)
|
||||||
@@ -27,15 +32,19 @@ tabAmount = 4
|
|||||||
|
|
||||||
data GenState = GenState {
|
data GenState = GenState {
|
||||||
indentAmount :: Word,
|
indentAmount :: Word,
|
||||||
gensymIndex :: Word
|
gensymIndex :: Word,
|
||||||
|
rng :: StdGen
|
||||||
}
|
}
|
||||||
|
|
||||||
initGenState :: GenState
|
initGenState :: IO GenState
|
||||||
initGenState = GenState { indentAmount = 0, gensymIndex = 0 }
|
initGenState =
|
||||||
|
do rng0 <- newStdGen
|
||||||
|
return GenState { indentAmount = 0, gensymIndex = 0, rng = rng0 }
|
||||||
|
|
||||||
runGen :: FilePath -> Gen a -> IO a
|
runGen :: FilePath -> Gen a -> IO a
|
||||||
runGen path action =
|
runGen path action =
|
||||||
do let (res, contents) = evalRWS (unGen action) () initGenState
|
do state0 <- initGenState
|
||||||
|
let (res, contents) = evalRWS (unGen action) () state0
|
||||||
writeFile path contents
|
writeFile path contents
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@@ -86,3 +95,21 @@ implFor' trait name middle =
|
|||||||
implFor'' :: String -> String -> Gen a -> Gen a
|
implFor'' :: String -> String -> Gen a -> Gen a
|
||||||
implFor'' trait name middle =
|
implFor'' trait name middle =
|
||||||
wrapIndent ("impl<'a,'b> " ++ trait ++ " for " ++ name) middle
|
wrapIndent ("impl<'a,'b> " ++ trait ++ " for " ++ name) middle
|
||||||
|
|
||||||
|
newNum :: Bool -> Word -> Gen Integer
|
||||||
|
newNum signed bits =
|
||||||
|
do gs <- get
|
||||||
|
let rng0 = rng gs
|
||||||
|
let high = (1 `shiftL` fromIntegral bits) - 1
|
||||||
|
let (v, rng1) = randomR (0, high) rng0
|
||||||
|
let (sign, rng2) = random rng1
|
||||||
|
let v' = if signed && sign then -v else v
|
||||||
|
put gs{ rng = rng2 }
|
||||||
|
return v'
|
||||||
|
|
||||||
|
class TestVariable a where
|
||||||
|
emitTestVariable :: Char -> a -> Gen ()
|
||||||
|
|
||||||
|
instance TestVariable Integer where
|
||||||
|
emitTestVariable c v =
|
||||||
|
out ([c] ++ ": " ++ showHex v "")
|
||||||
@@ -7,9 +7,8 @@ import Compare(comparisons)
|
|||||||
import Conversions(conversions)
|
import Conversions(conversions)
|
||||||
import CryptoNum(cryptoNum)
|
import CryptoNum(cryptoNum)
|
||||||
import Control.Monad(forM_,unless)
|
import Control.Monad(forM_,unless)
|
||||||
import Data.Maybe(mapMaybe)
|
|
||||||
import Data.Word(Word)
|
import Data.Word(Word)
|
||||||
import File(File,Task(..),addModuleTasks,makeTask)
|
import File(File,Task(..),addModuleTasks,makeTasks)
|
||||||
import Gen(runGen)
|
import Gen(runGen)
|
||||||
import System.Directory(createDirectoryIfMissing)
|
import System.Directory(createDirectoryIfMissing)
|
||||||
import System.Environment(getArgs)
|
import System.Environment(getArgs)
|
||||||
@@ -38,21 +37,24 @@ signedFiles :: [File]
|
|||||||
signedFiles = [
|
signedFiles = [
|
||||||
]
|
]
|
||||||
|
|
||||||
makeTasks :: FilePath -> [File] -> [Task]
|
makeTasks' :: FilePath -> FilePath -> [File] -> [Task]
|
||||||
makeTasks basePath files =
|
makeTasks' srcPath testPath files =
|
||||||
concatMap (\ sz -> mapMaybe (makeTask basePath sz bitsizes) files) bitsizes
|
concatMap (\ sz -> concatMap (makeTasks srcPath testPath sz bitsizes) files) bitsizes
|
||||||
|
|
||||||
makeAllTasks :: FilePath -> [Task]
|
makeAllTasks :: FilePath -> FilePath -> [Task]
|
||||||
makeAllTasks basePath = addModuleTasks basePath $
|
makeAllTasks srcPath testPath = addModuleTasks srcPath $
|
||||||
makeTasks (basePath </> "unsigned") unsignedFiles ++
|
makeTasks' (srcPath </> "unsigned") testPath unsignedFiles ++
|
||||||
makeTasks (basePath </> "signed") signedFiles
|
makeTasks' (srcPath </> "signed") testPath signedFiles
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main =
|
main =
|
||||||
do args <- getArgs
|
do args <- getArgs
|
||||||
unless (length args == 1) $
|
unless (length args == 1) $
|
||||||
die ("generation takes exactly one argument, the target directory")
|
die ("generation takes exactly one argument, the target directory")
|
||||||
let tasks = makeAllTasks (head args)
|
let topLevel = head args
|
||||||
|
srcPath = topLevel </> "src"
|
||||||
|
testPath = topLevel </> "testdata"
|
||||||
|
tasks = makeAllTasks srcPath testPath
|
||||||
total = length tasks
|
total = length tasks
|
||||||
forM_ (zip [(1::Word)..] tasks) $ \ (i, task) ->
|
forM_ (zip [(1::Word)..] tasks) $ \ (i, task) ->
|
||||||
do putStrLn ("[" ++ show i ++ "/" ++ show total ++ "] " ++ outputFile task)
|
do putStrLn ("[" ++ show i ++ "/" ++ show total ++ "] " ++ outputFile task)
|
||||||
|
|||||||
4
generation/src/Testing.hs
Normal file
4
generation/src/Testing.hs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
module Testing(
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
Reference in New Issue
Block a user