From 09dd11bb344c61f36d7c6531147ba83437749459 Mon Sep 17 00:00:00 2001 From: Christian Brier Date: Sun, 23 Jun 2024 17:38:52 +0200 Subject: [PATCH 01/16] updated docs --- Something.java | 5 +++++ doc/bytecode.md | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 Something.java diff --git a/Something.java b/Something.java new file mode 100644 index 0000000..a083d83 --- /dev/null +++ b/Something.java @@ -0,0 +1,5 @@ +public class Something +{ + public int a = 4134123; + //public Something something = null; +} \ No newline at end of file diff --git a/doc/bytecode.md b/doc/bytecode.md index 1002122..9bc7479 100644 --- a/doc/bytecode.md +++ b/doc/bytecode.md @@ -76,4 +76,7 @@ instance Serializable Operation where serialize (Opgetfield index) = 0xB4 : unpackWord16 index ``` -Die Struktur ClassFile ruft für deren Kinder rekursiv diese `serialize` Funktion auf und konkateniert die Ergebnisse. Am Ende bleibt eine flache Word8-Liste übrig, die Serialisierung ist damit abgeschlossen. Da der Typecheck sicherstellt, dass alle referenzierten Methoden/Felder gültig sind, kann die Übersetzung der einzelnen Klassen voneinander unabhängig geschehen. \ No newline at end of file +Die Struktur ClassFile ruft für deren Kinder rekursiv diese `serialize` Funktion auf und konkateniert die Ergebnisse. Am Ende bleibt eine flache Word8-Liste übrig, die Serialisierung ist damit abgeschlossen. Da der Typecheck sicherstellt, dass alle referenzierten Methoden/Felder gültig sind, kann die Übersetzung der einzelnen Klassen voneinander unabhängig geschehen. + + +Die Bytecodegenerierung wurde von Matthias Raba und Christian Brier im Stille des Pair Programming zu zweit erarbeitet. Durch bisher gute Erfahrungen in vorherigen Projekten, sowie dem Interesse alle Teile der Bytecodegenerierung zu sehen, wurde diese Programmierungsform als die Beste ausgewählt. Insgesamt lief die Implementierungsphase wie geplant und ohne weitere Komplikationen ab. \ No newline at end of file From 9ecadc89461f3c32e1edfa14df16e23d45b2511e Mon Sep 17 00:00:00 2001 From: Christian Brier Date: Sun, 23 Jun 2024 17:39:40 +0200 Subject: [PATCH 02/16] updated docs again --- doc/bytecode.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/bytecode.md b/doc/bytecode.md index 9bc7479..bc3a6db 100644 --- a/doc/bytecode.md +++ b/doc/bytecode.md @@ -78,5 +78,6 @@ instance Serializable Operation where Die Struktur ClassFile ruft für deren Kinder rekursiv diese `serialize` Funktion auf und konkateniert die Ergebnisse. Am Ende bleibt eine flache Word8-Liste übrig, die Serialisierung ist damit abgeschlossen. Da der Typecheck sicherstellt, dass alle referenzierten Methoden/Felder gültig sind, kann die Übersetzung der einzelnen Klassen voneinander unabhängig geschehen. +## Aufgabenteilung Die Bytecodegenerierung wurde von Matthias Raba und Christian Brier im Stille des Pair Programming zu zweit erarbeitet. Durch bisher gute Erfahrungen in vorherigen Projekten, sowie dem Interesse alle Teile der Bytecodegenerierung zu sehen, wurde diese Programmierungsform als die Beste ausgewählt. Insgesamt lief die Implementierungsphase wie geplant und ohne weitere Komplikationen ab. \ No newline at end of file From 946a1f374c87fc26a29dfd3dfa8cede4cf0aadc6 Mon Sep 17 00:00:00 2001 From: Matthias Raba Date: Mon, 24 Jun 2024 07:44:04 +0200 Subject: [PATCH 03/16] fixed stack op depth for getfield --- src/ByteCode/Util.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ByteCode/Util.hs b/src/ByteCode/Util.hs index f177342..7f48b17 100644 --- a/src/ByteCode/Util.hs +++ b/src/ByteCode/Util.hs @@ -279,14 +279,14 @@ operationStackCost constants (Opiload _) = 1 operationStackCost constants (Opastore _) = -1 operationStackCost constants (Opistore _) = -1 operationStackCost constants (Opputfield _) = -2 -operationStackCost constants (Opgetfield _) = -1 +operationStackCost constants (Opgetfield _) = 0 -simulateStackOperation :: [ConstantInfo] -> Operation -> (Int, Int) -> (Int, Int) -simulateStackOperation constants op (cd, md) = let +simulateStackOperation :: (Int, Int) -> [ConstantInfo] -> Operation -> (Int, Int) +simulateStackOperation (cd, md) constants op = let depth = cd + operationStackCost constants op in if depth < 0 then error ("Consuming value off of empty stack: " ++ show op) else (depth, max depth md) maxStackDepth :: [ConstantInfo] -> [Operation] -> Int -maxStackDepth constants ops = snd $ foldr (simulateStackOperation constants) (0, 0) (reverse ops) +maxStackDepth constants ops = snd $ foldl (`simulateStackOperation` constants) (0, 0) ops From 74f52c3c35024201cd4ff163f3089f0dabdf1ea0 Mon Sep 17 00:00:00 2001 From: Matthias Raba Date: Mon, 24 Jun 2024 07:47:43 +0200 Subject: [PATCH 04/16] fixed small typo in documentation --- Something.java | 5 ----- doc/bytecode.md | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) delete mode 100644 Something.java diff --git a/Something.java b/Something.java deleted file mode 100644 index a083d83..0000000 --- a/Something.java +++ /dev/null @@ -1,5 +0,0 @@ -public class Something -{ - public int a = 4134123; - //public Something something = null; -} \ No newline at end of file diff --git a/doc/bytecode.md b/doc/bytecode.md index bc3a6db..5624b72 100644 --- a/doc/bytecode.md +++ b/doc/bytecode.md @@ -4,7 +4,7 @@ Die Bytecodegenerierung ist letztendlich eine zweistufige Transformation: `Getypter AST -> [ClassFile] -> [[Word8]]` -Vom AST, der bereits den Typcheck durchlaufen hat, wird zunächst eine Abbildung in die einzelnen ClassFiles vorgenommen. Diese ClassFiles werden anschließend in deren Byte-Repräsentation serialisiert. Dieser Teil der Aufgabenstellung wurde gemeinsam von Christian Brier und Matthias Raba umgesetzt. +Vom AST, der bereits den Typcheck durchlaufen hat, wird zunächst eine Abbildung in die einzelnen ClassFiles vorgenommen. Diese ClassFiles werden anschließend in deren Byte-Repräsentation serialisiert. ## Codegenerierung @@ -80,4 +80,4 @@ Die Struktur ClassFile ruft für deren Kinder rekursiv diese `serialize` Funktio ## Aufgabenteilung -Die Bytecodegenerierung wurde von Matthias Raba und Christian Brier im Stille des Pair Programming zu zweit erarbeitet. Durch bisher gute Erfahrungen in vorherigen Projekten, sowie dem Interesse alle Teile der Bytecodegenerierung zu sehen, wurde diese Programmierungsform als die Beste ausgewählt. Insgesamt lief die Implementierungsphase wie geplant und ohne weitere Komplikationen ab. \ No newline at end of file +Die Bytecodegenerierung wurde von Matthias Raba und Christian Brier im Stile des Pair Programmings zu zweit erarbeitet. Durch bisher gute Erfahrungen in vorherigen Projekten, sowie dem Interesse, alle Teile der Bytecodegenerierung zu sehen, wurde diese Programmierungsform als die Beste ausgewählt. Insgesamt lief die Implementierungsphase wie geplant und ohne weitere Komplikationen ab. \ No newline at end of file From 6346cb237bb4cc920b9e38f11f499379908ded4c Mon Sep 17 00:00:00 2001 From: Matthias Raba Date: Mon, 24 Jun 2024 11:20:24 +0200 Subject: [PATCH 05/16] removed redundant definitions for opcodeEncodingLength --- src/ByteCode/ClassFile.hs | 35 +---------------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/src/ByteCode/ClassFile.hs b/src/ByteCode/ClassFile.hs index 7a20e97..3546ded 100644 --- a/src/ByteCode/ClassFile.hs +++ b/src/ByteCode/ClassFile.hs @@ -96,40 +96,7 @@ className classFile = let opcodeEncodingLength :: Operation -> Word16 -opcodeEncodingLength Opiadd = 1 -opcodeEncodingLength Opisub = 1 -opcodeEncodingLength Opimul = 1 -opcodeEncodingLength Opidiv = 1 -opcodeEncodingLength Opirem = 1 -opcodeEncodingLength Opiand = 1 -opcodeEncodingLength Opior = 1 -opcodeEncodingLength Opixor = 1 -opcodeEncodingLength Opineg = 1 -opcodeEncodingLength Opdup = 1 -opcodeEncodingLength (Opnew _) = 3 -opcodeEncodingLength (Opif_icmplt _) = 3 -opcodeEncodingLength (Opif_icmple _) = 3 -opcodeEncodingLength (Opif_icmpgt _) = 3 -opcodeEncodingLength (Opif_icmpge _) = 3 -opcodeEncodingLength (Opif_icmpeq _) = 3 -opcodeEncodingLength (Opif_icmpne _) = 3 -opcodeEncodingLength Opaconst_null = 1 -opcodeEncodingLength Opreturn = 1 -opcodeEncodingLength Opireturn = 1 -opcodeEncodingLength Opareturn = 1 -opcodeEncodingLength Opdup_x1 = 1 -opcodeEncodingLength Oppop = 1 -opcodeEncodingLength (Opinvokespecial _) = 3 -opcodeEncodingLength (Opinvokevirtual _) = 3 -opcodeEncodingLength (Opgoto _) = 3 -opcodeEncodingLength (Opsipush _) = 3 -opcodeEncodingLength (Opldc_w _) = 3 -opcodeEncodingLength (Opaload _) = 4 -opcodeEncodingLength (Opiload _) = 4 -opcodeEncodingLength (Opastore _) = 4 -opcodeEncodingLength (Opistore _) = 4 -opcodeEncodingLength (Opputfield _) = 3 -opcodeEncodingLength (Opgetfield _) = 3 +opcodeEncodingLength op = fromIntegral . length . serialize $ op class Serializable a where serialize :: a -> [Word8] From 87f629282f815381aa3ffe2503e8b8e58bfb8e97 Mon Sep 17 00:00:00 2001 From: Matthias Raba Date: Mon, 24 Jun 2024 11:28:57 +0200 Subject: [PATCH 06/16] fixed order of compound AST types --- src/Ast.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ast.hs b/src/Ast.hs index fdf088b..794b030 100644 --- a/src/Ast.hs +++ b/src/Ast.hs @@ -6,8 +6,8 @@ type Identifier = String data ParameterDeclaration = ParameterDeclaration DataType Identifier deriving (Show, Eq) data VariableDeclaration = VariableDeclaration DataType Identifier (Maybe Expression) deriving (Show, Eq) -data Class = Class DataType [MethodDeclaration] [VariableDeclaration] deriving (Show, Eq) data MethodDeclaration = MethodDeclaration DataType Identifier [ParameterDeclaration] Statement deriving (Show, Eq) +data Class = Class DataType [MethodDeclaration] [VariableDeclaration] deriving (Show, Eq) data Statement = If Expression Statement (Maybe Statement) @@ -23,11 +23,11 @@ data StatementExpression = Assignment Expression Expression | ConstructorCall DataType [Expression] | MethodCall Expression Identifier [Expression] - | TypedStatementExpression DataType StatementExpression | PostIncrement Expression | PostDecrement Expression | PreIncrement Expression | PreDecrement Expression + | TypedStatementExpression DataType StatementExpression deriving (Show, Eq) data BinaryOperator From 6b4b9b496d33b46e6a81a7888fabcf5c66a73f64 Mon Sep 17 00:00:00 2001 From: mrab Date: Wed, 26 Jun 2024 09:42:07 +0200 Subject: [PATCH 07/16] fixed local variable declaration being ignore inside for loop --- Test/JavaSources/Main.java | 2 ++ Test/JavaSources/TestLoop.java | 12 ++++++++++++ src/ByteCode/Assembler.hs | 14 +++++++------- 3 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 Test/JavaSources/TestLoop.java diff --git a/Test/JavaSources/Main.java b/Test/JavaSources/Main.java index b912e91..e3b7b76 100644 --- a/Test/JavaSources/Main.java +++ b/Test/JavaSources/Main.java @@ -15,6 +15,7 @@ public class Main { TestMultipleClasses multipleClasses = new TestMultipleClasses(); TestRecursion recursion = new TestRecursion(10); TestMalicious malicious = new TestMalicious(); + TestLoop loop = new TestLoop(); // constructing a basic class works assert empty != null; @@ -32,6 +33,7 @@ public class Main { assert recursion.child.child.child.child.child.value == 5; // self-referencing methods work. assert recursion.fibonacci(15) == 610; + assert loop.factorial(5) == 120; // intentionally dodgy expressions work assert malicious.assignNegativeIncrement(42) == -42; assert malicious.tripleAddition(1, 2, 3) == 6; diff --git a/Test/JavaSources/TestLoop.java b/Test/JavaSources/TestLoop.java new file mode 100644 index 0000000..40491fb --- /dev/null +++ b/Test/JavaSources/TestLoop.java @@ -0,0 +1,12 @@ +public class TestLoop { + public int factorial(int n) + { + int tally = 1; + for(int i = 1; i <= n; i++) + { + tally *= i; + } + + return tally; + } +} diff --git a/src/ByteCode/Assembler.hs b/src/ByteCode/Assembler.hs index 101d7aa..555569b 100644 --- a/src/ByteCode/Assembler.hs +++ b/src/ByteCode/Assembler.hs @@ -221,26 +221,26 @@ assembleStatement (constants, ops, lvars) (TypedStatement _ (Block statements)) assembleStatement (constants, ops, lvars) (TypedStatement dtype (If expr if_stmt else_stmt)) = let (constants_cmp, ops_cmp, _) = assembleExpression (constants, [], lvars) expr - (constants_ifa, ops_ifa, _) = assembleStatement (constants_cmp, [], lvars) if_stmt + (constants_ifa, ops_ifa, lvars_ifa) = assembleStatement (constants_cmp, [], lvars) if_stmt (constants_elsea, ops_elsea, _) = case else_stmt of - Nothing -> (constants_ifa, [], lvars) - Just stmt -> assembleStatement (constants_ifa, [], lvars) stmt + Nothing -> (constants_ifa, [], lvars_ifa) + Just stmt -> assembleStatement (constants_ifa, [], lvars_ifa) stmt -- +6 because we insert 2 gotos, one for if, one for else if_length = sum (map opcodeEncodingLength ops_ifa) -- +3 because we need to account for the goto in the if statement. else_length = sum (map opcodeEncodingLength ops_elsea) in case dtype of - "void" -> (constants_ifa, ops ++ ops_cmp ++ [Opsipush 0, Opif_icmpeq (if_length + 6)] ++ ops_ifa ++ [Opgoto (else_length + 3)] ++ ops_elsea, lvars) - _ -> (constants_ifa, ops ++ ops_cmp ++ [Opsipush 0, Opif_icmpeq (if_length + 3)] ++ ops_ifa ++ ops_elsea, lvars) + "void" -> (constants_ifa, ops ++ ops_cmp ++ [Opsipush 0, Opif_icmpeq (if_length + 6)] ++ ops_ifa ++ [Opgoto (else_length + 3)] ++ ops_elsea, lvars_ifa) + _ -> (constants_ifa, ops ++ ops_cmp ++ [Opsipush 0, Opif_icmpeq (if_length + 3)] ++ ops_ifa ++ ops_elsea, lvars_ifa) assembleStatement (constants, ops, lvars) (TypedStatement _ (While expr stmt)) = let (constants_cmp, ops_cmp, _) = assembleExpression (constants, [], lvars) expr - (constants_stmta, ops_stmta, _) = assembleStatement (constants_cmp, [], lvars) stmt + (constants_stmta, ops_stmta, lvars_stmta) = assembleStatement (constants_cmp, [], lvars) stmt -- +3 because we insert 2 gotos, one for the comparison, one for the goto back to the comparison stmt_length = sum (map opcodeEncodingLength ops_stmta) + 6 entire_length = stmt_length + sum (map opcodeEncodingLength ops_cmp) in - (constants_stmta, ops ++ ops_cmp ++ [Opsipush 0, Opif_icmpeq stmt_length] ++ ops_stmta ++ [Opgoto (-entire_length)], lvars) + (constants_stmta, ops ++ ops_cmp ++ [Opsipush 0, Opif_icmpeq stmt_length] ++ ops_stmta ++ [Opgoto (-entire_length)], lvars_stmta) assembleStatement (constants, ops, lvars) (TypedStatement _ (LocalVariableDeclaration (VariableDeclaration dtype name expr))) = let isPrimitive = elem dtype ["char", "boolean", "int"] From 504e26dcddc0b3dccabe63303f6096a7a0f74798 Mon Sep 17 00:00:00 2001 From: MisterChaos96 Date: Thu, 27 Jun 2024 18:08:36 +0200 Subject: [PATCH 08/16] fix objects not comparable to null --- src/Typecheck.hs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Typecheck.hs b/src/Typecheck.hs index 5ccc7d3..d56f3a5 100644 --- a/src/Typecheck.hs +++ b/src/Typecheck.hs @@ -408,9 +408,14 @@ checkComparisonOperation op expr1' expr2' type1 type2 checkEqualityOperation :: BinaryOperator -> Expression -> Expression -> DataType -> DataType -> Expression checkEqualityOperation op expr1' expr2' type1 type2 - | type1 == type2 = + | type1 == type2 || (type1 == "null" && isObjectType type2) || (type2 == "null" && isObjectType type1) = TypedExpression "boolean" (BinaryOperation op expr1' expr2') - | otherwise = error $ "Equality operation " ++ show op ++ " requires operands of the same type" + | type1 /= type2 = + error $ "Equality operation " ++ show op ++ " requires operands of the same type. Found types: " ++ type1 ++ " and " ++ type2 + | (type1 == "null" && not (isObjectType type2)) || (type2 == "null" && not (isObjectType type1)) = + error $ "Equality operation " ++ show op ++ " requires that null can only be compared with object types. Found types: " ++ type1 ++ " and " ++ type2 + | otherwise = error $ "Equality operation " ++ show op ++ " encountered unexpected types: " ++ type1 ++ " and " ++ type2 + checkLogicalOperation :: BinaryOperator -> Expression -> Expression -> DataType -> DataType -> Expression checkLogicalOperation op expr1' expr2' type1 type2 From 2154f8fd6225c7554dde1b43a09d7d493c6fbb18 Mon Sep 17 00:00:00 2001 From: MisterChaos96 Date: Thu, 27 Jun 2024 18:52:18 +0200 Subject: [PATCH 09/16] add method overloading --- Test/JavaSources/TestMethodOverload.java | 14 +++++++ src/Typecheck.hs | 52 +++++++++++++----------- 2 files changed, 42 insertions(+), 24 deletions(-) create mode 100644 Test/JavaSources/TestMethodOverload.java diff --git a/Test/JavaSources/TestMethodOverload.java b/Test/JavaSources/TestMethodOverload.java new file mode 100644 index 0000000..6fe9877 --- /dev/null +++ b/Test/JavaSources/TestMethodOverload.java @@ -0,0 +1,14 @@ +public class TestMethodOverload { + + public void MethodOverload() { + } + + public void MethodOverload(int a) { + } + + public void test() { + int a = 3; + MethodOverload(a); + } + +} diff --git a/src/Typecheck.hs b/src/Typecheck.hs index d56f3a5..9b66b43 100644 --- a/src/Typecheck.hs +++ b/src/Typecheck.hs @@ -178,31 +178,35 @@ typeCheckStatementExpression (MethodCall expr methodName args) symtab classes = let objExprTyped = typeCheckExpression expr symtab classes in case objExprTyped of TypedExpression objType _ -> - case find (\(Class className _ _) -> className == objType) classes of - Just (Class _ methods _) -> - case find (\(MethodDeclaration retType name params _) -> name == methodName) methods of - Just (MethodDeclaration retType _ params _) -> - let args' = zipWith - (\arg (ParameterDeclaration paramType _) -> + case find (\(Class className _ _ _) -> className == objType) classes of + Just (Class _ _ methods _) -> + let matchParams (ParameterDeclaration paramType _) arg = let argTyped = typeCheckExpression arg symtab classes - in if getTypeFromExpr argTyped == "null" && isObjectType paramType - then TypedExpression paramType NullLiteral - else argTyped - ) args params - expectedTypes = [dataType | ParameterDeclaration dataType _ <- params] - argTypes = map getTypeFromExpr args' - typeMatches = zipWith - (\expType argType -> (expType == argType || (argType == "null" && isObjectType expType), expType, argType)) - expectedTypes argTypes - mismatches = filter (not . fst3) typeMatches - fst3 (a, _, _) = a - in if null mismatches && length args == length params - then TypedStatementExpression retType (MethodCall objExprTyped methodName args') - else if not (null mismatches) - then error $ unlines $ ("Argument type mismatches for method '" ++ methodName ++ "':") - : [ "Expected: " ++ expType ++ ", Found: " ++ argType | (_, expType, argType) <- mismatches ] - else error $ "Incorrect number of arguments for method '" ++ methodName ++ "'. Expected " ++ show (length expectedTypes) ++ ", found " ++ show (length args) ++ "." - Nothing -> error $ "Method '" ++ methodName ++ "' not found in class '" ++ objType ++ "'." + argType = getTypeFromExpr argTyped + in if argType == "null" && isObjectType paramType + then Just (TypedExpression paramType NullLiteral) + else if argType == paramType + then Just argTyped + else Nothing + + matchMethod (MethodDeclaration retType name params _) = + let matchedArgs = sequence $ zipWith matchParams params args + in fmap (\checkedArgs -> (MethodDeclaration retType name params (Block []), checkedArgs)) matchedArgs + + validMethods = filter (\(MethodDeclaration _ name params _, _) -> name == methodName && length params == length args) $ mapMaybe matchMethod methods + + expectedSignatures = [ map (\(ParameterDeclaration t _) -> t) params | MethodDeclaration _ name params _ <- methods, name == methodName ] + actualSignature = map (\arg -> getTypeFromExpr (typeCheckExpression arg symtab classes)) args + mismatchDetails = "Method not found for class '" ++ objType ++ "' with given arguments.\n" ++ + "Expected signatures for method '" ++ methodName ++ "':\n" ++ unlines (map show expectedSignatures) ++ + "Actual arguments:\n" ++ show actualSignature + + in case validMethods of + [(MethodDeclaration retType _ params _, checkedArgs)] -> + TypedStatementExpression retType (MethodCall objExprTyped methodName checkedArgs) + [] -> error mismatchDetails + _ -> error $ "Multiple matching methods found for class '" ++ objType ++ "' and method '" ++ methodName ++ "' with given arguments." + Nothing -> error $ "Class for object type '" ++ objType ++ "' not found." _ -> error "Invalid object type for method call. Object must have a class type." From fe4ef2614f61abb5f1c6de5d118f8e4ef133f1cc Mon Sep 17 00:00:00 2001 From: MisterChaos96 Date: Thu, 27 Jun 2024 18:53:01 +0200 Subject: [PATCH 10/16] add new constructortypecheck with overloading --- Test/JavaSources/TestConstructorOverload.java | 13 +++ Test/JavaSources/TestSingleton.java | 15 +++ src/Typecheck.hs | 95 +++++++++++-------- 3 files changed, 82 insertions(+), 41 deletions(-) create mode 100644 Test/JavaSources/TestConstructorOverload.java create mode 100644 Test/JavaSources/TestSingleton.java diff --git a/Test/JavaSources/TestConstructorOverload.java b/Test/JavaSources/TestConstructorOverload.java new file mode 100644 index 0000000..44522bb --- /dev/null +++ b/Test/JavaSources/TestConstructorOverload.java @@ -0,0 +1,13 @@ +public class TestConstructorOverload { + + TestConstructorOverload() { + } + + TestConstructorOverload(int a) { + } + + public void test() { + int a = 3; + TestConstructorOverload test = new TestConstructorOverload(a); + } +} diff --git a/Test/JavaSources/TestSingleton.java b/Test/JavaSources/TestSingleton.java new file mode 100644 index 0000000..d764ea5 --- /dev/null +++ b/Test/JavaSources/TestSingleton.java @@ -0,0 +1,15 @@ +public class TestSingleton { + + TestSingleton instance; + + TestSingleton() { + } + + public TestSingleton getInstance() { + if (instance == null) { + instance = new TestSingleton(); + } + return instance; + } + +} diff --git a/src/Typecheck.hs b/src/Typecheck.hs index 9b66b43..889ba6b 100644 --- a/src/Typecheck.hs +++ b/src/Typecheck.hs @@ -3,18 +3,35 @@ import Data.List (find) import Data.Maybe import Ast + typeCheckCompilationUnit :: CompilationUnit -> CompilationUnit typeCheckCompilationUnit classes = map (`typeCheckClass` classes) classes typeCheckClass :: Class -> [Class] -> Class -typeCheckClass (Class className methods fields) classes = +typeCheckClass (Class className constructors methods fields) classes = let -- Fields and methods dont need to be added to the symtab because they are looked upon automatically under "this" -- if its not a declared local variable. Also shadowing wouldnt be possible then. initalSymTab = [("this", className)] + checkedConstructors = map (\constructor -> typeCheckConstructorDeclaration constructor initalSymTab classes) constructors checkedMethods = map (\method -> typeCheckMethodDeclaration method initalSymTab classes) methods checkedFields = map (\field -> typeCheckVariableDeclaration field initalSymTab classes) fields - in Class className checkedMethods checkedFields + in Class className checkedConstructors checkedMethods checkedFields + +typeCheckConstructorDeclaration :: ConstructorDeclaration -> [(Identifier, DataType)] -> [Class] -> ConstructorDeclaration +typeCheckConstructorDeclaration (ConstructorDeclaration name params body) symtab classes = + let + constructorParams = [(identifier, dataType) | ParameterDeclaration dataType identifier <- params] + initialSymtab = symtab ++ constructorParams + className = fromMaybe (error "Constructor Declaration: 'this' not found in symtab") (lookup "this" symtab) + checkedBody = typeCheckStatement body initialSymtab classes + bodyType = getTypeFromStmt checkedBody + in if name == className + then if bodyType == "void" + then ConstructorDeclaration name params checkedBody + else error $ "Constructor Declaration: Return type mismatch in constructor " ++ name ++ ": expected void, found " ++ bodyType + else error $ "Constructor Declaration: Constructor name " ++ name ++ " does not match class name " ++ className + typeCheckMethodDeclaration :: MethodDeclaration -> [(Identifier, DataType)] -> [Class] -> MethodDeclaration typeCheckMethodDeclaration (MethodDeclaration retType name params body) symtab classes = @@ -63,9 +80,9 @@ typeCheckExpression (Reference id) symtab classes = Nothing -> case lookup "this" symtab of Just className -> - let classDetails = find (\(Class name _ _) -> name == className) classes + let classDetails = find (\(Class name _ _ _) -> name == className) classes in case classDetails of - Just (Class _ _ fields) -> + Just (Class _ _ _ fields) -> let fieldTypes = [dt | VariableDeclaration dt fieldId _ <- fields, fieldId == id] -- this case only happens when its a field of its own class so the implicit this will be converted to explicit this in case fieldTypes of @@ -139,40 +156,36 @@ typeCheckStatementExpression (Assignment ref expr) symtab classes = typeCheckStatementExpression (ConstructorCall className args) symtab classes = - case find (\(Class name _ _) -> name == className) classes of + case find (\(Class name _ _ _) -> name == className) classes of Nothing -> error $ "Class '" ++ className ++ "' not found." - Just (Class _ methods _) -> - -- Find constructor matching the class name with void return type - case find (\(MethodDeclaration _ name params _) -> name == "") methods of - -- If no constructor is found, assume standard constructor with no parameters - Nothing -> - if null args then - TypedStatementExpression className (ConstructorCall className args) - else - error $ "No valid constructor found for class '" ++ className ++ "', but arguments were provided." - Just (MethodDeclaration _ _ params _) -> - let args' = zipWith - (\arg (ParameterDeclaration paramType _) -> - let argTyped = typeCheckExpression arg symtab classes - in if getTypeFromExpr argTyped == "null" && isObjectType paramType - then TypedExpression paramType NullLiteral - else argTyped - ) args params - expectedTypes = [dataType | ParameterDeclaration dataType _ <- params] - argTypes = map getTypeFromExpr args' - typeMatches = zipWith - (\expType argType -> (expType == argType || (argType == "null" && isObjectType expType), expType, argType)) - expectedTypes argTypes - mismatches = filter (not . fst3) typeMatches - fst3 (a, _, _) = a - in - if null mismatches && length args == length params then - TypedStatementExpression className (ConstructorCall className args') - else if not (null mismatches) then - error $ unlines $ ("Type mismatch in constructor arguments for class '" ++ className ++ "':") - : [ "Expected: " ++ expType ++ ", Found: " ++ argType | (_, expType, argType) <- mismatches ] - else - error $ "Incorrect number of arguments for constructor of class '" ++ className ++ "'. Expected " ++ show (length expectedTypes) ++ ", found " ++ show (length args) ++ "." + Just (Class _ constructors _ _) -> + let + matchParams (ParameterDeclaration paramType _) arg = + let argTyped = typeCheckExpression arg symtab classes + argType = getTypeFromExpr argTyped + in if argType == "null" && isObjectType paramType + then Just (TypedExpression paramType NullLiteral) + else if argType == paramType + then Just argTyped + else Nothing + + matchConstructor (ConstructorDeclaration name params _) = + let matchedArgs = sequence $ zipWith matchParams params args + in fmap (\checkedArgs -> (params, checkedArgs)) matchedArgs + + validConstructors = filter (\(params, _) -> length params == length args) $ mapMaybe matchConstructor constructors + + expectedSignatures = [ map (\(ParameterDeclaration t _) -> t) params | ConstructorDeclaration _ params _ <- constructors ] + actualSignature = map (\arg -> getTypeFromExpr (typeCheckExpression arg symtab classes)) args + mismatchDetails = "Constructor not found for class '" ++ className ++ "' with given arguments.\n" ++ + "Expected signatures:\n" ++ show expectedSignatures ++ + "\nActual arguments:" ++ show actualSignature + + in case validConstructors of + [(_, checkedArgs)] -> + TypedStatementExpression className (ConstructorCall className checkedArgs) + [] -> error mismatchDetails + _ -> error $ "Multiple matching constructors found for class '" ++ className ++ "' with given arguments." typeCheckStatementExpression (MethodCall expr methodName args) symtab classes = let objExprTyped = typeCheckExpression expr symtab classes @@ -181,7 +194,7 @@ typeCheckStatementExpression (MethodCall expr methodName args) symtab classes = case find (\(Class className _ _ _) -> className == objType) classes of Just (Class _ _ methods _) -> let matchParams (ParameterDeclaration paramType _) arg = - let argTyped = typeCheckExpression arg symtab classes + let argTyped = typeCheckExpression arg symtab classes argType = getTypeFromExpr argTyped in if argType == "null" && isObjectType paramType then Just (TypedExpression paramType NullLiteral) @@ -359,7 +372,7 @@ isSubtype subType superType classes | otherwise = False isUserDefinedClass :: DataType -> [Class] -> Bool -isUserDefinedClass dt classes = dt `elem` map (\(Class name _ _) -> name) classes +isUserDefinedClass dt classes = dt `elem` map (\(Class name _ _ _) -> name) classes isObjectType :: DataType -> Bool isObjectType dt = dt /= "int" && dt /= "boolean" && dt /= "char" @@ -431,8 +444,8 @@ resolveNameResolution :: Expression -> Expression -> [(Identifier, DataType)] -> resolveNameResolution expr1' (Reference ident2) symtab classes = case getTypeFromExpr expr1' of objType -> - case find (\(Class className _ _) -> className == objType) classes of - Just (Class _ _ fields) -> + case find (\(Class className _ _ _) -> className == objType) classes of + Just (Class _ _ _ fields) -> let fieldTypes = [dt | VariableDeclaration dt id _ <- fields, id == ident2] in case fieldTypes of [resolvedType] -> TypedExpression resolvedType (BinaryOperation NameResolution expr1' (TypedExpression resolvedType (FieldVariable ident2))) From d0bf34d3311f2daf7c01fa6c7b8596e1f0b4804c Mon Sep 17 00:00:00 2001 From: MisterChaos96 Date: Thu, 27 Jun 2024 19:02:19 +0200 Subject: [PATCH 11/16] update documentation --- doc/typecheck.md | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/doc/typecheck.md b/doc/typecheck.md index 0d5b9a2..33a071b 100644 --- a/doc/typecheck.md +++ b/doc/typecheck.md @@ -1,10 +1,11 @@ -# Typcheck (Fabian Noll) + +# Typecheck (Fabian Noll) ## Überblick und Struktur Die Typprüfung beginnt mit der Funktion `typeCheckCompilationUnit`, die eine Kompilationseinheit als Eingabe erhält. Diese Kompilationseinheit besteht aus einer Liste von Klassen. Jede Klasse wird einzeln durch die Funktion `typeCheckClass` überprüft. Innerhalb dieser Funktion wird eine Symboltabelle erstellt, die den Namen der Klasse als Typ und `this` als Identifier enthält. Diese Symboltabelle wird verwendet, um Typinformationen nach dem Lokalitätsprinzip während der Typprüfung zugänglich zu machen und zu verwalten. -Die Typprüfung einer Klasse umfasst die Überprüfung aller Methoden und Felder. Die Methode `typeCheckMethodDeclaration` ist für die Typprüfung einzelner Methodendeklarationen verantwortlich. Sie überprüft den Rückgabetyp der Methode, die Parameter und den Methodenrumpf. Der Methodenrumpf wird durch rekursive Aufrufe von `typeCheckStatement` überprüft, die verschiedene Arten von Anweisungen wie If-Anweisungen, While-Schleifen, Rückgabeanweisungen und Blockanweisungen behandelt. +Die Typprüfung einer Klasse umfasst die Überprüfung aller Konstruktoren, Methoden und Felder. Die Methode `typeCheckConstructorDeclaration` ist für die Typprüfung einzelner Konstruktordeklarationen verantwortlich, während `typeCheckMethodDeclaration` für die Typprüfung einzelner Methodendeklarationen zuständig ist. Beide Funktionen überprüfen die Parameter und den Rumpf der jeweiligen Konstruktoren bzw. Methoden. Der Rumpf wird durch rekursive Aufrufe von `typeCheckStatement` überprüft, die verschiedene Arten von Anweisungen wie If-Anweisungen, While-Schleifen, Rückgabeanweisungen und Blockanweisungen behandelt. ## Ablauf und Symboltabellen @@ -15,6 +16,9 @@ Eine zentrale Komponente des Typecheckers ist die Symboltabelle (symtab), die In - **Klassenkontext**: Beim Typcheck einer Klasse wird eine initiale Symboltabelle erstellt, die die `this`-Referenz enthält. Dies geschieht in der Funktion `typeCheckClass`. +- **Konstruktorkontext**: + Innerhalb eines Konstruktors wird die Symboltabelle um die Parameter des Konstruktors erweitert. Dies geschieht in `typeCheckConstructorDeclaration`. Der Rückgabetyp eines Konstruktors ist implizit `void`, was überprüft wird, um sicherzustellen, dass kein Wert zurückgegeben wird. + - **Methodenkontext**: Innerhalb einer Methode wird die Symboltabelle um die Parameter der Methode erweitert sowie den Rückgabetyp der Methode, um die einzelnen Returns dagegen zu prüfen. Dies geschieht in `typeCheckMethodDeclaration`. @@ -30,7 +34,7 @@ Bei der Typprüfung von Referenzen (`typeCheckExpression` für Reference) wird z Ein zentraler Aspekt des Typecheckers ist die Fehlerbehandlung. Bei Typinkonsistenzen oder ungültigen Operationen werden aussagekräftige Fehlermeldungen generiert. Beispiele für solche Fehlermeldungen sind: - **Typinkonsistenzen**: - Wenn der Rückgabetyp einer Methode nicht mit dem deklarierten Rückgabetyp übereinstimmt. Oder aber auch die Anzahl der Parameter nicht übereinstimmt. + Wenn der Rückgabetyp einer Methode nicht mit dem deklarierten Rückgabetyp übereinstimmt oder die Anzahl der Parameter nicht übereinstimmt. - **Ungültige Operationen**: Wenn eine arithmetische Operation auf inkompatiblen Typen durchgeführt wird. @@ -53,3 +57,23 @@ Die Typprüfung eines Blocks erfolgt in `typeCheckStatement` für Block. Jede An ### Rückgabeanweisungen Die Typprüfung einer Rückgabeanweisung (`typeCheckStatement` für Return) überprüft, ob der Rückgabewert der Anweisung mit dem deklarierten Rückgabetyp der Methode übereinstimmt. Dafür wurde zu Beginn der Methodentypprüfung der Rückgabetyp der Methode in die Symboltabelle eingetragen. Wenn der Rückgabewert `null` ist, wird überprüft, ob der deklarierte Rückgabetyp ein Objekttyp ist. Dies stellt sicher, dass Methoden immer den korrekten Typ zurückgeben. Generell wird bei der Prüfung nach dem UpperBound geschaut und ebenfalls wird nachgeschaut, ob, wenn der Rückgabetyp `Object` ist, der Return-Wert auch eine tatsächlich existierende Klasse ist, indem in die Klassentabelle geschaut wird. + +### Konstruktorüberladung und -prüfung + +Die Typprüfung unterstützt Konstruktorüberladung. Bei der Typprüfung von Konstruktoraufrufen (`typeCheckStatementExpression` für `ConstructorCall`) wird überprüft, ob es mehrere Konstruktoren mit derselben Anzahl von Parametern gibt. Falls mehrere passende Konstruktoren gefunden werden, wird ein Fehler gemeldet. + +- **Parameterabgleich**: + Die Parameter eines Konstruktors werden gegen die Argumente des Aufrufs abgeglichen. Dies umfasst die Prüfung der Typen und, falls es sich um `null` handelt, die Überprüfung, ob der Parameter ein Objekttyp ist. + +- **Fehlerbehandlung**: + Wenn kein passender Konstruktor gefunden wird, wird eine detaillierte Fehlermeldung generiert, die die erwarteten Signaturen und die tatsächlichen Argumenttypen anzeigt. Wenn mehrere passende Konstruktoren gefunden werden, wird ebenfalls ein Fehler gemeldet. + +### Methodenüberladung und -prüfung + +Die Typprüfung unterstützt auch Methodenüberladung. Bei der Typprüfung von Methodenaufrufen (`typeCheckStatementExpression` für `MethodCall`) wird überprüft, ob es mehrere Methoden mit demselben Namen, aber unterschiedlichen Parametertypen gibt. + +- **Parameterabgleich**: + Die Parameter einer Methode werden gegen die Argumente des Aufrufs abgeglichen. Dies umfasst die Prüfung der Typen und, falls es sich um `null` handelt, die Überprüfung, ob der Parameter ein Objekttyp ist. + +- **Fehlerbehandlung**: + Wenn keine passende Methode gefunden wird, wird eine detaillierte Fehlermeldung generiert, die die erwarteten Signaturen und die tatsächlichen Argumenttypen anzeigt. Wenn mehrere passende Methoden gefunden werden, wird ebenfalls ein Fehler gemeldet. From b5efc76c171a6181095661aed24c3820d6a60b17 Mon Sep 17 00:00:00 2001 From: MisterChaos96 Date: Fri, 28 Jun 2024 08:44:04 +0200 Subject: [PATCH 12/16] add default constructor if none are present --- src/Typecheck.hs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Typecheck.hs b/src/Typecheck.hs index 889ba6b..fe0588f 100644 --- a/src/Typecheck.hs +++ b/src/Typecheck.hs @@ -10,10 +10,14 @@ typeCheckCompilationUnit classes = map (`typeCheckClass` classes) classes typeCheckClass :: Class -> [Class] -> Class typeCheckClass (Class className constructors methods fields) classes = let + -- Add a default constructor if none are present + defaultConstructor = ConstructorDeclaration className [] (Block []) + constructorsWithDefault = if null constructors then [defaultConstructor] else constructors + -- Fields and methods dont need to be added to the symtab because they are looked upon automatically under "this" -- if its not a declared local variable. Also shadowing wouldnt be possible then. initalSymTab = [("this", className)] - checkedConstructors = map (\constructor -> typeCheckConstructorDeclaration constructor initalSymTab classes) constructors + checkedConstructors = map (\constructor -> typeCheckConstructorDeclaration constructor initalSymTab classes) constructorsWithDefault checkedMethods = map (\method -> typeCheckMethodDeclaration method initalSymTab classes) methods checkedFields = map (\field -> typeCheckVariableDeclaration field initalSymTab classes) fields in Class className checkedConstructors checkedMethods checkedFields From 9657731a93f9071e57c428d52ed0e7ac4b36880a Mon Sep 17 00:00:00 2001 From: MisterChaos96 Date: Fri, 28 Jun 2024 08:44:22 +0200 Subject: [PATCH 13/16] remove example.hs because its deprecated --- src/Example.hs | 267 ------------------------------------------------- 1 file changed, 267 deletions(-) delete mode 100644 src/Example.hs diff --git a/src/Example.hs b/src/Example.hs deleted file mode 100644 index 03ff209..0000000 --- a/src/Example.hs +++ /dev/null @@ -1,267 +0,0 @@ -module Example where - -import Ast -import Typecheck -import Control.Exception (catch, evaluate, SomeException, displayException) -import Control.Exception.Base -import System.IO (stderr, hPutStrLn) -import Data.Maybe -import Data.List - -green, red, yellow, blue, magenta, cyan, white :: String -> String -green str = "\x1b[32m" ++ str ++ "\x1b[0m" -red str = "\x1b[31m" ++ str ++ "\x1b[0m" -yellow str = "\x1b[33m" ++ str ++ "\x1b[0m" -blue str = "\x1b[34m" ++ str ++ "\x1b[0m" -magenta str = "\x1b[35m" ++ str ++ "\x1b[0m" -cyan str = "\x1b[36m" ++ str ++ "\x1b[0m" -white str = "\x1b[37m" ++ str ++ "\x1b[0m" - -printSuccess :: String -> IO () -printSuccess msg = putStrLn $ green "Success:" ++ white msg - -handleError :: SomeException -> IO () -handleError e = hPutStrLn stderr $ red ("Error: " ++ displayException e) - -printResult :: Show a => String -> a -> IO () -printResult title result = do - putStrLn $ green title - print result - -sampleClasses :: [Class] -sampleClasses = [ - Class "Person" [ - MethodDeclaration "void" "setAge" [ParameterDeclaration "int" "newAge"] - (Block [ - LocalVariableDeclaration (VariableDeclaration "int" "age" (Just (Reference "newAge"))) - ]), - MethodDeclaration "int" "getAge" [] (Return (Just (Reference "age"))), - MethodDeclaration "Person" "Person" [ParameterDeclaration "int" "initialAge"] (Block []) - ] [ - VariableDeclaration "int" "age" (Just (IntegerLiteral 25)) - ] - ] - -initialSymtab :: [(DataType, Identifier)] -initialSymtab = [] - -exampleExpression :: Expression -exampleExpression = BinaryOperation NameResolution (Reference "bob") (Reference "age") - -exampleAssignment :: Expression -exampleAssignment = StatementExpressionExpression (Assignment (Reference "a") (IntegerLiteral 30)) - -exampleMethodCall :: Statement -exampleMethodCall = StatementExpressionStatement (MethodCall (Reference "this") "setAge" [IntegerLiteral 30]) - -exampleConstructorCall :: Statement -exampleConstructorCall = LocalVariableDeclaration (VariableDeclaration "Person" "bob" (Just (StatementExpressionExpression (ConstructorCall "Person" [IntegerLiteral 30])))) - -exampleNameResolution :: Expression -exampleNameResolution = BinaryOperation NameResolution (Reference "bob2") (Reference "age") - -exampleBlockResolution :: Statement -exampleBlockResolution = Block [ - LocalVariableDeclaration (VariableDeclaration "Person" "bob" (Just (StatementExpressionExpression (ConstructorCall "Person" [IntegerLiteral 30])))), - LocalVariableDeclaration (VariableDeclaration "int" "age" (Just (StatementExpressionExpression (MethodCall (Reference "bob") "getAge" [])))), - StatementExpressionStatement (MethodCall (Reference "bob") "setAge" [IntegerLiteral 30]) - ] - -exampleBlockResolutionFail :: Statement -exampleBlockResolutionFail = Block [ - LocalVariableDeclaration (VariableDeclaration "Person" "bob" (Just (StatementExpressionExpression (ConstructorCall "Person" [IntegerLiteral 30])))), - LocalVariableDeclaration (VariableDeclaration "bool" "age" (Just (StatementExpressionExpression (MethodCall (Reference "bob") "getAge" [])))), - StatementExpressionStatement (MethodCall (Reference "bob") "setAge" [IntegerLiteral 30]) - ] - -exampleMethodCallAndAssignment :: Statement -exampleMethodCallAndAssignment = Block [ - LocalVariableDeclaration (VariableDeclaration "Person" "bob" (Just (StatementExpressionExpression (ConstructorCall "Person" [IntegerLiteral 30])))), - LocalVariableDeclaration (VariableDeclaration "int" "age" (Just (StatementExpressionExpression (MethodCall (Reference "bob") "getAge" [])))), - StatementExpressionStatement (MethodCall (Reference "bob") "setAge" [IntegerLiteral 30]), - LocalVariableDeclaration (VariableDeclaration "int" "a" Nothing), - StatementExpressionStatement (Assignment (Reference "a") (Reference "age")) - ] - - -exampleMethodCallAndAssignmentFail :: Statement -exampleMethodCallAndAssignmentFail = Block [ - LocalVariableDeclaration (VariableDeclaration "Person" "bob" (Just (StatementExpressionExpression (ConstructorCall "Person" [IntegerLiteral 30])))), - LocalVariableDeclaration (VariableDeclaration "int" "age" (Just (StatementExpressionExpression (MethodCall (Reference "bob") "getAge" [])))), - StatementExpressionStatement (MethodCall (Reference "bob") "setAge" [IntegerLiteral 30]), - StatementExpressionStatement (Assignment (Reference "a") (Reference "age")) - ] - -exampleNameResolutionAssignment :: Statement -exampleNameResolutionAssignment = Block [ - LocalVariableDeclaration (VariableDeclaration "Person" "bob" (Just (StatementExpressionExpression (ConstructorCall "Person" [IntegerLiteral 30])))), - StatementExpressionStatement (Assignment (BinaryOperation NameResolution (Reference "bob") (Reference "age")) (IntegerLiteral 30)) - ] - -exampleCharIntOperation :: Expression -exampleCharIntOperation = BinaryOperation Addition (CharacterLiteral 'a') (IntegerLiteral 1) - -exampleNullDeclaration :: Statement -exampleNullDeclaration = LocalVariableDeclaration (VariableDeclaration "Person" "bob" (Just NullLiteral)) - -exampleNullDeclarationFail :: Statement -exampleNullDeclarationFail = LocalVariableDeclaration (VariableDeclaration "int" "a" (Just NullLiteral)) - -exampleNullAssignment :: Statement -exampleNullAssignment = StatementExpressionStatement (Assignment (Reference "a") NullLiteral) - -exampleIncrement :: Statement -exampleIncrement = StatementExpressionStatement (PostIncrement (Reference "a")) - -testClasses :: [Class] -testClasses = [ - Class "Person" [ - MethodDeclaration "Person" "Person" [ParameterDeclaration "int" "initialAge"] - (Block [ - Return (Just (Reference "this")) - ]), - MethodDeclaration "void" "setAge" [ParameterDeclaration "int" "newAge"] - (Block [ - LocalVariableDeclaration (VariableDeclaration "int" "age" (Just (Reference "newAge"))) - ]), - MethodDeclaration "int" "getAge" [] - (Return (Just (Reference "age"))) - ] [ - VariableDeclaration "int" "age" Nothing -- initially unassigned - ], - Class "Main" [ - MethodDeclaration "int" "main" [] - (Block [ - LocalVariableDeclaration (VariableDeclaration "Person" "bob" (Just (StatementExpressionExpression (ConstructorCall "Person" [IntegerLiteral 25])))), - StatementExpressionStatement (MethodCall (Reference "bob") "setAge" [IntegerLiteral 30]), - LocalVariableDeclaration (VariableDeclaration "int" "bobAge" (Just (StatementExpressionExpression (MethodCall (Reference "bob2") "getAge" [])))), - Return (Just (Reference "bobAge")) - ]) - ] [ - VariableDeclaration "Person" "bob2" Nothing - ] - ] - -runTypeCheck :: IO () -runTypeCheck = do - catch (do - print "=====================================================================================" - evaluatedExpression <- evaluate (typeCheckExpression exampleExpression [("bob", "Person")] sampleClasses) - printSuccess "Type checking of expression completed successfully" - printResult "Result Expression:" evaluatedExpression - ) handleError - - catch (do - print "=====================================================================================" - evaluatedAssignment <- evaluate (typeCheckExpression exampleAssignment [("a", "int")] sampleClasses) - printSuccess "Type checking of assignment completed successfully" - printResult "Result Assignment:" evaluatedAssignment - ) handleError - - catch (do - print "=====================================================================================" - evaluatedMethodCall <- evaluate (typeCheckStatement exampleMethodCall [("this", "Person"), ("setAge", "Person"), ("getAge", "Person")] sampleClasses) - printSuccess "Type checking of method call this completed successfully" - printResult "Result MethodCall:" evaluatedMethodCall - ) handleError - - catch (do - print "=====================================================================================" - evaluatedConstructorCall <- evaluate (typeCheckStatement exampleConstructorCall [] sampleClasses) - printSuccess "Type checking of constructor call completed successfully" - printResult "Result Constructor Call:" evaluatedConstructorCall - ) handleError - - catch (do - print "=====================================================================================" - evaluatedNameResolution <- evaluate (typeCheckExpression exampleNameResolution [("this", "Main")] testClasses) - printSuccess "Type checking of name resolution completed successfully" - printResult "Result Name Resolution:" evaluatedNameResolution - ) handleError - - catch (do - print "=====================================================================================" - evaluatedBlockResolution <- evaluate (typeCheckStatement exampleBlockResolution [] sampleClasses) - printSuccess "Type checking of block resolution completed successfully" - printResult "Result Block Resolution:" evaluatedBlockResolution - ) handleError - - catch (do - print "=====================================================================================" - evaluatedBlockResolutionFail <- evaluate (typeCheckStatement exampleBlockResolutionFail [] sampleClasses) - printSuccess "Type checking of block resolution failed" - printResult "Result Block Resolution:" evaluatedBlockResolutionFail - ) handleError - - catch (do - print "=====================================================================================" - evaluatedMethodCallAndAssignment <- evaluate (typeCheckStatement exampleMethodCallAndAssignment [] sampleClasses) - printSuccess "Type checking of method call and assignment completed successfully" - printResult "Result Method Call and Assignment:" evaluatedMethodCallAndAssignment - ) handleError - - catch (do - print "=====================================================================================" - evaluatedMethodCallAndAssignmentFail <- evaluate (typeCheckStatement exampleMethodCallAndAssignmentFail [] sampleClasses) - printSuccess "Type checking of method call and assignment failed" - printResult "Result Method Call and Assignment:" evaluatedMethodCallAndAssignmentFail - ) handleError - - catch (do - print "=====================================================================================" - let mainClass = fromJust $ find (\(Class className _ _) -> className == "Main") testClasses - case mainClass of - Class _ [mainMethod] _ -> do - let result = typeCheckMethodDeclaration mainMethod [("this", "Main")] testClasses - printSuccess "Full program type checking completed successfully." - printResult "Main method result:" result - ) handleError - - catch (do - print "=====================================================================================" - let typedProgram = typeCheckCompilationUnit testClasses - printSuccess "Type checking of Program completed successfully" - printResult "Typed Program:" typedProgram - ) handleError - - catch (do - print "=====================================================================================" - typedAssignment <- evaluate (typeCheckStatement exampleNameResolutionAssignment [] sampleClasses) - printSuccess "Type checking of name resolution assignment completed successfully" - printResult "Result Name Resolution Assignment:" typedAssignment - ) handleError - - catch (do - print "=====================================================================================" - evaluatedCharIntOperation <- evaluate (typeCheckExpression exampleCharIntOperation [] sampleClasses) - printSuccess "Type checking of char int operation completed successfully" - printResult "Result Char Int Operation:" evaluatedCharIntOperation - ) handleError - - catch (do - print "=====================================================================================" - evaluatedNullDeclaration <- evaluate (typeCheckStatement exampleNullDeclaration [] sampleClasses) - printSuccess "Type checking of null declaration completed successfully" - printResult "Result Null Declaration:" evaluatedNullDeclaration - ) handleError - - catch (do - print "=====================================================================================" - evaluatedNullDeclarationFail <- evaluate (typeCheckStatement exampleNullDeclarationFail [] sampleClasses) - printSuccess "Type checking of null declaration failed" - printResult "Result Null Declaration:" evaluatedNullDeclarationFail - ) handleError - - catch (do - print "=====================================================================================" - evaluatedNullAssignment <- evaluate (typeCheckStatement exampleNullAssignment [("a", "Person")] sampleClasses) - printSuccess "Type checking of null assignment completed successfully" - printResult "Result Null Assignment:" evaluatedNullAssignment - ) handleError - - catch (do - print "=====================================================================================" - evaluatedIncrement <- evaluate (typeCheckStatement exampleIncrement [("a", "int")] sampleClasses) - printSuccess "Type checking of increment completed successfully" - printResult "Result Increment:" evaluatedIncrement - ) handleError From c7e72dbde381746b11c1df79ae4fbd17b791dede Mon Sep 17 00:00:00 2001 From: MisterChaos96 Date: Fri, 28 Jun 2024 08:51:35 +0200 Subject: [PATCH 14/16] move defaultconstructor injection to the beginning befor typechecking the classes --- src/Typecheck.hs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/Typecheck.hs b/src/Typecheck.hs index fe0588f..72a7747 100644 --- a/src/Typecheck.hs +++ b/src/Typecheck.hs @@ -5,19 +5,28 @@ import Ast typeCheckCompilationUnit :: CompilationUnit -> CompilationUnit -typeCheckCompilationUnit classes = map (`typeCheckClass` classes) classes +typeCheckCompilationUnit classes = + let + -- Helper function to add a default constructor if none are present + ensureDefaultConstructor :: Class -> Class + ensureDefaultConstructor (Class className constructors methods fields) = + let + defaultConstructor = ConstructorDeclaration className [] (Block []) + constructorsWithDefault = if null constructors then [defaultConstructor] else constructors + in Class className constructorsWithDefault methods fields + + -- Inject default constructors into all classes + classesWithDefaultConstructors = map ensureDefaultConstructor classes + + in map (`typeCheckClass` classesWithDefaultConstructors) classesWithDefaultConstructors typeCheckClass :: Class -> [Class] -> Class typeCheckClass (Class className constructors methods fields) classes = let - -- Add a default constructor if none are present - defaultConstructor = ConstructorDeclaration className [] (Block []) - constructorsWithDefault = if null constructors then [defaultConstructor] else constructors - -- Fields and methods dont need to be added to the symtab because they are looked upon automatically under "this" -- if its not a declared local variable. Also shadowing wouldnt be possible then. initalSymTab = [("this", className)] - checkedConstructors = map (\constructor -> typeCheckConstructorDeclaration constructor initalSymTab classes) constructorsWithDefault + checkedConstructors = map (\constructor -> typeCheckConstructorDeclaration constructor initalSymTab classes) constructors checkedMethods = map (\method -> typeCheckMethodDeclaration method initalSymTab classes) methods checkedFields = map (\field -> typeCheckVariableDeclaration field initalSymTab classes) fields in Class className checkedConstructors checkedMethods checkedFields From 0e1f31080e036b1b62bff4b604e8288b75094af3 Mon Sep 17 00:00:00 2001 From: Matthias Raba Date: Fri, 28 Jun 2024 09:20:03 +0200 Subject: [PATCH 15/16] fixed method/constructor overloading in bytecode generator --- Test/JavaSources/Main.java | 9 +++++- Test/JavaSources/TestConstructorOverload.java | 9 +++--- Test/JavaSources/TestMethodOverload.java | 12 +++---- project.cabal | 1 - src/ByteCode/Builder.hs | 28 ++++++++++------- src/ByteCode/Util.hs | 31 +++++++++---------- 6 files changed, 48 insertions(+), 42 deletions(-) diff --git a/Test/JavaSources/Main.java b/Test/JavaSources/Main.java index e3b7b76..acf0eed 100644 --- a/Test/JavaSources/Main.java +++ b/Test/JavaSources/Main.java @@ -16,7 +16,8 @@ public class Main { TestRecursion recursion = new TestRecursion(10); TestMalicious malicious = new TestMalicious(); TestLoop loop = new TestLoop(); - + TestMethodOverload overload = new TestMethodOverload(); + // constructing a basic class works assert empty != null; // initializers (and default initializers to 0/null) work @@ -34,6 +35,12 @@ public class Main { // self-referencing methods work. assert recursion.fibonacci(15) == 610; assert loop.factorial(5) == 120; + // methods with the same name but different parameters work + assert overload.MethodOverload() == 42; + assert overload.MethodOverload(15) == 42 + 15; + // constructor overloading works, too. + assert (new TestConstructorOverload()).a == 42; + assert (new TestConstructorOverload(12)).a == 12; // intentionally dodgy expressions work assert malicious.assignNegativeIncrement(42) == -42; assert malicious.tripleAddition(1, 2, 3) == 6; diff --git a/Test/JavaSources/TestConstructorOverload.java b/Test/JavaSources/TestConstructorOverload.java index 44522bb..ee6422a 100644 --- a/Test/JavaSources/TestConstructorOverload.java +++ b/Test/JavaSources/TestConstructorOverload.java @@ -1,13 +1,12 @@ public class TestConstructorOverload { + public int a = 42; + TestConstructorOverload() { + // nothing here, so a will assume the default value 42. } TestConstructorOverload(int a) { - } - - public void test() { - int a = 3; - TestConstructorOverload test = new TestConstructorOverload(a); + this.a = a; } } diff --git a/Test/JavaSources/TestMethodOverload.java b/Test/JavaSources/TestMethodOverload.java index 6fe9877..5a7d976 100644 --- a/Test/JavaSources/TestMethodOverload.java +++ b/Test/JavaSources/TestMethodOverload.java @@ -1,14 +1,10 @@ public class TestMethodOverload { - public void MethodOverload() { + public int MethodOverload() { + return 42; } - public void MethodOverload(int a) { + public int MethodOverload(int a) { + return 42 + a; } - - public void test() { - int a = 3; - MethodOverload(a); - } - } diff --git a/project.cabal b/project.cabal index baec851..abe3e09 100644 --- a/project.cabal +++ b/project.cabal @@ -18,7 +18,6 @@ executable compiler other-modules: Parser.Lexer, Parser.JavaParser, Ast, - Example, Typecheck, ByteCode.Util, ByteCode.ByteUtil, diff --git a/src/ByteCode/Builder.hs b/src/ByteCode/Builder.hs index b717247..9e2e804 100644 --- a/src/ByteCode/Builder.hs +++ b/src/ByteCode/Builder.hs @@ -56,10 +56,13 @@ methodBuilder (MethodDeclaration returntype name parameters statement) input = l methods = methods input ++ [method] } +constructorBuilder :: ClassFileBuilder ConstructorDeclaration +constructorBuilder (ConstructorDeclaration name parameters statement) = methodBuilder (MethodDeclaration "void" "" parameters statement) + methodAssembler :: ClassFileBuilder MethodDeclaration methodAssembler (MethodDeclaration returntype name parameters statement) input = let - methodConstantIndex = findMethodIndex input name + methodConstantIndex = findMethodIndex input (MethodDeclaration returntype name parameters statement) in case methodConstantIndex of Nothing -> error ("Cannot find method entry in method pool for method: " ++ name) Just index -> let @@ -84,9 +87,12 @@ methodAssembler (MethodDeclaration returntype name parameters statement) input = methods = pre ++ (assembledMethod : post) } +constructorAssembler :: ClassFileBuilder ConstructorDeclaration +constructorAssembler (ConstructorDeclaration name parameters statement) = methodAssembler (MethodDeclaration "void" "" parameters statement) + classBuilder :: ClassFileBuilder Class -classBuilder (Class name methods fields) _ = let +classBuilder (Class name constructors methods fields) _ = let baseConstants = [ ClassInfo 4, MethodRefInfo 1 3, @@ -108,15 +114,15 @@ classBuilder (Class name methods fields) _ = let attributes = [] } - -- if a class has no constructor, inject an empty one. - methodsWithInjectedConstructor = injectDefaultConstructor methods -- for every constructor, prepend all initialization assignments for fields. - methodsWithInjectedInitializers = injectFieldInitializers name fields methodsWithInjectedConstructor + constructorsWithInitializers = injectFieldInitializers name fields constructors - -- add fields, then method bodies to the classfile. After all referable names are known, - -- assemble the methods into bytecode. - classFileWithFields = foldr fieldBuilder nakedClassFile fields - classFileWithMethods = foldr methodBuilder classFileWithFields methodsWithInjectedInitializers - classFileWithAssembledMethods = foldr methodAssembler classFileWithMethods methodsWithInjectedInitializers + -- add fields, then method bodies, then constructor bodies to the classfile. After all referable names are known, + -- assemble the methods and constructors into bytecode. + fieldsAdded = foldr fieldBuilder nakedClassFile fields + methodsAdded = foldr methodBuilder fieldsAdded methods + constructorsAdded = foldr constructorBuilder methodsAdded constructorsWithInitializers + methodsAssembled = foldr methodAssembler constructorsAdded methods + constructorsAssembled = foldr constructorAssembler methodsAssembled constructorsWithInitializers in - classFileWithAssembledMethods \ No newline at end of file + constructorsAssembled \ No newline at end of file diff --git a/src/ByteCode/Util.hs b/src/ByteCode/Util.hs index 7f48b17..97e3721 100644 --- a/src/ByteCode/Util.hs +++ b/src/ByteCode/Util.hs @@ -1,3 +1,5 @@ +{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} +{-# HLINT ignore "Use lambda-case" #-} module ByteCode.Util where import Data.Int @@ -131,11 +133,12 @@ comparisonOffset anything_else = Nothing isComparisonOperation :: Operation -> Bool isComparisonOperation op = isJust (comparisonOffset op) -findMethodIndex :: ClassFile -> String -> Maybe Int -findMethodIndex classFile name = let +findMethodIndex :: ClassFile -> MethodDeclaration -> Maybe Int +findMethodIndex classFile (MethodDeclaration rtype name params stmt) = let constants = constantPool classFile + descriptor = methodDescriptor (MethodDeclaration rtype name params stmt) in - findIndex (\method -> memberInfoIsMethod constants method && memberInfoName constants method == name) (methods classFile) + findIndex (\method -> memberInfoIsMethod constants method && memberInfoName constants method == name && memberInfoDescriptor constants method == descriptor) (methods classFile) findClassIndex :: [ConstantInfo] -> String -> Maybe Int findClassIndex constants name = let @@ -211,16 +214,11 @@ findMemberIndex constants (cname, fname, ftype) = let allMembers = getKnownMembers constants desiredMember = find (\(index, (c, f, ft)) -> (c, f, ft) == (cname, fname, ftype)) allMembers in - fmap (\(index, _) -> index) desiredMember + fmap fst desiredMember -injectDefaultConstructor :: [MethodDeclaration] -> [MethodDeclaration] -injectDefaultConstructor pre - | any (\(MethodDeclaration _ name _ _) -> name == "") pre = pre - | otherwise = pre ++ [MethodDeclaration "void" "" [] (TypedStatement "void" (Block []))] - -injectFieldInitializers :: String -> [VariableDeclaration] -> [MethodDeclaration] -> [MethodDeclaration] -injectFieldInitializers classname vars pre = let - initializers = mapMaybe (\(variable) -> case variable of +injectFieldInitializers :: String -> [VariableDeclaration] -> [ConstructorDeclaration] -> [ConstructorDeclaration] +injectFieldInitializers classname vars constructors = let + initializers = mapMaybe (\variable -> case variable of VariableDeclaration dtype name (Just initializer) -> Just ( TypedStatement dtype ( StatementExpressionStatement ( @@ -235,10 +233,11 @@ injectFieldInitializers classname vars pre = let otherwise -> Nothing ) vars in - map (\method -> case method of - MethodDeclaration "void" "" params (TypedStatement "void" (Block statements)) -> MethodDeclaration "void" "" params (TypedStatement "void" (Block (initializers ++ statements))) - _ -> method - ) pre + map (\con -> let + ConstructorDeclaration classname params (TypedStatement "void" (Block statements)) = con + in + ConstructorDeclaration classname params (TypedStatement "void" (Block (initializers ++ statements))) + ) constructors -- effect of one instruction/operation on the stack operationStackCost :: [ConstantInfo] -> Operation -> Int From fa285a4af2c4d08ffe9c4ae2083bd0316ffa996c Mon Sep 17 00:00:00 2001 From: Matthias Raba Date: Fri, 28 Jun 2024 11:01:17 +0200 Subject: [PATCH 16/16] documentation goes LaTex --- .gitignore | 5 ++ doc/bytecode.md | 83 -------------------------------- doc/bytecode.tex | 107 ++++++++++++++++++++++++++++++++++++++++++ doc/documentation.pdf | Bin 0 -> 102245 bytes doc/documentation.tex | 34 ++++++++++++++ doc/features.md | 29 ------------ doc/features.tex | 34 ++++++++++++++ doc/generate.sh | 5 +- doc/parser.md | 19 -------- doc/parser.tex | 35 ++++++++++++++ doc/typecheck.md | 79 ------------------------------- doc/typecheck.tex | 105 +++++++++++++++++++++++++++++++++++++++++ doc/whodunit.tex | 19 ++++++++ 13 files changed, 340 insertions(+), 214 deletions(-) delete mode 100644 doc/bytecode.md create mode 100644 doc/bytecode.tex create mode 100644 doc/documentation.pdf create mode 100644 doc/documentation.tex delete mode 100644 doc/features.md create mode 100644 doc/features.tex mode change 100755 => 100644 doc/generate.sh delete mode 100644 doc/parser.md create mode 100644 doc/parser.tex delete mode 100644 doc/typecheck.md create mode 100644 doc/typecheck.tex create mode 100644 doc/whodunit.tex diff --git a/.gitignore b/.gitignore index 5dcf875..575a46e 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,8 @@ cabal.project.local cabal.project.local~ .HTF/ .ghc.environment.* +texput.log +doc/output/ +doc/*.aux +doc/*.log +doc/*.out \ No newline at end of file diff --git a/doc/bytecode.md b/doc/bytecode.md deleted file mode 100644 index 5624b72..0000000 --- a/doc/bytecode.md +++ /dev/null @@ -1,83 +0,0 @@ -# Bytecodegenerierung - -Die Bytecodegenerierung ist letztendlich eine zweistufige Transformation: - -`Getypter AST -> [ClassFile] -> [[Word8]]` - -Vom AST, der bereits den Typcheck durchlaufen hat, wird zunächst eine Abbildung in die einzelnen ClassFiles vorgenommen. Diese ClassFiles werden anschließend in deren Byte-Repräsentation serialisiert. - -## Codegenerierung - -Für die erste der beiden Transformationen (`Getypter AST -> [ClassFile]`) werden die Konzepte der "Builder" und "Assembler" eingeführt. Sie sind wie folgt definiert: -``` -type ClassFileBuilder a = a -> ClassFile -> ClassFile -type Assembler a = ([ConstantInfo], [Operation], [String]) -> a -> ([ConstantInfo], [Operation], [String]) -``` - -Die Idee hinter beiden ist, dass sie jeweils zwei Inputs haben, wobei der Rückgabewert immer den gleichen Typ hat wie einer der inputs. Das erlaubt es, eine Faltung durchzuführen. Ein ClassFileBuilder z.B bekommt als ersten Parameter den AST, und als zweiten Parameter (und Rückgabewert) eine ClassFile. Soll nun eine Klasse gebaut werden, wird der ClassFileBuilder mit dem AST und einer leeren ClassFile aufgerufen. Der Zustand dieser anfangs leeren ClassFile wird durch alle folgenden Builder/Assembler durchgeschleift, was es erlaubt, nach und nach kleinere Transformationen auf sie anzuwenden. - -Der Nutzer ruft beispielsweise die Funktion `classBuilder` auf. Diese wendet nach und nach folgende Transformationen an: - -``` - methodsWithInjectedConstructor = injectDefaultConstructor methods - methodsWithInjectedInitializers = injectFieldInitializers name fields methodsWithInjectedConstructor - - classFileWithFields = foldr fieldBuilder nakedClassFile fields - classFileWithMethods = foldr methodBuilder classFileWithFields methodsWithInjectedInitializers - classFileWithAssembledMethods = foldr methodAssembler classFileWithMethods methodsWithInjectedInitializers -``` - -Zuerst wird (falls notwendig) ein leerer Defaultkonstruktor in die Classfile eingefügt. Anschließend wird der AST so modifiziert, dass die Initialisierungen für alle Klassenfelder in allen Konstruktoren stattfinden. Nun beginnen die Faltungen: - - 1. Hinzufügen aller Klassenfelder - 2. Hinzufügen aller Methoden (nur Prototypen) - 3. Hinzufügen des Bytecodes in allen Methoden - -Die Unterteilung von Schritt 2 und 3 ist deswegen notwendig, weil der Code einer Methode auch eine andere, erst nachher deklarierte Methode aufrufen kann. Nach Schritt 2 sind alle Methoden der Klasse bekannt. Wie beschrieben wird auch hier der Zustand über alle Faltungen mitgenommen. Jeder Schritt hat Zugriff auf alle Daten, die aus dem vorherigen Schritt bleiben. Sukzessive wird eine korrekte ClassFile aufgebaut. - -Besonders interessant ist hierbei Schritt 3. Dort wird das Verhalten jeder einzelnen Methode in Bytecode übersetzt. In diesem Schritt werden zusätzlich zu den `Buildern` noch die `Assembler` verwendet (Definition siehe oben.) Die Assembler funktionieren ähnlich wie die Builder, arbeiten allerdings nicht auf einer ClassFile, sondern auf dem Inhalt einer Methode: Sie verarbeiten jeweils ein Tupel: - -`([ConstantInfo], [Operation], [String])` - -Dieses repräsentiert: - -`(Konstantenpool, Bytecode, Lokale Variablen)` - -In der Praxis werden oft nur Bytecode und Konstanten hinzugefügt. Prinzipiell können Assembler auch Code/Konstanten entfernen oder modifizieren. Als Beispiel dient hier der Assembler `assembleExpression`: - -``` -assembleExpression (constants, ops, lvars) (TypedExpression _ NullLiteral) = - (constants, ops ++ [Opaconst_null], lvars) -``` - -Hier werden die Konstanten und lokalen Variablen des Inputs nicht berührt, dem Bytecode wird lediglich die Operation `aconst_null` hinzugefügt. Damit ist das Verhalten des gematchten Inputs - eines Nullliterals - abgebildet. - -Die Assembler rufen sich teilweise rekursiv selbst auf, da ja auch der AST verschachteltes Verhalten abbilden kann. Der Startpunkt für die Assembly einer Methode ist der Builder `methodAssembler`. Dieser entspricht Schritt 3 in der obigen Übersicht. - -## Serialisierung - -Damit Bytecode generiert werden kann, braucht es Strukturen, die die Daten halten, die letztendlich serialisiert werden. Die JVM erwartet den kompilierten Code in handliche Pakete verpackt. Die Struktur dieser Pakete ist [so definiert](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html). - -Jede Struktur, die in dieser übergreifenden Class File vorkommt, haben wir in Haskell abgebildet. Es gibt z.B die Struktur "ClassFile", die wiederum weitere Strukturen wie z.B Informationen über Felder oder Methoden der Klasse beinhaltet. Alle diese Strukturen implementieren folgende TypeClass: - -``` -class Serializable a where - serialize :: a -> [Word8] -``` - -Hier ist ein Beispiel anhand der Serialisierung der einzelnen Operationen: - -``` -instance Serializable Operation where - serialize Opiadd = [0x60] - serialize Opisub = [0x64] - serialize Opimul = [0x68] - ... - serialize (Opgetfield index) = 0xB4 : unpackWord16 index -``` - -Die Struktur ClassFile ruft für deren Kinder rekursiv diese `serialize` Funktion auf und konkateniert die Ergebnisse. Am Ende bleibt eine flache Word8-Liste übrig, die Serialisierung ist damit abgeschlossen. Da der Typecheck sicherstellt, dass alle referenzierten Methoden/Felder gültig sind, kann die Übersetzung der einzelnen Klassen voneinander unabhängig geschehen. - -## Aufgabenteilung - -Die Bytecodegenerierung wurde von Matthias Raba und Christian Brier im Stile des Pair Programmings zu zweit erarbeitet. Durch bisher gute Erfahrungen in vorherigen Projekten, sowie dem Interesse, alle Teile der Bytecodegenerierung zu sehen, wurde diese Programmierungsform als die Beste ausgewählt. Insgesamt lief die Implementierungsphase wie geplant und ohne weitere Komplikationen ab. \ No newline at end of file diff --git a/doc/bytecode.tex b/doc/bytecode.tex new file mode 100644 index 0000000..3ca1727 --- /dev/null +++ b/doc/bytecode.tex @@ -0,0 +1,107 @@ +\section{Bytecodegenerierung} +Die Bytecodegenerierung ist letztendlich eine zweistufige Transformation: + +\vspace{20px} +\texttt{Getypter AST -> [ClassFile] -> [[Word8]]} +\vspace{20px} + +Vom AST, der bereits den Typcheck durchlaufen hat, wird zunächst eine Abbildung in die einzelnen ClassFiles vorgenommen. +Diese ClassFiles werden anschließend in deren Byte-Repräsentation serialisiert. + +\subsection{Codegenerierung} +Für die erste der beiden Transformationen (\texttt{Getypter AST -> [ClassFile]}) werden die Konzepte der ``Builder'' und ``Assembler'' eingeführt. +Sie sind wie folgt definiert: + +\vspace{20px} +\begin{lstlisting}[language=haskell] + type ClassFileBuilder a = a -> ClassFile -> ClassFile + type Assembler a = ([ConstantInfo], [Operation], [String]) -> a + -> ([ConstantInfo], [Operation], [String]) +\end{lstlisting} +\vspace{20px} + +Die Idee hinter beiden ist, dass sie jeweils zwei Inputs haben, wobei der Rückgabewert immer den gleichen Typ hat wie einer der Inputs. +Das erlaubt es, eine Faltung durchzuführen. Ein ClassFileBuilder z.B bekommt als ersten Parameter den AST, +und als zweiten Parameter (und Rückgabewert) eine ClassFile. Soll nun eine Klasse gebaut werden, +wird der ClassFileBuilder mit dem AST und einer leeren ClassFile aufgerufen. +Der Zustand dieser anfangs leeren ClassFile wird durch alle folgenden Builder/Assembler durchgeschleift, was es erlaubt, +nach und nach kleinere Transformationen auf sie anzuwenden. Der Nutzer ruft beispielsweise die Funktion \texttt{classBuilder} auf. +Diese wendet nach und nach folgende Transformationen an: + +\vspace{20px} +\begin{enumerate} + \item Allen Konstruktoren werden Initialisierer aller Felder hinzugefügt + \item Für jedes Feld der Klasse wird ein Eintrag im Konstantenpool \& der Classfile erstellt + \item Für jede Methode wird ein Eintrag im Konstantenpool \& der Classfile erstellt + \item Allen Methoden wird der zugehörige Bytecode erstellt und zugewiesen + \item Allen Konstruktoren wird der zugehörige Bytecode erstellt und zugewiesen +\end{enumerate} +\vspace{20px} + +Die Unterteilung von Deklaration der Methoden/Konstruktoren und Bytecodeerzeugung ist deswegen notwendig, +weil der Code einer Methode auch eine andere, erst nachher deklarierte Methode aufrufen kann. +Nach dem Hinzufügen der Deklarationen sind alle Methoden/Konstruktoren der Klasse bekannt. +Wie oben beschrieben wird auch hier der Zustand über alle Faltungen mitgenommen. +Jeder Schritt hat Zugriff auf alle Daten, die aus dem vorherigen Schritt bleiben. Sukzessive wird eine korrekte ClassFile aufgebaut. +Besonders interessant sind hierbei die beiden letzten Schritte. Dort wird das Verhalten jeder einzelnen Methode/Konstruktor in Bytecode übersetzt. +In diesem Schritt werden zusätzlich zu den \texttt{Buildern} noch die \texttt{Assembler} verwendet (Definition siehe oben.). +Die Assembler funktionieren ähnlich wie die Builder, arbeiten allerdings nicht auf einer ClassFile, sondern auf dem Inhalt einer Methode; +Sie verarbeiten jeweils ein Tupel der Form: + +\vspace{20px} +\texttt{([ConstantInfo], [Operation], [String])} +\vspace{20px} + +Dieses repräsentiert: + +\vspace{20px} +\texttt{(Konstantenpool, Bytecode, Lokale Variablen)} +\vspace{20px} + +In der Praxis werden meist nur Bytecode und Konstanten hinzugefügt. Prinzipiell können Assembler auch Code/Konstanten entfernen oder modifizieren. +Als Beispiel dient hier der Assembler \texttt{assembleExpression}: + +\vspace{20px} +\begin{lstlisting}[language=haskell] +assembleExpression (constants, ops, lvars) (TypedExpression _ NullLiteral) + = (constants, ops ++ [Opaconst_null], lvars) +\end{lstlisting} +\vspace{20px} + +Hier werden die Konstanten und lokalen Variablen des Inputs nicht berührt, dem Bytecode wird lediglich die Operation \texttt{aconst\_null} hinzugefügt. +Damit ist das Verhalten des gematchten Inputs - eines Nullliterals - abgebildet. +Die Assembler rufen sich teilweise rekursiv selbst auf, da ja auch der AST verschachteltes Verhalten abbilden kann. +Der Startpunkt für die Assembly einer Methode ist der Builder \texttt{methodAssembler}. Dieser entspricht Schritt 3 in der obigen Übersicht. + +\subsection{Serialisierung} +Damit Bytecode generiert werden kann, braucht es Strukturen, die die Daten halten, die letztendlich serialisiert werden. +Die JVM erwartet den kompilierten Code in handliche Pakete verpackt. +Die Struktur dieser Pakete ist \href{https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html}{hier dokumentiert}. +Jede Struktur, die in dieser übergreifenden Class File vorkommt, haben wir in Haskell abgebildet. +Es gibt z.B die Struktur "ClassFile", die wiederum weitere Strukturen wie z.B Informationen über Felder oder Methoden der Klasse beinhaltet. +Alle diese Strukturen implementieren folgende TypeClass: + +\vspace{20px} +\begin{lstlisting}[language=haskell] + class Serializable a where + serialize :: a -> [Word8] +\end{lstlisting} +\vspace{20px} + +Hier ist ein Beispiel anhand der Serialisierung der einzelnen Operationen: + +\vspace{20px} +\begin{lstlisting}[language=haskell] +instance Serializable Operation where + serialize Opiadd = [0x60] + serialize Opisub = [0x64] + serialize Opimul = [0x68] + ... + serialize (Opgetfield index) = 0xB4 : unpackWord16 index +\end{lstlisting} +\vspace{20px} + +Die Struktur ClassFile ruft für deren Kinder rekursiv diese \texttt{serialize} Funktion auf und konkateniert die Ergebnisse. +Am Ende bleibt eine flache Word8-Liste übrig, die Serialisierung ist damit abgeschlossen. +Da der Typecheck sicherstellt, dass alle referenzierten Methoden/Felder gültig sind, +kann die Übersetzung der einzelnen Klassen voneinander unabhängig geschehen. diff --git a/doc/documentation.pdf b/doc/documentation.pdf new file mode 100644 index 0000000000000000000000000000000000000000..13b81ef7369079bdb7f36e54ffa8a56fbbc6ffa5 GIT binary patch literal 102245 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%0Aul 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

DZ>`S@Ur^m2I&DnuzlAZj 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 W0QPjbfH&ot8kljZs=E5SaRC6)3RD>Y literal 0 HcmV?d00001 diff --git a/doc/documentation.tex b/doc/documentation.tex new file mode 100644 index 0000000..b9c2558 --- /dev/null +++ b/doc/documentation.tex @@ -0,0 +1,34 @@ +\documentclass[12pt, parskip=half, headheight=12pt, BCOR=8mm, footheight=16pt]{extarticle} + +% General document formatting +\usepackage[margin=1.0in]{geometry} +\usepackage[parfill]{parskip} +\usepackage[utf8]{inputenc} +\usepackage[german]{babel} +\usepackage{enumitem} +\usepackage{listings} +\usepackage{hyperref} + +\renewcommand\descriptionlabel[1]{$\bullet$ \textbf{#1}} +\hypersetup{ + colorlinks=true, + linkcolor=blue, + filecolor=magenta, + urlcolor=cyan, +} +\let\clearpage\relax + +\begin{document} + +\include{features} +\newpage +\include{parser} +\newpage +\include{typecheck} +\newpage +\include{bytecode} +\newpage +\include{whodunit} +\newpage + +\end{document} \ No newline at end of file diff --git a/doc/features.md b/doc/features.md deleted file mode 100644 index c724c93..0000000 --- a/doc/features.md +++ /dev/null @@ -1,29 +0,0 @@ -# Sprach-Features -- Klassen -- Felder -- Methoden (mit Parametern) -- Konstruktoren (mit Parametern) -- Standardkonstruktoren -- Lokale Variablen -- Zuweisungen (Feld- und lokale Variablen) -- Arithmetik (+, -, *, /, %, Klammern, Korrekte Operator Precedence) -- Arithmetische Zuweisungen (+=, -=, *=, /=, %=, &=, |=, ^=) -- Vergleichsoperationen (<, >, <=, >=, ==, !=) -- Boolsche Operationen (||, &&) -- Unäre Operationen (-, ~) -- Binar-Operationen (&, |, ^) -- Pre/Post-Inkrement & Dekrement -- Kontrollflussstrukturen: - - If/Else - - While - - For - - Return (mit/ohne Rückgabewert) -- Default-Werte für alle Klassenfelder -- Methodenaufrufe (mit Parametern), auch über Klassengrenzen -- Mehrere Klassen in einer Datei -- implizites "this" -- Beliebig verschachtelte Namensketten -- Beliebige Deklarationsreihenfolge -- Literale für Integer und Characters -- Deklaration und Zuweisung in einer Anweisung -- Beliebig verschachtelte Blöcke diff --git a/doc/features.tex b/doc/features.tex new file mode 100644 index 0000000..f3093f6 --- /dev/null +++ b/doc/features.tex @@ -0,0 +1,34 @@ +\section{Sprach-Features} +\begin{itemize} + \item Klassen + \item Felder + \item Methoden (mit Parametern) + \item Konstruktoren (mit Parametern) + \item Standardkonstruktoren + \item Lokale Variablen + \item Zuweisungen (Feld- und lokale Variablen) + \item Arithmetik (\texttt{+, -, *, /, \%,} Klammern, Korrekte Operations-Präzedenz) + \item Arithmetische Zuweisungen (\texttt{+=, -=, *=, /=, \%=, \&=, |=, \^{}=}) + \item Vergleichsoperationen (\texttt{<, >, <=, >=, ==, !=}) + \item Boolsche Operationen (\texttt{||, \&\&}) + \item Unäre Operationen (\texttt{-, ~}) + \item Binar-Operationen (\texttt{\&, |, \^}) + \item Pre/Post-Inkrement \& Dekrement + \item Kontrollflussstrukturen: + \begin{itemize} + \item If/Else + \item While + \item For + \item Return (mit/ohne Rückgabewert) + \end{itemize} + \item Default-Werte für alle Klassenfelder + \item Mehrere Klassen in einer Datei + \item Implizites \texttt{this} + \item Beliebig verschachtelte Namensketten + \item Beliebige Deklarationsreihenfolge + \item Literale für Integer, Characters, Booleans + \item Platzhalter/Separatoren in Integerliteralen (z.B. \texttt{1\_000\_000}) + \item Deklaration und Zuweisung in einer Anweisung + \item Beliebig verschachtelte Blöcke + \item Überladung von Methoden \& Konstruktoren +\end{itemize} \ No newline at end of file diff --git a/doc/generate.sh b/doc/generate.sh old mode 100755 new mode 100644 index a1a040e..f80d707 --- a/doc/generate.sh +++ b/doc/generate.sh @@ -1,4 +1 @@ -#!/usr/bin/sh - -pandoc bytecode.md -o bytecode.docx -pandoc bytecode.md -o bytecode.pdf \ No newline at end of file +pdflatex documentation.tex \ No newline at end of file diff --git a/doc/parser.md b/doc/parser.md deleted file mode 100644 index 65fb92b..0000000 --- a/doc/parser.md +++ /dev/null @@ -1,19 +0,0 @@ -# Lexer -(Marvin Schlegel) - -Der Lexer wurde mit dem Alex tool implementiert. Dieser ist dafür zuständig den langen String in einzelne Tokens umzuwandeln. In der Alex Datei gibt es für jedes Token einen regulären Ausdruck. Bei den meisten Tokens ist das einfach das Schlüsselwort. Etwas komplexer waren Identifier, Integerliterale Strings und Chars. Für die Definition wurde sich eng an die offizielle Java Language Specification gehalten. Es ist beispielsweise auch möglich Unterstriche in Integerliterale einzubauen (Bsp.: `234_343_000`) Es sind fast alle Schlüsselwörter von Java im Lexer implementiert, auch wenn nicht alle davon vom Parser geparst werden können. Whitespace und Kommentare werden direkt ignoriert und verworfen. Für Charliterale und Integerliterale gibt es auch spezielle Fehlermeldungen. Die meisten Tokens haben nur die Information, zu welchem Keyword sie gehören. Eine Ausnahme bilden der Identifier und die Literale. Für den Identifier wird noch der Name gespeichert und für die Literale der entsprechende Wert. Mit der Funktion alexScanTokens kann dann ein beliebiger String in Tokens umgewandelt werden. - -Die komplexeren Tokens haben Unittests, welche mit dem Testframework HUnit geschrieben wurden. Es gibt Tests für Kommentare, Identifier, Literale und ein paar weitere Tokens. - -# Parser -(Marvin Schlegel) - -Der Parser wurde mit dem Happy tool implementiert. Er baut aus einer Liste von Tokens einen ungetypten AST. Wir haben bereits eine Grammatik bekommen und mussten diese noch in den AST umwandeln. - -Um den Parser aufzubauen wurde zuerst ein Großteil der Grammatik auskommentiert und Stück für Stück wurden die Umwandlungen hinzugefügt. Immer wenn ein neues Feature umgesetzt wurde, wurde dafür ein weiterer Unit Test geschrieben. Es gibt also für jede komplexe Ableitungsregel mindestens einen Unittest. - -Als erstes wurden leere Methoden und Felder umgesetzt. Da in Java Methoden und Felder durcheinander vorkommen können geben die Ableitungsregeln einen Datentype namens `MethodOrFieldDeclaration` zurück. Über Pattern Matching baut die classbodydeclarations Regel dann eine Tupel mit einer Liste aus Methoden und einer aus Feldern. Über pattern matching werden diese Listen dann erweitert und in der darüberliegenden Regel schließlich extrahiert. Die Konstruktoren sind in diesem Fall auch normale Methoden mit dem Rückgabewert `void` und dem Namen ``. Auf diese Weise müssen sie nicht mehr vom Typcheck oder vom Bytecode verändert werden. - -In Java ist es möglich mehrere Variablen in einer Zeile zu deklarieren (Bsp.: `int x, y;`). Beim Parsen ergiebt sich dann die Schwierigkeit, dass man in dem Moment, wo man die Variable parst nicht weiß welchen Datentyp diese hat. Aus diesem Grund gibt es den Datentyp Declarator, welcher nur den Identifier und eventuell eine Zuweisung enthält. In den darüberliegenden Regeln fielddeclaration und localvariabledeclaration wird dann die Typinformation hinzugefügt mithilfe der Funktion convertDeclarator. - -Für die Zuweisung wird auch die Kombination mit Rechenoperatoren unterstützt. Das ganze ist als syntactic sugar im Parser umgesetzt. Wenn es einen Zuweisungsoperator gibt, dann wird der Ausdruck in eine Zuweisung und Rechnung aufgeteilt. Bsp.: `x += 3;` wird umgewandelt in `x = x + 3`. diff --git a/doc/parser.tex b/doc/parser.tex new file mode 100644 index 0000000..877e46a --- /dev/null +++ b/doc/parser.tex @@ -0,0 +1,35 @@ +\section{Lexer \& Parser} +\subsection{Lexer} + +Der Lexer wurde mit dem Alex tool implementiert. Dieser ist dafür zuständig den langen String in einzelne Tokens umzuwandeln. +In der Alex Datei gibt es für jedes Token einen regulären Ausdruck. Bei den meisten Tokens ist das einfach das Schlüsselwort. +Etwas komplexer waren Identifier, Integerliterale Strings und Chars. +Für die Definition wurde sich eng an die offizielle Java Language Specification gehalten. +Es ist beispielsweise auch möglich Unterstriche in Integerliterale einzubauen (Bsp.: \texttt{234\_343\_000}). +Es sind fast alle Schlüsselwörter von Java im Lexer implementiert, auch wenn nicht alle davon vom Parser geparst werden können. +Whitespace und Kommentare werden direkt ignoriert und verworfen. +Für Charliterale und Integerliterale gibt es auch spezielle Fehlermeldungen. Die meisten Tokens haben nur die Information, zu welchem Keyword sie gehören. +Eine Ausnahme bilden der Identifier und die Literale. Für den Identifier wird noch der Name gespeichert und für die Literale der entsprechende Wert. +Mit der Funktion alexScanTokens kann dann ein beliebiger String in Tokens umgewandelt werden. +Die komplexeren Tokens haben Unittests, welche mit dem Testframework HUnit geschrieben wurden. +Es gibt Tests für Kommentare, Identifier, Literale und ein paar weitere Tokens. + +\subsection{Parser} + +Der Parser wurde mit dem Happy tool implementiert. Er baut aus einer Liste von Tokens einen ungetypten AST. +Wir haben bereits eine Grammatik bekommen und mussten diese noch in den AST umwandeln. +Um den Parser aufzubauen wurde zuerst ein Großteil der Grammatik auskommentiert und Stück für Stück wurden die Umwandlungen hinzugefügt. +Immer wenn ein neues Feature umgesetzt wurde, wurde dafür ein weiterer Unit Test geschrieben. +Es gibt also für jede komplexe Ableitungsregel mindestens einen Unittest. Als erstes wurden leere Methoden und Felder umgesetzt. +Da in Java Methoden und Felder durcheinander vorkommen können geben die Ableitungsregeln einen Datentype namens \texttt{MethodOrFieldDeclaration} zurück. +Über Pattern Matching baut die classbodydeclarations Regel dann eine Tupel mit einer Liste aus Methoden und einer aus Feldern. +Über pattern matching werden diese Listen dann erweitert und in der darüberliegenden Regel schließlich extrahiert. +Die Konstruktoren sind in diesem Fall auch normale Methoden mit dem Rückgabewert `void` und dem Namen \texttt{}. +Auf diese Weise müssen sie nicht mehr vom Typcheck oder vom Bytecode verändert werden. +In Java ist es möglich mehrere Variablen in einer Zeile zu deklarieren (Bsp.: \texttt{int x, y;}). +Beim Parsen ergiebt sich dann die Schwierigkeit, dass man in dem Moment, wo man die Variable parst nicht weiß welchen Datentyp diese hat. +Aus diesem Grund gibt es den Datentyp Declarator, welcher nur den Identifier und eventuell eine Zuweisung enthält. +In den darüberliegenden Regeln fielddeclaration und localvariabledeclaration wird dann die Typinformation hinzugefügt mithilfe der Funktion convertDeclarator. +Für die Zuweisung wird auch die Kombination mit Rechenoperatoren unterstützt. Das Ganze ist als syntactic sugar im Parser umgesetzt. +Wenn es einen Zuweisungsoperator gibt, dann wird der Ausdruck in eine Zuweisung und Rechnung aufgeteilt. +Bsp.: \texttt{x += 3;} wird umgewandelt zu \texttt{x = x + 3}. diff --git a/doc/typecheck.md b/doc/typecheck.md deleted file mode 100644 index 33a071b..0000000 --- a/doc/typecheck.md +++ /dev/null @@ -1,79 +0,0 @@ - -# Typecheck (Fabian Noll) - -## Überblick und Struktur - -Die Typprüfung beginnt mit der Funktion `typeCheckCompilationUnit`, die eine Kompilationseinheit als Eingabe erhält. Diese Kompilationseinheit besteht aus einer Liste von Klassen. Jede Klasse wird einzeln durch die Funktion `typeCheckClass` überprüft. Innerhalb dieser Funktion wird eine Symboltabelle erstellt, die den Namen der Klasse als Typ und `this` als Identifier enthält. Diese Symboltabelle wird verwendet, um Typinformationen nach dem Lokalitätsprinzip während der Typprüfung zugänglich zu machen und zu verwalten. - -Die Typprüfung einer Klasse umfasst die Überprüfung aller Konstruktoren, Methoden und Felder. Die Methode `typeCheckConstructorDeclaration` ist für die Typprüfung einzelner Konstruktordeklarationen verantwortlich, während `typeCheckMethodDeclaration` für die Typprüfung einzelner Methodendeklarationen zuständig ist. Beide Funktionen überprüfen die Parameter und den Rumpf der jeweiligen Konstruktoren bzw. Methoden. Der Rumpf wird durch rekursive Aufrufe von `typeCheckStatement` überprüft, die verschiedene Arten von Anweisungen wie If-Anweisungen, While-Schleifen, Rückgabeanweisungen und Blockanweisungen behandelt. - -## Ablauf und Symboltabellen - -Eine zentrale Komponente des Typecheckers ist die Symboltabelle (symtab), die Informationen über die Bezeichner und ihre zugehörigen Datentypen speichert. Die Symboltabelle wird kontinuierlich angepasst, während der Typechecker die verschiedenen Teile des Programms durchläuft. - -### Anpassung der Symboltabelle - -- **Klassenkontext**: - Beim Typcheck einer Klasse wird eine initiale Symboltabelle erstellt, die die `this`-Referenz enthält. Dies geschieht in der Funktion `typeCheckClass`. - -- **Konstruktorkontext**: - Innerhalb eines Konstruktors wird die Symboltabelle um die Parameter des Konstruktors erweitert. Dies geschieht in `typeCheckConstructorDeclaration`. Der Rückgabetyp eines Konstruktors ist implizit `void`, was überprüft wird, um sicherzustellen, dass kein Wert zurückgegeben wird. - -- **Methodenkontext**: - Innerhalb einer Methode wird die Symboltabelle um die Parameter der Methode erweitert sowie den Rückgabetyp der Methode, um die einzelnen Returns dagegen zu prüfen. Dies geschieht in `typeCheckMethodDeclaration`. - -- **Blockkontext**: - Bei der Überprüfung eines Blocks (`typeCheckStatement` für Block) wird die Symboltabelle für jede Anweisung innerhalb des Blocks aktualisiert. Lokale Variablen, die innerhalb des Blocks deklariert werden, werden zur Symboltabelle hinzugefügt. Das bedeutet, dass automatisch, sobald der Block zu Ende ist, alle dort deklarierten Variablen danach nicht mehr zugänglich sind. - -### Unterscheidung zwischen lokalen und Feldvariablen - -Bei der Typprüfung von Referenzen (`typeCheckExpression` für Reference) wird zuerst in der Symboltabelle nach dem Bezeichner gesucht. Sollte dieser gefunden werden, handelt es sich um eine lokale Variable. Wenn der Bezeichner nicht gefunden wird, wird angenommen, dass es sich um eine Feldvariable handelt. In diesem Fall wird die Klasse, zu der die `this`-Referenz gehört, durchsucht, um die Feldvariable zu finden. Dies ermöglicht die Unterscheidung zwischen lokalen Variablen und Feldvariablen. Dies ist auch nur möglich, da wir die Feldvariablen und Methoden nicht in die Symboltabelle gelegt haben und stattdessen nur die `this`-Referenz. - -## Fehlerbehandlung - -Ein zentraler Aspekt des Typecheckers ist die Fehlerbehandlung. Bei Typinkonsistenzen oder ungültigen Operationen werden aussagekräftige Fehlermeldungen generiert. Beispiele für solche Fehlermeldungen sind: - -- **Typinkonsistenzen**: - Wenn der Rückgabetyp einer Methode nicht mit dem deklarierten Rückgabetyp übereinstimmt oder die Anzahl der Parameter nicht übereinstimmt. - -- **Ungültige Operationen**: - Wenn eine arithmetische Operation auf inkompatiblen Typen durchgeführt wird. - -- **Nicht gefundene Bezeichner**: - Wenn eine Referenz auf eine nicht definierte Variable verweist. - -Diese Fehlermeldungen helfen Entwicklern, die Ursachen von Typfehlern schnell zu identifizieren und zu beheben. Generell sind diese oftmals sehr spezifisch, was das Problem recht schnell identifizieren sollte. Z.B. falsche Reihenfolge / falsche Typen der Parameter beim Methodenaufruf sind direkt erkennbar. - -## Typprüfung von Kontrollstrukturen und Blöcken - -### If-Anweisungen - -Bei der Typprüfung einer If-Anweisung (`typeCheckStatement` für If) wird zuerst der Typ der Bedingung überprüft, um sicherzustellen, dass es sich um einen booleschen Ausdruck handelt. Anschließend werden die Then- und Else-Zweige geprüft. Der Typ der If-Anweisung selbst wird durch die Vereinheitlichung der Typen der Then- und Else-Zweige bestimmt. Falls einer der Zweige keinen Rückgabewert hat, wird angenommen, dass der Rückgabewert `void` ist. Dies wurde so gelöst, um im Typchecker feststellen zu können, ob beide Zweige einen Return haben. Wenn nur einer der Zweige ein Return hat, wird im umliegenden Block ein weiteres benötigt, was durch den Typ `void` erzwungen wird. Dadurch weiß der Typchecker Bescheid. - -### Block-Anweisungen - -Die Typprüfung eines Blocks erfolgt in `typeCheckStatement` für Block. Jede Anweisung im Block wird nacheinander überprüft und die Symboltabelle wird entsprechend aktualisiert. Der Typ des Blocks wird durch die Vereinheitlichung der Typen aller Anweisungen im Block bestimmt. Wenn der Block keine Anweisungen hat, wird der Typ `void` angenommen. - -### Rückgabeanweisungen - -Die Typprüfung einer Rückgabeanweisung (`typeCheckStatement` für Return) überprüft, ob der Rückgabewert der Anweisung mit dem deklarierten Rückgabetyp der Methode übereinstimmt. Dafür wurde zu Beginn der Methodentypprüfung der Rückgabetyp der Methode in die Symboltabelle eingetragen. Wenn der Rückgabewert `null` ist, wird überprüft, ob der deklarierte Rückgabetyp ein Objekttyp ist. Dies stellt sicher, dass Methoden immer den korrekten Typ zurückgeben. Generell wird bei der Prüfung nach dem UpperBound geschaut und ebenfalls wird nachgeschaut, ob, wenn der Rückgabetyp `Object` ist, der Return-Wert auch eine tatsächlich existierende Klasse ist, indem in die Klassentabelle geschaut wird. - -### Konstruktorüberladung und -prüfung - -Die Typprüfung unterstützt Konstruktorüberladung. Bei der Typprüfung von Konstruktoraufrufen (`typeCheckStatementExpression` für `ConstructorCall`) wird überprüft, ob es mehrere Konstruktoren mit derselben Anzahl von Parametern gibt. Falls mehrere passende Konstruktoren gefunden werden, wird ein Fehler gemeldet. - -- **Parameterabgleich**: - Die Parameter eines Konstruktors werden gegen die Argumente des Aufrufs abgeglichen. Dies umfasst die Prüfung der Typen und, falls es sich um `null` handelt, die Überprüfung, ob der Parameter ein Objekttyp ist. - -- **Fehlerbehandlung**: - Wenn kein passender Konstruktor gefunden wird, wird eine detaillierte Fehlermeldung generiert, die die erwarteten Signaturen und die tatsächlichen Argumenttypen anzeigt. Wenn mehrere passende Konstruktoren gefunden werden, wird ebenfalls ein Fehler gemeldet. - -### Methodenüberladung und -prüfung - -Die Typprüfung unterstützt auch Methodenüberladung. Bei der Typprüfung von Methodenaufrufen (`typeCheckStatementExpression` für `MethodCall`) wird überprüft, ob es mehrere Methoden mit demselben Namen, aber unterschiedlichen Parametertypen gibt. - -- **Parameterabgleich**: - Die Parameter einer Methode werden gegen die Argumente des Aufrufs abgeglichen. Dies umfasst die Prüfung der Typen und, falls es sich um `null` handelt, die Überprüfung, ob der Parameter ein Objekttyp ist. - -- **Fehlerbehandlung**: - Wenn keine passende Methode gefunden wird, wird eine detaillierte Fehlermeldung generiert, die die erwarteten Signaturen und die tatsächlichen Argumenttypen anzeigt. Wenn mehrere passende Methoden gefunden werden, wird ebenfalls ein Fehler gemeldet. diff --git a/doc/typecheck.tex b/doc/typecheck.tex new file mode 100644 index 0000000..25e86f7 --- /dev/null +++ b/doc/typecheck.tex @@ -0,0 +1,105 @@ +\section{Typecheck} +\subsection{Überblick \& Struktur} + +Die Typprüfung beginnt mit der Funktion \texttt{typeCheckCompilationUnit}, die eine Kompilationseinheit als Eingabe erhält. +Diese Kompilationseinheit besteht aus einer Liste von Klassen. Jede Klasse wird einzeln durch die Funktion \texttt{typeCheckClass} überprüft. +Innerhalb dieser Funktion wird eine Symboltabelle erstellt, die den Namen der Klasse als Typ und \texttt{this} als Identifier enthält. +Diese Symboltabelle wird verwendet, um Typinformationen nach dem Lokalitätsprinzip während der Typprüfung zugänglich zu machen und zu verwalten. +Die Typprüfung einer Klasse umfasst die Überprüfung aller Konstruktoren, Methoden und Felder. +Die Methode \texttt{typeCheckConstructorDeclaration} ist für die Typprüfung einzelner Konstruktordeklarationen verantwortlich, +während \texttt{typeCheckMethodDeclaration} für die Typprüfung einzelner Methodendeklarationen zuständig ist. +Beide Funktionen überprüfen die Parameter und den Rumpf der jeweiligen Konstruktoren bzw. Methoden. +Der Rumpf wird durch rekursive Aufrufe von \texttt{typeCheckStatement} überprüft, die verschiedene Arten von Anweisungen wie If-Anweisungen, +While-Schleifen, Rückgabeanweisungen und Blockanweisungen behandelt. + +\subsection{Ablauf \& Symboltabellen} + +Eine zentrale Komponente des Typecheckers ist die Symboltabelle ("symtab"), die Informationen über die Bezeichner und ihre zugehörigen Datentypen speichert. +Die Symboltabelle wird kontinuierlich angepasst, während der Typechecker die verschiedenen Teile des Programms durchläuft. + +\subsubsection{Anpassung der Symboltabelle} + +\begin{description} + \item[Klassenkontext] Beim Typcheck einer Klasse wird eine initiale Symboltabelle erstellt, die die \texttt{this}-Referenz enthält. + Dies geschieht in der Funktion \texttt{typeCheckClass}. + \item[Konstruktorkontext] Innerhalb eines Konstruktors wird die Symboltabelle um die Parameter des Konstruktors erweitert. + Dies geschieht in \texttt{typeCheckConstructorDeclaration}. Der Rückgabetyp eines Konstruktors ist implizit \texttt{void}, + was überprüft wird, um sicherzustellen, dass kein Wert zurückgegeben wird. + \item[Methodenkontext] Innerhalb einer Methode wird die Symboltabelle um die Parameter der Methode erweitert sowie den Rückgabetyp der Methode, + um die einzelnen Returns dagegen zu prüfen. Dies geschieht in \texttt{typeCheckMethodDeclaration}. + \item[Blockkontext] Bei der Überprüfung eines Blocks (\texttt{typeCheckStatement} für Block) wird die Symboltabelle für jede Anweisung + innerhalb des Blocks aktualisiert. Lokale Variablen, die innerhalb des Blocks deklariert werden, werden zur Symboltabelle hinzugefügt. + Das bedeutet, dass automatisch, sobald der Block zu Ende ist, alle dort deklarierten Variablen danach nicht mehr zugänglich sind. +\end{description} + +\subsubsection{Unterscheidung zwischen lokalen und Feldvariablen} + +Bei der Typprüfung von Referenzen (\texttt{typeCheckExpression} für Reference) wird zuerst in der Symboltabelle nach dem Bezeichner gesucht. +Sollte dieser gefunden werden, handelt es sich um eine lokale Variable. Wenn der Bezeichner nicht gefunden wird, wird angenommen, +dass es sich um eine Feldvariable handelt. In diesem Fall wird die Klasse, zu der die \texttt{this}-Referenz gehört, durchsucht, +um die Feldvariable zu finden. Dies ermöglicht die Unterscheidung zwischen lokalen Variablen und Feldvariablen. +Dies ist auch nur möglich, da wir die Feldvariablen und Methoden nicht in die Symboltabelle gelegt haben und stattdessen nur die \texttt{this}-Referenz. + +\subsection{Fehlerbehandlung} + +Ein zentraler Aspekt des Typecheckers ist die Fehlerbehandlung. Bei Typinkonsistenzen oder ungültigen Operationen werden +aussagekräftige Fehlermeldungen generiert. Beispiele für solche Fehlermeldungen sind: + +\begin{description} + \item[Typinkonsistenzen] Wenn der Rückgabetyp einer Methode nicht mit dem deklarierten Rückgabetyp übereinstimmt oder die Anzahl der Parameter nicht übereinstimmt. + \item[Ungültige Operationen] Wenn eine arithmetische Operation auf inkompatiblen Typen durchgeführt wird. + \item[Nicht gefundene Bezeichner] Wenn eine Referenz auf eine nicht definierte Variable verweist. +\end{description} + +Diese Fehlermeldungen helfen Entwicklern, die Ursachen von Typfehlern schnell zu identifizieren und zu beheben. +Generell sind diese oftmals sehr spezifisch, was das Problem recht schnell identifizieren sollte. +Z.B. falsche Reihenfolge / falsche Typen der Parameter beim Methodenaufruf sind direkt erkennbar. + +\subsection{Typprüfung von Kontrollstrukturen und Blöcken} +\subsubsection{If-Anweisungen} +Bei der Typprüfung einer If-Anweisung (\texttt{typeCheckStatement} für If) wird zuerst der Typ der Bedingung überprüft, um sicherzustellen, +dass es sich um einen booleschen Ausdruck handelt. Anschließend werden die Then- und Else-Zweige geprüft. +Der Typ der If-Anweisung selbst wird durch die Vereinheitlichung der Typen der Then- und Else-Zweige bestimmt. +Falls einer der Zweige keinen Rückgabewert hat, wird angenommen, dass der Rückgabewert \texttt{void} ist. +Dies wurde so gelöst, um im Typchecker feststellen zu können, ob beide Zweige einen Return haben. +Wenn nur einer der Zweige ein Return hat, wird im umliegenden Block ein weiteres benötigt, was durch den Typ \texttt{void} erzwungen wird. +Dadurch weiß der Typchecker Bescheid. + +\subsubsection{Block-Anweisungen} +Die Typprüfung eines Blocks erfolgt in \texttt{typeCheckStatement} für Block. +Jede Anweisung im Block wird nacheinander überprüft und die Symboltabelle wird entsprechend aktualisiert. +Der Typ des Blocks wird durch die Vereinheitlichung der Typen aller Anweisungen im Block bestimmt. +Wenn der Block keine Anweisungen hat, wird der Typ \texttt{void} angenommen. + +\subsubsection{Rückgabeanweisungen} +Die Typprüfung einer Rückgabeanweisung (\texttt{typeCheckStatement} für Return) überprüft, +ob der Rückgabewert der Anweisung mit dem deklarierten Rückgabetyp der Methode übereinstimmt. +Dafür wurde zu Beginn der Methodentypprüfung der Rückgabetyp der Methode in die Symboltabelle eingetragen. Wenn der Rückgabewert \texttt{null} ist, +wird überprüft, ob der deklarierte Rückgabetyp ein Objekttyp ist. Dies stellt sicher, dass Methoden immer den korrekten Typ zurückgeben. +Generell wird bei der Prüfung nach dem UpperBound geschaut und ebenfalls wird nachgeschaut, ob, wenn der Rückgabetyp \texttt{Object} ist, +der Return-Wert auch eine tatsächlich existierende Klasse ist, indem in die Klassentabelle geschaut wird. + +\subsubsection{Konstruktorüberladung und -prüfung} +Die Typprüfung unterstützt Konstruktorüberladung. Bei der Typprüfung von Konstruktoraufrufen (\texttt{typeCheckStatementExpression} +für \texttt{ConstructorCall}) wird überprüft, ob es mehrere Konstruktoren mit derselben Anzahl von Parametern gibt. +Falls mehrere passende Konstruktoren gefunden werden, wird ein Fehler gemeldet. + +\begin{description} + \item[Parameterabgleich] Die Parameter eines Konstruktors werden gegen die Argumente des Aufrufs abgeglichen. + Dies umfasst die Prüfung der Typen und, falls es sich um \texttt{null} handelt, die Überprüfung, ob der Parameter ein Objekttyp ist. + \item[Fehlerbehandlung] Wenn kein passender Konstruktor gefunden wird, wird eine detaillierte Fehlermeldung generiert, + die die erwarteten Signaturen und die tatsächlichen Argumenttypen anzeigt. Wenn mehrere passende Konstruktoren gefunden werden, + wird ebenfalls ein Fehler gemeldet. +\end{description} + +\subsubsection{Methodenüberladung und -prüfung} +Die Typprüfung unterstützt auch Methodenüberladung. Bei der Typprüfung von Methodenaufrufen (\texttt{typeCheckStatementExpression} für \texttt{MethodCall}) +wird überprüft, ob es mehrere Methoden mit demselben Namen, aber unterschiedlichen Parametertypen gibt. + +\begin{description} + \item[Parameterabgleich] Die Parameter einer Methode werden gegen die Argumente des Aufrufs abgeglichen. + Dies umfasst die Prüfung der Typen und, falls es sich um \texttt{null} handelt, die Überprüfung, ob der Parameter ein Objekttyp ist. + \item[Fehlerbehandlung] Wenn keine passende Methode gefunden wird, wird eine detaillierte Fehlermeldung generiert, + die die erwarteten Signaturen und die tatsächlichen Argumenttypen anzeigt. Wenn mehrere passende Methoden gefunden werden, + wird ebenfalls ein Fehler gemeldet. +\end{description} diff --git a/doc/whodunit.tex b/doc/whodunit.tex new file mode 100644 index 0000000..3bbf74e --- /dev/null +++ b/doc/whodunit.tex @@ -0,0 +1,19 @@ +\section{Aufgabenverteilung} +\begin{description} + \item[Marvin Schlegel] Parser \& Lexer + \item[Fabian Noll] Semantik- \& Typcheck + \item[Christian Brier] Bytecodegenerierung + \item[Matthias Raba] Bytecodegenerierung +\end{description} +\vspace{20px} + +Marvin Schlegel und Fabian Noll haben ihre Teilaufgaben eigenständig bearbeitet. + +Die Bytecodegenerierung wurde von Matthias Raba und Christian Brier im Stile des Pair Programmings zu zweit erarbeitet. +Durch bisher gute Erfahrungen in vorherigen Projekten, sowie dem Interesse, alle Teile der Bytecodegenerierung zu sehen, +wurde diese Programmierungsform als die Beste ausgewählt. + +Während der Implementierungsphase wurde viel zwischen den 3 einzelnen Teams kommuniziert. +Wurden Fehler in einer der Komponenten gefunden, wurden die jeweiligen Verantwortlichen informiert um das Problem zu beheben. +Jedes der Teams arbeitete auf einem eigenen Branch, die einzelnen Beiträge wurde regelmäßig auf dem master-Branch zusammengeführt. +Insgesamt lief die Implementierungsphase wie geplant und ohne weitere Komplikationen ab. \ No newline at end of file