51 Commits

Author SHA1 Message Date
mo 8416f1b9c8 fix classfile & constpool errors 2026-05-08 15:53:06 +02:00
timb 245e70169e fix tiny bug && cleanup 2026-05-06 17:21:03 +02:00
LeonProgrammiert 8497b949a4 Updated FieldDecl in Semantik Checker 2026-05-06 16:58:36 +02:00
timb 251d8daa0f feat: add optional assignment on field declaration 2026-05-06 15:04:49 +02:00
LeonProgrammiert 1ad70c0b00 Merge branch 'main' of ssh://gitea.hb.dhbw-stuttgart.de:2222/mo/Compoiler 2026-05-06 14:12:34 +02:00
LeonProgrammiert 7f6e8f5cb5 Fix
Co-authored-by: Copilot <copilot@github.com>
2026-05-06 14:12:27 +02:00
mo 88e9b545eb chore: remove src/Main 2026-05-06 14:05:15 +02:00
timb 11fda5eaa7 feat: add visibility in scanner and lexer (no logic changes yet); rename
folders to support haskell module structure
2026-05-06 14:01:12 +02:00
Konstantin Fastovski 1a153200fa update workflow in Main.hs 2026-05-06 13:51:03 +02:00
LeonProgrammiert 815388b842 Merge branch 'main' of ssh://gitea.hb.dhbw-stuttgart.de:2222/mo/Compoiler 2026-05-06 13:28:00 +02:00
LeonProgrammiert 22e238317c Added classes, methods and fields 2026-05-06 13:27:58 +02:00
Konstantin Fastovski 5d48938221 get shit to compile 2026-05-06 13:23:06 +02:00
Konstantin Fastovski 44c1032e15 extract shared types for ClassFile and ConstPool 2026-05-06 13:15:26 +02:00
Konstantin Fastovski 842149be5e remove unused stuff from Main 2026-05-06 12:49:38 +02:00
Konstantin Fastovski 48b6270fd0 fix search functions 2026-05-06 12:27:48 +02:00
LeonProgrammiert b668fc94ff Merge branch 'main' of ssh://gitea.hb.dhbw-stuttgart.de:2222/mo/Compoiler 2026-05-06 11:31:55 +02:00
LeonProgrammiert a829814cd6 Finished typeCheckStmt, typeCheckExpr and typeCheckStmtExpr
Co-authored-by: Copilot <copilot@github.com>
2026-05-06 11:31:52 +02:00
Konstantin Fastovski 66e0efba81 update generateClassFile signature and import generateConstPool 2026-05-06 11:27:13 +02:00
Konstantin Fastovski 31248ecc97 get shit to compile and update Main.hs 2026-05-06 11:21:17 +02:00
Silas ac60aeba3e fix: new method name 2026-05-06 11:14:50 +02:00
Silas b833d6ebe0 fix: cleaner code 2026-05-06 11:13:02 +02:00
Konstantin Fastovski 3f0e00ac63 fix: pass typed Class into generateClassfile 2026-05-06 11:00:50 +02:00
Konstantin Fastovski 4370ec3c1f add makeMethods function 2026-05-06 10:58:35 +02:00
Konstantin Fastovski 162bed1b12 fix: add constPool to necessary makefunction signatures 2026-05-06 10:58:35 +02:00
Silas e91fac7060 add ConstPool building method 2026-05-06 10:57:24 +02:00
LeonProgrammiert 0eb8c64fc6 Merge branch 'main' of ssh://gitea.hb.dhbw-stuttgart.de:2222/mo/Compoiler 2026-05-06 10:43:23 +02:00
LeonProgrammiert f96627fcc9 Typechecked WhileStmt 2026-05-06 10:43:20 +02:00
Vectabyte b5ddd0b5c9 Merge branch 'main' of https://gitea.hb.dhbw-stuttgart.de/mo/Compoiler 2026-05-06 10:38:14 +02:00
Vectabyte 531f5d7f7f fix: fixed missing test cases in main
Co-authored-by: Copilot <copilot@github.com>
2026-05-06 10:36:57 +02:00
Konstantin Fastovski 5a16f08ccc add generateClassFile funciton skeleton 2026-05-06 10:29:39 +02:00
Konstantin Fastovski e5eb3a9792 fix: remove Type from end of TypedClass, etc. 2026-05-06 10:21:19 +02:00
LeonProgrammiert 8e3b978ba4 Merge branch 'main' of ssh://gitea.hb.dhbw-stuttgart.de:2222/mo/Compoiler 2026-05-06 09:44:42 +02:00
LeonProgrammiert 1b2ae2ae22 Typed classes 2026-05-06 09:44:40 +02:00
Vectabyte d9b7ba40bc abc files
Co-authored-by: Copilot <copilot@github.com>
2026-05-05 00:41:07 +02:00
Vectabyte 03e0571f4e Moved more files around 2026-05-05 00:11:28 +02:00
Vectabyte 4978de5220 Moved Files 2026-05-05 00:09:31 +02:00
Vectabyte b80b71936a Merge pull request 'Initial Test Cases' (#3) from feature/test-cases into main
Reviewed-on: #3
2026-05-04 17:18:39 +00:00
Vectabyte 22de50b77e Merge branch 'main' into feature/test-cases 2026-05-04 17:18:29 +00:00
Vectabyte e84550025c Initial Test Cases 2026-05-04 19:16:34 +02:00
Silas 8470ab2d94 new ConstPool first steps 2026-04-29 16:29:28 +02:00
Konstantin Fastovski 1ad8aface9 Merge branch 'feature/create-codegen-skeleton-and-add-'return-int'-as-example' 2026-04-29 16:26:16 +02:00
Konstantin Fastovski daa741a090 Implement Classfile datatype, Lowerer and (untested) Serializer 2026-04-29 16:25:01 +02:00
Konstantin Fastovski e81ceb454f feat: moved ai generated files to subdirectory 2026-04-29 15:07:00 +02:00
LeonProgrammiert 87db9f6e23 Merge branch 'main' of ssh://gitea.hb.dhbw-stuttgart.de:2222/mo/Compoiler 2026-04-29 14:21:25 +02:00
LeonProgrammiert a447fad905 feat: updated AST, TAST and getType Methods 2026-04-29 14:21:22 +02:00
Felix b964bdda1b Merge branch 'feature/operators' 2026-04-29 11:28:04 +02:00
timb 2f908d1df1 feat: update typed abstract syntax 2026-04-29 11:13:37 +02:00
Silas 40e84e45f6 Merge branch 'main' of https://gitea.hb.dhbw-stuttgart.de/mo/Compoiler 2026-04-29 11:04:47 +02:00
tym 56d43bac38 Merge pull request 'Added operators' (#2) from feature/operators into main
Reviewed-on: #2
2026-04-29 08:05:11 +00:00
Silas 9230042b25 feat: add Konstantenpool 2026-04-29 09:29:12 +02:00
Konstantin Fastovski 76c4cb677c feat: codegen skeleton 2026-04-27 16:03:04 +02:00
47 changed files with 1749 additions and 89 deletions
+2
View File
@@ -31,3 +31,5 @@ cabal.project.local~
hie.yaml
src/Grammar/Scanner.hs
src/Grammar/Parser.hs
src/Grammar/Parser.info
src/Main
+81
View File
@@ -0,0 +1,81 @@
# Compilerbau
# Prüfungsleistung
## Spezifikation
Deklarationen: • Σ: EingabeAlphabet
- JC: Menge aller syntaktisch korrekten JavaKlassen mit folgenden Einschränkungen:
- keine generischen Klassen
- keine abstrakten Klassen
- keine Vererbung
- keine Interfaces
- keine Threads
- keine Exceptions
- keine Arrays
- als Basistypen sind nur int, boolean und char zugelassen
- keine Packages
- keine Imports
- keine LambdaExpressions
- BC: Menge aller BytecodeFiles
Eingabe: p∈Σ∗
Vorbedingung: ∅
Ausgabe: bc∈BC∗∪{error}
Nachbedingungen: • falls p∈(JC), so ist bc∈(BC)und p wird nach bc übersetzt wie es durch die Sprache Java definiert ist.
- falls p !∈(JC), so ist bc=error.
## Vorgehen
Arbeitsteam:Der JavaCompiler wird in einem Team von 5-7 Personen erstellt. Das
Team wird nochmals unterteilt:
- Gemeinsame Aufgabe
GIT-Repository: Einrichten eines GIT-Repositories auf den DHBW GITEA-Server
Abstrakte Syntax: Aufbau der abstrakten Syntax aus dem Parsetree
Dokumentation: Erstellen der Dokumentation
- Scannen/Parsen/Grammatik (12 Personen)
Scannen: alexFile oder Scanner von Hand programmieren
Grammatik (nur bei Bearbeitung durch 2 Personen):Erstellen einer Mini-Java-Grammatik an Hand der Spezifikation
Parsen:Erstellen des happyFiles oder des KombinatorParsers und Aufbau des abstrakten Syntaxbaums
- Semantische Analyse:Typisierung der abstrakten Syntax(1 Person)
Typisierung: Typisierung der abstrakten Syntax
- Codeerzeugung (2-3 Personen):
- Aufbau eines abstrakten ClassFiles (1 Person)
- Konstantenpool (1 Person)
- Nur bei Bearbeitung durch 3 Personen:Umwandlung des ClassFiles in Bytecode (1 Person)
- Tester (1 Person)
- Testsuite von JavaFiles, die alle implementierten Features abdecken.
- Händische Übersetzung aller Java-Files der Testsuite in die abstrakte Syntax (als TestEingaben für den Typ-Checker)
- Händische Übersetzung aller Testfälle der abstrakten Syntax in getypte abstrakte Syntax (als TestEingaben für den Code-Generierer).
- Händische Übersetzung aller Testfälle der getypten abstrakten Syntax in abstrakten Bytecode.
- Automatische Tests, die die jeweiligen Testsuite mit den implementierten Funktionen des Teams vergleichen
## Prüfungsleistung
Die Arbeitsleistung wird bewertet an Hand
- des Gesamtergebnis des Teams
- der Arbeitsleistung jeder/s Studierenden
an Hand folgender Kriterien:
- Projektergebnis
- wöchentlicher Projektfortschritt
- Mitarbeit im Team
Das Projektergebnis muss folgendes beinhalten:
- (Kurz)dokumentation aus der hervorgeht welche Leistung der jeweiligen Studierende erbrachten hat.
- Im Teilprojekt muss folgendes vorliegen:
- Eine Testsuite von JavaProgrammen, für die der Compilerteil funktioniert.
- Präsentation des Programms an Hand der erstellen Testsuites.
- Durchgehendes Beispiel, fur das der gesamte Compiler funktioniert.
- Abgabetermin: Letzte Semesterwoche
+179
View File
@@ -0,0 +1,179 @@
module Codegen.ClassFile where
import Codegen.ConstPool
( generateConstPool,
lookupClassIndex,
lookupUtf8Index,
)
import Codegen.Types
import Data.Word (Word16, Word32, Word8)
import Grammar.AST (Type)
import Grammar.TAST
( TypedClass (..),
TypedFieldDecl (..),
TypedMethodDecl (..),
)
data ClassFile = ClassFile
{ magic :: Magic,
minorVersion :: MinorVersion,
majorVersion :: MajorVersion,
constantPool :: CP_Infos,
accessFlags :: AccessFlags,
thisClass :: ThisClass,
superClass :: SuperClass,
interfaces :: Interfaces,
fields :: Field_Infos,
methods :: Method_Infos,
attributes :: Attribute_Infos
}
deriving (Show, Eq)
-- Counts are derivable from list lengths, so no need to store them separately
type Index = Int
type Interfaces = [Word16] -- constant pool indices
type Field_Infos = [Field_Info]
type Method_Infos = [Method_Info]
type Attribute_Infos = [Attribute_Info]
newtype Magic = Magic Word32 deriving (Show, Eq) -- always 0xCAFEBABE
newtype MinorVersion = MinorVersion Int deriving (Show, Eq)
newtype MajorVersion = MajorVersion Int deriving (Show, Eq) -- 52 = Java 8
newtype AccessFlags = AccessFlags [AccessFlag] deriving (Show, Eq)
newtype ThisClass = ThisClass Index deriving (Show, Eq)
newtype SuperClass = SuperClass Index deriving (Show, Eq)
data AccessFlag
= ACC_PUBLIC
| ACC_PRIVATE
| ACC_PROTECTED
| ACC_STATIC
| ACC_FINAL
| ACC_SUPER -- always set on classes
| ACC_INTERFACE
| ACC_ABSTRACT
| ACC_SYNTHETIC
| ACC_ENUM
deriving (Show, Eq)
data Field_Info = Field_Info
{ fieldAccessFlags :: AccessFlags,
fieldNameIndex :: Index,
fieldDescIndex :: Index,
fieldAttributes :: Attribute_Infos
}
deriving (Show, Eq)
data Method_Info = Method_Info
{ methodAccessFlags :: AccessFlags,
methodNameIndex :: Index,
methodDescIndex :: Index,
methodAttributes :: Attribute_Infos
}
deriving (Show, Eq)
data Attribute_Info
= Code_Attribute
{ codeNameIndex :: Index,
maxStack :: Int,
maxLocals :: Int,
codeBody :: [Word8], -- raw serialized bytecode
exceptionTable :: [ExceptionEntry],
codeAttributes :: Attribute_Infos
}
| Generic_Attribute
{ attrNameIndex :: Index,
attrData :: [Word8]
}
deriving (Show, Eq)
data ExceptionEntry = ExceptionEntry
{ startPc :: Int,
endPc :: Int,
handlerPc :: Int,
catchType :: Index -- 0 means catches all (finally)
}
deriving (Show, Eq)
generateClassFile :: TypedClass -> ClassFile
generateClassFile cls =
let constPool = makeConstpool cls
in ClassFile
{ magic = Magic 0xCAFEBABE,
minorVersion = MinorVersion 0,
majorVersion = MajorVersion 48,
constantPool = constPool,
accessFlags = makeAccessFlags,
thisClass = makeThisClass constPool cls,
superClass = makeSuperClass constPool,
interfaces = makeInterface,
fields = makeFields constPool cls,
methods = makeMethods constPool cls,
attributes = makeAttributes constPool
}
makeConstpool :: TypedClass -> CP_Infos
makeConstpool = generateConstPool
makeAccessFlags :: AccessFlags
makeAccessFlags = AccessFlags [ACC_PUBLIC, ACC_SUPER]
makeThisClass :: CP_Infos -> TypedClass -> ThisClass
makeThisClass constPool (Class name _ _) =
ThisClass (lookupClassIndex constPool name)
makeSuperClass :: CP_Infos -> SuperClass
makeSuperClass constPool =
SuperClass (lookupClassIndex constPool "java/lang/Object")
makeInterface :: Interfaces
makeInterface = []
makeFields :: CP_Infos -> TypedClass -> Field_Infos
makeFields constPool (Class _ fields _) = map (makeField constPool) fields
makeField :: CP_Infos -> TypedFieldDecl -> Field_Info
makeField constPool (Field fieldType name _) =
Field_Info
{ fieldAccessFlags = AccessFlags [ACC_PUBLIC],
fieldNameIndex = lookupUtf8Index constPool name,
fieldDescIndex = lookupUtf8Index constPool (typeDescriptor fieldType),
fieldAttributes = []
}
makeAttributes :: CP_Infos -> Attribute_Infos
makeAttributes _ = []
makeMethods :: CP_Infos -> TypedClass -> Method_Infos
makeMethods constPool (Class _ _ methods) = map (makeMethod constPool) methods
makeMethod :: CP_Infos -> TypedMethodDecl -> Method_Info
makeMethod constPool (Method returnType name params _body) =
Method_Info
{ methodAccessFlags = AccessFlags [ACC_PUBLIC],
methodNameIndex = lookupUtf8Index constPool name,
methodDescIndex = lookupUtf8Index constPool (methodDescriptor params returnType),
methodAttributes = []
}
methodDescriptor :: [(Type, String)] -> Type -> String
methodDescriptor params returnType =
"(" ++ concatMap (typeDescriptor . fst) params ++ ")" ++ typeDescriptor returnType
typeDescriptor :: Type -> String
typeDescriptor t = case t of
"int" -> "I"
"boolean" -> "Z"
"char" -> "C"
"void" -> "V"
_ -> "L" ++ t ++ ";"
+232
View File
@@ -0,0 +1,232 @@
module Codegen.ConstPool where
import Codegen.Types
import Data.List (isSuffixOf)
import qualified Grammar.TAST as TAST
------------------------------------------------------------------------
-- Recursive extraction of Tags from an already-built pool
------------------------------------------------------------------------
extractTags :: CP_Infos -> [Tag]
extractTags [] = []
extractTags (x : xs) = tag_cp x : extractTags xs
countEntries :: CP_Infos -> Int
countEntries [] = 0
countEntries (_ : xs) = 1 + countEntries xs
------------------------------------------------------------------------
-- Helper constructors
------------------------------------------------------------------------
mkClassInfo :: IndexConstantPool -> String -> CP_Info
mkClassInfo = ClassInfo TagClass
mkFieldRefInfo :: IndexConstantPool -> IndexConstantPool -> String -> CP_Info
mkFieldRefInfo = FieldRefInfo TagFieldRef
mkMethodRefInfo :: IndexConstantPool -> IndexConstantPool -> String -> CP_Info
mkMethodRefInfo = MethodRefInfo TagMethodRef
mkStringInfo :: IndexConstantPool -> String -> CP_Info
mkStringInfo = StringInfo TagString
mkIntegerInfo :: Int -> String -> CP_Info
mkIntegerInfo = IntegerInfo TagInteger
mkNameAndTypeInfo :: IndexConstantPool -> IndexConstantPool -> String -> CP_Info
mkNameAndTypeInfo = NameAndTypeInfo TagNameAndType
mkUtf8Info :: String -> String -> CP_Info
mkUtf8Info str = Utf8Info TagUtf8 (length str) str
------------------------------------------------------------------------
-- Tag utilities
------------------------------------------------------------------------
tagToByte :: Tag -> Int
tagToByte TagClass = 7
tagToByte TagFieldRef = 9
tagToByte TagMethodRef = 10
tagToByte TagString = 8
tagToByte TagInteger = 3
tagToByte TagNameAndType = 12
tagToByte TagUtf8 = 1
tagToString :: Tag -> String
tagToString TagClass = "Class"
tagToString TagFieldRef = "FieldRef"
tagToString TagMethodRef = "MethodRef"
tagToString TagString = "String"
tagToString TagInteger = "Integer"
tagToString TagNameAndType = "NameAndType"
tagToString TagUtf8 = "Utf8"
------------------------------------------------------------------------
-- Descriptor helpers
------------------------------------------------------------------------
typeToDescriptor :: String -> String
typeToDescriptor "int" = "I"
typeToDescriptor "boolean" = "Z"
typeToDescriptor "char" = "C"
typeToDescriptor "void" = "V"
typeToDescriptor t = "L" ++ t ++ ";"
methodDescriptor :: [(String, String)] -> String -> String
methodDescriptor params ret =
"(" ++ paramsDesc params ++ ")" ++ typeToDescriptor ret
where
paramsDesc [] = ""
paramsDesc ((pty, _) : ps) = typeToDescriptor pty ++ paramsDesc ps
------------------------------------------------------------------------
-- Recursive traversal of TAST to collect CP_Info entries
------------------------------------------------------------------------
collectFromExprs :: [TAST.TypedExpr] -> CP_Infos
collectFromExprs [] = []
collectFromExprs (e : es) = collectFromTypedExpr e ++ collectFromExprs es
collectFromTypedExpr :: TAST.TypedExpr -> CP_Infos
collectFromTypedExpr (TAST.Integer n _) =
[mkIntegerInfo (fromIntegral n) ("int " ++ show n)]
collectFromTypedExpr (TAST.Bool _ _) =
[]
collectFromTypedExpr (TAST.Char c _) =
[mkIntegerInfo (fromEnum c) ("char " ++ show c)]
collectFromTypedExpr (TAST.Null _) =
[]
collectFromTypedExpr (TAST.This _) =
[]
collectFromTypedExpr (TAST.LocalOrFieldVar _ _) =
[]
collectFromTypedExpr (TAST.InstVar obj name typ) =
collectFromTypedExpr obj
++ [ mkUtf8Info name ("field name: " ++ name),
mkUtf8Info (typeToDescriptor typ) ("field descriptor: " ++ typeToDescriptor typ),
mkNameAndTypeInfo 0 0 ("field NameAndType: " ++ name),
mkFieldRefInfo 0 0 ("field ref: " ++ name)
]
collectFromTypedExpr (TAST.Unary _ e _) =
collectFromTypedExpr e
collectFromTypedExpr (TAST.Binary _ e1 e2 _) =
collectFromTypedExpr e1 ++ collectFromTypedExpr e2
collectFromTypedExpr (TAST.StmtExprExpr se _) =
collectFromTypedStmtExpr se
collectFromTypedStmtExpr :: TAST.TypedStmtExpr -> CP_Infos
collectFromTypedStmtExpr (TAST.Assign lhs rhs _) =
collectFromTypedExpr lhs ++ collectFromTypedExpr rhs
collectFromTypedStmtExpr (TAST.New t args _) =
collectFromExprs args
++ [ mkUtf8Info t ("class name: " ++ t),
mkClassInfo 0 ("class: " ++ t)
]
collectFromTypedStmtExpr (TAST.MethodCall obj name args retTyp) =
collectFromTypedExpr obj
++ collectFromExprs args
++ [ mkUtf8Info name ("method name: " ++ name),
mkUtf8Info (typeToDescriptor retTyp) ("method ret desc: " ++ typeToDescriptor retTyp),
mkNameAndTypeInfo 0 0 ("method NameAndType: " ++ name),
mkMethodRefInfo 0 0 ("method ref: " ++ name)
]
collectFromStmts :: [TAST.TypedStmt] -> CP_Infos
collectFromStmts [] = []
collectFromStmts (s : ss) = collectFromTypedStmt s ++ collectFromStmts ss
collectFromTypedStmt :: TAST.TypedStmt -> CP_Infos
collectFromTypedStmt (TAST.Block stmts _) =
collectFromStmts stmts
collectFromTypedStmt (TAST.Return (Just e) _) =
collectFromTypedExpr e
collectFromTypedStmt (TAST.Return Nothing _) =
[]
collectFromTypedStmt (TAST.While cond body _) =
collectFromTypedExpr cond ++ collectFromTypedStmt body
collectFromTypedStmt (TAST.LocalVarDecl _ _ _) =
[]
collectFromTypedStmt (TAST.If cond then_ mElse _) =
collectFromTypedExpr cond
++ collectFromTypedStmt then_
++ collectFromMaybeStmt mElse
collectFromTypedStmt (TAST.StmtExprStmt se _) =
collectFromTypedStmtExpr se
collectFromMaybeStmt :: Maybe TAST.TypedStmt -> CP_Infos
collectFromMaybeStmt Nothing = []
collectFromMaybeStmt (Just s) = collectFromTypedStmt s
collectFromFields :: [TAST.TypedFieldDecl] -> CP_Infos
collectFromFields [] = []
collectFromFields (TAST.Field fType fName _: fs ) =
[ mkUtf8Info fName ("field: " ++ fName),
mkUtf8Info (typeToDescriptor fType) ("field desc: " ++ fType)
]
++ collectFromFields fs
collectFromMethods :: [TAST.TypedMethodDecl] -> CP_Infos
collectFromMethods [] = []
collectFromMethods (TAST.Method mType mName params body : ms) =
[ mkUtf8Info mName ("method: " ++ mName),
mkUtf8Info (methodDescriptor params mType) ("method desc: " ++ mName)
]
++ collectFromTypedStmt body
++ collectFromMethods ms
------------------------------------------------------------------------
-- Build the complete constant pool for a class
------------------------------------------------------------------------
generateConstPool :: TAST.TypedClass -> CP_Infos
generateConstPool (TAST.Class cName fields methods) =
[ mkUtf8Info cName ("this class: " ++ cName),
mkUtf8Info "java/lang/Object" "super class",
mkClassInfo 0 ("this class: " ++ cName),
mkClassInfo 0 "super class"
]
++ collectFromFields fields
++ collectFromMethods methods
lookupClassIndex :: CP_Infos -> String -> IndexConstantPool
lookupClassIndex pool name =
case findIndex matches pool of
Just i -> i
Nothing -> 0
where
matches entry = case entry of
ClassInfo {desc = descVal}
| name == "java/lang/Object" -> descVal == "super class" || isSuffixOf name descVal
| otherwise -> descVal == name || isSuffixOf name descVal
_ -> False
lookupUtf8Index :: CP_Infos -> String -> IndexConstantPool
lookupUtf8Index pool value =
case findIndex matches pool of
Just i -> i
Nothing -> 0
where
matches entry = case entry of
Utf8Info {cad_cp = cadVal, desc = descVal} -> cadVal == value || isSuffixOf value descVal
_ -> False
lookupNameAndTypeIndex :: CP_Infos -> String -> IndexConstantPool
lookupNameAndTypeIndex pool name =
case findIndex matches pool of
Just i -> i
Nothing -> lookupUtf8Index pool name
where
matches entry = case entry of
NameAndTypeInfo {desc = descVal} -> descVal == name || isSuffixOf name descVal
_ -> False
findIndex :: (CP_Info -> Bool) -> CP_Infos -> Maybe IndexConstantPool
findIndex predicate pool = go 1 pool
where
go _ [] = Nothing
go i (x : xs)
| predicate x = Just i
| otherwise = go (i + 1) xs
+139
View File
@@ -0,0 +1,139 @@
module Codegen.Lowerer where
import Codegen.ClassFile
import Codegen.Types (CP_Info (Utf8Info), CP_Infos)
import Data.Word (Word8)
import Numeric (showHex)
data Instruction
= ALoad Int
| ALoad0
| ILoad Int
| AStore Int
| IStore Int
| GetField Int
| PutField Int
| InvokeSpecial Int
| InvokeVirtual Int
| InvokeStatic Int
| IConst Int
| AConstNull
| Ldc Int
| IAdd
| ISub
| IMul
| IfEq Int
| IfNe Int
| Goto Int
| Return
| IReturn
| AReturn
deriving (Show, Eq)
data BtcLine = BtcLine
{ lineNumber :: Integer,
instruction :: Instruction
}
deriving (Show, Eq)
newtype BtcProgram = BtcProgram [BtcLine]
deriving (Show, Eq)
-- Lower a full ClassFile into a BtcProgram per method
-- Returns one BtcProgram per method, paired with its name
lowerClassFile :: ClassFile -> [(String, BtcProgram)]
lowerClassFile cf =
[ (methodName cf m, lowerMethod m)
| m <- methods cf
]
-- Extract method name from the constant pool
methodName :: ClassFile -> Method_Info -> String
methodName cf m =
case lookupPool (constantPool cf) (methodNameIndex m) of
Just (Utf8Info {cad_cp = name}) -> name
_ -> "<unknown>"
-- Lower a single method's Code attribute into a BtcProgram
lowerMethod :: Method_Info -> BtcProgram
lowerMethod m =
case findCodeAttr (methodAttributes m) of
Just attr -> lowerCode attr
Nothing -> BtcProgram [] -- abstract/native method, no body
findCodeAttr :: Attribute_Infos -> Maybe Attribute_Info
findCodeAttr = foldr step Nothing
where
step attr@Code_Attribute {} _ = Just attr
step _ acc = acc
-- Reconstruct BtcProgram from raw code bytes
-- This is the tricky direction — bytes → instructions
lowerCode :: Attribute_Info -> BtcProgram
lowerCode attr = BtcProgram (decodeBytes 0 (codeBody attr))
decodeBytes :: Int -> [Word8] -> [BtcLine]
decodeBytes _ [] = []
decodeBytes pos (b : bs) = case b of
0x2A -> line ALoad0 pos 1 bs
0x2B -> line (ALoad 1) pos 1 bs
0x2C -> line (ALoad 2) pos 1 bs
0x2D -> line (ALoad 3) pos 1 bs
0x19 -> withByte bs pos $ \n rest -> line (ALoad n) pos 2 rest
0x1A -> line (ILoad 0) pos 1 bs
0x1B -> line (ILoad 1) pos 1 bs
0x1C -> line (ILoad 2) pos 1 bs
0x1D -> line (ILoad 3) pos 1 bs
0x15 -> withByte bs pos $ \n rest -> line (ILoad n) pos 2 rest
0x4B -> line (AStore 0) pos 1 bs
0x4C -> line (AStore 1) pos 1 bs
0x4D -> line (AStore 2) pos 1 bs
0x4E -> line (AStore 3) pos 1 bs
0x3A -> withByte bs pos $ \n rest -> line (AStore n) pos 2 rest
0x3B -> line (IStore 0) pos 1 bs
0x3C -> line (IStore 1) pos 1 bs
0x3D -> line (IStore 2) pos 1 bs
0x3E -> line (IStore 3) pos 1 bs
0x36 -> withByte bs pos $ \n rest -> line (IStore n) pos 2 rest
0xB4 -> withIndex bs pos $ \i rest -> line (GetField i) pos 3 rest
0xB5 -> withIndex bs pos $ \i rest -> line (PutField i) pos 3 rest
0xB7 -> withIndex bs pos $ \i rest -> line (InvokeSpecial i) pos 3 rest
0xB6 -> withIndex bs pos $ \i rest -> line (InvokeVirtual i) pos 3 rest
0xB8 -> withIndex bs pos $ \i rest -> line (InvokeStatic i) pos 3 rest
0x02 -> line (IConst (-1)) pos 1 bs
0x03 -> line (IConst 0) pos 1 bs
0x04 -> line (IConst 1) pos 1 bs
0x05 -> line (IConst 2) pos 1 bs
0x06 -> line (IConst 3) pos 1 bs
0x07 -> line (IConst 4) pos 1 bs
0x08 -> line (IConst 5) pos 1 bs
0x01 -> line AConstNull pos 1 bs
0x12 -> withByte bs pos $ \i rest -> line (Ldc i) pos 2 rest
0x13 -> withIndex bs pos $ \i rest -> line (Ldc i) pos 3 rest
0x60 -> line IAdd pos 1 bs
0x64 -> line ISub pos 1 bs
0x68 -> line IMul pos 1 bs
0x99 -> withIndex bs pos $ \i rest -> line (IfEq i) pos 3 rest
0x9A -> withIndex bs pos $ \i rest -> line (IfNe i) pos 3 rest
0xA7 -> withIndex bs pos $ \i rest -> line (Goto i) pos 3 rest
0xB1 -> line Return pos 1 bs
0xAC -> line IReturn pos 1 bs
0xB0 -> line AReturn pos 1 bs
unknown -> error $ "Unknown opcode 0x" ++ showHex unknown "" ++ " at position " ++ show pos
-- Helpers
line :: Instruction -> Int -> Int -> [Word8] -> [BtcLine]
line instr pos size rest = BtcLine (toInteger pos) instr : decodeBytes (pos + size) rest
withByte :: [Word8] -> Int -> (Int -> [Word8] -> [BtcLine]) -> [BtcLine]
withByte (b : rest) _ f = f (fromIntegral b) rest
withByte [] pos _ = error $ "Unexpected end of bytecode at " ++ show pos
withIndex :: [Word8] -> Int -> (Int -> [Word8] -> [BtcLine]) -> [BtcLine]
withIndex (hi : lo : rest) _ f = f (fromIntegral hi * 256 + fromIntegral lo) rest
withIndex _ pos _ = error $ "Unexpected end of bytecode at " ++ show pos
lookupPool :: CP_Infos -> Index -> Maybe CP_Info
lookupPool pool i
| i < 1 || i > length pool = Nothing
| otherwise = Just (pool !! (i - 1)) -- pool is 1-indexed
+79
View File
@@ -0,0 +1,79 @@
module Codegen.Serializer where
import Data.Bits (shiftR, (.&.))
import Data.Word (Word8)
-- Split a 16-bit Int into two bytes (big-endian), as JVM expects
indexBytes :: Int -> [Word8]
indexBytes n =
[ fromIntegral (n `shiftR` 8 .&. 0xFF),
fromIntegral (n .&. 0xFF)
]
serializeInstruction :: Instruction -> [Word8]
serializeInstruction instr = case instr of
-- aload_<n>: 0x2A0x2D for 03, else wide form 0x19 <index>
ALoad 0 -> [0x2A]
ALoad 1 -> [0x2B]
ALoad 2 -> [0x2C]
ALoad 3 -> [0x2D]
ALoad n -> [0x19, fromIntegral n]
ALoad0 -> [0x2A] -- shorthand for ALoad 0
-- iload_<n>: 0x1A0x1D for 03, else wide form 0x15 <index>
ILoad 0 -> [0x1A]
ILoad 1 -> [0x1B]
ILoad 2 -> [0x1C]
ILoad 3 -> [0x1D]
ILoad n -> [0x15, fromIntegral n]
-- astore_<n>: 0x4B0x4E for 03, else 0x3A <index>
AStore 0 -> [0x4B]
AStore 1 -> [0x4C]
AStore 2 -> [0x4D]
AStore 3 -> [0x4E]
AStore n -> [0x3A, fromIntegral n]
-- istore_<n>: 0x3B0x3E for 03, else 0x36 <index>
IStore 0 -> [0x3B]
IStore 1 -> [0x3C]
IStore 2 -> [0x3D]
IStore 3 -> [0x3E]
IStore n -> [0x36, fromIntegral n]
-- Field access: opcode + 2-byte constant pool index
GetField i -> 0xB4 : indexBytes i
PutField i -> 0xB5 : indexBytes i
-- Method invocation: opcode + 2-byte constant pool index
InvokeSpecial i -> 0xB7 : indexBytes i
InvokeVirtual i -> 0xB6 : indexBytes i
InvokeStatic i -> 0xB8 : indexBytes i
-- iconst_<n>: -1 through 5 have dedicated opcodes
IConst (-1) -> [0x02]
IConst 0 -> [0x03]
IConst 1 -> [0x04]
IConst 2 -> [0x05]
IConst 3 -> [0x06]
IConst 4 -> [0x07]
IConst 5 -> [0x08]
IConst n -> error $ "IConst out of range (-1..5): " ++ show n
AConstNull -> [0x01]
-- ldc: 1-byte index (use ldc_w 0x13 for large pool indices > 255)
Ldc i
| i <= 255 -> [0x12, fromIntegral i]
| otherwise -> 0x13 : indexBytes i -- ldc_w
-- Arithmetic
IAdd -> [0x60]
ISub -> [0x64]
IMul -> [0x68]
-- Control flow: 2-byte signed branch offset
IfEq offset -> 0x99 : indexBytes offset
IfNe offset -> 0x9A : indexBytes offset
Goto offset -> 0xA7 : indexBytes offset
-- Return
Return -> [0xB1]
IReturn -> [0xAC]
AReturn -> [0xB0]
-- Serialize a full program — concatenate all instruction bytes
serializeProgram :: BtcProgram -> [Word8]
serializeProgram (BtcProgram lines) =
concatMap (serializeInstruction . instruction) lines
+57
View File
@@ -0,0 +1,57 @@
module Codegen.Types where
type IndexConstantPool = Int
type CP_Infos = [CP_Info]
data CP_Info
= ClassInfo
{ tag_cp :: Tag,
index_cp :: IndexConstantPool,
desc :: String
}
| FieldRefInfo
{ tag_cp :: Tag,
index_name_cp :: IndexConstantPool,
index_nameandtype_cp :: IndexConstantPool,
desc :: String
}
| MethodRefInfo
{ tag_cp :: Tag,
index_name_cp :: IndexConstantPool,
index_nameandtype_cp :: IndexConstantPool,
desc :: String
}
| StringInfo
{ tag_cp :: Tag,
index_cp :: IndexConstantPool,
desc :: String
}
| IntegerInfo
{ tag_cp :: Tag,
numi_cp :: Int,
desc :: String
}
| NameAndTypeInfo
{ tag_cp :: Tag,
index_name_cp :: IndexConstantPool,
index_descr_cp :: IndexConstantPool,
desc :: String
}
| Utf8Info
{ tag_cp :: Tag,
tam_cp :: Int,
cad_cp :: String,
desc :: String
}
deriving (Eq, Show)
data Tag
= TagClass
| TagFieldRef
| TagMethodRef
| TagString
| TagInteger
| TagNameAndType
| TagUtf8
deriving (Eq, Ord, Show)
+2 -2
View File
@@ -6,7 +6,7 @@ type Program = [Class]
data Class = Class Type [FieldDecl] [MethodDecl] deriving (Show, Eq)
data FieldDecl = Field Type String deriving (Show, Eq)
data FieldDecl = Field Type String (Maybe Expr) deriving (Show, Eq)
data MethodDecl = Method Type String [(Type, String)] Stmt deriving (Show, Eq)
@@ -19,7 +19,7 @@ data Expr
| Integer Integer
| Bool Bool
| Char Char
| Jnull
| Null
| StmtExprExpr StmtExpr
deriving (Show, Eq)
+19 -5
View File
@@ -10,6 +10,8 @@ import Grammar.AST
%error { parseError }
%token
public { TokenVisibility Public }
private { TokenVisibility Private }
class { TokenClass }
void { TokenVoid }
return { TokenReturn }
@@ -68,15 +70,26 @@ Classes : Classes ClassDecl { $2 : $1 }
| ClassDecl { [$1] }
ClassDecl : class id '{' Decls '}' { Class $2 (fst $4) (snd $4) }
| public class id '{' Decls '}' { Class $3 (fst $5) (snd $5) }
| private class id '{' Decls '}' { Class $3 (fst $5) (snd $5) }
Decls : FieldDecl Decls { ($1 : fst $2, snd $2) }
| MethodDecl Decls { (fst $2, $1 : snd $2) }
| {- leer -} { ([], []) }
FieldDecl : Type id ';' { Field $1 $2 }
FieldDecl : Type id ';' { Field $1 $2 Nothing }
| public Type id ';' { Field $2 $3 Nothing }
| private Type id ';' { Field $2 $3 Nothing }
| Type id '=' Expr ';' { Field $1 $2 (Just $4) }
| public Type id '=' Expr ';' { Field $2 $3 (Just $5) }
| private Type id '=' Expr ';' { Field $2 $3 (Just $5) }
MethodDecl : Type id '(' Params ')' Block { Method $1 $2 $4 $6 }
| void id '(' Params ')' Block { Method "void" $2 $4 $6 }
MethodDecl : Type id '(' Params ')' Block { Method $1 $2 $4 $6 }
| public Type id '(' Params ')' Block { Method $2 $3 $5 $7 }
| private Type id '(' Params ')' Block { Method $2 $3 $5 $7 }
| void id '(' Params ')' Block { Method "void" $2 $4 $6 }
| public void id '(' Params ')' Block { Method "void" $3 $5 $7 }
| private void id '(' Params ')' Block { Method "void" $3 $5 $7 }
Params : ParamList { $1 }
| {- leer -} { [] }
@@ -97,7 +110,8 @@ Stmts : Stmts Stmt { $2 : $1 }
| {- leer -} { [] }
Stmt : Block { $1 }
| return Expr ';' { Return $2 }
| return Expr ';' { Return (Just $2) }
| return ';' { Return Nothing }
| while '(' Expr ')' Stmt { While $3 $5 }
| if '(' Expr ')' Stmt else Stmt { If $3 $5 (Just $7) }
| if '(' Expr ')' Stmt { If $3 $5 Nothing }
@@ -141,5 +155,5 @@ ExprList : Expr { [$1] }
{
parseError :: [Token] -> a
parseError tokens = error $ "Lexical error or syntax error at: " ++ show tokens
parseError tokens = error $ "abc Lexical error or syntax error at: " ++ show tokens
}
+9 -1
View File
@@ -14,6 +14,8 @@ tokens :-
"/*"([^\*]|(\*+([^\*\/])))*\*+\/ ; -- multi-line comments
-- key words
public { \_ -> TokenVisibility Public }
private { \_ -> TokenVisibility Private }
class { \_ -> TokenClass }
void { \_ -> TokenVoid }
return { \_ -> TokenReturn }
@@ -49,8 +51,14 @@ tokens :-
$alpha [$alpha $digit]* { \s -> TokenIdent s }
{
data Visibility
= Public
| Private
deriving (Eq, Show)
data Token
= TokenClass
= TokenVisibility Visibility
| TokenClass
| TokenStatic
| TokenVoid
| TokenReturn
+30 -6
View File
@@ -1,13 +1,37 @@
module Grammar.TAST where
import Grammar.AST
import Grammar.AST (BinaryOperator, Type, UnaryOperator)
data TypedExpr = TypedExpr Expr Type
data TypedClass = Class Type [TypedFieldDecl] [TypedMethodDecl] deriving (Show, Eq)
data TypedStmtExpr = TypedStmtExpr StmtExpr Type
data TypedFieldDecl = Field Type String (Maybe TypedExpr) deriving (Show, Eq)
data TypedStmt = TypedStmt Stmt Type
data TypedMethodDecl = Method Type String [(Type, String)] TypedStmt deriving (Show, Eq)
data TypedBinaryOperator = TypedBinaryOperator BinaryOperator Type
data TypedExpr
= This Type
| LocalOrFieldVar String Type
| InstVar TypedExpr String Type
| Unary UnaryOperator TypedExpr Type
| Binary BinaryOperator TypedExpr TypedExpr Type
| Integer Integer Type
| Bool Bool Type
| Char Char Type
| Null Type
| StmtExprExpr TypedStmtExpr Type
deriving (Show, Eq)
data TypedUnaryOperator = TypedUnaryOperator UnaryOperator Type
data TypedStmtExpr
= Assign TypedExpr TypedExpr Type
| New Type [TypedExpr] Type
| MethodCall TypedExpr String [TypedExpr] Type
deriving (Show, Eq)
data TypedStmt
= Block [TypedStmt] Type
| Return (Maybe TypedExpr) Type
| While TypedExpr TypedStmt Type
| LocalVarDecl Type String Type
| If TypedExpr TypedStmt (Maybe TypedStmt) Type
| StmtExprStmt TypedStmtExpr Type
deriving (Show, Eq)
+40 -5
View File
@@ -1,10 +1,45 @@
module Main where
import Grammar.AST
import Grammar.TAST
import Typecheck.SemanticChecker
import Codegen.ClassFile (ClassFile, generateClassFile)
import Grammar.AST (Program)
import Grammar.Parser (parse)
import Grammar.Scanner (Token, alexScanTokens)
import Grammar.TAST (TypedClass (Class))
import System.Environment (getArgs)
import Typecheck.SemanticChecker (typeCheckClass)
main :: IO ()
main = do
let t = getTypeFromExpr $ TypedExpr This "myType"
putStrLn t
args <- getArgs
case args of
[path] -> runPipeline path
_ -> putStrLn "Usage: compoiler <input.java>"
runPipeline :: FilePath -> IO ()
runPipeline path = do
source <- readFile path
let tokens = scan source
let ast = parseProgram tokens
let typedClasses = typeCheckProgram ast
let classFiles = map generateClassFile typedClasses
reportSuccess ast typedClasses classFiles
scan :: String -> [Token]
scan = alexScanTokens
parseProgram :: [Token] -> Program
parseProgram = parse
typeCheckProgram :: Program -> [TypedClass]
typeCheckProgram classes =
map (\cls -> typeCheckClass cls [] classes) classes
reportSuccess :: Program -> [TypedClass] -> [ClassFile] -> IO ()
reportSuccess ast typed classFiles = do
putStrLn ("Parsed classes: " ++ show (length ast))
putStrLn ("Typed classes: " ++ show (length typed))
putStrLn ("Generated class files: " ++ show (length classFiles))
putStrLn ("Class names: " ++ unwords (map className typed))
className :: TypedClass -> String
className (Class name _ _) = name
@@ -0,0 +1,48 @@
module Testsuite.AstFiles.ArithmeticTestAST (expectedAST) where
import Grammar.AST
expectedAST =
[ Class
"ArithmeticTest"
[]
[ Method
"int"
"basic"
[("int", "a"), ("int", "b"), ("int", "c")]
( Block
[ Return
( Just
( Binary
Subtract
(Binary Add (LocalOrFieldVar "a") (LocalOrFieldVar "b"))
( Binary
Modulo
( Binary
Divide
(Binary Multiply (LocalOrFieldVar "c") (LocalOrFieldVar "a"))
(LocalOrFieldVar "b")
)
(LocalOrFieldVar "c")
)
)
)
]
),
Method
"boolean"
"logic"
[("boolean", "a"), ("boolean", "b"), ("boolean", "c")]
( Block
[ Return
( Just
( Binary
And
(Unary Not (LocalOrFieldVar "a"))
(Binary Or (LocalOrFieldVar "c") (LocalOrFieldVar "b"))
)
)
]
)
]
]
+3
View File
@@ -0,0 +1,3 @@
[
Class "EmptyTest" [] []
]
+95
View File
@@ -0,0 +1,95 @@
module Main where
import Codegen.Assemble (assembleProgram)
import Codegen.ClassFile (ClassFile(..), ClassFileMethod(..))
import Codegen.Errors (CodegenError(..))
import Codegen.IR
( ClassDef(..)
, Expr(..)
, Literal(..)
, MethodDef(..)
, Program(..)
, Stmt(..)
, Type(..)
)
import Data.List (isInfixOf)
import System.Exit (exitFailure)
main :: IO ()
main = do
testVerticalSliceReturnInt
testUnsupportedStatementFails
putStrLn "Codegen smoke tests passed."
testVerticalSliceReturnInt :: IO ()
testVerticalSliceReturnInt =
case assembleProgram program of
Left err -> failTest ("Expected successful assemble, got error: " ++ show err)
Right [classFile] -> do
assertEqual "class name" "Main" (cfClassName classFile)
case cfMethods classFile of
[method] -> do
assertEqual "method name" "constSeven" (cfmName method)
assertEqual "method descriptor" "()I" (cfmDescriptor method)
assertEqual "method instructions" ["iconst 7", "ireturn"] (cfmCode method)
_ -> failTest "Expected exactly one method in generated class"
Right _ -> failTest "Expected exactly one generated class"
where
program =
Program
[ ClassDef
{ className = "Main"
, classFields = []
, classMethods =
[ MethodDef
{ methodName = "constSeven"
, methodParams = []
, methodReturnType = IntType
, methodBody = Return (Literal (IntLit 7))
}
]
}
]
testUnsupportedStatementFails :: IO ()
testUnsupportedStatementFails =
case assembleProgram program of
Left (LoweringError msg)
| "Unsupported statement" `isInfixOf` msg -> pure ()
| otherwise -> failTest ("Lowering failed with unexpected message: " ++ msg)
Left err -> failTest ("Expected LoweringError, got: " ++ show err)
Right _ -> failTest "Expected lowering to fail for unsupported statement"
where
program =
Program
[ ClassDef
{ className = "Main"
, classFields = []
, classMethods =
[ MethodDef
{ methodName = "badMethod"
, methodParams = []
, methodReturnType = IntType
, methodBody = While (Literal (BoolLit True)) (Return (Literal (IntLit 0)))
}
]
}
]
assertEqual :: (Eq a, Show a) => String -> a -> a -> IO ()
assertEqual label expected actual
| expected == actual = pure ()
| otherwise =
failTest
( "Assertion failed for "
++ label
++ ": expected "
++ show expected
++ ", got "
++ show actual
)
failTest :: String -> IO ()
failTest message = do
putStrLn ("[FAIL] " ++ message)
exitFailure
@@ -0,0 +1,14 @@
module TestSuite.ExecutableTests.ArithmeticASTTest where
import Grammar.Parser
import Grammar.Scanner
import Testsuite.AstFiles.ArithmeticTestAST (expectedAST)
mainArithemticASTTest :: IO ()
mainArithemticASTTest = do
let javaFilePath = "Testsuite/javaFiles/ArithmeticTest.java"
java <- readFile javaFilePath
let actualAST = parse . alexScanTokens $ java
print $ actualAST == expectedAST
print actualAST
print expectedAST
@@ -0,0 +1 @@
[("basic",BtcProgram [BtcLine {lineNumber = 0, instruction = ILoad 0},BtcLine {lineNumber = 1, instruction = ILoad 1},BtcLine {lineNumber = 2, instruction = IAdd},BtcLine {lineNumber = 3, instruction = ILoad 2},BtcLine {lineNumber = 4, instruction = ILoad 0},BtcLine {lineNumber = 5, instruction = IMul},BtcLine {lineNumber = 6, instruction = ILoad 1},BtcLine {lineNumber = 7, instruction = ISub},BtcLine {lineNumber = 8, instruction = ILoad 2},BtcLine {lineNumber = 9, instruction = ISub},BtcLine {lineNumber = 10, instruction = IReturn}]),("logic",BtcProgram [BtcLine {lineNumber = 0, instruction = ILoad 0},BtcLine {lineNumber = 1, instruction = ILoad 2},BtcLine {lineNumber = 2, instruction = ILoad 1},BtcLine {lineNumber = 3, instruction = IAdd},BtcLine {lineNumber = 4, instruction = IAdd},BtcLine {lineNumber = 5, instruction = IReturn}])]
+1
View File
@@ -0,0 +1 @@
[]
@@ -0,0 +1,40 @@
public class AllSyntaxTest {
public int x;
private static int counter = 0;
public AllSyntaxTest(int initial) {
x = initial;
counter = counter + 1;
}
public int inc() {
x = x + 1;
return x;
}
public int inc(int delta) {
x = x + delta;
return x;
}
public static int sumUpTo(int n) {
int s = 0;
for (int i = 1; i <= n; i = i + 1) {
s = s + i;
}
return s;
}
public boolean predicate(char c) {
return c == 'a' || c == 'b';
}
public static void main(String[] args) {
AllSyntaxTest a = new AllSyntaxTest(0);
System.out.println(a.inc()); // 1
System.out.println(a.inc(4)); // 5
System.out.println(AllSyntaxTest.sumUpTo(5)); // 15
System.out.println(a.predicate('a') ? 1 : 0); // ternary used to produce int
System.out.println(counter); // static field access
}
}
@@ -0,0 +1,10 @@
public class ArithmeticTest {
public int basic(int a, int b, int c) {
return a + b - (((c * a) / b) % c);
}
public boolean logic(boolean a, boolean b, boolean c) {
return !a && (c || b);
}
}
@@ -0,0 +1,27 @@
public class CombinedControlTest {
int field;
public CombinedControlTest(int v) {
field = v;
}
public int compute() {
int i = 0;
int acc = 0;
while (i < field) {
if (i % 2 == 0) {
acc = acc + i;
} else {
acc = acc - i;
}
i = i + 1;
}
return acc;
}
public static void main(String[] args) {
CombinedControlTest t = new CombinedControlTest(6);
// Computation: 0 -1 +2 -3 +4 -5 = -3
System.out.println(t.compute());
}
}
@@ -0,0 +1,12 @@
public class ConstructorOverloadTest {
public int a = 42;
ConstructorOverloadTest() {
// nothing here, so a will assume the default value 42.
}
ConstructorOverloadTest(int a) {
this.a = a;
}
}
@@ -0,0 +1,9 @@
public class ConstructorTest
{
public int a = -1;
public ConstructorTest(int initial_value)
{
a = initial_value;
}
}
+4
View File
@@ -0,0 +1,4 @@
public class EmptyTest
{
}
@@ -0,0 +1,18 @@
public class ExpressionTest {
public static boolean shortCircuit(int a, int b) {
// short-circuit: when a==0 the right side must not be evaluated
boolean res = (a != 0) && ((10 / a) > b);
return res;
}
public static char charArithmetic(char c, int offset) {
char d = (char)(c + offset);
return d;
}
public static void main(String[] args) {
System.out.println(shortCircuit(2, 1)); // true
System.out.println(shortCircuit(0, 1)); // false (right side not evaluated)
System.out.println(charArithmetic('A', 2)); // 'C'
}
}
+5
View File
@@ -0,0 +1,5 @@
public class FieldsTest {
public int a;
public int b = 42;
}
+17
View File
@@ -0,0 +1,17 @@
public class IfTest {
public static boolean ifElseTest(int x) {
if (x < 0) {
return false;
} else if (x == 0) {
return true;
} else {
return x > 10;
}
}
public static void main(String[] args) {
System.out.println(ifElseTest(-1)); // false
System.out.println(ifElseTest(0)); // true
System.out.println(ifElseTest(11)); // true
}
}
+19
View File
@@ -0,0 +1,19 @@
public class LoopTest {
public int factorial(int n)
{
int tally = 1;
for(int i = 1; i <= n; i++)
{
tally *= i;
}
return tally;
}
int weirdFor() {
int k = 0;
for (; k < 5; k++) {
}
return k;
}
}
+83
View File
@@ -0,0 +1,83 @@
// compile all test files using:
// ls Test/JavaSources/*.java | grep -v ".*Main.java" | xargs -I {} cabal run compiler {}
// compile (in project root) using:
// pushd Test/JavaSources; javac -g:none Main.java; popd
// afterwards, run using
// java -ea -cp Test/JavaSources/ Main
public class Main {
public static void main(String[] args)
{
EmptyTest empty = new EmptyTest();
FieldsTest fields = new FieldsTest();
ConstructorTest constructor = new ConstructorTest(42);
ArithmeticTest arithmetic = new ArithmeticTest();
MultipleClassesTest multipleClasses = new MultipleClassesTest();
RecursionTest recursion = new RecursionTest(10);
MaliciousTest malicious = new MaliciousTest();
LoopTest loop = new LoopTest();
MethodOverloadTest overload = new MethodOverloadTest();
ShenaniganceTest shenanigance = new ShenaniganceTest();
AllSyntaxTest allSyntax = new AllSyntaxTest(0);
CombinedControlTest combined = new CombinedControlTest(6);
ExpressionTest expression = new ExpressionTest();
IfTest ifTest = new IfTest();
MultiClassTest multiClass = new MultiClassTest();
SingletonTest singleton = new SingletonTest();
WhileTest whileTest = new WhileTest();
// constructing a basic class works
System.out.println("test empty non-null. Expected: non-null, Real: " + (empty != null));
// initializers (and default initializers to 0/null) work
System.out.println("test fields initializers. Expected: a==0 and b==42, Real: a=" + fields.a + " and b=" + fields.b);
// constructor parameters override initializers
System.out.println("test constructor a. Expected result: 42, Real result: " + constructor.a);
// basic arithmetics
System.out.println("test arithmetic basic(1,2,3). Expected: 2, Real: " + arithmetic.basic(1, 2, 3));
// we have boolean logic as well
System.out.println("test arithmetic logic(false,false,true). Expected: true, Real: " + arithmetic.logic(false, false, true));
// multiple classes within one file work. Referencing another classes fields/methods works.
System.out.println("test multiple classes field. Expected: 42, Real: " + multipleClasses.a.a);
// self-referencing classes work.
System.out.println("test nested child value. Expected: 5, Real: " + recursion.child.child.child.child.child.value);
// self-referencing methods work.
System.out.println("test recursion fibonacci(15). Expected: 610, Real: " + recursion.fibonacci(15));
System.out.println("test factorial(5). Expected: 120, Real: " + loop.factorial(5));
System.out.println("test weirdFor(). Expected: 5, Real: " + loop.weirdFor());
// methods with the same name but different parameters work
System.out.println("test MethodOverload(). Expected: 42, Real: " + overload.MethodOverload());
System.out.println("test MethodOverload(15). Expected: 42+15, Real: " + overload.MethodOverload(15));
// constructor overloading works, too.
System.out.println("test ctor overload default. Expected: 42, Real: " + (new ConstructorOverloadTest()).a);
System.out.println("test ctor overload with arg. Expected: 12, Real: " + (new ConstructorOverloadTest(12)).a);
// intentionally dodgy expressions work
System.out.println("test assignNegativeIncrement(42). Expected: -42, Real: " + malicious.assignNegativeIncrement(42));
System.out.println("test tripleAddition(1,2,3). Expected: 6, Real: " + malicious.tripleAddition(1, 2, 3));
for(int i = 0; i < 3; i++)
{
System.out.println("test cursedFormatting i=" + i + ". Expected: " + i + ", Real: " + malicious.cursedFormatting(i));
}
// other syntactic sugar
System.out.println("test shenanigance.testAssignment(). Expected: 5, Real: " + shenanigance.testAssignment());
System.out.println("test shenanigance.divEqual(). Expected: " + (234_343_000 / 4) + ", Real: " + shenanigance.divEqual());
// AllSyntaxTest tests
System.out.println("test AllSyntaxTest.inc(). Expected: 1, Real: " + allSyntax.inc());
System.out.println("test AllSyntaxTest.inc(4). Expected: 5, Real: " + allSyntax.inc(4));
System.out.println("test AllSyntaxTest.sumUpTo(5). Expected: 15, Real: " + AllSyntaxTest.sumUpTo(5));
System.out.println("test AllSyntaxTest.predicate('a'). Expected: true, Real: " + allSyntax.predicate('a'));
// CombinedControlTest tests
System.out.println("test CombinedControlTest.compute(). Expected: -3, Real: " + combined.compute());
// ExpressionTest tests
System.out.println("test ExpressionTest.shortCircuit(2, 1). Expected: true, Real: " + ExpressionTest.shortCircuit(2, 1));
System.out.println("test ExpressionTest.shortCircuit(0, 1). Expected: false, Real: " + ExpressionTest.shortCircuit(0, 1));
System.out.println("test ExpressionTest.charArithmetic('A', 2). Expected: C, Real: " + ExpressionTest.charArithmetic('A', 2));
// IfTest tests
System.out.println("test IfTest.ifElseTest(-1). Expected: false, Real: " + IfTest.ifElseTest(-1));
System.out.println("test IfTest.ifElseTest(0). Expected: true, Real: " + IfTest.ifElseTest(0));
System.out.println("test IfTest.ifElseTest(11). Expected: true, Real: " + IfTest.ifElseTest(11));
// SingletonTest tests
System.out.println("test SingletonTest.getInstance(). Expected: non-null, Real: " + (singleton.getInstance() != null));
// WhileTest tests
System.out.println("test WhileTest.whileLoopTest(5). Expected: 15, Real: " + WhileTest.whileLoopTest(5));
}
}
@@ -0,0 +1,41 @@
public class MaliciousTest {
public int assignNegativeIncrement(int n)
{
return n=-++n+1;
}
public int tripleAddition(int a, int b, int c)
{
return a+++b+++c++;
}
public int cursedFormatting(int n)
{
if
(n == 0)
{
return ((((0))));
}
else
if(n ==
1)
{
return
1;
}else {
return
2
;
}
}
}
@@ -0,0 +1,10 @@
public class MethodOverloadTest {
public int MethodOverload() {
return 42;
}
public int MethodOverload(int a) {
return 42 + a;
}
}
@@ -0,0 +1,12 @@
public class MultiClassTest {
public static void main(String[] args) {
Helper h = new Helper(3);
System.out.println(h.doubleIt()); // expect 6
}
}
class Helper {
int v;
Helper(int v0) { v = v0; }
int doubleIt() { return v * 2; }
}
@@ -0,0 +1,9 @@
public class MultipleClassesTest
{
public AnotherTestClass a = new AnotherTestClass();
}
class AnotherTestClass
{
public int a = 42;
}
@@ -0,0 +1,34 @@
public class RecursionTest {
public int value = 0;
public RecursionTest child = null;
public RecursionTest(int n)
{
this.value = n;
if(n > 0)
{
child = new RecursionTest(n - 1);
}
}
public int fibonacci(int n)
{
if(n < 2)
{
return n;
}
else
{
return fibonacci(n - 1) + this.fibonacci(n - 2);
}
}
public int ackermann(int m, int n)
{
if (m == 0) return n + 1;
if (n == 0) return ackermann(m - 1, 1);
return ackermann(m - 1, ackermann(m, n - 1));
}
}
@@ -0,0 +1,14 @@
class ShenaniganceTest {
int testAssignment() {
int x = 1;
int y = x = 5;
return y;
}
int divEqual() {
int x = 234_343_000;
x /= 4;
return x;
}
}
@@ -0,0 +1,15 @@
public class SingletonTest {
SingletonTest instance;
SingletonTest() {
}
public SingletonTest getInstance() {
if (instance == null) {
instance = new SingletonTest();
}
return instance;
}
}
+15
View File
@@ -0,0 +1,15 @@
public class WhileTest {
public static int whileLoopTest(int n) {
int sum = 0;
while (n > 0) {
sum = sum + n;
n = n - 1;
}
return sum;
}
public static void main(String[] args) {
// Expect 1+2+3+4+5 = 15
System.out.println(whileLoopTest(5));
}
}
@@ -0,0 +1,55 @@
[
Class "ArithmeticTest"
[]
[ Method "int" "basic" [("int", "a"), ("int", "b"), ("int", "c")]
(TypedStmt
(Block
[ TypedStmt
(Return
(Just
(Binary Subtract
(Binary Add
(LocalOrFieldVar "a" "int")
(LocalOrFieldVar "b" "int")
)
(Binary Modulo
(Binary Divide
(Binary Multiply
(LocalOrFieldVar "c" "int")
(LocalOrFieldVar "a" "int")
)
(LocalOrFieldVar "b" "int")
)
(LocalOrFieldVar "c" "int")
)
)
)
)
"int"
]
)
"int"
)
, Method "boolean" "logic" [("boolean", "a"), ("boolean", "b"), ("boolean", "c")]
(TypedStmt
(Block
[ TypedStmt
(Return
(Just
(Binary And
(Unary Not (LocalOrFieldVar "a" "boolean") "boolean")
(Binary Or
(LocalOrFieldVar "c" "boolean")
(LocalOrFieldVar "b" "boolean")
"boolean"
)
)
)
)
"boolean"
]
)
"boolean"
)
]
]
+3
View File
@@ -0,0 +1,3 @@
[
Class "EmptyTest" [] []
]
@@ -0,0 +1,3 @@
class ClassWithInt {
Integer i;
}
+263 -34
View File
@@ -1,60 +1,289 @@
module Typecheck.SemanticChecker where
import Grammar.AST
import Grammar.TAST
import Data.List (find)
import Grammar.AST as AST
import Grammar.TAST as TAST
--------------------------------------------------
-- Helper functions
-- Get type functions
--------------------------------------------------
getExpr :: TypedExpr -> Expr
getExpr (TypedExpr e _) = e
getTypeFromTypedExpr :: TypedExpr -> Type
getTypeFromTypedExpr (TAST.This t) = t
getTypeFromTypedExpr (TAST.LocalOrFieldVar _ t) = t
getTypeFromTypedExpr (TAST.InstVar _ _ t) = t
getTypeFromTypedExpr (TAST.Unary _ _ t) = t
getTypeFromTypedExpr (TAST.Binary _ _ _ t) = t
getTypeFromTypedExpr (TAST.Integer _ t) = t
getTypeFromTypedExpr (TAST.Bool _ t) = t
getTypeFromTypedExpr (TAST.Char _ t) = t
getTypeFromTypedExpr (TAST.Null t) = t
getTypeFromTypedExpr (TAST.StmtExprExpr _ t) = t
getStmt :: TypedStmt -> Stmt
getStmt (TypedStmt s _) = s
getTypeFromExpr :: TypedExpr -> Type
getTypeFromExpr (TypedExpr _ typ) = typ
getTypeFromTypedStmtExpr :: TypedStmtExpr -> Type
getTypeFromTypedStmtExpr (TAST.Assign _ _ t) = t
getTypeFromTypedStmtExpr (TAST.New _ _ t) = t
getTypeFromTypedStmtExpr (TAST.MethodCall _ _ _ t) = t
getTypeFromStmt :: TypedStmt -> Type
getTypeFromStmt (TypedStmt _ typ) = typ
getTypeFromTypedStmt :: TypedStmt -> Type
getTypeFromTypedStmt (TAST.Block _ t) = t
getTypeFromTypedStmt (TAST.Return _ t) = t
getTypeFromTypedStmt (TAST.While _ _ t) = t
getTypeFromTypedStmt (TAST.LocalVarDecl _ _ t) = t
getTypeFromTypedStmt (TAST.If _ _ _ t) = t
getTypeFromTypedStmt (TAST.StmtExprStmt _ t) = t
--------------------------------------------------
-- Typechecking Class
--------------------------------------------------
typeCheckClass :: AST.Class -> [(String, Type)] -> [AST.Class] -> TAST.TypedClass
typeCheckClass (AST.Class typ fields methods) symtab cls =
let checkedFields = map (\f -> typeCheckField f symtab cls) fields
checkedMethods = map (\m -> typeCheckMethod m symtab cls) methods
in TAST.Class typ checkedFields checkedMethods
typeCheckField :: AST.FieldDecl -> [(String, Type)] -> [AST.Class] -> TAST.TypedFieldDecl
typeCheckField (AST.Field typ name Nothing) symtab cls =
TAST.Field typ name Nothing
typeCheckField (AST.Field expectedTyp name (Just expr)) symtab cls =
let checkedExpr = typeCheckExpr expr symtab cls
actualTyp = getTypeFromTypedExpr checkedExpr
in if expectedTyp == actualTyp
then TAST.Field expectedTyp name (Just checkedExpr)
else error $ "Type mismatch: Expected " ++ expectedTyp ++ " found " ++ actualTyp
typeCheckMethod :: AST.MethodDecl -> [(String, Type)] -> [AST.Class] -> TAST.TypedMethodDecl
typeCheckMethod (AST.Method typ name params stmt) symtab cls =
let checkedStmt = typeCheckStmt stmt symtab cls
stmtTyp = getTypeFromTypedStmt checkedStmt
in if typ == stmtTyp || stmtTyp == "void"
then TAST.Method typ name params checkedStmt
else error $ "Return type mismatch: expected " ++ typ ++ ", got " ++ stmtTyp
upperBound :: Type -> Type -> Type
upperBound t1 t2
| t1 == t2 = t1
| otherwise = "Object" -- // TODO: implement proper class hierarchy and find the least common ancestor of t1 and t2 in that hierarchy
--------------------------------------------------
-- Statement Typechecking
--------------------------------------------------
typeCheckStmt :: Stmt -> [(String, Type)] -> [Class] -> TypedStmt
-- If Statement
typeCheckStmt (If cond body Nothing) symtab cls =
typeCheckStmt :: AST.Stmt -> [(String, Type)] -> [AST.Class] -> TAST.TypedStmt
-- If-Statement
typeCheckStmt (AST.If cond body Nothing) symtab cls =
let checkedCond = typeCheckExpr cond symtab cls
checkedBody = typeCheckStmt body symtab cls
in if getTypeFromExpr checkedCond == "boolean"
then TypedStmt (If checkedCond checkedBody Nothing) (getTypeFromStmt checkedBody)
else error "Condition in if statement must be of type boolean"
-- If-Else Statement
typeCheckStmt (If cond body (Just elseBranch)) symtab cls =
typ = getTypeFromTypedStmt checkedBody
in if getTypeFromTypedExpr checkedCond == "boolean"
then TAST.If checkedCond checkedBody Nothing typ
else error "Condition in if statement must be of type boolean"
-- If-Else-Statement
typeCheckStmt (AST.If cond body (Just elseBody)) symtab cls =
let checkedCond = typeCheckExpr cond symtab cls
checkedBody = typeCheckStmt body symtab cls
checkedElse = fmap (\e -> typeCheckStmt e symtab cls) elseBranch
in if getTypeFromExpr checkedCond == "boolean"
then TypedStmt (If checkedCond checkedBody checkedElse) (upperBound (getTypeFromStmt checkedBody) (getTypeFromStmt checkedElse))
else error "Condition in if statement must be of type boolean"
checkedElse = Just (typeCheckStmt elseBody symtab cls)
typ = upperBound (getTypeFromTypedStmt checkedBody) (getTypeFromTypedStmt (maybe checkedBody id checkedElse))
in if getTypeFromTypedExpr checkedCond == "boolean"
then TAST.If checkedCond checkedBody checkedElse typ
else error "Condition in if statement must be of type boolean"
-- While-Statement
typeCheckStmt (AST.While cond body) symtab cls =
let checkedCond = typeCheckExpr cond symtab cls
checkedBody = typeCheckStmt body symtab cls
typ = getTypeFromTypedStmt checkedBody
in if getTypeFromTypedExpr checkedCond == "boolean"
then TAST.While checkedCond checkedBody typ
else error "Condition in if statement must be of type boolean"
-- Return Statement
typeCheckStmt (AST.Return maybeExpr) symtab cls =
let checkedExpr = fmap (\expr -> typeCheckExpr expr symtab cls) maybeExpr
typ = maybe "void" getTypeFromTypedExpr checkedExpr
in TAST.Return checkedExpr typ
typeCheckStmt (AST.LocalVarDecl typ str) _ _ =
TAST.LocalVarDecl typ str typ
-- Block statement
typeCheckStmt (AST.Block stmts) symtab cls =
let checkedStmts = map (\s -> typeCheckStmt s symtab cls) stmts
typ = case reverse checkedStmts of
[] -> "void"
(lastStmt : _) -> getTypeFromTypedStmt lastStmt
in TAST.Block checkedStmts typ
-- Expression statement
typeCheckStmt (AST.StmtExprStmt stmtExpr) symtab cls =
let checkedStmtExpr = typeCheckStmtExpr stmtExpr symtab cls
typ = getTypeFromTypedStmtExpr checkedStmtExpr
in TAST.StmtExprStmt checkedStmtExpr typ
--------------------------------------------------
-- Statement Expression Typechecking
--------------------------------------------------
typeCheckStmtExpr :: AST.StmtExpr -> [(String, Type)] -> [AST.Class] -> TAST.TypedStmtExpr
-- Assign
typeCheckStmtExpr (AST.Assign lhs rhs) symtab cls =
let checkedLhs = typeCheckExpr lhs symtab cls
checkedRhs = typeCheckExpr rhs symtab cls
lhsType = getTypeFromTypedExpr checkedLhs
rhsType = getTypeFromTypedExpr checkedRhs
in if lhsType == rhsType || rhsType == "null"
then TAST.Assign checkedLhs checkedRhs lhsType
else error "Type mismatch in assignment"
-- New
typeCheckStmtExpr (AST.New typ args) symtab cls =
let checkedArgs = map (\arg -> typeCheckExpr arg symtab cls) args
in TAST.New typ checkedArgs typ
-- Method call
typeCheckStmtExpr (AST.MethodCall target methodName args) symtab cls =
let checkedTarget = typeCheckExpr target symtab cls
checkedArgs = map (\arg -> typeCheckExpr arg symtab cls) args
targetType = getTypeFromTypedExpr checkedTarget
returnType = lookupMethodReturnType targetType methodName cls
in TAST.MethodCall checkedTarget methodName checkedArgs returnType
-- Helper methods
lookupMethodReturnType :: Type -> String -> [AST.Class] -> Type
lookupMethodReturnType classType methodName classes =
case findMethod classType methodName classes of
Just t -> t
Nothing -> error $ "Method not found: " ++ methodName ++ " in class " ++ classType
findMethod :: Type -> String -> [AST.Class] -> Maybe Type
findMethod classType methodName classes =
case find (\(AST.Class clsName _ _) -> clsName == classType) classes of
Just (AST.Class _ _ methods) ->
case find (\(AST.Method ret name _ _) -> name == methodName) methods of
Just (AST.Method ret _ _ _) -> Just ret
Nothing -> Nothing
Nothing -> Nothing
lookupFieldType :: Type -> String -> [AST.Class] -> Type
lookupFieldType classType fieldName classes =
case find (\(AST.Class clsName _ _) -> clsName == classType) classes of
Just (AST.Class _ fields _) ->
case find (\(AST.Field _ name _) -> name == fieldName) fields of
Just (AST.Field t _ _) -> t
Nothing -> error $ "Field not found: " ++ fieldName ++ " in class " ++ classType
Nothing -> error $ "Class not found: " ++ classType
--------------------------------------------------
-- Expression Typechecking
--------------------------------------------------
typeCheckExpr :: Expr -> [(String, Type)] -> [Class] -> TypedExpr
typeCheckExpr :: AST.Expr -> [(String, Type)] -> [AST.Class] -> TAST.TypedExpr
-- boolean literals
typeCheckExpr (Bool boolean) symtbl cls =
TypedExpr (Bool boolean) "boolean"
typeCheckExpr (AST.Bool value) symtbl cls =
TAST.Bool value "boolean"
-- integer literals
typeCheckExpr (Integer integer) symtbl cls =
TypedExpr (Integer integer) "int"
typeCheckExpr (AST.Integer value) symtbl cls =
TAST.Integer value "int"
-- variable references
typeCheckExpr (LocalOrFieldVar varName) symtbl cls =
typeCheckExpr (AST.LocalOrFieldVar varName) symtbl cls =
case lookup varName symtbl of
Just t -> TypedExpr (LocalOrFieldVar varName) t
Just t -> TAST.LocalOrFieldVar varName t
Nothing -> error $ "Undefined variable: " ++ varName
-- this reference
typeCheckExpr AST.This _ cls =
case cls of
(AST.Class className _ _ : _) -> TAST.This className
[] -> error "No class context for this expression"
-- field access on an object
typeCheckExpr (AST.InstVar obj fieldName) symtbl cls =
let checkedObj = typeCheckExpr obj symtbl cls
objType = getTypeFromTypedExpr checkedObj
fieldType = lookupFieldType objType fieldName cls
in TAST.InstVar checkedObj fieldName fieldType
-- unary operators
typeCheckExpr (AST.Unary op expr) symtbl cls =
let checkedExpr = typeCheckExpr expr symtbl cls
exprType = getTypeFromTypedExpr checkedExpr
in case op of
AST.Not -> if exprType == "boolean"
then TAST.Unary AST.Not checkedExpr "boolean"
else error "Operator ! requires boolean operand"
AST.Negate -> if exprType == "int"
then TAST.Unary AST.Negate checkedExpr "int"
else error "Unary - requires integer operand"
-- binary operators
typeCheckExpr (AST.Binary op left right) symtbl cls =
let checkedLeft = typeCheckExpr left symtbl cls
checkedRight = typeCheckExpr right symtbl cls
leftType = getTypeFromTypedExpr checkedLeft
rightType = getTypeFromTypedExpr checkedRight
in case op of
AST.Add -> checkIntOp leftType rightType checkedLeft checkedRight op
AST.Subtract -> checkIntOp leftType rightType checkedLeft checkedRight op
AST.Multiply -> checkIntOp leftType rightType checkedLeft checkedRight op
AST.Divide -> checkIntOp leftType rightType checkedLeft checkedRight op
AST.Modulo -> checkIntOp leftType rightType checkedLeft checkedRight op
AST.CompLessThan -> checkIntComparison leftType rightType checkedLeft checkedRight op
AST.CompLessOrEqual -> checkIntComparison leftType rightType checkedLeft checkedRight op
AST.CompGreaterThan -> checkIntComparison leftType rightType checkedLeft checkedRight op
AST.CompGreaterOrEqual -> checkIntComparison leftType rightType checkedLeft checkedRight op
AST.CompEqual -> checkEquality leftType rightType checkedLeft checkedRight op
AST.CompNotEqual -> checkEquality leftType rightType checkedLeft checkedRight op
AST.BitAnd -> checkIntOp leftType rightType checkedLeft checkedRight op
AST.BitOr -> checkIntOp leftType rightType checkedLeft checkedRight op
AST.BitXor -> checkIntOp leftType rightType checkedLeft checkedRight op
AST.And -> checkBoolOp leftType rightType checkedLeft checkedRight op
AST.Or -> checkBoolOp leftType rightType checkedLeft checkedRight op
-- character literals
typeCheckExpr (AST.Char value) _ _ =
TAST.Char value "char"
-- null literal
typeCheckExpr AST.Null _ _ =
TAST.Null "null"
-- statement expression
typeCheckExpr (AST.StmtExprExpr stmtExpr) symtbl cls =
let checkedStmtExpr = typeCheckStmtExpr stmtExpr symtbl cls
typ = getTypeFromTypedStmtExpr checkedStmtExpr
in TAST.StmtExprExpr checkedStmtExpr typ
-- Helper methods
checkIntOp :: Type -> Type -> TAST.TypedExpr -> TAST.TypedExpr -> AST.BinaryOperator -> TAST.TypedExpr
checkIntOp leftType rightType left right op =
if leftType == "int" && rightType == "int"
then TAST.Binary op left right "int"
else error "Integer binary operator requires int operands"
checkBoolOp :: Type -> Type -> TAST.TypedExpr -> TAST.TypedExpr -> AST.BinaryOperator -> TAST.TypedExpr
checkBoolOp leftType rightType left right op =
if leftType == "boolean" && rightType == "boolean"
then TAST.Binary op left right "boolean"
else error "Boolean binary operator requires boolean operands"
checkIntComparison :: Type -> Type -> TAST.TypedExpr -> TAST.TypedExpr -> AST.BinaryOperator -> TAST.TypedExpr
checkIntComparison leftType rightType left right op =
if leftType == "int" && rightType == "int"
then TAST.Binary op left right "boolean"
else error "Comparison operator requires int operands"
checkEquality :: Type -> Type -> TAST.TypedExpr -> TAST.TypedExpr -> AST.BinaryOperator -> TAST.TypedExpr
checkEquality leftType rightType left right op =
if leftType == rightType || leftType == "null" || rightType == "null"
then TAST.Binary op left right "boolean"
else error $ "Equality operator: cannot compare " ++ leftType ++ " with " ++ rightType
upperBound :: Type -> Type -> Type
upperBound t1 t2
| t1 == t2 = t1
| otherwise = "Object"
-4
View File
@@ -1,4 +0,0 @@
func Main.main : int
entry:
t0 = 0
return t0
-11
View File
@@ -1,11 +0,0 @@
[
Class "Main" []
[
Method "int" "main" []
(Block
[
Return (Integer 0)
]
)
]
]
-5
View File
@@ -1,5 +0,0 @@
class Main {
int main() {
return 0;
}
}
-16
View File
@@ -1,16 +0,0 @@
[
Class "Main" []
[
Method "int" "main" []
(TypedStmt
(Block
[
TypedStmt
(Return (Integer 0))
"int"
]
)
"int"
)
]
]