diff --git a/app/Args.hs b/app/Args.hs new file mode 100644 index 0000000..c502b03 --- /dev/null +++ b/app/Args.hs @@ -0,0 +1,61 @@ +{- +-- EPITECH PROJECT, 2023 +-- Koaky +-- File description: +-- args +-} + + +module Args + ( + Action(..), + Args(..), + RunOpt(..), + parseArgs, + printHelp + ) where + +data Action = ShowHelp | ShowVersion | Run +data RunOpt = RunStdin | RunFile + +data Args = Args { + action :: Action, + runOpt :: RunOpt, + filePath :: String +} + +parseArgs' :: [String] -> Args -> Either Args String +parseArgs' [] args = + Left args +parseArgs' ("--help":xs) args = + parseArgs' xs (args {action = ShowHelp}) +parseArgs' ("-h":xs) args = + parseArgs' xs (args {action = ShowHelp}) +parseArgs' ("--version":xs) args = + parseArgs' xs (args {action = ShowVersion}) +parseArgs' ("-v":xs) args = + parseArgs' xs (args {action = ShowVersion}) +parseArgs' ("-f":f:xs) args = + parseArgs' xs (args {action = Run, runOpt = RunFile, filePath = f}) +parseArgs' ["-f"] _ = + Right "No file specified" +parseArgs' ("-":xs) args = + parseArgs' xs (args {action = Run, runOpt = RunStdin}) +parseArgs' (x:_) _ = + Right ("Unknown option: " ++ x) + +parseArgs :: [String] -> Either Args String +parseArgs args = + parseArgs' args (Args {action = Run, runOpt = RunStdin, filePath = ""}) + +printHelp :: IO () +printHelp = putStr help + where + line1 = "Usage: koaky [OPTION]\n\nInterpret Lisp\n" + line2a = "With no options, koaky reads from standard input.\n\n" + line3 = "Options:\n" + line4 = "\t-h, --help\n\t\tDisplay this help and exit\n" + line5 = "\t-v, --version\n\t\tOutput version information and exit\n" + line6 = "\t-f FILE, --file FILE\n\t\tRead FILE and Interpret it\n" + line7 = "\t-\n\t\tRead from standard input and Interpret it\n" + help = line1 ++ line2a ++ line3 ++ line4 ++ line5 ++ line6 ++ line7 diff --git a/app/Main.hs b/app/Main.hs index ee3fcd9..cf421bf 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -5,35 +5,17 @@ -- Main -} -import Computing.ComputeAST -import Parsing.Parser -import Types -import System.IO - -printErrors :: (Env) -> IO () -printErrors (Env defines_ []) = - printErrors (Env defines_ ["Unable to compute"]) -printErrors (Env defines_ errors_) = - mapM_ putStrLn errors_ >> handleInput (Env defines_ []) - -checkComputing :: (Env, Maybe Result) -> IO () -checkComputing (env, Nothing) = printErrors env -checkComputing (env, Just result) = putStrLn (show result) >> handleInput env - -checkParsing :: Maybe (Tree, String) -> Env -> IO () -checkParsing Nothing _ = return () -checkParsing (Just (tree, _)) env = checkComputing (computeAST env tree) - -checkInput :: String -> Env -> IO () -checkInput ":q" _ = return () -checkInput input env = checkParsing (runParser (parseTree) input) env - -checkEOF :: Env -> Bool -> IO () -checkEOF _ True = return () -checkEOF env False = getLine >>= (\x -> checkInput x env) - -handleInput :: Env -> IO () -handleInput env = isEOF >>= (\x -> checkEOF env x) +import System.Environment (getArgs) +import Args +import Run +import Version + +dispatchAction :: Either Args String -> IO () +dispatchAction (Right error_) = putStrLn error_ +dispatchAction (Left (Args ShowHelp _ _)) = printHelp +dispatchAction (Left (Args ShowVersion _ _)) = printVersion +dispatchAction (Left (Args Run RunFile f)) = runFile f +dispatchAction (Left (Args Run RunStdin _)) = runStdin main :: IO () -main = handleInput (Env [] []) +main = getArgs >>= (dispatchAction . parseArgs) diff --git a/app/Run.hs b/app/Run.hs new file mode 100644 index 0000000..6fea60d --- /dev/null +++ b/app/Run.hs @@ -0,0 +1,65 @@ +{- +-- EPITECH PROJECT, 2023 +-- Koaky +-- File description: +-- run +-} + +module Run + ( + runStdin, + runFile + ) where + +import Computing.ComputeAST +import Parsing.Parser +import Types +import System.IO + +data HHHandle = HHHandle Handle Bool + +printErrors :: HHHandle -> Env -> IO () +printErrors hand (Env defines_ [] funcs_) = + printErrors hand (Env defines_ ["Unable to compute"] funcs_) +printErrors hand (Env defines_ errors_ funcs_) = + mapM_ putStrLn errors_ >> handleInput hand (Env defines_ [] funcs_) [] + +checkComputing :: HHHandle -> (Env, Result) -> IO () +checkComputing hand (env, Right _) = printErrors hand env +checkComputing hand (env, Left Nothing) = handleInput hand env [] +checkComputing hand (env, Left (Just result)) = + print result >> handleInput hand env [] + +checkParsing :: HHHandle -> String -> Maybe (Tree, String) -> Env -> IO () +checkParsing hand str Nothing env = handleInput hand env str +checkParsing hand _ (Just (tree, _)) env = + checkComputing hand (computeAST env tree) + +checkInput :: HHHandle -> String -> Env -> IO () +checkInput _ ":q" _ = return () +checkInput hand input env = + checkParsing hand input (runParser (parseTree) input) env + +checkEOF :: HHHandle -> Env -> String -> Bool -> IO () +checkEOF _ _ _ True = return () +checkEOF (HHHandle ff shouldClosee) env prevStr False = hGetLine ff >>= + (\x -> checkInput (HHHandle ff shouldClosee) (prevStr ++ x) env) + +handleInput :: HHHandle -> Env -> String -> IO () +handleInput (HHHandle ff shouldClosee) env prevStr = + hIsEOF ff >>= (\x -> checkEOF (HHHandle ff shouldClosee) env prevStr x) + +runStdin :: IO () +runStdin = runFileHandle stdin False + +runFile :: String -> IO () +runFile filePath = openFile filePath ReadMode >>= \x -> runFileHandle x True + +onEnd :: HHHandle -> IO () +onEnd (HHHandle ff True) = hClose ff +onEnd _ = return () + +runFileHandle :: Handle -> Bool -> IO () +runFileHandle ff shouldClosee = + handleInput (HHHandle ff shouldClosee) (Env [] [] []) [] >> + onEnd (HHHandle ff shouldClosee) diff --git a/app/Version.hs b/app/Version.hs new file mode 100644 index 0000000..815099c --- /dev/null +++ b/app/Version.hs @@ -0,0 +1,15 @@ +{- +-- EPITECH PROJECT, 2023 +-- koaky +-- File description: +-- version +-} + +module Version + ( printVersion + ) where + +import KoakyLibVersion + +printVersion :: IO () +printVersion = putStrLn koakyLibVersion diff --git a/koaky.cabal b/koaky.cabal index 026aa9d..06f416a 100644 --- a/koaky.cabal +++ b/koaky.cabal @@ -1,6 +1,6 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.2. +-- This file has been generated from package.yaml by hpack version 0.36.0. -- -- see: https://github.com/sol/hpack @@ -27,11 +27,15 @@ library exposed-modules: AST Computing.ComputeAST - Computing.ComputeDeepLists - Computing.ComputeLists Computing.Defines Computing.Errors Computing.Functions + Computing.ListContainList + Computing.Operators.Assert + Computing.Operators.Calculate + Computing.Operators.EvaluateSymbol + Computing.ReplaceFunctionParams + KoakyLibVersion Parsing.Parser Types other-modules: @@ -46,6 +50,9 @@ library executable koaky-exe main-is: Main.hs other-modules: + Args + Run + Version Paths_koaky hs-source-dirs: app diff --git a/src/Computing/ComputeAST.hs b/src/Computing/ComputeAST.hs index abb0dce..64cb53e 100644 --- a/src/Computing/ComputeAST.hs +++ b/src/Computing/ComputeAST.hs @@ -11,32 +11,152 @@ module Computing.ComputeAST ) where import Types +import Computing.ListContainList +import Computing.ReplaceFunctionParams import Computing.Defines +import Computing.Functions import Computing.Errors -import Computing.ComputeDeepLists -import Computing.ComputeLists - --- Handle AST that doesn't contain a list -handleNoList :: Env -> Tree -> (Env, Maybe Result) -handleNoList env (Number nbr) = (env, Just (Number nbr)) -handleNoList env (Boolean value) = (env, Just (Boolean value)) -handleNoList env (Symbol smbl) - | Nothing <- value = (env, Nothing) +import Computing.Operators.Calculate +import Computing.Operators.Assert + +------------------------- CONDITIONS --------------------------------- + +handleIf :: Env -> [Tree] -> (Env, Result) +handleIf env (Boolean (True) : thenBranch : _ : []) + = computeASTWithoutList env thenBranch +handleIf env (Boolean (False) : _ : elseBranch : []) + = computeASTWithoutList env elseBranch +handleIf env _ = (registerError env "Bad if statement", Right (undefined)) + +---------------------------------------------------------------------------------- + +-- Find nested lists and resolve them +resolveNestedLists :: Env -> [Tree] -> [Tree] -> (Env, Maybe [Tree]) +resolveNestedLists env resolvedList [] = (env, Just resolvedList) +resolveNestedLists env resolvedList (List list : rest) + | not (doesListContainsList list) = + case handleSimpleList env list of + (newEnv, Left (Just resolved)) -> + resolveNestedLists newEnv (resolvedList ++ [resolved]) rest + (newEnv, _) -> (newEnv, Nothing) + | otherwise = case resolveNestedLists env [] list of + (newEnv, Nothing) -> (newEnv, Nothing) + (newEnv, Just rvd) + -> resolveNestedLists newEnv (resolvedList ++ [List rvd]) rest +resolveNestedLists env resolvedList (Number number : rest) = + resolveNestedLists env (resolvedList ++ [Number number]) rest +resolveNestedLists env resolvedList (Boolean value : rest) = + resolveNestedLists env (resolvedList ++ [Boolean value]) rest +resolveNestedLists env resolvedList (Symbol smbl : rest) = + resolveNestedLists env (resolvedList ++ [Symbol smbl]) rest + +---------------------------------------------------------------------------------- + +-- Compute simple lists (no nested lists) +handleSimpleList :: Env -> [Tree] -> (Env, Result) +handleSimpleList env (Symbol "+" : rest) = addition env rest +handleSimpleList env (Symbol "*" : rest) = multiplication env rest +handleSimpleList env (Symbol "-" : rest) = subtraction env rest +handleSimpleList env (Symbol "div" : rest) = division env rest +handleSimpleList env (Symbol "mod" : rest) = modulo env rest +handleSimpleList env (Symbol "eq?" : rest) = equal env rest +handleSimpleList env (Symbol "diff?" : rest) = notEqual env rest +handleSimpleList env (Symbol "<" : rest) = inferior env rest +handleSimpleList env (Symbol ">" : rest) = superior env rest +handleSimpleList env (Symbol "<=" : rest) = inferiorOrEqual env rest +handleSimpleList env (Symbol ">=" : rest) = superiorOrEqual env rest +handleSimpleList env (Symbol "if" : rest) = handleIf env rest +handleSimpleList env (Symbol smbl : rest) = + case getFunctionByName env smbl of + Nothing -> (registerError env ("Function " ++ smbl ++ " not found"), + Right (undefined)) + Just func -> + case computeFunction env func rest of + (_, Left (Just result)) -> (env, Left (Just result)) + (newEnv, _) -> (env {errors = errors newEnv}, Right(undefined)) +handleSimpleList env _ = + (registerError env "Bad function call", Right (undefined)) + +----------------------------------------------------------------------------- + +handleLambda :: Env -> Tree -> (Env, Result) +handleLambda env (List (List (Symbol "lambda" : List fnParams : fnBodies): + (List args): _)) = + computeFunction env + (Function "" (getParams (List fnParams)) fnBodies) args +handleLambda env _ = (registerError env "Bad lambda", Left (Nothing)) + +--------------------------- COMPUTE FUNCTIONS -------------------------------- + +computeFunctionBody :: Env -> Function -> [Tree] -> (Env, Result) +computeFunctionBody env (Function _ _ []) _ = (env, Left (Nothing)) +computeFunctionBody env (Function _ fnParams (x:_)) args = + case replaceFunctionParams env fnParams x args of + (newEnv, Nothing) -> (newEnv, Right (undefined)) + (newEnv, Just replaced) -> computeAST newEnv replaced +computeFunction :: Env -> Function -> [Tree] -> (Env, Result) +computeFunction env (Function fnName fnParams (x:xs:rest)) args = + case computeFunctionBody env (Function fnName fnParams [x]) args of + (newEnv, Left (Nothing)) -> + computeFunction newEnv (Function fnName fnParams (xs:rest)) args + (_, _) -> + (registerError env "Bad return placement", Right (undefined)) +computeFunction env (Function fnName fnParams (x:_)) args = + case computeFunctionBody env (Function fnName fnParams [x]) args of + (newEnv, Left (Just replaced)) -> computeAST newEnv replaced + (newEnv, _) -> + (registerError newEnv "Missing return in func", Right (undefined)) +computeFunction env _ _ = + (registerError env "Bad function call", Right (undefined)) + +---------------------------- REGISTER DEFINE ---------------------------------- + +-- Register a define in the Defines list +registerDefine :: Env -> Symbol -> Tree -> Env +registerDefine env symb value@(Number _) = addDefineToEnv env symb value +registerDefine env symb value@(Boolean _) = addDefineToEnv env symb value +registerDefine env symb (List list) = case computeAST env (List list) of + (_, Left (Just result)) -> addDefineToEnv env symb result + (newEnv, _) -> registerError newEnv ("Bad define " ++ symb) +registerDefine env symb (Symbol smbl) = case getSymbolValue env smbl of + (_, Just result) -> addDefineToEnv env symb result + (newEnv, _) -> registerError newEnv ("Bad define " ++ symb) + +--------------------------- COMPUTE AST ------------------------------------- + +computeASTWithoutList :: Env -> Tree -> (Env, Result) +computeASTWithoutList env (Number nbr) = (env, Left (Just (Number nbr))) +computeASTWithoutList env (Boolean value) = (env, Left (Just (Boolean value))) +computeASTWithoutList env (Symbol smbl) + | Nothing <- value = (env, Right (undefined)) | Just (List list) <- value = computeAST env (List list) - | Just result <- value = (env, Just result) + | Just result <- value = (env, Left (Just result)) where (_, value) = getSymbolValue env smbl -handleNoList env _ = (env, Nothing) +computeASTWithoutList env _ = (env, Right (undefined)) + +computeAstWithList :: Env -> Tree -> (Env, Result) +computeAstWithList env (List list) + | not (doesListContainsList list) = handleSimpleList env list + | otherwise = case resolveNestedLists env [] list of + (newEnv, Nothing) -> (newEnv, Right (undefined)) + (newEnv, Just rvd) -> computeAST newEnv (List rvd) +computeAstWithList env _ = (registerError env "Bad list", Right (undefined)) --- Handle AST that register a define -handleDefine :: Env -> Tree -> (Env, Maybe Result) -handleDefine env (List [Symbol _, Symbol smbl, expr]) - = (registerDefine env smbl expr, Nothing) -handleDefine env _ = (registerError env "Bad define", Nothing) +handleDefine :: Env -> Tree -> (Env, Result) +handleDefine env (List [Symbol _, Symbol smbl, + List (Symbol "lambda": List fnParams : fnBodies)]) = + (registerFunction env smbl (List fnParams) fnBodies, Left (Nothing)) +handleDefine env (List [Symbol _, + (List (Symbol smbl : fnParams)), List fnBodies]) = + (registerFunction env smbl (List fnParams) + (List fnBodies : []), Left (Nothing)) +handleDefine env (List [Symbol _, Symbol smbl, expr]) = + (registerDefine env smbl expr, Left (Nothing)) +handleDefine env _ = (registerError env "Bad define", Right (undefined)) --- Compute entire AST -computeAST :: Env -> Tree -> (Env, Maybe Result) +computeAST :: Env -> Tree -> (Env, Result) computeAST env tree@(List (Symbol "define" : _)) = handleDefine env tree -computeAST env (List list) - | doesListContainsList list = handleDeepList env list - | otherwise = handleSimpleList env list -computeAST env tree = handleNoList env tree +computeAST env tree@(List (List (Symbol "lambda" : _) : _)) = + handleLambda env tree +computeAST env tree@(List _) = computeAstWithList env tree +computeAST env tree = computeASTWithoutList env tree diff --git a/src/Computing/ComputeDeepLists.hs b/src/Computing/ComputeDeepLists.hs deleted file mode 100644 index 0cca2b3..0000000 --- a/src/Computing/ComputeDeepLists.hs +++ /dev/null @@ -1,44 +0,0 @@ -{- --- EPITECH PROJECT, 2023 --- Koaky --- File description: --- Compute nested lists --} - -module Computing.ComputeDeepLists - ( - handleDeepList, - resolveNestedLists - ) where - -import Types -import Computing.ComputeLists - --- Find nested lists and resolve them -resolveNestedLists :: Env -> [Tree] -> [Tree] -> (Env, Maybe [Tree]) -resolveNestedLists env resolvedList [] = (env, Just resolvedList) -resolveNestedLists env resolvedList (List list : rest) - | not (doesListContainsList list) = - case handleSimpleList env list of - (newEnv, Nothing) -> (newEnv, Nothing) - (newEnv, Just resolved) -> - resolveNestedLists newEnv (resolvedList ++ [resolved]) rest - | otherwise = case resolveNestedLists env [] list of - (newEnv, Nothing) -> (newEnv, Nothing) - (newEnv, Just rvd) - -> resolveNestedLists newEnv (resolvedList ++ [List rvd]) rest -resolveNestedLists env resolvedList (Number number : rest) = - resolveNestedLists env (resolvedList ++ [Number number]) rest -resolveNestedLists env resolvedList (Boolean value : rest) = - resolveNestedLists env (resolvedList ++ [Boolean value]) rest -resolveNestedLists env resolvedList (Symbol smbl : rest) = - resolveNestedLists env (resolvedList ++ [Symbol smbl]) rest - --- Compute nested lists -handleDeepList :: Env -> [Tree] -> (Env, Maybe Result) -handleDeepList env list - | not (doesListContainsList list) = handleSimpleList env list - | otherwise = - case resolveNestedLists env [] list of - (newEnv, Nothing) -> (newEnv, Nothing) - (newEnv, Just resolvedList) -> handleDeepList newEnv resolvedList diff --git a/src/Computing/ComputeLists.hs b/src/Computing/ComputeLists.hs deleted file mode 100644 index ce000a2..0000000 --- a/src/Computing/ComputeLists.hs +++ /dev/null @@ -1,29 +0,0 @@ -{- --- EPITECH PROJECT, 2023 --- Koaky --- File description: --- Compute simple lists --} - -module Computing.ComputeLists - ( - doesListContainsList, - handleSimpleList - ) where - -import Types -import Computing.Functions - -doesListContainsList :: [Tree] -> Bool -doesListContainsList [] = False -doesListContainsList (List _ : _) = True -doesListContainsList (_ : rest) = doesListContainsList rest - --- Compute simple lists (no nested lists) -handleSimpleList :: Env -> [Tree] -> (Env, Maybe Result) -handleSimpleList env (Symbol "+" : rest) = addition env rest -handleSimpleList env (Symbol "*" : rest) = multiplication env rest -handleSimpleList env (Symbol "-" : rest) = subtraction env rest -handleSimpleList env (Symbol "div" : rest) = division env rest -handleSimpleList env (Symbol "mod" : rest) = modulo env rest -handleSimpleList env _ = (env, Nothing) diff --git a/src/Computing/Defines.hs b/src/Computing/Defines.hs index c971ed4..abc69fa 100644 --- a/src/Computing/Defines.hs +++ b/src/Computing/Defines.hs @@ -7,20 +7,50 @@ module Computing.Defines ( - registerDefine, - getSymbolValue + getSymbolValue, + addDefineToEnv, + registerFunction, + getParams ) where import Types +import Computing.Errors getSymbolValue :: Env -> String -> (Env, Maybe Tree) -getSymbolValue (Env { defines = [], errors = _ }) _ = - (Env { defines = [], errors = [] }, Nothing) -getSymbolValue (Env { defines = (Define smbl value):xs, errors = err }) expr - | smbl == expr = (Env { defines = xs, errors = err }, Just value) - | otherwise = getSymbolValue (Env { defines = xs, errors = err }) expr - --- Register a define in the Defines list -registerDefine :: Env -> Symbol -> Tree -> Env -registerDefine env symb value = - Env (defines env ++ [Define symb value]) (errors env) +getSymbolValue (Env { defines = [], errors = _, functions = _ }) _ = + (Env { defines = [], errors = [], functions = [] }, Nothing) +getSymbolValue (Env { defines = (Define smbl value):xs, + errors = err, functions = fcts }) expr + | smbl == expr = + (Env { defines = xs, errors = err, functions = fcts }, Just value) + | otherwise = getSymbolValue + (Env { defines = xs, errors = err, functions = fcts }) expr + +isAlreadyDefined :: Env -> Symbol -> Bool +isAlreadyDefined env symb = symb `elem` map (\(Define s _) -> s) (defines env) + +addDefineToEnv :: Env -> Symbol -> Tree -> Env +addDefineToEnv env symb value + | isAlreadyDefined env symb = registerError env ("Symbol " ++ symb ++ + " is already defined") + | otherwise = Env (defines env ++ [Define symb value]) (errors env) + (functions env) + +-- Add a function to the Functions list in the Env +addFunction :: Env -> String -> [String] -> [Tree] -> Env +addFunction env fnName fnParams fnBodies + = Env (defines env) (errors env) + (functions env ++ [Function fnName fnParams fnBodies]) + +-- Get params from a function +getParams :: Tree -> [String] +getParams (List []) = [] +getParams (List (Symbol smbl : xs)) = smbl : getParams (List xs) +getParams _ = [] + +-- Register a function in the Functions list +registerFunction :: Env -> Symbol -> Tree -> [Tree] -> Env +registerFunction env "" _ _ = + registerError env "function name must not be empty" +registerFunction env fnName fnParams fnBodies + = addFunction env fnName (getParams fnParams) fnBodies diff --git a/src/Computing/Errors.hs b/src/Computing/Errors.hs index ad04b06..20360c0 100644 --- a/src/Computing/Errors.hs +++ b/src/Computing/Errors.hs @@ -19,7 +19,7 @@ import Types -- Add a new error to env registerError :: Env -> String -> Env -registerError env err = Env (defines env) (errors env ++ [err]) +registerError env err = Env (defines env) (errors env ++ [err]) (functions env) -- Get all errors getErrors :: Env -> [String] @@ -31,7 +31,7 @@ getLastError env = last (errors env) -- Clear all errors clearErrors :: Env -> Env -clearErrors env = Env (defines env) [] +clearErrors env = Env (defines env) [] (functions env) -- Print all errors printErrors :: Env -> IO () diff --git a/src/Computing/Functions.hs b/src/Computing/Functions.hs index 9d93da9..a31c75e 100644 --- a/src/Computing/Functions.hs +++ b/src/Computing/Functions.hs @@ -6,127 +6,17 @@ -} module Computing.Functions - ( - addition, - subtraction, - multiplication, - division, - modulo + ( getFunctionByName ) where import Types -import Computing.Errors -import Computing.Defines --- Compute a "+ - div * mod" list, using defines if needed - -addition :: Env -> [Tree] -> (Env, Maybe Result) -addition env [Number a, Number b] = (env, Just (Number (a + b))) -addition env [Number a, Symbol b] - | (_, Just (Number symbolValue)) <- getSymbolValue env b = - (env, Just (Number (a + symbolValue))) - | otherwise = (registerError env "Symbol not found", Nothing) -addition env [Symbol a, Number b] - | (_, Just (Number symbolValue)) <- getSymbolValue env a = - (env, Just (Number (symbolValue + b))) - | otherwise = (registerError env "Symbol not found", Nothing) -addition env [Symbol a, Symbol b] - | (_, Just (Number symbolValueA)) <- getSymbolValue env a - , (_, Just (Number symbolValueB)) <- getSymbolValue env b = - (env, Just (Number (symbolValueA + symbolValueB))) - | otherwise = (registerError env "Symbol not found", Nothing) -addition env list - | length list /= 2 = (registerError env "Addition need 2 params", Nothing) - | otherwise = (registerError env "Bad types in addition", Nothing) - -multiplication :: Env -> [Tree] -> (Env, Maybe Result) -multiplication env [Number a, Number b] = (env, Just (Number (a * b))) -multiplication env [Number a, Symbol b] - | (_, Just (Number symbolValue)) <- getSymbolValue env b = - (env, Just (Number (a * symbolValue))) - | otherwise = (registerError env "Symbol not found", Nothing) -multiplication env [Symbol a, Number b] - | (_, Just (Number symbolValue)) <- getSymbolValue env a = - (env, Just (Number (symbolValue * b))) - | otherwise = (registerError env "Symbol not found", Nothing) -multiplication env [Symbol a, Symbol b] - | (_, Just (Number symbolValueA)) <- getSymbolValue env a - , (_, Just (Number symbolValueB)) <- getSymbolValue env b = - (env, Just (Number (symbolValueA * symbolValueB))) - | otherwise = (registerError env "Symbol not found", Nothing) -multiplication env list - | length list /= 2 = (registerError env "* need 2 params", Nothing) - | otherwise = (registerError env "Bad types in multiplication", Nothing) - -subtraction :: Env -> [Tree] -> (Env, Maybe Result) -subtraction env [Number a, Number b] = (env, Just (Number (a - b))) -subtraction env [Number a, Symbol b] - | (_, Just (Number symbolValue)) <- getSymbolValue env b = - (env, Just (Number (a - symbolValue))) - | otherwise = (registerError env "Symbol not found", Nothing) -subtraction env [Symbol a, Number b] - | (_, Just (Number symbolValue)) <- getSymbolValue env a = - (env, Just (Number (symbolValue - b))) - | otherwise = (registerError env "Symbol not found", Nothing) -subtraction env [Symbol a, Symbol b] - | (_, Just (Number symbolValueA)) <- getSymbolValue env a - , (_, Just (Number symbolValueB)) <- getSymbolValue env b = - (env, Just (Number (symbolValueA - symbolValueB))) - | otherwise = (registerError env "Symbol not found", Nothing) -subtraction env list - | length list /= 2 = (registerError env "- need 2 params", Nothing) - | otherwise = (registerError env "Bad types in subtraction", Nothing) - -division :: Env -> [Tree] -> (Env, Maybe Result) -division env [Number a, Number b] - | b == 0 = (registerError env "Division by 0", Nothing) - | otherwise = (env, Just (Number (a `div` b))) -division env [Symbol a, Number b] - | b == 0 = (registerError env "Division by 0", Nothing) - | (_, Just (Number symbolValue)) <- getSymbolValue env a = - (env, Just (Number (symbolValue `div` b))) - | otherwise = (registerError env "Symbol not found", Nothing) -division env [Number a, Symbol b] - | (_, Just (Number symbolValue)) <- getSymbolValue env b - , symbolValue == 0 = (registerError env "Division by 0", Nothing) - | (_, Just (Number symbolValue)) <- getSymbolValue env b = - (env, Just (Number (a `div` symbolValue))) - | otherwise = (registerError env "Symbol not found", Nothing) -division env [Symbol a, Symbol b] - | (_, Just (Number _)) <- getSymbolValue env a - , (_, Just (Number symbolValueB)) <- getSymbolValue env b - , symbolValueB == 0 = (registerError env "Division by 0", Nothing) - | (_, Just (Number symbolValueA)) <- getSymbolValue env a - , (_, Just (Number symbolValueB)) <- getSymbolValue env b = - (env, Just (Number (symbolValueA `div` symbolValueB))) - | otherwise = (registerError env "Symbol not found", Nothing) -division env list - | length list /= 2 = (registerError env "/ need 2 params", Nothing) - | otherwise = (registerError env "Bad types in division", Nothing) - -modulo :: Env -> [Tree] -> (Env, Maybe Result) -modulo env [Number a, Number b] - | b == 0 = (registerError env "Modulo by 0", Nothing) - | otherwise = (env, Just (Number (a `mod` b))) -modulo env [Symbol a, Number b] - | b == 0 = (registerError env "Modulo by 0", Nothing) - | (_, Just (Number symbolValue)) <- getSymbolValue env a = - (env, Just (Number (symbolValue `mod` b))) - | otherwise = (registerError env "Symbol not found", Nothing) -modulo env [Number a, Symbol b] - | (_, Just (Number symbolValue)) <- getSymbolValue env b - , symbolValue == 0 = (registerError env "Modulo by 0", Nothing) - | (_, Just (Number symbolValue)) <- getSymbolValue env b = - (env, Just (Number (a `mod` symbolValue))) - | otherwise = (registerError env "Symbol not found", Nothing) -modulo env [Symbol a, Symbol b] - | (_, Just (Number _)) <- getSymbolValue env a - , (_, Just (Number symbolValueB)) <- getSymbolValue env b - , symbolValueB == 0 = (registerError env "Modulo by 0", Nothing) - | (_, Just (Number symbolValueA)) <- getSymbolValue env a - , (_, Just (Number symbolValueB)) <- getSymbolValue env b = - (env, Just (Number (symbolValueA `mod` symbolValueB))) - | otherwise = (registerError env "Symbol not found", Nothing) -modulo env list - | length list /= 2 = (registerError env "% need 2 params", Nothing) - | otherwise = (registerError env "Bad types in modulo", Nothing) +-- Find and execute user defined function +getFunctionByName :: Env -> String -> Maybe Function +getFunctionByName (Env { functions = [] }) _ = Nothing +getFunctionByName (Env { functions = (Function fnName fnParams body):xs, + defines = defs, errors = errs }) expr + | fnName == expr = Just (Function fnName fnParams body) + | otherwise = + getFunctionByName (Env { functions = xs, defines = defs, + errors = errs }) expr diff --git a/src/Computing/ListContainList.hs b/src/Computing/ListContainList.hs new file mode 100644 index 0000000..71689dc --- /dev/null +++ b/src/Computing/ListContainList.hs @@ -0,0 +1,18 @@ +{- +-- EPITECH PROJECT, 2023 +-- Koaky +-- File description: +-- ListContainList +-} + +module Computing.ListContainList + ( + doesListContainsList + ) where + +import Types + +doesListContainsList :: [Tree] -> Bool +doesListContainsList [] = False +doesListContainsList (List _ : _) = True +doesListContainsList (_ : rest) = doesListContainsList rest diff --git a/src/Computing/Operators/Assert.hs b/src/Computing/Operators/Assert.hs new file mode 100644 index 0000000..b1e583d --- /dev/null +++ b/src/Computing/Operators/Assert.hs @@ -0,0 +1,68 @@ +{- +-- EPITECH PROJECT, 2023 +-- Koaky +-- File description: +-- Assert +-} + +module Computing.Operators.Assert + ( + equal, + notEqual, + inferior, + inferiorOrEqual, + superior, + superiorOrEqual + ) where + +import Types +import Computing.Operators.EvaluateSymbol +import Data.Int (Int64) +import Computing.Errors + +assert :: Int64 -> Int64 -> Symbol -> Tree +assert a b ">" = Boolean (a > b) +assert a b "<" = Boolean (a < b) +assert a b ">=" = Boolean (a >= b) +assert a b "<=" = Boolean (a <= b) +assert a b "eq?" = Boolean (a == b) +assert a b "diff?" = Boolean (a /= b) +assert _ _ _ = Boolean (False) + +maybeAssert :: Maybe Tree -> Maybe Tree -> Symbol -> Env -> (Env, Result) +maybeAssert (Just (Number a)) (Just (Number b)) operator env = + (env, Left (Just (assert a b operator))) +maybeAssert _ _ _ env = + (registerError env "Symbol not found", Right (undefined)) + +assertOperator :: Env -> [Tree] -> Symbol -> (Env, Result) +assertOperator env [Number a, Number b] operator = + (env, Left (Just (assert a b operator))) +assertOperator env [Number a, Symbol b] operator = + maybeAssert (Just (Number a)) (evaluateSymbol env b) operator env +assertOperator env [Symbol a, Number b] operator = + maybeAssert (evaluateSymbol env a) (Just (Number b)) operator env +assertOperator env [Symbol a, Symbol b] operator = + maybeAssert (evaluateSymbol env a) (evaluateSymbol env b) operator env +assertOperator env list _ + | length list /= 2 = + (registerError env "assert need 2 params", Right (undefined)) + | otherwise = (registerError env "Bad types in assert", Right (undefined)) + +equal :: Env -> [Tree] -> (Env, Result) +equal env trees = assertOperator env trees "eq?" + +notEqual :: Env -> [Tree] -> (Env, Result) +notEqual env trees = assertOperator env trees "diff?" + +inferior :: Env -> [Tree] -> (Env, Result) +inferior env trees = assertOperator env trees "<" + +superior :: Env -> [Tree] -> (Env, Result) +superior env trees = assertOperator env trees ">" + +inferiorOrEqual :: Env -> [Tree] -> (Env, Result) +inferiorOrEqual env trees = assertOperator env trees "<=" + +superiorOrEqual :: Env -> [Tree] -> (Env, Result) +superiorOrEqual env trees = assertOperator env trees ">=" diff --git a/src/Computing/Operators/Calculate.hs b/src/Computing/Operators/Calculate.hs new file mode 100644 index 0000000..c6db955 --- /dev/null +++ b/src/Computing/Operators/Calculate.hs @@ -0,0 +1,70 @@ +{- +-- EPITECH PROJECT, 2023 +-- Koaky +-- File description: +-- Calculate +-} + +module Computing.Operators.Calculate + ( + addition, + subtraction, + multiplication, + division, + modulo, + ) where + +import Types +import Data.Int (Int64) +import Computing.Errors +import Computing.Operators.EvaluateSymbol + +-- Compute a "+ - div * mod" list, using defines if needed + +calculate :: Int64 -> Int64 -> Symbol -> Tree +calculate a b "+" = Number (a + b) +calculate a b "-" = Number (a - b) +calculate a b "*" = Number (a * b) +calculate a b "div" = Number (a `div` b) +calculate a b "mod" = Number (a `mod` b) +calculate _ _ _ = Number 0 + +maybeCalculate :: Maybe Tree -> Maybe Tree -> Symbol -> Env -> (Env, Result) +maybeCalculate _ (Just (Number 0)) "div" env + = (registerError env "Division by 0", Right (undefined)) +maybeCalculate (Just (Number a)) (Just (Number b)) operator env = + (env, Left (Just (calculate a b operator))) +maybeCalculate _ _ _ env = + (registerError env "Symbol not found", Right (undefined)) + +calculateOperator :: Env -> [Tree] -> Symbol -> (Env, Result) +calculateOperator env [_, Number 0] "div" = + (registerError env "Division by 0", Right (undefined)) +calculateOperator env [Number a, Number b] operator = + (env, Left (Just (calculate a b operator))) +calculateOperator env [Number a, Symbol b] operator = + maybeCalculate (Just (Number a)) (evaluateSymbol env b) operator env +calculateOperator env [Symbol a, Number b] operator = + maybeCalculate (evaluateSymbol env a) (Just (Number b)) operator env +calculateOperator env [Symbol a, Symbol b] operator = + maybeCalculate (evaluateSymbol env a) (evaluateSymbol env b) operator env +calculateOperator env list _ + | length list /= 2 = + (registerError env "Addition need 2 params", Right (undefined)) + | otherwise = + (registerError env "Bad types in addition", Right (undefined)) + +addition :: Env -> [Tree] -> (Env, Result) +addition env trees = calculateOperator env trees "+" + +subtraction :: Env -> [Tree] -> (Env, Result) +subtraction env trees = calculateOperator env trees "-" + +multiplication :: Env -> [Tree] -> (Env, Result) +multiplication env trees = calculateOperator env trees "*" + +division :: Env -> [Tree] -> (Env, Result) +division env trees = calculateOperator env trees "div" + +modulo :: Env -> [Tree] -> (Env, Result) +modulo env trees = calculateOperator env trees "mod" diff --git a/src/Computing/Operators/EvaluateSymbol.hs b/src/Computing/Operators/EvaluateSymbol.hs new file mode 100644 index 0000000..aedac51 --- /dev/null +++ b/src/Computing/Operators/EvaluateSymbol.hs @@ -0,0 +1,24 @@ +{- +-- EPITECH PROJECT, 2023 +-- Koaky +-- File description: +-- EvaluateSymbol +-} + +module Computing.Operators.EvaluateSymbol + ( + evaluateSymbol + ) where + +import Types +import Computing.Defines + +-- Evaluate a symbol and return its value +evaluateSymbol :: Env -> Symbol -> Maybe Tree +evaluateSymbol env smbl = + case getSymbolValue env smbl of + (_, Nothing) -> Nothing + (_, Just (Number number)) -> Just (Number number) + (_, Just (Boolean value)) -> Just (Boolean value) + (_, Just (List list)) -> Just (List list) + (_, _) -> Nothing diff --git a/src/Computing/ReplaceFunctionParams.hs b/src/Computing/ReplaceFunctionParams.hs new file mode 100644 index 0000000..34f26ed --- /dev/null +++ b/src/Computing/ReplaceFunctionParams.hs @@ -0,0 +1,30 @@ +{- +-- EPITECH PROJECT, 2023 +-- Koaky +-- File description: +-- ReplaceFunctionParams +-} + +module Computing.ReplaceFunctionParams + ( + replaceFunctionParams + ) where + +import Types +import Computing.Errors + +replaceSymbol :: Tree -> String -> Tree -> Tree +replaceSymbol (Symbol smbl) toReplace replacement + | smbl == toReplace = replacement + | otherwise = Symbol smbl +replaceSymbol (List lst) toReplace replacement + = List (map (\t -> replaceSymbol t toReplace replacement) lst) +replaceSymbol t _ _ = t + +replaceFunctionParams :: Env -> [String] -> Tree -> [Tree] -> (Env, Maybe Tree) +replaceFunctionParams env fnParams body args + | length fnParams /= length args = + (registerError env "Mismatched number of arguments", Nothing) + | otherwise = + (env, Just $ foldl (\acc (param, arg) -> replaceSymbol acc param arg) + body (zip fnParams args)) diff --git a/src/KoakyLibVersion.hs b/src/KoakyLibVersion.hs new file mode 100644 index 0000000..9fc9a5b --- /dev/null +++ b/src/KoakyLibVersion.hs @@ -0,0 +1,33 @@ +{- +-- EPITECH PROJECT, 2023 +-- koaky +-- File description: +-- lib version +-} + +module KoakyLibVersion + ( + koakyLibVersionPatch, + koakyLibVersionMinor, + koakyLibVersionMajor, + koakyLibVersion + ) + where + +koakyLibVersionPatch :: Int +koakyLibVersionPatch = 0 + +koakyLibVersionMinor :: Int +koakyLibVersionMinor = 4 + +koakyLibVersionMajor :: Int +koakyLibVersionMajor = 0 + + +koakyLibVersion :: String +koakyLibVersion = fullVersion + where + fMaj = show koakyLibVersionMajor + fMin = show koakyLibVersionMinor + fPat = show koakyLibVersionPatch + fullVersion = fMaj ++ "." ++ fMin ++ "." ++ fPat diff --git a/src/Types.hs b/src/Types.hs index c6e4d95..9e6f4c3 100644 --- a/src/Types.hs +++ b/src/Types.hs @@ -11,10 +11,12 @@ module Types Tree(..), Define(..), Env(..), - Result + Result, + Function(..) ) where import Data.Int (Int64) +import Data.Void (Void) type Symbol = String @@ -25,12 +27,19 @@ data Define = Define { expression :: Tree } deriving (Show) +data Function = Function { + name :: String, + params :: [String], + bodies :: [Tree] +} deriving (Show) + data Env = Env { defines :: [Define], - errors :: [String] + errors :: [String], + functions :: [Function] } -type Result = Tree +type Result = Either (Maybe Tree) Void ---------- EQ INSTANCES ---------- @@ -59,5 +68,6 @@ instance Show Tree where show (List list) = "L: " ++ show list instance Show Env where - show (Env { defines = def, errors = err }) = - "Defines: " ++ show def ++ "\nErrors: " ++ show err + show (Env { defines = def, errors = err, functions = func }) = + "Defines: " ++ show def ++ "\nErrors: " + ++ show err ++ "\nFunctions: " ++ show func diff --git a/test/Spec.hs b/test/Spec.hs index 39d5905..4f623d2 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -17,7 +17,9 @@ tests = testGroup "Tests" unitTestsComputeDefines, unitTestsComputeSimpleFunctions, unitTestsComputeBasics, - unitTestsASTParse + unitTestsASTParse, + unitTestsComputeFunctions, + unitTestsComputeConditions ] unitTestsASTEqual :: TestTree @@ -93,108 +95,218 @@ unitTestsASTParse = testGroup "AST Parse Tests" testParser "(do (re (mi)) 12 (re (mi)))" (List [Symbol "do", List [Symbol "re", List [Symbol "mi"]], Number 12, List [Symbol "re", List [Symbol "mi"]]]) ] -computeAllAST :: Env -> [Tree] -> (Env, [Maybe Result]) +computeAllAST :: Env -> [Tree] -> (Env, [Result]) computeAllAST env [] = (env, []) computeAllAST env (x:xs) = do let (newEnv, result) = computeAST env x case result of - Nothing -> computeAllAST newEnv xs - Just tree -> do + Left (Just r) -> do let (newEnv2, results) = computeAllAST newEnv xs - (newEnv2, [Just tree] ++ results) + (newEnv2, (Left (Just r)):results) + _ -> do + let (newEnv2, results) = computeAllAST newEnv xs + (newEnv2, results) + +defaultEnv :: Env +defaultEnv = Env {defines = [], errors = [], functions = []} unitTestComputeTypes :: TestTree unitTestComputeTypes = testGroup "Tests Compute Types" [ testCase "bool true" $ assertEqual "bool true" - (Env {defines = [], errors = []}, Just (Boolean True)) - (computeAST (Env {defines = [], errors = []}) (Boolean True)) + (defaultEnv, Left (Just (Boolean True))) + (computeAST (defaultEnv) (Boolean True)) , testCase "bool false" $ assertEqual "bool false" - (Env {defines = [], errors = []}, Just (Boolean False)) - (computeAST (Env {defines = [], errors = []}) (Boolean False)) + (defaultEnv, Left (Just (Boolean False))) + (computeAST (defaultEnv) (Boolean False)) , testCase "number 42" $ assertEqual "number 42" - (Env {defines = [], errors = []}, Just (Number 42)) - (computeAST (Env {defines = [], errors = []}) (Number 42)) + (defaultEnv, Left (Just (Number 42))) + (computeAST (defaultEnv) (Number 42)) , testCase "number -42" $ assertEqual "number -42" - (Env {defines = [], errors = []}, Just (Number (-42))) - (computeAST (Env {defines = [], errors = []}) (Number (-42))) + (defaultEnv, Left (Just (Number (-42)))) + (computeAST (defaultEnv) (Number (-42))) ] unitTestsComputeDefines :: TestTree unitTestsComputeDefines = testGroup "Tests Compute defines" [ testCase "define x 42" $ assertEqual "define x 42" - (Env {defines = [Define {symbol = "x", expression = Number 42}], errors = []}, Nothing) - (computeAST (Env {defines = [], errors = []}) (List [Symbol "define", Symbol "x", Number 42])) + (Env {defines = [Define {symbol = "x", expression = Number 42}], errors = [], functions = []}, Left (Nothing)) + (computeAST (defaultEnv) (List [Symbol "define", Symbol "x", Number 42])) , testCase "define x 42; x" $ assertEqual "define x 42; x" - (Env {defines = [Define {symbol = "x", expression = Number 42}], errors = []}, [Just (Number 42)]) - (computeAllAST (Env {defines = [], errors = []}) [(List [Symbol "define", Symbol "x", Number 42]), (Symbol "x")]) + (Env {defines = [Define {symbol = "x", expression = Number 42}], errors = [], functions = []}, [Left (Just (Number 42))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "x", Number 42]), (Symbol "x")]) , testCase "define x 42; define y 84" $ assertEqual "define x 42; define y 84" - (Env {defines = [Define {symbol = "x", expression = Number 42}, Define {symbol = "y", expression = Number 84}], errors = []}, []) - (computeAllAST (Env {defines = [], errors = []}) [(List [Symbol "define", Symbol "x", Number 42]), (List [Symbol "define", Symbol "y", Number 84])]) + (Env {defines = [Define {symbol = "x", expression = Number 42}, Define {symbol = "y", expression = Number 84}], errors = [], functions = []}, []) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "x", Number 42]), (List [Symbol "define", Symbol "y", Number 84])]) , testCase "define x 42; define y 84; x; y" $ assertEqual "define x 42; define y 84; x; y" - (Env {defines = [Define {symbol = "x", expression = Number 42}, Define {symbol = "y", expression = Number 84}], errors = []}, [Just (Number 42), Just (Number 84)]) - (computeAllAST (Env {defines = [], errors = []}) [(List [Symbol "define", Symbol "x", Number 42]), (List [Symbol "define", Symbol "y", Number 84]), (Symbol "x"), (Symbol "y")]) + (Env {defines = [Define {symbol = "x", expression = Number 42}, Define {symbol = "y", expression = Number 84}], errors = [], functions = []}, [Left (Just (Number 42)), Left (Just (Number 84))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "x", Number 42]), (List [Symbol "define", Symbol "y", Number 84]), (Symbol "x"), (Symbol "y")]) , testCase "define x (42 + 6); x" $ assertEqual "define x (42 + 6); x" - (Env {defines = [Define {symbol = "x", expression = List [Symbol "+", Number 42, Number 6]}], errors = []}, [Just (Number 48)]) - (computeAllAST (Env {defines = [], errors = []}) [(List [Symbol "define", Symbol "x", (List [Symbol "+", Number 42, Number 6])]), (Symbol "x")]) + (Env {defines = [Define {symbol = "x", expression = Number 48}], errors = [], functions = []}, [Left (Just (Number 48))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "x", (List [Symbol "+", Number 42, Number 6])]), (Symbol "x")]) + , testCase "define foo (4 + 5); foo + foo" $ + assertEqual "define foo (4 + 5); foo + foo" + (Env {defines = [Define {symbol = "foo", expression = Number 9}], errors = [], functions = []}, [Left (Just (Number 18))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "foo", (List [Symbol "+", Number 4, Number 5])]), (List [Symbol "+", Symbol "foo", Symbol "foo"])]) + , testCase "define foo 42; define bar foo; bar + bar" $ + assertEqual "define foo 42; define bar foo; bar + bar" + (Env {defines = [Define {symbol = "foo", expression = Number 42}, Define {symbol = "bar", expression = Number 42}], errors = [], functions = []}, [Left (Just (Number 84))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "foo", Number 42]), (List [Symbol "define", Symbol "bar", Symbol "foo"]), (List [Symbol "+", Symbol "bar", Symbol "bar"])]) + , testCase "define foo 42; define foo 84" $ + assertEqual "define foo 42; define foo 84" + (Env {defines = [Define {symbol = "foo", expression = Number 42}], errors = ["Symbol foo is already defined"], functions = []}, []) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "foo", Number 42]), (List [Symbol "define", Symbol "foo", Number 84])]) ] unitTestsComputeSimpleFunctions :: TestTree unitTestsComputeSimpleFunctions = testGroup "Tests compute + - div mod" [ testCase "42 + 42" $ assertEqual "42 + 42" - (Env {defines = [], errors = []}, Just (Number 84)) - (computeAST (Env {defines = [], errors = []}) (List [Symbol "+", Number 42, Number 42])) + (defaultEnv, Left (Just (Number 84))) + (computeAST (defaultEnv) (List [Symbol "+", Number 42, Number 42])) , testCase "-42 + -42" $ assertEqual "-42 + -42" - (Env {defines = [], errors = []}, Just (Number (-84))) - (computeAST (Env {defines = [], errors = []}) (List [Symbol "+", Number (-42), Number (-42)])) + (defaultEnv, Left (Just (Number (-84)))) + (computeAST (defaultEnv) (List [Symbol "+", Number (-42), Number (-42)])) , testCase "42 + dontexist" $ assertEqual "42 + dontexist" - (Env {defines = [], errors = ["Symbol not found"]}, Nothing) - (computeAST (Env {defines = [], errors = []}) (List [Symbol "+", Number 42, Symbol "dontexist"])) + (Env {defines = [], errors = ["Symbol not found"], functions = []}, Right (undefined)) + (computeAST (defaultEnv) (List [Symbol "+", Number 42, Symbol "dontexist"])) , testCase "bool + number" $ assertEqual "bool + number" - (Env {defines = [], errors = ["Bad types in addition"]}, Nothing) - (computeAST (Env {defines = [], errors = []}) (List [Symbol "+", Boolean True, Number 42])) + (Env {defines = [], errors = ["Bad types in addition"], functions = []}, Right (undefined)) + (computeAST (defaultEnv) (List [Symbol "+", Boolean True, Number 42])) , testCase "20 / 2 + 3 * 5 - 10" $ assertEqual "20 / 2 + 3 * 5 - 10" - (Env {defines = [], errors = []}, Just (Number 15)) - (computeAST (Env {defines = [], errors = []}) (List [Symbol "-", (List [Symbol "+", (List [Symbol "div", Number 20, Number 2]), (List [Symbol "*", Number 3, Number 5])]), Number 10])) + (defaultEnv, Left (Just (Number 15))) + (computeAST (defaultEnv) (List [Symbol "-", (List [Symbol "+", (List [Symbol "div", Number 20, Number 2]), (List [Symbol "*", Number 3, Number 5])]), Number 10])) , testCase "11 mod 3" $ assertEqual "11 mod 3" - (Env {defines = [], errors = []}, Just (Number 2)) - (computeAST (Env {defines = [], errors = []}) (List [Symbol "mod", Number 11, Number 3])) + (defaultEnv, Left (Just (Number 2))) + (computeAST (defaultEnv) (List [Symbol "mod", Number 11, Number 3])) ] unitTestsComputeBasics :: TestTree unitTestsComputeBasics = testGroup "Tests compute basics" [ testCase "define foo 42; foo + foo" $ assertEqual "define foo 42; foo + foo" - (Env {defines = [Define {symbol = "foo", expression = Number 42}], errors = []}, [Just (Number 84)]) - (computeAllAST (Env {defines = [], errors = []}) [(List [Symbol "define", Symbol "foo", Number 42]), (List [Symbol "+", Symbol "foo", Symbol "foo"])]) + (Env {defines = [Define {symbol = "foo", expression = Number 42}], errors = [], functions = []}, [Left (Just (Number 84))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "foo", Number 42]), (List [Symbol "+", Symbol "foo", Symbol "foo"])]) , testCase "define foo 42; define bar 42; foo + bar" $ assertEqual "define foo 42; define bar 42; foo + bar" - (Env {defines = [Define {symbol = "foo", expression = Number 42}, Define {symbol = "bar", expression = Number 42}], errors = []}, [Just (Number 84)]) - (computeAllAST (Env {defines = [], errors = []}) [(List [Symbol "define", Symbol "foo", Number 42]), (List [Symbol "define", Symbol "bar", Number 42]), (List [Symbol "+", Symbol "foo", Symbol "bar"])]) + (Env {defines = [Define {symbol = "foo", expression = Number 42}, Define {symbol = "bar", expression = Number 42}], errors = [], functions = []}, [Left (Just (Number 84))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "foo", Number 42]), (List [Symbol "define", Symbol "bar", Number 42]), (List [Symbol "+", Symbol "foo", Symbol "bar"])]) , testCase "2 + 2 * 5" $ assertEqual "2 + 2 * 5" - (Env {defines = [], errors = []}, [Just (Number 12)]) - (computeAllAST (Env {defines = [], errors = []}) [(List [Symbol "+", Number 2, (List [Symbol "*", Number 2, Number 5])])]) + (defaultEnv, [Left (Just (Number 12))]) + (computeAllAST (defaultEnv) [(List [Symbol "+", Number 2, (List [Symbol "*", Number 2, Number 5])])]) , testCase "2 + 2 * (foo + 10) = 106" $ assertEqual "2 + 2 * (foo + 10) = 106" - (Env {defines = [Define {symbol = "foo", expression = Number 42}], errors = []}, [Just (Number 106)]) - (computeAllAST (Env {defines = [], errors = []}) [(List [Symbol "define", Symbol "foo", Number 42]), (List [Symbol "+", Number 2, (List [Symbol "*", Number 2, (List [Symbol "+", Symbol "foo", Number 10])])])]) + (Env {defines = [Define {symbol = "foo", expression = Number 42}], errors = [], functions = []}, [Left (Just (Number 106))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "foo", Number 42]), (List [Symbol "+", Number 2, (List [Symbol "*", Number 2, (List [Symbol "+", Symbol "foo", Number 10])])])]) , testCase "2 + 3 * (8 + (5* ( 2 + 3))) = 107" $ assertEqual "2 + 3 * (8 + (5* ( 2 + 3))) = 107" - (Env {defines = [], errors = []}, [Just (Number 101)]) - (computeAllAST (Env {defines = [], errors = []}) [(List [Symbol "+", Number 2, (List [Symbol "*", Number 3, (List [Symbol "+", Number 8, (List [Symbol "*", Number 5, (List [Symbol "+", Number 2, Number 3])])])])])]) + (defaultEnv, [Left (Just (Number 101))]) + (computeAllAST (defaultEnv) [(List [Symbol "+", Number 2, (List [Symbol "*", Number 3, (List [Symbol "+", Number 8, (List [Symbol "*", Number 5, (List [Symbol "+", Number 2, Number 3])])])])])]) + , testCase "div 42 0" $ + assertEqual "div 42 0" + (Env {defines = [], errors = ["Division by 0"], functions = []}, Right (undefined)) + (computeAST (defaultEnv) (List [Symbol "div", Number 42, Number 0])) + ] + +unitTestsComputeFunctions :: TestTree +unitTestsComputeFunctions = testGroup "Tests compute functions" + [ testCase "(define add (lambda (a b) (+ a b))); (add 1 2)" $ + assertEqual "(define add (lambda (a b) (+ a b))); (add 1 2)" + (Env {defines = [], errors = [], functions = [Function {name = "add", params = ["a", "b"], bodies = [(List [Symbol "+", Symbol "a", Symbol "b"])]}]}, [Left (Just (Number 3))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "add", List [Symbol "lambda", List [Symbol "a", Symbol "b" ], List [Symbol "+", Symbol "a", Symbol "b"]]]), (List [Symbol "add", Number 1, Number 2])]) + , testCase "(define sub (lambda (a b) (- a b))); (sub 84 42)" $ + assertEqual "(define sub (lambda (a b) (- a b))); (sub 84 42)" + (Env {defines = [], errors = [], functions = [Function {name = "sub", params = ["a", "b"], bodies = [(List [Symbol "-", Symbol "a", Symbol "b"])]}]}, [Left (Just (Number 42))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "sub", List [Symbol "lambda", List [Symbol "a", Symbol "b" ], List [Symbol "-", Symbol "a", Symbol "b"]]]), (List [Symbol "sub", Number 84, Number 42])]) + , testCase "(define sub (lambda (a b c d e) (+ a (+ b (* 8 (+ d e))); (sub 84 42 1 2 3)" $ + assertEqual "(define sub (lambda (a b c d e) (+ a (+ b (* 8 (+ d e))); (sub 84 42 1 2 3)" + (Env {defines = [], errors = [], functions = [Function {name = "sub", params = ["a", "b", "c", "d", "e"], bodies = [(List [Symbol "+", Symbol "a", (List [Symbol "+", Symbol "b", (List [Symbol "*", Number 8, (List [Symbol "+", Symbol "d", Symbol "e"])])])])]}]}, [Left (Just (Number 166))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "sub", List [Symbol "lambda", List [Symbol "a", Symbol "b", Symbol "c", Symbol "d", Symbol "e" ], List [Symbol "+", Symbol "a", (List [Symbol "+", Symbol "b", (List [Symbol "*", Number 8, (List [Symbol "+", Symbol "d", Symbol "e"])])])]]]), (List [Symbol "sub", Number 84, Number 42, Number 1, Number 2, Number 3])]) + , testCase "(define func (lambda (a b) (define foo a) (+ foo b))); (func 1 2)" $ + assertEqual "(define func (lambda (a b) (define foo a) (+ foo b))); (func 1 2)" + (Env {defines = [], errors = [], functions = [Function {name = "func", params = ["a", "b"], bodies = [(List [Symbol "define", Symbol "foo", Symbol "a", (List [Symbol "+", Symbol "foo", Symbol "b"])])]}]}, [Left (Just (Number 3))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "func", List [Symbol "lambda", List [Symbol "a", Symbol "b" ], List [Symbol "define", Symbol "foo", Symbol "a"], List [Symbol "+", Symbol "foo", Symbol "b"]]]), (List [Symbol "func", Number 1, Number 2])]) + , testCase "((lambda (a b) (+ a b)) 1 2)" $ + assertEqual "((lambda (a b) (+ a b)) 1 2)" + (defaultEnv, Left (Just (Number 3))) + (computeAST (defaultEnv) (List [List [Symbol "lambda", List [Symbol "a", Symbol "b"], List [Symbol "+", Symbol "a", Symbol "b"]], List [Number 1, Number 2]])) + , testCase "(define func (lambda () (define foo 42) (foo))); (func)" $ + assertEqual "(define func (lambda () (define foo 42) (foo))); (func)" + (Env {defines = [], errors = [], functions = [Function {name = "func", params = [], bodies = [List [Symbol "define", Symbol "foo", Number 42], Symbol "foo"]}]}, [Left (Just (Number 42))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "func", List [Symbol "lambda", List [], List [Symbol "define", Symbol "foo", Number 42], Symbol "foo"]]), (List [Symbol "func"])]) + , testCase "(define func (lambda () (+ 42 42))); (func)" $ + assertEqual "(define func (lambda () (+ 42 42))); (func)" + (Env {defines = [], errors = [], functions = [Function {name = "func", params = [], bodies = [List [Symbol "+", Number 42, Number 42]]}]}, [Left (Just (Number 84))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "func", List [Symbol "lambda", List [], List [Symbol "+", Number 42, Number 42]]]), (List [Symbol "func"])]) + , testCase "(define func (lambda (x) (+ 1 x))); (func 41)" $ + assertEqual "(define func (lambda (x) (+ 1 x))); (func 41)" + (Env {defines = [], errors = [], functions = [Function {name = "func", params = ["x"], bodies = [List [Symbol "+", Number 1, Symbol "x"]]}]}, [Left (Just (Number 42))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "func", List [Symbol "lambda", List [Symbol "x"], List [Symbol "+", Number 1, Symbol "x"]]]), (List [Symbol "func", Number 41])]) + , testCase "(define (add a b) (+ a b)); (add 1 2)" $ + assertEqual "(define (add a b) (+ a b)); (add 1 2)" + (Env {defines = [], errors = [], functions = [Function {name = "add", params = ["a", "b"], bodies = [List [Symbol "+", Symbol "a", Symbol "b"]]}]}, [Left (Just (Number 3))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", List [Symbol "add", Symbol "a", Symbol "b"], List [Symbol "+", Symbol "a", Symbol "b"]]), (List [Symbol "add", Number 1, Number 2])]) + , testCase "(define (func x) (+ x 1)); (func 41)" $ + assertEqual "(define (func x) (+ x 1)); (func 41)" + (Env {defines = [], errors = [], functions = [Function {name = "func", params = ["x"], bodies = [List [Symbol "+", Symbol "x", Number 1]]}]}, [Left (Just (Number 42))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", List [Symbol "func", Symbol "x"], List [Symbol "+", Symbol "x", Number 1]]), (List [Symbol "func", Number 41])]) + ] + +unitTestsComputeConditions :: TestTree +unitTestsComputeConditions = testGroup "Tests compute conditions" + [ testCase "(if #t 42 84)" $ + assertEqual "(if #t 42 84)" + (defaultEnv, Left (Just (Number 42))) + (computeAST (defaultEnv) (List [Symbol "if", Boolean True, Number 42, Number 84])) + , testCase "(if #f (3 + 3) (4 + 4))" $ + assertEqual "(if #f (3 + 3) (4 + 4))" + (defaultEnv, Left (Just (Number 8))) + (computeAST (defaultEnv) (List [Symbol "if", Boolean False, (List [Symbol "+", Number 3, Number 3]), (List [Symbol "+", Number 4, Number 4])])) + , testCase "define foo 42; (if (< foo 10) (* foo 3) (div foo 2))" $ + assertEqual "define foo 42; (if (< foo 10) (* foo 3) (div foo 2))" + (Env {defines = [Define {symbol = "foo", expression = Number 42}], errors = [], functions = []}, [Left (Just (Number 21))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "foo", Number 42]), (List [Symbol "if", (List [Symbol "<", Symbol "foo", Number 10]), (List [Symbol "*", Symbol "foo", Number 3]), (List [Symbol "div", Symbol "foo", Number 2])])]) + , testCase "define foo 42; (if (eq? foo 42) (+ foo 42) (- foo 42))" $ + assertEqual "define foo 42; (if (eq? foo 42) (+ foo 42) (- foo 42))" + (Env {defines = [Define {symbol = "foo", expression = Number 42}], errors = [], functions = []}, [Left (Just (Number 84))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "foo", Number 42]), (List [Symbol "if", (List [Symbol "eq?", Symbol "foo", Number 42]), (List [Symbol "+", Symbol "foo", Number 42]), (List [Symbol "-", Symbol "foo", Number 42])])]) + , testCase "define foo 42; (if (eq? foo 22) (+ foo 42) (- foo 42))" $ + assertEqual "define foo 42; (if (eq? foo 22) (+ foo 42) (- foo 42))" + (Env {defines = [Define {symbol = "foo", expression = Number 42}], errors = [], functions = []}, [Left (Just (Number 0))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "foo", Number 42]), (List [Symbol "if", (List [Symbol "eq?", Symbol "foo", Number 22]), (List [Symbol "+", Symbol "foo", Number 42]), (List [Symbol "-", Symbol "foo", Number 42])])]) + , testCase "define foo 42; (if (diff? foo 22) (false) (true))" $ + assertEqual "define foo 42; (if (diff? foo 22) (false) (true))" + (Env {defines = [Define {symbol = "foo", expression = Number 42}], errors = [], functions = []}, [Left (Just (Boolean True))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "foo", Number 42]), (List [Symbol "if", (List [Symbol "diff?", Symbol "foo", Number 22]), Boolean True, Boolean False])]) + , testCase "define foo 42; (if (diff? foo 42) (true) (false))" $ + assertEqual "define foo 42; (if (diff? foo 42) (true) (false))" + (Env {defines = [Define {symbol = "foo", expression = Number 42}], errors = [], functions = []}, [Left (Just (Boolean False))]) + (computeAllAST (defaultEnv) [(List [Symbol "define", Symbol "foo", Number 42]), (List [Symbol "if", (List [Symbol "diff?", Symbol "foo", Number 42]), Boolean True, Boolean False])]) + , testCase "(define foo 9); (define (func x) (if (< x 10) #t #f)); (func foo)" $ + assertEqual "(define foo 9); (define (func x) (if (< x 10) #t #f)); (func foo)" + (Env {defines = [Define {symbol = "foo", expression = Number 9}], errors = [], functions = [ + Function {name = "func", params = ["x"], bodies = [ + (List [Symbol "if", (List [Symbol "<", Symbol "x", Number 10]), Boolean True, Boolean False]) + ]} + ]}, [Left (Just (Boolean True))]) + (computeAllAST + (defaultEnv) + [ + (List [Symbol "define", Symbol "foo", Number 9]), + (List [Symbol "define", (List [Symbol "func", Symbol "x"]), (List [Symbol "if", (List [Symbol "<", Symbol "x", Number 10]), Boolean True, Boolean False])]), + (List [Symbol "func", Symbol "foo"])]) ]