Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into feature/compiler_func…
Browse files Browse the repository at this point in the history
…tion_args_handling
  • Loading branch information
AldricJourdain committed Jan 13, 2024
2 parents 3f6b86c + 08c72b8 commit 728de50
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 44 deletions.
11 changes: 8 additions & 3 deletions LobsterLang/LobsterLang.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,10 @@ library
AST
AstEval
Compiler
Lib
Parse
Scope
SExpr
Stack
Utils
Vm
other-modules:
Paths_LobsterLang
Expand All @@ -44,9 +42,9 @@ library
ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints
build-depends:
base >=4.7 && <5
, binary
, bytestring
, utf8-string
, binary
default-language: Haskell2010

executable LobsterLang-exe
Expand All @@ -61,13 +59,17 @@ executable LobsterLang-exe
build-depends:
LobsterLang
, base >=4.7 && <5
, binary
, bytestring
, utf8-string
default-language: Haskell2010

test-suite LobsterLang-test
type: exitcode-stdio-1.0
main-is: Spec.hs
other-modules:
AstEvalSpec
CompilerSpec
VmSpec
Paths_LobsterLang
autogen-modules:
Expand All @@ -78,5 +80,8 @@ test-suite LobsterLang-test
build-depends:
LobsterLang
, base >=4.7 && <5
, binary
, bytestring
, hspec
, utf8-string
default-language: Haskell2010
41 changes: 31 additions & 10 deletions LobsterLang/src/AstEval.hs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module AstEval
where

import AST
import qualified Data.Bifunctor
import Data.Bifunctor
import SExpr
import Scope

Expand All @@ -39,9 +39,13 @@ sexprToAst (SExpr.Symbol s) = Just (AST.Symbol s Nothing)
-- or a 'String' containing the error message in case of error
-- and the stack after evaluation.
evalAst :: [ScopeMb] -> Ast -> (Either String (Maybe Ast), [ScopeMb])
evalAst stack (Define s v) = case getVarInScope stack s of
Nothing -> (Right Nothing, addVarToScope stack s v)
Just _ -> (Right Nothing, updateVar stack s v)
evalAst stack (Define s v) = case defineVar defineFunc stack s v of
Left err -> (Left err, stack)
Right stack' -> (Right Nothing, stack')
where
defineFunc = case getVarInScope stack s of
Nothing -> addVarToScope
Just _ -> updateVar
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)
Expand Down Expand Up @@ -81,11 +85,21 @@ evalAst stack (Call "!!" astList) = case getElemInAstList stack (Call "!!" astLi
Left err -> (Left err, stack)
Right ast' -> (Right (Just ast'), stack)
evalAst stack (Call "len" astList) = evalUnListOp (AST.Value . length) stack (Call "len" astList)
evalAst stack (Call "$" [ast1, ast2]) = case evalAst stack ast1 of
(Left err, _) -> (Left err, stack)
(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 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 params ast (Just [])) =
(Right (Just (FunctionValue params ast Nothing)), stack)
evalAst stack (FunctionValue params ast (Just asts))
| length params /= length asts =
| length params < length asts =
( Left
( "Expression takes "
++ show (length params)
Expand All @@ -94,11 +108,11 @@ evalAst stack (FunctionValue params ast (Just asts))
),
stack
)
| otherwise = case evalSubParams stack asts of
Left err -> (Left err, stack)
Right mEAsts -> case mEAsts of
Nothing -> (Left "No evaluation in one or more parameters of expression", stack)
Just eAsts -> Data.Bifunctor.second clearScope (evalAst (addVarsToScope (beginScope stack) params eAsts) ast)
| 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)))
evalAst stack (Cond (AST.Boolean b) a1 (Just a2))
| b = evalAst stack a1
| otherwise = evalAst stack a2
Expand Down Expand Up @@ -247,6 +261,7 @@ getElemInAstList _ (Call "!!" [AST.FunctionValue _ _ Nothing, _]) =
getElemInAstList _ (Call "!!" [_, AST.FunctionValue _ _ Nothing]) =
Left "One or more parameters of binary operator '!!' is invalid"
getElemInAstList _ (Call "!!" [AST.List a, AST.Value b])
| b < 0 = Left "Index out of range"
| length a > b = Right (a !! b)
| otherwise = Left "Index out of range"
getElemInAstList stack (Call "!!" [ast1, ast2]) =
Expand Down Expand Up @@ -321,3 +336,9 @@ astToString stack ast = case evalAst stack ast of
(Left "Cannot convert no evaluation to string")
(astToString stack)
ast'

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')
(Right Nothing, _) -> Left "Cannot define with no value"
12 changes: 0 additions & 12 deletions LobsterLang/src/Lib.hs

This file was deleted.

14 changes: 0 additions & 14 deletions LobsterLang/src/Utils.hs

This file was deleted.

43 changes: 38 additions & 5 deletions LobsterLang/test/AstEvalSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import Scope

spec :: Spec
spec = do
describe "Ast evaluation tests" $ do
describe "Basic Ast evaluation tests" $ do
-- Basic evaluation
it "Check Value" $ do
evalAst [] (AST.Value 5) `shouldBe` (Right (Just (AST.Value 5)), [])
Expand Down Expand Up @@ -42,6 +42,7 @@ spec = do
evalAst [] (Call "@" [Define "a" (AST.Value 5)]) `shouldBe` (Left "Cannot convert no evaluation to string", [])
it "Check invalid String conversion 4" $ do
evalAst [] (Call "@" [Call "+" [AST.Value 5, AST.Boolean True]]) `shouldBe` (Left "One or more parameters of binary operator '+' is invalid", [])
describe "Value Ast evaluation tests" $ do
-- Value operators
it "Check valid operation +" $ do
evalAst [] (Call "+" [AST.Value 5, AST.Value 8]) `shouldBe` (Right (Just (AST.Value 13)), [])
Expand All @@ -65,6 +66,7 @@ spec = do
evalBiValOp (+) [] (Call "+" [AST.Value 8]) `shouldBe` (Left "Not enough parameter 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 '+'", [])
describe "Value comparison evaluation tests" $ do
-- Value comparison operators
it "Check valid operation ==" $ do
evalAst [] (Call "==" [AST.Value 5, AST.Value 5]) `shouldBe` (Right (Just (AST.Boolean True)), [])
Expand All @@ -86,6 +88,7 @@ spec = do
evalBiCompValOp (==) [] (Call "==" [AST.Value 8]) `shouldBe` (Left "Not enough parameter 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 '=='", [])
describe "Boolean operators evaluation tests" $ do
-- Boolean operators
it "Check valid operation &&" $ do
evalAst [] (Call "&&" [AST.Boolean True, AST.Boolean False]) `shouldBe` (Right (Just (AST.Boolean False)), [])
Expand Down Expand Up @@ -117,6 +120,7 @@ spec = do
evalBiBoolOp (&&) [] (Call "&&" [AST.Value 8]) `shouldBe` (Left "Not enough parameter 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 '&&'", [])
describe "Define and function evaluation tests" $ do
-- Check Define
it "Check unknown variable" $ do
evalAst (beginScope []) (AST.Symbol "bar" Nothing) `shouldBe` (Left "Symbol 'bar' doesn't exist in the current or global scope", [ScopeBegin 0])
Expand All @@ -125,7 +129,7 @@ spec = do
it "Check variable definition" $ do
evalAst (beginScope []) (Define "foo" (AST.Value 1)) `shouldBe` (Right Nothing, [Variable "foo" (AST.Value 1) 0, ScopeBegin 0])
it "Check variable definition 2" $ do
evalAst (beginScope []) (Define "bar" (Call "+" [AST.Value 1, AST.Value 5])) `shouldBe` (Right Nothing, [Variable "bar" (Call "+" [AST.Value 1, AST.Value 5]) 0, ScopeBegin 0])
evalAst (beginScope []) (Define "bar" (Call "+" [AST.Value 1, AST.Value 5])) `shouldBe` (Right Nothing, [Variable "bar" (AST.Value 6) 0, ScopeBegin 0])
it "Check variable usage" $ 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
Expand All @@ -136,8 +140,8 @@ spec = do
evalAst (beginScope []) (Define "foo" (FunctionValue ["x"] (Call "+" [AST.Symbol "x" Nothing, AST.Value 1]) Nothing)) `shouldBe` (Right Nothing, [Variable "foo" (FunctionValue ["x"] (Call "+" [AST.Symbol "x" Nothing, AST.Value 1]) Nothing) 0, ScopeBegin 0])
it "Check basic function usage" $ do
evalAst [Variable "foo" (FunctionValue ["x"] (Call "+" [AST.Symbol "x" Nothing, AST.Value 1]) Nothing) 0, ScopeBegin 0] (Symbol "foo" (Just [AST.Value 5])) `shouldBe` (Right (Just (AST.Value 6)), [Variable "foo" (FunctionValue ["x"] (Call "+" [AST.Symbol "x" Nothing, AST.Value 1]) Nothing) 0, ScopeBegin 0])
it "Check invalid basic function usage (not enough parameters)" $ do
evalAst [Variable "foo" (FunctionValue ["x"] (Call "+" [AST.Symbol "x" Nothing, AST.Value 1]) Nothing) 0, ScopeBegin 0] (Symbol "foo" (Just [])) `shouldBe` (Left "Expression takes 1 parameters, got 0", [Variable "foo" (FunctionValue ["x"] (Call "+" [AST.Symbol "x" Nothing, AST.Value 1]) Nothing) 0, ScopeBegin 0])
it "Check invalid basic function eval" $ do
evalAst [Variable "foo" (FunctionValue ["x"] (Call "+" [AST.Symbol "x" Nothing, AST.Value 1]) Nothing) 0, ScopeBegin 0] (Symbol "foo" (Just [])) `shouldBe` (Right (Just (FunctionValue ["x"] (Call "+" [AST.Symbol "x" Nothing, AST.Value 1]) Nothing)), [Variable "foo" (FunctionValue ["x"] (Call "+" [AST.Symbol "x" Nothing, AST.Value 1]) Nothing) 0, ScopeBegin 0])
it "Check invalid basic function usage (too much parameters)" $ do
evalAst [Variable "foo" (FunctionValue ["x"] (Call "+" [AST.Symbol "x" Nothing, AST.Value 1]) Nothing) 0, ScopeBegin 0] (Symbol "foo" (Just [AST.Value 5, AST.Value 5, AST.Value 5])) `shouldBe` (Left "Expression takes 1 parameters, got 3", [Variable "foo" (FunctionValue ["x"] (Call "+" [AST.Symbol "x" Nothing, AST.Value 1]) Nothing) 0, ScopeBegin 0])
it "Check invalid basic function usage (define inside parameters)" $ do
Expand All @@ -146,17 +150,19 @@ spec = do
evalAst (beginScope []) (Define "3+" (FunctionValue ["a", "b", "c"] (Call "+" [AST.Call "+" [AST.Symbol "a" Nothing, AST.Symbol "b" Nothing], AST.Symbol "c" Nothing]) Nothing)) `shouldBe` (Right Nothing, [Variable "3+" (FunctionValue ["a", "b", "c"] (Call "+" [AST.Call "+" [AST.Symbol "a" Nothing, AST.Symbol "b" Nothing], AST.Symbol "c" Nothing]) Nothing) 0, ScopeBegin 0])
it "Check multi-parameters function usage" $ do
evalAst [Variable "3+" (FunctionValue ["a", "b", "c"] (Call "+" [AST.Call "+" [AST.Symbol "a" Nothing, AST.Symbol "b" Nothing], AST.Symbol "c" Nothing]) Nothing) 0, ScopeBegin 0] (Symbol "3+" (Just [AST.Value 5, AST.Value 6, AST.Value (-9)])) `shouldBe` (Right (Just (AST.Value 2)), [Variable "3+" (FunctionValue ["a", "b", "c"] (Call "+" [AST.Call "+" [AST.Symbol "a" Nothing, AST.Symbol "b" Nothing], AST.Symbol "c" Nothing]) Nothing) 0, ScopeBegin 0])
describe "Lambda evaluation tests" $ do
-- Check Lambda usage
it "Check +1 lambda" $ do
evalAst [] (FunctionValue ["x"] (Call "+" [AST.Symbol "x" Nothing, AST.Value 1]) (Just [AST.Value 5])) `shouldBe` (Right (Just (AST.Value 6)), [])
it "Check square lambda" $ do
evalAst [] (FunctionValue ["x"] (Call "*" [AST.Symbol "x" Nothing, AST.Symbol "x" Nothing]) (Just [AST.Value 5])) `shouldBe` (Right (Just (AST.Value 25)), [])
it "Check invalid lambda usage (not enough parameters)" $ do
evalAst [] (FunctionValue ["x"] (Call "*" [AST.Symbol "x" Nothing, AST.Symbol "x" Nothing]) (Just [])) `shouldBe` (Left "Expression takes 1 parameters, got 0", [])
evalAst [] (FunctionValue ["x"] (Call "*" [AST.Symbol "x" Nothing, AST.Symbol "x" Nothing]) (Just [])) `shouldBe` (Right (Just (FunctionValue ["x"] (Call "*" [AST.Symbol "x" Nothing, AST.Symbol "x" Nothing]) Nothing)), [])
it "Check invalid lambda usage (too much parameters)" $ do
evalAst [] (FunctionValue ["x"] (Call "*" [AST.Symbol "x" Nothing, AST.Symbol "x" Nothing]) (Just [AST.Value 5, AST.Value 5])) `shouldBe` (Left "Expression takes 1 parameters, got 2", [])
it "Check invalid lambda usage (define inside parameters)" $ do
evalAst [] (FunctionValue ["x"] (Call "*" [AST.Symbol "x" Nothing, AST.Symbol "x" Nothing]) (Just [Define "a" (AST.Value 5)])) `shouldBe` (Left "No evaluation in one or more parameters of expression", [])
describe "Cond Ast evaluation tests" $ do
-- Check Cond
it "Check true Cond" $ do
evalAst [] (Cond (AST.Boolean True) (AST.Value 5) Nothing) `shouldBe` (Right (Just (AST.Value 5)), [])
Expand All @@ -170,6 +176,33 @@ spec = do
evalAst [] (Cond (Define "a" (AST.Value 5)) (AST.Value 6) Nothing) `shouldBe` (Left "No evaluation in condition", [])
it "Check invalid condition 2" $ do
evalAst [] (Cond (AST.Value 5) (AST.Value 6) Nothing) `shouldBe` (Left "Condition isn't a boolean", [])
describe "List Ast evaluation tests" $ do
-- Check List
it "Check empty list" $ do
evalAst [] (AST.List []) `shouldBe` (Right (Just (AST.List [])), [])
it "Check non empty list" $ do
evalAst [] (AST.List [AST.Value 5, AST.String "blegh"]) `shouldBe` (Right (Just (AST.List [AST.Value 5, AST.String "blegh"])), [])
it "Check good index list" $ do
evalAst [] (AST.Call "!!" [AST.List [AST.Value 5, AST.String "blegh"], AST.Value 1]) `shouldBe` (Right (Just (AST.String "blegh")), [])
it "Check bad index list" $ do
evalAst [] (AST.Call "!!" [AST.List [AST.Value 5, AST.String "blegh"], AST.Value 3]) `shouldBe` (Left "Index out of range", [])
it "Check bad index list 2" $ do
evalAst [] (AST.Call "!!" [AST.List [AST.Value 5, AST.String "blegh"], AST.Value (-1)]) `shouldBe` (Left "Index out of range", [])
it "Check length empty list" $ do
evalAst [] (Call "len" [AST.List []]) `shouldBe` (Right (Just (AST.Value 0)), [])
it "Check non empty list" $ do
evalAst [] (Call "len" [AST.List [AST.Value 5, AST.String "blegh"]]) `shouldBe` (Right (Just (AST.Value 2)), [])
it "Check append" $ do
evalAst [] (Call "++" [AST.List [AST.Value 5, AST.String "blegh"], AST.Value 8]) `shouldBe` (Right (Just (AST.List [AST.Value 5, AST.String "blegh", AST.Value 8])), [])
it "Check remove occurence" $ do
evalAst [] (Call "--" [AST.List [AST.Value 5, AST.String "blegh"], AST.Value 5]) `shouldBe` (Right (Just (AST.List [AST.String "blegh"])), [])
it "Check remove occurence 2" $ do
evalAst [] (Call "--" [AST.List [AST.Value 5, AST.String "blegh", AST.Value 5], AST.Value 5]) `shouldBe` (Right (Just (AST.List [AST.String "blegh"])), [])
it "Check remove occurence 3" $ do
evalAst [] (Call "--" [AST.List [], AST.Value 5]) `shouldBe` (Right (Just (AST.List [])), [])
it "Check remove occurence 3" $ do
evalAst [] (Call "--" [AST.List [AST.Value 5, AST.Value 5, AST.Value 5, AST.Value 5], AST.Value 5]) `shouldBe` (Right (Just (AST.List [])), [])
describe "Advanced Ast evaluation tests" $ do
-- Advanced tests
it "Check factorial definition" $ do
evalAst [] (Define "fact" (FunctionValue ["x"] (Cond (Call "==" [AST.Value 0, AST.Symbol "x" Nothing]) (AST.Value 1) (Just (Call "*" [AST.Symbol "x" Nothing, Symbol "fact" (Just [Call "-" [AST.Symbol "x" Nothing, AST.Value 1]])]))) Nothing)) `shouldBe` (Right Nothing, [Variable "fact" (FunctionValue ["x"] (Cond (Call "==" [AST.Value 0, AST.Symbol "x" Nothing]) (AST.Value 1) (Just (Call "*" [AST.Symbol "x" Nothing, AST.Symbol "fact" (Just [Call "-" [AST.Symbol "x" Nothing, AST.Value 1]])]))) Nothing) 0])
Expand Down

0 comments on commit 728de50

Please sign in to comment.