{- CAO Compiler
Copyright (C) 2014 Cryptography and Information Security Group, HASLab - INESC TEC and Universidade do Minho
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 3 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 . -}
{-
Module : $Header$
Description : Parsing of the platform configuration.
Copyright : (C) 2014 Cryptography and Information Security Group, HASLab - INESC TEC and Universidade do Minho
License : GPL
Maintainer : Paulo Silva
Stability : experimental
Portability : non-portable
-}
module Language.CAO.Parser.Config ( loadConfig ) where
import Control.Monad
import Data.Array
import Data.Char (isSpace, isAlphaNum, isNumber)
import Data.ConfigFile
import Data.Function (on)
import Data.List(foldl', sortBy)
import Data.Maybe (fromMaybe)
import Language.CAO.Platform.Literals
import Language.CAO.Platform.Specification
import Language.CAO.Platform.Naming
loadConfig :: String -> IO TranslationSpec
loadConfig confFile = do
readStr <- readfile emptyCP confFile
case readStr of
Left e -> configError $ show e
Right cf -> let
gpec = readGlobalSpec cf
spec = emptyTranslationSpec { globalTransSpec = gpec }
fpec = readType cf (defaultSafety gpec)
in return $ foldl' fpec spec (sections cf)
readGlobalSpec :: ConfigParser -> GlobalTransSpec
readGlobalSpec cff = GlobalTransSpec {
initProcName = readOptionDefault word' "initproc"
, disposeProcName = readOptionDefault word' "disposeproc"
, tpPrefix = readOptionDefault word' "typeprefix"
, callPrefix = readOptionDefault word' "callprefix"
, defaultHeader = readOptionDefault word' "header"
, defaultSafety = readOptionDefault parseSafety "safety"
, structFields = readOptionDefault parseFields "fields"
, wordSize = readOptionDefault parseWord "word"}
where
readOptionDefault p = run p . readOption cff "DEFAULT"
readType :: ConfigParser -> SafetyConv -> TranslationSpec -> SectionSpec -> TranslationSpec
readType cff safe tinfo sspec = updateTypes typinfo tinfo nrWords caotypes
where
typinfo = TypeSpec {
nameInPlat = sspec
, headerFile = readOptionS word' "header"
, code = readOptionS word' "code"
, declConv = readOptionS parseCall "declaration"
, memoryConv = readOptionS parseMemory "memory"
, funcCall = readOptionS parseFReturn "return"
, operands = readOptionS parseConsts "operands"
, literal = Nothing
, operations = array (0, 36) (auxOpers opers) }
nrWords = readOptionS parseSize "size"
caotypes = readOptionS parseType "type"
opcall = readOptionS parseOpCall "opcall"
opers = readOptionS (parseOperations opcall (operands typinfo) safe) "operations"
readOptionS p = run p . readOption cff sspec
-- Not very elegant solution
auxOpers = worker 0 . sortBy (compare `on` fst)
worker :: Int -> [(OpCode, (OpReturn, Consts, SafetyConv))] -> [(OpCode, Maybe (OpReturn, Consts, SafetyConv))]
worker 37 [] = []
worker 37 _ = error "Not expected configuration"
worker n [] = (n, Nothing) : worker (n+1) []
worker n l@((i, v):lst) = if n < i then (n, Nothing) : worker (n+1) l else (i, Just v) : worker (n+1) lst
updateTypes :: TypeSpec -> TranslationSpec -> Maybe (NumberOfWords, Maybe WordsPerChunk) -> [(String, Size)] -> TranslationSpec
updateTypes typinfo tspec nrWords = foldl' worker tspec
where
wordSz :: Maybe WordSize
wordSz = wordSize $ globalTransSpec tspec
worker :: TranslationSpec -> (String, Size) -> TranslationSpec
worker ti (typ, siz) = case typ of
"bool" -> tworker ti typ (\ ti' -> ti' { boolT = Just typinfo } ) boolT
"struct" -> tworker ti typ (\ ti' -> ti' { structT = Just typinfo } ) structT
"modpol" -> tworker ti typ (\ ti' -> ti' { modpolT = Just typinfo } ) modpolT
"int" -> tworker ti typ (\ ti' -> ti' { intT = Just $ typinfo { literal = checkInt wordSz nrWords } } ) intT
"rint" -> tworker ti typ (\ ti' -> ti' { rintT = Just $ typinfo { literal = checkInt wordSz nrWords } } ) rintT
"ubits" -> ti { typeTransSpec = (typeTransSpec ti) {
ubitsT = (siz, typinfo { literal = checkBits wordSz siz nrWords }) : ubitsT (typeTransSpec ti) } }
"sbits" -> ti { typeTransSpec = (typeTransSpec ti) {
sbitsT = (siz, typinfo { literal = checkBits wordSz siz nrWords }) : sbitsT (typeTransSpec ti) } }
"vector" -> ti { typeTransSpec = (typeTransSpec ti) {
vectorT = (siz, typinfo) : vectorT (typeTransSpec ti) } }
"matrix" -> ti { typeTransSpec = (typeTransSpec ti) {
matrixT = (siz, typinfo) : matrixT (typeTransSpec ti) } }
"mod" -> ti { typeTransSpec = (typeTransSpec ti) {
modT = (siz, typinfo { literal = checkMod wordSz siz nrWords }) : modT (typeTransSpec ti) } }
_ -> configError $ "Not known type identifier: " ++ typ
tworker :: TranslationSpec -> String -> (TypeTransSpec -> TypeTransSpec) -> (TypeTransSpec -> Maybe TypeSpec) -> TranslationSpec
tworker ti typ f1 sel = maybe
(ti {typeTransSpec = f1 (typeTransSpec ti) })
(configError $ "Configuration already found for type: " ++ typ)
(sel (typeTransSpec ti))
configError :: String -> a
configError err = error $
"[ERROR] There was an error in the configuration file:\n" ++ err
readOption :: ConfigParser -> SectionSpec -> OptionSpec -> String
readOption cff sspec opt =
either (configError . ("Option not found: " ++) . show) id
((get cff sspec opt)::Either CPError String)
--------------------------------------------------------------------------------
parseType :: ReadC [(String, Size)]
parseType = sepBy (comp (,) (word id) (option Generic (brackets tsize))) comma
parseCall :: ReadC VarDeclaration
parseCall
= keyword VarDecl "var"
<|> keyword MacroDecl "macro"
parseMemory :: ReadC VarMemory
parseMemory
= seqOpt (keyword Auto "auto")
(keyword AutoRef "ref")
Auto
<|> keyword Alloc "alloc"
parseFReturn :: ReadC FuncReturn
parseFReturn
= keyword FFuncReturn "val"
<|> keyword FFuncRef "ref"
parseOpCall :: ReadC OpReturn
parseOpCall
= seqOpt (keyword OMacroRef "macro")
(keyword OMacroReturn "val")
OMacroRef
<|> seqOpt (keyword OFuncRef "func")
(keyword OFuncReturn "val")
OFuncRef
parseSafety :: ReadC SafetyConv
parseSafety
= keyword Safe "safe"
<|> keyword Unsafe "unsafe"
<|> keyword ArgSafe "arg_safe"
parseConsts :: ReadC Consts
parseConsts
= keyword GlobalV "vars_global"
<|> keyword LocalV "vars_local"
<|> keyword Inlined "inlined"
<|> keyword Mixed "mixed"
parseFields :: ReadC FieldsConv
parseFields
= keyword GlobalF "global"
<|> keyword InlinedF "inlined"
parseWord :: ReadC (Maybe WordSize)
parseWord
= keyword Nothing "undefined"
<|> apply (Just . fromInteger) number
parseSize :: ReadC (Maybe (NumberOfWords, Maybe WordsPerChunk))
parseSize
= keyword Nothing "undefined"
<|> apply (Just . (\x -> (x, Nothing)) . fromInteger) number
<|> comp (\ _ (n1, n2) -> Just (fromInteger n1, Just $ fromInteger n2))
(keyword () "split")
(pair number number)
parseOperations :: OpReturn -> Consts -> SafetyConv -> ReadC [(OpCode, (OpReturn, Consts, SafetyConv))]
parseOperations defaultRet operand safe =
sepBy (comp (,) parseOperation
(option (defaultRet, operand, safe) (parens $
perm3 comma
parseOpCall
parseConsts
parseSafety
defaultRet operand safe))) comma
parseOperation :: ReadC OpCode
parseOperation = word aux
where
aux w = fromMaybe
(parseError $ "Not expected operation name: `" ++ w ++ "'") $ getCode w
--------------------------------------------------------------------------------
type ReadC a = String -> Either String (String, a)
parseError :: String -> a
parseError err = error $
"[ERROR] There was a parsing error while reading the configuration file:\n" ++
err
run :: ReadC a -> String -> a
run p str = case p str of
Left err -> parseError err
Right (str', v) ->
if null str'
then v
else parseError $ "Trailing string not expected: `" ++ str' ++
"' while reading `" ++ str ++ "'"
sepBy :: ReadC a -> ReadC sep -> ReadC [a]
sepBy p psep str = do
(str', v) <- p str
if null str' then return (str', [v])
else do
(str'', _) <- psep str'
(str''', lv) <- sepBy p psep str''
return (str''', v : lv)
comp :: (a -> b -> c) -> ReadC a -> ReadC b -> ReadC c
comp f p1 p2 str = do
(str1, v1) <- p1 str
(str2, v2) <- p2 str1
return (str2, f v1 v2)
inject :: a -> ReadC a
inject d = \ s -> return (s, d)
(<|>) :: ReadC a -> ReadC a -> ReadC a
(p1 <|> p2) str = p1 str `mplus` p2 str
option :: a -> ReadC a -> ReadC a
option a p str = p str `mplus` return (str, a)
comma :: ReadC ()
comma (',' : str) = white str
comma str = Left $ "Expected comma before `" ++ str ++ "'"
white :: ReadC ()
white str = return (dropWhile isSpace str, ())
delim :: Char -> Char -> ReadC a -> ReadC a
delim co cc p (co' : str) | co == co' = do
(str', _) <- white str
(str'', v) <- p str'
when (null str'' || head str'' /= cc) $ Left $
"Expected delimiters `" ++ [co] ++ "' `" ++ [cc] ++ "' around `" ++
str' ++ "'"
(str''', _) <- white (tail str'')
return (str''', v)
delim co cc _ _ = Left $ "Expected delimiters `" ++ [co] ++ "' `" ++ [cc] ++ "'"
parens :: ReadC a -> ReadC a
parens = delim '(' ')'
brackets :: ReadC a -> ReadC a
brackets = delim '[' ']'
number :: ReadC Integer
number str = do
let (n, str') = span isNumber str
when (null n) $ Left $ "Expected number in `" ++ str ++ "'"
(str'', _) <- white str'
return (str'', (read n::Integer))
keyword :: a -> String -> ReadC a
keyword val key str = do
(str', w) <- word id str
if key == w
then return (str', val)
else Left $
"Expected keyword `" ++ key ++ "'. Got `" ++ w ++ "' instead."
word :: (String -> b) -> ReadC b
word f str = do
let (w, str') = span (\ c -> isAlphaNum c || c == '.' || c == '_') str
(ww, _) <- white str'
return (ww, f w)
word' :: ReadC String
word' = word id
tsize :: ReadC Size
tsize str = do
(str', n1) <- option 0 number str
if n1 == 0
then return (str', Generic)
else if not (null str') && head str' == 'x'
then do
(str'', _) <- white (tail str')
(str''', n2) <- option 0 number str''
if n2 == 0
then Left "Invalid size (0)"
else return (str''', MSize n1 n2)
else return (str', Simple n1)
perm2 :: ReadC () -> ReadC a -> ReadC b -> a -> b -> ReadC (a, b)
perm2 sep p1 p2 d1 d2 =
comp (,) p1 (comp (curry snd) sep p2) <|>
comp (flip (,)) p2 (comp (curry snd) sep p1) <|>
comp (,) p1 (inject d2) <|>
comp (,) (inject d1) p2
perm3 :: ReadC () -> ReadC a -> ReadC b -> ReadC c -> a -> b -> c -> ReadC (a, b, c)
perm3 sep p1 p2 p3 d1 d2 d3 =
comp (\ x (y, z) -> (x, y, z)) p1 (comp (curry snd) sep (perm2 sep p2 p3 d2 d3)) <|>
comp (\ (y, z) x -> (x, y, z)) (perm2 sep p2 p3 d2 d3) (comp (curry snd) sep p1) <|>
comp (\ x (y, z) -> (x, y, z)) p1 (inject (d2, d3)) <|>
comp (\ x (y, z) -> (x, y, z)) (inject d1) (perm2 sep p2 p3 d2 d3)
seqOpt :: ReadC a -> ReadC b -> b -> ReadC b
seqOpt p1 p2 d1 = comp (\_ y -> y) p1 (option d1 (parens p2))
apply :: (a -> b) -> ReadC a -> ReadC b
apply f p str = do
(str', v) <- p str
return (str', f v)
pair :: ReadC a -> ReadC b -> ReadC (a, b)
pair p1 p2 = parens (\ str -> do
(str', v1) <- p1 str
(str'', _) <- comma str'
(str''', v2) <- p2 str''
return (str''', (v1, v2)))