Compare commits

...

3 Commits

Author SHA1 Message Date
ab7077d8f0 add public private to typecheck 2024-07-04 16:59:52 +02:00
53061fb73d add public private to parser 2024-07-04 16:59:37 +02:00
bac2a534b6 adjust AST for Modifier 2024-07-04 16:03:38 +02:00
3 changed files with 118 additions and 104 deletions

View File

@ -3,12 +3,13 @@ module Ast where
type CompilationUnit = [Class]
type DataType = String
type Identifier = String
type Modifier = String
data ParameterDeclaration = ParameterDeclaration DataType Identifier deriving (Show, Eq)
data VariableDeclaration = VariableDeclaration DataType Identifier (Maybe Expression) deriving (Show, Eq)
data Class = Class DataType [ConstructorDeclaration] [MethodDeclaration] [VariableDeclaration] deriving (Show, Eq)
data MethodDeclaration = MethodDeclaration DataType Identifier [ParameterDeclaration] Statement deriving (Show, Eq)
data ConstructorDeclaration = ConstructorDeclaration Identifier [ParameterDeclaration] Statement deriving (Show, Eq)
data VariableDeclaration = VariableDeclaration DataType Modifier Identifier (Maybe Expression) deriving (Show, Eq)
data Class = Class DataType Modifier [ConstructorDeclaration] [MethodDeclaration] [VariableDeclaration] deriving (Show, Eq)
data MethodDeclaration = MethodDeclaration DataType Modifier Identifier [ParameterDeclaration] Statement deriving (Show, Eq)
data ConstructorDeclaration = ConstructorDeclaration Modifier Identifier [ParameterDeclaration] Statement deriving (Show, Eq)
data Statement
= If Expression Statement (Maybe Statement)

View File

@ -93,14 +93,14 @@ qualifiedname : name DOT IDENTIFIER { BinaryOperation NameResolution $1 (Ref
simplename : IDENTIFIER { $1 }
classdeclaration : CLASS IDENTIFIER classbody { case $3 of (constructors, methods, fields) -> Class $2 constructors methods fields }
| modifiers CLASS IDENTIFIER classbody { case $4 of (constructors, methods, fields) -> Class $3 constructors methods fields }
classdeclaration : CLASS IDENTIFIER classbody { case $3 of (constructors, methods, fields) -> Class $2 "public" constructors methods fields }
| modifiers CLASS IDENTIFIER classbody { case $4 of (constructors, methods, fields) -> Class $3 (head $1) constructors methods fields }
classbody : LBRACKET RBRACKET { ([], [], []) }
| LBRACKET classbodydeclarations RBRACKET { $2 }
modifiers : modifier { }
| modifiers modifier { }
modifiers : modifier { [$1] }
| modifiers modifier { $1 ++ [$2] }
classbodydeclarations : classbodydeclaration {
case $1 of
@ -115,11 +115,11 @@ classbodydeclarations : classbodydeclaration {
((constructors, methods, fields), FieldDecls newFields) -> (constructors, methods, (fields ++ newFields))
}
modifier : PUBLIC { }
| PROTECTED { }
| PRIVATE { }
| STATIC { }
| ABSTRACT { }
modifier : PUBLIC { "public" }
| PROTECTED { "protected" }
| PRIVATE { "private" }
| STATIC { "static" }
| ABSTRACT { "abstract" }
classtype : classorinterfacetype { $1 }
@ -131,13 +131,13 @@ classorinterfacetype : simplename { $1 }
classmemberdeclaration : fielddeclaration { $1 }
| methoddeclaration { $1 }
constructordeclaration : constructordeclarator constructorbody { case $1 of (identifier, parameters) -> ConstructorDecl $ ConstructorDeclaration identifier parameters $2 }
| modifiers constructordeclarator constructorbody { case $2 of (identifier, parameters) -> ConstructorDecl $ ConstructorDeclaration identifier parameters $3 }
constructordeclaration : constructordeclarator constructorbody { case $1 of (identifier, parameters) -> ConstructorDecl $ ConstructorDeclaration "public" identifier parameters $2 }
| modifiers constructordeclarator constructorbody { case $2 of (identifier, parameters) -> ConstructorDecl $ ConstructorDeclaration (head $1) identifier parameters $3 }
fielddeclaration : type variabledeclarators SEMICOLON { FieldDecls $ map (convertDeclarator $1) $2 }
| modifiers type variabledeclarators SEMICOLON { FieldDecls $ map (convertDeclarator $2) $3 }
fielddeclaration : type variabledeclarators SEMICOLON { FieldDecls $ map (convertDeclarator "public" $1) $2 }
| modifiers type variabledeclarators SEMICOLON { FieldDecls $ map (convertDeclarator (head $1) $2) $3 }
methoddeclaration : methodheader methodbody { case $1 of (returnType, (name, (parameters, optionalparameters))) -> MethodDecl (MethodDeclarationWithOptionals returnType name parameters optionalparameters $2) }
methoddeclaration : methodheader methodbody { case $1 of (returnType, modifier, (name, (parameters, optionalparameters))) -> MethodDecl (MethodDeclarationWithOptionals returnType modifier name parameters optionalparameters $2) }
block : LBRACKET RBRACKET { Block [] }
| LBRACKET blockstatements RBRACKET { Block $2 }
@ -150,10 +150,10 @@ constructorbody : LBRACKET RBRACKET { Block [] }
| LBRACKET blockstatements RBRACKET { Block $2 }
-- | LBRACKET explicitconstructorinvocation blockstatements RBRACKET { }
methodheader : type methoddeclarator { ($1, $2) }
| modifiers type methoddeclarator { ($2, $3) }
| VOID methoddeclarator { ("void", $2) }
| modifiers VOID methoddeclarator { ("void", $3)}
methodheader : type methoddeclarator { ($1, "public", $2) }
| modifiers type methoddeclarator { ($2, head $1, $3) }
| VOID methoddeclarator { ("void", "public", $2) }
| modifiers VOID methoddeclarator { ("void", head $1, $3) }
type : primitivetype { $1 }
| referencetype { $1 }
@ -165,7 +165,7 @@ methodbody : block { $1 }
| SEMICOLON { Block [] }
blockstatements : blockstatement { $1 }
| blockstatements blockstatement { $1 ++ $2}
| blockstatements blockstatement { $1 ++ $2 }
formalandoptionalparameterlist : formalparameterlist { ($1, []) }
| formalparameterlist COMMA optionalparameterlist { ($1, $3) }
@ -193,7 +193,6 @@ primitivetype : BOOLEAN { "boolean" }
referencetype : classorinterfacetype { $1 }
variabledeclarator : variabledeclaratorid { Declarator $1 Nothing }
| variabledeclaratorid ASSIGN variableinitializer { Declarator $1 (Just $3) }
@ -225,7 +224,8 @@ expression : assignmentexpression { $1 }
integraltype : INT { "int" }
| CHAR { "char" }
localvariabledeclaration : type variabledeclarators { map LocalVariableDeclaration $ map (convertDeclarator $1) $2 }
localvariabledeclaration : type variabledeclarators { map (LocalVariableDeclaration . convertDeclarator "public" $1) $2 }
| modifiers type variabledeclarators { map (LocalVariableDeclaration . convertDeclarator (unwords $1) $2) $3 }
statementwithouttrailingsubstatement : block { [$1] }
| emptystatement { [] }
@ -402,28 +402,33 @@ data MemberDeclaration = MethodDecl MethodDeclarationWithOptionals
data Declarator = Declarator Identifier (Maybe Expression)
convertDeclarator :: DataType -> Declarator -> VariableDeclaration
convertDeclarator dataType (Declarator id assigment) = VariableDeclaration dataType id assigment
convertDeclarator :: Modifier -> DataType -> Declarator -> VariableDeclaration
convertDeclarator modifier dataType (Declarator id assignment) = VariableDeclaration dataType modifier id assignment
extractFunctionName :: Expression -> (Expression, Identifier)
extractFunctionName (BinaryOperation NameResolution exp (Reference functionname)) = (exp, functionname)
extractFunctionName (Reference functionname) = ((Reference "this"), functionname)
data OptionalParameter = OptionalParameter DataType Identifier Expression deriving (Show)
data MethodDeclarationWithOptionals = MethodDeclarationWithOptionals DataType Identifier [ParameterDeclaration] [OptionalParameter] Statement deriving (Show)
data MethodDeclarationWithOptionals = MethodDeclarationWithOptionals DataType String Identifier [ParameterDeclaration] [OptionalParameter] Statement deriving (Show)
convertMethodDeclarationWithOptionals :: MethodDeclarationWithOptionals -> [MethodDeclaration]
convertMethodDeclarationWithOptionals (MethodDeclarationWithOptionals returnType id param [] stmt) = [MethodDeclaration returnType id param stmt]
convertMethodDeclarationWithOptionals (MethodDeclarationWithOptionals returnType id param (opt : optRest) stmt) = generateHelperMethod returnType id param opt : convertMethodDeclarationWithOptionals (generateBaseMethod returnType id param opt optRest stmt)
convertMethodDeclarationWithOptionals (MethodDeclarationWithOptionals returnType modifier id param [] stmt) = [MethodDeclaration returnType modifier id param stmt]
convertMethodDeclarationWithOptionals (MethodDeclarationWithOptionals returnType modifier id param (opt : optRest) stmt) = generateHelperMethod returnType modifier id param opt : convertMethodDeclarationWithOptionals (generateBaseMethod returnType modifier id param opt optRest stmt)
convertOptionalParameter :: OptionalParameter -> ParameterDeclaration
convertOptionalParameter (OptionalParameter dtype id exp) = ParameterDeclaration dtype id
generateHelperMethod :: DataType -> Identifier -> [ParameterDeclaration] -> OptionalParameter -> MethodDeclaration
generateHelperMethod returnType methodName params (OptionalParameter dtype id exp) =
let references = ((map (\(ParameterDeclaration paramType ident) -> (Reference ident)) params) ++ [exp])
methodcall = (MethodCall (Reference "this") methodName references)
lastStatement = if returnType == "void" then StatementExpressionStatement methodcall else Return $ Just $ StatementExpressionExpression methodcall
in MethodDeclaration returnType methodName params $ Block [lastStatement]
generateBaseMethod :: DataType -> Identifier -> [ParameterDeclaration] -> OptionalParameter -> [OptionalParameter] -> Statement -> MethodDeclarationWithOptionals
generateBaseMethod returnType methodName params (OptionalParameter dtype id exp) optRest stmt = MethodDeclarationWithOptionals returnType methodName (params ++ [ParameterDeclaration dtype id]) optRest stmt
generateHelperMethod :: DataType -> Modifier -> Identifier -> [ParameterDeclaration] -> OptionalParameter -> MethodDeclaration
generateHelperMethod returnType modifier methodName params (OptionalParameter dtype id exp) =
let references = ((map (\(ParameterDeclaration paramType ident) -> (Reference ident)) params) ++ [exp])
methodcall = (MethodCall (Reference "this") methodName references)
lastStatement = if returnType == "void" then StatementExpressionStatement methodcall else Return $ Just $ StatementExpressionExpression methodcall
in MethodDeclaration returnType modifier methodName params $ Block [lastStatement]
generateBaseMethod :: DataType -> Modifier -> Identifier -> [ParameterDeclaration] -> OptionalParameter -> [OptionalParameter] -> Statement -> MethodDeclarationWithOptionals
generateBaseMethod returnType modifier methodName params (OptionalParameter dtype id exp) optRest stmt = MethodDeclarationWithOptionals returnType modifier methodName (params ++ [ParameterDeclaration dtype id]) optRest stmt
parseError :: ([Token], [String]) -> a
parseError (errortoken, expected) = error ("parse error on token: " ++ show errortoken ++ "\nexpected one of: " ++ show expected)

View File

@ -1,19 +1,19 @@
module Typecheck where
import Data.List (find)
import Data.Maybe
import Ast
typeCheckCompilationUnit :: CompilationUnit -> CompilationUnit
typeCheckCompilationUnit classes =
let
-- Helper function to add a default constructor if none are present
ensureDefaultConstructor :: Class -> Class
ensureDefaultConstructor (Class className constructors methods fields) =
ensureDefaultConstructor (Class className modifier constructors methods fields) =
let
defaultConstructor = ConstructorDeclaration className [] (Block [])
defaultConstructor = ConstructorDeclaration modifier className [] (Block [])
constructorsWithDefault = if null constructors then [defaultConstructor] else constructors
in Class className constructorsWithDefault methods fields
in Class className modifier constructorsWithDefault methods fields
-- Inject default constructors into all classes
classesWithDefaultConstructors = map ensureDefaultConstructor classes
@ -21,7 +21,7 @@ typeCheckCompilationUnit classes =
in map (`typeCheckClass` classesWithDefaultConstructors) classesWithDefaultConstructors
typeCheckClass :: Class -> [Class] -> Class
typeCheckClass (Class className constructors methods fields) classes =
typeCheckClass (Class className modifier 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.
@ -29,10 +29,10 @@ typeCheckClass (Class className constructors methods fields) classes =
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
in Class className modifier checkedConstructors checkedMethods checkedFields
typeCheckConstructorDeclaration :: ConstructorDeclaration -> [(Identifier, DataType)] -> [Class] -> ConstructorDeclaration
typeCheckConstructorDeclaration (ConstructorDeclaration name params body) symtab classes =
typeCheckConstructorDeclaration (ConstructorDeclaration modifier name params body) symtab classes =
let
constructorParams = [(identifier, dataType) | ParameterDeclaration dataType identifier <- params]
initialSymtab = symtab ++ constructorParams
@ -41,24 +41,23 @@ typeCheckConstructorDeclaration (ConstructorDeclaration name params body) symtab
bodyType = getTypeFromStmt checkedBody
in if name == className
then if bodyType == "void"
then ConstructorDeclaration name params checkedBody
then ConstructorDeclaration modifier 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 =
typeCheckMethodDeclaration (MethodDeclaration retType modifier name params body) symtab classes =
let
methodParams = [(identifier, dataType) | ParameterDeclaration dataType identifier <- params]
initialSymtab = ("thisMeth", retType) : symtab ++ methodParams
checkedBody = typeCheckStatement body initialSymtab classes
bodyType = getTypeFromStmt checkedBody
in if bodyType == retType || (bodyType == "void" && retType == "void") || (bodyType == "null" && isObjectType retType) || isSubtype bodyType retType classes
then MethodDeclaration retType name params checkedBody
then MethodDeclaration retType modifier name params checkedBody
else error $ "Method Declaration: Return type mismatch in method " ++ name ++ ": expected " ++ retType ++ ", found " ++ bodyType
typeCheckVariableDeclaration :: VariableDeclaration -> [(Identifier, DataType)] -> [Class] -> VariableDeclaration
typeCheckVariableDeclaration (VariableDeclaration dataType identifier maybeExpr) symtab classes =
typeCheckVariableDeclaration (VariableDeclaration dataType modifier identifier maybeExpr) symtab classes =
let
-- Ensure the type is valid (either a primitive type or a valid class name)
validType = dataType `elem` ["int", "boolean", "char"] || isUserDefinedClass dataType classes
@ -74,11 +73,10 @@ typeCheckVariableDeclaration (VariableDeclaration dataType identifier maybeExpr)
(False, _, _) -> error $ "Type '" ++ dataType ++ "' is not a valid type for variable '" ++ identifier ++ "'"
(_, True, _) -> error $ "Variable '" ++ identifier ++ "' is redefined in the same scope"
(_, _, Just t)
| t == "null" && isObjectType dataType -> VariableDeclaration dataType identifier checkedExprWithType
| t == "null" && isObjectType dataType -> VariableDeclaration dataType modifier identifier checkedExprWithType
| t /= dataType -> error $ "Type mismatch in declaration of '" ++ identifier ++ "': expected " ++ dataType ++ ", found " ++ t
| otherwise -> VariableDeclaration dataType identifier checkedExprWithType
(_, _, Nothing) -> VariableDeclaration dataType identifier checkedExprWithType
| otherwise -> VariableDeclaration dataType modifier identifier checkedExprWithType
(_, _, Nothing) -> VariableDeclaration dataType modifier identifier checkedExprWithType
-- ********************************** Type Checking: Expressions **********************************
@ -93,10 +91,10 @@ 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) ->
let fieldTypes = [dt | VariableDeclaration dt fieldId _ <- fields, fieldId == id]
Just (Class _ _ _ _ fields) ->
let fieldTypes = [dt | VariableDeclaration dt modifier 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
[fieldType] -> TypedExpression fieldType (BinaryOperation NameResolution (TypedExpression className (LocalVariable "this")) (TypedExpression fieldType (FieldVariable id)))
@ -167,35 +165,36 @@ typeCheckStatementExpression (Assignment ref expr) symtab classes =
else
error $ "Type mismatch in assignment to variable: expected " ++ type'' ++ ", found " ++ typeToAssign
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 _ 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
Just (Class _ modifier 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
matchConstructor (ConstructorDeclaration constructorModifier name params _)
| constructorModifier == "public" = fmap (\checkedArgs -> (ConstructorDeclaration constructorModifier name params (Block []), checkedArgs)) (sequence $ zipWith matchParams params args)
| constructorModifier == "private" = if checkAccess className (lookup "this" symtab)
then fmap (\checkedArgs -> (ConstructorDeclaration constructorModifier name params (Block []), checkedArgs)) (sequence $ zipWith matchParams params args)
else Nothing
| otherwise = Nothing
validConstructors = filter (\(params, _) -> length params == length args) $ mapMaybe matchConstructor constructors
validConstructors = filter (\(ConstructorDeclaration _ _ 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
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)] ->
[(ConstructorDeclaration _ _ params _, checkedArgs)] ->
TypedStatementExpression className (ConstructorCall className checkedArgs)
[] -> error mismatchDetails
_ -> error $ "Multiple matching constructors found for class '" ++ className ++ "' with given arguments."
@ -204,8 +203,8 @@ 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 (\(Class className _ _ _ _) -> className == objType) classes of
Just (Class className _ _ methods _) ->
let matchParams (ParameterDeclaration paramType _) arg =
let argTyped = typeCheckExpression arg symtab classes
argType = getTypeFromExpr argTyped
@ -215,27 +214,31 @@ typeCheckStatementExpression (MethodCall expr methodName args) symtab classes =
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
matchMethod (MethodDeclaration retType modifier name params _)
| modifier == "public" = fmap (\checkedArgs -> (MethodDeclaration retType modifier name params (Block []), checkedArgs)) (sequence $ zipWith matchParams params args)
| modifier == "private" = if checkAccess className (lookup "this" symtab)
then fmap (\checkedArgs -> (MethodDeclaration retType modifier name params (Block []), checkedArgs)) (sequence $ zipWith matchParams params args)
else Nothing
| otherwise = Nothing
validMethods = filter (\(MethodDeclaration _ name params _, _) -> name == methodName && length params == length args) $ mapMaybe matchMethod methods
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 ]
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)] ->
[(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."
typeCheckStatementExpression (PostIncrement expr) symtab classes =
let expr' = typeCheckExpression expr symtab classes
type' = getTypeFromExpr expr'
@ -292,8 +295,7 @@ typeCheckStatement (If cond thenStmt elseStmt) symtab classes =
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 modifier identifier maybeExpr)) symtab classes =
-- Check for redefinition in the current scope
if any ((== identifier) . snd) symtab
then error $ "Variable '" ++ identifier ++ "' is redefined in the same scope"
@ -307,11 +309,10 @@ typeCheckStatement (LocalVariableDeclaration (VariableDeclaration dataType ident
in case exprType of
Just t
| t == "null" && isObjectType dataType ->
TypedStatement dataType (LocalVariableDeclaration (VariableDeclaration dataType identifier checkedExprWithType))
TypedStatement dataType (LocalVariableDeclaration (VariableDeclaration dataType modifier identifier checkedExprWithType))
| t /= dataType -> error $ "Type mismatch in declaration of '" ++ identifier ++ "': expected " ++ dataType ++ ", found " ++ t
| otherwise -> TypedStatement dataType (LocalVariableDeclaration (VariableDeclaration dataType identifier checkedExprWithType))
Nothing -> TypedStatement dataType (LocalVariableDeclaration (VariableDeclaration dataType identifier checkedExpr))
| otherwise -> TypedStatement dataType (LocalVariableDeclaration (VariableDeclaration dataType modifier identifier checkedExprWithType))
Nothing -> TypedStatement dataType (LocalVariableDeclaration (VariableDeclaration dataType modifier identifier checkedExpr))
typeCheckStatement (While cond stmt) symtab classes =
let cond' = typeCheckExpression cond symtab classes
@ -326,7 +327,7 @@ typeCheckStatement (Block statements) symtab classes =
let
processStatements (accSts, currentSymtab, types) stmt =
case stmt of
LocalVariableDeclaration (VariableDeclaration dataType identifier maybeExpr) ->
LocalVariableDeclaration (VariableDeclaration dataType modifier identifier maybeExpr) ->
let
alreadyDefined = any (\(id, _) -> id == identifier) currentSymtab
newSymtab = if alreadyDefined
@ -355,7 +356,6 @@ typeCheckStatement (Block statements) symtab classes =
in TypedStatement blockType (Block checkedStatements)
typeCheckStatement (Return expr) symtab classes =
let methodReturnType = fromMaybe (error "Method return type not found in symbol table") (lookup "thisMeth" symtab)
expr' = case expr of
@ -369,7 +369,6 @@ typeCheckStatement (Return expr) symtab classes =
then TypedStatement returnType (Return expr')
else error $ "Return: Return type mismatch: expected " ++ methodReturnType ++ ", found " ++ returnType
typeCheckStatement (StatementExpressionStatement stmtExpr) symtab classes =
let stmtExpr' = typeCheckStatementExpression stmtExpr symtab classes
in TypedStatement (getTypeFromStmtExpr stmtExpr') (StatementExpressionStatement stmtExpr')
@ -385,7 +384,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"
@ -446,7 +445,6 @@ checkEqualityOperation op expr1' expr2' type1 type2
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
| type1 == "boolean" && type2 == "boolean" =
@ -457,12 +455,22 @@ 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) ->
let fieldTypes = [dt | VariableDeclaration dt id _ <- fields, id == ident2]
in case fieldTypes of
[resolvedType] -> TypedExpression resolvedType (BinaryOperation NameResolution expr1' (TypedExpression resolvedType (FieldVariable ident2)))
case find (\(Class className _ _ _ _) -> className == objType) classes of
Just (Class _ _ _ _ fields) ->
let fieldDetails = [(dt, mod) | VariableDeclaration dt mod id _ <- fields, id == ident2]
className = objType
in case fieldDetails of
[(resolvedType, mod)] ->
if mod == "public" || (mod == "private" && checkAccess objType (lookup "this" symtab))
then TypedExpression resolvedType (BinaryOperation NameResolution expr1' (TypedExpression resolvedType (FieldVariable ident2)))
else error $ "Field '" ++ ident2 ++ "' is private and cannot be accessed outside its class."
[] -> error $ "Field '" ++ ident2 ++ "' not found in class '" ++ objType ++ "'"
_ -> error $ "Ambiguous reference to field '" ++ ident2 ++ "' in class '" ++ objType ++ "'"
Nothing -> error $ "Class '" ++ objType ++ "' not found"
resolveNameResolution _ _ _ _ = error "Name resolution requires object reference and field name"
checkAccess :: DataType -> Maybe DataType -> Bool
checkAccess objType (Just thisClass) = objType == thisClass
checkAccess _ Nothing = False