mirror of
https://github.com/macocianradu/RosettaHaskellCompiler.git
synced 2026-03-18 13:00:08 +00:00
211 lines
9.8 KiB
Haskell
211 lines
9.8 KiB
Haskell
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
module Main where
|
|
|
|
import Parser.Enum
|
|
import Parser.Type
|
|
import Parser.Function
|
|
import Parser.General
|
|
import Model.RosettaObject
|
|
import qualified Data.Text as Text
|
|
import Text.Megaparsec
|
|
import PrettyPrinter.Enum
|
|
import PrettyPrinter.Type
|
|
import PrettyPrinter.Function
|
|
import PrettyPrinter.RosettaObject
|
|
import Semantic.TypeChecker
|
|
import Semantic.ExpressionChecker
|
|
import Semantic.FunctionChecker
|
|
import Model.Type
|
|
import Model.Function
|
|
import System.Environment.Blank (getArgs)
|
|
import Model.Enum
|
|
import Data.Either
|
|
import Model.Header
|
|
import Parser.Header
|
|
import PrettyPrinter.Header
|
|
import Data.Tuple (fst, snd)
|
|
import Data.Void
|
|
import System.FilePath
|
|
import Utils.Utils
|
|
import System.Directory
|
|
import Data.List
|
|
import Data.Text (Text)
|
|
import Parser.Expression (expressionParser)
|
|
-- :l resources/Generated/ContractDSL.hs resources/Generated/ImportsType.hs resources/Generated/ImportsEnum.hs
|
|
-- :set args resources/Rosetta/Contracts/
|
|
-- :set args resources/CDM/
|
|
|
|
-- |Reads a rosetta string from the first input argument and writes a haskell output to the file given as a second argument
|
|
main :: IO ()
|
|
main = do
|
|
args <- getArgs
|
|
let mainFolder = head args
|
|
files <- getDirectoryContents mainFolder
|
|
parseResult <- parseFolder [mainFolder ++ f | f <- files, f `notElem` [".", ".."]]
|
|
let completeFiles = [createSymbolTable f parseResult | f <- parseResult]
|
|
let checked = checkObjects completeFiles
|
|
let headers = fstlst checked
|
|
let objects = nestedRights $ sndlst checked
|
|
--_ <- error $ show [(map typeName t, map symbolName s) | (path, t, s, (head, obj)) <- completeFiles, path == "resources/Rosetta/Contracts/contractDSL.rosetta"]
|
|
if null $ lefts $ concat $ sndlst checked
|
|
then
|
|
let input = pairRights checked in
|
|
mapM_ generateFile input
|
|
else error $ show $ lefts $ concat $ sndlst checked
|
|
|
|
{- | Parse the contents of a folder, skip any files without the Rosetta extension
|
|
Return a list of Header, Rosettaobject, filename pairs
|
|
-}
|
|
parseFolder :: [FilePath] -> IO [(FilePath, Header, [RosettaObject])]
|
|
parseFolder [] = return []
|
|
parseFolder (file : files) = do
|
|
plain <- readFile file
|
|
rest <- parseFolder files
|
|
case parseFile plain of
|
|
Left errorBundle -> error $ errorBundlePretty errorBundle ++ " on file " ++ file
|
|
Right (header, objs) ->
|
|
return ((file, header, objs) : rest)
|
|
|
|
-- | Create the symbol table for each file from the imported types enums and functions
|
|
createSymbolTable :: (FilePath, Header, [RosettaObject]) -> [(FilePath, Header, [RosettaObject])] -> (FilePath, [Type], [Symbol], (Header, [RosettaObject]))
|
|
createSymbolTable (file, MakeHeader name desc ver imp, objs) imps =
|
|
(file, concat (fstlst imports), concat (sndlst imports), (MakeHeader name desc ver actualImport, objs))
|
|
where
|
|
imports = [(getTypes imObjs, getFunctions imObjs) | (_, MakeHeader imName _ _ _, imObjs) <- imps, imName `elem` map getNamespace imp || imName == name]
|
|
actualImport =
|
|
[removeChar (takeFileName (dropExtension p)) '-' | (p, MakeHeader imName _ _ _, imObjs) <- imps, imName `elem` map getNamespace imp
|
|
|| (imName == name && p /= file && not ("enum.rosetta" `isSuffixOf` file) && not ("type.rosetta" `isSuffixOf` file && "func.rosetta" `isSuffixOf` p))]
|
|
|
|
-- |Get the defined data types from a list of rosetta objects
|
|
getTypes :: [RosettaObject] -> [Type]
|
|
getTypes [] = []
|
|
getTypes (TypeObject o : os) = o{typeAttributes = map convertAttributeType (typeAttributes o)} : getTypes os
|
|
getTypes (EnumObject o : os) = convertEnumToType o : getTypes os
|
|
getTypes (FunctionObject o : os) = getTypes os
|
|
|
|
convertAttributeType :: TypeAttribute -> TypeAttribute
|
|
convertAttributeType (MakeTypeAttribute n (MakeType "int" _ _ _ _) c d) = MakeTypeAttribute n (BasicType "Integer") c d
|
|
convertAttributeType (MakeTypeAttribute n (MakeType "string" _ _ _ _) c d) = MakeTypeAttribute n (BasicType "String") c d
|
|
convertAttributeType (MakeTypeAttribute n (MakeType "number" _ _ _ _) c d) = MakeTypeAttribute n (BasicType "Double") c d
|
|
convertAttributeType (MakeTypeAttribute n (MakeType "boolean" _ _ _ _) c d) = MakeTypeAttribute n (BasicType "Bool") c d
|
|
convertAttributeType (MakeTypeAttribute n (MakeType "time" _ _ _ _) c d) = MakeTypeAttribute n (BasicType "Time") c d
|
|
convertAttributeType t = t
|
|
|
|
getFunctions :: [RosettaObject] -> [Symbol]
|
|
getFunctions [] = []
|
|
getFunctions (FunctionObject o : os) = functionToSymbol o : getFunctions os
|
|
getFunctions (o : os) = getFunctions os
|
|
|
|
functionToSymbol :: Function -> Symbol
|
|
functionToSymbol (MakeFunction (MakeFunctionSignature name _ inp out) _ _) = Func name [(attributeType (convertAttributeType i), Model.Type.cardinality i)| i <- inp] (attributeType out, Model.Type.cardinality out)
|
|
|
|
{- |Recursively parse a file and all the imports into a list of headers and objects
|
|
The first argument is a list of files already parsed (to handle cyclic imports) and the second is the name of the file to be parsed
|
|
-}
|
|
parseWithImport :: [String] -> String -> IO [(([Type], [Symbol]), (Header, [RosettaObject]))]
|
|
parseWithImport files file
|
|
| file `elem` files = error $ "cyclic dependency on " ++ file
|
|
| otherwise =
|
|
do
|
|
plain <- readFile file
|
|
case parseFile plain of
|
|
Left errorBundle -> error $ errorBundlePretty errorBundle ++ "on file" ++ file
|
|
Right (MakeHeader name desc vers imp, objs) ->
|
|
do
|
|
let modules = map namespaceToName imp
|
|
importFiles <- getFiles modules
|
|
imports <- mapM (parseWithImport (file : files)) importFiles
|
|
let importedSymbolTable = fstlst (concat imports)
|
|
let importedTypes = concat $ fstlst importedSymbolTable
|
|
let importedFunctions = concat $ sndlst importedSymbolTable
|
|
case addNewTypes importedTypes objs of
|
|
Left errors -> error $ show errors ++ "\n on file " ++ file
|
|
Right definedTypes -> case addNewFunctions (definedTypes, importedFunctions) objs of
|
|
Left errors -> error $ show errors ++ "\n on file " ++ file
|
|
Right definedFunctions -> return $ ((definedTypes, definedFunctions), (MakeHeader name desc vers imp, objs)) : concat imports
|
|
|
|
|
|
getFiles :: [String] -> IO [String]
|
|
getFiles [] = return []
|
|
getFiles (n : ns)
|
|
| ".*" `isSuffixOf` n = do
|
|
files <- getDirectoryContents $ takeDirectory n
|
|
rest <- getFiles ns
|
|
return $ [f | f <- files, dropExtension n `isPrefixOf` f] ++ rest
|
|
| otherwise = do
|
|
files <- getFiles ns
|
|
return (n : files)
|
|
|
|
-- |Parse a file into a list of RosettaObjects
|
|
parseFile :: String -> Either (ParseErrorBundle Text Void) (Header, [RosettaObject])
|
|
parseFile plainText = parse rosettaParser "" (Text.pack plainText)
|
|
|
|
-- |Checks all the objects from a list
|
|
checkObjects :: [(FilePath, [Type], [Symbol], (Header, [RosettaObject]))] -> [((FilePath, Header), [Either [TypeCheckError] CheckedRosettaObject])]
|
|
checkObjects [] = []
|
|
checkObjects ((file, definedTypes, definedSymbols, (header, objs)) : rest) = ((file, header), checked) : checkObjects rest
|
|
where
|
|
checked = map (checkObject (definedTypes, definedSymbols)) objs
|
|
|
|
-- |Checks the RosettaObject for type errors
|
|
checkObject :: ([Type], [Symbol]) -> RosettaObject -> Either [TypeCheckError] CheckedRosettaObject
|
|
-- |Checks the type and attributes of a type
|
|
checkObject (definedTypes, _) (TypeObject t) =
|
|
case checkType definedTypes t of
|
|
Left errors -> Left errors
|
|
Right typ -> Right $ CheckedTypeObject typ
|
|
-- |If an enum parses, it cannot throw an error
|
|
checkObject _ (EnumObject e) = Right $ CheckedEnumObject e
|
|
-- |Checks the function inputs, output and assignment
|
|
checkObject (definedTypes, definedFunctions) (FunctionObject fun) =
|
|
case checkFunction (definedTypes, defaultMap ++ definedFunctions) fun of
|
|
Left errors -> Left errors
|
|
Right func -> Right $ CheckedFunctionObject func
|
|
|
|
-- |Adds new defined functions into the symbol table
|
|
addNewFunctions :: ([Type], [Symbol]) -> [RosettaObject] -> Either [TypeCheckError] [Symbol]
|
|
addNewFunctions (_, s) [] = Right []
|
|
addNewFunctions (t, s) ((FunctionObject f):os) =
|
|
case addNewFunctions (t, s) os of
|
|
Left errors -> Left errors
|
|
Right symbs -> addFunction (t, symbs) f
|
|
addNewFunctions (t, s) (_:os) = addNewFunctions (t, s) os
|
|
|
|
-- |Adds new defined types into the symbol table
|
|
addNewTypes :: [Type] -> [RosettaObject] -> Either [TypeCheckError] [Type]
|
|
addNewTypes l [] = Right l
|
|
addNewTypes defined (TypeObject o: os) =
|
|
case addNewTypes defined os of
|
|
Left errors -> Left errors
|
|
Right types -> addDefinedTypes types [o]
|
|
addNewTypes defined (EnumObject e: os) = addNewTypes defined (TypeObject (convertEnumToType e) : os)
|
|
addNewTypes defined (_ :os) = addNewTypes defined os
|
|
|
|
-- |Parses any supported Rosetta types into a list of RosettaObject
|
|
rosettaParser :: Parser (Header, [RosettaObject])
|
|
rosettaParser = do
|
|
header <- headerParser
|
|
objects <- many (try parseEnum <|> try parseType <|> try parseFunction) <* eof
|
|
return (header, objects)
|
|
|
|
-- |Reads an enum into a RosettaObject
|
|
parseEnum :: Parser RosettaObject
|
|
parseEnum = do
|
|
EnumObject <$> enumParser
|
|
|
|
-- |Parse a type into a RosettaObject
|
|
parseType :: Parser RosettaObject
|
|
parseType = do
|
|
TypeObject <$> typeParser
|
|
|
|
-- |Parse a function into a RosettaObject
|
|
parseFunction :: Parser RosettaObject
|
|
parseFunction = do
|
|
FunctionObject <$> functionParser
|
|
|
|
-- |Generate a new haskell file based on the rosetta objects and header
|
|
generateFile :: ((FilePath, Header), [CheckedRosettaObject]) -> IO ()
|
|
generateFile ((path, header), objects) =
|
|
writeFile (haskellFileName $ dropExtension $ takeFileName path)
|
|
(printHeader (dropExtension $ takeFileName path) header ++ concatMap printRosettaObject objects) |