diff --git a/book.toml b/book.toml index c40f347..9725d91 100644 --- a/book.toml +++ b/book.toml @@ -1,7 +1,7 @@ [book] -title = "Koaky (Yet Another Programming Language)" +title = "Leviator (Yet Another Programming Language)" authors = ["@Saverio976", "@TTENSHII", "@guillaumeAbel"] -description = "Documentation of Koaky" +description = "Documentation of Leviator" src = "docs" [build] @@ -10,8 +10,8 @@ create-missing = false use-default-preprocessors = true [output.html] -git-repository-url = "https://github.com/X-R-G-B/koak" -edit-url-template = "https://github.com/X-R-G-B/koak/edit/main/{path}" +git-repository-url = "https://github.com/X-R-G-B/Leviator" +edit-url-template = "https://github.com/X-R-G-B/Leviator/edit/main/{path}" [output.html.search] enable = true diff --git a/docs/BNF.md b/docs/BNF.md index 1434673..fa144da 100644 --- a/docs/BNF.md +++ b/docs/BNF.md @@ -27,7 +27,7 @@ ::= ": " ::= ::= ";\n" - ::= | | | | + ::= | | | | | ::= "@" " " " = " ::= " = " ::= "(" * ")" @@ -39,6 +39,7 @@ ::= ::= "if (" ")\n{\n" * "}\n" ::= "else\n{\n" * "}\n" + ::= "while (" ")\n{\n" * "}\n" ::= "'" "'" ::= "True" | "False" diff --git a/lvtc/app/Args.hs b/lvtc/app/Args.hs index 5d40246..56ae89a 100644 --- a/lvtc/app/Args.hs +++ b/lvtc/app/Args.hs @@ -22,7 +22,8 @@ data Args = Args { action :: Action, folderPath :: String, outFile :: String, - verbose :: Bool + verbose :: Bool, + folders :: [String] } parseArgs' :: [String] -> Args -> Either Args String @@ -42,6 +43,10 @@ parseArgs' ["-o"] _ = Right "Missing argument for -o" parseArgs' ("--verbose":xs) args = parseArgs' xs (args {verbose = True}) +parseArgs' ("--lib":x:xs) args = + parseArgs' xs (args {folders = x : folders args}) +parseArgs' ("-l":x:xs) args = + parseArgs' xs (args {folders = x : folders args}) parseArgs' (('-':xs):_) _ = Right ("Unknown option: " ++ xs) parseArgs' (x:xs) args = @@ -51,7 +56,8 @@ parseArgs :: [String] -> IO (Either Args String) parseArgs args = getCurrentDirectory >>= \path -> return (parseArgs' args (Args { - action = Run, folderPath = path, outFile = "out.wasm", verbose = False + action = Run, folderPath = path, outFile = "out.wasm", + verbose = False, folders = [] })) hLine1 :: String @@ -77,9 +83,11 @@ hLine9 = part1 ++ part2 part2 = " source code recursively from FOLDER\n" hLine10 :: String hLine10 = "\t--verbose\n\t\tVerbose mode\n" +hLine11 :: String +hLine11 = "\t-l, --lib\n\t\tAdd folder to compiled Leviator source code\n" printHelp :: IO () printHelp = putStr hLine1 >> putStr hLine2 >> putStr hLine3 >> putStr hLine4 >> putStr hLine5 >> putStr hLine6 >> putStr hLine7 >> putStr hLine8 - >> putStr hLine9 >> putStr hLine10 + >> putStr hLine9 >> putStr hLine10 >> putStr hLine11 diff --git a/lvtc/app/Main.hs b/lvtc/app/Main.hs index cb24862..0f41aca 100644 --- a/lvtc/app/Main.hs +++ b/lvtc/app/Main.hs @@ -14,9 +14,9 @@ import Run (run) import Version (printVersion) dispatchArgs :: Args -> IO () -dispatchArgs (Args Run fPath oFile v) = run (Args Run fPath oFile v) -dispatchArgs (Args ShowHelp _ _ _) = printHelp -dispatchArgs (Args ShowVersion _ _ _) = printVersion +dispatchArgs (Args Run fPath oFile v fs) = run (Args Run fPath oFile v fs) +dispatchArgs (Args ShowHelp _ _ _ _) = printHelp +dispatchArgs (Args ShowVersion _ _ _ _) = printVersion dispatchIfOk :: Either Args String -> IO () dispatchIfOk (Left args) = dispatchArgs args diff --git a/lvtc/app/Run.hs b/lvtc/app/Run.hs index 1a454fe..5efb4b8 100644 --- a/lvtc/app/Run.hs +++ b/lvtc/app/Run.hs @@ -21,6 +21,7 @@ import WatLikeToWat (watsLikeToWat) import Wasm (Wasm) import WatToWasm (watToWasm) import WriteWasm (writeWasm) +import TypeCheck (typeCheck) import Args import System.Directory (listDirectory) @@ -67,6 +68,13 @@ listAllFiles v path = p True = putStrLn ("Compiling Folder: " ++ show path) p False = return () +listsAllFiles :: Bool -> [FilePath] -> IO [FilePath] +listsAllFiles _ [] = return [] +listsAllFiles v (f:fs) = + listAllFiles v f + >>= (\files -> listsAllFiles v fs + >>= (\others -> return (files ++ others))) + getAllFunc :: Bool -> [Expression] -> IO [FuncDeclaration] getAllFunc _ [] = return [] getAllFunc v ((Expression.Function str):expressions) = @@ -83,9 +91,9 @@ getAllFunc v (x : expressions) = p v >> getAllFunc v expressions checkAst :: Bool -> IO [FuncDeclaration] -> IO [FuncDeclaration] checkAst _ funcsIo = funcsIo - >>= (\funcs -> case Just funcs of - Just f -> return f - Nothing -> fail "Invalid Code") + >>= (\funcs -> case typeCheck funcs of + True -> return funcs + False -> fail "Invalid Code") transformToWatLike :: Bool -> IO [FuncDeclaration] -> IO [FuncDeclare] transformToWatLike v funcsIo = @@ -119,10 +127,10 @@ showDebug True wasm = print wasm showDebug False _ = return () run :: Args -> IO () -run (Args Run fPath oFile v) = +run (Args Run fPath oFile v fPaths) = transformedWasm >>= \wasm -> (showDebug v wasm >> writeWasm wasm oFile) where - expressions = listAllFiles v fPath >>= getFilesExpression v + expressions = listsAllFiles v (fPath:fPaths) >>= getFilesExpression v funcs = expressions >>= getAllFunc v transformedWatLike = transformToWatLike v (checkAst v funcs) transformedWat = transformToWat v transformedWatLike diff --git a/lvtc/lvtc.cabal b/lvtc/lvtc.cabal index 275f994..bf73442 100644 --- a/lvtc/lvtc.cabal +++ b/lvtc/lvtc.cabal @@ -37,6 +37,7 @@ library Parser ParseUtil ShuntingYard + TypeCheck Wasm WasmUtils WatAST @@ -82,6 +83,7 @@ test-suite lvtc-test UTLexeme UTParseLvt UTShuntingYard + UTTypeCheck UTWasm UTWat UTWatLike diff --git a/lvtc/src/AST.hs b/lvtc/src/AST.hs index 16a6e80..bea228b 100644 --- a/lvtc/src/AST.hs +++ b/lvtc/src/AST.hs @@ -9,6 +9,7 @@ module AST ( Type , Value (..) , Var + , Symbol , FuncCall , FuncPrototype , FuncDeclaration @@ -16,9 +17,9 @@ module AST , VarDeclaration , VarAssignation , Condition - , Symbol - , IsFuncExport - ) where + , Export + , WhileBlock +) where import Data.Int (Int32) @@ -58,9 +59,9 @@ instance Eq Value where type Var = (Symbol, Type) -type IsFuncExport = Bool +type Export = Bool -type FuncPrototype = (IsFuncExport, Symbol, [Var], Type) +type FuncPrototype = (Export, Symbol, [Var], Type) type FuncDeclaration = (FuncPrototype, [Instruction]) @@ -77,12 +78,15 @@ type VarDeclaration = (Var, Value) type VarAssignation = (Symbol, Value) +type WhileBlock = (Value, [Instruction]) + data Instruction = Function FuncCall | Return Value | Declaration VarDeclaration | Assignation VarAssignation | Cond Condition + | While WhileBlock instance Show Instruction where show (Function x) = @@ -95,6 +99,8 @@ instance Show Instruction where "Assignation[< " ++ show x ++ " >]" show (Cond x) = "Cond[< " ++ show x ++ " >]" + show (While x) = + "While[< " ++ show x ++ " >]" instance Eq Instruction where (==) (Function x) (Function y) = x == y diff --git a/lvtc/src/Lexeme.hs b/lvtc/src/Lexeme.hs index f102d87..fe8a845 100644 --- a/lvtc/src/Lexeme.hs +++ b/lvtc/src/Lexeme.hs @@ -13,6 +13,7 @@ module Lexeme replaceN :: Int -> String -> String replaceN _ [] = [] replaceN 0 ('"':xs) = '"' : replaceN 1 xs +replaceN 1 ('\\':'0':xs) = '\0' : replaceN 1 xs replaceN 1 ('\\':'n':xs) = '\n' : replaceN 1 xs replaceN 1 ('\\':'t':xs) = '\t' : replaceN 1 xs replaceN 1 ('\\':'v':xs) = '\v' : replaceN 1 xs @@ -66,6 +67,8 @@ lexeme 0 ('i':'f':' ':xs) = lexeme 0 ('i':'f':xs) lexeme 0 (' ':'i':'f':xs) = lexeme 0 ('i':'f':xs) lexeme 0 ('e':'l':'s':'e':' ':xs) = lexeme 0 ('e':'l':'s':'e':xs) lexeme 0 (' ':'e':'l':'s':'e':xs) = lexeme 0 ('e':'l':'s':'e':xs) +lexeme 0 ('w':'h':'i':'l':'e':' ':xs) = lexeme 0 ('w':'h':'i':'l':'e':xs) +lexeme 0 (' ':'w':'h':'i':'l':'e':xs) = lexeme 0 ('w':'h':'i':'l':'e':xs) lexeme 0 ('\\':x:xs) = x : lexeme 0 xs lexeme 1 ('\\':x:xs) = x : lexeme 1 xs lexeme 0 (' ':' ':xs) = lexeme 0 (' ':xs) diff --git a/lvtc/src/ParseLvt.hs b/lvtc/src/ParseLvt.hs index c93fa17..8899fba 100644 --- a/lvtc/src/ParseLvt.hs +++ b/lvtc/src/ParseLvt.hs @@ -24,6 +24,7 @@ module ParseLvt parseDeclaration, parseAssignation, parseCond, + parseWhile, -- Function parseFuncDeclaration ) where @@ -94,8 +95,8 @@ parseOperatorOp = Var <$> (parseString "+" <|> parseString "-" <|> parseString "*" <|> parseString "/" <|> parseString "{" <|> parseString "}" - <|> parseString "==" <|> parseString "!=" <|> parseString "<" - <|> parseString ">" <|> parseString "<=" <|> parseString ">=") + <|> parseString "==" <|> parseString "!=" <|> parseString "<=" + <|> parseString ">=" <|> parseString "<" <|> parseString ">") parseOperator' :: ShuntingYardState -> Parser ShuntingYardState parseOperator' sys = @@ -269,9 +270,19 @@ parseCond = Parser f Nothing -> Nothing Just (ifBlock, ys) -> runParser (parseCond' val ifBlock) ys +parseWhileComp :: Parser Value +parseWhileComp = parseString "while(" *> parseValue <* parseString ")" + +parseWhileBlock :: Parser [Instruction] +parseWhileBlock = parseString "{" *> parseInstructions <* parseString "}" + +parseWhile :: Parser Instruction +parseWhile = While <$> ((,) <$> parseWhileComp <*> parseWhileBlock) + parseInstruction :: Parser Instruction parseInstruction = (parseCond + <|> parseWhile <|> parseReturn <|> parseDeclaration <|> parseAssignation @@ -299,7 +310,7 @@ parseFuncVars = <|> parseFuncVar) <* parseChar ')' -parseFuncName :: Parser (IsFuncExport, Symbol) +parseFuncName :: Parser (Export, Symbol) parseFuncName = ((\x -> (True, x)) <$> (parseString "export fn " *> parseVarName)) <|> ((\x -> (False, x)) <$> (parseString "fn " *> parseVarName)) diff --git a/lvtc/src/TypeCheck.hs b/lvtc/src/TypeCheck.hs new file mode 100644 index 0000000..6300c87 --- /dev/null +++ b/lvtc/src/TypeCheck.hs @@ -0,0 +1,220 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator compiler +-- File description: +-- Type checker +-} + +module TypeCheck + ( typeCheck + ) where + +import AST + +data Env = Env FuncPrototype [VarAssignation] [FuncPrototype] + +findVarInParams :: Symbol -> [Var] -> Env -> Maybe Type +findVarInParams _ [] _ = Nothing +findVarInParams s ((s', t):xs) env + | s == s' = Just t + | otherwise = findVarInParams s xs env + +findVarInVar :: Symbol -> [VarAssignation] -> Env -> Maybe Type +findVarInVar _ [] _ = Nothing +findVarInVar s ((s', v):xs) env + | s == s' = findValueType v env + | otherwise = findVarInVar s xs env + +findTypeReturnFunc :: Symbol -> Env -> Maybe FuncPrototype +findTypeReturnFunc s (Env (e, s', ps, t) _ _) | s == s' = Just (e, s', ps, t) +findTypeReturnFunc _ (Env _ _ []) = Nothing +findTypeReturnFunc s (Env proto vars ((e, s', ps, t):xs)) + | s == s' = Just (e, s', ps, t) + | otherwise = findTypeReturnFunc s (Env proto vars xs) + +getValueType :: Value -> Maybe Type +getValueType (Integer _) = Just "Int" +getValueType (Boolean _) = Just "Bool" +getValueType (StringView _) = Just "String" +getValueType _ = Nothing + +checkFuncType :: [Value] -> Env -> Maybe FuncPrototype -> Maybe Type +checkFuncType _ _ Nothing = Nothing +checkFuncType callParams (Env proto params env) (Just (_, _, ps, t)) + | checkVarsTypes callParams (Just ps) (Env proto params env) = Just t + | otherwise = Nothing + +handleFinders :: Maybe Type -> Symbol -> Env -> Maybe Type +handleFinders _ _ (Env (_, _, [], _) [] _) = Nothing +handleFinders Nothing s (Env proto vars env) = + findVarInVar s vars (Env proto vars env) +handleFinders (Just t) _ _ = Just t + +findValueType :: Value -> Env -> Maybe Type +findValueType (Var s) (Env (e, ps, params, t) vars env) = + handleFinders (findVarInParams s params (Env (e, ps, params, t) vars env)) + s (Env (e, ps, params, t) vars env) +findValueType (FuncValue (s, vs)) env = + checkFuncType vs env (findTypeReturnFunc s env) +findValueType v _ = getValueType v + +checkValueType :: Type -> Maybe Type -> Bool +checkValueType _ Nothing = False +checkValueType t (Just t') = t == t' + +assertTypeAndValue :: Type -> Value -> Env -> Bool +assertTypeAndValue _ (Var _) (Env (_, _, [], _) [] _) = False +assertTypeAndValue t v env = checkValueType t (findValueType v env) + +assertMTypeAndValue :: Maybe Type -> Value -> Env -> Bool +assertMTypeAndValue Nothing _ _ = False +assertMTypeAndValue (Just t) v env = assertTypeAndValue t v env + +isTypeValid :: Type -> Bool +isTypeValid "Int" = True +isTypeValid "Bool" = True +isTypeValid _ = False + +checkVarsTypes :: [Value] -> Maybe [Var] -> Env -> Bool +checkVarsTypes _ Nothing _ = False +checkVarsTypes [] (Just []) _ = True +checkVarsTypes values (Just param) _ | length values /= length param = False +checkVarsTypes [v] (Just [(_, t)]) env = assertTypeAndValue t v env +checkVarsTypes (v:vs) (Just ((_, t):xs)) env + | assertTypeAndValue t v env = checkVarsTypes vs (Just xs) env + | otherwise = False +checkVarsTypes _ _ _ = False + +findFunc :: Symbol -> [FuncPrototype] -> Maybe [Var] +findFunc _ [] = Nothing +findFunc s ((_, s', params, _):_) | s == s' = Just params +findFunc s (_:xs) = findFunc s xs + +checkCall :: FuncCall -> Env -> [Instruction] -> Bool +checkCall (symbol, values) (Env (e, s, params, t) vars env) xs + | s == symbol && + checkVarsTypes values (Just params) (Env (e, s, params, t) vars env) || + checkVarsTypes values (findFunc symbol env) + (Env (e, s, params, t) vars env) = + checkInstructions xs (Env (e, s, params, t) vars env) + | otherwise = False + +checkReturn :: Value -> Env -> [Instruction] -> Bool +checkReturn v (Env (e, s, params, t) vars env) xs + | assertTypeAndValue t v (Env (e, s, params, t) vars env) = + checkInstructions xs (Env (e, s, params, t) vars env) +checkReturn _ _ _ = False + +findInVar :: Symbol -> [Var] -> Bool +findInVar _ [] = False +findInVar s ((s', _):_) | s == s' = True +findInVar s (_:xs) = findInVar s xs + +searchName :: Symbol -> [VarAssignation] -> [Var] -> Bool +searchName s [] vars = findInVar s vars +searchName s ((s', _):_) _ | s == s' = True +searchName s (_:xs) params = searchName s xs params + +nameExist :: Symbol -> FuncPrototype -> [VarAssignation] -> Bool +nameExist s (_, s', _, _) _ | s == s' = True +nameExist s (_, _, params, _) vs = searchName s vs params + +checkDeclaration :: VarDeclaration -> Env -> [Instruction] -> Bool +checkDeclaration ((s, t), v) (Env proto vars env) xs + | not (nameExist s proto vars) && + assertTypeAndValue t v (Env proto vars env) = + checkInstructions xs (Env proto ((s, v):vars) env) +checkDeclaration _ _ _ = False + +checkAssignation :: VarAssignation -> Env -> [Instruction] -> Bool +checkAssignation (s, v) env xs | + assertMTypeAndValue (findValueType (Var s) env) v env = + checkInstructions xs env +checkAssignation _ _ _ = False + +checkCondition :: Condition -> Env -> [Instruction] -> Bool +checkCondition (v, fst_, snd_) env xs + | assertTypeAndValue "Bool" v env && checkInstructions fst_ env && + checkInstructions snd_ env = checkInstructions xs env +checkCondition _ _ _ = False + +checkWhile :: WhileBlock -> Env -> [Instruction] -> Bool +checkWhile (v, instructions) env _ | assertTypeAndValue "Bool" v env = + checkInstructions instructions env +checkWhile _ _ _ = False + +checkInstructions :: [Instruction] -> Env -> Bool +checkInstructions [] _ = True +checkInstructions ((Function func):xs) env = checkCall func env xs +checkInstructions ((Return ret):xs) env = checkReturn ret env xs +checkInstructions ((Declaration declaration):xs) env = + checkDeclaration declaration env xs +checkInstructions ((Assignation assignation):xs) env = + checkAssignation assignation env xs +checkInstructions ((Cond condition):xs) env = checkCondition condition env xs +checkInstructions ((While while):xs) env = checkWhile while env xs + +checkVarTypes :: [Var] -> Bool +checkVarTypes [] = True +checkVarTypes [x] = isTypeValid (snd x) +checkVarTypes (x:xs) | isTypeValid (snd x) = checkVarTypes xs +checkVarTypes _ = False + +checkFunction :: FuncDeclaration -> [FuncPrototype] -> Bool +checkFunction ((_, _, args, _), _) _ | not (checkVarTypes args) = False +checkFunction ((_, _, _, t), _) _ | not (isTypeValid t) = False +checkFunction (prototype, instructions) env + | checkInstructions instructions (Env prototype [] env) = True +checkFunction _ _ = False + +checkNotExisting :: FuncDeclaration -> [FuncPrototype] -> Bool +checkNotExisting _ [] = True +checkNotExisting ((e, s, args, t), instr) ((_, ls, _, _):xs) + | s == ls = False + | otherwise = checkNotExisting ((e, s, args, t), instr) xs + +receiveCheckFuncRes :: Bool -> [FuncDeclaration] -> FuncDeclaration -> [FuncPrototype] -> Bool +receiveCheckFuncRes True xs (prototype, _) env = + checkDeclarations xs (Just (prototype:env)) +receiveCheckFuncRes _ _ _ _ = False + +checkDeclarations :: [FuncDeclaration] -> Maybe [FuncPrototype] -> Bool +checkDeclarations _ Nothing = False +checkDeclarations [] (Just _) = True +checkDeclarations (func:xs) (Just env) + | checkNotExisting func env = + receiveCheckFuncRes (checkFunction func env) xs func env + | otherwise = False + +createCalcPrototype :: Symbol -> Type -> FuncPrototype +createCalcPrototype s t = (False, s, [("x", t), ("y", t)], "Int") + +createCompPrototype :: Symbol -> Type -> FuncPrototype +createCompPrototype s t = (False, s, [("x", t), ("y", t)], "Bool") + +createCompPolyMorph :: Symbol -> [Type] -> [FuncPrototype] +createCompPolyMorph _ [] = [] +createCompPolyMorph s (x:xs) = + createCompPrototype s x : createCompPolyMorph s xs + +createCompOp :: [Symbol] -> [FuncPrototype] +createCompOp [] = [] +createCompOp (x:xs) = createCompPolyMorph x ["Int", "Bool"] ++ createCompOp xs + +createCalcOp :: [Symbol] -> [FuncPrototype] +createCalcOp [] = [] +createCalcOp (x:xs) = createCalcPrototype x "Int" : createCalcOp xs + +defaultEnv :: Maybe [FuncPrototype] +defaultEnv = Just (createCalcOp ["+", "-", "*", "%", "/"] ++ + createCompOp ["==", "!=", "<", ">", "<=", ">="]) + +checkStart :: [FuncDeclaration] -> Bool +checkStart (((True, "start", _, _), _):_) = True +checkStart (((_, _, _, _), _):xs) = checkStart xs +checkStart [] = False + +typeCheck :: [FuncDeclaration] -> Bool +typeCheck expressions | checkStart expressions = + checkDeclarations expressions defaultEnv +typeCheck _ = False diff --git a/lvtc/src/WasmUtils.hs b/lvtc/src/WasmUtils.hs index 07be5e2..f8fd87b 100644 --- a/lvtc/src/WasmUtils.hs +++ b/lvtc/src/WasmUtils.hs @@ -181,6 +181,8 @@ getSizeOpCode (LocalSet _) = 2 getSizeOpCode (I32Const _) = 2 getSizeOpCode (Call _) = 2 getSizeOpCode (If _) = 2 +getSizeOpCode (Loop _) = 2 +getSizeOpCode (Br _) = 2 getSizeOpCode _ = 1 fillBlankCodeSectionCode :: CodeSectionCode -> CodeSectionCode @@ -254,6 +256,8 @@ opCodeByte (Call _) = 0x10 opCodeByte (If EmptyType) = 0x04 opCodeByte Else = 0x05 opCodeByte End = 0x0b +opCodeByte (Loop EmptyType) = 0x03 +opCodeByte (Br _) = 0x0c ifTypeByte :: IfType -> Int ifTypeByte EmptyType = 0x40 diff --git a/lvtc/src/WatAST.hs b/lvtc/src/WatAST.hs index 91b353b..98cee4f 100644 --- a/lvtc/src/WatAST.hs +++ b/lvtc/src/WatAST.hs @@ -41,6 +41,8 @@ data OpCode = | If IfType | Else | End + | Loop IfType + | Br Int32 deriving (Show, Eq) data Type = diff --git a/lvtc/src/WatLike.hs b/lvtc/src/WatLike.hs index 0306fc3..2c9d5b8 100644 --- a/lvtc/src/WatLike.hs +++ b/lvtc/src/WatLike.hs @@ -74,6 +74,12 @@ modifyAll' x varsIndex funcsIndex = (x, varsIndex, funcsIndex) --- +transformType :: Type -> Type +transformType "Void" = "Int" +transformType "Char" = "Int" +transformType "Bool" = "Int" +transformType x = x + modifyAll :: [Instruction] -> [Index] -> [Index] -> ([Instruction], [Index], [Index]) modifyAll [] varsIndex funcsIndex = ([], varsIndex, funcsIndex) @@ -119,12 +125,13 @@ modifyAll ((Cond (vValue, insIf, insElse)):xs) vsInd fsInd = (insElse', vsInd''', fsInd''') = modifyAll insElse vsInd'' fsInd'' newCond = Cond (vValue', insIf', insElse') (ins', vsInd'''', fsInd'''') = modifyAll xs vsInd''' fsInd''' - -transformType :: Type -> Type -transformType "Void" = "Int" -transformType "Char" = "Int" -transformType "Bool" = "Int" -transformType x = x +modifyAll ((While (vValue, ins)):xs) vsInd fsInd = + (newWhile:ins', vsInd''', fsInd''') + where + (vValue', vsInd', fsInd') = modifyAll' vValue vsInd fsInd + (insWhile, vsInd'', fsInd'') = modifyAll ins vsInd' fsInd' + newWhile = While (vValue', insWhile) + (ins', vsInd''', fsInd''') = modifyAll xs vsInd'' fsInd'' registerParams :: FuncDeclare -> FuncDeclare registerParams (((isExp, fName, [], typ), ins), varsIndex, oName) = @@ -237,7 +244,7 @@ instructionToWatLike (varsIndex', ins' ++ [newDeclaration]) where (varsIndex', ins', vValue') = valueToWatLike vValue oldFuncs varsIndex - newDeclaration = Declaration ((vName, vTyp), vValue') + newDeclaration = Declaration ((vName, transformType vTyp), vValue') instructionToWatLike (Assignation (vName, vValue)) oldFuncs varsIndex = (varsIndex', ins' ++ [newAssignation]) @@ -266,6 +273,13 @@ instructionToWatLike (vsInd''', vInsFalse') = instructionsToWatLike vInsFalse oldFuncs vsInd'' newCond = Cond (vValCond', vInsTrue', vInsFalse') +instructionToWatLike + (While (vValCond, vIns)) oldFuncs vsInd = + (vsInd'', insCond ++ [newWhile]) + where + (vsInd', insCond, vValCond') = valueToWatLike vValCond oldFuncs vsInd + (vsInd'', vIns') = instructionsToWatLike vIns oldFuncs vsInd' + newWhile = While (vValCond', vIns' ++ insCond) instructionsToWatLike :: [Instruction] -> ([FuncDeclare], [Index]) -> [Index] -> ([Index], [Instruction]) diff --git a/lvtc/src/WatLikeToWat.hs b/lvtc/src/WatLikeToWat.hs index 5279a04..e0ce686 100644 --- a/lvtc/src/WatLikeToWat.hs +++ b/lvtc/src/WatLikeToWat.hs @@ -39,6 +39,8 @@ findTypeFromInstructions name ((Declaration ((name', typ), _)):xs) | otherwise = findTypeFromInstructions name xs findTypeFromInstructions name ((Cond (_, insIf, insElse)):xs) = findTypeFromInstructions name (insIf ++ insElse ++ xs) +findTypeFromInstructions name ((While (_, ins)):xs) = + findTypeFromInstructions name (ins ++ xs) findTypeFromInstructions name (_:xs) = findTypeFromInstructions name xs varsToDecl :: [Index] -> [Instruction] -> [Var] -> [(WatAST.Type, Int32)] @@ -76,43 +78,47 @@ valueToWat _ = error "value not supported" valuesToWat :: [Value] -> [OpCode] valuesToWat = concatMap valueToWat -instructionToWat :: Instruction -> [OpCode] -instructionToWat (AST.Return (Var indexName)) = +instructionToWat :: Instruction -> Int32 -> [OpCode] +instructionToWat (AST.Return (Var indexName)) _ = [ LocalGet (read indexName :: Int32) , WatAST.Return ] -instructionToWat (AST.Return _) = error "Return need a var" -instructionToWat (Declaration ((indexName, _), val)) = +instructionToWat (AST.Return _) _ = error "Return need a var" +instructionToWat (Declaration ((indexName, _), val)) _ = valueToWat val - ++ [ - LocalSet (read indexName :: Int32) - ] -instructionToWat (Assignation (indexName, val)) = + ++ [ LocalSet (read indexName :: Int32) ] +instructionToWat (Assignation (indexName, val)) _ = valueToWat val - ++ [ - LocalSet (read indexName :: Int32) - ] -instructionToWat (Function (indexName, values)) = + ++ [ LocalSet (read indexName :: Int32) ] +instructionToWat (Function (indexName, values)) _ = valuesToWat values - ++ [ - Call (read indexName :: Int32) - ] -instructionToWat (Cond (value, ifTrue, [])) = + ++ [ Call (read indexName :: Int32) ] +instructionToWat (Cond (value, ifTrue, [])) n = + valueToWat value + ++ [ If EmptyType ] ++ ins ++ [ End ] + where + ins = instructionsToWat ifTrue (n + 1) +instructionToWat (Cond (value, ifTrue, ifFalse)) n = valueToWat value - ++ [ If EmptyType ] - ++ instructionsToWat ifTrue - ++ [ End ] -instructionToWat (Cond (value, ifTrue, ifFalse)) = + ++ [ If EmptyType ] ++ insT ++ [ Else ] ++ insF ++ [ End ] + where + insT = instructionsToWat ifTrue (n + 1) + insF = instructionsToWat ifFalse (n + 1) +instructionToWat (While (value, ins)) n = valueToWat value - ++ [ If EmptyType ] - ++ instructionsToWat ifTrue - ++ [ Else ] - ++ instructionsToWat ifFalse - ++ [ End ] + ++ [ If EmptyType, Loop EmptyType ] + ++ ins' ++ valueToWat value ++ [ If EmptyType, Br (n + 1), End] + ++ [ End, End ] + where + ins' = instructionsToWat ins (n + 2) -instructionsToWat :: [Instruction] -> [OpCode] -instructionsToWat = concatMap instructionToWat +instructionsToWat :: [Instruction] -> Int32 -> [OpCode] +instructionsToWat [] _ = [] +instructionsToWat (x:xs) n = ins ++ inss + where + ins = instructionToWat x n + inss = instructionsToWat xs n -- -- instructionsToWat = foldr ((++) . instructionToWat) [] -- @@ -130,7 +136,7 @@ watLikeToWat (((isExp, fName, params, returnType), ins), vars, originName) pType = paramsToTypes params rType = typeStringToType returnType vDecl = groupVarsToDecl $ varsToDecl vars ins params - opcodes = instructionsToWat ins + opcodes = instructionsToWat ins 0 watsLikeToWat :: [FuncDeclare] -> [FuncDef] watsLikeToWat = map watLikeToWat diff --git a/lvtc/src/WriteWasm.hs b/lvtc/src/WriteWasm.hs index 8182161..7f8dd67 100644 --- a/lvtc/src/WriteWasm.hs +++ b/lvtc/src/WriteWasm.hs @@ -125,6 +125,10 @@ opCodeToByte (Call a) = B.pack [fromIntegral (opCodeByte (Call a)), fromIntegral a] opCodeToByte (If a) = B.pack [fromIntegral (opCodeByte (If a)), fromIntegral (ifTypeByte a)] +opCodeToByte (Br a) = + B.pack [fromIntegral (opCodeByte (Br a)), fromIntegral a] +opCodeToByte (Loop a) = + B.pack [fromIntegral (opCodeByte (Loop a)), fromIntegral (ifTypeByte a)] opCodeToByte op = B.pack [fromIntegral (opCodeByte op)] codeSectionCodeToByte :: CodeSectionCode -> B.ByteString diff --git a/lvtc/stdlib/Convert.lvt b/lvtc/stdlib/Convert.lvt new file mode 100644 index 0000000..b60fbf6 --- /dev/null +++ b/lvtc/stdlib/Convert.lvt @@ -0,0 +1,30 @@ +fn intToBool(a: Int) -> Bool +{ + if (a == 1) + { + <- True; + }; + <- False; +}; + +fn boolToInt(a: Bool) -> Int +{ + if (a) + { + <- 1; + }; + <- 0; +}; + +fn charToInt(a: Char) -> Int +{ + @Char i = '\0'; + @Int res = 0; + + while (i < a) + { + res = res + 1; + i = i + 1; + }; + <- res; +}; diff --git a/lvtc/stdlib/Logic.lvt b/lvtc/stdlib/Logic.lvt new file mode 100644 index 0000000..66289a3 --- /dev/null +++ b/lvtc/stdlib/Logic.lvt @@ -0,0 +1,40 @@ +fn or(a: Bool, b: Bool) -> Bool +{ + if (a) + { + <- True; + }; + <- b; +}; + +fn and(a: Bool, b: Bool) -> Bool +{ + if (a) + { + <- b; + }; + <- False; +}; + +fn not(a: Bool) -> Bool +{ + if (a) + { + <- False; + }; + <- True; +}; + +fn xor(a: Bool, b: Bool) -> Bool +{ + if (a) + { + <- not(b); + }; + <- b; +}; + +fn nand(a: Bool, b: Bool) -> Bool +{ + <- not(and(a, b)); +}; diff --git a/lvtc/stdlib/Math.lvt b/lvtc/stdlib/Math.lvt new file mode 100644 index 0000000..7b734b4 --- /dev/null +++ b/lvtc/stdlib/Math.lvt @@ -0,0 +1,57 @@ +fn mod(a: Int, b: Int) -> Int +{ + @Int res = a; + @Int q = a / b; + @Int r = q * b; + res = res - r; + <- res; +}; + +fn square(a: Int) -> Int +{ + <- a * a; +}; + +fn cube(a: Int) -> Int +{ + <- a * a * a; +}; + +fn pow(a: Int, b: Int) -> Int +{ + @Int res = 1; + @Int i = 0; + + while (i < b) + { + res = res * a; + i = i + 1; + }; + <- res; +}; + +fn factorial(a: Int) -> Int +{ + @Int res = 1; + @Int i = 2; + + while (i <= a) + { + res = res * i; + i = i + 1; + }; + <- res; +}; + +fn factorialRec(a: Int) -> Int +{ + if (a == 0) + { + <- 1; + }; + if (a == 1) + { + <- 1; + }; + <- a * factorialRec(a - 1); +}; diff --git a/lvtc/test/Spec.hs b/lvtc/test/Spec.hs index 9e5229b..e106fca 100644 --- a/lvtc/test/Spec.hs +++ b/lvtc/test/Spec.hs @@ -18,6 +18,7 @@ import UTWatLike import UTWat import UTWasm import UTLexeme +import UTTypeCheck main :: IO () main = defaultMain tests @@ -33,7 +34,8 @@ tests = testGroup "Leviator Tests - Compiler" utWatLike, utWat, utWasm, - utLexeme + utLexeme, + uTTypeCheck ] testParserHelper :: String -> String -> Expression -> IO () diff --git a/lvtc/test/UTTypeCheck.hs b/lvtc/test/UTTypeCheck.hs new file mode 100644 index 0000000..7ffb992 --- /dev/null +++ b/lvtc/test/UTTypeCheck.hs @@ -0,0 +1,77 @@ +{- +-- EPITECH PROJECT, 2023 +-- Leviator compiler +-- File description: +-- UTShuntingYard +-} + +module UTTypeCheck ( + uTTypeCheck +) where + +import Test.Tasty +import Test.Tasty.HUnit +import AST +import TypeCheck + +uTTypeCheck :: TestTree +uTTypeCheck = testGroup "typeCheck tests" + [testReturn, testFuncCall, testVars, testWhile] + +testReturn :: TestTree +testReturn = testGroup "testReturn" + [ + testCase "test no start" (shouldBeFalse + [((True, "main", [], "Int"), [Return (Integer 0)])]), + testCase "testReturn" (shouldBeTrue + [((True, "start", [], "Int"), ([Return (Integer 0)]))]), + testCase "testWrongReturnType" (shouldBeFalse + [((True, "start", [], "Int"), ([Return (Boolean True)]))]), + testCase "test return param" (shouldBeFalse + [((True, "start", [("a", "Int"), ("b", "Bool")], "Int"), [Return (Var "b")])]) + ] + +testFuncCall :: TestTree +testFuncCall = testGroup "testFuncCall" + [ + testCase "test recursive" (shouldBeTrue + [((True, "start", [], "Int"), [Return (FuncValue ("start", []))])]), + testCase "test return wrong type" (shouldBeFalse + [((True, "start", [], "Int"), [Return (Integer 0)]), ((True, "snd", [], "Bool"), [Return (FuncValue ("start", []))])]), + testCase "test call with params" (shouldBeTrue + [((True, "start", [("a", "Int"), ("b", "Bool")], "Int"), [Return (Var "a")]), ((True, "snd", [], "Int"), [Return (FuncValue ("start", [(Integer 0), (Boolean True)]))])]), + testCase "test call with wrongs params" (shouldBeFalse + [((True, "start", [("a", "Int"), ("b", "Bool")], "Int"), [Return (Var "a")]), ((True, "snd", [], "Int"), [Return (FuncValue ("start", [(Integer 0), (Integer 2)]))])]) + ] + +testVars :: TestTree +testVars = testGroup "testVars" + [ + testCase "test vars" (shouldBeTrue + [((True, "start", [], "Int"), [Declaration (("a", "Int"), Integer 0), Assignation ("a", (FuncValue ("+", [(Var "a"), (Integer 1)]))), Return (Var "a")])]), + testCase "test params" (shouldBeTrue + [((True, "start", [("a", "Int")], "Int"), [Assignation ("a", (FuncValue ("+", [(Var "a"), (Integer 1)]))), Return (Var "a")])]), + testCase "test params" (shouldBeTrue + [((True, "start", [("a", "Int")], "Int"), [Declaration (("b", "Int"), (Var "a")), Declaration (("c", "Int"), (Var "b")), Return (Var "c")])]), + testCase "test wrong var declaration" (shouldBeFalse + [((True, "start", [("a", "Int")], "Int"), [Declaration (("a", "Int"), Integer 0)])]), + testCase "test double func name" (shouldBeFalse + [((True, "start", [("a", "Int")], "Int"), [Return (Integer 0)]), ((True, "start", [("a", "Int")], "Int"), [Return (Integer 0)])]) + ] + +testWhile :: TestTree +testWhile = testGroup "testWhile" + [ + testCase "test while" (shouldBeTrue + [((True, "start", [], "Int"), [While (Boolean True, [Return (Integer 0)])])]), + testCase "test while" (shouldBeFalse + [((True, "start", [], "Int"), [While (Integer 0, [Return (Integer 0)])])]), + testCase "test while" (shouldBeFalse + [((True, "start", [], "Int"), [While (Boolean True, [Return (Boolean True)])])]) + ] + +shouldBeTrue :: [FuncDeclaration] -> Assertion +shouldBeTrue funcs = (typeCheck funcs) @?= True + +shouldBeFalse :: [FuncDeclaration] -> Assertion +shouldBeFalse funcs = (typeCheck funcs) @?= False diff --git a/lvtc/test/lvt/Test.lvt b/lvtc/test/lvt/Test.lvt index cb0a607..4acfa3e 100644 --- a/lvtc/test/lvt/Test.lvt +++ b/lvtc/test/lvt/Test.lvt @@ -1,14 +1,8 @@ -fn factorial(n: Int) -> Int -{ - @Int a = n - 1; - if (a == 0) - { - <- 1; - }; - <- n * factorial(a); -}; - export fn start() -> Int { - <- factorial(5); + @Int a = 0 + 1; + @Int b = a - 1; + @Int c = a * b; + @Int d = c / a; + <- d; }; diff --git a/lvtrun/README.md b/lvtrun/README.md index 66f147e..cb22056 100644 --- a/lvtrun/README.md +++ b/lvtrun/README.md @@ -32,17 +32,21 @@ # ----------------------- 2 ----------------------- -02 +02 24 # 02 is the id of the import section -24 01 16 77 -61 73 69 5f -73 6e 61 70 -73 68 6f 74 -5f 70 72 65 -76 69 65 77 -31 09 70 72 -6f 63 5f 65 -78 69 74 00 +# 1 vector +01 +# name is 16hex = 22dec bytes long +16 +# name = "wasi_snapshot_preview1" +77 61 73 69 5f 73 6e 61 70 73 68 6f 74 5f 70 72 65 76 69 65 77 31 +# name is 09 +09 +# name = "proct_exit" +70 72 6f 63 5f 65 78 69 74 +# 00 = func = function +00 +# 00 = typeidx = 0 02 # ----------------------- 3 ----------------------- diff --git a/lvtrun/src/OpCodes.hs b/lvtrun/src/OpCodes.hs index a8788da..9c2030c 100644 --- a/lvtrun/src/OpCodes.hs +++ b/lvtrun/src/OpCodes.hs @@ -21,7 +21,6 @@ import Leb128 (getLEB128ToI32, getLEB128ToI64) import Types (Instruction(..), MemArg(..), BlockType(..)) extractOpCode' :: [Word8] -> ([Word8], BSL.ByteString) -extractOpCode' (0x03:rest) = ([0x03], BSL.pack rest) extractOpCode' (0x11:rest) = ([0x11], BSL.pack rest) extractOpCode' (0x00:rest) = ([0x00], BSL.pack rest) extractOpCode' (0x0b:rest) = ([0x0b], BSL.pack rest) @@ -58,21 +57,23 @@ extractOpCode' (0x4a:rest) = ([0x4a], BSL.pack rest) extractOpCode' (0x4c:rest) = ([0x4c], BSL.pack rest) extractOpCode' (0x4e:rest) = ([0x4e], BSL.pack rest) extractOpCode' (0x47:rest) = ([0x47], BSL.pack rest) +extractOpCode' (0x05:rest) = ([0x05], BSL.pack rest) extractOpCode' (0x3f:0x00:rest) = ([0x3f, 0x00], BSL.pack rest) extractOpCode' (0x40:0x00:rest) = ([0x40, 0x00], BSL.pack rest) extractOpCode' (0x04:0x40:rest) = ([0x04, 0x40], BSL.pack rest) -extractOpCode' _ = throw $ WasmError "ExtractOpCode: bad opcode" +extractOpCode' (0x03:0x40:rest) = ([0x03, 0x40], BSL.pack rest) +extractOpCode' idx = throw $ WasmError "ExtractOpCode: bad opcode" extractOpCode :: BSL.ByteString -> ([Word8], BSL.ByteString) extractOpCode bytes = extractOpCode' (BSL.unpack bytes) createInstruction :: [Word8] -> BSL.ByteString -> (Instruction, BSL.ByteString) -createInstruction [0x03] bytes = (Nop, bytes) createInstruction [0x11] bytes = (Nop, bytes) createInstruction [0x00] bytes = (Unreachable, bytes) createInstruction [0x01] bytes = (Nop, bytes) createInstruction [0x02] bytes = (Block EmptyType, bytes) createInstruction [0x0b] bytes = (End, bytes) +createInstruction [0x05] bytes = (Else, bytes) createInstruction [0x48] bytes = (I32Lts, bytes) createInstruction [0x0f] bytes = (Return, bytes) createInstruction [0x4b] bytes = (I32Gtu, bytes) @@ -89,6 +90,7 @@ createInstruction [0x4e] bytes = (I32Ges, bytes) createInstruction [0x4c] bytes = (I32Les, bytes) createInstruction [0x71] bytes = (I32And, bytes) createInstruction [0x04, 0x40] bytes = (If, bytes) +createInstruction [0x03, 0x40] bytes = (Loop, bytes) createInstruction [0x3f, 0x00] bytes = (MemorySize, bytes) createInstruction [0x40, 0x00] bytes = (MemoryGrow, bytes) createInstruction [0x0d] bytes = (\(value, rest) -> diff --git a/lvtrun/src/Run/Functions.hs b/lvtrun/src/Run/Functions.hs index 293a968..b85f113 100644 --- a/lvtrun/src/Run/Functions.hs +++ b/lvtrun/src/Run/Functions.hs @@ -21,16 +21,16 @@ import Errors (CustomException(..)) import Types (Export(..), ExportDesc(..), Function(..), FuncType(..)) getStartFunctionId :: [Export] -> Int32 -getStartFunctionId [] = throw $ WasmError "No start function" +getStartFunctionId [] = throw $ RuntimeError "No start function" getStartFunctionId (x:xs) | expName x == "start" = case expDesc x of ExportFunc idx -> idx - _ -> throw $ WasmError "getStartFunctionId: bad export" + _ -> throw $ RuntimeError "getStartFunctionId: bad export" | otherwise = getStartFunctionId xs getFunctionFromId :: Int32 -> [Function] -> Function -getFunctionFromId _ [] = throw $ WasmError "getFunctionFromId: bad id" +getFunctionFromId _ [] = throw $ RuntimeError "getFunctionFromId: bad id" getFunctionFromId id (x:xs) | funcIdx x == id = x | otherwise = getFunctionFromId id xs @@ -40,7 +40,7 @@ getStartFunction exports functions = getFunctionFromId (getStartFunctionId exports) functions getFuncTypeFromId :: Int32 -> [FuncType] -> FuncType -getFuncTypeFromId _ [] = throw $ WasmError "getFuncTypeFromId: bad id" +getFuncTypeFromId _ [] = throw $ RuntimeError "getFuncTypeFromId: bad id" getFuncTypeFromId id (x:xs) | typeId x == id = x | otherwise = getFuncTypeFromId id xs diff --git a/lvtrun/src/Run/Locals.hs b/lvtrun/src/Run/Locals.hs index 4c9c30f..8ee99f0 100644 --- a/lvtrun/src/Run/Locals.hs +++ b/lvtrun/src/Run/Locals.hs @@ -25,9 +25,9 @@ import Run.Stack (Stack, stackPopN) type Locals = [Value] getLocalFromId' :: Int32 -> LocalIdx -> Locals -> Value -getLocalFromId' _ _ [] = throw $ WasmError "getLocalFromId: bad id" +getLocalFromId' _ _ [] = throw $ RuntimeError "getLocalFromId: bad id" getLocalFromId' idx idntifier (x:xs) - | idx > idntifier = throw $ WasmError "getLocalFromId: bad id" + | idx > idntifier = throw $ RuntimeError "getLocalFromId: bad id" | idx == idntifier = x | otherwise = getLocalFromId' (idx + 1) idntifier xs @@ -35,9 +35,9 @@ getLocalFromId :: Locals -> LocalIdx -> Value getLocalFromId lcals idntifier = getLocalFromId' 0 idntifier lcals setLocalWithId :: Int32 -> Locals -> Value -> LocalIdx -> Locals -setLocalWithId _ [] _ _ = throw $ WasmError "setLocalWithId: bad id" +setLocalWithId _ [] _ _ = throw $ RuntimeError "setLocalWithId: bad id" setLocalWithId idx (x:xs) value idntifier - | idx > idntifier = throw $ WasmError "setLocalWithId: bad id" + | idx > idntifier = throw $ RuntimeError "setLocalWithId: bad id" | idx == idntifier = value : xs | otherwise = x : setLocalWithId (idx + 1) xs value idntifier @@ -64,7 +64,7 @@ createLocalsParams (F32:xs) (F_32 val:xs2) = (F_32 val : createLocalsParams xs xs2) createLocalsParams (F64:xs) (F_64 val:xs2) = (F_64 val : createLocalsParams xs xs2) -createLocalsParams _ _ = throw $ WasmError "createLocalsParams: bad type" +createLocalsParams _ _ = throw $ RuntimeError "createLocalsParams: bad type" initLocalsParams' :: (Locals, Stack) -> [TypeName] -> (Locals, Stack) initLocalsParams' ([], newStack) _ = ([], newStack) @@ -74,7 +74,7 @@ initLocalsParams' (values, newStack) prms = initLocalsParams :: [TypeName] -> Stack -> (Locals, Stack) initLocalsParams [] stack = ([], stack) initLocalsParams prms stack - | length prms > length stack = throw $ WasmError "initLocalsParam: bad nb" + | length prms > length stack = throw $ RuntimeError "initLocalsParam: bad nb" | otherwise = initLocalsParams' (stackPopN stack (length prms)) prms initLocals :: [Local] -> [TypeName] -> Stack -> (Locals, Stack) diff --git a/lvtrun/src/Run/Types.hs b/lvtrun/src/Run/Types.hs index e3f8de6..737efb7 100644 --- a/lvtrun/src/Run/Types.hs +++ b/lvtrun/src/Run/Types.hs @@ -13,22 +13,29 @@ module Run.Types createVm, incrementInstIdx, createEmptyExec, - decrementBlockIdx + decrementBlockIdx, + getLabelOpIdx, + addLabel, + incrementBlockIdx, + goToLabel ) where import Data.Word (Word8) +import Control.Exception (throw) import Types +import Data.Int (Int32) import Run.Stack (Stack) import Run.Locals (Locals) +import Errors (CustomException(..)) data CurrentExec = CurrentExec { ceLocals :: Locals, ceStack :: Stack, ceInstructions :: [Instruction], ceInstIdx :: Int, - ceLabels :: [Int], + ceLabels :: [Int32], ceParams :: [TypeName], ceResults :: [TypeName], crBlockIndents :: Int @@ -58,9 +65,35 @@ createVm wasmMod = VM { vmStack = [], wasmModule = wasmMod } +goToLabel :: CurrentExec -> LabelIdx -> CurrentExec +goToLabel cEx labelIdx = + cEx {ceInstIdx = fromIntegral (getLabelOpIdx cEx labelIdx)} + +getLabelOpIdx :: CurrentExec -> LabelIdx -> Int +getLabelOpIdx cEx labelIdx + | labelIdx >= fromIntegral (length (ceLabels cEx)) = + throw $ RuntimeError "getLabelOpIdx: bad index" + | otherwise = (fromIntegral (ceLabels cEx !! fromIntegral labelIdx)) + +doesLabelExist :: [Int32] -> LabelIdx -> Bool +doesLabelExist [] _ = False +doesLabelExist (x:xs) labelIdx + | x == labelIdx = True + | otherwise = doesLabelExist xs labelIdx + +addLabel :: CurrentExec -> CurrentExec +addLabel cEx + | doesLabelExist (ceLabels cEx) labelIdx = cEx + | otherwise = cEx { ceLabels = (ceLabels cEx) ++ [labelIdx] } + where + labelIdx = fromIntegral (ceInstIdx cEx) + incrementInstIdx :: CurrentExec -> CurrentExec incrementInstIdx cEx = cEx { ceInstIdx = ceInstIdx cEx + 1 } +incrementBlockIdx :: CurrentExec -> CurrentExec +incrementBlockIdx cEx = cEx { crBlockIndents = (crBlockIndents cEx) + 1 } + decrementBlockIdx :: CurrentExec -> CurrentExec decrementBlockIdx cEx = cEx { crBlockIndents = (crBlockIndents cEx) - 1 } diff --git a/lvtrun/src/Run/Vm.hs b/lvtrun/src/Run/Vm.hs index 6bee410..9b7c161 100644 --- a/lvtrun/src/Run/Vm.hs +++ b/lvtrun/src/Run/Vm.hs @@ -102,20 +102,84 @@ execCall vm cEx funcIdx = cEx { ceStack = newStack } currentStack = ceStack cEx res = ceResults (currentExec newVm) +doesElseExist' :: [Instruction] -> Bool +doesElseExist' [] = False +doesElseExist' (Else:_) = True +doesElseExist' (_:rest) = doesElseExist' rest + +doesElseExist :: CurrentExec -> Bool +doesElseExist cEx = doesElseExist' (drop (ceInstIdx cEx) (ceInstructions cEx)) + +getElseIndex' :: [Instruction] -> Int -> Int +getElseIndex' [] _ = throw $ RuntimeError "getElseIndex: missing else" +getElseIndex' (Else:_) idx = idx +getElseIndex' (_:rest) idx = getElseIndex' rest (idx + 1) + +getElseIndex :: CurrentExec -> Int +getElseIndex cEx = getElseIndex' (drop (ceInstIdx cEx) (ceInstructions cEx)) 0 + +executeElse :: CurrentExec -> CurrentExec +executeElse cEx@(CurrentExec {ceStack = stack}) = + case doesElseExist cEx of + False -> cEx + True -> cEx { ceInstIdx = getElseIndex cEx } + execIf :: CurrentExec -> CurrentExec execIf cEx@(CurrentExec {ceStack = stack}) = case stackTop stack of I_32 0 -> goToEndInstruction cEx - I_32 1 -> cEx { crBlockIndents = (crBlockIndents cEx) + 1 } + I_32 1 -> + executeElse (addLabel (cEx { crBlockIndents = (crBlockIndents cEx) + 1 })) I_32 _ -> throw $ RuntimeError "execIf: bad if statement" _ -> throw $ RuntimeError "execIf: bad type" +execI32GtS :: CurrentExec -> CurrentExec +execI32GtS cEx@(CurrentExec {ceStack = stack}) = + case (stackPopN stack 2) of + ([I_32 val2, I_32 val1], newStack) -> case (val1 > val2) of + True -> cEx { ceStack = stackPush newStack (I_32 1) } + False -> cEx { ceStack = stackPush newStack (I_32 0) } + +execI32GeS :: CurrentExec -> CurrentExec +execI32GeS cEx@(CurrentExec {ceStack = stack}) = + case (stackPopN stack 2) of + ([I_32 val2, I_32 val1], newStack) -> case (val1 >= val2) of + True -> cEx { ceStack = stackPush newStack (I_32 1) } + False -> cEx { ceStack = stackPush newStack (I_32 0) } + +execI32LtS :: CurrentExec -> CurrentExec +execI32LtS cEx@(CurrentExec {ceStack = stack}) = + case (stackPopN stack 2) of + ([I_32 val2, I_32 val1], newStack) -> case (val1 < val2) of + True -> cEx { ceStack = stackPush newStack (I_32 1) } + False -> cEx { ceStack = stackPush newStack (I_32 0) } + +execI32LeS :: CurrentExec -> CurrentExec +execI32LeS cEx@(CurrentExec {ceStack = stack}) = + case (stackPopN stack 2) of + ([I_32 val2, I_32 val1], newStack) -> case (val1 <= val2) of + True -> cEx { ceStack = stackPush newStack (I_32 1) } + False -> cEx { ceStack = stackPush newStack (I_32 0) } + +execI32GtU :: CurrentExec -> CurrentExec +execI32GtU cEx@(CurrentExec {ceStack = stack}) = + case (stackPopN stack 2) of + ([I_32 val2, I_32 val1], newStack) -> + case ((fromIntegral val1) > (fromIntegral val2)) of + True -> cEx { ceStack = stackPush newStack (I_32 1) } + False -> cEx { ceStack = stackPush newStack (I_32 0) } + +incrementBlockIndent :: CurrentExec -> CurrentExec +incrementBlockIndent cEx = cEx { crBlockIndents = (crBlockIndents cEx) + 1 } + +execBr :: CurrentExec -> LabelIdx -> CurrentExec +execBr cEx labelIdx = goToLabel cEx labelIdx + execOpCode :: VM -> CurrentExec -> Instruction -> CurrentExec execOpCode _ cEx (Unreachable) = throw $ RuntimeError "execOpCode: unreachable" execOpCode _ cEx (End) = decrementBlockIdx cEx execOpCode _ cEx (Return) = decrementBlockIdx cEx execOpCode _ cEx (I32Const val) = execI32Const cEx val execOpCode _ cEx (I32Eqz) = execI32Eqz cEx -execOpCode _ cEx (Block _) = cEx { crBlockIndents = (crBlockIndents cEx) + 1 } execOpCode _ cEx (I32Eq) = execI32Eq cEx execOpCode _ cEx (I32Add) = execI32Add cEx execOpCode _ cEx (I32Sub) = execI32Sub cEx @@ -126,7 +190,16 @@ execOpCode _ cEx (SetLocal localIdx) = execSetLocal cEx localIdx execOpCode _ cEx (BrIf labelIdx) = execBrIf cEx execOpCode vm cEx (Call funcIdx) = execCall vm cEx funcIdx execOpCode _ cEx (If) = execIf cEx -execOpCode _ cEx _ = cEx +execOpCode _ cEx (I32Gts) = execI32GtS cEx +execOpCode _ cEx (I32Ges) = execI32GeS cEx +execOpCode _ cEx (I32Lts) = execI32LtS cEx +execOpCode _ cEx (I32Les) = execI32LeS cEx +execOpCode _ cEx (I32Gtu) = execI32GtU cEx +execOpCode _ cEx (Block _) = incrementBlockIndent (addLabel cEx) +execOpCode _ cEx (Br labelIdx) = execBr cEx labelIdx +execOpCode _ cEx (Loop) = incrementBlockIndent (addLabel cEx) +execOpCode _ cEx (Else) = throw $ RuntimeError "elseWithoutIf" +execOpCode _ cEx _ = throw $ RuntimeError "execOpCode: not implemented" execOpCodes :: VM -> [Instruction] -> CurrentExec execOpCodes vm [] = currentExec vm diff --git a/lvtrun/src/Types.hs b/lvtrun/src/Types.hs index 6807b19..121c239 100644 --- a/lvtrun/src/Types.hs +++ b/lvtrun/src/Types.hs @@ -117,10 +117,12 @@ data Instruction = | I32Leu | I32Eq | I32Lts + | Else | I32Gts | I32Les | I32Ges | I32Ne + | Loop | LocalTee LocalIdx | BrIf LabelIdx | If @@ -130,6 +132,9 @@ data Instruction = | MemorySize | MemoryGrow deriving (Eq) +--IF/ELSE +--LOOP +--BR instance Show Instruction where show Unreachable = "\n\t\t\t\tunreachable" @@ -170,6 +175,7 @@ instance Show Instruction where show (Br idx) = "\n\t\t\t\tbr " ++ (show idx) show End = "\n\t\t\t\tend" show (Block blockType) = "\n\t\t\t\tblock " ++ (show blockType) + show (Loop) = "\n\t\t\t\tloop" -- Module section diff --git a/lvtrun/test/while.wasm b/lvtrun/test/while.wasm new file mode 100644 index 0000000..6e810de Binary files /dev/null and b/lvtrun/test/while.wasm differ