From 666856b33abf1d5df1f136dca9b3c2c5ed366a7e Mon Sep 17 00:00:00 2001 From: Marvin Schlegel Date: Fri, 31 May 2024 12:02:25 +0200 Subject: [PATCH 1/8] parser add constructor call --- Test/TestParser.hs | 15 ++++++++++++++- src/Parser/JavaParser.y | 10 +++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Test/TestParser.hs b/Test/TestParser.hs index 521a615..99ad2c9 100644 --- a/Test/TestParser.hs +++ b/Test/TestParser.hs @@ -200,6 +200,9 @@ testExpressionSimpleFieldAccess = TestCase $ testExpressionFieldSubAccess = TestCase $ assertEqual "expect NameResolution without this" (BinaryOperation NameResolution (Reference "a") (Reference "b")) $ parseExpression [IDENTIFIER "a",DOT,IDENTIFIER "b"] +testExpressionConstructorCall = TestCase $ + assertEqual "expect constructor call" (StatementExpressionExpression (ConstructorCall "Foo" [])) $ + parseExpression [NEW,IDENTIFIER "Foo",LBRACE,RBRACE] testStatementIfThen = TestCase $ assertEqual "expect empty ifthen" [If (Reference "a") (Block [Block []]) Nothing] $ @@ -217,6 +220,13 @@ testStatementAssign = TestCase $ testStatementMethodCallNoParams = TestCase $ assertEqual "expect methodcall statement no params" [StatementExpressionStatement (MethodCall (Reference "this") "foo" [])] $ parseStatement [IDENTIFIER "foo",LBRACE,RBRACE,SEMICOLON] +testStatementConstructorCall = TestCase $ + assertEqual "expect constructor call" [StatementExpressionStatement (ConstructorCall "Foo" [])] $ + parseStatement [NEW,IDENTIFIER "Foo",LBRACE,RBRACE,SEMICOLON] +testStatementConstructorCallWithArgs = TestCase $ + assertEqual "expect constructor call" [StatementExpressionStatement (ConstructorCall "Foo" [Reference "b"])] $ + parseStatement [NEW,IDENTIFIER "Foo",LBRACE,IDENTIFIER "b",RBRACE,SEMICOLON] + @@ -279,9 +289,12 @@ tests = TestList [ testExpressionFieldAccess, testExpressionSimpleFieldAccess, testExpressionFieldSubAccess, + testExpressionConstructorCall, testStatementIfThen, testStatementIfThenElse, testStatementWhile, testStatementAssign, - testStatementMethodCallNoParams + testStatementMethodCallNoParams, + testStatementConstructorCall, + testStatementConstructorCallWithArgs ] \ No newline at end of file diff --git a/src/Parser/JavaParser.y b/src/Parser/JavaParser.y index fd694b6..54c4b55 100644 --- a/src/Parser/JavaParser.y +++ b/src/Parser/JavaParser.y @@ -117,7 +117,7 @@ modifier : PUBLIC { } | STATIC { } | ABSTRACT { } -classtype : classorinterfacetype{ } +classtype : classorinterfacetype { $1 } classbodydeclaration : classmemberdeclaration { $1 } | constructordeclaration { $1 } @@ -254,7 +254,7 @@ statementexpression : assignment { $1 } -- | postincrementexpression { } -- | postdecrementexpression { } | methodinvocation { $1 } - -- | classinstancecreationexpression { } + | classinstancecreationexpression { $1 } ifthenelsestatementnoshortif :IF LBRACE expression RBRACE statementnoshortif ELSE statementnoshortif { } @@ -292,8 +292,8 @@ methodinvocation : simplename LBRACE RBRACE { MethodCall (Reference "this") $ | primary DOT IDENTIFIER LBRACE RBRACE { MethodCall $1 $3 [] } | primary DOT IDENTIFIER LBRACE argumentlist RBRACE { MethodCall $1 $3 $5 } -classinstancecreationexpression : NEW classtype LBRACE RBRACE { } - | NEW classtype LBRACE argumentlist RBRACE { } +classinstancecreationexpression : NEW classtype LBRACE RBRACE { ConstructorCall $2 [] } + | NEW classtype LBRACE argumentlist RBRACE { ConstructorCall $2 $4 } conditionalandexpression : inclusiveorexpression { $1 } @@ -318,7 +318,7 @@ inclusiveorexpression : exclusiveorexpression { $1 } primarynonewarray : literal { $1 } | THIS { Reference "this" } | LBRACE expression RBRACE { $2 } - -- | classinstancecreationexpression { } + | classinstancecreationexpression { StatementExpressionExpression $1 } | fieldaccess { $1 } | methodinvocation { StatementExpressionExpression $1 } From af093fa3bbcc1cbd36818dd63d884b647ac22601 Mon Sep 17 00:00:00 2001 From: Marvin Schlegel Date: Fri, 31 May 2024 12:10:00 +0200 Subject: [PATCH 2/8] parser add increment statement --- Test/TestParser.hs | 7 +++++-- src/Parser/JavaParser.y | 8 ++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Test/TestParser.hs b/Test/TestParser.hs index 99ad2c9..5727203 100644 --- a/Test/TestParser.hs +++ b/Test/TestParser.hs @@ -227,7 +227,9 @@ testStatementConstructorCallWithArgs = TestCase $ assertEqual "expect constructor call" [StatementExpressionStatement (ConstructorCall "Foo" [Reference "b"])] $ parseStatement [NEW,IDENTIFIER "Foo",LBRACE,IDENTIFIER "b",RBRACE,SEMICOLON] - +testStatementPreIncrement = TestCase $ + assertEqual "expect increment" [StatementExpressionStatement $ PostIncrement $ Reference "a"] $ + parseStatement [IDENTIFIER "a",INCREMENT,SEMICOLON] tests = TestList [ @@ -296,5 +298,6 @@ tests = TestList [ testStatementAssign, testStatementMethodCallNoParams, testStatementConstructorCall, - testStatementConstructorCallWithArgs + testStatementConstructorCallWithArgs, + testStatementIncrement ] \ No newline at end of file diff --git a/src/Parser/JavaParser.y b/src/Parser/JavaParser.y index 54c4b55..aa22bb8 100644 --- a/src/Parser/JavaParser.y +++ b/src/Parser/JavaParser.y @@ -249,10 +249,10 @@ assignment : lefthandside assignmentoperator assignmentexpression { statementexpression : assignment { $1 } - -- | preincrementexpression { } - -- | predecrementexpression { } - -- | postincrementexpression { } - -- | postdecrementexpression { } + | preincrementexpression { $1 } + | predecrementexpression { $1 } + | postincrementexpression { $1 } + | postdecrementexpression { $1 } | methodinvocation { $1 } | classinstancecreationexpression { $1 } From 05b599b8ff04bfc676d895a0d7ab7f39a4051cff Mon Sep 17 00:00:00 2001 From: Fabian Noll Date: Fri, 31 May 2024 17:10:50 +0200 Subject: [PATCH 3/8] fix if typecheck --- src/Typecheck.hs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Typecheck.hs b/src/Typecheck.hs index ba49157..f7e63ae 100644 --- a/src/Typecheck.hs +++ b/src/Typecheck.hs @@ -21,7 +21,7 @@ typeCheckMethodDeclaration (MethodDeclaration retType name params body) classFie let -- Combine class fields with method parameters to form the initial symbol table for the method methodParams = [(identifier, dataType) | ParameterDeclaration dataType identifier <- params] - initialSymtab = classFields ++ methodParams + initialSymtab = ("thisMeth", retType) : classFields ++ methodParams checkedBody = typeCheckStatement body initialSymtab classes bodyType = getTypeFromStmt checkedBody -- Check if the type of the body matches the declared return type @@ -37,6 +37,7 @@ typeCheckExpression (CharacterLiteral c) _ _ = TypedExpression "char" (Character typeCheckExpression (BooleanLiteral b) _ _ = TypedExpression "boolean" (BooleanLiteral b) typeCheckExpression NullLiteral _ _ = TypedExpression "null" NullLiteral typeCheckExpression (Reference id) symtab classes = + -- TODO: maybe maje exception for "this" in first lookup? case lookup id symtab of Just t -> TypedExpression t (LocalVariable id) Nothing -> @@ -208,12 +209,16 @@ typeCheckStatement (If cond thenStmt elseStmt) symtab classes = elseStmt' = case elseStmt of Just stmt -> Just (typeCheckStatement stmt symtab classes) Nothing -> Nothing + thenType = getTypeFromStmt thenStmt' + elseType = maybe "void" getTypeFromStmt elseStmt' + ifType = if thenType /= "void" && elseType /= "void" && thenType == elseType then thenType else "void" in if getTypeFromExpr cond' == "boolean" then - TypedStatement (getTypeFromStmt thenStmt') (If cond' thenStmt' elseStmt') + TypedStatement ifType (If cond' thenStmt' elseStmt') else error "If condition must be of type boolean" + typeCheckStatement (LocalVariableDeclaration (VariableDeclaration dataType identifier maybeExpr)) symtab classes = -- Check for redefinition in the current scope if any ((== identifier) . snd) symtab From 3d351ee02b8067fd92fd8c94232913545be29a0f Mon Sep 17 00:00:00 2001 From: Fabian Noll Date: Fri, 31 May 2024 17:11:46 +0200 Subject: [PATCH 4/8] fix false error message --- src/Typecheck.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Typecheck.hs b/src/Typecheck.hs index f7e63ae..ac08d39 100644 --- a/src/Typecheck.hs +++ b/src/Typecheck.hs @@ -324,7 +324,7 @@ checkBitwiseOperation :: BinaryOperator -> Expression -> Expression -> DataType checkBitwiseOperation op expr1' expr2' type1 type2 | type1 == "int" && type2 == "int" = TypedExpression "int" (BinaryOperation op expr1' expr2') - | otherwise = error $ "Bitwise operation " ++ show op ++ " requires operands of type int" + | otherwise = error $ "Bitwise operation " ++ show op ++ " requires operands of type int or char" checkComparisonOperation :: BinaryOperator -> Expression -> Expression -> DataType -> DataType -> Expression checkComparisonOperation op expr1' expr2' type1 type2 From 82b2b4a6e1bf5d2442dd10be819cbb7c0423f51c Mon Sep 17 00:00:00 2001 From: Marvin Schlegel Date: Fri, 31 May 2024 17:39:56 +0200 Subject: [PATCH 5/8] fix intliteral 0 --- src/Parser/Lexer.x | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Parser/Lexer.x b/src/Parser/Lexer.x index ef76773..cb0d075 100644 --- a/src/Parser/Lexer.x +++ b/src/Parser/Lexer.x @@ -72,7 +72,7 @@ tokens :- -- end keywords $JavaLetter$JavaLetterOrDigit* { \s -> IDENTIFIER s } -- Literals - [1-9]([0-9\_]*[0-9])* { \s -> case readMaybe $ filter ((/=) '_') s of Just a -> INTEGERLITERAL a; Nothing -> error ("failed to parse INTLITERAL " ++ s) } + [0-9]([0-9\_]*[0-9])* { \s -> case readMaybe $ filter ((/=) '_') s of Just a -> INTEGERLITERAL a; Nothing -> error ("failed to parse INTLITERAL " ++ s) } "'"."'" { \s -> case (s) of _ : c : _ -> CHARLITERAL c; _ -> error ("failed to parse CHARLITERAL " ++ s) } -- separators "(" { \_ -> LBRACE } From 98b02446ba4d8ab4d032b250c5a53db1db6feefd Mon Sep 17 00:00:00 2001 From: fanoll Date: Mon, 10 Jun 2024 12:53:59 +0200 Subject: [PATCH 6/8] remove unused thisMeth type. Returns are combined and already checked against return Type --- src/Typecheck.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Typecheck.hs b/src/Typecheck.hs index ac08d39..fd58f17 100644 --- a/src/Typecheck.hs +++ b/src/Typecheck.hs @@ -21,7 +21,7 @@ typeCheckMethodDeclaration (MethodDeclaration retType name params body) classFie let -- Combine class fields with method parameters to form the initial symbol table for the method methodParams = [(identifier, dataType) | ParameterDeclaration dataType identifier <- params] - initialSymtab = ("thisMeth", retType) : classFields ++ methodParams + initialSymtab = classFields ++ methodParams checkedBody = typeCheckStatement body initialSymtab classes bodyType = getTypeFromStmt checkedBody -- Check if the type of the body matches the declared return type From 7c52084bbedf052ff1522869b8663ffdd77b06ee Mon Sep 17 00:00:00 2001 From: Marvin Schlegel Date: Mon, 10 Jun 2024 17:19:56 +0200 Subject: [PATCH 7/8] fix test --- Test/TestParser.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/TestParser.hs b/Test/TestParser.hs index 5727203..5041e3e 100644 --- a/Test/TestParser.hs +++ b/Test/TestParser.hs @@ -299,5 +299,5 @@ tests = TestList [ testStatementMethodCallNoParams, testStatementConstructorCall, testStatementConstructorCallWithArgs, - testStatementIncrement + testStatementPreIncrement ] \ No newline at end of file From b525d141924e84cdb9f4f3062d950b45bd3c16b2 Mon Sep 17 00:00:00 2001 From: MisterChaos96 Date: Tue, 11 Jun 2024 20:04:59 +0200 Subject: [PATCH 8/8] add typechecking for returns, fix finding of constructors, fix if statement --- src/Typecheck.hs | 68 ++++++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/src/Typecheck.hs b/src/Typecheck.hs index fd58f17..e4aab3b 100644 --- a/src/Typecheck.hs +++ b/src/Typecheck.hs @@ -19,15 +19,14 @@ typeCheckClass (Class className methods fields) classes = typeCheckMethodDeclaration :: MethodDeclaration -> [(Identifier, DataType)] -> [Class] -> MethodDeclaration typeCheckMethodDeclaration (MethodDeclaration retType name params body) classFields classes = let - -- Combine class fields with method parameters to form the initial symbol table for the method methodParams = [(identifier, dataType) | ParameterDeclaration dataType identifier <- params] - initialSymtab = classFields ++ methodParams + initialSymtab = ("thisMeth", retType) : classFields ++ methodParams checkedBody = typeCheckStatement body initialSymtab classes bodyType = getTypeFromStmt checkedBody - -- Check if the type of the body matches the declared return type - in if bodyType == retType || (bodyType == "void" && retType == "void") || (bodyType == "null" && isObjectType retType) + in if bodyType == retType || (bodyType == "void" && retType == "void") || (bodyType == "null" && isObjectType retType) || isSubtype bodyType retType classes then MethodDeclaration retType name params checkedBody - else error $ "Return type mismatch in method " ++ name ++ ": expected " ++ retType ++ ", found " ++ bodyType + else error $ "Method Declaration: Return type mismatch in method " ++ name ++ ": expected " ++ retType ++ ", found " ++ bodyType + -- ********************************** Type Checking: Expressions ********************************** @@ -119,7 +118,7 @@ typeCheckStatementExpression (ConstructorCall className args) symtab classes = Nothing -> error $ "Class '" ++ className ++ "' not found." Just (Class _ methods fields) -> -- Constructor needs the same name as the class - case find (\(MethodDeclaration retType name params _) -> name == className && retType == className) methods of + case find (\(MethodDeclaration retType name params _) -> name == "" && retType == "void") methods of Nothing -> error $ "No valid constructor found for class '" ++ className ++ "'." Just (MethodDeclaration _ _ params _) -> let @@ -204,19 +203,21 @@ typeCheckStatementExpression (PreDecrement expr) symtab classes = typeCheckStatement :: Statement -> [(Identifier, DataType)] -> [Class] -> Statement typeCheckStatement (If cond thenStmt elseStmt) symtab classes = - let cond' = typeCheckExpression cond symtab classes - thenStmt' = typeCheckStatement thenStmt symtab classes - elseStmt' = case elseStmt of - Just stmt -> Just (typeCheckStatement stmt symtab classes) - Nothing -> Nothing - thenType = getTypeFromStmt thenStmt' - elseType = maybe "void" getTypeFromStmt elseStmt' - ifType = if thenType /= "void" && elseType /= "void" && thenType == elseType then thenType else "void" - in if getTypeFromExpr cond' == "boolean" - then - TypedStatement ifType (If cond' thenStmt' elseStmt') - else - error "If condition must be of type boolean" + let + cond' = typeCheckExpression cond symtab classes + thenStmt' = typeCheckStatement thenStmt symtab classes + elseStmt' = fmap (\stmt -> typeCheckStatement stmt symtab classes) elseStmt + + thenType = getTypeFromStmt thenStmt' + elseType = maybe "void" getTypeFromStmt elseStmt' + + ifType = if thenType == "void" || elseType == "void" + then "void" + else unifyReturnTypes thenType elseType + + in if getTypeFromExpr cond' == "boolean" + then TypedStatement ifType (If cond' thenStmt' elseStmt') + else error "If condition must be of type boolean" typeCheckStatement (LocalVariableDeclaration (VariableDeclaration dataType identifier maybeExpr)) symtab classes = @@ -229,7 +230,7 @@ typeCheckStatement (LocalVariableDeclaration (VariableDeclaration dataType ident exprType = fmap getTypeFromExpr checkedExpr in case exprType of Just t - | t == "null" && isObjectType dataType -> + | t == "null" && isObjectType dataType -> TypedStatement dataType (LocalVariableDeclaration (VariableDeclaration dataType identifier checkedExpr)) | t /= dataType -> error $ "Type mismatch in declaration of '" ++ identifier ++ "': expected " ++ dataType ++ ", found " ++ t | otherwise -> TypedStatement dataType (LocalVariableDeclaration (VariableDeclaration dataType identifier checkedExpr)) @@ -272,12 +273,14 @@ typeCheckStatement (Block statements) symtab classes = in TypedStatement blockType (Block checkedStatements) typeCheckStatement (Return expr) symtab classes = - let expr' = case expr of + let methodReturnType = fromMaybe (error "Method return type not found in symbol table") (lookup "thisMeth" symtab) + expr' = case expr of Just e -> Just (typeCheckExpression e symtab classes) Nothing -> Nothing - in case expr' of - Just e' -> TypedStatement (getTypeFromExpr e') (Return (Just e')) - Nothing -> TypedStatement "void" (Return Nothing) + returnType = maybe "void" getTypeFromExpr expr' + in if returnType == methodReturnType || isSubtype returnType methodReturnType classes + then TypedStatement returnType (Return expr') + else error $ "Return: Return type mismatch: expected " ++ methodReturnType ++ ", found " ++ returnType typeCheckStatement (StatementExpressionStatement stmtExpr) symtab classes = let stmtExpr' = typeCheckStatementExpression stmtExpr symtab classes @@ -285,6 +288,17 @@ typeCheckStatement (StatementExpressionStatement stmtExpr) symtab classes = -- ********************************** Type Checking: Helpers ********************************** +isSubtype :: DataType -> DataType -> [Class] -> Bool +isSubtype subType superType classes + | subType == superType = True + | subType == "null" && isObjectType superType = True + | superType == "Object" && isObjectType subType = True + | superType == "Object" && isUserDefinedClass subType classes = True + | otherwise = False + +isUserDefinedClass :: DataType -> [Class] -> Bool +isUserDefinedClass dt classes = dt `elem` map (\(Class name _ _) -> name) classes + isObjectType :: DataType -> Bool isObjectType dt = dt /= "int" && dt /= "boolean" && dt /= "char" @@ -302,8 +316,10 @@ getTypeFromStmtExpr _ = error "Untyped statement expression found where typed wa unifyReturnTypes :: DataType -> DataType -> DataType unifyReturnTypes dt1 dt2 - | dt1 == dt2 = dt1 - | otherwise = "Object" + | dt1 == dt2 = dt1 + | dt1 == "null" = dt2 + | dt2 == "null" = dt1 + | otherwise = "Object" resolveResultType :: DataType -> DataType -> DataType resolveResultType "char" "char" = "char"