30 Commits

Author SHA1 Message Date
Vectabyte 69ae49a346 updated tests and added more 2026-05-15 11:25:18 +02:00
Vectabyte e43a783d33 updated corresponding ast and tast 2026-05-14 17:59:04 +02:00
Vectabyte 0496e6e1b4 Updated test cases to comply with constraints 2026-05-14 17:45:33 +02:00
Vectabyte 4f5e4febdf More test's, removed old harness 2026-05-14 16:52:43 +02:00
Konstantin Fastovski ffae200d3c fix merge conflict residue 2026-05-13 15:56:10 +02:00
Konstantin Fastovski 85b93a604c properly handle large integers in constpool 2026-05-13 15:54:49 +02:00
Vectabyte 3ffac1ffe6 Merge remote-tracking branch 'origin/main' into test-creation 2026-05-13 15:42:06 +02:00
Silas 3cf088b818 update ConstPool 2026-05-13 15:37:10 +02:00
Vectabyte e86389baf8 overhauled test suite 2026-05-13 15:33:49 +02:00
Vectabyte 6aedcfe4e1 Merge remote-tracking branch 'origin/main' into test-creation 2026-05-13 14:18:54 +02:00
Felix fe71942a65 Added Maybe Stmt to LocalVarDecl to allow in place assignment
Added Operator Assignment Statements (test *= 2)
Added for loop support -> While in the AST
2026-05-13 14:18:25 +02:00
Vectabyte a2ebe25901 Merge remote-tracking branch 'origin/main' into test-creation 2026-05-13 12:12:43 +02:00
Konstantin Fastovski 8ac6223c6b add BIPush and SIPush Instructions 2026-05-13 12:12:21 +02:00
Vectabyte 9d4106aee8 Folder rename 2026-05-13 12:11:09 +02:00
Vectabyte 95a80ca5d3 included constant pool index strip 2026-05-13 12:01:59 +02:00
Vectabyte b0abf5ee72 Merge remote-tracking branch 'origin/main' into test-creation 2026-05-13 11:39:33 +02:00
Vectabyte 69b8bcba4d adjusted and added tests 2026-05-13 11:36:47 +02:00
timb 8ecda70a5f feat: add for loops to parser 2026-05-13 11:25:35 +02:00
LeonProgrammiert 309d52715a Merge branch 'main' of ssh://gitea.hb.dhbw-stuttgart.de:2222/mo/Compoiler 2026-05-13 10:57:03 +02:00
LeonProgrammiert 0f2469f007 Fixed symtab in semantik checker 2026-05-13 10:56:50 +02:00
Konstantin Fastovski bfd70838aa fix test.sh 2026-05-13 10:50:00 +02:00
Konstantin Fastovski e48012c3ed fix Makefile 2026-05-13 10:41:15 +02:00
Konstantin Fastovski fc2b31693d fix test.sh 2026-05-13 10:40:28 +02:00
Vectabyte 1bc9babd4f Merge remote-tracking branch 'origin/main' into test-creation 2026-05-13 10:35:02 +02:00
Vectabyte 604db760ac added missing type 2026-05-13 10:34:36 +02:00
Vectabyte 4e24b2fb8a Test Fixes 2026-05-13 10:29:47 +02:00
Konstantin Fastovski 94e23b57fd add Makefile 2026-05-13 10:06:28 +02:00
Vectabyte 22fd6733f0 missing test cases 2026-05-13 10:05:29 +02:00
Vectabyte 62f42611bc txt for test commands 2026-05-13 10:01:35 +02:00
Vectabyte 2e601b86d5 more tests 2026-05-13 09:47:52 +02:00
75 changed files with 1869 additions and 834 deletions
-1
View File
@@ -33,7 +33,6 @@ src/Grammar/Scanner.hs
src/Grammar/Parser.hs
src/Grammar/Parser.info
src/Main
*.class
out/
mine.txt
ref.txt
+5
View File
@@ -0,0 +1,5 @@
# use make build to compile all binaries
build:
alex ./src/Grammar/Scanner.x
happy ./src/Grammar/Parser.y -o ./src/Grammar/Parser.hs
ghc -dynamic -i./src src/Main.hs
+64 -13
View File
@@ -4,6 +4,7 @@ import Codegen.ConstPool
( generateConstPool,
lookupClassIndex,
lookupFieldRefIndex,
lookupIntegerIndex,
lookupUtf8Index,
)
import Codegen.Types
@@ -15,6 +16,7 @@ import Grammar.TAST
TypedExpr (..),
TypedFieldDecl (..),
TypedMethodDecl (..),
TypedStmt (..),
)
data ClassFile = ClassFile
@@ -188,33 +190,82 @@ fieldInitBytes :: CP_Infos -> TypedFieldDecl -> [Word8]
fieldInitBytes constPool (Field _ name (Just expr)) =
let idx = lookupFieldRefIndex constPool ("field ref: " ++ name)
in [0x2A]
++ pushExpr expr
++ [0xB5, fromIntegral (idx `shiftR` 8 .&. 0xFF), fromIntegral (idx .&. 0xFF)]
++ pushExpr constPool expr
++ [0xB5, fromIntegral (idx `shiftR` 8 .&. 0xFF), fromIntegral (idx .&. 0xFF)]
fieldInitBytes _ _ = []
pushExpr :: TypedExpr -> [Word8]
pushExpr (Integer n _) = pushInt (fromIntegral n)
pushExpr (Bool True _) = [0x04]
pushExpr (Bool False _) = [0x03]
pushExpr (Char c _) = pushInt (fromEnum c)
pushExpr _ = []
pushExpr :: CP_Infos -> TypedExpr -> [Word8]
pushExpr constPool (Integer n _) = pushInt constPool (fromIntegral n)
pushExpr _ (Bool True _) = [0x04]
pushExpr _ (Bool False _) = [0x03]
pushExpr constPool (Char c _) = pushInt constPool (fromEnum c)
pushExpr _ (Null _) = [0x01]
pushExpr _ _ = []
pushInt :: Int -> [Word8]
pushInt n
pushInt :: CP_Infos -> Int -> [Word8]
pushInt constPool n
| n >= -1 && n <= 5 = [fromIntegral (0x03 + n)]
| n >= -128 && n <= 127 = [0x10, fromIntegral n]
| n >= -32768 && n <= 32767 = [0x11, fromIntegral (n `shiftR` 8 .&. 0xFF), fromIntegral (n .&. 0xFF)]
| otherwise = [0x10, 0]
| otherwise =
let idx = lookupIntegerIndex constPool n
in if idx == 0
then error $ "Missing Integer constant in pool: " ++ show n
else
if idx <= 255
then [0x12, fromIntegral idx]
else [0x13, fromIntegral (idx `shiftR` 8 .&. 0xFF), fromIntegral (idx .&. 0xFF)]
makeMethod :: CP_Infos -> TypedMethodDecl -> Method_Info
makeMethod constPool (Method returnType name params _body) =
makeMethod constPool (Method returnType name params body) =
Method_Info
{ methodAccessFlags = AccessFlags [ACC_PUBLIC],
methodNameIndex = lookupUtf8Index constPool name,
methodDescIndex = lookupUtf8Index constPool (methodDescriptor params returnType),
methodAttributes = []
methodAttributes = [makeMethodCode constPool returnType params body]
}
makeMethodCode :: CP_Infos -> Type -> [(Type, String)] -> TypedStmt -> Attribute_Info
makeMethodCode constPool returnType params body =
let (code, stack) = stmtCode constPool returnType body
locals = 1 + length params
in Code_Attribute
{ codeNameIndex = lookupUtf8Index constPool "Code",
maxStack = stack,
maxLocals = locals,
codeBody = code,
exceptionTable = [],
codeAttributes = []
}
stmtCode :: CP_Infos -> Type -> TypedStmt -> ([Word8], Int)
stmtCode constPool returnType stmt = case stmt of
Return Nothing _
| returnType == "void" -> ([0xB1], 0)
| otherwise -> error "Non-void method must return a value"
Return (Just expr) _ ->
let (exprBytes, stack) = exprCode constPool expr
retOp = case returnType of
"int" -> 0xAC
"boolean" -> 0xAC
"char" -> 0xAC
_ -> error $ "Unsupported return type: " ++ returnType
in (exprBytes ++ [retOp], stack)
Block [single] _ -> stmtCode constPool returnType single
Block [] _
| returnType == "void" -> ([0xB1], 0)
| otherwise -> error "Non-void method must return a value"
_ -> error "Unsupported method body"
exprCode :: CP_Infos -> TypedExpr -> ([Word8], Int)
exprCode constPool expr = case expr of
Integer n _ -> (pushInt constPool (fromIntegral n), 1)
Bool True _ -> ([0x04], 1)
Bool False _ -> ([0x03], 1)
Char c _ -> (pushInt constPool (fromEnum c), 1)
Null _ -> ([0x01], 1)
_ -> error "Unsupported return expression"
methodDescriptor :: [(Type, String)] -> Type -> String
methodDescriptor params returnType =
"(" ++ concatMap (typeDescriptor . fst) params ++ ")" ++ typeDescriptor returnType
+134 -31
View File
@@ -1,7 +1,7 @@
module Codegen.ConstPool where
import Codegen.Types
import Data.List (isSuffixOf)
import Data.List (isPrefixOf, isSuffixOf)
import qualified Grammar.TAST as TAST
------------------------------------------------------------------------
@@ -89,6 +89,18 @@ collectFromExprs :: [TAST.TypedExpr] -> CP_Infos
collectFromExprs [] = []
collectFromExprs (e : es) = collectFromTypedExpr e ++ collectFromExprs es
exprType :: TAST.TypedExpr -> String
exprType (TAST.This t) = t
exprType (TAST.LocalOrFieldVar _ t) = t
exprType (TAST.InstVar _ _ t) = t
exprType (TAST.Unary _ _ t) = t
exprType (TAST.Binary _ _ _ t) = t
exprType (TAST.Integer _ t) = t
exprType (TAST.Bool _ t) = t
exprType (TAST.Char _ t) = t
exprType (TAST.Null t) = t
exprType (TAST.StmtExprExpr _ t) = t
collectFromTypedExpr :: TAST.TypedExpr -> CP_Infos
collectFromTypedExpr (TAST.Integer n _) =
[mkIntegerInfo (fromIntegral n) ("int " ++ show n)]
@@ -103,12 +115,14 @@ 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)
]
let objType = exprType obj
desc = typeToDescriptor typ
in collectFromTypedExpr obj
++ [ mkUtf8Info name ("field name: " ++ name),
mkUtf8Info desc ("field descriptor: " ++ desc),
mkNameAndTypeInfo 0 0 ("field NameAndType: " ++ name ++ ":" ++ desc),
mkFieldRefInfo 0 0 ("field ref: " ++ objType ++ "." ++ name ++ ":" ++ desc)
]
collectFromTypedExpr (TAST.Unary _ e _) =
collectFromTypedExpr e
collectFromTypedExpr (TAST.Binary _ e1 e2 _) =
@@ -122,16 +136,20 @@ collectFromTypedStmtExpr (TAST.Assign lhs rhs _) =
collectFromTypedStmtExpr (TAST.New t args _) =
collectFromExprs args
++ [ mkUtf8Info t ("class name: " ++ t),
mkClassInfo 0 ("class: " ++ t)
mkClassInfo 0 ("class: " ++ t),
mkMethodRefInfo 0 0 ("new method ref: " ++ t ++ ".<init>:()V")
]
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)
]
let objType = exprType obj
argTypes = map exprType args
fullDesc = "(" ++ concatMap typeToDescriptor argTypes ++ ")" ++ typeToDescriptor retTyp
in collectFromTypedExpr obj
++ collectFromExprs args
++ [ mkUtf8Info name ("method name: " ++ name),
mkUtf8Info fullDesc ("method desc: " ++ name),
mkNameAndTypeInfo 0 0 ("method NameAndType: " ++ name ++ ":" ++ fullDesc),
mkMethodRefInfo 0 0 ("method ref: " ++ objType ++ "." ++ name ++ ":" ++ fullDesc)
]
collectFromStmts :: [TAST.TypedStmt] -> CP_Infos
collectFromStmts [] = []
@@ -161,9 +179,8 @@ collectFromMaybeStmt (Just s) = collectFromTypedStmt s
collectFromFields :: [TAST.TypedFieldDecl] -> CP_Infos
collectFromFields [] = []
collectFromFields (TAST.Field fType fName _: fs ) =
collectFromFields (TAST.Field fType fName _ : fs) =
[ mkUtf8Info fName ("field: " ++ fName),
mkUtf8Info (typeToDescriptor fType) ("field desc: " ++ fType)
]
++ collectFromFields fs
@@ -183,19 +200,24 @@ collectFromMethods (TAST.Method mType mName params body : ms) =
generateConstPool :: TAST.TypedClass -> CP_Infos
generateConstPool (TAST.Class cName fields methods) =
[ mkUtf8Info cName ("this class: " ++ cName), -- #1
mkUtf8Info "java/lang/Object" "super class", -- #2
mkClassInfo 1 ("this class: " ++ cName), -- #3 → #1
mkClassInfo 2 "super class", -- #4 → #2
mkUtf8Info "<init>" "<init>", -- #5
mkUtf8Info "()V" "init descriptor", -- #6
mkNameAndTypeInfo 5 6 "<init>:()V", -- #7
mkMethodRefInfo 4 7 "java/lang/Object.<init>:()V", -- #8
mkUtf8Info "Code" "Code" -- #9
]
++ collectFromFields fields
++ buildInitFieldPool fields (length fields)
++ collectFromMethods methods
let basePool =
[ mkUtf8Info cName ("this class: " ++ cName), -- #1
mkUtf8Info "java/lang/Object" "super class", -- #2
mkClassInfo 1 ("this class: " ++ cName), -- #3 → #1
mkClassInfo 2 "super class", -- #4 → #2
mkUtf8Info "<init>" "<init>", -- #5
mkUtf8Info "()V" "init descriptor", -- #6
mkNameAndTypeInfo 5 6 "<init>:()V", -- #7
mkMethodRefInfo 4 7 "java/lang/Object.<init>:()V", -- #8
mkUtf8Info "Code" "Code" -- #9
]
++ collectFromFields fields
++ buildInitFieldPool fields (length fields)
initExprs = [e | TAST.Field _ _ (Just e) <- fields]
initPool = concatMap collectFromTypedExpr initExprs
methodPool = collectFromMethods methods
rawPool = basePool ++ initPool ++ methodPool
in resolvePool rawPool
-- Build NameAndType + Fieldref entries for each field with an initializer.
-- The base pool has 9 fixed entries; collectFromFields adds 2 per field.
@@ -210,7 +232,7 @@ buildInitFieldPool fields numFields = go 0 0 fields
go fieldIdx initIdx (TAST.Field _ name _ : rest) =
let nameUtf8Idx = 9 + 2 * fieldIdx + 1
descUtf8Idx = 9 + 2 * fieldIdx + 2
natCpIdx = 9 + 2 * numFields + 2 * initIdx + 1
natCpIdx = 9 + 2 * numFields + 2 * initIdx + 1
in [ mkNameAndTypeInfo nameUtf8Idx descUtf8Idx ("field NameAndType: " ++ name),
mkFieldRefInfo 3 natCpIdx ("field ref: " ++ name)
]
@@ -258,6 +280,87 @@ lookupFieldRefIndex pool name =
FieldRefInfo {desc = descVal} -> descVal == name || isSuffixOf name descVal
_ -> False
lookupMethodRefIndex :: CP_Infos -> String -> IndexConstantPool
lookupMethodRefIndex pool name =
case findIndex matches pool of
Just i -> i
Nothing -> 0
where
matches entry = case entry of
MethodRefInfo {desc = descVal} -> descVal == name || isSuffixOf name descVal
_ -> False
lookupIntegerIndex :: CP_Infos -> Int -> IndexConstantPool
lookupIntegerIndex pool value =
case findIndex matches pool of
Just i -> i
Nothing -> 0
where
matches entry = case entry of
IntegerInfo {numi_cp = n} -> n == value
_ -> False
resolvePool :: CP_Infos -> CP_Infos
resolvePool pool = map resolveEntry pool
where
resolveEntry (ClassInfo tag 0 desc) =
ClassInfo tag (lookupUtf8Index pool (classNameFromDesc desc)) desc
resolveEntry (NameAndTypeInfo tag 0 0 desc) =
let (name, descr) = nameAndTypeFromDesc desc
nameIdx = lookupUtf8Index pool name
descIdx = lookupUtf8Index pool descr
in NameAndTypeInfo tag nameIdx descIdx desc
resolveEntry (FieldRefInfo tag 0 0 desc) =
let (clsName, natDesc) = fieldRefFromDesc desc
clsIdx = if null clsName then 0 else lookupClassIndex pool clsName
natIdx = if null natDesc then 0 else lookupNameAndTypeIndex pool natDesc
in FieldRefInfo tag clsIdx natIdx desc
resolveEntry (MethodRefInfo tag 0 0 desc) =
let (clsName, natDesc) = methodRefFromDesc desc
clsIdx = if null clsName then 0 else lookupClassIndex pool clsName
natIdx = if null natDesc then 0 else lookupNameAndTypeIndex pool natDesc
in MethodRefInfo tag clsIdx natIdx desc
resolveEntry other = other
classNameFromDesc :: String -> String
classNameFromDesc desc
| "class: " `isPrefixOf` desc = drop 7 desc
| "this class: " `isPrefixOf` desc = drop 12 desc
| desc == "super class" = "java/lang/Object"
| otherwise = desc
nameAndTypeFromDesc :: String -> (String, String)
nameAndTypeFromDesc desc
| "field NameAndType: " `isPrefixOf` desc =
let rest = drop 19 desc
(name, descPart) = break (== ':') rest
in (name, drop 1 descPart)
| "method NameAndType: " `isPrefixOf` desc =
let rest = drop 20 desc
(name, descPart) = break (== ':') rest
in (name, drop 1 descPart)
| otherwise = ("", "")
fieldRefFromDesc :: String -> (String, String)
fieldRefFromDesc desc
| "field ref: " `isPrefixOf` desc =
let rest = drop 11 desc
(clsName, natDesc') = break (== '.') rest
in (clsName, drop 1 natDesc')
| otherwise = ("", "")
methodRefFromDesc :: String -> (String, String)
methodRefFromDesc desc
| "new method ref: " `isPrefixOf` desc =
let rest = drop 16 desc
(clsName, natDesc') = break (== '.') rest
in (clsName, drop 1 natDesc')
| "method ref: " `isPrefixOf` desc =
let rest = drop 12 desc
(clsName, natDesc') = break (== '.') rest
in (clsName, drop 1 natDesc')
| otherwise = ("", "")
findIndex :: (CP_Info -> Bool) -> CP_Infos -> Maybe IndexConstantPool
findIndex predicate pool = go 1 pool
where
+33 -1
View File
@@ -17,13 +17,20 @@ data Instruction
| InvokeVirtual Int
| InvokeStatic Int
| IConst Int
| BIPush Int
| SIPush Int
| AConstNull
| Ldc Int
| IAdd
| ISub
| IMul
| IDiv
| IfEq Int
| IfNe Int
| IfLt Int
| IfGe Int
| IfGt Int
| IfLe Int
| Goto Int
| Return
| IReturn
@@ -108,13 +115,20 @@ decodeBytes pos (b : bs) = case b of
0x07 -> line (IConst 4) pos 1 bs
0x08 -> line (IConst 5) pos 1 bs
0x01 -> line AConstNull pos 1 bs
0x10 -> withSignedByte bs pos $ \n rest -> line (BIPush n) pos 2 rest
0x11 -> withSignedShort bs pos $ \n rest -> line (SIPush n) pos 3 rest
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
0x6C -> line IDiv 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
0x9B -> withIndex bs pos $ \i rest -> line (IfLt i) pos 3 rest
0x9C -> withIndex bs pos $ \i rest -> line (IfGe i) pos 3 rest
0x9D -> withIndex bs pos $ \i rest -> line (IfGt i) pos 3 rest
0x9E -> withIndex bs pos $ \i rest -> line (IfLe 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
@@ -133,7 +147,25 @@ 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
withSignedByte :: [Word8] -> Int -> (Int -> [Word8] -> [BtcLine]) -> [BtcLine]
withSignedByte (b : rest) _ f = f (signedByte b) rest
withSignedByte [] pos _ = error $ "Unexpected end of bytecode at " ++ show pos
withSignedShort :: [Word8] -> Int -> (Int -> [Word8] -> [BtcLine]) -> [BtcLine]
withSignedShort (hi : lo : rest) _ f = f (signedShort hi lo) rest
withSignedShort _ pos _ = error $ "Unexpected end of bytecode at " ++ show pos
signedByte :: Word8 -> Int
signedByte b =
let n = fromIntegral b :: Int
in if n >= 128 then n - 256 else n
signedShort :: Word8 -> Word8 -> Int
signedShort hi lo =
let n = fromIntegral hi * 256 + fromIntegral lo :: Int
in if n >= 32768 then n - 65536 else n
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
| otherwise = Just (pool !! (i - 1)) -- pool is 1-indexed
+57 -25
View File
@@ -58,6 +58,13 @@ serializeInstruction instr = case instr of
IConst 4 -> [0x07]
IConst 5 -> [0x08]
IConst n -> error $ "IConst out of range (-1..5): " ++ show n
-- bipush/sipush: push signed byte/short
BIPush n
| n >= -128 && n <= 127 -> [0x10, fromIntegral n]
| otherwise -> error $ "BIPush out of range (-128..127): " ++ show n
SIPush n
| n >= -32768 && n <= 32767 -> 0x11 : indexBytes (n .&. 0xFFFF)
| otherwise -> error $ "SIPush out of range (-32768..32767): " ++ show n
AConstNull -> [0x01]
-- ldc: 1-byte index (use ldc_w 0x13 for large pool indices > 255)
Ldc i
@@ -71,6 +78,10 @@ serializeInstruction instr = case instr of
-- Control flow: 2-byte signed branch offset
IfEq offset -> 0x99 : indexBytes offset
IfNe offset -> 0x9A : indexBytes offset
IfLt offset -> 0x9B : indexBytes offset
IfGe offset -> 0x9C : indexBytes offset
IfGt offset -> 0x9D : indexBytes offset
IfLe offset -> 0x9E : indexBytes offset
Goto offset -> 0xA7 : indexBytes offset
-- Return
Return -> [0xB1]
@@ -94,38 +105,59 @@ serializeClassFile cf =
++ u2 (let SuperClass i = superClass cf in i)
++ u2 (length (interfaces cf))
++ concatMap (\i -> u2 (fromIntegral (i :: Word16) :: Int)) (interfaces cf)
++ u2 (length (fields cf)) ++ concatMap fieldEntry (fields cf)
++ u2 (length (methods cf)) ++ concatMap methodEntry (methods cf)
++ u2 (length (attributes cf)) ++ concatMap attrEntry (attributes cf)
++ u2 (length (fields cf))
++ concatMap fieldEntry (fields cf)
++ u2 (length (methods cf))
++ concatMap methodEntry (methods cf)
++ u2 (length (attributes cf))
++ concatMap attrEntry (attributes cf)
where
u2 :: Int -> [Word8]
u2 n = [fromIntegral ((n `shiftR` 8) .&. 0xFF), fromIntegral (n .&. 0xFF)]
u2 n = [fromIntegral ((n `shiftR` 8) .&. 0xFF), fromIntegral (n .&. 0xFF)]
u4 :: (Integral a) => a -> [Word8]
u4 n = let w = fromIntegral n :: Word32
in map (\s -> fromIntegral ((w `shiftR` s) .&. 0xFF)) [24,16,8,0]
u4 n =
let w = fromIntegral n :: Word32
in map (\s -> fromIntegral ((w `shiftR` s) .&. 0xFF)) [24, 16, 8, 0]
aflags (AccessFlags fs) = u2 (foldr (\f a -> a .|. fval f) (0 :: Int) fs)
fval :: AccessFlag -> Int
fval ACC_PUBLIC = 0x0001; fval ACC_PRIVATE = 0x0002
fval ACC_PROTECTED = 0x0004; fval ACC_STATIC = 0x0008
fval ACC_FINAL = 0x0010; fval ACC_SUPER = 0x0020
fval ACC_INTERFACE = 0x0200; fval ACC_ABSTRACT = 0x0400
fval ACC_SYNTHETIC = 0x1000; fval ACC_ENUM = 0x4000
cpEntry (Utf8Info _ _ s _) = [1] ++ u2 (length s) ++ map (fromIntegral . ord) s
cpEntry (ClassInfo _ i _) = [7] ++ u2 i
cpEntry (FieldRefInfo _ c n _) = [9] ++ u2 c ++ u2 n
cpEntry (MethodRefInfo _ c n _) = [10] ++ u2 c ++ u2 n
cpEntry (StringInfo _ i _) = [8] ++ u2 i
cpEntry (IntegerInfo _ v _) = [3] ++ u4 v
fval ACC_PUBLIC = 0x0001
fval ACC_PRIVATE = 0x0002
fval ACC_PROTECTED = 0x0004
fval ACC_STATIC = 0x0008
fval ACC_FINAL = 0x0010
fval ACC_SUPER = 0x0020
fval ACC_INTERFACE = 0x0200
fval ACC_ABSTRACT = 0x0400
fval ACC_SYNTHETIC = 0x1000
fval ACC_ENUM = 0x4000
cpEntry (Utf8Info _ _ s _) = [1] ++ u2 (length s) ++ map (fromIntegral . ord) s
cpEntry (ClassInfo _ i _) = [7] ++ u2 i
cpEntry (FieldRefInfo _ c n _) = [9] ++ u2 c ++ u2 n
cpEntry (MethodRefInfo _ c n _) = [10] ++ u2 c ++ u2 n
cpEntry (StringInfo _ i _) = [8] ++ u2 i
cpEntry (IntegerInfo _ v _) = [3] ++ u4 v
cpEntry (NameAndTypeInfo _ n d _) = [12] ++ u2 n ++ u2 d
fieldEntry fi = aflags (fieldAccessFlags fi) ++ u2 (fieldNameIndex fi)
++ u2 (fieldDescIndex fi) ++ u2 (0 :: Int)
methodEntry mi = aflags (methodAccessFlags mi) ++ u2 (methodNameIndex mi)
++ u2 (methodDescIndex mi) ++ u2 (length (methodAttributes mi))
++ concatMap attrEntry (methodAttributes mi)
fieldEntry fi =
aflags (fieldAccessFlags fi)
++ u2 (fieldNameIndex fi)
++ u2 (fieldDescIndex fi)
++ u2 (0 :: Int)
methodEntry mi =
aflags (methodAccessFlags mi)
++ u2 (methodNameIndex mi)
++ u2 (methodDescIndex mi)
++ u2 (length (methodAttributes mi))
++ concatMap attrEntry (methodAttributes mi)
attrEntry (Code_Attribute ni ms ml code exc ca) =
let body = u2 ms ++ u2 ml ++ u4 (length code) ++ code
++ u2 (length exc) ++ concatMap excEntry exc
++ u2 (length ca) ++ concatMap attrEntry ca
let body =
u2 ms
++ u2 ml
++ u4 (length code)
++ code
++ u2 (length exc)
++ concatMap excEntry exc
++ u2 (length ca)
++ concatMap attrEntry ca
in u2 ni ++ u4 (length body) ++ body
attrEntry (Generic_Attribute ni dat) = u2 ni ++ u4 (length dat) ++ dat
excEntry e = u2 (startPc e) ++ u2 (endPc e) ++ u2 (handlerPc e) ++ u2 (catchType e)
+2 -1
View File
@@ -33,9 +33,10 @@ data Stmt
= Block [Stmt]
| Return (Maybe Expr)
| While Expr Stmt
| LocalVarDecl Type String
| LocalVarDecl Type String (Maybe Expr)
| If Expr Stmt (Maybe Stmt)
| StmtExprStmt StmtExpr
| EmptyStmt
deriving (Show, Eq)
data BinaryOperator
+19 -8
View File
@@ -20,6 +20,7 @@ import Grammar.AST
if { TokenIf }
else { TokenElse }
while { TokenWhile }
for { TokenFor }
int { TokenIntType }
boolean { TokenBoolType }
char { TokenCharType }
@@ -109,19 +110,29 @@ Block : '{' Stmts '}' { Block (reverse $2) }
Stmts : Stmts Stmt { $2 : $1 }
| {- leer -} { [] }
Stmt : Block { $1 }
| 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 }
| Type id ';' { LocalVarDecl $1 $2 }
| StmtExpr ';' { StmtExprStmt $1 }
Stmt : Block { $1 }
| return Expr ';' { Return (Just $2) }
| return ';' { Return Nothing }
| while '(' Expr ')' Stmt { While $3 $5 }
| for '(' Stmt Expr ';' StmtExpr ')' Stmt { Block [$3, (While $4 (Block [$8, (StmtExprStmt $6)]))] }
| for '(' Stmt Expr ';' ')' Stmt { Block [$3, (While $4 $7)] }
| if '(' Expr ')' Stmt else Stmt { If $3 $5 (Just $7) }
| if '(' Expr ')' Stmt { If $3 $5 Nothing }
| Type id ';' { LocalVarDecl $1 $2 Nothing }
| Type id '=' Expr ';' { LocalVarDecl $1 $2 (Just $4) }
| StmtExpr ';' { StmtExprStmt $1 }
| ';' { EmptyStmt }
StmtExpr : Expr '=' Expr { Assign $1 $3 }
| new id '(' Exprs ')' { New $2 $4 }
| Expr '.' id '(' Exprs ')' { MethodCall $1 $3 $5 }
| id '(' Exprs ')' { MethodCall This $1 $3 }
| Expr '+' '=' Expr { Assign $1 (Binary Add $1 $4) }
| Expr '-' '=' Expr { Assign $1 (Binary Subtract $1 $4) }
| Expr '*' '=' Expr { Assign $1 (Binary Multiply $1 $4) }
| Expr '/' '=' Expr { Assign $1 (Binary Divide $1 $4) }
| Expr '%' '=' Expr { Assign $1 (Binary Modulo $1 $4) }
Expr : this { This }
| id { LocalOrFieldVar $1 }
@@ -0,0 +1,25 @@
module Testsuite.AbcFiles.ConstructorOverloadTestABC (expectedABC) where
import Codegen.Lowerer (BtcLine (..), BtcProgram (..), Instruction (..))
expectedABC :: [(String, BtcProgram)]
expectedABC =
[
( "<init>",
BtcProgram
[ BtcLine {lineNumber = 0, instruction = ALoad0},
BtcLine {lineNumber = 1, instruction = InvokeSpecial 8},
BtcLine {lineNumber = 4, instruction = Return}
]
),
( "<init>",
BtcProgram
[ BtcLine {lineNumber = 0, instruction = ALoad0},
BtcLine {lineNumber = 1, instruction = InvokeSpecial 8},
BtcLine {lineNumber = 4, instruction = ALoad0},
BtcLine {lineNumber = 5, instruction = ILoad 1},
BtcLine {lineNumber = 6, instruction = PutField 2},
BtcLine {lineNumber = 9, instruction = Return}
]
)
]
@@ -0,0 +1,18 @@
module Testsuite.AbcFiles.ConstructorTestABC (expectedABC) where
import Codegen.Lowerer (BtcLine (..), BtcProgram (..), Instruction (..))
expectedABC :: [(String, BtcProgram)]
expectedABC =
[
( "<init>",
BtcProgram
[ BtcLine {lineNumber = 0, instruction = ALoad0},
BtcLine {lineNumber = 1, instruction = InvokeSpecial 8},
BtcLine {lineNumber = 4, instruction = ALoad0},
BtcLine {lineNumber = 5, instruction = ILoad 1},
BtcLine {lineNumber = 6, instruction = PutField 2},
BtcLine {lineNumber = 9, instruction = Return}
]
)
]
+1 -1
View File
@@ -8,7 +8,7 @@ expectedABC =
( "<init>",
BtcProgram
[ BtcLine {lineNumber = 0, instruction = ALoad0},
BtcLine {lineNumber = 1, instruction = InvokeSpecial 1},
BtcLine {lineNumber = 1, instruction = InvokeSpecial 8},
BtcLine {lineNumber = 4, instruction = Return}
]
)
+18
View File
@@ -0,0 +1,18 @@
module Testsuite.AbcFiles.FieldsTestABC (expectedABC) where
import Codegen.Lowerer (BtcLine (..), BtcProgram (..), Instruction (..))
expectedABC :: [(String, BtcProgram)]
expectedABC =
[
( "<init>",
BtcProgram
[ BtcLine {lineNumber = 0, instruction = ALoad0},
BtcLine {lineNumber = 1, instruction = InvokeSpecial 8},
BtcLine {lineNumber = 4, instruction = ALoad0},
BtcLine {lineNumber = 5, instruction = BIPush 42},
BtcLine {lineNumber = 7, instruction = PutField 15},
BtcLine {lineNumber = 10, instruction = Return}
]
)
]
+27
View File
@@ -0,0 +1,27 @@
module Testsuite.AbcFiles.IfTestABC (expectedABC) where
import Codegen.Lowerer (BtcLine (..), BtcProgram (..), Instruction (..))
expectedABC :: [(String, BtcProgram)]
expectedABC =
[
( "ifElseTest",
BtcProgram
[ BtcLine {lineNumber = 0, instruction = ILoad 0},
BtcLine {lineNumber = 1, instruction = IfGe 6},
BtcLine {lineNumber = 4, instruction = IConst 0},
BtcLine {lineNumber = 5, instruction = IReturn},
BtcLine {lineNumber = 6, instruction = ILoad 0},
BtcLine {lineNumber = 7, instruction = IfNe 14},
BtcLine {lineNumber = 10, instruction = IConst 1},
BtcLine {lineNumber = 11, instruction = IReturn},
BtcLine {lineNumber = 12, instruction = ILoad 0},
BtcLine {lineNumber = 13, instruction = IConst 10},
BtcLine {lineNumber = 14, instruction = IfGt 18},
BtcLine {lineNumber = 17, instruction = IConst 0},
BtcLine {lineNumber = 18, instruction = IReturn},
BtcLine {lineNumber = 19, instruction = IConst 1},
BtcLine {lineNumber = 20, instruction = IReturn}
]
)
]
@@ -0,0 +1,28 @@
module Testsuite.AbcFiles.MultiClassTestABC (expectedABC) where
import Codegen.Lowerer (BtcLine (..), BtcProgram (..), Instruction (..))
expectedABC :: [(String, BtcProgram)]
expectedABC =
[
( "<init>",
BtcProgram
[ BtcLine {lineNumber = 0, instruction = ALoad0},
BtcLine {lineNumber = 1, instruction = InvokeSpecial 8},
BtcLine {lineNumber = 4, instruction = ALoad0},
BtcLine {lineNumber = 5, instruction = ILoad 1},
BtcLine {lineNumber = 6, instruction = PutField 2},
BtcLine {lineNumber = 9, instruction = Return}
]
),
( "doubleIt",
BtcProgram
[ BtcLine {lineNumber = 0, instruction = ALoad0},
BtcLine {lineNumber = 1, instruction = GetField 3},
BtcLine {lineNumber = 4, instruction = IConst 2},
BtcLine {lineNumber = 5, instruction = IMul},
BtcLine {lineNumber = 6, instruction = IReturn}
]
)
]
@@ -0,0 +1,78 @@
module Testsuite.AbcFiles.RecursionTestABC (expectedABC) where
import Codegen.Lowerer (BtcLine (..), BtcProgram (..), Instruction (..))
expectedABC :: [(String, BtcProgram)]
expectedABC =
[ ( "<init>",
BtcProgram
[ BtcLine {lineNumber = 0, instruction = ALoad0},
BtcLine {lineNumber = 1, instruction = InvokeSpecial 1},
BtcLine {lineNumber = 4, instruction = ALoad0},
BtcLine {lineNumber = 5, instruction = ILoad 1},
BtcLine {lineNumber = 6, instruction = PutField 2},
BtcLine {lineNumber = 9, instruction = ILoad 1},
BtcLine {lineNumber = 10, instruction = IfLe 26},
BtcLine {lineNumber = 13, instruction = ALoad0},
BtcLine {lineNumber = 14, instruction = Ldc 3},
BtcLine {lineNumber = 16, instruction = ILoad 1},
BtcLine {lineNumber = 17, instruction = IConst 1},
BtcLine {lineNumber = 18, instruction = ISub},
BtcLine {lineNumber = 19, instruction = InvokeSpecial 4},
BtcLine {lineNumber = 22, instruction = PutField 5},
BtcLine {lineNumber = 25, instruction = Return}
]
),
( "fibonacci",
BtcProgram
[ BtcLine {lineNumber = 0, instruction = ILoad 1},
BtcLine {lineNumber = 1, instruction = IConst 2},
BtcLine {lineNumber = 2, instruction = IfGe 7},
BtcLine {lineNumber = 5, instruction = ILoad 1},
BtcLine {lineNumber = 6, instruction = IReturn},
BtcLine {lineNumber = 7, instruction = ALoad0},
BtcLine {lineNumber = 8, instruction = ILoad 1},
BtcLine {lineNumber = 9, instruction = IConst 1},
BtcLine {lineNumber = 10, instruction = ISub},
BtcLine {lineNumber = 11, instruction = InvokeVirtual 6},
BtcLine {lineNumber = 14, instruction = ALoad0},
BtcLine {lineNumber = 15, instruction = ILoad 1},
BtcLine {lineNumber = 16, instruction = IConst 2},
BtcLine {lineNumber = 17, instruction = ISub},
BtcLine {lineNumber = 18, instruction = InvokeVirtual 6},
BtcLine {lineNumber = 21, instruction = IAdd},
BtcLine {lineNumber = 22, instruction = IReturn}
]
),
( "ackermann",
BtcProgram
[ BtcLine {lineNumber = 0, instruction = ILoad 1},
BtcLine {lineNumber = 1, instruction = IfNe 8},
BtcLine {lineNumber = 4, instruction = ILoad 2},
BtcLine {lineNumber = 5, instruction = IConst 1},
BtcLine {lineNumber = 6, instruction = IAdd},
BtcLine {lineNumber = 7, instruction = IReturn},
BtcLine {lineNumber = 8, instruction = ILoad 2},
BtcLine {lineNumber = 9, instruction = IfNe 21},
BtcLine {lineNumber = 12, instruction = ALoad0},
BtcLine {lineNumber = 13, instruction = ILoad 1},
BtcLine {lineNumber = 14, instruction = IConst 1},
BtcLine {lineNumber = 15, instruction = ISub},
BtcLine {lineNumber = 16, instruction = IConst 1},
BtcLine {lineNumber = 17, instruction = InvokeVirtual 7},
BtcLine {lineNumber = 20, instruction = IReturn},
BtcLine {lineNumber = 21, instruction = ALoad0},
BtcLine {lineNumber = 22, instruction = ILoad 1},
BtcLine {lineNumber = 23, instruction = IConst 1},
BtcLine {lineNumber = 24, instruction = ISub},
BtcLine {lineNumber = 25, instruction = ALoad0},
BtcLine {lineNumber = 26, instruction = ILoad 1},
BtcLine {lineNumber = 27, instruction = ILoad 2},
BtcLine {lineNumber = 28, instruction = IConst 1},
BtcLine {lineNumber = 29, instruction = ISub},
BtcLine {lineNumber = 30, instruction = InvokeVirtual 7},
BtcLine {lineNumber = 33, instruction = InvokeVirtual 7},
BtcLine {lineNumber = 36, instruction = IReturn}
]
)
]
+14
View File
@@ -0,0 +1,14 @@
module Testsuite.AbcFiles.ReturnTestABC (expectedABC) where
import Codegen.Lowerer (BtcLine (..), BtcProgram (..), Instruction (..))
expectedABC :: [(String, BtcProgram)]
expectedABC =
[
( "main",
BtcProgram
[ BtcLine {lineNumber = 0, instruction = BIPush 42},
BtcLine {lineNumber = 1, instruction = IReturn}
]
)
]
@@ -0,0 +1,28 @@
module Testsuite.AbcFiles.SingletonTestABC (expectedABC) where
import Codegen.Lowerer (BtcLine (..), BtcProgram (..), Instruction (..))
expectedABC :: [(String, BtcProgram)]
expectedABC =
[ ( "<init>",
BtcProgram
[ BtcLine {lineNumber = 0, instruction = ALoad0},
BtcLine {lineNumber = 1, instruction = InvokeSpecial 1},
BtcLine {lineNumber = 4, instruction = Return}
]
),
( "getInstance",
BtcProgram
[ BtcLine {lineNumber = 0, instruction = ALoad0},
BtcLine {lineNumber = 1, instruction = GetField 2},
BtcLine {lineNumber = 4, instruction = IfNe 17},
BtcLine {lineNumber = 7, instruction = ALoad0},
BtcLine {lineNumber = 8, instruction = Ldc 3},
BtcLine {lineNumber = 10, instruction = InvokeSpecial 1},
BtcLine {lineNumber = 13, instruction = PutField 2},
BtcLine {lineNumber = 17, instruction = ALoad0},
BtcLine {lineNumber = 18, instruction = GetField 2},
BtcLine {lineNumber = 21, instruction = AReturn}
]
)
]
+27
View File
@@ -0,0 +1,27 @@
module Testsuite.AbcFiles.WhileTestABC (expectedABC) where
import Codegen.Lowerer (BtcLine (..), BtcProgram (..), Instruction (..))
expectedABC :: [(String, BtcProgram)]
expectedABC =
[
( "whileLoopTest",
BtcProgram
[ BtcLine {lineNumber = 0, instruction = IConst 0},
BtcLine {lineNumber = 1, instruction = IStore 1},
BtcLine {lineNumber = 2, instruction = ILoad 0},
BtcLine {lineNumber = 3, instruction = IfLe 16},
BtcLine {lineNumber = 6, instruction = ILoad 1},
BtcLine {lineNumber = 7, instruction = ILoad 0},
BtcLine {lineNumber = 8, instruction = IAdd},
BtcLine {lineNumber = 9, instruction = IStore 1},
BtcLine {lineNumber = 10, instruction = ILoad 0},
BtcLine {lineNumber = 11, instruction = IConst 1},
BtcLine {lineNumber = 12, instruction = ISub},
BtcLine {lineNumber = 13, instruction = IStore 0},
BtcLine {lineNumber = 14, instruction = Goto 2},
BtcLine {lineNumber = 17, instruction = ILoad 1},
BtcLine {lineNumber = 18, instruction = IReturn}
]
)
]
@@ -0,0 +1,20 @@
module Testsuite.AstFiles.ConstructorOverloadTestAST (expectedAST) where
import Grammar.AST
expectedAST =
[ Class
"ConstructorOverloadTest"
[Field "int" "a" (Just (Integer 42))]
[ Method
"void"
"<init>"
[]
(Block []),
Method
"void"
"<init>"
[("int", "a")]
(Block [StmtExprStmt (Assign (InstVar This "a") (LocalOrFieldVar "a"))])
]
]
@@ -0,0 +1,15 @@
module Testsuite.AstFiles.ConstructorTestAST (expectedAST) where
import Grammar.AST
expectedAST =
[ Class
"ConstructorTest"
[Field "int" "a" (Just (Integer (-1)))]
[ Method
"void"
"<init>"
[("int", "initial_value")]
(Block [StmtExprStmt (Assign (LocalOrFieldVar "a") (LocalOrFieldVar "initial_value"))])
]
]
+12
View File
@@ -0,0 +1,12 @@
module Testsuite.AstFiles.FieldsTestAST (expectedAST) where
import Grammar.AST
expectedAST =
[ Class
"FieldsTest"
[ Field "int" "a" Nothing,
Field "int" "b" (Just (Integer 42))
]
[]
]
+27
View File
@@ -0,0 +1,27 @@
module Testsuite.AstFiles.IfTestAST (expectedAST) where
import Grammar.AST
expectedAST =
[ Class
"IfTest"
[]
[ Method
"boolean"
"ifElseTest"
[("int", "x")]
( Block
[ If
(Binary CompLessThan (LocalOrFieldVar "x") (Integer 0))
(Block [Return (Just (Bool False))])
( Just
( If
(Binary CompEqual (LocalOrFieldVar "x") (Integer 0))
(Block [Return (Just (Bool True))])
(Just (Block [Return (Just (Binary CompGreaterThan (LocalOrFieldVar "x") (Integer 10)))]))
)
)
]
)
]
]
@@ -0,0 +1,24 @@
module Testsuite.AstFiles.MultiClassTestAST (expectedAST) where
import Grammar.AST
expectedAST =
[ Class
"MultiClassTest"
[]
[],
Class
"Helper"
[Field "int" "v" Nothing]
[ Method
"void"
"<init>"
[("int", "v0")]
(Block [StmtExprStmt (Assign (LocalOrFieldVar "v") (LocalOrFieldVar "v0"))]),
Method
"int"
"doubleIt"
[]
(Block [Return (Just (Binary Multiply (LocalOrFieldVar "v") (Integer 2)))])
]
]
@@ -0,0 +1,77 @@
module Testsuite.AstFiles.RecursionTestAST (expectedAST) where
import Grammar.AST
expectedAST =
[ Class
"RecursionTest"
[ Field "int" "value" (Just (Integer 0))
, Field "RecursionTest" "child" (Just Null)
]
[ Method
"void"
"<init>"
[("int", "n")]
( Block
[ StmtExprStmt (Assign (LocalOrFieldVar "value") (LocalOrFieldVar "n"))
, If
(Binary CompGreaterThan (LocalOrFieldVar "n") (Integer 0))
( Block
[ StmtExprStmt (Assign (LocalOrFieldVar "child") (StmtExprExpr (New "RecursionTest" [Binary Subtract (LocalOrFieldVar "n") (Integer 1)])))
]
)
Nothing
]
)
, Method
"int"
"fibonacci"
[("int", "n")]
( Block
[ If
(Binary CompLessThan (LocalOrFieldVar "n") (Integer 2))
(Return (Just (LocalOrFieldVar "n")))
( Just
( Block
[ Return
( Just
( Binary
Add
(StmtExprExpr (MethodCall This "fibonacci" [Binary Subtract (LocalOrFieldVar "n") (Integer 1)]))
(StmtExprExpr (MethodCall This "fibonacci" [Binary Subtract (LocalOrFieldVar "n") (Integer 2)]))
)
)
]
)
)
]
)
, Method
"int"
"ackermann"
[("int", "m"), ("int", "n")]
( Block
[ If
(Binary CompEqual (LocalOrFieldVar "m") (Integer 0))
(Return (Just (Binary Add (LocalOrFieldVar "n") (Integer 1))))
Nothing
, If
(Binary CompEqual (LocalOrFieldVar "n") (Integer 0))
(Return (Just (StmtExprExpr (MethodCall This "ackermann" [Binary Subtract (LocalOrFieldVar "m") (Integer 1), Integer 1]))))
Nothing
, Return
( Just
( StmtExprExpr
( MethodCall
This
"ackermann"
[ Binary Subtract (LocalOrFieldVar "m") (Integer 1)
, StmtExprExpr (MethodCall This "ackermann" [LocalOrFieldVar "m", Binary Subtract (LocalOrFieldVar "n") (Integer 1)])
]
)
)
)
]
)
]
]
+15
View File
@@ -0,0 +1,15 @@
module Testsuite.AstFiles.ReturnTestAST (expectedAST) where
import Grammar.AST
expectedAST =
[ Class
"ReturnTest"
[]
[ Method
"int"
"main"
[]
( Block [Return (Just (Integer 42))] )
]
]
@@ -0,0 +1,28 @@
module Testsuite.AstFiles.SingletonTestAST (expectedAST) where
import Grammar.AST
expectedAST =
[ Class
"SingletonTest"
[ Field "SingletonTest" "instance" Nothing
]
[ Method
"void"
"<init>"
[]
(Block [])
, Method
"SingletonTest"
"getInstance"
[]
( Block
[ If
(Binary CompEqual (LocalOrFieldVar "instance") Null)
(Block [StmtExprStmt (Assign (LocalOrFieldVar "instance") (StmtExprExpr (New "SingletonTest" [])))])
Nothing
, Return (Just (LocalOrFieldVar "instance"))
]
)
]
]
+34
View File
@@ -0,0 +1,34 @@
module Testsuite.AstFiles.WhileTestAST (expectedAST) where
import Grammar.AST
expectedAST =
[ Class
"WhileTest"
[]
[ Method
"int"
"whileLoopTest"
[("int", "n")]
( Block
[ LocalVarDecl "int" "sum" Nothing,
While
(Binary CompGreaterThan (LocalOrFieldVar "n") (Integer 0))
( Block
[ StmtExprStmt
(Assign
(LocalOrFieldVar "sum")
(Binary Add (LocalOrFieldVar "sum") (LocalOrFieldVar "n"))
),
StmtExprStmt
(Assign
(LocalOrFieldVar "n")
(Binary Subtract (LocalOrFieldVar "n") (Integer 1))
)
]
),
Return (Just (LocalOrFieldVar "sum"))
]
)
]
]
@@ -1,63 +0,0 @@
import Control.Exception (throwIO)
import Control.Monad (unless)
import Data.List (sort, isSuffixOf)
import System.Directory (doesDirectoryExist, listDirectory, doesFileExist)
import System.Exit (ExitCode (..), exitFailure)
import System.FilePath (takeExtension, takeFileName, (</>), isExtensionOf, takeDirectory)
import System.Process (readProcessWithExitCode)
import Testsuite.ExecutableTests.HarnessSupport (TestFailure (..), exitOnFailures, printTestResults, runNamedTests)
main :: IO ()
main = do
let executableTestsDir = "src/Testsuite/ExecutableTests"
directoryExists <- doesDirectoryExist executableTestsDir
unless directoryExists $ do
putStrLn ("ExecutableTests directory not found: " ++ executableTestsDir)
exitFailure
harnessFiles <- discoverHarnessFiles executableTestsDir
if null harnessFiles
then do
putStrLn "No harness files found to execute."
exitFailure
else do
let namedHarnesses =
[ (takeFileName harnessFile, runHarness harnessFile)
| harnessFile <- harnessFiles
]
results <- runNamedTests namedHarnesses
printTestResults "Full Testsuite Harness" results
exitOnFailures results
discoverHarnessFiles :: FilePath -> IO [FilePath]
discoverHarnessFiles baseDir = do
entries <- listDirectory baseDir
harnessFiles <- concat <$> mapM (findHarnessesInDir baseDir) entries
pure (sort harnessFiles)
findHarnessesInDir :: FilePath -> String -> IO [FilePath]
findHarnessesInDir baseDir entry = do
let fullPath = baseDir </> entry
isDir <- doesDirectoryExist fullPath
if isDir && entry /= "." && entry /= ".."
then do
subEntries <- listDirectory fullPath
let harnesses =
[ fullPath </> file
| file <- subEntries,
takeExtension file == ".hs",
"Harness.hs" `isSuffixOf` file,
file /= "AllHarness.hs"
]
pure harnesses
else pure []
runHarness :: FilePath -> IO ()
runHarness harnessPath = do
(exitCode, stdOut, stdErr) <- readProcessWithExitCode "runghc" ["-isrc", harnessPath] ""
putStrLn ("\n--- Output from " ++ takeFileName harnessPath ++ " ---")
unless (null stdOut) (putStrLn stdOut)
unless (null stdErr) (putStrLn stdErr)
case exitCode of
ExitSuccess -> pure ()
ExitFailure _ -> throwIO (TestFailure "")
@@ -1,30 +0,0 @@
module Testsuite.ExecutableTests.Arithmetic.ArithmeticABCTest where
import Codegen.ClassFile (generateClassFile)
import Codegen.Lowerer (BtcProgram, lowerClassFile)
import Control.Exception (throwIO)
import Grammar.TAST (TypedClass)
import Testsuite.AbcFiles.ArithmeticTestABC (expectedABC)
import Testsuite.ExecutableTests.HarnessSupport (TestFailure (..))
import Testsuite.TastFiles.ArithmeticTestTAST (expectedTAST)
mainArithmeticABCTest :: IO ()
mainArithmeticABCTest = do
let actualABC = generateABCProgram expectedTAST
if actualABC == expectedABC
then pure ()
else
throwIO
( TestFailure
( unlines
[ "Actual ABC:",
show actualABC,
"Expected ABC:",
show expectedABC
]
)
)
generateABCProgram :: [TypedClass] -> [(String, BtcProgram)]
generateABCProgram [typedClass] = lowerClassFile (generateClassFile typedClass)
generateABCProgram _ = error "Expected exactly one typed class for the ABC test"
@@ -1,27 +0,0 @@
module Testsuite.ExecutableTests.Arithmetic.ArithmeticASTTest where
import Grammar.Parser
import Grammar.Scanner
import Control.Exception (throwIO)
import Testsuite.AstFiles.ArithmeticTestAST (expectedAST)
import Testsuite.ExecutableTests.HarnessSupport (TestFailure (..))
mainArithmeticASTTest :: IO ()
mainArithmeticASTTest = do
let javaFilePath = "src/Testsuite/javaFiles/ArithmeticTest.java"
java <- readFile javaFilePath
let actualAST = parse . alexScanTokens $ java
if actualAST == expectedAST
then pure ()
else
throwIO
( TestFailure
( unlines
[ "Arithmetic AST test failed.",
"Actual AST:",
show actualAST,
"Expected AST:",
show expectedAST
]
)
)
@@ -1,227 +0,0 @@
module Testsuite.ExecutableTests.Arithmetic.ArithmeticBytecodeBytesTest where
import Codegen.Lowerer (BtcProgram)
import Codegen.Serializer (serializeProgram)
import Control.Exception (throwIO)
import Data.Bits ((.|.), shiftL)
import qualified Data.ByteString as BS
import qualified Data.Map.Strict as Map
import Data.Word (Word16, Word32, Word8)
import Testsuite.AbcFiles.ArithmeticTestABC (expectedABC)
import Testsuite.ExecutableTests.HarnessSupport (TestFailure (..))
mainArithmeticBytecodeBytesTest :: IO ()
mainArithmeticBytecodeBytesTest = do
let classFilePath = "src/Testsuite/classFiles/ArithmeticTest.class"
methodCodes <- parseMethodCodeBytesFromFile classFilePath
failures <- fmap concat (mapM (checkMethod methodCodes) expectedABC)
if null failures
then pure ()
else throwIO (TestFailure (unlines failures))
checkMethod :: [(String, [Word8])] -> (String, BtcProgram) -> IO [String]
checkMethod methodCodes (methodName, expectedProgram) = do
let expectedBytes = serializeProgram expectedProgram
maybeActualBytes = lookup methodName methodCodes
case maybeActualBytes of
Nothing ->
pure
[ "Method '" ++ methodName ++ "' not found in reference class file"
]
Just actualBytes ->
if expectedBytes == actualBytes
then pure []
else
pure ["Method '" ++ methodName ++ "' bytecode mismatch"]
newtype Parser a = Parser
{ runParser :: [Word8] -> Either String (a, [Word8])
}
instance Functor Parser where
fmap f p = Parser $ \input -> do
(value, rest) <- runParser p input
pure (f value, rest)
instance Applicative Parser where
pure value = Parser $ \input -> Right (value, input)
pf <*> px = Parser $ \input -> do
(f, rest1) <- runParser pf input
(x, rest2) <- runParser px rest1
pure (f x, rest2)
instance Monad Parser where
p >>= f = Parser $ \input -> do
(value, rest) <- runParser p input
runParser (f value) rest
parseFail :: String -> Parser a
parseFail msg = Parser $ \_ -> Left msg
u1 :: Parser Word8
u1 = Parser $ \input ->
case input of
[] -> Left "Unexpected end of input while reading u1"
(b : rest) -> Right (b, rest)
u2 :: Parser Word16
u2 = do
hi <- u1
lo <- u1
pure ((fromIntegral hi `shiftL` 8) .|. fromIntegral lo)
u4 :: Parser Word32
u4 = do
b1 <- u1
b2 <- u1
b3 <- u1
b4 <- u1
pure
( (fromIntegral b1 `shiftL` 24)
.|. (fromIntegral b2 `shiftL` 16)
.|. (fromIntegral b3 `shiftL` 8)
.|. fromIntegral b4
)
takeN :: Int -> Parser [Word8]
takeN n
| n < 0 = parseFail "Negative takeN requested"
| otherwise = Parser $ \input ->
if length input < n
then Left ("Unexpected end of input while reading " ++ show n ++ " bytes")
else Right (splitAt n input)
skipN :: Int -> Parser ()
skipN n = do
_ <- takeN n
pure ()
parseMethodCodeBytesFromFile :: FilePath -> IO [(String, [Word8])]
parseMethodCodeBytesFromFile path = do
bytes <- BS.readFile path
case runParser parseClassFile (BS.unpack bytes) of
Left err -> error ("Failed parsing class file '" ++ path ++ "': " ++ err)
Right (methods, _) -> pure methods
parseClassFile :: Parser [(String, [Word8])]
parseClassFile = do
magic <- u4
if magic /= 0xCAFEBABE
then parseFail "Invalid class file magic"
else pure ()
_minorVersion <- u2
_majorVersion <- u2
cpCount <- fromIntegral <$> u2
utf8Map <- parseConstantPool cpCount 1 Map.empty
_accessFlags <- u2
_thisClass <- u2
_superClass <- u2
interfacesCount <- fromIntegral <$> u2
skipN (interfacesCount * 2)
fieldsCount <- fromIntegral <$> u2
skipMembers fieldsCount
methodsCount <- fromIntegral <$> u2
parseMethods utf8Map methodsCount
parseConstantPool :: Int -> Int -> Map.Map Int String -> Parser (Map.Map Int String)
parseConstantPool cpCount idx utf8Map
| idx >= cpCount = pure utf8Map
| otherwise = do
tag <- u1
case tag of
1 -> do
len <- fromIntegral <$> u2
bytes <- takeN len
let value = map (toEnum . fromIntegral) bytes
parseConstantPool cpCount (idx + 1) (Map.insert idx value utf8Map)
3 -> skipN 4 >> parseConstantPool cpCount (idx + 1) utf8Map
4 -> skipN 4 >> parseConstantPool cpCount (idx + 1) utf8Map
5 -> skipN 8 >> parseConstantPool cpCount (idx + 2) utf8Map
6 -> skipN 8 >> parseConstantPool cpCount (idx + 2) utf8Map
7 -> skipN 2 >> parseConstantPool cpCount (idx + 1) utf8Map
8 -> skipN 2 >> parseConstantPool cpCount (idx + 1) utf8Map
9 -> skipN 4 >> parseConstantPool cpCount (idx + 1) utf8Map
10 -> skipN 4 >> parseConstantPool cpCount (idx + 1) utf8Map
11 -> skipN 4 >> parseConstantPool cpCount (idx + 1) utf8Map
12 -> skipN 4 >> parseConstantPool cpCount (idx + 1) utf8Map
15 -> skipN 3 >> parseConstantPool cpCount (idx + 1) utf8Map
16 -> skipN 2 >> parseConstantPool cpCount (idx + 1) utf8Map
17 -> skipN 4 >> parseConstantPool cpCount (idx + 1) utf8Map
18 -> skipN 4 >> parseConstantPool cpCount (idx + 1) utf8Map
19 -> skipN 2 >> parseConstantPool cpCount (idx + 1) utf8Map
20 -> skipN 2 >> parseConstantPool cpCount (idx + 1) utf8Map
_ -> parseFail ("Unknown constant pool tag " ++ show tag)
skipMembers :: Int -> Parser ()
skipMembers 0 = pure ()
skipMembers n = do
_accessFlags <- u2
_nameIndex <- u2
_descriptorIndex <- u2
attrCount <- fromIntegral <$> u2
skipAttributes attrCount
skipMembers (n - 1)
skipAttributes :: Int -> Parser ()
skipAttributes 0 = pure ()
skipAttributes n = do
_attrNameIndex <- u2
attrLen <- fromIntegral <$> u4
skipN attrLen
skipAttributes (n - 1)
parseMethods :: Map.Map Int String -> Int -> Parser [(String, [Word8])]
parseMethods _ 0 = pure []
parseMethods utf8Map n = do
_accessFlags <- u2
nameIndex <- fromIntegral <$> u2
_descriptorIndex <- u2
attrCount <- fromIntegral <$> u2
let methodName = Map.findWithDefault ("<unknown-" ++ show nameIndex ++ ">") nameIndex utf8Map
maybeCode <- parseMethodAttributes utf8Map attrCount
rest <- parseMethods utf8Map (n - 1)
case maybeCode of
Nothing -> pure rest
Just code -> pure ((methodName, code) : rest)
parseMethodAttributes :: Map.Map Int String -> Int -> Parser (Maybe [Word8])
parseMethodAttributes _ 0 = pure Nothing
parseMethodAttributes utf8Map n = do
attrNameIndex <- fromIntegral <$> u2
attrLen <- fromIntegral <$> u4
let attrName = Map.findWithDefault "" attrNameIndex utf8Map
current <-
if attrName == "Code"
then parseCodeAttribute attrLen
else skipN attrLen >> pure Nothing
next <- parseMethodAttributes utf8Map (n - 1)
pure (pickFirst current next)
pickFirst :: Maybe a -> Maybe a -> Maybe a
pickFirst (Just x) _ = Just x
pickFirst Nothing y = y
parseCodeAttribute :: Int -> Parser (Maybe [Word8])
parseCodeAttribute _declaredLength = do
_maxStack <- u2
_maxLocals <- u2
codeLen <- fromIntegral <$> u4
codeBytes <- takeN codeLen
exceptionTableLength <- fromIntegral <$> u2
skipN (exceptionTableLength * 8)
nestedAttrCount <- fromIntegral <$> u2
skipAttributes nestedAttrCount
pure (Just codeBytes)
@@ -1,19 +0,0 @@
module Testsuite.ExecutableTests.Arithmetic.ArithmeticHarness where
import Testsuite.ExecutableTests.Arithmetic.ArithmeticABCTest (mainArithmeticABCTest)
import Testsuite.ExecutableTests.Arithmetic.ArithmeticASTTest (mainArithmeticASTTest)
import Testsuite.ExecutableTests.Arithmetic.ArithmeticBytecodeBytesTest (mainArithmeticBytecodeBytesTest)
import Testsuite.ExecutableTests.Arithmetic.ArithmeticTASTTest (mainArithmeticTASTTest)
import Testsuite.ExecutableTests.HarnessSupport
main :: IO ()
main = do
let tests =
[ ("Arithmetic AST", mainArithmeticASTTest),
("Arithmetic TAST", mainArithmeticTASTTest),
("Arithmetic ABC", mainArithmeticABCTest),
("Arithmetic Bytecode Bytes", mainArithmeticBytecodeBytesTest)
]
results <- runNamedTests tests
printTestResults "Arithmetic Harness" results
exitOnFailures results
@@ -1,29 +0,0 @@
module Testsuite.ExecutableTests.Arithmetic.ArithmeticTASTTest where
import Control.Exception (throwIO)
import Grammar.AST (Class)
import Grammar.TAST (TypedClass)
import Testsuite.AstFiles.ArithmeticTestAST (expectedAST)
import Testsuite.TastFiles.ArithmeticTestTAST (expectedTAST)
import Testsuite.ExecutableTests.HarnessSupport (TestFailure (..))
import Typecheck.SemanticChecker (typeCheckClass)
mainArithmeticTASTTest :: IO ()
mainArithmeticTASTTest = do
let actualTAST = typeCheckProgram expectedAST
if actualTAST == expectedTAST
then pure ()
else
throwIO
( TestFailure
( unlines
[ "Actual TAST:",
show actualTAST,
"Expected TAST:",
show expectedTAST
]
)
)
typeCheckProgram :: [Class] -> [TypedClass]
typeCheckProgram classes = map (\cls -> typeCheckClass cls [] classes) classes
@@ -1,30 +0,0 @@
module Testsuite.ExecutableTests.Empty.EmptyABCTest where
import Codegen.ClassFile (generateClassFile)
import Codegen.Lowerer (BtcProgram, lowerClassFile)
import Control.Exception (throwIO)
import Grammar.TAST (TypedClass)
import Testsuite.AbcFiles.EmptyTestABC (expectedABC)
import Testsuite.ExecutableTests.HarnessSupport (TestFailure (..))
import Testsuite.TastFiles.EmptyTestTAST (expectedTAST)
mainEmptyABCTest :: IO ()
mainEmptyABCTest = do
let actualABC = generateABCProgram expectedTAST
if actualABC == expectedABC
then pure ()
else
throwIO
( TestFailure
( unlines
[ "Actual ABC:",
show actualABC,
"Expected ABC:",
show expectedABC
]
)
)
generateABCProgram :: [TypedClass] -> [(String, BtcProgram)]
generateABCProgram [typedClass] = lowerClassFile (generateClassFile typedClass)
generateABCProgram _ = error "Expected exactly one typed class for the ABC test"
@@ -1,27 +0,0 @@
module Testsuite.ExecutableTests.Empty.EmptyASTTest where
import Control.Exception (throwIO)
import Grammar.Parser
import Grammar.Scanner
import Testsuite.AstFiles.EmptyTestAST (expectedAST)
import Testsuite.ExecutableTests.HarnessSupport (TestFailure (..))
mainEmptyASTTest :: IO ()
mainEmptyASTTest = do
let javaFilePath = "src/Testsuite/javaFiles/EmptyTest.java"
java <- readFile javaFilePath
let actualAST = parse . alexScanTokens $ java
if actualAST == expectedAST
then pure ()
else
throwIO
( TestFailure
( unlines
[ "Empty AST test failed.",
"Actual AST:",
show actualAST,
"Expected AST:",
show expectedAST
]
)
)
@@ -1,19 +0,0 @@
module Testsuite.ExecutableTests.Empty.EmptyHarness where
import Testsuite.ExecutableTests.Empty.EmptyABCTest (mainEmptyABCTest)
import Testsuite.ExecutableTests.Empty.EmptyASTTest (mainEmptyASTTest)
import Testsuite.ExecutableTests.Empty.EmptyBytecodeBytesTest (mainEmptyBytecodeBytesTest)
import Testsuite.ExecutableTests.Empty.EmptyTASTTest (mainEmptyTASTTest)
import Testsuite.ExecutableTests.HarnessSupport
main :: IO ()
main = do
let tests =
[ ("Empty AST", mainEmptyASTTest),
("Empty TAST", mainEmptyTASTTest),
("Empty ABC", mainEmptyABCTest),
("Empty Bytecode Bytes", mainEmptyBytecodeBytesTest)
]
results <- runNamedTests tests
printTestResults "Empty Harness" results
exitOnFailures results
@@ -1,29 +0,0 @@
module Testsuite.ExecutableTests.Empty.EmptyTASTTest where
import Control.Exception (throwIO)
import Grammar.AST (Class)
import Grammar.TAST (TypedClass)
import Testsuite.AstFiles.EmptyTestAST (expectedAST)
import Testsuite.ExecutableTests.HarnessSupport (TestFailure (..))
import Testsuite.TastFiles.EmptyTestTAST (expectedTAST)
import Typecheck.SemanticChecker (typeCheckClass)
mainEmptyTASTTest :: IO ()
mainEmptyTASTTest = do
let actualTAST = typeCheckProgram expectedAST
if actualTAST == expectedTAST
then pure ()
else
throwIO
( TestFailure
( unlines
[ "Actual TAST:",
show actualTAST,
"Expected TAST:",
show expectedTAST
]
)
)
typeCheckProgram :: [Class] -> [TypedClass]
typeCheckProgram classes = map (\cls -> typeCheckClass cls [] classes) classes
@@ -70,10 +70,14 @@ printResult :: TestResult -> IO ()
printResult (TestResult name True _) =
putStrLn ("[PASS] " ++ name)
printResult (TestResult name False info) = do
putStrLn ("[FAIL] " ++ name)
if null info
let ls = lines info
firstLine = if null ls then "" else head ls
rest = if null ls then [] else tail ls
header = "[FAIL] " ++ name ++ (if null firstLine then "" else " - " ++ firstLine)
putStrLn header
if null rest
then pure ()
else mapM_ (putStrLn . (" " ++)) (lines info)
else mapM_ (putStrLn . (" " ++)) rest
exitOnFailures :: [TestResult] -> IO ()
exitOnFailures results =
+79
View File
@@ -0,0 +1,79 @@
module Testsuite.ExecutableTests.Main where
import System.Environment (getArgs)
import System.Exit (exitFailure)
import Control.Monad (forM)
import Control.Exception (try, SomeException)
import Testsuite.ExecutableTests.TestCase
import Testsuite.ExecutableTests.Registry
import Testsuite.ExecutableTests.Runner.AST
import Testsuite.ExecutableTests.Runner.TAST
import Testsuite.ExecutableTests.Runner.ABC
import Testsuite.ExecutableTests.Runner.Bytecode
import Testsuite.ExecutableTests.HarnessSupport (TestResult(..), printTestResults, exitOnFailures, runNamedTests)
main :: IO ()
main = do
args <- getArgs
case args of
[] -> runAll
[testName] -> runSpecific testName
_ -> do
putStrLn "Usage: runghc -isrc src/Testsuite/ExecutableTests/Main.hs [testName]"
putStrLn " No argument: run all tests"
putStrLn " testName: run specific test (e.g., EmptyTest)"
exitFailure
runAll :: IO ()
runAll = do
resultLists <- forM allTests $ \tc -> do
putStrLn ""
putStrLn ("=== " ++ tcName tc ++ " ===")
eres <- try (runTest tc) :: IO (Either SomeException [TestResult])
case eres of
Right rs -> pure rs
Left ex -> do
let msg = unwords (words (show ex))
putStrLn ("[ERROR] " ++ tcName tc ++ " - " ++ msg)
pure [TestResult (tcName tc) False msg]
let results = concat resultLists
printTestResults "Full Testsuite" results
-- Per-test summary
putStrLn "\n=== Summary ==="
let perTest = zip allTests resultLists
mapM_ (\(tc, rs) -> do
let total = length rs
passedCount = length (filter passed rs)
status = if passedCount == total then "PASS" else "FAIL"
putStrLn ("[" ++ status ++ "] " ++ tcName tc ++ " - " ++ show passedCount ++ "/" ++ show total ++ " tests passed")
) perTest
exitOnFailures results
runSpecific :: String -> IO ()
runSpecific testName =
case findTestByName testName allTests of
Nothing -> do
putStrLn $ "Test not found: " ++ testName
putStrLn "Available tests:"
mapM_ (putStrLn . (" - " ++) . tcName) allTests
exitFailure
Just tc -> do
results <- runTest tc
printTestResults testName results
exitOnFailures results
findTestByName :: String -> [TestCase] -> Maybe TestCase
findTestByName _ [] = Nothing
findTestByName name (tc:tcs)
| tcName tc == name = Just tc
| otherwise = findTestByName name tcs
runTest :: TestCase -> IO [TestResult]
runTest tc =
let prefix = tcName tc ++ " "
in runNamedTests
[ (prefix ++ "AST", runASTTest tc)
, (prefix ++ "TAST", runTASTTest tc)
, (prefix ++ "ABC", runABCTest tc)
, (prefix ++ "Bytecode", runBytecodeTest tc)
]
+125
View File
@@ -0,0 +1,125 @@
module Testsuite.ExecutableTests.Registry where
import Testsuite.ExecutableTests.TestCase
import qualified Testsuite.AstFiles.EmptyTestAST as EmptyAST
import qualified Testsuite.TastFiles.EmptyTestTAST as EmptyTAST
import qualified Testsuite.AbcFiles.EmptyTestABC as EmptyABC
import qualified Testsuite.AstFiles.ArithmeticTestAST as ArithmeticAST
import qualified Testsuite.TastFiles.ArithmeticTestTAST as ArithmeticTAST
import qualified Testsuite.AbcFiles.ArithmeticTestABC as ArithmeticABC
import qualified Testsuite.AstFiles.FieldsTestAST as FieldsAST
import qualified Testsuite.TastFiles.FieldsTestTAST as FieldsTAST
import qualified Testsuite.AbcFiles.FieldsTestABC as FieldsABC
import qualified Testsuite.AstFiles.IfTestAST as IfAST
import qualified Testsuite.TastFiles.IfTestTAST as IfTAST
import qualified Testsuite.AbcFiles.IfTestABC as IfABC
import qualified Testsuite.AstFiles.WhileTestAST as WhileAST
import qualified Testsuite.TastFiles.WhileTestTAST as WhileTAST
import qualified Testsuite.AbcFiles.WhileTestABC as WhileABC
import qualified Testsuite.AstFiles.ConstructorTestAST as ConstructorAST
import qualified Testsuite.TastFiles.ConstructorTestTAST as ConstructorTAST
import qualified Testsuite.AbcFiles.ConstructorTestABC as ConstructorABC
import qualified Testsuite.AstFiles.ConstructorOverloadTestAST as ConstructorOverloadAST
import qualified Testsuite.TastFiles.ConstructorOverloadTestTAST as ConstructorOverloadTAST
import qualified Testsuite.AbcFiles.ConstructorOverloadTestABC as ConstructorOverloadABC
import qualified Testsuite.AstFiles.MultiClassTestAST as MultiClassAST
import qualified Testsuite.TastFiles.MultiClassTestTAST as MultiClassTAST
import qualified Testsuite.AbcFiles.MultiClassTestABC as MultiClassABC
import Codegen.Serializer (serializeProgram)
import Data.Word (Word8)
import qualified Codegen.Lowerer
toBytecode :: [(String, Codegen.Lowerer.BtcProgram)] -> [(String, [Word8])]
toBytecode = map (fmap (serializeProgram))
emptyTest :: TestCase
emptyTest =
TestCase
{ tcName = "EmptyTest"
, tcJavaFile = "src/Testsuite/javaFiles/EmptyTest.java"
, tcExpectedAST = EmptyAST.expectedAST
, tcExpectedTAST = EmptyTAST.expectedTAST
, tcExpectedABC = EmptyABC.expectedABC
, tcExpectedBytecode = toBytecode EmptyABC.expectedABC
}
arithmeticTest :: TestCase
arithmeticTest =
TestCase
{ tcName = "ArithmeticTest"
, tcJavaFile = "src/Testsuite/javaFiles/ArithmeticTest.java"
, tcExpectedAST = ArithmeticAST.expectedAST
, tcExpectedTAST = ArithmeticTAST.expectedTAST
, tcExpectedABC = ArithmeticABC.expectedABC
, tcExpectedBytecode = toBytecode ArithmeticABC.expectedABC
}
fieldsTest :: TestCase
fieldsTest =
TestCase
{ tcName = "FieldsTest"
, tcJavaFile = "src/Testsuite/javaFiles/FieldsTest.java"
, tcExpectedAST = FieldsAST.expectedAST
, tcExpectedTAST = FieldsTAST.expectedTAST
, tcExpectedABC = FieldsABC.expectedABC
, tcExpectedBytecode = toBytecode FieldsABC.expectedABC
}
ifTest :: TestCase
ifTest =
TestCase
{ tcName = "IfTest"
, tcJavaFile = "src/Testsuite/javaFiles/IfTest.java"
, tcExpectedAST = IfAST.expectedAST
, tcExpectedTAST = IfTAST.expectedTAST
, tcExpectedABC = IfABC.expectedABC
, tcExpectedBytecode = toBytecode IfABC.expectedABC
}
whileTest :: TestCase
whileTest =
TestCase
{ tcName = "WhileTest"
, tcJavaFile = "src/Testsuite/javaFiles/WhileTest.java"
, tcExpectedAST = WhileAST.expectedAST
, tcExpectedTAST = WhileTAST.expectedTAST
, tcExpectedABC = WhileABC.expectedABC
, tcExpectedBytecode = toBytecode WhileABC.expectedABC
}
allTests :: [TestCase]
allTests = [emptyTest, arithmeticTest, fieldsTest, ifTest, whileTest, constructorTest, constructorOverloadTest, multiClassTest]
constructorTest :: TestCase
constructorTest =
TestCase
{ tcName = "ConstructorTest"
, tcJavaFile = "src/Testsuite/javaFiles/ConstructorTest.java"
, tcExpectedAST = ConstructorAST.expectedAST
, tcExpectedTAST = ConstructorTAST.expectedTAST
, tcExpectedABC = ConstructorABC.expectedABC
, tcExpectedBytecode = toBytecode ConstructorABC.expectedABC
}
constructorOverloadTest :: TestCase
constructorOverloadTest =
TestCase
{ tcName = "ConstructorOverloadTest"
, tcJavaFile = "src/Testsuite/javaFiles/ConstructorOverloadTest.java"
, tcExpectedAST = ConstructorOverloadAST.expectedAST
, tcExpectedTAST = ConstructorOverloadTAST.expectedTAST
, tcExpectedABC = ConstructorOverloadABC.expectedABC
, tcExpectedBytecode = toBytecode ConstructorOverloadABC.expectedABC
}
multiClassTest :: TestCase
multiClassTest =
TestCase
{ tcName = "MultiClassTest"
, tcJavaFile = "src/Testsuite/javaFiles/MultiClassTest.java"
, tcExpectedAST = MultiClassAST.expectedAST
, tcExpectedTAST = MultiClassTAST.expectedTAST
, tcExpectedABC = MultiClassABC.expectedABC
, tcExpectedBytecode = toBytecode MultiClassABC.expectedABC
}
@@ -0,0 +1,24 @@
module Testsuite.ExecutableTests.Runner.ABC where
import Control.Exception (throwIO)
import Codegen.ClassFile (generateClassFile)
import Codegen.Lowerer (lowerClassFile)
import Testsuite.ExecutableTests.TestCase
import Testsuite.ExecutableTests.HarnessSupport (TestFailure(..))
runABCTest :: TestCase -> IO ()
runABCTest tc =
case tcExpectedTAST tc of
[t] ->
let actual = lowerClassFile (generateClassFile t)
in if actual == tcExpectedABC tc
then pure ()
else
let body =
tcName tc
++ " ABC mismatch\nActual ABC:\n"
++ show actual
++ "\nExpected ABC:\n"
++ show (tcExpectedABC tc)
in throwIO $ TestFailure body
_ -> error "ABC test expects exactly one class"
@@ -0,0 +1,20 @@
module Testsuite.ExecutableTests.Runner.AST where
import Control.Exception (throwIO)
import Grammar.Parser
import Grammar.Scanner
import Testsuite.ExecutableTests.TestCase
import Testsuite.ExecutableTests.HarnessSupport (TestFailure(..))
runASTTest :: TestCase -> IO ()
runASTTest tc = do
java <- readFile (tcJavaFile tc)
let actual = parse . alexScanTokens $ java
if actual == tcExpectedAST tc
then pure ()
else throwIO $
TestFailure $
tcName tc ++ " AST mismatch\n"
++ "Actual: " ++ show actual ++ "\n"
++ "Expected: " ++ show (tcExpectedAST tc)
@@ -0,0 +1,36 @@
module Testsuite.ExecutableTests.Runner.Bytecode where
import Control.Exception (throwIO)
import System.FilePath (takeFileName, replaceExtension)
import Testsuite.ExecutableTests.TestCase
import Testsuite.ExecutableTests.HarnessSupport (TestFailure(..))
import Testsuite.ExecutableTests.Shared.BytecodeNormalize
import Testsuite.ExecutableTests.Shared.ClassFileParser
import Data.Word (Word8)
import Data.List (intercalate)
bytesToHex :: [Word8] -> String
bytesToHex bs = intercalate " " $ map (\b -> let h = showHex b in if length h == 1 then '0':h else h) (map fromIntegral bs)
showHex :: Int -> String
showHex n = let hex = "0123456789ABCDEF" in [hex !! (n `div` 16), hex !! (n `mod` 16)]
runBytecodeTest :: TestCase -> IO ()
runBytecodeTest tc = do
let javaFilePath = tcJavaFile tc
javaFileName = takeFileName javaFilePath
classFilePath = "src/Testsuite/classFiles/" ++ replaceExtension javaFileName ".class"
actual <- parseMethodCodeBytesFromFile classFilePath
let expected = tcExpectedBytecode tc
let nExpected = map (fmap normalizeBytecode) expected
nActual = map (fmap normalizeBytecode) actual
if nExpected == nActual
then pure ()
else
let formatEntry (n, bs) = n ++ ": " ++ bytesToHex bs
expectedLines = map formatEntry nExpected
actualLines = map formatEntry nActual
body = "bytecode mismatch for " ++ tcName tc ++ "\nExpected:\n" ++ unlines expectedLines ++ "Actual:\n" ++ unlines actualLines
in throwIO $ TestFailure body
@@ -0,0 +1,18 @@
module Testsuite.ExecutableTests.Runner.TAST where
import Control.Exception (throwIO)
import Typecheck.SemanticChecker (typeCheckClass)
import Testsuite.ExecutableTests.TestCase
import Testsuite.ExecutableTests.HarnessSupport (TestFailure(..))
runTASTTest :: TestCase -> IO ()
runTASTTest tc =
let actual =
map (\c -> typeCheckClass c [] (tcExpectedAST tc))
(tcExpectedAST tc)
in if actual == tcExpectedTAST tc
then pure ()
else throwIO $
TestFailure $
tcName tc ++ " TAST mismatch\n"
@@ -0,0 +1,17 @@
module Testsuite.ExecutableTests.Shared.BytecodeNormalize where
import Data.Word (Word8)
-- | Normalize bytecode by replacing constant pool indices with a fixed placeholder (0xFF)
normalizeBytecode :: [Word8] -> [Word8]
normalizeBytecode [] = []
normalizeBytecode (op:rest)
-- 1-byte constant pool index instructions: ldc
| op `elem` [0x12] && not (null rest) =
op : 0xFF : normalizeBytecode (tail rest)
-- 2-byte constant pool index instructions
| op `elem` [0x13, 0x14, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8] && length rest >= 2 =
let (_i1:_i2:restBytes) = rest
in op : 0xFF : 0xFF : normalizeBytecode restBytes
-- lookupswitch, tableswitch, wide, etc. could be handled here if needed
| otherwise = op : normalizeBytecode rest
@@ -1,59 +1,50 @@
module Testsuite.ExecutableTests.Empty.EmptyBytecodeBytesTest where
{-# LANGUAGE LambdaCase #-}
module Testsuite.ExecutableTests.Shared.ClassFileParser
( parseClassFile
, parseMethodCodeBytesFromFile
) where
import Codegen.Lowerer (BtcProgram)
import Codegen.Serializer (serializeProgram)
import Control.Exception (throwIO)
import Data.Bits ((.|.), shiftL)
import qualified Data.ByteString as BS
import qualified Data.Map.Strict as Map
import Data.Bits ((.|.), shiftL)
import Data.Word (Word16, Word32, Word8)
import Testsuite.AbcFiles.EmptyTestABC (expectedABC)
import Testsuite.ExecutableTests.HarnessSupport (TestFailure (..))
import Control.Monad (MonadFail)
mainEmptyBytecodeBytesTest :: IO ()
mainEmptyBytecodeBytesTest = do
let classFilePath = "src/Testsuite/classFiles/EmptyTest.class"
methodCodes <- parseMethodCodeBytesFromFile classFilePath
failures <- fmap concat (mapM (checkMethod methodCodes) expectedABC)
if null failures
then pure ()
else throwIO (TestFailure (unlines failures))
parseClassFile :: BS.ByteString -> Either String [(String, [Word8])]
parseClassFile bs =
case runParser parseClassFileParser (BS.unpack bs) of
Left e -> Left e
Right (r, _) -> Right r
checkMethod :: [(String, [Word8])] -> (String, BtcProgram) -> IO [String]
checkMethod methodCodes (methodName, expectedProgram) = do
let expectedBytes = serializeProgram expectedProgram
maybeActualBytes = lookup methodName methodCodes
case maybeActualBytes of
Nothing ->
pure
[ "Method '" ++ methodName ++ "' not found in reference class file"
]
Just actualBytes ->
if expectedBytes == actualBytes
then pure []
else
pure ["Method '" ++ methodName ++ "' bytecode mismatch"]
parseMethodCodeBytesFromFile :: FilePath -> IO [(String, [Word8])]
parseMethodCodeBytesFromFile path = do
bytes <- BS.readFile path
case runParser parseClassFileParser (BS.unpack bytes) of
Left err -> error ("Failed parsing class file '" ++ path ++ "': " ++ err)
Right (methods, _) -> pure methods
newtype Parser a = Parser
{ runParser :: [Word8] -> Either String (a, [Word8])
}
newtype Parser a = Parser { runParser :: [Word8] -> Either String (a,[Word8]) }
instance Functor Parser where
fmap f p = Parser $ \input -> do
(value, rest) <- runParser p input
pure (f value, rest)
fmap f p = Parser $ \i -> do
(a,r) <- runParser p i
pure (f a,r)
instance Applicative Parser where
pure value = Parser $ \input -> Right (value, input)
pf <*> px = Parser $ \input -> do
(f, rest1) <- runParser pf input
(x, rest2) <- runParser px rest1
pure (f x, rest2)
pure x = Parser (\i -> Right (x,i))
pf <*> px = Parser $ \i -> do
(f,r1) <- runParser pf i
(x,r2) <- runParser px r1
pure (f x,r2)
instance Monad Parser where
p >>= f = Parser $ \input -> do
(value, rest) <- runParser p input
runParser (f value) rest
p >>= f = Parser $ \i -> do
(a,r) <- runParser p i
runParser (f a) r
instance MonadFail Parser where
fail msg = Parser $ \_ -> Left msg
parseFail :: String -> Parser a
parseFail msg = Parser $ \_ -> Left msg
@@ -96,15 +87,26 @@ skipN n = do
_ <- takeN n
pure ()
parseMethodCodeBytesFromFile :: FilePath -> IO [(String, [Word8])]
parseMethodCodeBytesFromFile path = do
bytes <- BS.readFile path
case runParser parseClassFile (BS.unpack bytes) of
Left err -> error ("Failed parsing class file '" ++ path ++ "': " ++ err)
Right (methods, _) -> pure methods
skipMembers :: Int -> Parser ()
skipMembers 0 = pure ()
skipMembers n = do
_accessFlags <- u2
_nameIndex <- u2
_descriptorIndex <- u2
attrCount <- fromIntegral <$> u2
skipAttributes attrCount
skipMembers (n - 1)
parseClassFile :: Parser [(String, [Word8])]
parseClassFile = do
skipAttributes :: Int -> Parser ()
skipAttributes 0 = pure ()
skipAttributes n = do
_attrNameIndex <- u2
attrLen <- fromIntegral <$> u4
skipN attrLen
skipAttributes (n - 1)
parseClassFileParser :: Parser [(String, [Word8])]
parseClassFileParser = do
magic <- u4
if magic /= 0xCAFEBABE
then parseFail "Invalid class file magic"
@@ -158,24 +160,6 @@ parseConstantPool cpCount idx utf8Map
20 -> skipN 2 >> parseConstantPool cpCount (idx + 1) utf8Map
_ -> parseFail ("Unknown constant pool tag " ++ show tag)
skipMembers :: Int -> Parser ()
skipMembers 0 = pure ()
skipMembers n = do
_accessFlags <- u2
_nameIndex <- u2
_descriptorIndex <- u2
attrCount <- fromIntegral <$> u2
skipAttributes attrCount
skipMembers (n - 1)
skipAttributes :: Int -> Parser ()
skipAttributes 0 = pure ()
skipAttributes n = do
_attrNameIndex <- u2
attrLen <- fromIntegral <$> u4
skipN attrLen
skipAttributes (n - 1)
parseMethods :: Map.Map Int String -> Int -> Parser [(String, [Word8])]
parseMethods _ 0 = pure []
parseMethods utf8Map n = do
@@ -221,4 +205,4 @@ parseCodeAttribute _declaredLength = do
skipN (exceptionTableLen * 8)
attrCount <- fromIntegral <$> u2
skipAttributes attrCount
pure (Just codeBytes)
pure (Just codeBytes)
+16
View File
@@ -0,0 +1,16 @@
module Testsuite.ExecutableTests.TestCase where
import Grammar.AST (Class)
import Grammar.TAST (TypedClass)
import Codegen.Lowerer (BtcProgram)
import Data.Word (Word8)
data TestCase = TestCase
{ tcName :: String
, tcJavaFile :: FilePath
, tcExpectedAST :: [Class]
, tcExpectedTAST :: [TypedClass]
, tcExpectedABC :: [(String, BtcProgram)]
, tcExpectedBytecode :: [(String, [Word8])]
}
+11
View File
@@ -0,0 +1,11 @@
runghc -isrc src/Testsuite/ExecutableTests/Main.hs WhileTest
runghc -isrc src/Testsuite/ExecutableTests/Main.hs
cabal run compiler src/Testsuite/javaFiles/ArithmeticTest.java
cabal run compiler src/Testsuite/javaFiles/ConstructorTest.java
# ... etc for all individual test files
java -cp /path/to/classFiles Main
cd src/Testsuite/classFiles
java -cp . Main
@@ -0,0 +1,21 @@
module Testsuite.TastFiles.ConstructorOverloadTestTAST (expectedTAST) where
import Grammar.AST (BinaryOperator (..), UnaryOperator (..))
import Grammar.TAST
expectedTAST =
[ Class
"ConstructorOverloadTest"
[Field "int" "a" (Just (Integer 42 "int"))]
[ Method
"void"
"<init>"
[]
(Block [] "void"),
Method
"void"
"<init>"
[("int", "a")]
(Block [StmtExprStmt (Assign (InstVar (This "ConstructorOverloadTest") "a" "int") (LocalOrFieldVar "a" "int") "void") "void"] "void")
]
]
@@ -0,0 +1,16 @@
module Testsuite.TastFiles.ConstructorTestTAST (expectedTAST) where
import Grammar.AST (BinaryOperator (..), UnaryOperator (..))
import Grammar.TAST
expectedTAST =
[ Class
"ConstructorTest"
[Field "int" "a" (Just (Integer (-1) "int"))]
[ Method
"void"
"<init>"
[("int", "initial_value")]
(Block [StmtExprStmt (Assign (LocalOrFieldVar "a" "int") (LocalOrFieldVar "initial_value" "int") "void") "void"] "void")
]
]
+13
View File
@@ -0,0 +1,13 @@
module Testsuite.TastFiles.FieldsTestTAST (expectedTAST) where
import Grammar.AST (BinaryOperator (..), UnaryOperator (..))
import Grammar.TAST
expectedTAST =
[ Class
"FieldsTest"
[ Field "int" "a" Nothing,
Field "int" "b" (Just (Integer 42 "int"))
]
[]
]
+60
View File
@@ -0,0 +1,60 @@
module Testsuite.TastFiles.IfTestTAST (expectedTAST) where
import Grammar.AST (BinaryOperator (..))
import Grammar.TAST
expectedTAST :: [TypedClass]
expectedTAST =
[ Class
"IfTest"
[]
[ Method
"boolean"
"ifElseTest"
[("int", "x")]
( Block
[ If
( Binary CompLessThan
(LocalOrFieldVar "x" "int")
(Integer 0 "int")
"boolean"
)
( Block
[ Return (Just (Bool False "boolean")) "boolean" ]
"boolean"
)
( Just
( If
( Binary CompEqual
(LocalOrFieldVar "x" "int")
(Integer 0 "int")
"boolean"
)
( Block
[ Return (Just (Bool True "boolean")) "boolean" ]
"boolean"
)
( Just
( Block
[ Return
( Just
( Binary CompGreaterThan
(LocalOrFieldVar "x" "int")
(Integer 10 "int")
"boolean"
)
)
"boolean"
]
"boolean"
)
)
"boolean"
)
)
"boolean"
]
"boolean"
)
]
]
@@ -0,0 +1,25 @@
module Testsuite.TastFiles.MultiClassTestTAST (expectedTAST) where
import Grammar.AST (BinaryOperator (..), UnaryOperator (..))
import Grammar.TAST
expectedTAST =
[ Class
"MultiClassTest"
[]
[],
Class
"Helper"
[Field "int" "v" Nothing]
[ Method
"void"
"<init>"
[("int", "v0")]
(Block [StmtExprStmt (Assign (LocalOrFieldVar "v" "int") (LocalOrFieldVar "v0" "int") "void") "void"] "void"),
Method
"int"
"doubleIt"
[]
(Block [Return (Just (Binary Multiply (LocalOrFieldVar "v" "int") (Integer 2 "int") "int")) "int"] "int")
]
]
@@ -0,0 +1,92 @@
module Testsuite.TastFiles.RecursionTestTAST (expectedTAST) where
import Grammar.AST (BinaryOperator (..), UnaryOperator (..))
import Grammar.TAST
expectedTAST =
[ Class
"RecursionTest"
[ Field "int" "value" (Just (Integer 0 "int"))
, Field "RecursionTest" "child" (Just (Null "RecursionTest"))
]
[ Method
"void"
"<init>"
[("int", "n")]
( Block
[ StmtExprStmt (Assign (LocalOrFieldVar "value" "int") (LocalOrFieldVar "n" "int") "int") "void"
, If
(Binary CompGreaterThan (LocalOrFieldVar "n" "int") (Integer 0 "int") "boolean")
( Block
[ StmtExprStmt (Assign (LocalOrFieldVar "child" "RecursionTest") (StmtExprExpr (New "RecursionTest" [Binary Subtract (LocalOrFieldVar "n" "int") (Integer 1 "int") "int"] "RecursionTest") "RecursionTest") "RecursionTest") "void"
]
"void"
)
Nothing
"void"
]
"void"
)
, Method
"int"
"fibonacci"
[("int", "n")]
( Block
[ If
(Binary CompLessThan (LocalOrFieldVar "n" "int") (Integer 2 "int") "boolean")
(Return (Just (LocalOrFieldVar "n" "int")) "int")
( Just
( Block
[ Return
( Just
( Binary
Add
(StmtExprExpr (MethodCall (This "RecursionTest") "fibonacci" [Binary Subtract (LocalOrFieldVar "n" "int") (Integer 1 "int") "int"] "int") "int")
(StmtExprExpr (MethodCall (This "RecursionTest") "fibonacci" [Binary Subtract (LocalOrFieldVar "n" "int") (Integer 2 "int") "int"] "int") "int")
"int"
)
)
"int"
]
"void"
)
)
"void"
]
"void"
)
, Method
"int"
"ackermann"
[("int", "m"), ("int", "n")]
( Block
[ If
(Binary CompEqual (LocalOrFieldVar "m" "int") (Integer 0 "int") "boolean")
(Return (Just (Binary Add (LocalOrFieldVar "n" "int") (Integer 1 "int") "int")) "int")
Nothing
"void"
, If
(Binary CompEqual (LocalOrFieldVar "n" "int") (Integer 0 "int") "boolean")
(Return (Just (StmtExprExpr (MethodCall (This "RecursionTest") "ackermann" [Binary Subtract (LocalOrFieldVar "m" "int") (Integer 1 "int") "int", Integer 1 "int"] "int") "int")) "int")
Nothing
"void"
, Return
( Just
( StmtExprExpr
( MethodCall
(This "RecursionTest")
"ackermann"
[ Binary Subtract (LocalOrFieldVar "m" "int") (Integer 1 "int") "int"
, StmtExprExpr (MethodCall (This "RecursionTest") "ackermann" [LocalOrFieldVar "m" "int", Binary Subtract (LocalOrFieldVar "n" "int") (Integer 1 "int") "int"] "int") "int"
]
"int"
)
"int"
)
)
"int"
]
"int"
)
]
]
+19
View File
@@ -0,0 +1,19 @@
module Testsuite.TastFiles.ReturnTestTAST (expectedTAST) where
import Grammar.TAST
expectedTAST :: [TypedClass]
expectedTAST =
[ Class
"ReturnTest"
[]
[ Method
"int"
"main"
[]
( Block
[ Return (Just (Integer 42 "int")) "int" ]
"int"
)
]
]
@@ -0,0 +1,31 @@
module Testsuite.TastFiles.SingletonTestTAST (expectedTAST) where
import Grammar.AST (BinaryOperator (..), UnaryOperator (..))
import Grammar.TAST
expectedTAST =
[ Class
"SingletonTest"
[ Field "SingletonTest" "instance" Nothing
]
[ Method
"void"
"<init>"
[]
(Block [] "void")
, Method
"SingletonTest"
"getInstance"
[]
( Block
[ If
(Binary CompEqual (LocalOrFieldVar "instance" "SingletonTest") (Null "SingletonTest") "boolean")
(Block [StmtExprStmt (Assign (LocalOrFieldVar "instance" "SingletonTest") (StmtExprExpr (New "SingletonTest" [] "SingletonTest") "SingletonTest") "SingletonTest") "void"] "void")
Nothing
"void"
, Return (Just (LocalOrFieldVar "instance" "SingletonTest")) "SingletonTest"
]
"SingletonTest"
)
]
]
+43
View File
@@ -0,0 +1,43 @@
module Testsuite.TastFiles.WhileTestTAST (expectedTAST) where
import Grammar.AST (BinaryOperator (..))
import Grammar.TAST
expectedTAST :: [TypedClass]
expectedTAST =
[ Class
"WhileTest"
[]
[ Method
"int"
"whileLoopTest"
[("int", "n")]
( Block
[ LocalVarDecl "int" "sum" "int",
While
(Binary CompGreaterThan (LocalOrFieldVar "n" "int") (Integer 0 "int") "boolean")
( Block
[ StmtExprStmt
(Assign
(LocalOrFieldVar "sum" "int")
(Binary Add (LocalOrFieldVar "sum" "int") (LocalOrFieldVar "n" "int") "int")
"int"
)
"int",
StmtExprStmt
(Assign
(LocalOrFieldVar "n" "int")
(Binary Subtract (LocalOrFieldVar "n" "int") (Integer 1 "int") "int")
"int"
)
"int"
]
"int"
)
"int",
Return (Just (LocalOrFieldVar "sum" "int")) "int"
]
"int"
)
]
]
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+5 -13
View File
@@ -1,9 +1,10 @@
public class AllSyntaxTest {
public int x;
private static int counter = 0;
public int counter;
public AllSyntaxTest(int initial) {
x = initial;
counter = 0;
counter = counter + 1;
}
@@ -17,7 +18,7 @@ public class AllSyntaxTest {
return x;
}
public static int sumUpTo(int n) {
public int sumUpTo(int n) {
int s = 0;
for (int i = 1; i <= n; i = i + 1) {
s = s + i;
@@ -28,13 +29,4 @@ public class AllSyntaxTest {
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
}
}
}
@@ -18,10 +18,4 @@ public class CombinedControlTest {
}
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());
}
}
+4 -11
View File
@@ -1,18 +1,11 @@
public class ExpressionTest {
public static boolean shortCircuit(int a, int b) {
// short-circuit: when a==0 the right side must not be evaluated
public boolean shortCircuit(int a, int b) {
boolean res = (a != 0) && ((10 / a) > b);
return res;
}
public static char charArithmetic(char c, int offset) {
char d = (char)(c + offset);
public char charArithmetic(char c, int offset) {
char d = c;
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'
}
}
}
-6
View File
@@ -8,10 +8,4 @@ public class IfTest {
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
}
}
+3 -1
View File
@@ -61,10 +61,12 @@ public class Main {
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 constructor state. Expected: x=0 and counter=1, Real: x=" + allSyntax.x + " and counter=" + allSyntax.counter);
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.sumUpTo(5). Expected: 15, Real: " + allSyntax.sumUpTo(5));
System.out.println("test AllSyntaxTest.predicate('a'). Expected: true, Real: " + allSyntax.predicate('a'));
System.out.println("test AllSyntaxTest.predicate('z'). Expected: false, Real: " + allSyntax.predicate('z'));
// CombinedControlTest tests
System.out.println("test CombinedControlTest.compute(). Expected: -3, Real: " + combined.compute());
// ExpressionTest tests
@@ -1,8 +1,4 @@
public class MultiClassTest {
public static void main(String[] args) {
Helper h = new Helper(3);
System.out.println(h.doubleIt()); // expect 6
}
}
class Helper {
+5
View File
@@ -0,0 +1,5 @@
public class ReturnTest {
public int main () {
return 42;
}
}
-5
View File
@@ -7,9 +7,4 @@ public class WhileTest {
}
return sum;
}
public static void main(String[] args) {
// Expect 1+2+3+4+5 = 15
System.out.println(whileLoopTest(5));
}
}
+286 -125
View File
@@ -4,9 +4,16 @@ import Data.List (find)
import Grammar.AST as AST
import Grammar.TAST as TAST
--------------------------------------------------
-- Types
--------------------------------------------------
type Symtab = [(String, Type)]
--------------------------------------------------
-- Get type functions
--------------------------------------------------
getTypeFromTypedExpr :: TypedExpr -> Type
getTypeFromTypedExpr (TAST.This t) = t
getTypeFromTypedExpr (TAST.LocalOrFieldVar _ t) = t
@@ -19,7 +26,6 @@ getTypeFromTypedExpr (TAST.Char _ t) = t
getTypeFromTypedExpr (TAST.Null t) = t
getTypeFromTypedExpr (TAST.StmtExprExpr _ t) = t
getTypeFromTypedStmtExpr :: TypedStmtExpr -> Type
getTypeFromTypedStmtExpr (TAST.Assign _ _ t) = t
getTypeFromTypedStmtExpr (TAST.New _ _ t) = t
@@ -33,129 +39,225 @@ 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 -> Symtab -> [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
let
fieldSymtab = map (\(AST.Field t n _) -> (n, t)) fields
extendedSymtab = fieldSymtab ++ symtab
checkedFields = map (\f -> typeCheckField f extendedSymtab cls) fields
checkedMethods = map (\m -> typeCheckMethod m extendedSymtab 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 =
typeCheckField :: AST.FieldDecl -> Symtab -> [AST.Class] -> TAST.TypedFieldDecl
typeCheckField (AST.Field typ name Nothing) _ _ =
TAST.Field typ name Nothing
typeCheckField (AST.Field expectedTyp name (Just expr)) symtab cls =
let checkedExpr = typeCheckExpr 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
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.MethodDecl -> Symtab -> [AST.Class] -> TAST.TypedMethodDecl
typeCheckMethod (AST.Method typ name params stmt) symtab cls =
let checkedStmt = typeCheckStmt stmt symtab cls
let
paramSymtab = map (\(t, n) -> (n, t)) params
extendedSymtab = paramSymtab ++ symtab
(checkedStmt, _) = typeCheckStmt stmt extendedSymtab 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
in
if typ == stmtTyp || stmtTyp == "void"
then TAST.Method typ name params checkedStmt
else error $ "Return type mismatch: expected " ++ typ ++ ", got " ++ stmtTyp
--------------------------------------------------
-- Statement Typechecking
--------------------------------------------------
typeCheckStmt :: AST.Stmt -> [(String, Type)] -> [AST.Class] -> TAST.TypedStmt
-- If-Statement
typeCheckStmt :: AST.Stmt -> Symtab -> [AST.Class] -> (TAST.TypedStmt, Symtab)
--------------------------------------------------
-- If Statement
--------------------------------------------------
typeCheckStmt (AST.If cond body Nothing) symtab cls =
let checkedCond = typeCheckExpr cond symtab cls
checkedBody = typeCheckStmt body symtab cls
let
checkedCond = typeCheckExpr cond symtab cls
(checkedBody, _) = typeCheckStmt body 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"
in
if getTypeFromTypedExpr checkedCond == "boolean"
then (TAST.If checkedCond checkedBody Nothing typ, symtab)
else error "Condition in if statement must be boolean"
--------------------------------------------------
-- If Else Statement
--------------------------------------------------
-- If-Else-Statement
typeCheckStmt (AST.If cond body (Just elseBody)) symtab cls =
let checkedCond = typeCheckExpr cond symtab cls
checkedBody = typeCheckStmt body symtab cls
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"
let
checkedCond = typeCheckExpr cond symtab cls
(checkedBody, _) = typeCheckStmt body symtab cls
(checkedElse, _) = typeCheckStmt elseBody symtab cls
typ =
upperBound
(getTypeFromTypedStmt checkedBody)
(getTypeFromTypedStmt checkedElse)
in
if getTypeFromTypedExpr checkedCond == "boolean"
then (TAST.If checkedCond checkedBody (Just checkedElse) typ, symtab)
else error "Condition in if statement must be boolean"
--------------------------------------------------
-- While Statement
--------------------------------------------------
-- While-Statement
typeCheckStmt (AST.While cond body) symtab cls =
let checkedCond = typeCheckExpr cond symtab cls
checkedBody = typeCheckStmt 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"
in
if getTypeFromTypedExpr checkedCond == "boolean"
then (TAST.While checkedCond checkedBody typ, symtab)
else error "Condition in while statement must be boolean"
--------------------------------------------------
-- Return Statement
--------------------------------------------------
typeCheckStmt (AST.Return maybeExpr) symtab cls =
let checkedExpr = fmap (\expr -> typeCheckExpr expr symtab cls) maybeExpr
let
checkedExpr = fmap (\expr -> typeCheckExpr expr symtab cls) maybeExpr
typ = maybe "void" getTypeFromTypedExpr checkedExpr
in TAST.Return checkedExpr typ
in
(TAST.Return checkedExpr typ, symtab)
--------------------------------------------------
-- Local Variable Declaration
--------------------------------------------------
typeCheckStmt (AST.LocalVarDecl typ str) _ _ =
TAST.LocalVarDecl typ str typ
typeCheckStmt (AST.LocalVarDecl typ str maybeExpr) symtab cls =
if any (\(name, _) -> name == str) symtab
then error $ "Variable already defined: " ++ str
else
let
checkedExpr = fmap (\expr -> typeCheckExpr expr symtab cls) maybeExpr
exprTyp = fmap getTypeFromTypedExpr checkedExpr
in
case exprTyp of
Just actualTyp
| actualTyp /= typ ->
error $ "Type mismatch: Expected " ++ typ ++ " found " ++ actualTyp
_ ->
()
`seq` (TAST.LocalVarDecl typ str typ, (str, typ) : symtab)
--------------------------------------------------
-- Block Statement
--------------------------------------------------
-- 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
let
(checkedStmts, _) = typeCheckStmtList stmts symtab cls
typ =
case reverse checkedStmts of
[] -> "void"
(lastStmt : _) ->
getTypeFromTypedStmt lastStmt
in (TAST.Block checkedStmts typ, symtab)
--------------------------------------------------
-- Expression Statement
--------------------------------------------------
-- Expression statement
typeCheckStmt (AST.StmtExprStmt stmtExpr) symtab cls =
let checkedStmtExpr = typeCheckStmtExpr stmtExpr symtab cls
let
checkedStmtExpr = typeCheckStmtExpr stmtExpr symtab cls
typ = getTypeFromTypedStmtExpr checkedStmtExpr
in TAST.StmtExprStmt checkedStmtExpr typ
in
(TAST.StmtExprStmt checkedStmtExpr typ, symtab)
--------------------------------------------------
-- Statement List Helper
--------------------------------------------------
typeCheckStmtList :: [AST.Stmt] -> Symtab -> [AST.Class] -> ([TAST.TypedStmt], Symtab)
typeCheckStmtList [] symtab _ =
([], symtab)
typeCheckStmtList (stmt:rest) symtab cls =
let
(checkedStmt, newSymtab) = typeCheckStmt stmt symtab cls
(checkedRest, finalSymtab) = typeCheckStmtList rest newSymtab cls
in
(checkedStmt : checkedRest, finalSymtab)
--------------------------------------------------
-- Statement Expression Typechecking
--------------------------------------------------
typeCheckStmtExpr :: AST.StmtExpr -> [(String, Type)] -> [AST.Class] -> TAST.TypedStmtExpr
typeCheckStmtExpr :: AST.StmtExpr -> Symtab -> [AST.Class] -> TAST.TypedStmtExpr
--------------------------------------------------
-- Assign
--------------------------------------------------
typeCheckStmtExpr (AST.Assign lhs rhs) symtab cls =
let checkedLhs = typeCheckExpr lhs 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"
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
--------------------------------------------------
typeCheckStmtExpr (AST.New typ args) symtab cls =
let
checkedArgs = map (\arg -> typeCheckExpr arg symtab cls) args
in
TAST.New typ checkedArgs typ
--------------------------------------------------
-- Method Call
--------------------------------------------------
-- Method call
typeCheckStmtExpr (AST.MethodCall target methodName args) symtab cls =
let checkedTarget = typeCheckExpr target 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
in
TAST.MethodCall checkedTarget methodName checkedArgs returnType
--------------------------------------------------
-- Helper Methods
--------------------------------------------------
-- 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
Nothing ->
error $
"Method not found: " ++ methodName ++ " in class " ++ classType
findMethod :: Type -> String -> [AST.Class] -> Maybe Type
findMethod classType methodName classes =
@@ -170,96 +272,142 @@ 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
case find (\(AST.Field t name _) -> name == fieldName) fields of
Just (AST.Field t _ _) -> t
Nothing -> error $ "Field not found: " ++ fieldName ++ " in class " ++ classType
Nothing ->
error $ "Field not found: " ++ fieldName ++ " in class " ++ classType
Nothing -> error $ "Class not found: " ++ classType
--------------------------------------------------
-- Expression Typechecking
--------------------------------------------------
typeCheckExpr :: AST.Expr -> [(String, Type)] -> [AST.Class] -> TAST.TypedExpr
-- boolean literals
typeCheckExpr (AST.Bool value) symtbl cls =
typeCheckExpr :: AST.Expr -> Symtab -> [AST.Class] -> TAST.TypedExpr
--------------------------------------------------
-- Boolean Literals
--------------------------------------------------
typeCheckExpr (AST.Bool value) _ _ =
TAST.Bool value "boolean"
-- integer literals
typeCheckExpr (AST.Integer value) symtbl cls =
--------------------------------------------------
-- Integer Literals
--------------------------------------------------
typeCheckExpr (AST.Integer value) _ _ =
TAST.Integer value "int"
-- variable references
typeCheckExpr (AST.LocalOrFieldVar varName) symtbl cls =
case lookup varName symtbl of
Just t -> TAST.LocalOrFieldVar varName t
--------------------------------------------------
-- Variable References
--------------------------------------------------
typeCheckExpr (AST.LocalOrFieldVar varName) symtab _ =
case lookup varName symtab of
Just t ->
TAST.LocalOrFieldVar varName t
Nothing -> error $ "Undefined variable: " ++ varName
-- this reference
--------------------------------------------------
-- 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
--------------------------------------------------
-- Field Access
--------------------------------------------------
typeCheckExpr (AST.InstVar obj fieldName) symtab cls =
let
checkedObj = typeCheckExpr obj symtab cls
objType = getTypeFromTypedExpr checkedObj
fieldType = lookupFieldType objType fieldName cls
in TAST.InstVar checkedObj fieldName fieldType
in
TAST.InstVar checkedObj fieldName fieldType
-- unary operators
typeCheckExpr (AST.Unary op expr) symtbl cls =
let checkedExpr = typeCheckExpr expr symtbl cls
--------------------------------------------------
-- Unary Operators
--------------------------------------------------
typeCheckExpr (AST.Unary op expr) symtab cls =
let
checkedExpr = typeCheckExpr expr symtab 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"
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
--------------------------------------------------
-- Binary Operators
--------------------------------------------------
typeCheckExpr (AST.Binary op left right) symtab cls =
let
checkedLeft = typeCheckExpr left symtab cls
checkedRight = typeCheckExpr right symtab 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
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
--------------------------------------------------
-- character literals
typeCheckExpr (AST.Char value) _ _ =
TAST.Char value "char"
-- null literal
--------------------------------------------------
-- 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
--------------------------------------------------
-- Statement Expression
--------------------------------------------------
typeCheckExpr (AST.StmtExprExpr stmtExpr) symtab cls =
let
checkedStmtExpr = typeCheckStmtExpr stmtExpr symtab cls
typ = getTypeFromTypedStmtExpr checkedStmtExpr
in
TAST.StmtExprExpr checkedStmtExpr typ
--------------------------------------------------
-- Expression Helper Methods
--------------------------------------------------
checkIntOp :: Type -> Type -> TAST.TypedExpr -> TAST.TypedExpr -> AST.BinaryOperator -> TAST.TypedExpr
-- 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"
@@ -272,18 +420,31 @@ checkBoolOp leftType rightType left right op =
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"
if leftType == rightType
|| leftType == "null"
|| rightType == "null"
then TAST.Binary op left right "boolean"
else error $ "Equality operator: cannot compare " ++ leftType ++ " with " ++ rightType
else
error $
"Equality operator: cannot compare "
++ leftType
++ " with "
++ rightType
--------------------------------------------------
-- Upper Bound
--------------------------------------------------
upperBound :: Type -> Type -> Type
upperBound t1 t2
| t1 == t2 = t1
| otherwise = "Object"
| otherwise = "Object"
+9 -9
View File
@@ -1,11 +1,11 @@
#!/bin/bash
# run all tests: ./test.sh 2>&1 | grep -E "Testing|Results"
# inspect a diff: diff <(javap -c /tmp/compoiler-ref/FieldsTest.class) <(javap -c /tmp/compoiler-out/FieldsTest.class)
# inspect a diff: diff <(javap -c out/compoiler-ref/FieldsTest.class) <(javap -c out/compoiler-out/FieldsTest.class)
JAVA_DIR="src/Testsuite/javaFiles"
REF_DIR="/tmp/compoiler-ref"
OUT_DIR="/tmp/compoiler-out"
REF_DIR="./out/compoiler-ref"
OUT_DIR="./out"
PASS=0
FAIL=0
ERROR=0
@@ -26,7 +26,7 @@ for javafile in "$JAVA_DIR"/*.java; do
fi
# Move generated .class to OUT_DIR
mv "out/${classname}.class" "$OUT_DIR/" 2>/dev/null
mv "$OUT_DIR/${classname}.class" "$OUT_DIR/" 2>/dev/null
if [ ! -f "$OUT_DIR/${classname}.class" ]; then
echo "ERROR (no .class generated)"
((ERROR++))
@@ -44,15 +44,15 @@ for javafile in "$JAVA_DIR"/*.java; do
# Compare structure and bytecode only (strip CP indices from instructions,
# skip debug info like LineNumberTable and SourceFile)
normalize() {
javap -c "$1" 2>&1 \
| grep -v "^Classfile\|Last modified\|SHA-256\|Compiled from\|LineNumberTable\|line [0-9]\|SourceFile" \
| sed 's/#[0-9]*/\#N/g' \
| sed 's/[[:space:]]*\/\/.*$//'
javap -c "$1" 2>&1 |
grep -v "^Classfile\|Last modified\|SHA-256\|Compiled from\|LineNumberTable\|line [0-9]\|SourceFile" |
sed 's/#[0-9]*/\#N/g' |
sed 's/[[:space:]]*\/\/.*$//'
}
ref=$(normalize "$REF_DIR/${classname}.class")
mine=$(normalize "$OUT_DIR/${classname}.class")
if diff <(echo "$ref") <(echo "$mine") > /dev/null 2>&1; then
if diff <(echo "$ref") <(echo "$mine") >/dev/null 2>&1; then
echo "PASS"
((PASS++))
else