From 6bb1582de44857cae81cf1fa3ae89c9b5b85e8de Mon Sep 17 00:00:00 2001 From: Axel Humeau Date: Mon, 8 Jan 2024 01:18:21 +0100 Subject: [PATCH 01/15] feat: optimization of define and call --- LobsterLang/LobsterLang.cabal | 1 + LobsterLang/src/AstEval.hs | 5 ++- LobsterLang/src/AstOptimizer.hs | 61 +++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 LobsterLang/src/AstOptimizer.hs diff --git a/LobsterLang/LobsterLang.cabal b/LobsterLang/LobsterLang.cabal index 27d1965..807ffd7 100644 --- a/LobsterLang/LobsterLang.cabal +++ b/LobsterLang/LobsterLang.cabal @@ -27,6 +27,7 @@ library exposed-modules: AST AstEval + AstOptimizer Parse Scope SExpr diff --git a/LobsterLang/src/AstEval.hs b/LobsterLang/src/AstEval.hs index 8f64209..d89d262 100644 --- a/LobsterLang/src/AstEval.hs +++ b/LobsterLang/src/AstEval.hs @@ -51,7 +51,10 @@ evalAst stack (AST.Symbol s asts) = case getVarInScope stack s of Nothing -> (Left ("Symbol '" ++ s ++ "' doesn't exist in the current or global scope"), stack) Just (FunctionValue params ast Nothing) -> evalAst stack (FunctionValue params ast asts) Just value -> evalAst stack value -evalAst stack (AST.List l) = (Right (Just (AST.List l)), stack) +evalAst stack (AST.List l) = case evalSubParams stack l of + (Left err) -> (Left err, stack) + (Right (Just l')) -> (Right (Just (AST.List l')), stack) + (Right Nothing) -> (Left "Cannot have Nothing in a list", stack) evalAst stack (AST.String str) = (Right (Just (AST.String str)), stack) evalAst stack (Boolean b) = (Right (Just (Boolean b)), stack) evalAst stack (Call "+" astList) = evalBiValOp (+) stack (Call "+" astList) diff --git a/LobsterLang/src/AstOptimizer.hs b/LobsterLang/src/AstOptimizer.hs new file mode 100644 index 0000000..7b634ae --- /dev/null +++ b/LobsterLang/src/AstOptimizer.hs @@ -0,0 +1,61 @@ +{- +-- EPITECH PROJECT, 2024 +-- GLaDOS +-- File description: +-- AstOptimizer +-} + +module AstOptimizer where + +import AST +import Scope (ScopeMb) +import AstEval + +data AstError = Error String Ast + | Warning String Ast + +optimizeAst :: [ScopeMb] -> [Ast] -> [Either AstError Ast] +optimizeAst stack ((Define n ast):xs) = case optimizeAst stack [ast] of + [Left err] -> Left err : optimizeAst stack xs + [Right opAst] -> Right (Define n opAst) : optimizeAst stack xs + _ -> Left (Warning "This situation shouldn't happen" (Define n ast)) : optimizeAst stack xs +optimizeAst stack ((Call op asts):xs) + | foldr ((&&) . isUnoptimizable) True asts && + foldr ((&&) . isValue) True asts = case evalAst stack (Call op asts) of + (Left err, _) -> Left (Error err (Call op asts)) : optimizeAst stack xs + (Right (Just ast), stack') -> Right ast : optimizeAst stack' xs + _ -> Left (Warning "This situation shouldn't happen" (Call op asts)) : optimizeAst stack xs + | foldr ((&&) . isUnoptimizable) True asts = Right (Call op asts) : optimizeAst stack xs + | otherwise = case sequence (optimizeAst stack asts) of + Left err -> Left err : optimizeAst stack xs + Right asts' -> optimizeAst stack (Call op asts':xs) +optimizeAst stack (ast:xs) = Right ast : optimizeAst stack xs +optimizeAst _ [] = [] + +isUnoptimizable :: Ast -> Bool +isUnoptimizable (Define _ ast) = isUnoptimizable ast +isUnoptimizable (Value _) = True +isUnoptimizable (Boolean _) = True +isUnoptimizable (String _) = True +isUnoptimizable (List asts) = foldr ((&&) . isUnoptimizable) True asts +isUnoptimizable (Call _ asts) = foldr ((&&) . isUnoptimizable) True asts +isUnoptimizable (Symbol _ Nothing) = True +isUnoptimizable (Symbol _ (Just asts)) = foldr ((&&) . isUnoptimizable) True asts +isUnoptimizable (FunctionValue _ ast Nothing) = isUnoptimizable ast +isUnoptimizable (FunctionValue params ast (Just asts)) = + isUnoptimizable ast && + foldr ((&&) . isUnoptimizable) True asts && + length params > length asts +isUnoptimizable (Cond (Boolean _) _ _) = False +isUnoptimizable (Cond condAst bodyAst Nothing) = + isUnoptimizable condAst && isUnoptimizable bodyAst +isUnoptimizable (Cond condAst bodyAst (Just elseAst)) = + isUnoptimizable condAst && isUnoptimizable bodyAst && isUnoptimizable elseAst + +isValue :: Ast -> Bool +isValue (Value _) = True +isValue (Boolean _) = True +isValue (String _) = True +isValue (List _) = True +isValue (FunctionValue _ _ Nothing) = True +isValue _ = False From 9d2550b9da595c142f2e652bae4581f357468389 Mon Sep 17 00:00:00 2001 From: Axel Humeau Date: Mon, 8 Jan 2024 01:36:07 +0100 Subject: [PATCH 02/15] feat: optimization of values --- LobsterLang/src/AstOptimizer.hs | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/LobsterLang/src/AstOptimizer.hs b/LobsterLang/src/AstOptimizer.hs index 7b634ae..c7a8780 100644 --- a/LobsterLang/src/AstOptimizer.hs +++ b/LobsterLang/src/AstOptimizer.hs @@ -12,24 +12,33 @@ import Scope (ScopeMb) import AstEval data AstError = Error String Ast - | Warning String Ast -optimizeAst :: [ScopeMb] -> [Ast] -> [Either AstError Ast] +data AstOptimised = Result Ast + | Warning String Ast + +optimizeAst :: [ScopeMb] -> [Ast] -> [Either AstError AstOptimised] +optimizeAst stack ((Value v):xs) = Right (Result (Value v)) : optimizeAst stack xs +optimizeAst stack ((Boolean b):xs) = Right (Result (Boolean b)) : optimizeAst stack xs +optimizeAst stack ((String str):xs) = Right (Result (String str)) : optimizeAst stack xs +optimizeAst stack ((List asts):xs) = case sequence (optimizeAst stack asts) of + Left err -> Left err : optimizeAst stack xs + Right opAst -> Right (Result (List (map fromOptimised opAst))) : optimizeAst stack xs optimizeAst stack ((Define n ast):xs) = case optimizeAst stack [ast] of [Left err] -> Left err : optimizeAst stack xs - [Right opAst] -> Right (Define n opAst) : optimizeAst stack xs - _ -> Left (Warning "This situation shouldn't happen" (Define n ast)) : optimizeAst stack xs + [Right (Result opAst)] -> Right (Result (Define n opAst)) : optimizeAst stack xs + [Right (Warning mes opAst)] -> Right (Warning mes (Define n opAst)) : optimizeAst stack xs + _ -> Right (Warning "This situation shouldn't happen" (Define n ast)) : optimizeAst stack xs optimizeAst stack ((Call op asts):xs) | foldr ((&&) . isUnoptimizable) True asts && foldr ((&&) . isValue) True asts = case evalAst stack (Call op asts) of (Left err, _) -> Left (Error err (Call op asts)) : optimizeAst stack xs - (Right (Just ast), stack') -> Right ast : optimizeAst stack' xs - _ -> Left (Warning "This situation shouldn't happen" (Call op asts)) : optimizeAst stack xs - | foldr ((&&) . isUnoptimizable) True asts = Right (Call op asts) : optimizeAst stack xs + (Right (Just ast), stack') -> Right (Result ast) : optimizeAst stack' xs + _ -> Right (Warning "This situation shouldn't happen" (Call op asts)) : optimizeAst stack xs + | foldr ((&&) . isUnoptimizable) True asts = Right (Result (Call op asts)) : optimizeAst stack xs | otherwise = case sequence (optimizeAst stack asts) of Left err -> Left err : optimizeAst stack xs - Right asts' -> optimizeAst stack (Call op asts':xs) -optimizeAst stack (ast:xs) = Right ast : optimizeAst stack xs + Right asts' -> optimizeAst stack (Call op (map fromOptimised asts'):xs) +optimizeAst stack (ast:xs) = Right (Result ast) : optimizeAst stack xs optimizeAst _ [] = [] isUnoptimizable :: Ast -> Bool @@ -59,3 +68,7 @@ isValue (String _) = True isValue (List _) = True isValue (FunctionValue _ _ Nothing) = True isValue _ = False + +fromOptimised :: AstOptimised -> Ast +fromOptimised (Warning _ ast) = ast +fromOptimised (Result ast) = ast From 269a338ca1ace55a968846f5f51f13303852164d Mon Sep 17 00:00:00 2001 From: Axel Humeau Date: Mon, 8 Jan 2024 01:55:13 +0100 Subject: [PATCH 03/15] feat: optimization of symbols --- LobsterLang/src/AstOptimizer.hs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/LobsterLang/src/AstOptimizer.hs b/LobsterLang/src/AstOptimizer.hs index c7a8780..00c3e6f 100644 --- a/LobsterLang/src/AstOptimizer.hs +++ b/LobsterLang/src/AstOptimizer.hs @@ -5,7 +5,9 @@ -- AstOptimizer -} -module AstOptimizer where +module AstOptimizer( + optimizeAst +) where import AST import Scope (ScopeMb) @@ -28,13 +30,25 @@ optimizeAst stack ((Define n ast):xs) = case optimizeAst stack [ast] of [Right (Result opAst)] -> Right (Result (Define n opAst)) : optimizeAst stack xs [Right (Warning mes opAst)] -> Right (Warning mes (Define n opAst)) : optimizeAst stack xs _ -> Right (Warning "This situation shouldn't happen" (Define n ast)) : optimizeAst stack xs +optimizeAst stack ((Symbol s Nothing):xs) = Right (Result (Symbol s Nothing)) : optimizeAst stack xs +optimizeAst stack ((Symbol s (Just asts)):xs) + | foldr ((&&) . isUnoptimizable) True asts = case evalAst stack (Symbol s (Just asts)) of + (Left err, _) -> Left (Error err (Symbol s (Just asts))) : optimizeAst stack xs + (Right (Just _), stack') -> Right (Result (Symbol s (Just asts))) : optimizeAst stack' xs + _ -> Right (Warning "This situation shouldn't happen" (Symbol s (Just asts))) : optimizeAst stack xs + | otherwise = case sequence (optimizeAst stack asts) of + Left err -> Left err : optimizeAst stack xs + Right opAst -> Right (Result (Symbol s (Just (map fromOptimised opAst)))) : optimizeAst stack xs optimizeAst stack ((Call op asts):xs) | foldr ((&&) . isUnoptimizable) True asts && foldr ((&&) . isValue) True asts = case evalAst stack (Call op asts) of (Left err, _) -> Left (Error err (Call op asts)) : optimizeAst stack xs (Right (Just ast), stack') -> Right (Result ast) : optimizeAst stack' xs _ -> Right (Warning "This situation shouldn't happen" (Call op asts)) : optimizeAst stack xs - | foldr ((&&) . isUnoptimizable) True asts = Right (Result (Call op asts)) : optimizeAst stack xs + | foldr ((&&) . isUnoptimizable) True asts = case evalAst stack (Call op asts) of + (Left err, _) -> Left (Error err (Call op asts)) : optimizeAst stack xs + (Right (Just _), stack') -> Right (Result (Call op asts)) : optimizeAst stack' xs + _ -> Right (Warning "This situation shouldn't happen" (Call op asts)) : optimizeAst stack xs | otherwise = case sequence (optimizeAst stack asts) of Left err -> Left err : optimizeAst stack xs Right asts' -> optimizeAst stack (Call op (map fromOptimised asts'):xs) From 46106d194d6bfddb67171ff9b624000313d64c37 Mon Sep 17 00:00:00 2001 From: Axel Humeau Date: Mon, 8 Jan 2024 11:01:21 +0100 Subject: [PATCH 04/15] feat: optimization of conditions --- LobsterLang/src/AstOptimizer.hs | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/LobsterLang/src/AstOptimizer.hs b/LobsterLang/src/AstOptimizer.hs index 00c3e6f..b5a098e 100644 --- a/LobsterLang/src/AstOptimizer.hs +++ b/LobsterLang/src/AstOptimizer.hs @@ -6,17 +6,19 @@ -} module AstOptimizer( - optimizeAst + optimizeAst, ) where import AST import Scope (ScopeMb) import AstEval +import Data.Maybe -data AstError = Error String Ast +data AstError = Error String Ast deriving (Eq, Show) data AstOptimised = Result Ast | Warning String Ast + deriving (Eq, Show) optimizeAst :: [ScopeMb] -> [Ast] -> [Either AstError AstOptimised] optimizeAst stack ((Value v):xs) = Right (Result (Value v)) : optimizeAst stack xs @@ -52,6 +54,27 @@ optimizeAst stack ((Call op asts):xs) | otherwise = case sequence (optimizeAst stack asts) of Left err -> Left err : optimizeAst stack xs Right asts' -> optimizeAst stack (Call op (map fromOptimised asts'):xs) +optimizeAst stack ((Cond condAst trueAst mFalseAst):xs) + | not (isUnoptimizable condAst) = case optimizeAst stack [condAst] of + [Left err] -> Left err : optimizeAst stack xs + [Right (Result condAst')] -> optimizeAst stack (Cond condAst' trueAst mFalseAst:xs) + [Right (Warning _ condAst')] -> optimizeAst stack (Cond condAst' trueAst mFalseAst:xs) + _ -> Right (Warning "This situation shouldn't happen" (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs + | not (isUnoptimizable trueAst) = case optimizeAst stack [trueAst] of + [Left err] -> Left err : optimizeAst stack xs + [Right (Result trueAst')] -> optimizeAst stack (Cond condAst trueAst' mFalseAst:xs) + [Right (Warning _ trueAst')] -> optimizeAst stack (Cond condAst trueAst' mFalseAst:xs) + _ -> Right (Warning "This situation shouldn't happen" (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs + | isJust mFalseAst && not (isUnoptimizable (fromJust mFalseAst)) = case optimizeAst stack [fromJust mFalseAst] of + [Left err] -> Left err : optimizeAst stack xs + [Right (Result falseAst')] -> optimizeAst stack (Cond condAst trueAst (Just falseAst'):xs) + [Right (Warning _ falseAst')] -> optimizeAst stack (Cond condAst trueAst (Just falseAst'):xs) + _ -> Right (Warning "This situation shouldn't happen" (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs + | otherwise = case condAst of + Boolean True -> Right (Warning "Condition is always true" trueAst) : optimizeAst stack xs + Boolean False -> Right (Warning "Condition is always false" + (fromMaybe (Cond condAst trueAst mFalseAst) mFalseAst)) : optimizeAst stack xs + _ -> Right (Result (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs optimizeAst stack (ast:xs) = Right (Result ast) : optimizeAst stack xs optimizeAst _ [] = [] @@ -61,7 +84,8 @@ isUnoptimizable (Value _) = True isUnoptimizable (Boolean _) = True isUnoptimizable (String _) = True isUnoptimizable (List asts) = foldr ((&&) . isUnoptimizable) True asts -isUnoptimizable (Call _ asts) = foldr ((&&) . isUnoptimizable) True asts +isUnoptimizable (Call _ asts) = foldr ((&&) . isUnoptimizable) True asts && + not (foldr ((&&) . isValue) True asts) isUnoptimizable (Symbol _ Nothing) = True isUnoptimizable (Symbol _ (Just asts)) = foldr ((&&) . isUnoptimizable) True asts isUnoptimizable (FunctionValue _ ast Nothing) = isUnoptimizable ast From c53a8539121057dd24d52efd1cc12a8d5750cd4c Mon Sep 17 00:00:00 2001 From: Axel Humeau Date: Mon, 8 Jan 2024 11:06:06 +0100 Subject: [PATCH 05/15] style: lint AstOptimizer.hs --- LobsterLang/src/AstOptimizer.hs | 146 +++++++++++++++++--------------- 1 file changed, 77 insertions(+), 69 deletions(-) diff --git a/LobsterLang/src/AstOptimizer.hs b/LobsterLang/src/AstOptimizer.hs index b5a098e..36f4db2 100644 --- a/LobsterLang/src/AstOptimizer.hs +++ b/LobsterLang/src/AstOptimizer.hs @@ -5,77 +5,84 @@ -- AstOptimizer -} -module AstOptimizer( - optimizeAst, -) where +module AstOptimizer + ( optimizeAst, + ) +where import AST -import Scope (ScopeMb) import AstEval import Data.Maybe +import Scope (ScopeMb) data AstError = Error String Ast deriving (Eq, Show) -data AstOptimised = Result Ast - | Warning String Ast - deriving (Eq, Show) +data AstOptimised + = Result Ast + | Warning String Ast + deriving (Eq, Show) optimizeAst :: [ScopeMb] -> [Ast] -> [Either AstError AstOptimised] -optimizeAst stack ((Value v):xs) = Right (Result (Value v)) : optimizeAst stack xs -optimizeAst stack ((Boolean b):xs) = Right (Result (Boolean b)) : optimizeAst stack xs -optimizeAst stack ((String str):xs) = Right (Result (String str)) : optimizeAst stack xs -optimizeAst stack ((List asts):xs) = case sequence (optimizeAst stack asts) of - Left err -> Left err : optimizeAst stack xs - Right opAst -> Right (Result (List (map fromOptimised opAst))) : optimizeAst stack xs -optimizeAst stack ((Define n ast):xs) = case optimizeAst stack [ast] of - [Left err] -> Left err : optimizeAst stack xs - [Right (Result opAst)] -> Right (Result (Define n opAst)) : optimizeAst stack xs - [Right (Warning mes opAst)] -> Right (Warning mes (Define n opAst)) : optimizeAst stack xs - _ -> Right (Warning "This situation shouldn't happen" (Define n ast)) : optimizeAst stack xs -optimizeAst stack ((Symbol s Nothing):xs) = Right (Result (Symbol s Nothing)) : optimizeAst stack xs -optimizeAst stack ((Symbol s (Just asts)):xs) - | foldr ((&&) . isUnoptimizable) True asts = case evalAst stack (Symbol s (Just asts)) of - (Left err, _) -> Left (Error err (Symbol s (Just asts))) : optimizeAst stack xs - (Right (Just _), stack') -> Right (Result (Symbol s (Just asts))) : optimizeAst stack' xs - _ -> Right (Warning "This situation shouldn't happen" (Symbol s (Just asts))) : optimizeAst stack xs - | otherwise = case sequence (optimizeAst stack asts) of - Left err -> Left err : optimizeAst stack xs - Right opAst -> Right (Result (Symbol s (Just (map fromOptimised opAst)))) : optimizeAst stack xs -optimizeAst stack ((Call op asts):xs) - | foldr ((&&) . isUnoptimizable) True asts && - foldr ((&&) . isValue) True asts = case evalAst stack (Call op asts) of - (Left err, _) -> Left (Error err (Call op asts)) : optimizeAst stack xs - (Right (Just ast), stack') -> Right (Result ast) : optimizeAst stack' xs - _ -> Right (Warning "This situation shouldn't happen" (Call op asts)) : optimizeAst stack xs - | foldr ((&&) . isUnoptimizable) True asts = case evalAst stack (Call op asts) of - (Left err, _) -> Left (Error err (Call op asts)) : optimizeAst stack xs - (Right (Just _), stack') -> Right (Result (Call op asts)) : optimizeAst stack' xs - _ -> Right (Warning "This situation shouldn't happen" (Call op asts)) : optimizeAst stack xs - | otherwise = case sequence (optimizeAst stack asts) of - Left err -> Left err : optimizeAst stack xs - Right asts' -> optimizeAst stack (Call op (map fromOptimised asts'):xs) -optimizeAst stack ((Cond condAst trueAst mFalseAst):xs) - | not (isUnoptimizable condAst) = case optimizeAst stack [condAst] of - [Left err] -> Left err : optimizeAst stack xs - [Right (Result condAst')] -> optimizeAst stack (Cond condAst' trueAst mFalseAst:xs) - [Right (Warning _ condAst')] -> optimizeAst stack (Cond condAst' trueAst mFalseAst:xs) - _ -> Right (Warning "This situation shouldn't happen" (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs - | not (isUnoptimizable trueAst) = case optimizeAst stack [trueAst] of - [Left err] -> Left err : optimizeAst stack xs - [Right (Result trueAst')] -> optimizeAst stack (Cond condAst trueAst' mFalseAst:xs) - [Right (Warning _ trueAst')] -> optimizeAst stack (Cond condAst trueAst' mFalseAst:xs) - _ -> Right (Warning "This situation shouldn't happen" (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs - | isJust mFalseAst && not (isUnoptimizable (fromJust mFalseAst)) = case optimizeAst stack [fromJust mFalseAst] of - [Left err] -> Left err : optimizeAst stack xs - [Right (Result falseAst')] -> optimizeAst stack (Cond condAst trueAst (Just falseAst'):xs) - [Right (Warning _ falseAst')] -> optimizeAst stack (Cond condAst trueAst (Just falseAst'):xs) - _ -> Right (Warning "This situation shouldn't happen" (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs - | otherwise = case condAst of - Boolean True -> Right (Warning "Condition is always true" trueAst) : optimizeAst stack xs - Boolean False -> Right (Warning "Condition is always false" - (fromMaybe (Cond condAst trueAst mFalseAst) mFalseAst)) : optimizeAst stack xs - _ -> Right (Result (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs -optimizeAst stack (ast:xs) = Right (Result ast) : optimizeAst stack xs +optimizeAst stack ((Value v) : xs) = Right (Result (Value v)) : optimizeAst stack xs +optimizeAst stack ((Boolean b) : xs) = Right (Result (Boolean b)) : optimizeAst stack xs +optimizeAst stack ((String str) : xs) = Right (Result (String str)) : optimizeAst stack xs +optimizeAst stack ((List asts) : xs) = case sequence (optimizeAst stack asts) of + Left err -> Left err : optimizeAst stack xs + Right opAst -> Right (Result (List (map fromOptimised opAst))) : optimizeAst stack xs +optimizeAst stack ((Define n ast) : xs) = case optimizeAst stack [ast] of + [Left err] -> Left err : optimizeAst stack xs + [Right (Result opAst)] -> Right (Result (Define n opAst)) : optimizeAst stack xs + [Right (Warning mes opAst)] -> Right (Warning mes (Define n opAst)) : optimizeAst stack xs + _ -> Right (Warning "This situation shouldn't happen" (Define n ast)) : optimizeAst stack xs +optimizeAst stack ((Symbol s Nothing) : xs) = Right (Result (Symbol s Nothing)) : optimizeAst stack xs +optimizeAst stack ((Symbol s (Just asts)) : xs) + | foldr ((&&) . isUnoptimizable) True asts = case evalAst stack (Symbol s (Just asts)) of + (Left err, _) -> Left (Error err (Symbol s (Just asts))) : optimizeAst stack xs + (Right (Just _), stack') -> Right (Result (Symbol s (Just asts))) : optimizeAst stack' xs + _ -> Right (Warning "This situation shouldn't happen" (Symbol s (Just asts))) : optimizeAst stack xs + | otherwise = case sequence (optimizeAst stack asts) of + Left err -> Left err : optimizeAst stack xs + Right opAst -> Right (Result (Symbol s (Just (map fromOptimised opAst)))) : optimizeAst stack xs +optimizeAst stack ((Call op asts) : xs) + | foldr ((&&) . isUnoptimizable) True asts + && foldr ((&&) . isValue) True asts = case evalAst stack (Call op asts) of + (Left err, _) -> Left (Error err (Call op asts)) : optimizeAst stack xs + (Right (Just ast), stack') -> Right (Result ast) : optimizeAst stack' xs + _ -> Right (Warning "This situation shouldn't happen" (Call op asts)) : optimizeAst stack xs + | foldr ((&&) . isUnoptimizable) True asts = case evalAst stack (Call op asts) of + (Left err, _) -> Left (Error err (Call op asts)) : optimizeAst stack xs + (Right (Just _), stack') -> Right (Result (Call op asts)) : optimizeAst stack' xs + _ -> Right (Warning "This situation shouldn't happen" (Call op asts)) : optimizeAst stack xs + | otherwise = case sequence (optimizeAst stack asts) of + Left err -> Left err : optimizeAst stack xs + Right asts' -> optimizeAst stack (Call op (map fromOptimised asts') : xs) +optimizeAst stack ((Cond condAst trueAst mFalseAst) : xs) + | not (isUnoptimizable condAst) = case optimizeAst stack [condAst] of + [Left err] -> Left err : optimizeAst stack xs + [Right (Result condAst')] -> optimizeAst stack (Cond condAst' trueAst mFalseAst : xs) + [Right (Warning _ condAst')] -> optimizeAst stack (Cond condAst' trueAst mFalseAst : xs) + _ -> Right (Warning "This situation shouldn't happen" (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs + | not (isUnoptimizable trueAst) = case optimizeAst stack [trueAst] of + [Left err] -> Left err : optimizeAst stack xs + [Right (Result trueAst')] -> optimizeAst stack (Cond condAst trueAst' mFalseAst : xs) + [Right (Warning _ trueAst')] -> optimizeAst stack (Cond condAst trueAst' mFalseAst : xs) + _ -> Right (Warning "This situation shouldn't happen" (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs + | isJust mFalseAst && not (isUnoptimizable (fromJust mFalseAst)) = case optimizeAst stack [fromJust mFalseAst] of + [Left err] -> Left err : optimizeAst stack xs + [Right (Result falseAst')] -> optimizeAst stack (Cond condAst trueAst (Just falseAst') : xs) + [Right (Warning _ falseAst')] -> optimizeAst stack (Cond condAst trueAst (Just falseAst') : xs) + _ -> Right (Warning "This situation shouldn't happen" (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs + | otherwise = case condAst of + Boolean True -> Right (Warning "Condition is always true" trueAst) : optimizeAst stack xs + Boolean False -> + Right + ( Warning + "Condition is always false" + (fromMaybe (Cond condAst trueAst mFalseAst) mFalseAst) + ) + : optimizeAst stack xs + _ -> Right (Result (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs +optimizeAst stack (ast : xs) = Right (Result ast) : optimizeAst stack xs optimizeAst _ [] = [] isUnoptimizable :: Ast -> Bool @@ -84,20 +91,21 @@ isUnoptimizable (Value _) = True isUnoptimizable (Boolean _) = True isUnoptimizable (String _) = True isUnoptimizable (List asts) = foldr ((&&) . isUnoptimizable) True asts -isUnoptimizable (Call _ asts) = foldr ((&&) . isUnoptimizable) True asts && - not (foldr ((&&) . isValue) True asts) +isUnoptimizable (Call _ asts) = + foldr ((&&) . isUnoptimizable) True asts + && not (foldr ((&&) . isValue) True asts) isUnoptimizable (Symbol _ Nothing) = True isUnoptimizable (Symbol _ (Just asts)) = foldr ((&&) . isUnoptimizable) True asts isUnoptimizable (FunctionValue _ ast Nothing) = isUnoptimizable ast isUnoptimizable (FunctionValue params ast (Just asts)) = - isUnoptimizable ast && - foldr ((&&) . isUnoptimizable) True asts && - length params > length asts + isUnoptimizable ast + && foldr ((&&) . isUnoptimizable) True asts + && length params > length asts isUnoptimizable (Cond (Boolean _) _ _) = False isUnoptimizable (Cond condAst bodyAst Nothing) = - isUnoptimizable condAst && isUnoptimizable bodyAst + isUnoptimizable condAst && isUnoptimizable bodyAst isUnoptimizable (Cond condAst bodyAst (Just elseAst)) = - isUnoptimizable condAst && isUnoptimizable bodyAst && isUnoptimizable elseAst + isUnoptimizable condAst && isUnoptimizable bodyAst && isUnoptimizable elseAst isValue :: Ast -> Bool isValue (Value _) = True From 03dab3d8756b35dda3a474f1fb9985ca3aef32bd Mon Sep 17 00:00:00 2001 From: Axel Humeau Date: Mon, 8 Jan 2024 23:24:24 +0100 Subject: [PATCH 06/15] feat: optimization for function value wip --- LobsterLang/src/AstOptimizer.hs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/LobsterLang/src/AstOptimizer.hs b/LobsterLang/src/AstOptimizer.hs index 36f4db2..3341aa0 100644 --- a/LobsterLang/src/AstOptimizer.hs +++ b/LobsterLang/src/AstOptimizer.hs @@ -82,7 +82,25 @@ optimizeAst stack ((Cond condAst trueAst mFalseAst) : xs) ) : optimizeAst stack xs _ -> Right (Result (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs -optimizeAst stack (ast : xs) = Right (Result ast) : optimizeAst stack xs +optimizeAst stack ((FunctionValue params ast Nothing) : xs) = case optimizeAst stack [ast] of + [Left err] -> Left err : optimizeAst stack xs + [Right (Result ast')] -> Right (Result (FunctionValue params ast' Nothing)) : optimizeAst stack xs + [Right (Warning mes ast')] -> Right (Warning mes (FunctionValue params ast' Nothing)) : optimizeAst stack xs + _ -> Right (Warning "This situation shouldn't happen" (FunctionValue params ast Nothing)) : optimizeAst stack xs +optimizeAst stack ((FunctionValue params ast (Just asts)) : xs) + | not (isUnoptimizable ast) = case optimizeAst stack [ast] of + [Left err] -> Left err : optimizeAst stack xs + [Right (Result ast')] -> optimizeAst stack (FunctionValue params ast' Nothing : xs) + [Right (Warning _ ast')] -> optimizeAst stack (FunctionValue params ast' Nothing : xs) + _ -> Right (Warning "This situation shouldn't happen" (FunctionValue params ast (Just asts))) : optimizeAst stack xs + | not (foldr ((&&) . isUnoptimizable) True asts) = case sequence (optimizeAst stack asts) of + Left err -> Left err : optimizeAst stack xs + Right asts' -> optimizeAst stack (FunctionValue params ast (Just (map fromOptimised asts')) : xs) + | length params > length asts = case evalAst stack (FunctionValue params ast (Just asts)) of + (Left err, _) -> Left (Error err (FunctionValue params ast (Just asts))) : optimizeAst stack xs + (Right (Just ast'), stack') -> Right (Result ast') : optimizeAst stack' xs + (Right Nothing, _) -> Right (Warning "This situation shouldn't happen" (FunctionValue params ast (Just asts))) : optimizeAst stack xs + | otherwise = Right (Result (FunctionValue params ast (Just asts))) : optimizeAst stack xs optimizeAst _ [] = [] isUnoptimizable :: Ast -> Bool From 5f287764fad4cc032aa138d5b858c41c2f28ec56 Mon Sep 17 00:00:00 2001 From: Axel Humeau Date: Mon, 8 Jan 2024 23:47:24 +0100 Subject: [PATCH 07/15] fix: optimization of function value detecting unknown symbol when it can't be resolved if they exist --- LobsterLang/src/AstOptimizer.hs | 145 +++++++++++++++++--------------- 1 file changed, 77 insertions(+), 68 deletions(-) diff --git a/LobsterLang/src/AstOptimizer.hs b/LobsterLang/src/AstOptimizer.hs index 3341aa0..07132ff 100644 --- a/LobsterLang/src/AstOptimizer.hs +++ b/LobsterLang/src/AstOptimizer.hs @@ -22,86 +22,95 @@ data AstOptimised | Warning String Ast deriving (Eq, Show) -optimizeAst :: [ScopeMb] -> [Ast] -> [Either AstError AstOptimised] -optimizeAst stack ((Value v) : xs) = Right (Result (Value v)) : optimizeAst stack xs -optimizeAst stack ((Boolean b) : xs) = Right (Result (Boolean b)) : optimizeAst stack xs -optimizeAst stack ((String str) : xs) = Right (Result (String str)) : optimizeAst stack xs -optimizeAst stack ((List asts) : xs) = case sequence (optimizeAst stack asts) of - Left err -> Left err : optimizeAst stack xs - Right opAst -> Right (Result (List (map fromOptimised opAst))) : optimizeAst stack xs -optimizeAst stack ((Define n ast) : xs) = case optimizeAst stack [ast] of - [Left err] -> Left err : optimizeAst stack xs - [Right (Result opAst)] -> Right (Result (Define n opAst)) : optimizeAst stack xs - [Right (Warning mes opAst)] -> Right (Warning mes (Define n opAst)) : optimizeAst stack xs - _ -> Right (Warning "This situation shouldn't happen" (Define n ast)) : optimizeAst stack xs -optimizeAst stack ((Symbol s Nothing) : xs) = Right (Result (Symbol s Nothing)) : optimizeAst stack xs -optimizeAst stack ((Symbol s (Just asts)) : xs) +optimizeAst :: [ScopeMb] -> [Ast] -> Bool -> [Either AstError AstOptimised] +optimizeAst stack ((Value v) : xs) inFunc = Right (Result (Value v)) : optimizeAst stack xs inFunc +optimizeAst stack ((Boolean b) : xs) inFunc = Right (Result (Boolean b)) : optimizeAst stack xs inFunc +optimizeAst stack ((String str) : xs) inFunc = Right (Result (String str)) : optimizeAst stack xs inFunc +optimizeAst stack ((List asts) : xs) inFunc= case sequence (optimizeAst stack asts inFunc) of + Left err -> Left err : optimizeAst stack xs inFunc + Right opAst -> Right (Result (List (map fromOptimised opAst))) : optimizeAst stack xs inFunc +optimizeAst stack ((Define n ast) : xs) inFunc = case optimizeAst stack [ast] inFunc of + [Left err] -> Left err : optimizeAst stack xs inFunc + [Right (Result opAst)] -> Right (Result (Define n opAst)) : optimizeAst stack xs inFunc + [Right (Warning mes opAst)] -> Right (Warning mes (Define n opAst)) : optimizeAst stack xs inFunc + _ -> Right (Warning "This situation shouldn't happen" (Define n ast)) : optimizeAst stack xs inFunc +optimizeAst stack ((Symbol s Nothing) : xs) inFunc = Right (Result (Symbol s Nothing)) : optimizeAst stack xs inFunc +optimizeAst stack ((Symbol s (Just asts)) : xs) inFunc | foldr ((&&) . isUnoptimizable) True asts = case evalAst stack (Symbol s (Just asts)) of - (Left err, _) -> Left (Error err (Symbol s (Just asts))) : optimizeAst stack xs - (Right (Just _), stack') -> Right (Result (Symbol s (Just asts))) : optimizeAst stack' xs - _ -> Right (Warning "This situation shouldn't happen" (Symbol s (Just asts))) : optimizeAst stack xs - | otherwise = case sequence (optimizeAst stack asts) of - Left err -> Left err : optimizeAst stack xs - Right opAst -> Right (Result (Symbol s (Just (map fromOptimised opAst)))) : optimizeAst stack xs -optimizeAst stack ((Call op asts) : xs) + (Left ('S':'y':'m':'b':'o':'l':' ':'\'':xs'), _) + | inFunc -> Right (Result (Symbol s (Just asts))) : optimizeAst stack xs inFunc + | otherwise -> Left (Error ('S':'y':'m':'b':'o':'l':' ':'\'':xs') (Symbol s (Just asts))) : optimizeAst stack xs inFunc + (Left err, _) -> Left (Error err (Symbol s (Just asts))) : optimizeAst stack xs inFunc + (Right (Just _), stack') -> Right (Result (Symbol s (Just asts))) : optimizeAst stack' xs inFunc + _ -> Right (Warning "This situation shouldn't happen" (Symbol s (Just asts))) : optimizeAst stack xs inFunc + | otherwise = case sequence (optimizeAst stack asts inFunc) of + Left err -> Left err : optimizeAst stack xs inFunc + Right opAst -> Right (Result (Symbol s (Just (map fromOptimised opAst)))) : optimizeAst stack xs inFunc +optimizeAst stack ((Call op asts) : xs) inFunc | foldr ((&&) . isUnoptimizable) True asts && foldr ((&&) . isValue) True asts = case evalAst stack (Call op asts) of - (Left err, _) -> Left (Error err (Call op asts)) : optimizeAst stack xs - (Right (Just ast), stack') -> Right (Result ast) : optimizeAst stack' xs - _ -> Right (Warning "This situation shouldn't happen" (Call op asts)) : optimizeAst stack xs + (Left ('S':'y':'m':'b':'o':'l':' ':'\'':xs'), _) + | inFunc -> Right (Result (Call op asts)) : optimizeAst stack xs inFunc + | otherwise -> Left (Error ('S':'y':'m':'b':'o':'l':' ':'\'':xs') (Call op asts)) : optimizeAst stack xs inFunc + (Left err, _) -> Left (Error err (Call op asts)) : optimizeAst stack xs inFunc + (Right (Just ast), stack') -> Right (Result ast) : optimizeAst stack' xs inFunc + _ -> Right (Warning "This situation shouldn't happen" (Call op asts)) : optimizeAst stack xs inFunc | foldr ((&&) . isUnoptimizable) True asts = case evalAst stack (Call op asts) of - (Left err, _) -> Left (Error err (Call op asts)) : optimizeAst stack xs - (Right (Just _), stack') -> Right (Result (Call op asts)) : optimizeAst stack' xs - _ -> Right (Warning "This situation shouldn't happen" (Call op asts)) : optimizeAst stack xs - | otherwise = case sequence (optimizeAst stack asts) of - Left err -> Left err : optimizeAst stack xs - Right asts' -> optimizeAst stack (Call op (map fromOptimised asts') : xs) -optimizeAst stack ((Cond condAst trueAst mFalseAst) : xs) - | not (isUnoptimizable condAst) = case optimizeAst stack [condAst] of - [Left err] -> Left err : optimizeAst stack xs - [Right (Result condAst')] -> optimizeAst stack (Cond condAst' trueAst mFalseAst : xs) - [Right (Warning _ condAst')] -> optimizeAst stack (Cond condAst' trueAst mFalseAst : xs) - _ -> Right (Warning "This situation shouldn't happen" (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs - | not (isUnoptimizable trueAst) = case optimizeAst stack [trueAst] of - [Left err] -> Left err : optimizeAst stack xs - [Right (Result trueAst')] -> optimizeAst stack (Cond condAst trueAst' mFalseAst : xs) - [Right (Warning _ trueAst')] -> optimizeAst stack (Cond condAst trueAst' mFalseAst : xs) - _ -> Right (Warning "This situation shouldn't happen" (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs - | isJust mFalseAst && not (isUnoptimizable (fromJust mFalseAst)) = case optimizeAst stack [fromJust mFalseAst] of - [Left err] -> Left err : optimizeAst stack xs - [Right (Result falseAst')] -> optimizeAst stack (Cond condAst trueAst (Just falseAst') : xs) - [Right (Warning _ falseAst')] -> optimizeAst stack (Cond condAst trueAst (Just falseAst') : xs) - _ -> Right (Warning "This situation shouldn't happen" (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs + (Left ('S':'y':'m':'b':'o':'l':' ':'\'':xs'), _) + | inFunc -> Right (Result (Call op asts)) : optimizeAst stack xs inFunc + | otherwise -> Left (Error ('S':'y':'m':'b':'o':'l':' ':'\'':xs') (Call op asts)) : optimizeAst stack xs inFunc + (Left err, _) -> Left (Error err (Call op asts)) : optimizeAst stack xs inFunc + (Right (Just _), stack') -> Right (Result (Call op asts)) : optimizeAst stack' xs inFunc + _ -> Right (Warning "This situation shouldn't happen" (Call op asts)) : optimizeAst stack xs inFunc + | otherwise = case sequence (optimizeAst stack asts inFunc) of + Left err -> Left err : optimizeAst stack xs inFunc + Right asts' -> optimizeAst stack (Call op (map fromOptimised asts') : xs) inFunc +optimizeAst stack ((Cond condAst trueAst mFalseAst) : xs) inFunc + | not (isUnoptimizable condAst) = case optimizeAst stack [condAst] inFunc of + [Left err] -> Left err : optimizeAst stack xs inFunc + [Right (Result condAst')] -> optimizeAst stack (Cond condAst' trueAst mFalseAst : xs) inFunc + [Right (Warning _ condAst')] -> optimizeAst stack (Cond condAst' trueAst mFalseAst : xs) inFunc + _ -> Right (Warning "This situation shouldn't happen" (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs inFunc + | not (isUnoptimizable trueAst) = case optimizeAst stack [trueAst] inFunc of + [Left err] -> Left err : optimizeAst stack xs inFunc + [Right (Result trueAst')] -> optimizeAst stack (Cond condAst trueAst' mFalseAst : xs) inFunc + [Right (Warning _ trueAst')] -> optimizeAst stack (Cond condAst trueAst' mFalseAst : xs) inFunc + _ -> Right (Warning "This situation shouldn't happen" (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs inFunc + | isJust mFalseAst && not (isUnoptimizable (fromJust mFalseAst)) = case optimizeAst stack [fromJust mFalseAst] inFunc of + [Left err] -> Left err : optimizeAst stack xs inFunc + [Right (Result falseAst')] -> optimizeAst stack (Cond condAst trueAst (Just falseAst') : xs) inFunc + [Right (Warning _ falseAst')] -> optimizeAst stack (Cond condAst trueAst (Just falseAst') : xs) inFunc + _ -> Right (Warning "This situation shouldn't happen" (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs inFunc | otherwise = case condAst of - Boolean True -> Right (Warning "Condition is always true" trueAst) : optimizeAst stack xs + Boolean True -> Right (Warning "Condition is always true" trueAst) : optimizeAst stack xs inFunc Boolean False -> Right ( Warning "Condition is always false" (fromMaybe (Cond condAst trueAst mFalseAst) mFalseAst) ) - : optimizeAst stack xs - _ -> Right (Result (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs -optimizeAst stack ((FunctionValue params ast Nothing) : xs) = case optimizeAst stack [ast] of - [Left err] -> Left err : optimizeAst stack xs - [Right (Result ast')] -> Right (Result (FunctionValue params ast' Nothing)) : optimizeAst stack xs - [Right (Warning mes ast')] -> Right (Warning mes (FunctionValue params ast' Nothing)) : optimizeAst stack xs - _ -> Right (Warning "This situation shouldn't happen" (FunctionValue params ast Nothing)) : optimizeAst stack xs -optimizeAst stack ((FunctionValue params ast (Just asts)) : xs) - | not (isUnoptimizable ast) = case optimizeAst stack [ast] of - [Left err] -> Left err : optimizeAst stack xs - [Right (Result ast')] -> optimizeAst stack (FunctionValue params ast' Nothing : xs) - [Right (Warning _ ast')] -> optimizeAst stack (FunctionValue params ast' Nothing : xs) - _ -> Right (Warning "This situation shouldn't happen" (FunctionValue params ast (Just asts))) : optimizeAst stack xs - | not (foldr ((&&) . isUnoptimizable) True asts) = case sequence (optimizeAst stack asts) of - Left err -> Left err : optimizeAst stack xs - Right asts' -> optimizeAst stack (FunctionValue params ast (Just (map fromOptimised asts')) : xs) + : optimizeAst stack xs inFunc + _ -> Right (Result (Cond condAst trueAst mFalseAst)) : optimizeAst stack xs inFunc +optimizeAst stack ((FunctionValue params ast Nothing) : xs) inFunc = case optimizeAst stack [ast] True of + [Left err] -> Left err : optimizeAst stack xs inFunc + [Right (Result ast')] -> Right (Result (FunctionValue params ast' Nothing)) : optimizeAst stack xs inFunc + [Right (Warning mes ast')] -> Right (Warning mes (FunctionValue params ast' Nothing)) : optimizeAst stack xs inFunc + _ -> Right (Warning "This situation shouldn't happen" (FunctionValue params ast Nothing)) : optimizeAst stack xs inFunc +optimizeAst stack ((FunctionValue params ast (Just asts)) : xs) inFunc + | not (isUnoptimizable ast) = case optimizeAst stack [ast] True of + [Left err] -> Left err : optimizeAst stack xs inFunc + [Right (Result ast')] -> optimizeAst stack (FunctionValue params ast' Nothing : xs) inFunc + [Right (Warning _ ast')] -> optimizeAst stack (FunctionValue params ast' Nothing : xs) inFunc + _ -> Right (Warning "This situation shouldn't happen" (FunctionValue params ast (Just asts))) : optimizeAst stack xs inFunc + | not (foldr ((&&) . isUnoptimizable) True asts) = case sequence (optimizeAst stack asts inFunc) of + Left err -> Left err : optimizeAst stack xs inFunc + Right asts' -> optimizeAst stack (FunctionValue params ast (Just (map fromOptimised asts')) : xs) inFunc | length params > length asts = case evalAst stack (FunctionValue params ast (Just asts)) of - (Left err, _) -> Left (Error err (FunctionValue params ast (Just asts))) : optimizeAst stack xs - (Right (Just ast'), stack') -> Right (Result ast') : optimizeAst stack' xs - (Right Nothing, _) -> Right (Warning "This situation shouldn't happen" (FunctionValue params ast (Just asts))) : optimizeAst stack xs - | otherwise = Right (Result (FunctionValue params ast (Just asts))) : optimizeAst stack xs -optimizeAst _ [] = [] + (Left err, _) -> Left (Error err (FunctionValue params ast (Just asts))) : optimizeAst stack xs inFunc + (Right (Just ast'), stack') -> Right (Result ast') : optimizeAst stack' xs inFunc + (Right Nothing, _) -> Right (Warning "This situation shouldn't happen" (FunctionValue params ast (Just asts))) : optimizeAst stack xs inFunc + | otherwise = Right (Result (FunctionValue params ast (Just asts))) : optimizeAst stack xs inFunc +optimizeAst _ [] _ = [] isUnoptimizable :: Ast -> Bool isUnoptimizable (Define _ ast) = isUnoptimizable ast From 905291008ac5d96a3ff734a05e5cda24acdb8248 Mon Sep 17 00:00:00 2001 From: Axel Humeau Date: Tue, 9 Jan 2024 11:03:44 +0100 Subject: [PATCH 08/15] test: test optimization for basic values and call --- LobsterLang/LobsterLang.cabal | 1 + LobsterLang/src/AstOptimizer.hs | 2 ++ LobsterLang/test/AstOptimizerSpec.hs | 53 ++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 LobsterLang/test/AstOptimizerSpec.hs diff --git a/LobsterLang/LobsterLang.cabal b/LobsterLang/LobsterLang.cabal index 807ffd7..db06005 100644 --- a/LobsterLang/LobsterLang.cabal +++ b/LobsterLang/LobsterLang.cabal @@ -63,6 +63,7 @@ test-suite LobsterLang-test main-is: Spec.hs other-modules: AstEvalSpec + AstOptimizerSpec VmSpec Paths_LobsterLang autogen-modules: diff --git a/LobsterLang/src/AstOptimizer.hs b/LobsterLang/src/AstOptimizer.hs index 07132ff..326913d 100644 --- a/LobsterLang/src/AstOptimizer.hs +++ b/LobsterLang/src/AstOptimizer.hs @@ -7,6 +7,8 @@ module AstOptimizer ( optimizeAst, + AstError(..), + AstOptimised(..), ) where diff --git a/LobsterLang/test/AstOptimizerSpec.hs b/LobsterLang/test/AstOptimizerSpec.hs new file mode 100644 index 0000000..719a42d --- /dev/null +++ b/LobsterLang/test/AstOptimizerSpec.hs @@ -0,0 +1,53 @@ +{- +-- EPITECH PROJECT, 2024 +-- GLaDOS +-- File description: +-- AstOptimizerSpec +-} + +module AstOptimizerSpec where + +import Test.Hspec +import AST +import AstOptimizer +import Scope + +spec :: Spec +spec = do + describe "Value Ast optimization tests" $ do + it "Basic Value" $ do + optimizeAst [] [Value 5] False `shouldBe` [Right (Result (Value 5))] + it "Basic Value (multiple)" $ do + optimizeAst [] [Value 5, Value 8] False `shouldBe` [Right (Result (Value 5)), Right (Result (Value 8))] + describe "Boolean Ast optimization tests" $ do + it "Basic Boolean" $ do + optimizeAst [] [Boolean True] False `shouldBe` [Right (Result (Boolean True))] + it "Basic Boolean (multiple)" $ do + optimizeAst [] [Boolean True, Boolean False] False `shouldBe` [Right (Result (Boolean True)), Right (Result (Boolean False))] + describe "String Ast optimization tests" $ do + it "Basic String" $ do + optimizeAst [] [String "blegh"] False `shouldBe` [Right (Result (String "blegh"))] + it "Basic String (multiple)" $ do + optimizeAst [] [String "blegh", String False] "aaaaa" `shouldBe` [Right (Result (String "blegh")), Right (Result (String "aaaaa"))] + describe "Operator Ast optimization tests" $ do + it "Optimize +" $ do + optimizeAst [] [Call "+" [Value 5, Value 8]] False `shouldBe` [Right (Result (Value 13))] + it "Optimize -" $ do + optimizeAst [] [Call "-" [Value 5, Value 8]] False `shouldBe` [Right (Result (Value (-3)))] + it "Optimize &&" $ do + optimizeAst [] [Call "&&" [Boolean True, Boolean False]] False `shouldBe` [Right (Result (Boolean False))] + it "Optimize !" $ do + optimizeAst [] [Call "!" [Boolean True]] False `shouldBe` [Right (Result (Boolean False))] + it "Optimize @" $ do + optimizeAst [] [Call "@" [Value 56]] False `shouldBe` [Right (Result (String "56"))] + it "Optimize nested operators" $ do + optimizeAst [] [Call "*" [Call "+" [Value 8, Value 2], Call "-" [Value 9, Call "%" [Value 126, Value 10]]]] False `shouldBe` [Right (Result (Value 30))] + it "Optimize + with symbol" $ do + optimizeAst [Variable "a" (Value 5) 0] [Call "+" [Symbol "a" Nothing, Value 8]] False `shouldBe` [Right (Result (Call "+" [Symbol "a" Nothing, Value 8]))] + it "Error not Value" $ do + optimizeAst [] [Call "+" [List [Value 8, Value 9], Value 8]] False `shouldBe` [Left (Error "One or more parameters of binary operator '+' is invalid" (Call "+" [List [Value 8, Value 9], Value 8]))] + it "Error symbol doesn't exist" $ do + optimizeAst [] [Call "+" [Symbol "a" Nothing, Value 8]] False `shouldBe` [Left (Error "Symbol 'a' doesn't exist in the current or global scope" (Call "+" [Symbol "a" Nothing, Value 8]))] + describe "Advanced Ast optimization tests" $ do + it "Call then symbol" $ do + optimizeAst [Variable "a" (Value 5) 0] [Call "-" [Value 5, Value 8], Call "+" [Symbol "a" Nothing, Value 8]] False `shouldBe` [Right (Result (Value (-3))), Right (Result (Call "+" [Symbol "a" Nothing, Value 8]))] From e352784894f3319f1477bd36efadee9b637de6d6 Mon Sep 17 00:00:00 2001 From: Axel Humeau Date: Tue, 9 Jan 2024 19:12:43 +0100 Subject: [PATCH 09/15] test: add test for optimization of list and define --- LobsterLang/src/AstOptimizer.hs | 10 +++++++--- LobsterLang/test/AstOptimizerSpec.hs | 18 +++++++++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/LobsterLang/src/AstOptimizer.hs b/LobsterLang/src/AstOptimizer.hs index 326913d..fcdf085 100644 --- a/LobsterLang/src/AstOptimizer.hs +++ b/LobsterLang/src/AstOptimizer.hs @@ -15,7 +15,7 @@ where import AST import AstEval import Data.Maybe -import Scope (ScopeMb) +import Scope (ScopeMb, getVarInScope) data AstError = Error String Ast deriving (Eq, Show) @@ -28,7 +28,7 @@ optimizeAst :: [ScopeMb] -> [Ast] -> Bool -> [Either AstError AstOptimised] optimizeAst stack ((Value v) : xs) inFunc = Right (Result (Value v)) : optimizeAst stack xs inFunc optimizeAst stack ((Boolean b) : xs) inFunc = Right (Result (Boolean b)) : optimizeAst stack xs inFunc optimizeAst stack ((String str) : xs) inFunc = Right (Result (String str)) : optimizeAst stack xs inFunc -optimizeAst stack ((List asts) : xs) inFunc= case sequence (optimizeAst stack asts inFunc) of +optimizeAst stack ((List asts) : xs) inFunc = case sequence (optimizeAst stack asts inFunc) of Left err -> Left err : optimizeAst stack xs inFunc Right opAst -> Right (Result (List (map fromOptimised opAst))) : optimizeAst stack xs inFunc optimizeAst stack ((Define n ast) : xs) inFunc = case optimizeAst stack [ast] inFunc of @@ -36,7 +36,11 @@ optimizeAst stack ((Define n ast) : xs) inFunc = case optimizeAst stack [ast] in [Right (Result opAst)] -> Right (Result (Define n opAst)) : optimizeAst stack xs inFunc [Right (Warning mes opAst)] -> Right (Warning mes (Define n opAst)) : optimizeAst stack xs inFunc _ -> Right (Warning "This situation shouldn't happen" (Define n ast)) : optimizeAst stack xs inFunc -optimizeAst stack ((Symbol s Nothing) : xs) inFunc = Right (Result (Symbol s Nothing)) : optimizeAst stack xs inFunc +optimizeAst stack ((Symbol s Nothing) : xs) inFunc + | inFunc = Right (Result (Symbol s Nothing)) : optimizeAst stack xs inFunc + | otherwise = case getVarInScope stack s of + Nothing -> Left (Error ("Symbol '" ++ s ++ "' doesn't exist in the current or global scope") (Symbol s Nothing)) : optimizeAst stack xs inFunc + Just _ -> Right (Result (Symbol s Nothing)) : optimizeAst stack xs inFunc optimizeAst stack ((Symbol s (Just asts)) : xs) inFunc | foldr ((&&) . isUnoptimizable) True asts = case evalAst stack (Symbol s (Just asts)) of (Left ('S':'y':'m':'b':'o':'l':' ':'\'':xs'), _) diff --git a/LobsterLang/test/AstOptimizerSpec.hs b/LobsterLang/test/AstOptimizerSpec.hs index 719a42d..3119587 100644 --- a/LobsterLang/test/AstOptimizerSpec.hs +++ b/LobsterLang/test/AstOptimizerSpec.hs @@ -28,7 +28,23 @@ spec = do it "Basic String" $ do optimizeAst [] [String "blegh"] False `shouldBe` [Right (Result (String "blegh"))] it "Basic String (multiple)" $ do - optimizeAst [] [String "blegh", String False] "aaaaa" `shouldBe` [Right (Result (String "blegh")), Right (Result (String "aaaaa"))] + optimizeAst [] [String "blegh", String "aaaaa"] False `shouldBe` [Right (Result (String "blegh")), Right (Result (String "aaaaa"))] + describe "List Ast optimization tests" $ do + it "Empty list" $ do + optimizeAst [] [List []] False `shouldBe` [Right (Result (List []))] + it "Unoptimizable list" $ do + optimizeAst [] [List [Value 5, Boolean True, Value 9, String "vzb"]] False `shouldBe` [Right (Result (List [Value 5, Boolean True, Value 9, String "vzb"]))] + it "Unoptimizable list 2" $ do + optimizeAst [Variable "a" (Value 5) 0] [List [Value 5, Symbol "a" Nothing]] False `shouldBe` [Right (Result (List [Value 5, Symbol "a" Nothing]))] + it "Unoptimizable list error" $ do + optimizeAst [] [List [Value 5, Symbol "a" Nothing]] False `shouldBe` [Left (Error "Symbol 'a' doesn't exist in the current or global scope" (Symbol "a" Nothing))] + describe "Define Ast optimization tests" $ do + it "Unoptimizable Define" $ do + optimizeAst [] [Define "a" (Value 5)] False `shouldBe` [Right (Result (Define "a" (Value 5)))] + it "Optimizable Define" $ do + optimizeAst [] [Define "a" (Call "+" [Value 5, Value 5])] False `shouldBe` [Right (Result (Define "a" (Value 10)))] + it "Error Define" $ do + optimizeAst [] [Define "a" (Call "+" [Value 5])] False `shouldBe` [Left (Error "Not enough parameter for binary operator '+'" (Call "+" [Value 5]))] describe "Operator Ast optimization tests" $ do it "Optimize +" $ do optimizeAst [] [Call "+" [Value 5, Value 8]] False `shouldBe` [Right (Result (Value 13))] From e713b8d5b527460f75c543fac0d4f31d207c0c7e Mon Sep 17 00:00:00 2001 From: Axel Humeau Date: Wed, 10 Jan 2024 00:40:48 +0100 Subject: [PATCH 10/15] test: add test for symbols and improve define tests --- LobsterLang/src/AstOptimizer.hs | 9 +++++++-- LobsterLang/test/AstOptimizerSpec.hs | 27 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/LobsterLang/src/AstOptimizer.hs b/LobsterLang/src/AstOptimizer.hs index fcdf085..f75d006 100644 --- a/LobsterLang/src/AstOptimizer.hs +++ b/LobsterLang/src/AstOptimizer.hs @@ -33,7 +33,12 @@ optimizeAst stack ((List asts) : xs) inFunc = case sequence (optimizeAst stack a Right opAst -> Right (Result (List (map fromOptimised opAst))) : optimizeAst stack xs inFunc optimizeAst stack ((Define n ast) : xs) inFunc = case optimizeAst stack [ast] inFunc of [Left err] -> Left err : optimizeAst stack xs inFunc - [Right (Result opAst)] -> Right (Result (Define n opAst)) : optimizeAst stack xs inFunc + [Right (Result opAst)] -> case evalAst stack (Define n opAst) of + (Right _, stack') -> Right (Result (Define n opAst)) : optimizeAst stack' xs inFunc + (Left ('S':'y':'m':'b':'o':'l':' ':'\'':xs'), _) + | inFunc -> Right (Result (Define n opAst)) : optimizeAst stack xs inFunc + | otherwise -> Left (Error ('S':'y':'m':'b':'o':'l':' ':'\'':xs') (Define n opAst)) : optimizeAst stack xs inFunc + (Left err, _) -> Left (Error err (Define n opAst)) : optimizeAst stack xs inFunc [Right (Warning mes opAst)] -> Right (Warning mes (Define n opAst)) : optimizeAst stack xs inFunc _ -> Right (Warning "This situation shouldn't happen" (Define n ast)) : optimizeAst stack xs inFunc optimizeAst stack ((Symbol s Nothing) : xs) inFunc @@ -51,7 +56,7 @@ optimizeAst stack ((Symbol s (Just asts)) : xs) inFunc _ -> Right (Warning "This situation shouldn't happen" (Symbol s (Just asts))) : optimizeAst stack xs inFunc | otherwise = case sequence (optimizeAst stack asts inFunc) of Left err -> Left err : optimizeAst stack xs inFunc - Right opAst -> Right (Result (Symbol s (Just (map fromOptimised opAst)))) : optimizeAst stack xs inFunc + Right opAst -> optimizeAst stack (Symbol s (Just (map fromOptimised opAst)):xs) inFunc optimizeAst stack ((Call op asts) : xs) inFunc | foldr ((&&) . isUnoptimizable) True asts && foldr ((&&) . isValue) True asts = case evalAst stack (Call op asts) of diff --git a/LobsterLang/test/AstOptimizerSpec.hs b/LobsterLang/test/AstOptimizerSpec.hs index 3119587..7c8c3ea 100644 --- a/LobsterLang/test/AstOptimizerSpec.hs +++ b/LobsterLang/test/AstOptimizerSpec.hs @@ -45,6 +45,31 @@ spec = do optimizeAst [] [Define "a" (Call "+" [Value 5, Value 5])] False `shouldBe` [Right (Result (Define "a" (Value 10)))] it "Error Define" $ do optimizeAst [] [Define "a" (Call "+" [Value 5])] False `shouldBe` [Left (Error "Not enough parameter for binary operator '+'" (Call "+" [Value 5]))] + it "Error Define 2" $ do + optimizeAst [] [Define "a" (Define "b" (Value 2))] False `shouldBe` [Left (Error "Cannot define with no value" (Define "a" (Define "b" (Value 2))))] + it "Error Define 3" $ do + optimizeAst [] [Define "a" (Symbol "b" Nothing)] False `shouldBe` [Left (Error "Symbol 'b' doesn't exist in the current or global scope" (Symbol "b" Nothing))] + it "Define from symbol in function" $ do + optimizeAst [] [Define "a" (Symbol "b" Nothing)] True `shouldBe` [Right (Result (Define "a" (Symbol "b" Nothing)))] + describe "Symbol Ast optimization tests" $ do + it "Simple symbol (in function)" $ do + optimizeAst [] [Symbol "a" Nothing] True `shouldBe` [Right (Result (Symbol "a" Nothing))] + it "Simple symbol (out of function)" $ do + optimizeAst [Variable "a" (Value 5) 0] [Symbol "a" Nothing] False `shouldBe` [Right (Result (Symbol "a" Nothing))] + it "Error doesn't exist symbol (out of function)" $ do + optimizeAst [] [Symbol "a" Nothing] False `shouldBe` [Left (Error "Symbol 'a' doesn't exist in the current or global scope" (Symbol "a" Nothing))] + it "Error function symbol (in function) with unoptimizable params (doesn't exist)" $ do + optimizeAst [] [Symbol "a" (Just [Value 5])] True `shouldBe` [Right (Result (Symbol "a" (Just [Value 5])))] + it "Error function symbol (out of function) with unoptimizable params" $ do + optimizeAst [] [Symbol "a" (Just [Value 5])] False `shouldBe` [Left (Error "Symbol 'a' doesn't exist in the current or global scope" (Symbol "a" (Just [Value 5])))] + it "Simple function symbol (out of function) with unoptimizable params" $ do + optimizeAst [Variable "a" (FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Value 1]) Nothing) 0] [Symbol "a" (Just [Value 5])] False `shouldBe` [Right (Result (Symbol "a" (Just [Value 5])))] + it "Error function symbol (out of function) with unoptimizable params" $ do + optimizeAst [Variable "a" (FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Value 1]) Nothing) 0] [Symbol "a" (Just [Value 5, Value 6])] False `shouldBe` [Left (Error "Expression takes 1 parameters, got 2" (Symbol "a" (Just [Value 5, Value 6])))] + it "Simple function symbol (out of function) with optimizable params" $ do + optimizeAst [Variable "a" (FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Value 1]) Nothing) 0] [Symbol "a" (Just [Call "+" [Value 1, Value 5]])] False `shouldBe` [Right (Result (Symbol "a" (Just [Value 6])))] + it "Error function symbol (out of function) with optimizable params" $ do + optimizeAst [Variable "a" (FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Value 1]) Nothing) 0] [Symbol "a" (Just [Call "+" [Value 1, Boolean True]])] False `shouldBe` [Left (Error "One or more parameters of binary operator '+' is invalid" (Call "+" [Value 1, Boolean True]))] describe "Operator Ast optimization tests" $ do it "Optimize +" $ do optimizeAst [] [Call "+" [Value 5, Value 8]] False `shouldBe` [Right (Result (Value 13))] @@ -67,3 +92,5 @@ spec = do describe "Advanced Ast optimization tests" $ do it "Call then symbol" $ do optimizeAst [Variable "a" (Value 5) 0] [Call "-" [Value 5, Value 8], Call "+" [Symbol "a" Nothing, Value 8]] False `shouldBe` [Right (Result (Value (-3))), Right (Result (Call "+" [Symbol "a" Nothing, Value 8]))] + it "Define then call" $ do + optimizeAst [] [Define "a" (Value 5), Call "-" [Symbol "a" Nothing, Value 8]] False `shouldBe` [Right (Result (Define "a" (Value 5))), Right (Result (Call "-" [Symbol "a" Nothing, Value 8]))] From 01a552d08b8dd50ef43e36e19e933dcba43ad042 Mon Sep 17 00:00:00 2001 From: Axel Humeau Date: Wed, 10 Jan 2024 15:42:52 +0100 Subject: [PATCH 11/15] test: add optimizer tests for cond ast --- LobsterLang/test/AstOptimizerSpec.hs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/LobsterLang/test/AstOptimizerSpec.hs b/LobsterLang/test/AstOptimizerSpec.hs index 7c8c3ea..76e958b 100644 --- a/LobsterLang/test/AstOptimizerSpec.hs +++ b/LobsterLang/test/AstOptimizerSpec.hs @@ -89,6 +89,31 @@ spec = do optimizeAst [] [Call "+" [List [Value 8, Value 9], Value 8]] False `shouldBe` [Left (Error "One or more parameters of binary operator '+' is invalid" (Call "+" [List [Value 8, Value 9], Value 8]))] it "Error symbol doesn't exist" $ do optimizeAst [] [Call "+" [Symbol "a" Nothing, Value 8]] False `shouldBe` [Left (Error "Symbol 'a' doesn't exist in the current or global scope" (Call "+" [Symbol "a" Nothing, Value 8]))] + describe "Cond Ast Optimizations" $ do + it "Optimize condition in Cond" $ do + optimizeAst [Variable "a" (Boolean True) 0] [Cond (Call "&&" [Call "||" [Boolean True, Boolean False], Symbol "a" Nothing]) (Value 1) Nothing] False `shouldBe` [Right (Result (Cond (Call "&&" [Boolean True, Symbol "a" Nothing]) (Value 1) Nothing))] + it "Optimize true ast in Cond" $ do + optimizeAst [Variable "a" (Boolean True) 0] [Cond (Call "&&" [Symbol "a" Nothing, Boolean True]) (Call "+" [Value 5, Value 8]) Nothing] False `shouldBe` [Right (Result (Cond (Call "&&" [Symbol "a" Nothing, Boolean True]) (Value 13) Nothing))] + it "Optimize false ast in Cond" $ do + optimizeAst [Variable "a" (Boolean True) 0] [Cond (Call "&&" [Symbol "a" Nothing, Boolean True]) (Value 1) (Just (Call "+" [Value 5, Value 8]))] False `shouldBe` [Right (Result (Cond (Call "&&" [Symbol "a" Nothing, Boolean True]) (Value 1) (Just (Value 13))))] + it "Optimize condition in Cond error" $ do + optimizeAst [] [Cond (Call "&&" [Symbol "a" Nothing, Call "||" [Boolean True, Boolean False]]) (Value 1) Nothing] False `shouldBe` [Left (Error "Symbol 'a' doesn't exist in the current or global scope" (Symbol "a" Nothing))] + it "Optimize true ast in Cond error" $ do + optimizeAst [Variable "a" (Boolean True) 0] [Cond (Call "&&" [Symbol "a" Nothing, Boolean True]) (Call "+" [String "bleg", Value 8]) Nothing] False `shouldBe` [Left (Error "One or more parameters of binary operator '+' is invalid" (Call "+" [String "bleg", Value 8]))] + it "Optimize false ast in Cond error" $ do + optimizeAst [Variable "a" (Boolean True) 0] [Cond (Call "&&" [Symbol "a" Nothing, Boolean True]) (Value 1) (Just (Call "+" [Value 5, List [String "bleg"]]))] False `shouldBe` [Left (Error "One or more parameters of binary operator '+' is invalid" (Call "+" [Value 5, List [String "bleg"]]))] + it "Optimize condition in Cond warning" $ do + optimizeAst [Variable "a" (Boolean True) 0] [Cond (Cond (Boolean True) (Symbol "a" Nothing) Nothing) (Value 1) Nothing] False `shouldBe` [Right (Result (Cond (Symbol "a" Nothing) (Value 1) Nothing))] + it "Optimize true ast in Cond warning" $ do + optimizeAst [Variable "a" (Boolean True) 0] [Cond (Call "&&" [Symbol "a" Nothing, Boolean True]) (Cond (Boolean True) (Symbol "a" Nothing) Nothing) Nothing] False `shouldBe` [Right (Result (Cond (Call "&&" [Symbol "a" Nothing, Boolean True]) (Symbol "a" Nothing) Nothing))] + it "Optimize false ast in Cond warning" $ do + optimizeAst [Variable "a" (Boolean True) 0] [Cond (Call "&&" [Symbol "a" Nothing, Boolean True]) (Value 1) (Just (Cond (Boolean True) (Symbol "a" Nothing) Nothing))] False `shouldBe` [Right (Result (Cond (Call "&&" [Symbol "a" Nothing, Boolean True]) (Value 1) (Just (Symbol "a" Nothing))))] + it "Optimize always true" $ do + optimizeAst [] [Cond (Boolean True) (Value 1) (Just (Value 8))] False `shouldBe` [Right (Warning "Condition is always true" (Value 1))] + it "Optimize always false" $ do + optimizeAst [] [Cond (Boolean False) (Value 1) (Just (Value 8))] False `shouldBe` [Right (Warning "Condition is always false" (Value 8))] + it "Optimize always false (no else)" $ do + optimizeAst [] [Cond (Boolean False) (Value 1) Nothing] False `shouldBe` [Right (Warning "Condition is always false" (Cond (Boolean False) (Value 1) Nothing))] describe "Advanced Ast optimization tests" $ do it "Call then symbol" $ do optimizeAst [Variable "a" (Value 5) 0] [Call "-" [Value 5, Value 8], Call "+" [Symbol "a" Nothing, Value 8]] False `shouldBe` [Right (Result (Value (-3))), Right (Result (Call "+" [Symbol "a" Nothing, Value 8]))] From 8ffd32de6356079f64917eca4922009063425175 Mon Sep 17 00:00:00 2001 From: Axel Humeau Date: Wed, 10 Jan 2024 19:06:49 +0100 Subject: [PATCH 12/15] test: ad tests for optimization of functions --- LobsterLang/src/AstOptimizer.hs | 12 ++++++--- LobsterLang/test/AstOptimizerSpec.hs | 40 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/LobsterLang/src/AstOptimizer.hs b/LobsterLang/src/AstOptimizer.hs index f75d006..db69279 100644 --- a/LobsterLang/src/AstOptimizer.hs +++ b/LobsterLang/src/AstOptimizer.hs @@ -110,8 +110,8 @@ optimizeAst stack ((FunctionValue params ast Nothing) : xs) inFunc = case optimi optimizeAst stack ((FunctionValue params ast (Just asts)) : xs) inFunc | not (isUnoptimizable ast) = case optimizeAst stack [ast] True of [Left err] -> Left err : optimizeAst stack xs inFunc - [Right (Result ast')] -> optimizeAst stack (FunctionValue params ast' Nothing : xs) inFunc - [Right (Warning _ ast')] -> optimizeAst stack (FunctionValue params ast' Nothing : xs) inFunc + [Right (Result ast')] -> optimizeAst stack (FunctionValue params ast' (Just asts) : xs) inFunc + [Right (Warning _ ast')] -> optimizeAst stack (FunctionValue params ast' (Just asts) : xs) inFunc _ -> Right (Warning "This situation shouldn't happen" (FunctionValue params ast (Just asts))) : optimizeAst stack xs inFunc | not (foldr ((&&) . isUnoptimizable) True asts) = case sequence (optimizeAst stack asts inFunc) of Left err -> Left err : optimizeAst stack xs inFunc @@ -120,7 +120,13 @@ optimizeAst stack ((FunctionValue params ast (Just asts)) : xs) inFunc (Left err, _) -> Left (Error err (FunctionValue params ast (Just asts))) : optimizeAst stack xs inFunc (Right (Just ast'), stack') -> Right (Result ast') : optimizeAst stack' xs inFunc (Right Nothing, _) -> Right (Warning "This situation shouldn't happen" (FunctionValue params ast (Just asts))) : optimizeAst stack xs inFunc - | otherwise = Right (Result (FunctionValue params ast (Just asts))) : optimizeAst stack xs inFunc + | otherwise = case evalAst stack (FunctionValue params ast (Just asts)) of + (Left ('S':'y':'m':'b':'o':'l':' ':'\'':xs'), _) + | inFunc -> Right (Result (FunctionValue params ast (Just asts))) : optimizeAst stack xs inFunc + | otherwise -> Left (Error ('S':'y':'m':'b':'o':'l':' ':'\'':xs') (FunctionValue params ast (Just asts))) : optimizeAst stack xs inFunc + (Left err, _) -> Left (Error err (FunctionValue params ast (Just asts))) : optimizeAst stack xs inFunc + (Right (Just _), stack') -> Right (Result (FunctionValue params ast (Just asts))) : optimizeAst stack' xs inFunc + _ -> Right (Warning "This situation shouldn't happen" (FunctionValue params ast (Just asts))) : optimizeAst stack xs inFunc optimizeAst _ [] _ = [] isUnoptimizable :: Ast -> Bool diff --git a/LobsterLang/test/AstOptimizerSpec.hs b/LobsterLang/test/AstOptimizerSpec.hs index 76e958b..3c1ee6c 100644 --- a/LobsterLang/test/AstOptimizerSpec.hs +++ b/LobsterLang/test/AstOptimizerSpec.hs @@ -32,6 +32,8 @@ spec = do describe "List Ast optimization tests" $ do it "Empty list" $ do optimizeAst [] [List []] False `shouldBe` [Right (Result (List []))] + it "Optimizable list" $ do + optimizeAst [] [List [Call "+" [Value 1, Value 2], Value 5]] False `shouldBe` [Right (Result (List [Value 3, Value 5]))] it "Unoptimizable list" $ do optimizeAst [] [List [Value 5, Boolean True, Value 9, String "vzb"]] False `shouldBe` [Right (Result (List [Value 5, Boolean True, Value 9, String "vzb"]))] it "Unoptimizable list 2" $ do @@ -114,6 +116,44 @@ spec = do optimizeAst [] [Cond (Boolean False) (Value 1) (Just (Value 8))] False `shouldBe` [Right (Warning "Condition is always false" (Value 8))] it "Optimize always false (no else)" $ do optimizeAst [] [Cond (Boolean False) (Value 1) Nothing] False `shouldBe` [Right (Warning "Condition is always false" (Cond (Boolean False) (Value 1) Nothing))] + it "Unoptimizable Cond" $ do + optimizeAst [Variable "a" (Boolean True) 0] [Cond (Call "&&" [Boolean True, Symbol "a" Nothing]) (Value 1) Nothing] False `shouldBe` [Right (Result (Cond (Call "&&" [Boolean True, Symbol "a" Nothing]) (Value 1) Nothing))] + describe "FunctionValue Ast optimization tests" $ do + -- without params + it "Optimize inner ast in FunctionValue error" $ do + optimizeAst [] [FunctionValue ["x"] (Call "$" [Define "a" (Call "+" [Boolean True, Value 1]), Call "+" [Symbol "a" Nothing, Value 1]]) Nothing] False `shouldBe` [Left (Error "One or more parameters of binary operator '+' is invalid" (Call "+" [Boolean True, Value 1]))] + it "Optimize inner ast in FunctionValue" $ do + optimizeAst [] [FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Call "+" [Value 1, Value 1]]) Nothing] False `shouldBe` [Right (Result (FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Value 2]) Nothing))] + it "Optimize inner ast in FunctionValue warning" $ do + optimizeAst [] [FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Cond (Boolean True) (Value 2) Nothing]) Nothing] False `shouldBe` [Right (Result (FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Value 2]) Nothing))] + -- with params (inner) + it "Optimize inner ast in FunctionValue with params" $ do + optimizeAst [] [FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Call "+" [Value 1, Value 1]]) (Just [Value 2])] False `shouldBe` [Right (Result (FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Value 2]) (Just [Value 2])))] + it "Optimize inner ast in FunctionValue with params warning" $ do + optimizeAst [] [FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Cond (Boolean True) (Value 2) Nothing]) (Just [Value 2])] False `shouldBe` [Right (Result (FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Value 2]) (Just [Value 2])))] + it "Optimize inner ast in FunctionValue with params error" $ do + optimizeAst [] [FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Call "+" [Boolean True, Value 1]]) (Just [Value 2])] False `shouldBe` [Left (Error "One or more parameters of binary operator '+' is invalid" (Call "+" [Boolean True, Value 1]))] + -- with params (params) + it "Empty params" $ do + optimizeAst [] [FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Value 2]) (Just [])] False `shouldBe` [Right (Result (FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Value 2]) Nothing))] + it "Unoptimizable params error" $ do + optimizeAst [] [FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Value 2]) (Just [Call "+" [Value 1, Boolean True]])] False `shouldBe` [Left (Error "One or more parameters of binary operator '+' is invalid" (Call "+" [Value 1, Boolean True]))] + it "Optimizable params" $ do + optimizeAst [] [FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Value 2]) (Just [Call "+" [Value 1, Value 1]])] False `shouldBe` [Right (Result (FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Value 2]) (Just [Value 2])))] + it "Unoptimizable params" $ do + optimizeAst [] [FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Value 2]) (Just [Value 2])] False `shouldBe` [Right (Result (FunctionValue ["x"] (Call "+" [Symbol "x" Nothing, Value 2]) (Just [Value 2])))] + -- currying + it "Currying" $ do + optimizeAst [] [FunctionValue ["a", "b"] (Call "+" [Symbol "a" Nothing, Symbol "b" Nothing]) (Just [Value 2])] False `shouldBe` [Right (Result (FunctionValue ["b"] (Call "$" [Define "a" (Value 2), Call "+" [Symbol "a" Nothing, Symbol "b" Nothing]]) Nothing))] + -- Check valaidity of func + it "Unoptimizable func with params" $ do + optimizeAst [] [FunctionValue ["a", "b"] (Call "+" [Symbol "a" Nothing, Symbol "b" Nothing]) (Just [Value 2, Value 3])] False `shouldBe` [Right (Result (FunctionValue ["a", "b"] (Call "+" [Symbol "a" Nothing, Symbol "b" Nothing]) (Just [Value 2, Value 3])))] + it "Unoptimizable func with params (symbol don't exist in func)" $ do + optimizeAst [] [FunctionValue ["a", "b"] (Call "+" [Symbol "a" Nothing, Symbol "b" Nothing]) (Just [Symbol "c" Nothing, Value 3])] True `shouldBe` [Right (Result (FunctionValue ["a", "b"] (Call "+" [Symbol "a" Nothing, Symbol "b" Nothing]) (Just [Symbol "c" Nothing, Value 3])))] + it "Unoptimizable func with params (symbol don't exist out of func)" $ do + optimizeAst [] [FunctionValue ["a", "b"] (Call "+" [Symbol "a" Nothing, Symbol "b" Nothing]) (Just [Symbol "c" Nothing, Value 3])] False `shouldBe` [Left (Error "Symbol 'c' doesn't exist in the current or global scope" (FunctionValue ["a", "b"] (Call "+" [Symbol "a" Nothing, Symbol "b" Nothing]) (Just [Symbol "c" Nothing, Value 3])))] + it "Unoptimizable func with params error" $ do + optimizeAst [] [FunctionValue ["a", "b"] (Call "+" [Symbol "a" Nothing, Symbol "b" Nothing]) (Just [Value 2, Boolean True])] False `shouldBe` [Left (Error "One or more parameters of binary operator '+' is invalid" (FunctionValue ["a", "b"] (Call "+" [Symbol "a" Nothing, Symbol "b" Nothing]) (Just [Value 2, Boolean True])))] describe "Advanced Ast optimization tests" $ do it "Call then symbol" $ do optimizeAst [Variable "a" (Value 5) 0] [Call "-" [Value 5, Value 8], Call "+" [Symbol "a" Nothing, Value 8]] False `shouldBe` [Right (Result (Value (-3))), Right (Result (Call "+" [Symbol "a" Nothing, Value 8]))] From cfc439dd0bc245425b127515e8d69ac0882471e4 Mon Sep 17 00:00:00 2001 From: Axel Humeau Date: Thu, 11 Jan 2024 16:20:32 +0100 Subject: [PATCH 13/15] feat: eval of parameters of '!' --- LobsterLang/src/AstEval.hs | 7 +++++-- LobsterLang/test/AstEvalSpec.hs | 6 ++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/LobsterLang/src/AstEval.hs b/LobsterLang/src/AstEval.hs index d89d262..80d84bb 100644 --- a/LobsterLang/src/AstEval.hs +++ b/LobsterLang/src/AstEval.hs @@ -74,8 +74,11 @@ evalAst stack (Call "&&" astList) = evalBiBoolOp (&&) stack (Call "&&" astList) evalAst stack (Call "||" astList) = evalBiBoolOp (||) stack (Call "||" astList) evalAst stack (Call "^^" astList) = evalBiBoolOp (\a b -> (a || b) && not (a && b)) stack (Call "||" astList) evalAst stack (Call "!" [AST.Boolean b]) = (Right (Just (AST.Boolean (not b))), stack) --- TODO: add ! support for evaluation of sub parameters -evalAst stack (Call "!" [_]) = (Left "Parameter of unary operator '!' isn't a boolean", stack) +evalAst stack (Call "!" [ast]) = case evalAst stack ast of + (Left err, _) -> (Left err, stack) + (Right (Just (Boolean b)), _) -> (Right (Just (AST.Boolean (not b))), stack) + (Right Nothing, _) -> (Left "No evaluation in parameter of unary operator '!'", stack) + (Right _, _) -> (Left "Parameter of unary operator '!' isn't a boolean", stack) evalAst stack (Call "!" _) = (Left "Invalid number of parameter for unary operator '!'", stack) evalAst stack (Call "@" [ast]) = case astToString stack ast of Left err -> (Left err, stack) diff --git a/LobsterLang/test/AstEvalSpec.hs b/LobsterLang/test/AstEvalSpec.hs index 628da23..a53bcaa 100644 --- a/LobsterLang/test/AstEvalSpec.hs +++ b/LobsterLang/test/AstEvalSpec.hs @@ -108,10 +108,16 @@ spec = do evalAst [] (Call "!" [AST.Boolean False]) `shouldBe` (Right (Just (AST.Boolean True)), []) it "Check valid operation ! (not) 2" $ do evalAst [] (Call "!" [AST.Boolean True]) `shouldBe` (Right (Just (AST.Boolean False)), []) + it "Check valid operation ! (not) with symbol" $ do + evalAst [Variable "a" (Boolean True) 0] (Call "!" [AST.Symbol "a" Nothing]) `shouldBe` (Right (Just (AST.Boolean False)), [Variable "a" (Boolean True) 0]) + it "Check valid operation ! (not) with eval" $ do + evalAst [] (Call "!" [Call "&&" [AST.Boolean True, AST.Boolean True]]) `shouldBe` (Right (Just (AST.Boolean False)), []) it "Check invalid operation ! (not)" $ do evalAst [] (Call "!" [AST.Boolean True, AST.Boolean False]) `shouldBe` (Left "Invalid number of parameter for unary operator '!'", []) it "Check invalid operation ! (not) 2" $ do evalAst [] (Call "!" [AST.Value 5]) `shouldBe` (Left "Parameter of unary operator '!' isn't a boolean", []) + it "Check invalid operation ! (not) with symbol" $ do + evalAst [Variable "a" (Value 8) 0] (Call "!" [Symbol "a" Nothing]) `shouldBe` (Left "Parameter of unary operator '!' isn't a boolean", [Variable "a" (Value 8) 0]) it "Check invalid value comparison binary operation (wrong type)" $ do evalBiBoolOp (&&) [] (Call "&&" [AST.Boolean True, AST.Value 8]) `shouldBe` (Left "One or more parameters of binary operator '&&' is invalid", []) it "Check invalid value comparison binary operation (wrong type 2)" $ do From 27842f433c8b753db65fc4779b920f969bc91ec3 Mon Sep 17 00:00:00 2001 From: Axel Humeau Date: Thu, 11 Jan 2024 17:11:21 +0100 Subject: [PATCH 14/15] feat: handle passing parameters to symbol that isn't a function as an error --- LobsterLang/src/AstEval.hs | 12 +++++++----- LobsterLang/test/AstEvalSpec.hs | 2 ++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/LobsterLang/src/AstEval.hs b/LobsterLang/src/AstEval.hs index 80d84bb..de6b362 100644 --- a/LobsterLang/src/AstEval.hs +++ b/LobsterLang/src/AstEval.hs @@ -50,7 +50,9 @@ evalAst stack (AST.Value i) = (Right (Just (AST.Value i)), stack) evalAst stack (AST.Symbol s asts) = case getVarInScope stack s of Nothing -> (Left ("Symbol '" ++ s ++ "' doesn't exist in the current or global scope"), stack) Just (FunctionValue params ast Nothing) -> evalAst stack (FunctionValue params ast asts) - Just value -> evalAst stack value + Just value -> case asts of + Nothing -> evalAst stack value + _ -> (Left ("Symbol '" ++ s ++ "' isn't a function"), stack) evalAst stack (AST.List l) = case evalSubParams stack l of (Left err) -> (Left err, stack) (Right (Just l')) -> (Right (Just (AST.List l')), stack) @@ -115,10 +117,10 @@ evalAst stack (FunctionValue params ast (Just asts)) stack ) | otherwise = case evalAst stack (head asts) of - (Left err, _) -> (Left err, stack) - (Right Nothing, _) -> (Left "No evaluation in one or more parameters of expression", stack) - (Right (Just ast'), _) -> - evalAst stack (FunctionValue (tail params) (Call "$" [Define (head params) ast', ast]) (Just (tail asts))) + (Left err, _) -> (Left err, stack) + (Right Nothing, _) -> (Left "No evaluation in one or more parameters of expression", stack) + (Right (Just ast'), _) -> + evalAst stack (FunctionValue (tail params) (Call "$" [Define (head params) ast', ast]) (Just (tail asts))) evalAst stack (Cond (AST.Boolean b) a1 (Just a2)) | b = evalAst stack a1 | otherwise = evalAst stack a2 diff --git a/LobsterLang/test/AstEvalSpec.hs b/LobsterLang/test/AstEvalSpec.hs index a53bcaa..4b61cae 100644 --- a/LobsterLang/test/AstEvalSpec.hs +++ b/LobsterLang/test/AstEvalSpec.hs @@ -140,6 +140,8 @@ spec = do evalAst [Variable "foo" (AST.Value 1) 0, ScopeBegin 0] (AST.Symbol "foo" Nothing) `shouldBe` (Right (Just (AST.Value 1)), [Variable "foo" (AST.Value 1) 0, ScopeBegin 0]) it "Check variable usage 2" $ do evalAst [Variable "bar" (Call "+" [AST.Value 1, AST.Value 5]) 0, ScopeBegin 0] (AST.Symbol "bar" Nothing) `shouldBe` (Right (Just (AST.Value 6)), [Variable "bar" (Call "+" [AST.Value 1, AST.Value 5]) 0, ScopeBegin 0]) + it "Check invalid variable usage" $ do + evalAst [Variable "bar" (Call "+" [AST.Value 1, AST.Value 5]) 0, ScopeBegin 0] (AST.Symbol "bar" (Just [Value 1])) `shouldBe` (Left "Symbol 'bar' isn't a function", [Variable "bar" (Call "+" [AST.Value 1, AST.Value 5]) 0, ScopeBegin 0]) it "Check invalid function" $ do evalAst [Variable "foo" (FunctionValue ["x"] (Call "+" [AST.Symbol "x" Nothing, AST.Boolean True]) Nothing) 0, ScopeBegin 0] (Symbol "foo" (Just [AST.Value 5])) `shouldBe` (Left "One or more parameters of binary operator '+' is invalid", [Variable "foo" (FunctionValue ["x"] (Call "+" [AST.Symbol "x" Nothing, AST.Boolean True]) Nothing) 0, ScopeBegin 0]) it "Check basic function definition" $ do From 6f46bc99e1e30e3520712aff3dc756b22aad20cf Mon Sep 17 00:00:00 2001 From: Axel Humeau Date: Thu, 11 Jan 2024 18:20:54 +0100 Subject: [PATCH 15/15] style: remove some long lines in AstEval --- LobsterLang/src/AstEval.hs | 186 ++++++++++++++++----------- LobsterLang/test/AstEvalSpec.hs | 12 +- LobsterLang/test/AstOptimizerSpec.hs | 2 +- 3 files changed, 119 insertions(+), 81 deletions(-) diff --git a/LobsterLang/src/AstEval.hs b/LobsterLang/src/AstEval.hs index de6b362..6ca026f 100644 --- a/LobsterLang/src/AstEval.hs +++ b/LobsterLang/src/AstEval.hs @@ -32,6 +32,19 @@ sexprToAst (SExpr.Symbol "true") = Just (Boolean True) sexprToAst (SExpr.Symbol "false") = Just (Boolean False) sexprToAst (SExpr.Symbol s) = Just (AST.Symbol s Nothing) +noEvaluationError :: String -> String +noEvaluationError s = "No evaluation in one or more parameters of " ++ s + +invalidParamsBiOp :: String -> String +invalidParamsBiOp op = + "One or more parameters of binary operator '" ++ op ++ "' is invalid" + +tooMuchParams :: String -> String +tooMuchParams s = "Too much parameters for " ++ s + +notEnoughParams :: String -> String +notEnoughParams s = "Not enough parameters for " ++ s + -- | Evaluate a 'Ast'. -- Takes a stack representing variables and the Ast to evaluate. -- Returns a tuple containing either the resulting Ast @@ -85,8 +98,8 @@ evalAst stack (Call "!" _) = (Left "Invalid number of parameter for unary operat evalAst stack (Call "@" [ast]) = case astToString stack ast of Left err -> (Left err, stack) Right ast' -> (Right (Just ast'), stack) -evalAst stack (Call "@" (_ : _)) = (Left "Too much parameters for string conversion", stack) -evalAst stack (Call "@" []) = (Left "Not enough parameters for string conversion", stack) +evalAst stack (Call "@" (_ : _)) = (Left (tooMuchParams "string conversion"), stack) +evalAst stack (Call "@" []) = (Left (notEnoughParams "string conversion"), stack) evalAst stack (Call "++" astList) = evalBiListOp (\l el -> l ++ [el]) stack (Call "++" astList) evalAst stack (Call "--" astList) = evalBiListOp (\l el -> filter (/= el) l) stack (Call "++" astList) evalAst stack (Call "!!" astList) = case getElemInAstList stack (Call "!!" astList) of @@ -98,12 +111,13 @@ evalAst stack (Call "$" [ast1, ast2]) = case evalAst stack ast1 of (Right _, stack') -> case evalAst stack' ast2 of (Left err', _) -> (Left err', stack) (Right ast, stack'') -> (Right ast, stack'') -evalAst stack (Call "$" (_ : _)) = (Left "Too much parameters for operator $ (needs 2)", stack) -evalAst stack (Call "$" []) = (Left "Not enough parameters for operator $ (needs 2)", stack) +evalAst stack (Call "$" (_ : _)) = (Left (tooMuchParams "operator $ (needs 2)"), stack) +evalAst stack (Call "$" []) = (Left (notEnoughParams "operator $ (needs 2)"), stack) evalAst stack (Call unknown _) = (Left ("Unknown operator: " ++ unknown), stack) evalAst stack (FunctionValue params ast Nothing) = (Right (Just (FunctionValue params ast Nothing)), stack) -evalAst stack (FunctionValue [] ast (Just [])) = Data.Bifunctor.second clearScope (evalAst (beginScope stack) ast) +evalAst stack (FunctionValue [] ast (Just [])) = + Data.Bifunctor.second clearScope (evalAst (beginScope stack) ast) evalAst stack (FunctionValue params ast (Just [])) = (Right (Just (FunctionValue params ast Nothing)), stack) evalAst stack (FunctionValue params ast (Just asts)) @@ -118,7 +132,7 @@ evalAst stack (FunctionValue params ast (Just asts)) ) | otherwise = case evalAst stack (head asts) of (Left err, _) -> (Left err, stack) - (Right Nothing, _) -> (Left "No evaluation in one or more parameters of expression", stack) + (Right Nothing, _) -> (Left (noEvaluationError "expression"), stack) (Right (Just ast'), _) -> evalAst stack (FunctionValue (tail params) (Call "$" [Define (head params) ast', ast]) (Just (tail asts))) evalAst stack (Cond (AST.Boolean b) a1 (Just a2)) @@ -143,25 +157,25 @@ evalAst stack (Cond ast a1 a2) = case fst (evalAst stack ast) of -- application of the function onto the values inside the given 'Ast' -- or a 'String' containing the error message in case of error evalBiValOp :: (Int -> Int -> Int) -> [ScopeMb] -> Ast -> (Either String (Maybe Ast), [ScopeMb]) -evalBiValOp _ stack (Call op [AST.Boolean _, _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiValOp _ stack (Call op [_, AST.Boolean _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiValOp _ stack (Call op [AST.String _, _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiValOp _ stack (Call op [_, AST.String _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiValOp _ stack (Call op [AST.List _, _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiValOp _ stack (Call op [_, AST.List _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiValOp _ stack (Call op [AST.FunctionValue _ _ Nothing, _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiValOp _ stack (Call op [_, AST.FunctionValue _ _ Nothing]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) +evalBiValOp _ stack (Call op [AST.Boolean _, _]) = (Left (invalidParamsBiOp op), stack) +evalBiValOp _ stack (Call op [_, AST.Boolean _]) = (Left (invalidParamsBiOp op), stack) +evalBiValOp _ stack (Call op [AST.String _, _]) = (Left (invalidParamsBiOp op), stack) +evalBiValOp _ stack (Call op [_, AST.String _]) = (Left (invalidParamsBiOp op), stack) +evalBiValOp _ stack (Call op [AST.List _, _]) = (Left (invalidParamsBiOp op), stack) +evalBiValOp _ stack (Call op [_, AST.List _]) = (Left (invalidParamsBiOp op), stack) +evalBiValOp _ stack (Call op [AST.FunctionValue _ _ Nothing, _]) = (Left (invalidParamsBiOp op), stack) +evalBiValOp _ stack (Call op [_, AST.FunctionValue _ _ Nothing]) = (Left (invalidParamsBiOp op), stack) evalBiValOp f stack (Call _ [AST.Value a, AST.Value b]) = (Right (Just (AST.Value (f a b))), stack) evalBiValOp _ stack (Call op [ast1, ast2]) = case evalSubParams stack [ast1, ast2] of Left err -> (Left err, stack) Right asts -> maybe - (Left ("No evaluation in one or more parameters of binary operator '" ++ op ++ "'"), stack) + (Left (noEvaluationError "binary operator '" ++ op ++ "'"), stack) (evalAst stack . Call op) asts -evalBiValOp _ stack (Call op (_ : _ : _)) = (Left ("Too much parameter for binary operator '" ++ op ++ "'"), stack) -evalBiValOp _ stack (Call op _) = (Left ("Not enough parameter for binary operator '" ++ op ++ "'"), stack) +evalBiValOp _ stack (Call op (_ : _ : _)) = (Left (tooMuchParams "binary operator '" ++ op ++ "'"), stack) +evalBiValOp _ stack (Call op _) = (Left (notEnoughParams "binary operator '" ++ op ++ "'"), stack) evalBiValOp _ stack _ = (Left "Ast isn't a Call", stack) -- | Evaluate the 'Ast' for a given binary boolean operator @@ -172,25 +186,25 @@ evalBiValOp _ stack _ = (Left "Ast isn't a Call", stack) -- application of the function onto the booleans inside the given 'Ast' -- or a 'String' containing the error message in case of error evalBiBoolOp :: (Bool -> Bool -> Bool) -> [ScopeMb] -> Ast -> (Either String (Maybe Ast), [ScopeMb]) -evalBiBoolOp _ stack (Call op [AST.Value _, _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiBoolOp _ stack (Call op [_, AST.Value _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiBoolOp _ stack (Call op [AST.String _, _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiBoolOp _ stack (Call op [_, AST.String _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiBoolOp _ stack (Call op [AST.List _, _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiBoolOp _ stack (Call op [_, AST.List _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiBoolOp _ stack (Call op [AST.FunctionValue _ _ Nothing, _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiBoolOp _ stack (Call op [_, AST.FunctionValue _ _ Nothing]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) +evalBiBoolOp _ stack (Call op [AST.Value _, _]) = (Left (invalidParamsBiOp op), stack) +evalBiBoolOp _ stack (Call op [_, AST.Value _]) = (Left (invalidParamsBiOp op), stack) +evalBiBoolOp _ stack (Call op [AST.String _, _]) = (Left (invalidParamsBiOp op), stack) +evalBiBoolOp _ stack (Call op [_, AST.String _]) = (Left (invalidParamsBiOp op), stack) +evalBiBoolOp _ stack (Call op [AST.List _, _]) = (Left (invalidParamsBiOp op), stack) +evalBiBoolOp _ stack (Call op [_, AST.List _]) = (Left (invalidParamsBiOp op), stack) +evalBiBoolOp _ stack (Call op [AST.FunctionValue _ _ Nothing, _]) = (Left (invalidParamsBiOp op), stack) +evalBiBoolOp _ stack (Call op [_, AST.FunctionValue _ _ Nothing]) = (Left (invalidParamsBiOp op), stack) evalBiBoolOp f stack (Call _ [AST.Boolean a, AST.Boolean b]) = (Right (Just (AST.Boolean (f a b))), stack) evalBiBoolOp _ stack (Call op [ast1, ast2]) = case evalSubParams stack [ast1, ast2] of Left err -> (Left err, stack) Right asts -> maybe - (Left ("No evaluation in one or more parameters of binary operator '" ++ op ++ "'"), stack) + (Left (noEvaluationError "binary operator '" ++ op ++ "'"), stack) (evalAst stack . Call op) asts -evalBiBoolOp _ stack (Call op (_ : _ : _)) = (Left ("Too much parameter for binary operator '" ++ op ++ "'"), stack) -evalBiBoolOp _ stack (Call op _) = (Left ("Not enough parameter for binary operator '" ++ op ++ "'"), stack) +evalBiBoolOp _ stack (Call op (_ : _ : _)) = (Left (tooMuchParams "binary operator '" ++ op ++ "'"), stack) +evalBiBoolOp _ stack (Call op _) = (Left (notEnoughParams "binary operator '" ++ op ++ "'"), stack) evalBiBoolOp _ stack _ = (Left "Ast isn't a Call", stack) -- | Evaluate the 'Ast' for a given binary comparison operator @@ -201,25 +215,25 @@ evalBiBoolOp _ stack _ = (Left "Ast isn't a Call", stack) -- application of the function onto the values inside the given 'Ast' -- or a 'String' containing the error message in case of error evalBiCompValOp :: (Int -> Int -> Bool) -> [ScopeMb] -> Ast -> (Either String (Maybe Ast), [ScopeMb]) -evalBiCompValOp _ stack (Call op [AST.Boolean _, _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiCompValOp _ stack (Call op [_, AST.Boolean _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiCompValOp _ stack (Call op [AST.String _, _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiCompValOp _ stack (Call op [_, AST.String _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiCompValOp _ stack (Call op [AST.List _, _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiCompValOp _ stack (Call op [_, AST.List _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiCompValOp _ stack (Call op [AST.FunctionValue _ _ Nothing, _]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiCompValOp _ stack (Call op [_, AST.FunctionValue _ _ Nothing]) = (Left ("One or more parameters of binary operator '" ++ op ++ "' is invalid"), stack) +evalBiCompValOp _ stack (Call op [AST.Boolean _, _]) = (Left (invalidParamsBiOp op), stack) +evalBiCompValOp _ stack (Call op [_, AST.Boolean _]) = (Left (invalidParamsBiOp op), stack) +evalBiCompValOp _ stack (Call op [AST.String _, _]) = (Left (invalidParamsBiOp op), stack) +evalBiCompValOp _ stack (Call op [_, AST.String _]) = (Left (invalidParamsBiOp op), stack) +evalBiCompValOp _ stack (Call op [AST.List _, _]) = (Left (invalidParamsBiOp op), stack) +evalBiCompValOp _ stack (Call op [_, AST.List _]) = (Left (invalidParamsBiOp op), stack) +evalBiCompValOp _ stack (Call op [AST.FunctionValue _ _ Nothing, _]) = (Left (invalidParamsBiOp op), stack) +evalBiCompValOp _ stack (Call op [_, AST.FunctionValue _ _ Nothing]) = (Left (invalidParamsBiOp op), stack) evalBiCompValOp f stack (Call _ [AST.Value a, AST.Value b]) = (Right (Just (AST.Boolean (f a b))), stack) evalBiCompValOp _ stack (Call op [ast1, ast2]) = case evalSubParams stack [ast1, ast2] of Left err -> (Left err, stack) Right asts -> maybe - (Left ("No evaluation in one or more parameters of binary operator '" ++ op ++ "'"), stack) + (Left (noEvaluationError "binary operator '" ++ op ++ "'"), stack) (evalAst stack . Call op) asts -evalBiCompValOp _ stack (Call op (_ : _ : _)) = (Left ("Too much parameter for binary operator '" ++ op ++ "'"), stack) -evalBiCompValOp _ stack (Call op _) = (Left ("Not enough parameter for binary operator '" ++ op ++ "'"), stack) +evalBiCompValOp _ stack (Call op (_ : _ : _)) = (Left (tooMuchParams "binary operator '" ++ op ++ "'"), stack) +evalBiCompValOp _ stack (Call op _) = (Left (notEnoughParams "binary operator '" ++ op ++ "'"), stack) evalBiCompValOp _ stack _ = (Left "Ast isn't a Call", stack) -- | Evaluate the 'Ast' for a given binary list operator @@ -230,21 +244,36 @@ evalBiCompValOp _ stack _ = (Left "Ast isn't a Call", stack) -- application of the function onto the values inside the given 'Ast' -- or a 'String' containing the error message in case of error evalBiListOp :: ([Ast] -> Ast -> [Ast]) -> [ScopeMb] -> Ast -> (Either String (Maybe Ast), [ScopeMb]) -evalBiListOp _ stack (Call op [AST.Boolean _, _]) = (Left ("First parameter of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiListOp _ stack (Call op [AST.Value _, _]) = (Left ("First parameter of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiListOp _ stack (Call op [AST.String _, _]) = (Left ("First parameter of binary operator '" ++ op ++ "' is invalid"), stack) -evalBiListOp _ stack (Call op [AST.FunctionValue _ _ Nothing, _]) = (Left ("First parameter of binary operator '" ++ op ++ "' is invalid"), stack) +evalBiListOp _ stack (Call op [AST.Boolean _, _]) = + ( Left ("First parameter of binary operator '" ++ op ++ "' is invalid"), + stack + ) +evalBiListOp _ stack (Call op [AST.Value _, _]) = + ( Left ("First parameter of binary operator '" ++ op ++ "' is invalid"), + stack + ) +evalBiListOp _ stack (Call op [AST.String _, _]) = + ( Left ("First parameter of binary operator '" ++ op ++ "' is invalid"), + stack + ) +evalBiListOp _ stack (Call op [AST.FunctionValue _ _ Nothing, _]) = + ( Left ("First parameter of binary operator '" ++ op ++ "' is invalid"), + stack + ) evalBiListOp f stack (Call _ [AST.List a, ast]) = (Right (Just (AST.List (f a ast))), stack) -evalBiListOp _ stack (Call op [ast1, ast2]) = case evalSubParams stack [ast1, ast2] of - Left err -> (Left err, stack) - Right asts -> - maybe - (Left ("No evaluation in one or more parameters of binary operator '" ++ op ++ "'"), stack) - (evalAst stack . Call op) - asts -evalBiListOp _ stack (Call op (_ : _ : _)) = (Left ("Too much parameter for binary operator '" ++ op ++ "'"), stack) -evalBiListOp _ stack (Call op _) = (Left ("Not enough parameter for binary operator '" ++ op ++ "'"), stack) +evalBiListOp _ stack (Call op [ast1, ast2]) = + case evalSubParams stack [ast1, ast2] of + Left err -> (Left err, stack) + Right asts -> + maybe + (Left (noEvaluationError "binary operator '" ++ op ++ "'"), stack) + (evalAst stack . Call op) + asts +evalBiListOp _ stack (Call op (_ : _ : _)) = + (Left (tooMuchParams "binary operator '" ++ op ++ "'"), stack) +evalBiListOp _ stack (Call op _) = + (Left (notEnoughParams "binary operator '" ++ op ++ "'"), stack) evalBiListOp _ stack _ = (Left "Ast isn't a Call", stack) -- | Evaluate the 'Ast' for '!!'. @@ -253,21 +282,21 @@ evalBiListOp _ stack _ = (Left "Ast isn't a Call", stack) -- or a 'String' containing the error message in case of error getElemInAstList :: [ScopeMb] -> Ast -> Either String Ast getElemInAstList _ (Call "!!" [AST.Boolean _, _]) = - Left "One or more parameters of binary operator '!!' is invalid" + Left (invalidParamsBiOp "!!") getElemInAstList _ (Call "!!" [_, AST.Boolean _]) = - Left "One or more parameters of binary operator '!!' is invalid" + Left (invalidParamsBiOp "!!") getElemInAstList _ (Call "!!" [AST.String _, _]) = - Left "One or more parameters of binary operator '!!' is invalid" + Left (invalidParamsBiOp "!!") getElemInAstList _ (Call "!!" [_, AST.String _]) = - Left "One or more parameters of binary operator '!!' is invalid" + Left (invalidParamsBiOp "!!") getElemInAstList _ (Call "!!" [_, AST.List _]) = - Left "One or more parameters of binary operator '!!' is invalid" + Left (invalidParamsBiOp "!!") getElemInAstList _ (Call "!!" [AST.Value _, _]) = - Left "One or more parameters of binary operator '!!' is invalid" + Left (invalidParamsBiOp "!!") getElemInAstList _ (Call "!!" [AST.FunctionValue _ _ Nothing, _]) = - Left "One or more parameters of binary operator '!!' is invalid" + Left (invalidParamsBiOp "!!") getElemInAstList _ (Call "!!" [_, AST.FunctionValue _ _ Nothing]) = - Left "One or more parameters of binary operator '!!' is invalid" + Left (invalidParamsBiOp "!!") getElemInAstList _ (Call "!!" [AST.List a, AST.Value b]) | b < 0 = Left "Index out of range" | length a > b = Right (a !! b) @@ -276,7 +305,7 @@ getElemInAstList stack (Call "!!" [ast1, ast2]) = case evalSubParams stack [ast1, ast2] of Left err -> Left err Right asts -> case maybe - ( Left "No evaluation in one or more parameters of binary operator '!!'", + ( Left (noEvaluationError "binary operator '!!'"), stack ) (evalAst stack . Call "!!") @@ -285,14 +314,14 @@ getElemInAstList stack (Call "!!" [ast1, ast2]) = (Right ast, _) -> maybe ( Left - "No evaluation in one or more parameters of binary operator '!!'" + (noEvaluationError "binary operator '!!'") ) Right ast getElemInAstList _ (Call "!!" (_ : _ : _)) = - Left "Too much parameter for binary operator '!!'" + Left (tooMuchParams "binary operator '!!'") getElemInAstList _ (Call "!!" _) = - Left "Not enough parameter for binary operator '!!'" + Left (notEnoughParams "binary operator '!!'") getElemInAstList _ _ = Left "Ast isn't a '!!' Call" -- | Evaluate the 'Ast' for a given unary list operator @@ -302,22 +331,29 @@ getElemInAstList _ _ = Left "Ast isn't a '!!' Call" -- Return a tuple containing the new stack post evaluation, and the -- application of the function onto the values inside the given 'Ast' -- or a 'String' containing the error message in case of error -evalUnListOp :: ([Ast] -> Ast) -> [ScopeMb] -> Ast -> (Either String (Maybe Ast), [ScopeMb]) -evalUnListOp _ stack (Call op [AST.Boolean _]) = (Left ("The parameter of unary operator '" ++ op ++ "' is invalid"), stack) -evalUnListOp _ stack (Call op [AST.String _]) = (Left ("The parameter of unary operator '" ++ op ++ "' is invalid"), stack) -evalUnListOp _ stack (Call op [AST.Value _]) = (Left ("The parameter of unary operator '" ++ op ++ "' is invalid"), stack) -evalUnListOp _ stack (Call op [AST.FunctionValue _ _ Nothing]) = (Left ("The parameter of unary operator '" ++ op ++ "' is invalid"), stack) +evalUnListOp :: ([Ast] -> Ast) -> [ScopeMb] -> + Ast -> (Either String (Maybe Ast), [ScopeMb]) +evalUnListOp _ stack (Call op [AST.Boolean _]) = + (Left ("The parameter of unary operator '" ++ op ++ "' is invalid"), stack) +evalUnListOp _ stack (Call op [AST.String _]) = + (Left ("The parameter of unary operator '" ++ op ++ "' is invalid"), stack) +evalUnListOp _ stack (Call op [AST.Value _]) = + (Left ("The parameter of unary operator '" ++ op ++ "' is invalid"), stack) +evalUnListOp _ stack (Call op [AST.FunctionValue _ _ Nothing]) = + (Left ("The parameter of unary operator '" ++ op ++ "' is invalid"), stack) evalUnListOp f stack (Call _ [AST.List a]) = (Right (Just (f a)), stack) evalUnListOp _ stack (Call op [ast]) = case evalSubParams stack [ast] of Left err -> (Left err, stack) Right asts -> maybe - (Left ("No evaluation in one or more parameters of binary operator '" ++ op ++ "'"), stack) + (Left (noEvaluationError "binary operator '" ++ op ++ "'"), stack) (evalAst stack . Call op) asts -evalUnListOp _ stack (Call op (_ : _ : _)) = (Left ("Too much parameter for unary operator '" ++ op ++ "'"), stack) -evalUnListOp _ stack (Call op _) = (Left ("Not enough parameter for unary operator '" ++ op ++ "'"), stack) +evalUnListOp _ stack (Call op (_ : _ : _)) = + (Left (tooMuchParams "unary operator '" ++ op ++ "'"), stack) +evalUnListOp _ stack (Call op _) = + (Left (notEnoughParams "unary operator '" ++ op ++ "'"), stack) evalUnListOp _ stack _ = (Left "Ast isn't a Call", stack) -- | Evaluate the list of 'Ast' @@ -336,7 +372,8 @@ astToString :: [ScopeMb] -> Ast -> Either String Ast astToString _ (AST.String str) = Right (AST.String str) astToString _ (AST.Value val) = Right (AST.String (show val)) astToString _ (AST.Boolean bool) = Right (AST.String (show bool)) -astToString _ (AST.FunctionValue _ _ Nothing) = Left "Cannot convert lambda to string" +astToString _ (AST.FunctionValue _ _ Nothing) = + Left "Cannot convert lambda to string" astToString stack ast = case evalAst stack ast of (Left err, _) -> Left err (Right ast', _) -> @@ -345,7 +382,8 @@ astToString stack ast = case evalAst stack ast of (astToString stack) ast' -defineVar :: ([ScopeMb] -> String -> Ast -> [ScopeMb]) -> [ScopeMb] -> String -> Ast -> Either String [ScopeMb] +defineVar :: ([ScopeMb] -> String -> Ast -> [ScopeMb]) + -> [ScopeMb] -> String -> Ast -> Either String [ScopeMb] defineVar f stack name ast = case evalAst stack ast of (Left err, _) -> Left err (Right (Just ast'), _) -> Right (f stack name ast') diff --git a/LobsterLang/test/AstEvalSpec.hs b/LobsterLang/test/AstEvalSpec.hs index 4b61cae..a28be89 100644 --- a/LobsterLang/test/AstEvalSpec.hs +++ b/LobsterLang/test/AstEvalSpec.hs @@ -63,9 +63,9 @@ spec = do it "Check invalid value binary operation (wrong type 2)" $ do evalBiValOp (+) [] (Call "+" [AST.Value 8, AST.Boolean False]) `shouldBe` (Left "One or more parameters of binary operator '+' is invalid", []) it "Check invalid value binary operation (not enough ast parameters)" $ do - evalBiValOp (+) [] (Call "+" [AST.Value 8]) `shouldBe` (Left "Not enough parameter for binary operator '+'", []) + evalBiValOp (+) [] (Call "+" [AST.Value 8]) `shouldBe` (Left "Not enough parameters for binary operator '+'", []) it "Check invalid value binary operation (too much ast parameters)" $ do - evalBiValOp (+) [] (Call "+" [AST.Value 8, AST.Value 9, AST.Value 3]) `shouldBe` (Left "Too much parameter for binary operator '+'", []) + evalBiValOp (+) [] (Call "+" [AST.Value 8, AST.Value 9, AST.Value 3]) `shouldBe` (Left "Too much parameters for binary operator '+'", []) describe "Value comparison evaluation tests" $ do -- Value comparison operators it "Check valid operation ==" $ do @@ -85,9 +85,9 @@ spec = do it "Check invalid value comparison binary operation (wrong type 2)" $ do evalBiCompValOp (==) [] (Call "==" [AST.Value 8, AST.Boolean False]) `shouldBe` (Left "One or more parameters of binary operator '==' is invalid", []) it "Check invalid value comparison binary operation (not enough ast parameters)" $ do - evalBiCompValOp (==) [] (Call "==" [AST.Value 8]) `shouldBe` (Left "Not enough parameter for binary operator '=='", []) + evalBiCompValOp (==) [] (Call "==" [AST.Value 8]) `shouldBe` (Left "Not enough parameters for binary operator '=='", []) it "Check invalid value comparison binary operation (too much ast parameters)" $ do - evalBiCompValOp (==) [] (Call "==" [AST.Value 8, AST.Value 9, AST.Value 3]) `shouldBe` (Left "Too much parameter for binary operator '=='", []) + evalBiCompValOp (==) [] (Call "==" [AST.Value 8, AST.Value 9, AST.Value 3]) `shouldBe` (Left "Too much parameters for binary operator '=='", []) describe "Boolean operators evaluation tests" $ do -- Boolean operators it "Check valid operation &&" $ do @@ -123,9 +123,9 @@ spec = do it "Check invalid value comparison binary operation (wrong type 2)" $ do evalBiBoolOp (&&) [] (Call "&&" [AST.Value 8, AST.Boolean False]) `shouldBe` (Left "One or more parameters of binary operator '&&' is invalid", []) it "Check invalid value comparison binary operation (not enough ast parameters)" $ do - evalBiBoolOp (&&) [] (Call "&&" [AST.Value 8]) `shouldBe` (Left "Not enough parameter for binary operator '&&'", []) + evalBiBoolOp (&&) [] (Call "&&" [AST.Value 8]) `shouldBe` (Left "Not enough parameters for binary operator '&&'", []) it "Check invalid value comparison binary operation (too much ast parameters)" $ do - evalBiBoolOp (&&) [] (Call "&&" [AST.Value 8, AST.Value 9, AST.Value 3]) `shouldBe` (Left "Too much parameter for binary operator '&&'", []) + evalBiBoolOp (&&) [] (Call "&&" [AST.Value 8, AST.Value 9, AST.Value 3]) `shouldBe` (Left "Too much parameters for binary operator '&&'", []) describe "Define and function evaluation tests" $ do -- Check Define it "Check unknown variable" $ do diff --git a/LobsterLang/test/AstOptimizerSpec.hs b/LobsterLang/test/AstOptimizerSpec.hs index 3c1ee6c..2557ee2 100644 --- a/LobsterLang/test/AstOptimizerSpec.hs +++ b/LobsterLang/test/AstOptimizerSpec.hs @@ -46,7 +46,7 @@ spec = do it "Optimizable Define" $ do optimizeAst [] [Define "a" (Call "+" [Value 5, Value 5])] False `shouldBe` [Right (Result (Define "a" (Value 10)))] it "Error Define" $ do - optimizeAst [] [Define "a" (Call "+" [Value 5])] False `shouldBe` [Left (Error "Not enough parameter for binary operator '+'" (Call "+" [Value 5]))] + optimizeAst [] [Define "a" (Call "+" [Value 5])] False `shouldBe` [Left (Error "Not enough parameters for binary operator '+'" (Call "+" [Value 5]))] it "Error Define 2" $ do optimizeAst [] [Define "a" (Define "b" (Value 2))] False `shouldBe` [Left (Error "Cannot define with no value" (Define "a" (Define "b" (Value 2))))] it "Error Define 3" $ do