Compare commits
3 Commits
3acbce8afc
...
9e43b015b7
Author | SHA1 | Date | |
---|---|---|---|
|
9e43b015b7 | ||
|
79a989eecf | ||
f02226bca8 |
@ -6,7 +6,8 @@ module ByteCode.ClassFile(
|
|||||||
Operation(..),
|
Operation(..),
|
||||||
serialize,
|
serialize,
|
||||||
emptyClassFile,
|
emptyClassFile,
|
||||||
opcodeEncodingLength
|
opcodeEncodingLength,
|
||||||
|
className
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.Word
|
import Data.Word
|
||||||
@ -96,6 +97,14 @@ emptyClassFile = ClassFile {
|
|||||||
attributes = []
|
attributes = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
className :: ClassFile -> String
|
||||||
|
className classFile = let
|
||||||
|
classInfo = (constantPool classFile)!!(fromIntegral (thisClass classFile))
|
||||||
|
in case classInfo of
|
||||||
|
Utf8Info className -> className
|
||||||
|
otherwise -> error ("expected Utf8Info but got: " ++ show otherwise)
|
||||||
|
|
||||||
|
|
||||||
opcodeEncodingLength :: Operation -> Word16
|
opcodeEncodingLength :: Operation -> Word16
|
||||||
opcodeEncodingLength Opiadd = 1
|
opcodeEncodingLength Opiadd = 1
|
||||||
opcodeEncodingLength Opisub = 1
|
opcodeEncodingLength Opisub = 1
|
||||||
|
@ -16,6 +16,7 @@ import Ast
|
|||||||
import Data.Char
|
import Data.Char
|
||||||
import Data.List
|
import Data.List
|
||||||
import Data.Word
|
import Data.Word
|
||||||
|
import Data.Maybe (mapMaybe)
|
||||||
|
|
||||||
type ClassFileBuilder a = a -> ClassFile -> ClassFile
|
type ClassFileBuilder a = a -> ClassFile -> ClassFile
|
||||||
type Assembler a = ([ConstantInfo], [Operation], [String]) -> a -> ([ConstantInfo], [Operation], [String])
|
type Assembler a = ([ConstantInfo], [Operation], [String]) -> a -> ([ConstantInfo], [Operation], [String])
|
||||||
@ -308,6 +309,28 @@ injectDefaultConstructor pre
|
|||||||
| any (\(MethodDeclaration _ name _ _) -> name == "<init>") pre = pre
|
| any (\(MethodDeclaration _ name _ _) -> name == "<init>") pre = pre
|
||||||
| otherwise = pre ++ [MethodDeclaration "void" "<init>" [] (TypedStatement "void" (Block []))]
|
| otherwise = pre ++ [MethodDeclaration "void" "<init>" [] (TypedStatement "void" (Block []))]
|
||||||
|
|
||||||
|
injectFieldInitializers :: String -> [VariableDeclaration] -> [MethodDeclaration] -> [MethodDeclaration]
|
||||||
|
injectFieldInitializers classname vars pre = let
|
||||||
|
initializers = mapMaybe (\(variable) -> case variable of
|
||||||
|
VariableDeclaration dtype name (Just initializer) -> Just (
|
||||||
|
TypedStatement dtype (
|
||||||
|
StatementExpressionStatement (
|
||||||
|
TypedStatementExpression dtype (
|
||||||
|
Assignment
|
||||||
|
(TypedExpression dtype (BinaryOperation NameResolution (TypedExpression classname (LocalVariable "this")) (TypedExpression dtype (FieldVariable name))))
|
||||||
|
initializer
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
otherwise -> Nothing
|
||||||
|
) vars
|
||||||
|
in
|
||||||
|
map (\(method) -> case method of
|
||||||
|
MethodDeclaration "void" "<init>" params (TypedStatement "void" (Block statements)) -> MethodDeclaration "void" "<init>" params (TypedStatement "void" (Block (initializers ++ statements)))
|
||||||
|
otherwise -> method
|
||||||
|
) pre
|
||||||
|
|
||||||
|
|
||||||
classBuilder :: ClassFileBuilder Class
|
classBuilder :: ClassFileBuilder Class
|
||||||
classBuilder (Class name methods fields) _ = let
|
classBuilder (Class name methods fields) _ = let
|
||||||
@ -332,10 +355,11 @@ classBuilder (Class name methods fields) _ = let
|
|||||||
}
|
}
|
||||||
|
|
||||||
methodsWithInjectedConstructor = injectDefaultConstructor methods
|
methodsWithInjectedConstructor = injectDefaultConstructor methods
|
||||||
|
methodsWithInjectedInitializers = injectFieldInitializers name fields methodsWithInjectedConstructor
|
||||||
|
|
||||||
classFileWithFields = foldr fieldBuilder nakedClassFile fields
|
classFileWithFields = foldr fieldBuilder nakedClassFile fields
|
||||||
classFileWithMethods = foldr methodBuilder classFileWithFields methodsWithInjectedConstructor
|
classFileWithMethods = foldr methodBuilder classFileWithFields methodsWithInjectedInitializers
|
||||||
classFileWithAssembledMethods = foldr methodAssembler classFileWithMethods methodsWithInjectedConstructor
|
classFileWithAssembledMethods = foldr methodAssembler classFileWithMethods methodsWithInjectedInitializers
|
||||||
in
|
in
|
||||||
classFileWithAssembledMethods
|
classFileWithAssembledMethods
|
||||||
|
|
||||||
|
20
src/Main.hs
20
src/Main.hs
@ -9,12 +9,22 @@ import ByteCode.ClassFile
|
|||||||
import Data.ByteString (pack, writeFile)
|
import Data.ByteString (pack, writeFile)
|
||||||
|
|
||||||
main = do
|
main = do
|
||||||
|
-- read source code from disk
|
||||||
file <- readFile "Testklasse.java"
|
file <- readFile "Testklasse.java"
|
||||||
|
|
||||||
|
-- parse source code
|
||||||
let untypedAST = parse $ alexScanTokens file
|
let untypedAST = parse $ alexScanTokens file
|
||||||
let typedAST = head (typeCheckCompilationUnit untypedAST)
|
-- typecheck AST
|
||||||
--print typedAST
|
let typedAST = (typeCheckCompilationUnit untypedAST)
|
||||||
let abstractClassFile = classBuilder typedAST emptyClassFile
|
-- assemble classes
|
||||||
let assembledClassFile = pack (serialize abstractClassFile)
|
let assembledClasses = map (\(typedClass) -> classBuilder typedClass emptyClassFile) typedAST
|
||||||
|
|
||||||
Data.ByteString.writeFile "Testklasse.class" assembledClassFile
|
-- write class files to disk
|
||||||
|
mapM_ (\(classFile) -> let
|
||||||
|
fileContent = pack (serialize classFile)
|
||||||
|
fileName = (className classFile) ++ ".class"
|
||||||
|
in Data.ByteString.writeFile fileName fileContent
|
||||||
|
) assembledClasses
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ module Typecheck where
|
|||||||
import Data.List (find)
|
import Data.List (find)
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Ast
|
import Ast
|
||||||
import Debug.Trace (trace)
|
|
||||||
|
|
||||||
typeCheckCompilationUnit :: CompilationUnit -> CompilationUnit
|
typeCheckCompilationUnit :: CompilationUnit -> CompilationUnit
|
||||||
typeCheckCompilationUnit classes = map (`typeCheckClass` classes) classes
|
typeCheckCompilationUnit classes = map (`typeCheckClass` classes) classes
|
||||||
@ -10,13 +9,12 @@ typeCheckCompilationUnit classes = map (`typeCheckClass` classes) classes
|
|||||||
typeCheckClass :: Class -> [Class] -> Class
|
typeCheckClass :: Class -> [Class] -> Class
|
||||||
typeCheckClass (Class className methods fields) classes =
|
typeCheckClass (Class className methods fields) classes =
|
||||||
let
|
let
|
||||||
-- Fields dont need to be added to the symtab because they are looked upon automatically under this if its not a declared local variable
|
-- Fields and methods dont need to be added to the symtab because they are looked upon automatically under "this"
|
||||||
-- TODO: Maybe remove method entries from the symbol table? I dont think we need them but if yes the next line would be
|
-- if its not a declared local variable. Also shadowing wouldnt be possible then.
|
||||||
-- initalSymTab = ("this", className) : methodEntries
|
|
||||||
-- methodEntries = [(methodName, className) | MethodDeclaration _ methodName _ _ <- methods]
|
|
||||||
initalSymTab = [("this", className)]
|
initalSymTab = [("this", className)]
|
||||||
checkedMethods = map (\method -> typeCheckMethodDeclaration method initalSymTab classes) methods
|
checkedMethods = map (\method -> typeCheckMethodDeclaration method initalSymTab classes) methods
|
||||||
in Class className checkedMethods fields
|
checkedFields = map (\field -> typeCheckVariableDeclaration field initalSymTab classes) fields
|
||||||
|
in Class className checkedMethods checkedFields
|
||||||
|
|
||||||
typeCheckMethodDeclaration :: MethodDeclaration -> [(Identifier, DataType)] -> [Class] -> MethodDeclaration
|
typeCheckMethodDeclaration :: MethodDeclaration -> [(Identifier, DataType)] -> [Class] -> MethodDeclaration
|
||||||
typeCheckMethodDeclaration (MethodDeclaration retType name params body) symtab classes =
|
typeCheckMethodDeclaration (MethodDeclaration retType name params body) symtab classes =
|
||||||
@ -29,8 +27,24 @@ typeCheckMethodDeclaration (MethodDeclaration retType name params body) symtab c
|
|||||||
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 $ "Method Declaration: Return type mismatch in method " ++ name ++ ": expected " ++ retType ++ ", found " ++ bodyType
|
||||||
|
|
||||||
-- TODO: It could be that TypeCheckVariableDeclaration is missing. If it comes up -> just check wether the type is correct. The maybe expression needs to be
|
typeCheckVariableDeclaration :: VariableDeclaration -> [(Identifier, DataType)] -> [Class] -> VariableDeclaration
|
||||||
-- checked as well. Also if its a class type, check wether the class exists.
|
typeCheckVariableDeclaration (VariableDeclaration dataType 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
|
||||||
|
-- Ensure no redefinition in the same scope
|
||||||
|
redefined = any ((== identifier) . snd) symtab
|
||||||
|
-- Type check the initializer expression if it exists
|
||||||
|
checkedExpr = fmap (\expr -> typeCheckExpression expr symtab classes) maybeExpr
|
||||||
|
exprType = fmap getTypeFromExpr checkedExpr
|
||||||
|
in case (validType, redefined, exprType) of
|
||||||
|
(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 checkedExpr
|
||||||
|
| t /= dataType -> error $ "Type mismatch in declaration of '" ++ identifier ++ "': expected " ++ dataType ++ ", found " ++ t
|
||||||
|
| otherwise -> VariableDeclaration dataType identifier checkedExpr
|
||||||
|
(_, _, Nothing) -> VariableDeclaration dataType identifier checkedExpr
|
||||||
|
|
||||||
-- ********************************** Type Checking: Expressions **********************************
|
-- ********************************** Type Checking: Expressions **********************************
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user