From f82c21d107fffaeb98590100fe36f2aa91475dcc Mon Sep 17 00:00:00 2001 From: Anton Kesy Date: Mon, 11 Mar 2024 11:27:42 +0100 Subject: [PATCH] add if --- examples/control.mmm | 5 +++++ src/AST.hs | 4 ++++ src/Interpreter/Interpreter.hs | 28 ++++++++++++++++++++++++++++ src/Interpreter/Validator.hs | 1 + src/Parser/Statement.hs | 20 +++++++++++++++++++- test/Unit/Parser/Statement.hs | 30 ++++++++++++++++++++++++++++-- 6 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 examples/control.mmm diff --git a/examples/control.mmm b/examples/control.mmm new file mode 100644 index 0000000..f27a57d --- /dev/null +++ b/examples/control.mmm @@ -0,0 +1,5 @@ +if 1 < 2 { + println("true"); +} else { + println("false"); +} diff --git a/src/AST.hs b/src/AST.hs index 9681d4b..2531f03 100644 --- a/src/AST.hs +++ b/src/AST.hs @@ -28,12 +28,16 @@ type Comment = String data Type = IntType | FloatType | BoolType | UnitType | CustomType Name | StringType deriving (Show, Eq) +data Control = IfControl Expression [Statement] (Maybe [Statement]) + deriving (Show, Eq) + data Statement = VariableStatement Variable | AssignmentStatement Assignment | FunctionDefinitionStatement Function | ExpressionStatement Expression | ReturnStatement Expression + | ControlStatement Control deriving (Show, Eq) data Function = Function Name [VariableDeclaration] Type [Statement] diff --git a/src/Interpreter/Interpreter.hs b/src/Interpreter/Interpreter.hs index e5aee2a..8f29d8a 100644 --- a/src/Interpreter/Interpreter.hs +++ b/src/Interpreter/Interpreter.hs @@ -37,6 +37,8 @@ interpretStatement (InterpretState state _) (FunctionDefinitionStatement _) = do interpretStatement (InterpretState state _) (ReturnStatement expression) = do ret <- interpretExpression state expression return (InterpretState state (Just ret)) +interpretStatement (InterpretState state _) (ControlStatement control) = do + interpretControl state control updateState :: ProgramState -> Name -> Value -> ProgramState updateState (ProgramState vars funs) name value = ProgramState (Map.insert name value vars) funs @@ -106,4 +108,30 @@ interpretOperation Minus (FloatValue left) (FloatValue right) = FloatValue $ lef interpretOperation Multiply (IntValue left) (IntValue right) = IntValue $ left * right interpretOperation Multiply (FloatValue left) (FloatValue right) = FloatValue $ left * right interpretOperation Divide (IntValue left) (IntValue right) = IntValue $ left `div` right +interpretOperation Lt (IntValue left) (IntValue right) = BoolValue $ left < right +interpretOperation Gt (IntValue left) (IntValue right) = BoolValue $ left > right +interpretOperation Le (IntValue left) (IntValue right) = BoolValue $ left <= right +interpretOperation Ge (IntValue left) (IntValue right) = BoolValue $ left >= right +interpretOperation Eq (IntValue left) (IntValue right) = BoolValue $ left == right interpretOperation operator left right = error $ "Unsupported operation: " ++ show operator ++ " " ++ show left ++ " " ++ show right + +interpretControl :: ProgramState -> Control -> IO InterpretState +interpretControl (ProgramState vars funs) (IfControl test body elseBody) = do + testValue <- interpretExpression (ProgramState vars funs) test + if not (isBoolValue testValue) + then do error "Control statement test must be a boolean value." + else do + if testValue == BoolValue True + then do + (InterpretState _ ret) <- foldM interpretStatement (InterpretState (ProgramState vars funs) Nothing) body + return $ InterpretState (ProgramState vars funs) ret + else do + case elseBody of + Just elseStatements -> do + (InterpretState _ ret) <- foldM interpretStatement (InterpretState (ProgramState vars funs) Nothing) elseStatements + return $ InterpretState (ProgramState vars funs) ret + Nothing -> return $ InterpretState (ProgramState vars funs) Nothing + where + isBoolValue :: Value -> Bool + isBoolValue (BoolValue _) = True + isBoolValue _ = False diff --git a/src/Interpreter/Validator.hs b/src/Interpreter/Validator.hs index 0d35cad..262a1a5 100644 --- a/src/Interpreter/Validator.hs +++ b/src/Interpreter/Validator.hs @@ -31,6 +31,7 @@ hasEntryPoint (Program statements) = isMainFunction _ = False isGlobalStatement (AssignmentStatement _) = True isGlobalStatement (ExpressionStatement _) = True + isGlobalStatement (ControlStatement _) = True isGlobalStatement _ = False -- TODO: check no name clash with built-in functions diff --git a/src/Parser/Statement.hs b/src/Parser/Statement.hs index 692f9f3..c215cd8 100644 --- a/src/Parser/Statement.hs +++ b/src/Parser/Statement.hs @@ -14,7 +14,8 @@ import Text.Parsec.String parseStatement :: Parser Statement parseStatement = - try parseReturnStatement + (ControlStatement <$> try (spaces' *> try parseControl)) + <|> try parseReturnStatement <|> (FunctionDefinitionStatement <$> try (spaces' *> try parseFunction)) <|> (VariableStatement <$> try (spaces' *> try parseVariable) <* endOfStatement) <|> (AssignmentStatement <$> try (spaces' *> try parseAssignment) <* endOfStatement) @@ -46,3 +47,20 @@ parseFunction = do _ <- spaces' statements <- manyTill (try parseStatement <* spaces') (spaces' *> char '}' <* spaces') return $ Function name vars fnType statements + +parseControl :: Parser Control +parseControl = + try parseIfControl + +parseIfControl :: Parser Control +parseIfControl = do + _ <- spaces' + _ <- try (string "if") + test <- try (spaces1' *> parseExpression) + _ <- spaces1' *> char '{' + trueBlock <- spaces' *> many (try parseStatement) + _ <- spaces' *> char '}' + elseBlock <- optionMaybe (try (spaces' *> string "else" *> spaces' *> char '{' *> spaces' *> many parseStatement <* spaces' <* char '}')) + case elseBlock of + Nothing -> return $ IfControl test trueBlock Nothing + Just els -> return $ IfControl test trueBlock (Just els) diff --git a/test/Unit/Parser/Statement.hs b/test/Unit/Parser/Statement.hs index 3a350ad..8039017 100644 --- a/test/Unit/Parser/Statement.hs +++ b/test/Unit/Parser/Statement.hs @@ -10,7 +10,8 @@ allTests :: [Test] allTests = [ TestLabel "simple" testSimple, TestLabel "functions" testFunctions, - TestLabel "return" testReturn + TestLabel "return" testReturn, + TestLabel "if" testIf ] emptyTestStatement :: Statement @@ -79,7 +80,7 @@ testFunctions = TestCase $ do (fromRight emptyTestFunction (parse parseFunction "" "void main() { int i = 1; i = 2; }")) assertEqual "float test(int i, int k) { }" - ( (Function "test" [VariableDeclaration "i" IntType, VariableDeclaration "k" IntType] FloatType []) + ( Function "test" [VariableDeclaration "i" IntType, VariableDeclaration "k" IntType] FloatType [] ) (fromRight emptyTestFunction (parse parseFunction "" "float test(int i, int k) { }")) @@ -105,3 +106,28 @@ testReturn = TestCase $ do "return 1; -> statement" (fromRight emptyTestStatement (parse parseReturnStatement "" "return 1;")) (fromRight emptyTestStatement (parse parseStatement "" "return 1;")) + +testIf :: Test +testIf = TestCase $ do + assertEqual + "empty" + False + (isRight (parse parseControl "" "")) + assertEqual + "if true {}" + (ControlStatement (IfControl (AtomicExpression (LiteralAtomic (BoolLiteral True))) [] Nothing)) + (either (const emptyTestStatement) ControlStatement (parse parseControl "" "if true {}")) + assertEqual + "if true {} -> statement" + (either (const emptyTestStatement) ControlStatement (parse parseControl "" "if true {}")) + (fromRight emptyTestStatement (parse parseStatement "" "if true {}")) + assertEqual + "if true { return 0; } else { return 1; }" + ( ControlStatement + ( IfControl + (AtomicExpression (LiteralAtomic (BoolLiteral True))) + [ReturnStatement (AtomicExpression (LiteralAtomic (IntLiteral 0)))] + (Just [ReturnStatement (AtomicExpression (LiteralAtomic (IntLiteral 1)))]) + ) + ) + (either (const emptyTestStatement) ControlStatement (parse parseControl "" "if true { return 0; } else { return 1; }"))