Compare commits

..

No commits in common. "b525d141924e84cdb9f4f3062d950b45bd3c16b2" and "98b02446ba4d8ab4d032b250c5a53db1db6feefd" have entirely different histories.

4 changed files with 37 additions and 69 deletions

View File

@ -200,9 +200,6 @@ testExpressionSimpleFieldAccess = TestCase $
testExpressionFieldSubAccess = TestCase $ testExpressionFieldSubAccess = TestCase $
assertEqual "expect NameResolution without this" (BinaryOperation NameResolution (Reference "a") (Reference "b")) $ assertEqual "expect NameResolution without this" (BinaryOperation NameResolution (Reference "a") (Reference "b")) $
parseExpression [IDENTIFIER "a",DOT,IDENTIFIER "b"] parseExpression [IDENTIFIER "a",DOT,IDENTIFIER "b"]
testExpressionConstructorCall = TestCase $
assertEqual "expect constructor call" (StatementExpressionExpression (ConstructorCall "Foo" [])) $
parseExpression [NEW,IDENTIFIER "Foo",LBRACE,RBRACE]
testStatementIfThen = TestCase $ testStatementIfThen = TestCase $
assertEqual "expect empty ifthen" [If (Reference "a") (Block [Block []]) Nothing] $ assertEqual "expect empty ifthen" [If (Reference "a") (Block [Block []]) Nothing] $
@ -220,16 +217,7 @@ testStatementAssign = TestCase $
testStatementMethodCallNoParams = TestCase $ testStatementMethodCallNoParams = TestCase $
assertEqual "expect methodcall statement no params" [StatementExpressionStatement (MethodCall (Reference "this") "foo" [])] $ assertEqual "expect methodcall statement no params" [StatementExpressionStatement (MethodCall (Reference "this") "foo" [])] $
parseStatement [IDENTIFIER "foo",LBRACE,RBRACE,SEMICOLON] parseStatement [IDENTIFIER "foo",LBRACE,RBRACE,SEMICOLON]
testStatementConstructorCall = TestCase $
assertEqual "expect constructor call" [StatementExpressionStatement (ConstructorCall "Foo" [])] $
parseStatement [NEW,IDENTIFIER "Foo",LBRACE,RBRACE,SEMICOLON]
testStatementConstructorCallWithArgs = TestCase $
assertEqual "expect constructor call" [StatementExpressionStatement (ConstructorCall "Foo" [Reference "b"])] $
parseStatement [NEW,IDENTIFIER "Foo",LBRACE,IDENTIFIER "b",RBRACE,SEMICOLON]
testStatementPreIncrement = TestCase $
assertEqual "expect increment" [StatementExpressionStatement $ PostIncrement $ Reference "a"] $
parseStatement [IDENTIFIER "a",INCREMENT,SEMICOLON]
tests = TestList [ tests = TestList [
@ -291,13 +279,9 @@ tests = TestList [
testExpressionFieldAccess, testExpressionFieldAccess,
testExpressionSimpleFieldAccess, testExpressionSimpleFieldAccess,
testExpressionFieldSubAccess, testExpressionFieldSubAccess,
testExpressionConstructorCall,
testStatementIfThen, testStatementIfThen,
testStatementIfThenElse, testStatementIfThenElse,
testStatementWhile, testStatementWhile,
testStatementAssign, testStatementAssign,
testStatementMethodCallNoParams, testStatementMethodCallNoParams
testStatementConstructorCall,
testStatementConstructorCallWithArgs,
testStatementPreIncrement
] ]

View File

@ -117,7 +117,7 @@ modifier : PUBLIC { }
| STATIC { } | STATIC { }
| ABSTRACT { } | ABSTRACT { }
classtype : classorinterfacetype { $1 } classtype : classorinterfacetype{ }
classbodydeclaration : classmemberdeclaration { $1 } classbodydeclaration : classmemberdeclaration { $1 }
| constructordeclaration { $1 } | constructordeclaration { $1 }
@ -249,12 +249,12 @@ assignment : lefthandside assignmentoperator assignmentexpression {
statementexpression : assignment { $1 } statementexpression : assignment { $1 }
| preincrementexpression { $1 } -- | preincrementexpression { }
| predecrementexpression { $1 } -- | predecrementexpression { }
| postincrementexpression { $1 } -- | postincrementexpression { }
| postdecrementexpression { $1 } -- | postdecrementexpression { }
| methodinvocation { $1 } | methodinvocation { $1 }
| classinstancecreationexpression { $1 } -- | classinstancecreationexpression { }
ifthenelsestatementnoshortif :IF LBRACE expression RBRACE statementnoshortif ifthenelsestatementnoshortif :IF LBRACE expression RBRACE statementnoshortif
ELSE statementnoshortif { } ELSE statementnoshortif { }
@ -292,8 +292,8 @@ methodinvocation : simplename LBRACE RBRACE { MethodCall (Reference "this") $
| primary DOT IDENTIFIER LBRACE RBRACE { MethodCall $1 $3 [] } | primary DOT IDENTIFIER LBRACE RBRACE { MethodCall $1 $3 [] }
| primary DOT IDENTIFIER LBRACE argumentlist RBRACE { MethodCall $1 $3 $5 } | primary DOT IDENTIFIER LBRACE argumentlist RBRACE { MethodCall $1 $3 $5 }
classinstancecreationexpression : NEW classtype LBRACE RBRACE { ConstructorCall $2 [] } classinstancecreationexpression : NEW classtype LBRACE RBRACE { }
| NEW classtype LBRACE argumentlist RBRACE { ConstructorCall $2 $4 } | NEW classtype LBRACE argumentlist RBRACE { }
conditionalandexpression : inclusiveorexpression { $1 } conditionalandexpression : inclusiveorexpression { $1 }
@ -318,7 +318,7 @@ inclusiveorexpression : exclusiveorexpression { $1 }
primarynonewarray : literal { $1 } primarynonewarray : literal { $1 }
| THIS { Reference "this" } | THIS { Reference "this" }
| LBRACE expression RBRACE { $2 } | LBRACE expression RBRACE { $2 }
| classinstancecreationexpression { StatementExpressionExpression $1 } -- | classinstancecreationexpression { }
| fieldaccess { $1 } | fieldaccess { $1 }
| methodinvocation { StatementExpressionExpression $1 } | methodinvocation { StatementExpressionExpression $1 }

View File

@ -72,7 +72,7 @@ tokens :-
-- end keywords -- end keywords
$JavaLetter$JavaLetterOrDigit* { \s -> IDENTIFIER s } $JavaLetter$JavaLetterOrDigit* { \s -> IDENTIFIER s }
-- Literals -- Literals
[0-9]([0-9\_]*[0-9])* { \s -> case readMaybe $ filter ((/=) '_') s of Just a -> INTEGERLITERAL a; Nothing -> error ("failed to parse INTLITERAL " ++ s) } [1-9]([0-9\_]*[0-9])* { \s -> case readMaybe $ filter ((/=) '_') s of Just a -> INTEGERLITERAL a; Nothing -> error ("failed to parse INTLITERAL " ++ s) }
"'"."'" { \s -> case (s) of _ : c : _ -> CHARLITERAL c; _ -> error ("failed to parse CHARLITERAL " ++ s) } "'"."'" { \s -> case (s) of _ : c : _ -> CHARLITERAL c; _ -> error ("failed to parse CHARLITERAL " ++ s) }
-- separators -- separators
"(" { \_ -> LBRACE } "(" { \_ -> LBRACE }

View File

@ -19,14 +19,15 @@ typeCheckClass (Class className methods fields) classes =
typeCheckMethodDeclaration :: MethodDeclaration -> [(Identifier, DataType)] -> [Class] -> MethodDeclaration typeCheckMethodDeclaration :: MethodDeclaration -> [(Identifier, DataType)] -> [Class] -> MethodDeclaration
typeCheckMethodDeclaration (MethodDeclaration retType name params body) classFields classes = typeCheckMethodDeclaration (MethodDeclaration retType name params body) classFields classes =
let let
-- Combine class fields with method parameters to form the initial symbol table for the method
methodParams = [(identifier, dataType) | ParameterDeclaration dataType identifier <- params] methodParams = [(identifier, dataType) | ParameterDeclaration dataType identifier <- params]
initialSymtab = ("thisMeth", retType) : classFields ++ methodParams initialSymtab = classFields ++ methodParams
checkedBody = typeCheckStatement body initialSymtab classes checkedBody = typeCheckStatement body initialSymtab classes
bodyType = getTypeFromStmt checkedBody bodyType = getTypeFromStmt checkedBody
in if bodyType == retType || (bodyType == "void" && retType == "void") || (bodyType == "null" && isObjectType retType) || isSubtype bodyType retType classes -- Check if the type of the body matches the declared return type
in if bodyType == retType || (bodyType == "void" && retType == "void") || (bodyType == "null" && isObjectType retType)
then MethodDeclaration retType name params checkedBody then MethodDeclaration retType name params checkedBody
else error $ "Method Declaration: Return type mismatch in method " ++ name ++ ": expected " ++ retType ++ ", found " ++ bodyType else error $ "Return type mismatch in method " ++ name ++ ": expected " ++ retType ++ ", found " ++ bodyType
-- ********************************** Type Checking: Expressions ********************************** -- ********************************** Type Checking: Expressions **********************************
@ -118,7 +119,7 @@ typeCheckStatementExpression (ConstructorCall className args) symtab classes =
Nothing -> error $ "Class '" ++ className ++ "' not found." Nothing -> error $ "Class '" ++ className ++ "' not found."
Just (Class _ methods fields) -> Just (Class _ methods fields) ->
-- Constructor needs the same name as the class -- Constructor needs the same name as the class
case find (\(MethodDeclaration retType name params _) -> name == "<init>" && retType == "void") methods of case find (\(MethodDeclaration retType name params _) -> name == className && retType == className) methods of
Nothing -> error $ "No valid constructor found for class '" ++ className ++ "'." Nothing -> error $ "No valid constructor found for class '" ++ className ++ "'."
Just (MethodDeclaration _ _ params _) -> Just (MethodDeclaration _ _ params _) ->
let let
@ -203,21 +204,19 @@ typeCheckStatementExpression (PreDecrement expr) symtab classes =
typeCheckStatement :: Statement -> [(Identifier, DataType)] -> [Class] -> Statement typeCheckStatement :: Statement -> [(Identifier, DataType)] -> [Class] -> Statement
typeCheckStatement (If cond thenStmt elseStmt) symtab classes = typeCheckStatement (If cond thenStmt elseStmt) symtab classes =
let let cond' = typeCheckExpression cond symtab classes
cond' = typeCheckExpression cond symtab classes thenStmt' = typeCheckStatement thenStmt symtab classes
thenStmt' = typeCheckStatement thenStmt symtab classes elseStmt' = case elseStmt of
elseStmt' = fmap (\stmt -> typeCheckStatement stmt symtab classes) elseStmt Just stmt -> Just (typeCheckStatement stmt symtab classes)
Nothing -> Nothing
thenType = getTypeFromStmt thenStmt' thenType = getTypeFromStmt thenStmt'
elseType = maybe "void" getTypeFromStmt elseStmt' elseType = maybe "void" getTypeFromStmt elseStmt'
ifType = if thenType /= "void" && elseType /= "void" && thenType == elseType then thenType else "void"
ifType = if thenType == "void" || elseType == "void" in if getTypeFromExpr cond' == "boolean"
then "void" then
else unifyReturnTypes thenType elseType TypedStatement ifType (If cond' thenStmt' elseStmt')
else
in if getTypeFromExpr cond' == "boolean" error "If condition must be of type boolean"
then TypedStatement ifType (If cond' thenStmt' elseStmt')
else error "If condition must be of type boolean"
typeCheckStatement (LocalVariableDeclaration (VariableDeclaration dataType identifier maybeExpr)) symtab classes = typeCheckStatement (LocalVariableDeclaration (VariableDeclaration dataType identifier maybeExpr)) symtab classes =
@ -273,14 +272,12 @@ typeCheckStatement (Block statements) symtab classes =
in TypedStatement blockType (Block checkedStatements) in TypedStatement blockType (Block checkedStatements)
typeCheckStatement (Return expr) symtab classes = typeCheckStatement (Return expr) symtab classes =
let methodReturnType = fromMaybe (error "Method return type not found in symbol table") (lookup "thisMeth" symtab) let expr' = case expr of
expr' = case expr of
Just e -> Just (typeCheckExpression e symtab classes) Just e -> Just (typeCheckExpression e symtab classes)
Nothing -> Nothing Nothing -> Nothing
returnType = maybe "void" getTypeFromExpr expr' in case expr' of
in if returnType == methodReturnType || isSubtype returnType methodReturnType classes Just e' -> TypedStatement (getTypeFromExpr e') (Return (Just e'))
then TypedStatement returnType (Return expr') Nothing -> TypedStatement "void" (Return Nothing)
else error $ "Return: Return type mismatch: expected " ++ methodReturnType ++ ", found " ++ returnType
typeCheckStatement (StatementExpressionStatement stmtExpr) symtab classes = typeCheckStatement (StatementExpressionStatement stmtExpr) symtab classes =
let stmtExpr' = typeCheckStatementExpression stmtExpr symtab classes let stmtExpr' = typeCheckStatementExpression stmtExpr symtab classes
@ -288,17 +285,6 @@ typeCheckStatement (StatementExpressionStatement stmtExpr) symtab classes =
-- ********************************** Type Checking: Helpers ********************************** -- ********************************** Type Checking: Helpers **********************************
isSubtype :: DataType -> DataType -> [Class] -> Bool
isSubtype subType superType classes
| subType == superType = True
| subType == "null" && isObjectType superType = True
| superType == "Object" && isObjectType subType = True
| superType == "Object" && isUserDefinedClass subType classes = True
| otherwise = False
isUserDefinedClass :: DataType -> [Class] -> Bool
isUserDefinedClass dt classes = dt `elem` map (\(Class name _ _) -> name) classes
isObjectType :: DataType -> Bool isObjectType :: DataType -> Bool
isObjectType dt = dt /= "int" && dt /= "boolean" && dt /= "char" isObjectType dt = dt /= "int" && dt /= "boolean" && dt /= "char"
@ -316,10 +302,8 @@ getTypeFromStmtExpr _ = error "Untyped statement expression found where typed wa
unifyReturnTypes :: DataType -> DataType -> DataType unifyReturnTypes :: DataType -> DataType -> DataType
unifyReturnTypes dt1 dt2 unifyReturnTypes dt1 dt2
| dt1 == dt2 = dt1 | dt1 == dt2 = dt1
| dt1 == "null" = dt2 | otherwise = "Object"
| dt2 == "null" = dt1
| otherwise = "Object"
resolveResultType :: DataType -> DataType -> DataType resolveResultType :: DataType -> DataType -> DataType
resolveResultType "char" "char" = "char" resolveResultType "char" "char" = "char"