Compare commits
106 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5643aa703e | |||
| 74f68cb525 | |||
| f10f7883dd | |||
| a8c6a056da | |||
| c7796b4f25 | |||
| cc2d157674 | |||
| d576e64534 | |||
| cec34d1d9e | |||
| 69ae49a346 | |||
| e43a783d33 | |||
| 0496e6e1b4 | |||
| 4f5e4febdf | |||
| ffae200d3c | |||
| 85b93a604c | |||
| 3ffac1ffe6 | |||
| 3cf088b818 | |||
| e86389baf8 | |||
| 6aedcfe4e1 | |||
| fe71942a65 | |||
| a2ebe25901 | |||
| 8ac6223c6b | |||
| 9d4106aee8 | |||
| 95a80ca5d3 | |||
| b0abf5ee72 | |||
| 69b8bcba4d | |||
| 8ecda70a5f | |||
| 309d52715a | |||
| 0f2469f007 | |||
| bfd70838aa | |||
| e48012c3ed | |||
| fc2b31693d | |||
| 1bc9babd4f | |||
| 604db760ac | |||
| 4e24b2fb8a | |||
| 94e23b57fd | |||
| 22fd6733f0 | |||
| 62f42611bc | |||
| 2e601b86d5 | |||
| 2141bd44cb | |||
| 3ac33af1b5 | |||
| 9046e3621b | |||
| 7c0f49ccca | |||
| 6e16a502aa | |||
| b45502a550 | |||
| 83ff583c1c | |||
| f19d6f6d39 | |||
| cbd7df780a | |||
| 9d405fac61 | |||
| dde92cc64d | |||
| c8261b361a | |||
| d0d37dd05e | |||
| e8ab0ed082 | |||
| 544774d8b9 | |||
| f2cc603690 | |||
| a0f5d736f9 | |||
| 8416f1b9c8 | |||
| 245e70169e | |||
| 8497b949a4 | |||
| 251d8daa0f | |||
| 1ad70c0b00 | |||
| 7f6e8f5cb5 | |||
| 88e9b545eb | |||
| 11fda5eaa7 | |||
| 1a153200fa | |||
| 815388b842 | |||
| 22e238317c | |||
| 5d48938221 | |||
| 44c1032e15 | |||
| 842149be5e | |||
| 48b6270fd0 | |||
| b668fc94ff | |||
| a829814cd6 | |||
| 66e0efba81 | |||
| 31248ecc97 | |||
| ac60aeba3e | |||
| b833d6ebe0 | |||
| 3f0e00ac63 | |||
| 4370ec3c1f | |||
| 162bed1b12 | |||
| e91fac7060 | |||
| 0eb8c64fc6 | |||
| f96627fcc9 | |||
| b5ddd0b5c9 | |||
| 531f5d7f7f | |||
| 5a16f08ccc | |||
| e5eb3a9792 | |||
| 8e3b978ba4 | |||
| 1b2ae2ae22 | |||
| d9b7ba40bc | |||
| 03e0571f4e | |||
| 4978de5220 | |||
| b80b71936a | |||
| 22de50b77e | |||
| e84550025c | |||
| 8470ab2d94 | |||
| 1ad8aface9 | |||
| daa741a090 | |||
| e81ceb454f | |||
| 87db9f6e23 | |||
| a447fad905 | |||
| b964bdda1b | |||
| 2f908d1df1 | |||
| 40e84e45f6 | |||
| 56d43bac38 | |||
| 9230042b25 | |||
| 76c4cb677c |
@@ -31,3 +31,8 @@ cabal.project.local~
|
||||
hie.yaml
|
||||
src/Grammar/Scanner.hs
|
||||
src/Grammar/Parser.hs
|
||||
src/Grammar/Parser.info
|
||||
src/Main
|
||||
out/
|
||||
mine.txt
|
||||
ref.txt
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
# Compilerbau
|
||||
|
||||
# – Prüfungsleistung
|
||||
|
||||
## Spezifikation
|
||||
|
||||
Deklarationen: • Σ: Eingabe–Alphabet
|
||||
|
||||
- JC: Menge aller syntaktisch korrekten Java–Klassen mit folgenden Einschränkungen:
|
||||
- keine generischen Klassen
|
||||
- keine abstrakten Klassen
|
||||
- keine Vererbung
|
||||
- keine Interfaces
|
||||
- keine Threads
|
||||
- keine Exceptions
|
||||
- keine Arrays
|
||||
- als Basistypen sind nur int, boolean und char zugelassen
|
||||
- keine Packages
|
||||
- keine Imports
|
||||
- keine Lambda–Expressions
|
||||
- BC: Menge aller Bytecode–Files
|
||||
|
||||
Eingabe: p∈Σ∗
|
||||
|
||||
Vorbedingung: ∅
|
||||
|
||||
Ausgabe: bc∈BC∗∪{error}
|
||||
|
||||
Nachbedingungen: • falls p∈(JC)∗, so ist bc∈(BC)∗und p wird nach bc übersetzt wie es durch die Sprache Java definiert ist.
|
||||
|
||||
- falls p !∈(JC)∗, so ist bc=error.
|
||||
|
||||
## Vorgehen
|
||||
|
||||
Arbeitsteam:Der Java–Compiler wird in einem Team von 5-7 Personen erstellt. Das
|
||||
Team wird nochmals unterteilt:
|
||||
|
||||
|
||||
- Gemeinsame Aufgabe
|
||||
GIT-Repository: Einrichten eines GIT-Repositories auf den DHBW GITEA-Server
|
||||
Abstrakte Syntax: Aufbau der abstrakten Syntax aus dem Parsetree
|
||||
Dokumentation: Erstellen der Dokumentation
|
||||
- Scannen/Parsen/Grammatik (1–2 Personen)
|
||||
Scannen: alex–File oder Scanner von Hand programmieren
|
||||
Grammatik (nur bei Bearbeitung durch 2 Personen):Erstellen einer Mini-Java-Grammatik an Hand der Spezifikation
|
||||
Parsen:Erstellen des happy–Files oder des Kombinator–Parsers und Aufbau des abstrakten Syntaxbaums
|
||||
- Semantische Analyse:Typisierung der abstrakten Syntax(1 Person)
|
||||
Typisierung: Typisierung der abstrakten Syntax
|
||||
- Codeerzeugung (2-3 Personen):
|
||||
- Aufbau eines abstrakten ClassFiles (1 Person)
|
||||
- Konstantenpool (1 Person)
|
||||
- Nur bei Bearbeitung durch 3 Personen:Umwandlung des ClassFiles in Bytecode (1 Person)
|
||||
- Tester (1 Person)
|
||||
- Testsuite von Java–Files, die alle implementierten Features abdecken.
|
||||
- Händische Übersetzung aller Java-Files der Testsuite in die abstrakte Syntax (als Test–Eingaben für den Typ-Checker)
|
||||
- Händische Übersetzung aller Testfälle der abstrakten Syntax in getypte abstrakte Syntax (als Test–Eingaben für den Code-Generierer).
|
||||
- Händische Übersetzung aller Testfälle der getypten abstrakten Syntax in abstrakten Bytecode.
|
||||
- Automatische Tests, die die jeweiligen Testsuite mit den implementierten Funktionen des Teams vergleichen
|
||||
|
||||
## Prüfungsleistung
|
||||
|
||||
Die Arbeitsleistung wird bewertet an Hand
|
||||
|
||||
- des Gesamtergebnis des Teams
|
||||
- der Arbeitsleistung jeder/s Studierenden
|
||||
|
||||
an Hand folgender Kriterien:
|
||||
|
||||
- Projektergebnis
|
||||
- wöchentlicher Projektfortschritt
|
||||
- Mitarbeit im Team
|
||||
|
||||
Das Projektergebnis muss folgendes beinhalten:
|
||||
|
||||
- (Kurz)dokumentation aus der hervorgeht welche Leistung der jeweiligen Studierende erbrachten hat.
|
||||
- Im Teilprojekt muss folgendes vorliegen:
|
||||
- Eine Testsuite von Java–Programmen, für die der Compilerteil funktioniert.
|
||||
- Präsentation des Programms an Hand der erstellen Testsuites.
|
||||
- Durchgehendes Beispiel, fur das der gesamte Compiler funktioniert.
|
||||
- Abgabetermin: Letzte Semesterwoche
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
public class Runner { public static void main(String[] args) { FieldsTest f = new FieldsTest(); System.out.println(f.b); } }
|
||||
@@ -0,0 +1,279 @@
|
||||
module Codegen.ClassFile where
|
||||
|
||||
import Codegen.ConstPool
|
||||
( generateConstPool,
|
||||
lookupClassIndex,
|
||||
lookupFieldRefIndex,
|
||||
lookupIntegerIndex,
|
||||
lookupUtf8Index,
|
||||
)
|
||||
import Codegen.Types
|
||||
import Data.Bits (shiftR, (.&.))
|
||||
import Data.Word (Word16, Word32, Word8)
|
||||
import Grammar.AST (Type)
|
||||
import Grammar.TAST
|
||||
( TypedClass (..),
|
||||
TypedExpr (..),
|
||||
TypedFieldDecl (..),
|
||||
TypedMethodDecl (..),
|
||||
TypedStmt (..),
|
||||
)
|
||||
|
||||
data ClassFile = ClassFile
|
||||
{ magic :: Magic,
|
||||
minorVersion :: MinorVersion,
|
||||
majorVersion :: MajorVersion,
|
||||
constantPool :: CP_Infos,
|
||||
accessFlags :: AccessFlags,
|
||||
thisClass :: ThisClass,
|
||||
superClass :: SuperClass,
|
||||
interfaces :: Interfaces,
|
||||
fields :: Field_Infos,
|
||||
methods :: Method_Infos,
|
||||
attributes :: Attribute_Infos
|
||||
}
|
||||
deriving (Show, Eq)
|
||||
|
||||
-- Counts are derivable from list lengths, so no need to store them separately
|
||||
|
||||
type Index = Int
|
||||
|
||||
type Interfaces = [Word16] -- constant pool indices
|
||||
|
||||
type Field_Infos = [Field_Info]
|
||||
|
||||
type Method_Infos = [Method_Info]
|
||||
|
||||
type Attribute_Infos = [Attribute_Info]
|
||||
|
||||
newtype Magic = Magic Word32 deriving (Show, Eq) -- always 0xCAFEBABE
|
||||
|
||||
newtype MinorVersion = MinorVersion Int deriving (Show, Eq)
|
||||
|
||||
newtype MajorVersion = MajorVersion Int deriving (Show, Eq) -- 52 = Java 8
|
||||
|
||||
newtype AccessFlags = AccessFlags [AccessFlag] deriving (Show, Eq)
|
||||
|
||||
newtype ThisClass = ThisClass Index deriving (Show, Eq)
|
||||
|
||||
newtype SuperClass = SuperClass Index deriving (Show, Eq)
|
||||
|
||||
data AccessFlag
|
||||
= ACC_PUBLIC
|
||||
| ACC_PRIVATE
|
||||
| ACC_PROTECTED
|
||||
| ACC_STATIC
|
||||
| ACC_FINAL
|
||||
| ACC_SUPER -- always set on classes
|
||||
| ACC_INTERFACE
|
||||
| ACC_ABSTRACT
|
||||
| ACC_SYNTHETIC
|
||||
| ACC_ENUM
|
||||
deriving (Show, Eq)
|
||||
|
||||
data Field_Info = Field_Info
|
||||
{ fieldAccessFlags :: AccessFlags,
|
||||
fieldNameIndex :: Index,
|
||||
fieldDescIndex :: Index,
|
||||
fieldAttributes :: Attribute_Infos
|
||||
}
|
||||
deriving (Show, Eq)
|
||||
|
||||
data Method_Info = Method_Info
|
||||
{ methodAccessFlags :: AccessFlags,
|
||||
methodNameIndex :: Index,
|
||||
methodDescIndex :: Index,
|
||||
methodAttributes :: Attribute_Infos
|
||||
}
|
||||
deriving (Show, Eq)
|
||||
|
||||
data Attribute_Info
|
||||
= Code_Attribute
|
||||
{ codeNameIndex :: Index,
|
||||
maxStack :: Int,
|
||||
maxLocals :: Int,
|
||||
codeBody :: [Word8], -- raw serialized bytecode
|
||||
exceptionTable :: [ExceptionEntry],
|
||||
codeAttributes :: Attribute_Infos
|
||||
}
|
||||
| Generic_Attribute
|
||||
{ attrNameIndex :: Index,
|
||||
attrData :: [Word8]
|
||||
}
|
||||
deriving (Show, Eq)
|
||||
|
||||
data ExceptionEntry = ExceptionEntry
|
||||
{ startPc :: Int,
|
||||
endPc :: Int,
|
||||
handlerPc :: Int,
|
||||
catchType :: Index -- 0 means catches all (finally)
|
||||
}
|
||||
deriving (Show, Eq)
|
||||
|
||||
generateClassFile :: TypedClass -> ClassFile
|
||||
generateClassFile cls =
|
||||
let constPool = makeConstpool cls
|
||||
in ClassFile
|
||||
{ magic = Magic 0xCAFEBABE,
|
||||
minorVersion = MinorVersion 0,
|
||||
majorVersion = MajorVersion 48,
|
||||
constantPool = constPool,
|
||||
accessFlags = makeAccessFlags,
|
||||
thisClass = makeThisClass constPool cls,
|
||||
superClass = makeSuperClass constPool,
|
||||
interfaces = makeInterface,
|
||||
fields = makeFields constPool cls,
|
||||
methods = makeMethods constPool cls,
|
||||
attributes = makeAttributes constPool
|
||||
}
|
||||
|
||||
makeConstpool :: TypedClass -> CP_Infos
|
||||
makeConstpool = generateConstPool
|
||||
|
||||
makeAccessFlags :: AccessFlags
|
||||
makeAccessFlags = AccessFlags [ACC_PUBLIC, ACC_SUPER]
|
||||
|
||||
makeThisClass :: CP_Infos -> TypedClass -> ThisClass
|
||||
makeThisClass constPool (Class name _ _) =
|
||||
ThisClass (lookupClassIndex constPool name)
|
||||
|
||||
makeSuperClass :: CP_Infos -> SuperClass
|
||||
makeSuperClass constPool =
|
||||
SuperClass (lookupClassIndex constPool "java/lang/Object")
|
||||
|
||||
makeInterface :: Interfaces
|
||||
makeInterface = []
|
||||
|
||||
makeFields :: CP_Infos -> TypedClass -> Field_Infos
|
||||
makeFields constPool (Class _ fields _) = map (makeField constPool) fields
|
||||
|
||||
makeField :: CP_Infos -> TypedFieldDecl -> Field_Info
|
||||
makeField constPool (Field fieldType name _) =
|
||||
Field_Info
|
||||
{ fieldAccessFlags = AccessFlags [ACC_PUBLIC],
|
||||
fieldNameIndex = lookupUtf8Index constPool name,
|
||||
fieldDescIndex = lookupUtf8Index constPool (typeDescriptor fieldType),
|
||||
fieldAttributes = []
|
||||
}
|
||||
|
||||
makeAttributes :: CP_Infos -> Attribute_Infos
|
||||
makeAttributes _ = []
|
||||
|
||||
makeMethods :: CP_Infos -> TypedClass -> Method_Infos
|
||||
makeMethods constPool (Class _ fields methods) =
|
||||
makeInitMethod constPool fields : map (makeMethod constPool) methods
|
||||
|
||||
makeInitMethod :: CP_Infos -> [TypedFieldDecl] -> Method_Info
|
||||
makeInitMethod constPool fields =
|
||||
Method_Info
|
||||
{ methodAccessFlags = AccessFlags [ACC_PUBLIC],
|
||||
methodNameIndex = lookupUtf8Index constPool "<init>",
|
||||
methodDescIndex = lookupUtf8Index constPool "()V",
|
||||
methodAttributes = [makeInitCode constPool fields]
|
||||
}
|
||||
|
||||
makeInitCode :: CP_Infos -> [TypedFieldDecl] -> Attribute_Info
|
||||
makeInitCode constPool fields =
|
||||
Code_Attribute
|
||||
{ codeNameIndex = lookupUtf8Index constPool "Code",
|
||||
maxStack = if null initFields then 1 else 2,
|
||||
maxLocals = 1,
|
||||
codeBody = [0x2A, 0xB7, 0x00, 0x08] ++ fieldInits ++ [0xB1],
|
||||
exceptionTable = [],
|
||||
codeAttributes = []
|
||||
}
|
||||
where
|
||||
initFields = [f | f@(Field _ _ (Just _)) <- fields]
|
||||
fieldInits = concatMap (fieldInitBytes constPool) initFields
|
||||
|
||||
fieldInitBytes :: CP_Infos -> TypedFieldDecl -> [Word8]
|
||||
fieldInitBytes constPool (Field _ name (Just expr)) =
|
||||
let idx = lookupFieldRefIndex constPool ("field ref: " ++ name)
|
||||
in [0x2A]
|
||||
++ pushExpr constPool expr
|
||||
++ [0xB5, fromIntegral (idx `shiftR` 8 .&. 0xFF), fromIntegral (idx .&. 0xFF)]
|
||||
fieldInitBytes _ _ = []
|
||||
|
||||
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 :: 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 =
|
||||
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) =
|
||||
Method_Info
|
||||
{ methodAccessFlags = AccessFlags [ACC_PUBLIC],
|
||||
methodNameIndex = lookupUtf8Index constPool name,
|
||||
methodDescIndex = lookupUtf8Index constPool (methodDescriptor params returnType),
|
||||
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
|
||||
|
||||
typeDescriptor :: Type -> String
|
||||
typeDescriptor t = case t of
|
||||
"int" -> "I"
|
||||
"boolean" -> "Z"
|
||||
"char" -> "C"
|
||||
"void" -> "V"
|
||||
_ -> "L" ++ t ++ ";"
|
||||
@@ -0,0 +1,372 @@
|
||||
module Codegen.ConstPool where
|
||||
|
||||
import Codegen.Types
|
||||
import Data.List (isPrefixOf, isSuffixOf)
|
||||
import qualified Grammar.TAST as TAST
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Recursive extraction of Tags from an already-built pool
|
||||
------------------------------------------------------------------------
|
||||
|
||||
extractTags :: CP_Infos -> [Tag]
|
||||
extractTags [] = []
|
||||
extractTags (x : xs) = tag_cp x : extractTags xs
|
||||
|
||||
countEntries :: CP_Infos -> Int
|
||||
countEntries [] = 0
|
||||
countEntries (_ : xs) = 1 + countEntries xs
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Helper constructors
|
||||
------------------------------------------------------------------------
|
||||
|
||||
mkClassInfo :: IndexConstantPool -> String -> CP_Info
|
||||
mkClassInfo = ClassInfo TagClass
|
||||
|
||||
mkFieldRefInfo :: IndexConstantPool -> IndexConstantPool -> String -> CP_Info
|
||||
mkFieldRefInfo = FieldRefInfo TagFieldRef
|
||||
|
||||
mkMethodRefInfo :: IndexConstantPool -> IndexConstantPool -> String -> CP_Info
|
||||
mkMethodRefInfo = MethodRefInfo TagMethodRef
|
||||
|
||||
mkStringInfo :: IndexConstantPool -> String -> CP_Info
|
||||
mkStringInfo = StringInfo TagString
|
||||
|
||||
mkIntegerInfo :: Int -> String -> CP_Info
|
||||
mkIntegerInfo = IntegerInfo TagInteger
|
||||
|
||||
mkNameAndTypeInfo :: IndexConstantPool -> IndexConstantPool -> String -> CP_Info
|
||||
mkNameAndTypeInfo = NameAndTypeInfo TagNameAndType
|
||||
|
||||
mkUtf8Info :: String -> String -> CP_Info
|
||||
mkUtf8Info str = Utf8Info TagUtf8 (length str) str
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Tag utilities
|
||||
------------------------------------------------------------------------
|
||||
|
||||
tagToByte :: Tag -> Int
|
||||
tagToByte TagClass = 7
|
||||
tagToByte TagFieldRef = 9
|
||||
tagToByte TagMethodRef = 10
|
||||
tagToByte TagString = 8
|
||||
tagToByte TagInteger = 3
|
||||
tagToByte TagNameAndType = 12
|
||||
tagToByte TagUtf8 = 1
|
||||
|
||||
tagToString :: Tag -> String
|
||||
tagToString TagClass = "Class"
|
||||
tagToString TagFieldRef = "FieldRef"
|
||||
tagToString TagMethodRef = "MethodRef"
|
||||
tagToString TagString = "String"
|
||||
tagToString TagInteger = "Integer"
|
||||
tagToString TagNameAndType = "NameAndType"
|
||||
tagToString TagUtf8 = "Utf8"
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Descriptor helpers
|
||||
------------------------------------------------------------------------
|
||||
|
||||
typeToDescriptor :: String -> String
|
||||
typeToDescriptor "int" = "I"
|
||||
typeToDescriptor "boolean" = "Z"
|
||||
typeToDescriptor "char" = "C"
|
||||
typeToDescriptor "void" = "V"
|
||||
typeToDescriptor t = "L" ++ t ++ ";"
|
||||
|
||||
methodDescriptor :: [(String, String)] -> String -> String
|
||||
methodDescriptor params ret =
|
||||
"(" ++ paramsDesc params ++ ")" ++ typeToDescriptor ret
|
||||
where
|
||||
paramsDesc [] = ""
|
||||
paramsDesc ((pty, _) : ps) = typeToDescriptor pty ++ paramsDesc ps
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Recursive traversal of TAST to collect CP_Info entries
|
||||
------------------------------------------------------------------------
|
||||
|
||||
collectFromExprs :: [TAST.TypedExpr] -> CP_Infos
|
||||
collectFromExprs [] = []
|
||||
collectFromExprs (e : es) = collectFromTypedExpr e ++ collectFromExprs es
|
||||
|
||||
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)]
|
||||
collectFromTypedExpr (TAST.Bool _ _) =
|
||||
[]
|
||||
collectFromTypedExpr (TAST.Char c _) =
|
||||
[mkIntegerInfo (fromEnum c) ("char " ++ show c)]
|
||||
collectFromTypedExpr (TAST.Null _) =
|
||||
[]
|
||||
collectFromTypedExpr (TAST.This _) =
|
||||
[]
|
||||
collectFromTypedExpr (TAST.LocalOrFieldVar _ _) =
|
||||
[]
|
||||
collectFromTypedExpr (TAST.InstVar obj name typ) =
|
||||
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 _) =
|
||||
collectFromTypedExpr e1 ++ collectFromTypedExpr e2
|
||||
collectFromTypedExpr (TAST.StmtExprExpr se _) =
|
||||
collectFromTypedStmtExpr se
|
||||
|
||||
collectFromTypedStmtExpr :: TAST.TypedStmtExpr -> CP_Infos
|
||||
collectFromTypedStmtExpr (TAST.Assign lhs rhs _) =
|
||||
collectFromTypedExpr lhs ++ collectFromTypedExpr rhs
|
||||
collectFromTypedStmtExpr (TAST.New t args _) =
|
||||
collectFromExprs args
|
||||
++ [ mkUtf8Info t ("class name: " ++ t),
|
||||
mkClassInfo 0 ("class: " ++ t),
|
||||
mkMethodRefInfo 0 0 ("new method ref: " ++ t ++ ".<init>:()V")
|
||||
]
|
||||
collectFromTypedStmtExpr (TAST.MethodCall obj name args retTyp) =
|
||||
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 [] = []
|
||||
collectFromStmts (s : ss) = collectFromTypedStmt s ++ collectFromStmts ss
|
||||
|
||||
collectFromTypedStmt :: TAST.TypedStmt -> CP_Infos
|
||||
collectFromTypedStmt (TAST.Block stmts _) =
|
||||
collectFromStmts stmts
|
||||
collectFromTypedStmt (TAST.Return (Just e) _) =
|
||||
collectFromTypedExpr e
|
||||
collectFromTypedStmt (TAST.Return Nothing _) =
|
||||
[]
|
||||
collectFromTypedStmt (TAST.While cond body _) =
|
||||
collectFromTypedExpr cond ++ collectFromTypedStmt body
|
||||
collectFromTypedStmt (TAST.LocalVarDecl _ _ _) =
|
||||
[]
|
||||
collectFromTypedStmt (TAST.If cond then_ mElse _) =
|
||||
collectFromTypedExpr cond
|
||||
++ collectFromTypedStmt then_
|
||||
++ collectFromMaybeStmt mElse
|
||||
collectFromTypedStmt (TAST.StmtExprStmt se _) =
|
||||
collectFromTypedStmtExpr se
|
||||
collectFromTypedStmt (TAST.EmptyStmt _) =
|
||||
[]
|
||||
|
||||
collectFromMaybeStmt :: Maybe TAST.TypedStmt -> CP_Infos
|
||||
collectFromMaybeStmt Nothing = []
|
||||
collectFromMaybeStmt (Just s) = collectFromTypedStmt s
|
||||
|
||||
collectFromFields :: [TAST.TypedFieldDecl] -> CP_Infos
|
||||
collectFromFields [] = []
|
||||
collectFromFields (TAST.Field fType fName _ : fs) =
|
||||
[ mkUtf8Info fName ("field: " ++ fName),
|
||||
mkUtf8Info (typeToDescriptor fType) ("field desc: " ++ fType)
|
||||
]
|
||||
++ collectFromFields fs
|
||||
|
||||
collectFromMethods :: [TAST.TypedMethodDecl] -> CP_Infos
|
||||
collectFromMethods [] = []
|
||||
collectFromMethods (TAST.Method mType mName params body : ms) =
|
||||
[ mkUtf8Info mName ("method: " ++ mName),
|
||||
mkUtf8Info (methodDescriptor params mType) ("method desc: " ++ mName)
|
||||
]
|
||||
++ collectFromTypedStmt body
|
||||
++ collectFromMethods ms
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Build the complete constant pool for a class
|
||||
------------------------------------------------------------------------
|
||||
|
||||
generateConstPool :: TAST.TypedClass -> CP_Infos
|
||||
generateConstPool (TAST.Class cName fields methods) =
|
||||
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.
|
||||
-- So for field at 0-based index i: name Utf8 = #(9 + 2i + 1), desc = #(9 + 2i + 2).
|
||||
-- NameAndType entries follow at #(9 + 2*numFields + 2j + 1) for j-th init field.
|
||||
buildInitFieldPool :: [TAST.TypedFieldDecl] -> Int -> CP_Infos
|
||||
buildInitFieldPool fields numFields = go 0 0 fields
|
||||
where
|
||||
go _ _ [] = []
|
||||
go fieldIdx initIdx (TAST.Field _ _ Nothing : rest) =
|
||||
go (fieldIdx + 1) initIdx rest
|
||||
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
|
||||
in [ mkNameAndTypeInfo nameUtf8Idx descUtf8Idx ("field NameAndType: " ++ name),
|
||||
mkFieldRefInfo 3 natCpIdx ("field ref: " ++ name)
|
||||
]
|
||||
++ go (fieldIdx + 1) (initIdx + 1) rest
|
||||
|
||||
lookupClassIndex :: CP_Infos -> String -> IndexConstantPool
|
||||
lookupClassIndex pool name =
|
||||
case findIndex matches pool of
|
||||
Just i -> i
|
||||
Nothing -> 0
|
||||
where
|
||||
matches entry = case entry of
|
||||
ClassInfo {desc = descVal}
|
||||
| name == "java/lang/Object" -> descVal == "super class" || isSuffixOf name descVal
|
||||
| otherwise -> descVal == name || isSuffixOf name descVal
|
||||
_ -> False
|
||||
|
||||
lookupUtf8Index :: CP_Infos -> String -> IndexConstantPool
|
||||
lookupUtf8Index pool value =
|
||||
case findIndex matches pool of
|
||||
Just i -> i
|
||||
Nothing -> 0
|
||||
where
|
||||
matches entry = case entry of
|
||||
Utf8Info {cad_cp = cadVal, desc = descVal} -> cadVal == value || isSuffixOf value descVal
|
||||
_ -> False
|
||||
|
||||
lookupNameAndTypeIndex :: CP_Infos -> String -> IndexConstantPool
|
||||
lookupNameAndTypeIndex pool name =
|
||||
case findIndex matches pool of
|
||||
Just i -> i
|
||||
Nothing -> lookupUtf8Index pool name
|
||||
where
|
||||
matches entry = case entry of
|
||||
NameAndTypeInfo {desc = descVal} -> descVal == name || isSuffixOf name descVal
|
||||
_ -> False
|
||||
|
||||
lookupFieldRefIndex :: CP_Infos -> String -> IndexConstantPool
|
||||
lookupFieldRefIndex pool name =
|
||||
case findIndex matches pool of
|
||||
Just i -> i
|
||||
Nothing -> 0
|
||||
where
|
||||
matches entry = case entry of
|
||||
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
|
||||
go _ [] = Nothing
|
||||
go i (x : xs)
|
||||
| predicate x = Just i
|
||||
| otherwise = go (i + 1) xs
|
||||
@@ -0,0 +1,171 @@
|
||||
module Codegen.Lowerer where
|
||||
|
||||
import Codegen.ClassFile
|
||||
import Codegen.Types (CP_Info (..), CP_Infos)
|
||||
import Data.Word (Word8)
|
||||
import Numeric (showHex)
|
||||
|
||||
data Instruction
|
||||
= ALoad Int
|
||||
| ALoad0
|
||||
| ILoad Int
|
||||
| AStore Int
|
||||
| IStore Int
|
||||
| GetField Int
|
||||
| PutField Int
|
||||
| InvokeSpecial Int
|
||||
| InvokeVirtual Int
|
||||
| InvokeStatic Int
|
||||
| IConst Int
|
||||
| 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
|
||||
| AReturn
|
||||
deriving (Show, Eq)
|
||||
|
||||
data BtcLine = BtcLine
|
||||
{ lineNumber :: Integer,
|
||||
instruction :: Instruction
|
||||
}
|
||||
deriving (Show, Eq)
|
||||
|
||||
newtype BtcProgram = BtcProgram [BtcLine]
|
||||
deriving (Show, Eq)
|
||||
|
||||
-- Lower a full ClassFile into a BtcProgram per method
|
||||
-- Returns one BtcProgram per method, paired with its name
|
||||
lowerClassFile :: ClassFile -> [(String, BtcProgram)]
|
||||
lowerClassFile cf =
|
||||
[ (methodName cf m, lowerMethod m)
|
||||
| m <- methods cf
|
||||
]
|
||||
|
||||
-- Extract method name from the constant pool
|
||||
methodName :: ClassFile -> Method_Info -> String
|
||||
methodName cf m =
|
||||
case lookupPool (constantPool cf) (methodNameIndex m) of
|
||||
Just (Utf8Info {cad_cp = name}) -> name
|
||||
_ -> "<unknown>"
|
||||
|
||||
-- Lower a single method's Code attribute into a BtcProgram
|
||||
lowerMethod :: Method_Info -> BtcProgram
|
||||
lowerMethod m =
|
||||
case findCodeAttr (methodAttributes m) of
|
||||
Just attr -> lowerCode attr
|
||||
Nothing -> BtcProgram [] -- abstract/native method, no body
|
||||
|
||||
findCodeAttr :: Attribute_Infos -> Maybe Attribute_Info
|
||||
findCodeAttr = foldr step Nothing
|
||||
where
|
||||
step attr@Code_Attribute {} _ = Just attr
|
||||
step _ acc = acc
|
||||
|
||||
-- Reconstruct BtcProgram from raw code bytes
|
||||
-- This is the tricky direction — bytes → instructions
|
||||
lowerCode :: Attribute_Info -> BtcProgram
|
||||
lowerCode attr = BtcProgram (decodeBytes 0 (codeBody attr))
|
||||
|
||||
decodeBytes :: Int -> [Word8] -> [BtcLine]
|
||||
decodeBytes _ [] = []
|
||||
decodeBytes pos (b : bs) = case b of
|
||||
0x2A -> line ALoad0 pos 1 bs
|
||||
0x2B -> line (ALoad 1) pos 1 bs
|
||||
0x2C -> line (ALoad 2) pos 1 bs
|
||||
0x2D -> line (ALoad 3) pos 1 bs
|
||||
0x19 -> withByte bs pos $ \n rest -> line (ALoad n) pos 2 rest
|
||||
0x1A -> line (ILoad 0) pos 1 bs
|
||||
0x1B -> line (ILoad 1) pos 1 bs
|
||||
0x1C -> line (ILoad 2) pos 1 bs
|
||||
0x1D -> line (ILoad 3) pos 1 bs
|
||||
0x15 -> withByte bs pos $ \n rest -> line (ILoad n) pos 2 rest
|
||||
0x4B -> line (AStore 0) pos 1 bs
|
||||
0x4C -> line (AStore 1) pos 1 bs
|
||||
0x4D -> line (AStore 2) pos 1 bs
|
||||
0x4E -> line (AStore 3) pos 1 bs
|
||||
0x3A -> withByte bs pos $ \n rest -> line (AStore n) pos 2 rest
|
||||
0x3B -> line (IStore 0) pos 1 bs
|
||||
0x3C -> line (IStore 1) pos 1 bs
|
||||
0x3D -> line (IStore 2) pos 1 bs
|
||||
0x3E -> line (IStore 3) pos 1 bs
|
||||
0x36 -> withByte bs pos $ \n rest -> line (IStore n) pos 2 rest
|
||||
0xB4 -> withIndex bs pos $ \i rest -> line (GetField i) pos 3 rest
|
||||
0xB5 -> withIndex bs pos $ \i rest -> line (PutField i) pos 3 rest
|
||||
0xB7 -> withIndex bs pos $ \i rest -> line (InvokeSpecial i) pos 3 rest
|
||||
0xB6 -> withIndex bs pos $ \i rest -> line (InvokeVirtual i) pos 3 rest
|
||||
0xB8 -> withIndex bs pos $ \i rest -> line (InvokeStatic i) pos 3 rest
|
||||
0x02 -> line (IConst (-1)) pos 1 bs
|
||||
0x03 -> line (IConst 0) pos 1 bs
|
||||
0x04 -> line (IConst 1) pos 1 bs
|
||||
0x05 -> line (IConst 2) pos 1 bs
|
||||
0x06 -> line (IConst 3) pos 1 bs
|
||||
0x07 -> line (IConst 4) pos 1 bs
|
||||
0x08 -> line (IConst 5) pos 1 bs
|
||||
0x01 -> line AConstNull pos 1 bs
|
||||
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
|
||||
0xB0 -> line AReturn pos 1 bs
|
||||
unknown -> error $ "Unknown opcode 0x" ++ showHex unknown "" ++ " at position " ++ show pos
|
||||
|
||||
-- Helpers
|
||||
line :: Instruction -> Int -> Int -> [Word8] -> [BtcLine]
|
||||
line instr pos size rest = BtcLine (toInteger pos) instr : decodeBytes (pos + size) rest
|
||||
|
||||
withByte :: [Word8] -> Int -> (Int -> [Word8] -> [BtcLine]) -> [BtcLine]
|
||||
withByte (b : rest) _ f = f (fromIntegral b) rest
|
||||
withByte [] pos _ = error $ "Unexpected end of bytecode at " ++ show pos
|
||||
|
||||
withIndex :: [Word8] -> Int -> (Int -> [Word8] -> [BtcLine]) -> [BtcLine]
|
||||
withIndex (hi : lo : rest) _ f = f (fromIntegral hi * 256 + fromIntegral lo) rest
|
||||
withIndex _ pos _ = error $ "Unexpected end of bytecode at " ++ show pos
|
||||
|
||||
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
|
||||
@@ -0,0 +1,164 @@
|
||||
module Codegen.Serializer where
|
||||
|
||||
import Codegen.ClassFile
|
||||
import Codegen.Lowerer (BtcLine (..), BtcProgram (..), Instruction (..))
|
||||
import Codegen.Types
|
||||
import Data.Bits (shiftR, (.&.), (.|.))
|
||||
import Data.Char (ord)
|
||||
import Data.Word (Word16, Word32, Word8)
|
||||
|
||||
-- Split a 16-bit Int into two bytes (big-endian), as JVM expects
|
||||
indexBytes :: Int -> [Word8]
|
||||
indexBytes n =
|
||||
[ fromIntegral (n `shiftR` 8 .&. 0xFF),
|
||||
fromIntegral (n .&. 0xFF)
|
||||
]
|
||||
|
||||
serializeInstruction :: Instruction -> [Word8]
|
||||
serializeInstruction instr = case instr of
|
||||
-- aload_<n>: 0x2A–0x2D for 0–3, else wide form 0x19 <index>
|
||||
ALoad 0 -> [0x2A]
|
||||
ALoad 1 -> [0x2B]
|
||||
ALoad 2 -> [0x2C]
|
||||
ALoad 3 -> [0x2D]
|
||||
ALoad n -> [0x19, fromIntegral n]
|
||||
ALoad0 -> [0x2A] -- shorthand for ALoad 0
|
||||
|
||||
-- iload_<n>: 0x1A–0x1D for 0–3, else wide form 0x15 <index>
|
||||
ILoad 0 -> [0x1A]
|
||||
ILoad 1 -> [0x1B]
|
||||
ILoad 2 -> [0x1C]
|
||||
ILoad 3 -> [0x1D]
|
||||
ILoad n -> [0x15, fromIntegral n]
|
||||
-- astore_<n>: 0x4B–0x4E for 0–3, else 0x3A <index>
|
||||
AStore 0 -> [0x4B]
|
||||
AStore 1 -> [0x4C]
|
||||
AStore 2 -> [0x4D]
|
||||
AStore 3 -> [0x4E]
|
||||
AStore n -> [0x3A, fromIntegral n]
|
||||
-- istore_<n>: 0x3B–0x3E for 0–3, else 0x36 <index>
|
||||
IStore 0 -> [0x3B]
|
||||
IStore 1 -> [0x3C]
|
||||
IStore 2 -> [0x3D]
|
||||
IStore 3 -> [0x3E]
|
||||
IStore n -> [0x36, fromIntegral n]
|
||||
-- Field access: opcode + 2-byte constant pool index
|
||||
GetField i -> 0xB4 : indexBytes i
|
||||
PutField i -> 0xB5 : indexBytes i
|
||||
-- Method invocation: opcode + 2-byte constant pool index
|
||||
InvokeSpecial i -> 0xB7 : indexBytes i
|
||||
InvokeVirtual i -> 0xB6 : indexBytes i
|
||||
InvokeStatic i -> 0xB8 : indexBytes i
|
||||
-- iconst_<n>: -1 through 5 have dedicated opcodes
|
||||
IConst (-1) -> [0x02]
|
||||
IConst 0 -> [0x03]
|
||||
IConst 1 -> [0x04]
|
||||
IConst 2 -> [0x05]
|
||||
IConst 3 -> [0x06]
|
||||
IConst 4 -> [0x07]
|
||||
IConst 5 -> [0x08]
|
||||
IConst n -> error $ "IConst out of range (-1..5): " ++ show n
|
||||
-- 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
|
||||
| i <= 255 -> [0x12, fromIntegral i]
|
||||
| otherwise -> 0x13 : indexBytes i -- ldc_w
|
||||
|
||||
-- Arithmetic
|
||||
IAdd -> [0x60]
|
||||
ISub -> [0x64]
|
||||
IMul -> [0x68]
|
||||
IDiv -> [0x6C]
|
||||
-- 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]
|
||||
IReturn -> [0xAC]
|
||||
AReturn -> [0xB0]
|
||||
|
||||
-- Serialize a full program — concatenate all instruction bytes
|
||||
serializeProgram :: BtcProgram -> [Word8]
|
||||
serializeProgram (BtcProgram lines) =
|
||||
concatMap (serializeInstruction . instruction) lines
|
||||
|
||||
serializeClassFile :: ClassFile -> [Word8]
|
||||
serializeClassFile cf =
|
||||
u4 (let Magic m = magic cf in m)
|
||||
++ u2 (let MinorVersion v = minorVersion cf in v)
|
||||
++ u2 (let MajorVersion v = majorVersion cf in v)
|
||||
++ u2 (length (constantPool cf) + 1)
|
||||
++ concatMap cpEntry (constantPool cf)
|
||||
++ aflags (accessFlags cf)
|
||||
++ u2 (let ThisClass i = thisClass cf in i)
|
||||
++ 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)
|
||||
where
|
||||
u2 :: Int -> [Word8]
|
||||
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]
|
||||
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
|
||||
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)
|
||||
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
|
||||
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)
|
||||
@@ -0,0 +1,57 @@
|
||||
module Codegen.Types where
|
||||
|
||||
type IndexConstantPool = Int
|
||||
|
||||
type CP_Infos = [CP_Info]
|
||||
|
||||
data CP_Info
|
||||
= ClassInfo
|
||||
{ tag_cp :: Tag,
|
||||
index_cp :: IndexConstantPool,
|
||||
desc :: String
|
||||
}
|
||||
| FieldRefInfo
|
||||
{ tag_cp :: Tag,
|
||||
index_name_cp :: IndexConstantPool,
|
||||
index_nameandtype_cp :: IndexConstantPool,
|
||||
desc :: String
|
||||
}
|
||||
| MethodRefInfo
|
||||
{ tag_cp :: Tag,
|
||||
index_name_cp :: IndexConstantPool,
|
||||
index_nameandtype_cp :: IndexConstantPool,
|
||||
desc :: String
|
||||
}
|
||||
| StringInfo
|
||||
{ tag_cp :: Tag,
|
||||
index_cp :: IndexConstantPool,
|
||||
desc :: String
|
||||
}
|
||||
| IntegerInfo
|
||||
{ tag_cp :: Tag,
|
||||
numi_cp :: Int,
|
||||
desc :: String
|
||||
}
|
||||
| NameAndTypeInfo
|
||||
{ tag_cp :: Tag,
|
||||
index_name_cp :: IndexConstantPool,
|
||||
index_descr_cp :: IndexConstantPool,
|
||||
desc :: String
|
||||
}
|
||||
| Utf8Info
|
||||
{ tag_cp :: Tag,
|
||||
tam_cp :: Int,
|
||||
cad_cp :: String,
|
||||
desc :: String
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
data Tag
|
||||
= TagClass
|
||||
| TagFieldRef
|
||||
| TagMethodRef
|
||||
| TagString
|
||||
| TagInteger
|
||||
| TagNameAndType
|
||||
| TagUtf8
|
||||
deriving (Eq, Ord, Show)
|
||||
+4
-3
@@ -6,7 +6,7 @@ type Program = [Class]
|
||||
|
||||
data Class = Class Type [FieldDecl] [MethodDecl] deriving (Show, Eq)
|
||||
|
||||
data FieldDecl = Field Type String deriving (Show, Eq)
|
||||
data FieldDecl = Field Type String (Maybe Expr) deriving (Show, Eq)
|
||||
|
||||
data MethodDecl = Method Type String [(Type, String)] Stmt deriving (Show, Eq)
|
||||
|
||||
@@ -19,7 +19,7 @@ data Expr
|
||||
| Integer Integer
|
||||
| Bool Bool
|
||||
| Char Char
|
||||
| Jnull
|
||||
| Null
|
||||
| StmtExprExpr StmtExpr
|
||||
deriving (Show, Eq)
|
||||
|
||||
@@ -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
|
||||
|
||||
+41
-11
@@ -10,14 +10,18 @@ import Grammar.AST
|
||||
%error { parseError }
|
||||
|
||||
%token
|
||||
public { TokenVisibility Public }
|
||||
private { TokenVisibility Private }
|
||||
class { TokenClass }
|
||||
void { TokenVoid }
|
||||
return { TokenReturn }
|
||||
new { TokenNew }
|
||||
this { TokenThis }
|
||||
null { TokenNull }
|
||||
if { TokenIf }
|
||||
else { TokenElse }
|
||||
while { TokenWhile }
|
||||
for { TokenFor }
|
||||
int { TokenIntType }
|
||||
boolean { TokenBoolType }
|
||||
char { TokenCharType }
|
||||
@@ -68,15 +72,29 @@ Classes : Classes ClassDecl { $2 : $1 }
|
||||
| ClassDecl { [$1] }
|
||||
|
||||
ClassDecl : class id '{' Decls '}' { Class $2 (fst $4) (snd $4) }
|
||||
| public class id '{' Decls '}' { Class $3 (fst $5) (snd $5) }
|
||||
| private class id '{' Decls '}' { Class $3 (fst $5) (snd $5) }
|
||||
|
||||
Decls : FieldDecl Decls { ($1 : fst $2, snd $2) }
|
||||
| MethodDecl Decls { (fst $2, $1 : snd $2) }
|
||||
| {- leer -} { ([], []) }
|
||||
|
||||
FieldDecl : Type id ';' { Field $1 $2 }
|
||||
FieldDecl : Type id ';' { Field $1 $2 Nothing }
|
||||
| public Type id ';' { Field $2 $3 Nothing }
|
||||
| private Type id ';' { Field $2 $3 Nothing }
|
||||
| Type id '=' Expr ';' { Field $1 $2 (Just $4) }
|
||||
| public Type id '=' Expr ';' { Field $2 $3 (Just $5) }
|
||||
| private Type id '=' Expr ';' { Field $2 $3 (Just $5) }
|
||||
|
||||
MethodDecl : Type id '(' Params ')' Block { Method $1 $2 $4 $6 }
|
||||
| void id '(' Params ')' Block { Method "void" $2 $4 $6 }
|
||||
MethodDecl : Type id '(' Params ')' Block { Method $1 $2 $4 $6 }
|
||||
| public Type id '(' Params ')' Block { Method $2 $3 $5 $7 }
|
||||
| private Type id '(' Params ')' Block { Method $2 $3 $5 $7 }
|
||||
| void id '(' Params ')' Block { Method "void" $2 $4 $6 }
|
||||
| public void id '(' Params ')' Block { Method "void" $3 $5 $7 }
|
||||
| private void id '(' Params ')' Block { Method "void" $3 $5 $7 }
|
||||
| id '(' Params ')' Block { Method "void" "<init>" $3 $5 }
|
||||
| private id '(' Params ')' Block { Method "void" "<init>" $4 $6 }
|
||||
| public id '(' Params ')' Block { Method "void" "<init>" $4 $6 }
|
||||
|
||||
Params : ParamList { $1 }
|
||||
| {- leer -} { [] }
|
||||
@@ -96,18 +114,29 @@ Block : '{' Stmts '}' { Block (reverse $2) }
|
||||
Stmts : Stmts Stmt { $2 : $1 }
|
||||
| {- leer -} { [] }
|
||||
|
||||
Stmt : Block { $1 }
|
||||
| return Expr ';' { Return $2 }
|
||||
| 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 }
|
||||
@@ -115,6 +144,7 @@ Expr : this { This }
|
||||
| intLit { Integer $1 }
|
||||
| boolLit { Bool $1 }
|
||||
| charLit { Char $1 }
|
||||
| null { Null }
|
||||
| '(' Expr ')' { $2 }
|
||||
| '!' Expr { Unary Not $2 }
|
||||
| '-' Expr { Unary Negate $2 }
|
||||
@@ -141,5 +171,5 @@ ExprList : Expr { [$1] }
|
||||
|
||||
{
|
||||
parseError :: [Token] -> a
|
||||
parseError tokens = error $ "Lexical error or syntax error at: " ++ show tokens
|
||||
parseError tokens = error $ "abc Lexical error or syntax error at: " ++ show tokens
|
||||
}
|
||||
|
||||
+11
-1
@@ -14,11 +14,14 @@ tokens :-
|
||||
"/*"([^\*]|(\*+([^\*\/])))*\*+\/ ; -- multi-line comments
|
||||
|
||||
-- key words
|
||||
public { \_ -> TokenVisibility Public }
|
||||
private { \_ -> TokenVisibility Private }
|
||||
class { \_ -> TokenClass }
|
||||
void { \_ -> TokenVoid }
|
||||
return { \_ -> TokenReturn }
|
||||
new { \_ -> TokenNew }
|
||||
this { \_ -> TokenThis }
|
||||
null { \_ -> TokenNull }
|
||||
if { \_ -> TokenIf }
|
||||
else { \_ -> TokenElse }
|
||||
while { \_ -> TokenWhile }
|
||||
@@ -49,13 +52,20 @@ tokens :-
|
||||
$alpha [$alpha $digit]* { \s -> TokenIdent s }
|
||||
|
||||
{
|
||||
data Visibility
|
||||
= Public
|
||||
| Private
|
||||
deriving (Eq, Show)
|
||||
|
||||
data Token
|
||||
= TokenClass
|
||||
= TokenVisibility Visibility
|
||||
| TokenClass
|
||||
| TokenStatic
|
||||
| TokenVoid
|
||||
| TokenReturn
|
||||
| TokenNew
|
||||
| TokenThis
|
||||
| TokenNull
|
||||
| TokenIf
|
||||
| TokenElse
|
||||
| TokenWhile
|
||||
|
||||
+31
-6
@@ -1,13 +1,38 @@
|
||||
module Grammar.TAST where
|
||||
|
||||
import Grammar.AST
|
||||
import Grammar.AST (BinaryOperator, Type, UnaryOperator)
|
||||
|
||||
data TypedExpr = TypedExpr Expr Type
|
||||
data TypedClass = Class Type [TypedFieldDecl] [TypedMethodDecl] deriving (Show, Eq)
|
||||
|
||||
data TypedStmtExpr = TypedStmtExpr StmtExpr Type
|
||||
data TypedFieldDecl = Field Type String (Maybe TypedExpr) deriving (Show, Eq)
|
||||
|
||||
data TypedStmt = TypedStmt Stmt Type
|
||||
data TypedMethodDecl = Method Type String [(Type, String)] TypedStmt deriving (Show, Eq)
|
||||
|
||||
data TypedBinaryOperator = TypedBinaryOperator BinaryOperator Type
|
||||
data TypedExpr
|
||||
= This Type
|
||||
| LocalOrFieldVar String Type
|
||||
| InstVar TypedExpr String Type
|
||||
| Unary UnaryOperator TypedExpr Type
|
||||
| Binary BinaryOperator TypedExpr TypedExpr Type
|
||||
| Integer Integer Type
|
||||
| Bool Bool Type
|
||||
| Char Char Type
|
||||
| Null Type
|
||||
| StmtExprExpr TypedStmtExpr Type
|
||||
deriving (Show, Eq)
|
||||
|
||||
data TypedUnaryOperator = TypedUnaryOperator UnaryOperator Type
|
||||
data TypedStmtExpr
|
||||
= Assign TypedExpr TypedExpr Type
|
||||
| New Type [TypedExpr] Type
|
||||
| MethodCall TypedExpr String [TypedExpr] Type
|
||||
deriving (Show, Eq)
|
||||
|
||||
data TypedStmt
|
||||
= Block [TypedStmt] Type
|
||||
| Return (Maybe TypedExpr) Type
|
||||
| While TypedExpr TypedStmt Type
|
||||
| LocalVarDecl Type String Type
|
||||
| If TypedExpr TypedStmt (Maybe TypedStmt) Type
|
||||
| StmtExprStmt TypedStmtExpr Type
|
||||
| EmptyStmt Type
|
||||
deriving (Show, Eq)
|
||||
|
||||
+49
-5
@@ -1,10 +1,54 @@
|
||||
module Main where
|
||||
|
||||
import Grammar.AST
|
||||
import Grammar.TAST
|
||||
import Typecheck.SemanticChecker
|
||||
import Codegen.ClassFile (ClassFile, generateClassFile)
|
||||
import Codegen.Serializer (serializeClassFile)
|
||||
import qualified Data.ByteString as BS
|
||||
import Grammar.AST (Program)
|
||||
import Grammar.Parser (parse)
|
||||
import Grammar.Scanner (Token, alexScanTokens)
|
||||
import Grammar.TAST (TypedClass (Class))
|
||||
import System.Environment (getArgs)
|
||||
import Typecheck.SemanticChecker (typeCheckClass)
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
let t = getTypeFromExpr $ TypedExpr This "myType"
|
||||
putStrLn t
|
||||
args <- getArgs
|
||||
case args of
|
||||
[path] -> runPipeline path
|
||||
_ -> putStrLn "Usage: compoiler <input.java>"
|
||||
|
||||
runPipeline :: FilePath -> IO ()
|
||||
runPipeline path = do
|
||||
source <- readFile path
|
||||
let tokens = scan source
|
||||
let ast = parseProgram tokens
|
||||
let typedClasses = typeCheckProgram ast
|
||||
let classFiles = map generateClassFile typedClasses
|
||||
mapM_ writeClassFile (zip typedClasses classFiles)
|
||||
reportSuccess ast typedClasses classFiles
|
||||
|
||||
writeClassFile :: (TypedClass, ClassFile) -> IO ()
|
||||
writeClassFile (tc, cf) = do
|
||||
let outPath = "out/" ++ className tc ++ ".class"
|
||||
BS.writeFile outPath (BS.pack (serializeClassFile cf))
|
||||
putStrLn ("Written: " ++ outPath)
|
||||
|
||||
scan :: String -> [Token]
|
||||
scan = alexScanTokens
|
||||
|
||||
parseProgram :: [Token] -> Program
|
||||
parseProgram = parse
|
||||
|
||||
typeCheckProgram :: Program -> [TypedClass]
|
||||
typeCheckProgram classes =
|
||||
map (\cls -> typeCheckClass cls [] classes) classes
|
||||
|
||||
reportSuccess :: Program -> [TypedClass] -> [ClassFile] -> IO ()
|
||||
reportSuccess ast typed classFiles = do
|
||||
putStrLn ("Parsed classes: " ++ show (length ast))
|
||||
putStrLn ("Typed classes: " ++ show (length typed))
|
||||
putStrLn ("Generated class files: " ++ show (length classFiles))
|
||||
putStrLn ("Class names: " ++ unwords (map className typed))
|
||||
|
||||
className :: TypedClass -> String
|
||||
className (Class name _ _) = name
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
module Testsuite.AbcFiles.AllSyntaxTestABC (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 = ALoad0},
|
||||
BtcLine {lineNumber = 10, instruction = IConst 0},
|
||||
BtcLine {lineNumber = 11, instruction = PutField 3},
|
||||
BtcLine {lineNumber = 14, instruction = ALoad0},
|
||||
BtcLine {lineNumber = 15, instruction = ALoad0},
|
||||
BtcLine {lineNumber = 16, instruction = GetField 3},
|
||||
BtcLine {lineNumber = 19, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 20, instruction = IAdd},
|
||||
BtcLine {lineNumber = 21, instruction = PutField 3},
|
||||
BtcLine {lineNumber = 24, instruction = Return}
|
||||
]
|
||||
),
|
||||
( "inc",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = ALoad0},
|
||||
BtcLine {lineNumber = 1, instruction = ALoad0},
|
||||
BtcLine {lineNumber = 2, instruction = GetField 2},
|
||||
BtcLine {lineNumber = 5, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 6, instruction = IAdd},
|
||||
BtcLine {lineNumber = 7, instruction = PutField 2},
|
||||
BtcLine {lineNumber = 10, instruction = ALoad0},
|
||||
BtcLine {lineNumber = 11, instruction = GetField 2},
|
||||
BtcLine {lineNumber = 14, instruction = IReturn}
|
||||
]
|
||||
),
|
||||
( "inc",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = ALoad0},
|
||||
BtcLine {lineNumber = 1, instruction = ALoad0},
|
||||
BtcLine {lineNumber = 2, instruction = GetField 2},
|
||||
BtcLine {lineNumber = 5, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 6, instruction = IAdd},
|
||||
BtcLine {lineNumber = 7, instruction = PutField 2},
|
||||
BtcLine {lineNumber = 10, instruction = ALoad0},
|
||||
BtcLine {lineNumber = 11, instruction = GetField 2},
|
||||
BtcLine {lineNumber = 14, instruction = IReturn}
|
||||
]
|
||||
),
|
||||
( "sumUpTo",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = IConst 0},
|
||||
BtcLine {lineNumber = 1, instruction = IStore 2},
|
||||
BtcLine {lineNumber = 2, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 3, instruction = IStore 3},
|
||||
BtcLine {lineNumber = 4, instruction = ILoad 3},
|
||||
BtcLine {lineNumber = 5, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 6, instruction = IfGt 20},
|
||||
BtcLine {lineNumber = 9, instruction = ILoad 2},
|
||||
BtcLine {lineNumber = 10, instruction = ILoad 3},
|
||||
BtcLine {lineNumber = 11, instruction = IAdd},
|
||||
BtcLine {lineNumber = 12, instruction = IStore 2},
|
||||
BtcLine {lineNumber = 13, instruction = ILoad 3},
|
||||
BtcLine {lineNumber = 14, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 15, instruction = IAdd},
|
||||
BtcLine {lineNumber = 16, instruction = IStore 3},
|
||||
BtcLine {lineNumber = 17, instruction = Goto 4},
|
||||
BtcLine {lineNumber = 20, instruction = ILoad 2},
|
||||
BtcLine {lineNumber = 21, instruction = IReturn}
|
||||
]
|
||||
),
|
||||
( "predicate",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 1, instruction = BIPush 97},
|
||||
BtcLine {lineNumber = 2, instruction = IfEq 10},
|
||||
BtcLine {lineNumber = 5, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 6, instruction = BIPush 98},
|
||||
BtcLine {lineNumber = 7, instruction = IfEq 10},
|
||||
BtcLine {lineNumber = 10, instruction = IConst 0},
|
||||
BtcLine {lineNumber = 11, instruction = IReturn},
|
||||
BtcLine {lineNumber = 12, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 13, instruction = IReturn}
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -0,0 +1,33 @@
|
||||
module Testsuite.AbcFiles.ArithmeticTestABC (expectedABC) where
|
||||
|
||||
import Codegen.Lowerer (BtcLine (..), BtcProgram (..), Instruction (..))
|
||||
|
||||
expectedABC :: [(String, BtcProgram)]
|
||||
expectedABC =
|
||||
[
|
||||
( "basic",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = ILoad 0},
|
||||
BtcLine {lineNumber = 1, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 2, instruction = IAdd},
|
||||
BtcLine {lineNumber = 3, instruction = ILoad 2},
|
||||
BtcLine {lineNumber = 4, instruction = ILoad 0},
|
||||
BtcLine {lineNumber = 5, instruction = IMul},
|
||||
BtcLine {lineNumber = 6, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 7, instruction = ISub},
|
||||
BtcLine {lineNumber = 8, instruction = ILoad 2},
|
||||
BtcLine {lineNumber = 9, instruction = ISub},
|
||||
BtcLine {lineNumber = 10, instruction = IReturn}
|
||||
]
|
||||
),
|
||||
( "logic",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = ILoad 0},
|
||||
BtcLine {lineNumber = 1, instruction = ILoad 2},
|
||||
BtcLine {lineNumber = 2, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 3, instruction = IAdd},
|
||||
BtcLine {lineNumber = 4, instruction = IAdd},
|
||||
BtcLine {lineNumber = 5, instruction = IReturn}
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -0,0 +1,53 @@
|
||||
module Testsuite.AbcFiles.CombinedControlTestABC (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 = Return}
|
||||
]
|
||||
),
|
||||
( "compute",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = IConst 0},
|
||||
BtcLine {lineNumber = 1, instruction = IStore 1},
|
||||
BtcLine {lineNumber = 2, instruction = IConst 0},
|
||||
BtcLine {lineNumber = 3, instruction = IStore 2},
|
||||
BtcLine {lineNumber = 4, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 5, instruction = ALoad0},
|
||||
BtcLine {lineNumber = 6, instruction = GetField 2},
|
||||
BtcLine {lineNumber = 9, instruction = IfGe 37},
|
||||
BtcLine {lineNumber = 12, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 13, instruction = IConst 2},
|
||||
BtcLine {lineNumber = 14, instruction = IDiv},
|
||||
BtcLine {lineNumber = 15, instruction = IConst 2},
|
||||
BtcLine {lineNumber = 16, instruction = IMul},
|
||||
BtcLine {lineNumber = 17, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 18, instruction = ISub},
|
||||
BtcLine {lineNumber = 19, instruction = IfNe 31},
|
||||
BtcLine {lineNumber = 22, instruction = ILoad 2},
|
||||
BtcLine {lineNumber = 23, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 24, instruction = IAdd},
|
||||
BtcLine {lineNumber = 25, instruction = IStore 2},
|
||||
BtcLine {lineNumber = 26, instruction = Goto 35},
|
||||
BtcLine {lineNumber = 29, instruction = ILoad 2},
|
||||
BtcLine {lineNumber = 30, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 31, instruction = ISub},
|
||||
BtcLine {lineNumber = 32, instruction = IStore 2},
|
||||
BtcLine {lineNumber = 35, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 36, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 37, instruction = IAdd},
|
||||
BtcLine {lineNumber = 38, instruction = IStore 1},
|
||||
BtcLine {lineNumber = 39, instruction = Goto 4},
|
||||
BtcLine {lineNumber = 42, instruction = ILoad 2},
|
||||
BtcLine {lineNumber = 43, instruction = IReturn}
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -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}
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -0,0 +1,15 @@
|
||||
module Testsuite.AbcFiles.EmptyTestABC (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}
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -0,0 +1,34 @@
|
||||
module Testsuite.AbcFiles.ExpressionTestABC (expectedABC) where
|
||||
|
||||
import Codegen.Lowerer (BtcLine (..), BtcProgram (..), Instruction (..))
|
||||
|
||||
expectedABC :: [(String, BtcProgram)]
|
||||
expectedABC =
|
||||
[
|
||||
( "shortCircuit",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 1, instruction = IConst 0},
|
||||
BtcLine {lineNumber = 2, instruction = IfEq 15},
|
||||
BtcLine {lineNumber = 5, instruction = BIPush 10},
|
||||
BtcLine {lineNumber = 6, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 7, instruction = IDiv},
|
||||
BtcLine {lineNumber = 8, instruction = ILoad 2},
|
||||
BtcLine {lineNumber = 9, instruction = IfLe 15},
|
||||
BtcLine {lineNumber = 12, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 13, instruction = Goto 16},
|
||||
BtcLine {lineNumber = 15, instruction = IConst 0},
|
||||
BtcLine {lineNumber = 16, instruction = IStore 3},
|
||||
BtcLine {lineNumber = 17, instruction = ILoad 3},
|
||||
BtcLine {lineNumber = 18, instruction = IReturn}
|
||||
]
|
||||
),
|
||||
( "charArithmetic",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 1, instruction = IStore 3},
|
||||
BtcLine {lineNumber = 2, instruction = ILoad 3},
|
||||
BtcLine {lineNumber = 3, instruction = IReturn}
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -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}
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -0,0 +1,49 @@
|
||||
module Testsuite.AbcFiles.IfTestABC (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}
|
||||
]
|
||||
),
|
||||
( "ifElseTest",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = ILoad 0},
|
||||
BtcLine {lineNumber = 1, instruction = IConst 0},
|
||||
BtcLine {lineNumber = 2, instruction = ISub},
|
||||
BtcLine {lineNumber = 3, instruction = IfGe 8},
|
||||
|
||||
BtcLine {lineNumber = 6, instruction = IConst 0},
|
||||
BtcLine {lineNumber = 7, instruction = IReturn},
|
||||
|
||||
BtcLine {lineNumber = 8, instruction = Goto 27},
|
||||
|
||||
BtcLine {lineNumber = 11, instruction = ILoad 0},
|
||||
BtcLine {lineNumber = 12, instruction = IConst 0},
|
||||
BtcLine {lineNumber = 13, instruction = ISub},
|
||||
BtcLine {lineNumber = 14, instruction = IfNe 8},
|
||||
|
||||
BtcLine {lineNumber = 17, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 18, instruction = IReturn},
|
||||
|
||||
BtcLine {lineNumber = 19, instruction = Goto 16},
|
||||
|
||||
BtcLine {lineNumber = 22, instruction = ILoad 0},
|
||||
BtcLine {lineNumber = 23, instruction = BIPush 10},
|
||||
BtcLine {lineNumber = 25, instruction = ISub},
|
||||
BtcLine {lineNumber = 26, instruction = IfGt 7},
|
||||
|
||||
BtcLine {lineNumber = 29, instruction = IConst 0},
|
||||
BtcLine {lineNumber = 30, instruction = Goto 4},
|
||||
|
||||
BtcLine {lineNumber = 33, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 34, instruction = IReturn}
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -0,0 +1,52 @@
|
||||
module Testsuite.AbcFiles.LoopTestABC (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}
|
||||
]
|
||||
),
|
||||
( "factorial",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 1, instruction = IStore 2},
|
||||
BtcLine {lineNumber = 2, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 3, instruction = IStore 3},
|
||||
BtcLine {lineNumber = 4, instruction = ILoad 3},
|
||||
BtcLine {lineNumber = 5, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 6, instruction = IfGt 21},
|
||||
BtcLine {lineNumber = 9, instruction = ILoad 2},
|
||||
BtcLine {lineNumber = 10, instruction = ILoad 3},
|
||||
BtcLine {lineNumber = 11, instruction = IMul},
|
||||
BtcLine {lineNumber = 12, instruction = IStore 2},
|
||||
BtcLine {lineNumber = 13, instruction = ILoad 3},
|
||||
BtcLine {lineNumber = 14, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 15, instruction = IAdd},
|
||||
BtcLine {lineNumber = 16, instruction = IStore 3},
|
||||
BtcLine {lineNumber = 17, instruction = Goto 4},
|
||||
BtcLine {lineNumber = 20, instruction = ILoad 2},
|
||||
BtcLine {lineNumber = 21, instruction = IReturn}
|
||||
]
|
||||
),
|
||||
( "weirdFor",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = IConst 0},
|
||||
BtcLine {lineNumber = 1, instruction = IStore 1},
|
||||
BtcLine {lineNumber = 2, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 3, instruction = IConst 5},
|
||||
BtcLine {lineNumber = 4, instruction = IfGe 15},
|
||||
BtcLine {lineNumber = 7, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 8, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 9, instruction = IAdd},
|
||||
BtcLine {lineNumber = 10, instruction = IStore 1},
|
||||
BtcLine {lineNumber = 11, instruction = Goto 2},
|
||||
BtcLine {lineNumber = 14, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 15, instruction = IReturn}
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -0,0 +1,66 @@
|
||||
module Testsuite.AbcFiles.MaliciousTestABC (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}
|
||||
]
|
||||
),
|
||||
( "assignNegativeIncrement",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 1, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 2, instruction = IAdd},
|
||||
BtcLine {lineNumber = 3, instruction = ISub}, -- Using subtraction patterns for Unary Negative emulation
|
||||
BtcLine {lineNumber = 4, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 5, instruction = IAdd},
|
||||
BtcLine {lineNumber = 6, instruction = IStore 1},
|
||||
BtcLine {lineNumber = 7, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 8, instruction = IReturn}
|
||||
]
|
||||
),
|
||||
( "tripleAddition",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 1, instruction = ILoad 2},
|
||||
BtcLine {lineNumber = 2, instruction = ILoad 3},
|
||||
BtcLine {lineNumber = 3, instruction = IAdd},
|
||||
BtcLine {lineNumber = 4, instruction = IAdd},
|
||||
BtcLine {lineNumber = 5, instruction = IStore 4},
|
||||
BtcLine {lineNumber = 6, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 7, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 8, instruction = IAdd},
|
||||
BtcLine {lineNumber = 9, instruction = IStore 1},
|
||||
BtcLine {lineNumber = 10, instruction = ILoad 2},
|
||||
BtcLine {lineNumber = 11, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 12, instruction = IAdd},
|
||||
BtcLine {lineNumber = 13, instruction = IStore 2},
|
||||
BtcLine {lineNumber = 14, instruction = ILoad 3},
|
||||
BtcLine {lineNumber = 15, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 16, instruction = IAdd},
|
||||
BtcLine {lineNumber = 17, instruction = IStore 3},
|
||||
BtcLine {lineNumber = 18, instruction = ILoad 4},
|
||||
BtcLine {lineNumber = 19, instruction = IReturn}
|
||||
]
|
||||
),
|
||||
( "cursedFormatting",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 1, instruction = IfNe 7},
|
||||
BtcLine {lineNumber = 4, instruction = IConst 0},
|
||||
BtcLine {lineNumber = 5, instruction = IReturn},
|
||||
BtcLine {lineNumber = 7, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 8, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 9, instruction = IfNe 15},
|
||||
BtcLine {lineNumber = 12, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 13, instruction = IReturn},
|
||||
BtcLine {lineNumber = 15, instruction = IConst 2},
|
||||
BtcLine {lineNumber = 16, instruction = IReturn}
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -0,0 +1,22 @@
|
||||
module Testsuite.AbcFiles.MethodOverloadTestABC (expectedABC) where
|
||||
|
||||
import Codegen.Lowerer (BtcLine (..), BtcProgram (..), Instruction (..))
|
||||
|
||||
expectedABC :: [(String, BtcProgram)]
|
||||
expectedABC =
|
||||
[
|
||||
( "MethodOverload",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = BIPush 42},
|
||||
BtcLine {lineNumber = 1, instruction = IReturn}
|
||||
]
|
||||
),
|
||||
( "MethodOverload",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = BIPush 42},
|
||||
BtcLine {lineNumber = 1, instruction = ILoad 0},
|
||||
BtcLine {lineNumber = 2, instruction = IAdd},
|
||||
BtcLine {lineNumber = 3, 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,28 @@
|
||||
module Testsuite.AbcFiles.MultipleClassesTestABC (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 = InvokeSpecial 2},
|
||||
BtcLine {lineNumber = 8, instruction = PutField 3},
|
||||
BtcLine {lineNumber = 11, instruction = Return}
|
||||
]
|
||||
),
|
||||
( "<init>",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = ALoad0},
|
||||
BtcLine {lineNumber = 1, instruction = InvokeSpecial 1},
|
||||
BtcLine {lineNumber = 4, instruction = ALoad0},
|
||||
BtcLine {lineNumber = 5, instruction = BIPush 42},
|
||||
BtcLine {lineNumber = 6, instruction = PutField 4},
|
||||
BtcLine {lineNumber = 9, instruction = Return}
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -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}
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -0,0 +1,21 @@
|
||||
module Testsuite.AbcFiles.ReturnTestABC (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}
|
||||
]
|
||||
),
|
||||
( "main",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = BIPush 42},
|
||||
BtcLine {lineNumber = 2, instruction = IReturn}
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -0,0 +1,38 @@
|
||||
module Testsuite.AbcFiles.ShenaniganceTestABC (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}
|
||||
]
|
||||
),
|
||||
( "testAssignment",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 1, instruction = IStore 1},
|
||||
BtcLine {lineNumber = 2, instruction = IConst 5},
|
||||
BtcLine {lineNumber = 3, instruction = IStore 1},
|
||||
BtcLine {lineNumber = 4, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 5, instruction = IStore 2},
|
||||
BtcLine {lineNumber = 6, instruction = ILoad 2},
|
||||
BtcLine {lineNumber = 7, instruction = IReturn}
|
||||
]
|
||||
),
|
||||
( "divEqual",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = Ldc 2},
|
||||
BtcLine {lineNumber = 2, instruction = IStore 1},
|
||||
BtcLine {lineNumber = 3, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 4, instruction = IConst 4},
|
||||
BtcLine {lineNumber = 5, instruction = IDiv},
|
||||
BtcLine {lineNumber = 6, instruction = IStore 1},
|
||||
BtcLine {lineNumber = 7, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 8, 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}
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -0,0 +1,40 @@
|
||||
module Testsuite.AbcFiles.WhileTestABC (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}
|
||||
]
|
||||
),
|
||||
( "whileLoopTest",
|
||||
BtcProgram
|
||||
[ BtcLine {lineNumber = 0, instruction = IConst 0},
|
||||
BtcLine {lineNumber = 1, instruction = IStore 1},
|
||||
BtcLine {lineNumber = 2, instruction = ILoad 0},
|
||||
BtcLine {lineNumber = 3, instruction = IConst 0},
|
||||
BtcLine {lineNumber = 4, instruction = ISub},
|
||||
BtcLine {lineNumber = 5, instruction = IfLe 14},
|
||||
|
||||
BtcLine {lineNumber = 8, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 9, instruction = ILoad 0},
|
||||
BtcLine {lineNumber = 10, instruction = IAdd},
|
||||
BtcLine {lineNumber = 11, instruction = IStore 1},
|
||||
|
||||
BtcLine {lineNumber = 12, instruction = ILoad 0},
|
||||
BtcLine {lineNumber = 13, instruction = IConst 1},
|
||||
BtcLine {lineNumber = 14, instruction = ISub},
|
||||
BtcLine {lineNumber = 15, instruction = IStore 0},
|
||||
|
||||
BtcLine {lineNumber = 16, instruction = Goto 65522},
|
||||
|
||||
BtcLine {lineNumber = 19, instruction = ILoad 1},
|
||||
BtcLine {lineNumber = 20, instruction = IReturn}
|
||||
]
|
||||
)
|
||||
]
|
||||
@@ -0,0 +1,76 @@
|
||||
module Testsuite.AstFiles.AllSyntaxTestAST (expectedAST) where
|
||||
|
||||
import Grammar.AST
|
||||
|
||||
expectedAST =
|
||||
[ Class
|
||||
"AllSyntaxTest"
|
||||
[ Field "int" "x" Nothing
|
||||
, Field "int" "counter" Nothing
|
||||
]
|
||||
[ Method
|
||||
"void"
|
||||
"<init>"
|
||||
[("int", "initial")]
|
||||
( Block
|
||||
[ StmtExprStmt (Assign (LocalOrFieldVar "x") (LocalOrFieldVar "initial"))
|
||||
, StmtExprStmt (Assign (LocalOrFieldVar "counter") (Integer 0))
|
||||
, StmtExprStmt (Assign (LocalOrFieldVar "counter") (Binary Add (LocalOrFieldVar "counter") (Integer 1)))
|
||||
]
|
||||
)
|
||||
, Method
|
||||
"int"
|
||||
"inc"
|
||||
[]
|
||||
( Block
|
||||
[ StmtExprStmt (Assign (LocalOrFieldVar "x") (Binary Add (LocalOrFieldVar "x") (Integer 1)))
|
||||
, Return (Just (LocalOrFieldVar "x"))
|
||||
]
|
||||
)
|
||||
, Method
|
||||
"int"
|
||||
"inc"
|
||||
[("int", "delta")]
|
||||
( Block
|
||||
[ StmtExprStmt (Assign (LocalOrFieldVar "x") (Binary Add (LocalOrFieldVar "x") (LocalOrFieldVar "delta")))
|
||||
, Return (Just (LocalOrFieldVar "x"))
|
||||
]
|
||||
)
|
||||
, Method
|
||||
"int"
|
||||
"sumUpTo"
|
||||
[("int", "n")]
|
||||
( Block
|
||||
[ LocalVarDecl "int" "s" (Just (Integer 0))
|
||||
, Block
|
||||
[ LocalVarDecl "int" "i" (Just (Integer 1))
|
||||
, While
|
||||
(Binary CompLessOrEqual (LocalOrFieldVar "i") (LocalOrFieldVar "n"))
|
||||
( Block
|
||||
[ Block
|
||||
[ StmtExprStmt (Assign (LocalOrFieldVar "s") (Binary Add (LocalOrFieldVar "s") (LocalOrFieldVar "i")))
|
||||
]
|
||||
, StmtExprStmt (Assign (LocalOrFieldVar "i") (Binary Add (LocalOrFieldVar "i") (Integer 1)))
|
||||
]
|
||||
)
|
||||
]
|
||||
, Return (Just (LocalOrFieldVar "s"))
|
||||
]
|
||||
)
|
||||
, Method
|
||||
"boolean"
|
||||
"predicate"
|
||||
[("char", "c")]
|
||||
( Block
|
||||
[ Return
|
||||
( Just
|
||||
( Binary
|
||||
Or
|
||||
(Binary CompEqual (LocalOrFieldVar "c") (Char 'a'))
|
||||
(Binary CompEqual (LocalOrFieldVar "c") (Char 'b'))
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,48 @@
|
||||
module Testsuite.AstFiles.ArithmeticTestAST (expectedAST) where
|
||||
|
||||
import Grammar.AST
|
||||
|
||||
expectedAST =
|
||||
[ Class
|
||||
"ArithmeticTest"
|
||||
[]
|
||||
[ Method
|
||||
"int"
|
||||
"basic"
|
||||
[("int", "a"), ("int", "b"), ("int", "c")]
|
||||
( Block
|
||||
[ Return
|
||||
( Just
|
||||
( Binary
|
||||
Subtract
|
||||
(Binary Add (LocalOrFieldVar "a") (LocalOrFieldVar "b"))
|
||||
( Binary
|
||||
Modulo
|
||||
( Binary
|
||||
Divide
|
||||
(Binary Multiply (LocalOrFieldVar "c") (LocalOrFieldVar "a"))
|
||||
(LocalOrFieldVar "b")
|
||||
)
|
||||
(LocalOrFieldVar "c")
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
),
|
||||
Method
|
||||
"boolean"
|
||||
"logic"
|
||||
[("boolean", "a"), ("boolean", "b"), ("boolean", "c")]
|
||||
( Block
|
||||
[ Return
|
||||
( Just
|
||||
( Binary
|
||||
And
|
||||
(Unary Not (LocalOrFieldVar "a"))
|
||||
(Binary Or (LocalOrFieldVar "c") (LocalOrFieldVar "b"))
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,39 @@
|
||||
module Testsuite.AstFiles.CombinedControlTestAST (expectedAST) where
|
||||
|
||||
import Grammar.AST
|
||||
|
||||
expectedAST =
|
||||
[ Class
|
||||
"CombinedControlTest"
|
||||
[ Field "int" "field" Nothing
|
||||
]
|
||||
[ Method
|
||||
"void"
|
||||
"<init>"
|
||||
[("int", "v")]
|
||||
( Block
|
||||
[ StmtExprStmt (Assign (LocalOrFieldVar "field") (LocalOrFieldVar "v"))
|
||||
]
|
||||
)
|
||||
, Method
|
||||
"int"
|
||||
"compute"
|
||||
[]
|
||||
( Block
|
||||
[ LocalVarDecl "int" "i" (Just (Integer 0))
|
||||
, LocalVarDecl "int" "acc" (Just (Integer 0))
|
||||
, While
|
||||
(Binary CompLessThan (LocalOrFieldVar "i") (LocalOrFieldVar "field"))
|
||||
( Block
|
||||
[ If
|
||||
(Binary CompEqual (Binary Modulo (LocalOrFieldVar "i") (Integer 2)) (Integer 0))
|
||||
(Block [StmtExprStmt (Assign (LocalOrFieldVar "acc") (Binary Add (LocalOrFieldVar "acc") (LocalOrFieldVar "i")))])
|
||||
(Just (Block [StmtExprStmt (Assign (LocalOrFieldVar "acc") (Binary Subtract (LocalOrFieldVar "acc") (LocalOrFieldVar "i")))]))
|
||||
, StmtExprStmt (Assign (LocalOrFieldVar "i") (Binary Add (LocalOrFieldVar "i") (Integer 1)))
|
||||
]
|
||||
)
|
||||
, Return (Just (LocalOrFieldVar "acc"))
|
||||
]
|
||||
)
|
||||
]
|
||||
]
|
||||
@@ -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 (Unary Negate (Integer 1)))]
|
||||
[ Method
|
||||
"void"
|
||||
"<init>"
|
||||
[("int", "initial_value")]
|
||||
(Block [StmtExprStmt (Assign (LocalOrFieldVar "a") (LocalOrFieldVar "initial_value"))])
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,10 @@
|
||||
module Testsuite.AstFiles.EmptyTestAST (expectedAST) where
|
||||
|
||||
import Grammar.AST
|
||||
|
||||
expectedAST =
|
||||
[ Class
|
||||
"EmptyTest"
|
||||
[]
|
||||
[]
|
||||
]
|
||||
@@ -0,0 +1,28 @@
|
||||
module Testsuite.AstFiles.ExpressionTestAST (expectedAST) where
|
||||
|
||||
import Grammar.AST
|
||||
|
||||
expectedAST =
|
||||
[ Class
|
||||
"ExpressionTest"
|
||||
[]
|
||||
[ Method
|
||||
"boolean"
|
||||
"shortCircuit"
|
||||
[("int", "a"), ("int", "b")]
|
||||
( Block
|
||||
[ LocalVarDecl "boolean" "res" (Just (Binary And (Binary CompNotEqual (LocalOrFieldVar "a") (Integer 0)) (Binary CompGreaterThan (Binary Divide (Integer 10) (LocalOrFieldVar "a")) (LocalOrFieldVar "b"))))
|
||||
, Return (Just (LocalOrFieldVar "res"))
|
||||
]
|
||||
)
|
||||
, Method
|
||||
"char"
|
||||
"charArithmetic"
|
||||
[("char", "c"), ("int", "offset")]
|
||||
( Block
|
||||
[ LocalVarDecl "char" "d" (Just (LocalOrFieldVar "c"))
|
||||
, Return (Just (LocalOrFieldVar "d"))
|
||||
]
|
||||
)
|
||||
]
|
||||
]
|
||||
@@ -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))
|
||||
]
|
||||
[]
|
||||
]
|
||||
@@ -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,50 @@
|
||||
module Testsuite.AstFiles.LoopTestAST (expectedAST) where
|
||||
|
||||
import Grammar.AST
|
||||
|
||||
expectedAST =
|
||||
[ Class
|
||||
"LoopTest"
|
||||
[]
|
||||
[ Method
|
||||
"int"
|
||||
"factorial"
|
||||
[("int", "n")]
|
||||
( Block
|
||||
[ LocalVarDecl "int" "tally" (Just (Integer 1))
|
||||
, Block
|
||||
[ LocalVarDecl "int" "i" (Just (Integer 1))
|
||||
, While
|
||||
(Binary CompLessOrEqual (LocalOrFieldVar "i") (LocalOrFieldVar "n"))
|
||||
( Block
|
||||
[ Block
|
||||
[ StmtExprStmt (Assign (LocalOrFieldVar "tally") (Binary Multiply (LocalOrFieldVar "tally") (LocalOrFieldVar "i")))
|
||||
]
|
||||
, StmtExprStmt (Assign (LocalOrFieldVar "i") (Binary Add (LocalOrFieldVar "i") (Integer 1)))
|
||||
]
|
||||
)
|
||||
]
|
||||
, Return (Just (LocalOrFieldVar "tally"))
|
||||
]
|
||||
)
|
||||
, Method
|
||||
"int"
|
||||
"weirdFor"
|
||||
[]
|
||||
( Block
|
||||
[ LocalVarDecl "int" "k" (Just (Integer 0))
|
||||
, Block
|
||||
[ EmptyStmt
|
||||
, While
|
||||
(Binary CompLessThan (LocalOrFieldVar "k") (Integer 5))
|
||||
( Block
|
||||
[ Block []
|
||||
, StmtExprStmt (Assign (LocalOrFieldVar "k") (Binary Add (LocalOrFieldVar "k") (Integer 1)))
|
||||
]
|
||||
)
|
||||
]
|
||||
, Return (Just (LocalOrFieldVar "k"))
|
||||
]
|
||||
)
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,27 @@
|
||||
module Testsuite.AstFiles.MaliciousTestAST (expectedAST) where
|
||||
|
||||
import Grammar.AST
|
||||
|
||||
expectedAST =
|
||||
[ Class
|
||||
"MaliciousTest"
|
||||
[]
|
||||
[ Method
|
||||
"int"
|
||||
"cursedFormatting"
|
||||
[("int", "n")]
|
||||
( Block
|
||||
[ If
|
||||
(Binary CompEqual (LocalOrFieldVar "n") (Integer 0))
|
||||
(Block [Return (Just (Integer 0))])
|
||||
( Just
|
||||
( If
|
||||
(Binary CompEqual (LocalOrFieldVar "n") (Integer 1))
|
||||
(Block [Return (Just (Integer 1))])
|
||||
(Just (Block [Return (Just (Integer 2))]))
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
module Testsuite.AstFiles.MethodOverloadTestAST (expectedAST) where
|
||||
|
||||
import Grammar.AST
|
||||
|
||||
expectedAST =
|
||||
[ Class
|
||||
"MethodOverloadTest"
|
||||
[]
|
||||
[ Method
|
||||
"int"
|
||||
"MethodOverload"
|
||||
[]
|
||||
(Block [Return (Just (Integer 42))]),
|
||||
Method
|
||||
"int"
|
||||
"MethodOverload"
|
||||
[("int", "a")]
|
||||
(Block [Return (Just (Binary Add (Integer 42) (LocalOrFieldVar "a")))])
|
||||
]
|
||||
]
|
||||
@@ -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,16 @@
|
||||
module Testsuite.AstFiles.MultipleClassesTestAST (expectedAST) where
|
||||
|
||||
import Grammar.AST
|
||||
|
||||
expectedAST =
|
||||
[ Class
|
||||
"MultipleClassesTest"
|
||||
[ Field "AnotherTestClass" "a" (Just (StmtExprExpr (New "AnotherTestClass" [])))
|
||||
]
|
||||
[]
|
||||
, Class
|
||||
"AnotherTestClass"
|
||||
[ Field "int" "a" (Just (Integer 42))
|
||||
]
|
||||
[]
|
||||
]
|
||||
@@ -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 (InstVar This "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))
|
||||
(Block [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)])
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
]
|
||||
@@ -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,30 @@
|
||||
module Testsuite.AstFiles.ShenaniganceTestAST (expectedAST) where
|
||||
|
||||
import Grammar.AST
|
||||
|
||||
expectedAST =
|
||||
[ Class
|
||||
"ShenaniganceTest"
|
||||
[]
|
||||
[ Method
|
||||
"int"
|
||||
"testAssignment"
|
||||
[]
|
||||
( Block
|
||||
[ LocalVarDecl "int" "x" (Just (Integer 1))
|
||||
, LocalVarDecl "int" "y" (Just (StmtExprExpr (Assign (LocalOrFieldVar "x") (Integer 5))))
|
||||
, Return (Just (LocalOrFieldVar "y"))
|
||||
]
|
||||
)
|
||||
, Method
|
||||
"int"
|
||||
"divEqual"
|
||||
[]
|
||||
( Block
|
||||
[ LocalVarDecl "int" "x" (Just (Integer 234343000))
|
||||
, StmtExprStmt (Assign (LocalOrFieldVar "x") (Binary Divide (LocalOrFieldVar "x") (Integer 4)))
|
||||
, Return (Just (LocalOrFieldVar "x"))
|
||||
]
|
||||
)
|
||||
]
|
||||
]
|
||||
@@ -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"))
|
||||
]
|
||||
)
|
||||
]
|
||||
]
|
||||
@@ -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" (Just (Integer 0)),
|
||||
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"))
|
||||
]
|
||||
)
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,114 @@
|
||||
module Testsuite.ExecutableTests.HarnessSupport
|
||||
( TestResult (..),
|
||||
TestFailure (..),
|
||||
runNamedTests,
|
||||
printTestResults,
|
||||
exitOnFailures
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Exception (Exception, SomeException, fromException, try)
|
||||
import Data.List (dropWhileEnd, intercalate)
|
||||
import System.Exit (ExitCode (..), exitFailure)
|
||||
import System.IO (BufferMode (LineBuffering), hFlush, hSetBuffering, stderr, stdout)
|
||||
|
||||
newtype TestFailure = TestFailure {failureDetails :: String}
|
||||
deriving (Show)
|
||||
|
||||
instance Exception TestFailure
|
||||
|
||||
data TestResult = TestResult
|
||||
{ testName :: String,
|
||||
passed :: Bool,
|
||||
details :: String
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
runNamedTests :: [(String, IO ())] -> IO [TestResult]
|
||||
runNamedTests tests = do
|
||||
hSetBuffering stdout LineBuffering
|
||||
hSetBuffering stderr LineBuffering
|
||||
runNamedTests' tests
|
||||
|
||||
runNamedTests' :: [(String, IO ())] -> IO [TestResult]
|
||||
runNamedTests' [] = pure []
|
||||
runNamedTests' (test : rest) = do
|
||||
result <- runNamedTest test
|
||||
results <- runNamedTests' rest
|
||||
pure (result : results)
|
||||
|
||||
runNamedTest :: (String, IO ()) -> IO TestResult
|
||||
runNamedTest (name, action) = do
|
||||
putStrLn ("[RUN ] " ++ name)
|
||||
hFlush stdout
|
||||
result <- try action :: IO (Either SomeException ())
|
||||
case result of
|
||||
Right () -> do
|
||||
putStrLn ("[DONE] " ++ name)
|
||||
hFlush stdout
|
||||
pure (TestResult name True "")
|
||||
Left ex -> do
|
||||
let details = failureDetailsForSummary ex
|
||||
let runtimeDetails = failureDetailsForRuntime ex
|
||||
if null runtimeDetails
|
||||
then pure ()
|
||||
else putStrLn runtimeDetails
|
||||
putStrLn ("[DONE] " ++ name)
|
||||
hFlush stdout
|
||||
pure (TestResult name False details)
|
||||
|
||||
printTestResults :: String -> [TestResult] -> IO ()
|
||||
printTestResults title results = do
|
||||
putStrLn ("\n== " ++ title ++ " ==")
|
||||
mapM_ printResult results
|
||||
let total = length results
|
||||
failures = length (filter (not . passed) results)
|
||||
successes = total - failures
|
||||
putStrLn ("Summary: " ++ show successes ++ "/" ++ show total ++ " passed")
|
||||
|
||||
printResult :: TestResult -> IO ()
|
||||
printResult (TestResult name True _) =
|
||||
putStrLn ("[PASS] " ++ name)
|
||||
printResult (TestResult name False info) = do
|
||||
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 . (" " ++)) rest
|
||||
|
||||
exitOnFailures :: [TestResult] -> IO ()
|
||||
exitOnFailures results =
|
||||
if any (not . passed) results
|
||||
then exitFailure
|
||||
else pure ()
|
||||
|
||||
renderException :: SomeException -> String
|
||||
renderException ex =
|
||||
case fromException ex :: Maybe ExitCode of
|
||||
Just ExitSuccess -> "unexpected ExitSuccess exception"
|
||||
Just (ExitFailure code) -> "exit failure code " ++ show code
|
||||
Nothing -> sanitize (show ex)
|
||||
|
||||
failureDetailsForRuntime :: SomeException -> String
|
||||
failureDetailsForRuntime ex =
|
||||
case fromException ex :: Maybe TestFailure of
|
||||
Just (TestFailure details) -> stripTrailingNewlines details
|
||||
Nothing -> indentOneLine (renderException ex)
|
||||
|
||||
failureDetailsForSummary :: SomeException -> String
|
||||
failureDetailsForSummary ex =
|
||||
case fromException ex :: Maybe TestFailure of
|
||||
Just (TestFailure details) -> stripTrailingNewlines details
|
||||
Nothing -> renderException ex
|
||||
|
||||
indentOneLine :: String -> String
|
||||
indentOneLine text = " " ++ text
|
||||
|
||||
stripTrailingNewlines :: String -> String
|
||||
stripTrailingNewlines = dropWhileEnd (== '\n') . dropWhileEnd (== '\r')
|
||||
|
||||
sanitize :: String -> String
|
||||
sanitize = intercalate " " . words
|
||||
@@ -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)
|
||||
]
|
||||
@@ -0,0 +1,337 @@
|
||||
module Testsuite.ExecutableTests.Registry where
|
||||
|
||||
import Testsuite.ExecutableTests.TestCase
|
||||
|
||||
-- AllSyntaxTest
|
||||
import qualified Testsuite.AstFiles.AllSyntaxTestAST as AllSyntaxAST
|
||||
import qualified Testsuite.TastFiles.AllSyntaxTestTAST as AllSyntaxTAST
|
||||
import qualified Testsuite.AbcFiles.AllSyntaxTestABC as AllSyntaxABC
|
||||
|
||||
-- ArithmeticTest
|
||||
import qualified Testsuite.AstFiles.ArithmeticTestAST as ArithmeticAST
|
||||
import qualified Testsuite.TastFiles.ArithmeticTestTAST as ArithmeticTAST
|
||||
import qualified Testsuite.AbcFiles.ArithmeticTestABC as ArithmeticABC
|
||||
|
||||
-- CombinedControlTest
|
||||
import qualified Testsuite.AstFiles.CombinedControlTestAST as CombinedControlAST
|
||||
import qualified Testsuite.TastFiles.CombinedControlTestTAST as CombinedControlTAST
|
||||
import qualified Testsuite.AbcFiles.CombinedControlTestABC as CombinedControlABC
|
||||
|
||||
-- ConstructorOverloadTest
|
||||
import qualified Testsuite.AstFiles.ConstructorOverloadTestAST as ConstructorOverloadAST
|
||||
import qualified Testsuite.TastFiles.ConstructorOverloadTestTAST as ConstructorOverloadTAST
|
||||
import qualified Testsuite.AbcFiles.ConstructorOverloadTestABC as ConstructorOverloadABC
|
||||
|
||||
-- ConstructorTest
|
||||
import qualified Testsuite.AstFiles.ConstructorTestAST as ConstructorAST
|
||||
import qualified Testsuite.TastFiles.ConstructorTestTAST as ConstructorTAST
|
||||
import qualified Testsuite.AbcFiles.ConstructorTestABC as ConstructorABC
|
||||
|
||||
-- EmptyTest
|
||||
import qualified Testsuite.AstFiles.EmptyTestAST as EmptyAST
|
||||
import qualified Testsuite.TastFiles.EmptyTestTAST as EmptyTAST
|
||||
import qualified Testsuite.AbcFiles.EmptyTestABC as EmptyABC
|
||||
|
||||
-- ExpressionTest
|
||||
import qualified Testsuite.AstFiles.ExpressionTestAST as ExpressionAST
|
||||
import qualified Testsuite.TastFiles.ExpressionTestTAST as ExpressionTAST
|
||||
import qualified Testsuite.AbcFiles.ExpressionTestABC as ExpressionABC
|
||||
|
||||
-- FieldsTest
|
||||
import qualified Testsuite.AstFiles.FieldsTestAST as FieldsAST
|
||||
import qualified Testsuite.TastFiles.FieldsTestTAST as FieldsTAST
|
||||
import qualified Testsuite.AbcFiles.FieldsTestABC as FieldsABC
|
||||
|
||||
-- IfTest
|
||||
import qualified Testsuite.AstFiles.IfTestAST as IfAST
|
||||
import qualified Testsuite.TastFiles.IfTestTAST as IfTAST
|
||||
import qualified Testsuite.AbcFiles.IfTestABC as IfABC
|
||||
|
||||
-- LoopTest
|
||||
import qualified Testsuite.AstFiles.LoopTestAST as LoopAST
|
||||
import qualified Testsuite.TastFiles.LoopTestTAST as LoopTAST
|
||||
import qualified Testsuite.AbcFiles.LoopTestABC as LoopABC
|
||||
|
||||
-- MaliciousTest
|
||||
import qualified Testsuite.AstFiles.MaliciousTestAST as MaliciousAST
|
||||
import qualified Testsuite.TastFiles.MaliciousTestTAST as MaliciousTAST
|
||||
import qualified Testsuite.AbcFiles.MaliciousTestABC as MaliciousABC
|
||||
|
||||
-- MethodOverloadTest
|
||||
import qualified Testsuite.AstFiles.MethodOverloadTestAST as MethodOverloadAST
|
||||
import qualified Testsuite.TastFiles.MethodOverloadTestTAST as MethodOverloadTAST
|
||||
import qualified Testsuite.AbcFiles.MethodOverloadTestABC as MethodOverloadABC
|
||||
|
||||
-- MultiClassTest
|
||||
import qualified Testsuite.AstFiles.MultiClassTestAST as MultiClassAST
|
||||
import qualified Testsuite.TastFiles.MultiClassTestTAST as MultiClassTAST
|
||||
import qualified Testsuite.AbcFiles.MultiClassTestABC as MultiClassABC
|
||||
|
||||
-- MultipleClassesTest
|
||||
import qualified Testsuite.AstFiles.MultipleClassesTestAST as MultipleClassesAST
|
||||
import qualified Testsuite.TastFiles.MultipleClassesTestTAST as MultipleClassesTAST
|
||||
import qualified Testsuite.AbcFiles.MultipleClassesTestABC as MultipleClassesABC
|
||||
|
||||
-- RecursionTest
|
||||
import qualified Testsuite.AstFiles.RecursionTestAST as RecursionAST
|
||||
import qualified Testsuite.TastFiles.RecursionTestTAST as RecursionTAST
|
||||
import qualified Testsuite.AbcFiles.RecursionTestABC as RecursionABC
|
||||
|
||||
-- ReturnTest
|
||||
import qualified Testsuite.AstFiles.ReturnTestAST as ReturnAST
|
||||
import qualified Testsuite.TastFiles.ReturnTestTAST as ReturnTAST
|
||||
import qualified Testsuite.AbcFiles.ReturnTestABC as ReturnABC
|
||||
|
||||
-- ShenaniganceTest
|
||||
import qualified Testsuite.AstFiles.ShenaniganceTestAST as ShenaniganceAST
|
||||
import qualified Testsuite.TastFiles.ShenaniganceTestTAST as ShenaniganceTAST
|
||||
import qualified Testsuite.AbcFiles.ShenaniganceTestABC as ShenaniganceABC
|
||||
|
||||
-- SingletonTest
|
||||
import qualified Testsuite.AstFiles.SingletonTestAST as SingletonAST
|
||||
import qualified Testsuite.TastFiles.SingletonTestTAST as SingletonTAST
|
||||
import qualified Testsuite.AbcFiles.SingletonTestABC as SingletonABC
|
||||
|
||||
-- WhileTest
|
||||
import qualified Testsuite.AstFiles.WhileTestAST as WhileAST
|
||||
import qualified Testsuite.TastFiles.WhileTestTAST as WhileTAST
|
||||
import qualified Testsuite.AbcFiles.WhileTestABC as WhileABC
|
||||
|
||||
import Codegen.Serializer (serializeProgram)
|
||||
import Data.Word (Word8)
|
||||
import qualified Codegen.Lowerer
|
||||
|
||||
toBytecode :: [(String, Codegen.Lowerer.BtcProgram)] -> [(String, [Word8])]
|
||||
toBytecode = map (fmap (serializeProgram))
|
||||
|
||||
allTests :: [TestCase]
|
||||
allTests =
|
||||
[ allSyntaxTest
|
||||
, arithmeticTest
|
||||
, combinedControlTest
|
||||
, constructorOverloadTest
|
||||
, constructorTest
|
||||
, emptyTest
|
||||
, expressionTest
|
||||
, fieldsTest
|
||||
, ifTest
|
||||
, loopTest
|
||||
, maliciousTest
|
||||
, methodOverloadTest
|
||||
, multiClassTest
|
||||
, multipleClassesTest
|
||||
, recursionTest
|
||||
, returnTest
|
||||
, shenaniganceTest
|
||||
, singletonTest
|
||||
, whileTest
|
||||
]
|
||||
|
||||
allSyntaxTest :: TestCase
|
||||
allSyntaxTest =
|
||||
TestCase
|
||||
{ tcName = "AllSyntaxTest"
|
||||
, tcJavaFile = "src/Testsuite/javaFiles/AllSyntaxTest.java"
|
||||
, tcExpectedAST = AllSyntaxAST.expectedAST
|
||||
, tcExpectedTAST = AllSyntaxTAST.expectedTAST
|
||||
, tcExpectedABC = AllSyntaxABC.expectedABC
|
||||
, tcExpectedBytecode = toBytecode AllSyntaxABC.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
|
||||
}
|
||||
|
||||
combinedControlTest :: TestCase
|
||||
combinedControlTest =
|
||||
TestCase
|
||||
{ tcName = "CombinedControlTest"
|
||||
, tcJavaFile = "src/Testsuite/javaFiles/CombinedControlTest.java"
|
||||
, tcExpectedAST = CombinedControlAST.expectedAST
|
||||
, tcExpectedTAST = CombinedControlTAST.expectedTAST
|
||||
, tcExpectedABC = CombinedControlABC.expectedABC
|
||||
, tcExpectedBytecode = toBytecode CombinedControlABC.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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
expressionTest :: TestCase
|
||||
expressionTest =
|
||||
TestCase
|
||||
{ tcName = "ExpressionTest"
|
||||
, tcJavaFile = "src/Testsuite/javaFiles/ExpressionTest.java"
|
||||
, tcExpectedAST = ExpressionAST.expectedAST
|
||||
, tcExpectedTAST = ExpressionTAST.expectedTAST
|
||||
, tcExpectedABC = ExpressionABC.expectedABC
|
||||
, tcExpectedBytecode = toBytecode ExpressionABC.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
|
||||
}
|
||||
|
||||
loopTest :: TestCase
|
||||
loopTest =
|
||||
TestCase
|
||||
{ tcName = "LoopTest"
|
||||
, tcJavaFile = "src/Testsuite/javaFiles/LoopTest.java"
|
||||
, tcExpectedAST = LoopAST.expectedAST
|
||||
, tcExpectedTAST = LoopTAST.expectedTAST
|
||||
, tcExpectedABC = LoopABC.expectedABC
|
||||
, tcExpectedBytecode = toBytecode LoopABC.expectedABC
|
||||
}
|
||||
|
||||
maliciousTest :: TestCase
|
||||
maliciousTest =
|
||||
TestCase
|
||||
{ tcName = "MaliciousTest"
|
||||
, tcJavaFile = "src/Testsuite/javaFiles/MaliciousTest.java"
|
||||
, tcExpectedAST = MaliciousAST.expectedAST
|
||||
, tcExpectedTAST = MaliciousTAST.expectedTAST
|
||||
, tcExpectedABC = MaliciousABC.expectedABC
|
||||
, tcExpectedBytecode = toBytecode MaliciousABC.expectedABC
|
||||
}
|
||||
|
||||
methodOverloadTest :: TestCase
|
||||
methodOverloadTest =
|
||||
TestCase
|
||||
{ tcName = "MethodOverloadTest"
|
||||
, tcJavaFile = "src/Testsuite/javaFiles/MethodOverloadTest.java"
|
||||
, tcExpectedAST = MethodOverloadAST.expectedAST
|
||||
, tcExpectedTAST = MethodOverloadTAST.expectedTAST
|
||||
, tcExpectedABC = MethodOverloadABC.expectedABC
|
||||
, tcExpectedBytecode = toBytecode MethodOverloadABC.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
|
||||
}
|
||||
|
||||
multipleClassesTest :: TestCase
|
||||
multipleClassesTest =
|
||||
TestCase
|
||||
{ tcName = "MultipleClassesTest"
|
||||
, tcJavaFile = "src/Testsuite/javaFiles/MultipleClassesTest.java"
|
||||
, tcExpectedAST = MultipleClassesAST.expectedAST
|
||||
, tcExpectedTAST = MultipleClassesTAST.expectedTAST
|
||||
, tcExpectedABC = MultipleClassesABC.expectedABC
|
||||
, tcExpectedBytecode = toBytecode MultipleClassesABC.expectedABC
|
||||
}
|
||||
|
||||
recursionTest :: TestCase
|
||||
recursionTest =
|
||||
TestCase
|
||||
{ tcName = "RecursionTest"
|
||||
, tcJavaFile = "src/Testsuite/javaFiles/RecursionTest.java"
|
||||
, tcExpectedAST = RecursionAST.expectedAST
|
||||
, tcExpectedTAST = RecursionTAST.expectedTAST
|
||||
, tcExpectedABC = RecursionABC.expectedABC
|
||||
, tcExpectedBytecode = toBytecode RecursionABC.expectedABC
|
||||
}
|
||||
|
||||
returnTest :: TestCase
|
||||
returnTest =
|
||||
TestCase
|
||||
{ tcName = "ReturnTest"
|
||||
, tcJavaFile = "src/Testsuite/javaFiles/ReturnTest.java"
|
||||
, tcExpectedAST = ReturnAST.expectedAST
|
||||
, tcExpectedTAST = ReturnTAST.expectedTAST
|
||||
, tcExpectedABC = ReturnABC.expectedABC
|
||||
, tcExpectedBytecode = toBytecode ReturnABC.expectedABC
|
||||
}
|
||||
|
||||
shenaniganceTest :: TestCase
|
||||
shenaniganceTest =
|
||||
TestCase
|
||||
{ tcName = "ShenaniganceTest"
|
||||
, tcJavaFile = "src/Testsuite/javaFiles/ShenaniganceTest.java"
|
||||
, tcExpectedAST = ShenaniganceAST.expectedAST
|
||||
, tcExpectedTAST = ShenaniganceTAST.expectedTAST
|
||||
, tcExpectedABC = ShenaniganceABC.expectedABC
|
||||
, tcExpectedBytecode = toBytecode ShenaniganceABC.expectedABC
|
||||
}
|
||||
|
||||
singletonTest :: TestCase
|
||||
singletonTest =
|
||||
TestCase
|
||||
{ tcName = "SingletonTest"
|
||||
, tcJavaFile = "src/Testsuite/javaFiles/SingletonTest.java"
|
||||
, tcExpectedAST = SingletonAST.expectedAST
|
||||
, tcExpectedTAST = SingletonTAST.expectedTAST
|
||||
, tcExpectedABC = SingletonABC.expectedABC
|
||||
, tcExpectedBytecode = toBytecode SingletonABC.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
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
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 =
|
||||
let actual = concatMap (lowerClassFile . generateClassFile) (tcExpectedTAST tc)
|
||||
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
|
||||
@@ -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 AST: " ++ show actual ++ "\n"
|
||||
++ "Expected AST: " ++ 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 Bytecode:\n" ++ unlines expectedLines ++ "Actual Bytecode:\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"
|
||||
++ "Actual TAST: " ++ show actual ++ "\n"
|
||||
++ "Expected TAST: " ++ show (tcExpectedTAST tc)
|
||||
@@ -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
|
||||
@@ -0,0 +1,208 @@
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
|
||||
module Testsuite.ExecutableTests.Shared.ClassFileParser
|
||||
( parseClassFile
|
||||
, parseMethodCodeBytesFromFile
|
||||
) where
|
||||
|
||||
import qualified Data.ByteString as BS
|
||||
import qualified Data.Map.Strict as Map
|
||||
import Data.Bits ((.|.), shiftL)
|
||||
import Data.Word (Word16, Word32, Word8)
|
||||
import Control.Monad (MonadFail)
|
||||
|
||||
parseClassFile :: BS.ByteString -> Either String [(String, [Word8])]
|
||||
parseClassFile bs =
|
||||
case runParser parseClassFileParser (BS.unpack bs) of
|
||||
Left e -> Left e
|
||||
Right (r, _) -> Right r
|
||||
|
||||
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]) }
|
||||
|
||||
instance Functor Parser where
|
||||
fmap f p = Parser $ \i -> do
|
||||
(a,r) <- runParser p i
|
||||
pure (f a,r)
|
||||
|
||||
instance Applicative Parser where
|
||||
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 $ \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
|
||||
|
||||
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 ()
|
||||
|
||||
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)
|
||||
|
||||
parseClassFileParser :: Parser [(String, [Word8])]
|
||||
parseClassFileParser = 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)
|
||||
|
||||
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
|
||||
exceptionTableLen <- fromIntegral <$> u2
|
||||
skipN (exceptionTableLen * 8)
|
||||
attrCount <- fromIntegral <$> u2
|
||||
skipAttributes attrCount
|
||||
pure (Just codeBytes)
|
||||
@@ -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])]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
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
|
||||
|
||||
|
||||
get lexical test errors:
|
||||
runghc -isrc src/Testsuite/ExecutableTests/Main.hs | grep --line-buffered -i -n -B1 'Lexical' | grep 'RUN'
|
||||
@@ -0,0 +1,100 @@
|
||||
module Testsuite.TastFiles.AllSyntaxTestTAST (expectedTAST) where
|
||||
|
||||
import Grammar.AST (BinaryOperator (..), UnaryOperator (..))
|
||||
import Grammar.TAST
|
||||
|
||||
expectedTAST =
|
||||
[ Class
|
||||
"AllSyntaxTest"
|
||||
[ Field "int" "x" Nothing
|
||||
, Field "int" "counter" Nothing
|
||||
]
|
||||
[ Method
|
||||
"void"
|
||||
"<init>"
|
||||
[("int", "initial")]
|
||||
( Block
|
||||
[ StmtExprStmt (Assign (LocalOrFieldVar "x" "int") (LocalOrFieldVar "initial" "int") "int") "void"
|
||||
, StmtExprStmt (Assign (LocalOrFieldVar "counter" "int") (Integer 0 "int") "int") "void"
|
||||
, StmtExprStmt (Assign (LocalOrFieldVar "counter" "int") (Binary Add (LocalOrFieldVar "counter" "int") (Integer 1 "int") "int") "int") "void"
|
||||
]
|
||||
"void"
|
||||
)
|
||||
, Method
|
||||
"int"
|
||||
"inc"
|
||||
[]
|
||||
( Block
|
||||
[ StmtExprStmt (Assign (LocalOrFieldVar "x" "int") (Binary Add (LocalOrFieldVar "x" "int") (Integer 1 "int") "int") "int") "void"
|
||||
, Return (Just (LocalOrFieldVar "x" "int")) "int"
|
||||
]
|
||||
"int"
|
||||
)
|
||||
, Method
|
||||
"int"
|
||||
"inc"
|
||||
[("int", "delta")]
|
||||
( Block
|
||||
[ StmtExprStmt (Assign (LocalOrFieldVar "x" "int") (Binary Add (LocalOrFieldVar "x" "int") (LocalOrFieldVar "delta" "int") "int") "int") "void"
|
||||
, Return (Just (LocalOrFieldVar "x" "int")) "int"
|
||||
]
|
||||
"int"
|
||||
)
|
||||
, Method
|
||||
"int"
|
||||
"sumUpTo"
|
||||
[("int", "n")]
|
||||
( Block
|
||||
[ LocalVarDecl "int" "s" "int"
|
||||
, Block
|
||||
[ LocalVarDecl "int" "i" "int"
|
||||
, While
|
||||
(Binary CompLessOrEqual (LocalOrFieldVar "i" "int") (LocalOrFieldVar "n" "int") "boolean")
|
||||
( Block
|
||||
[ Block
|
||||
[ StmtExprStmt
|
||||
(Assign
|
||||
(LocalOrFieldVar "s" "int")
|
||||
(Binary Add (LocalOrFieldVar "s" "int") (LocalOrFieldVar "i" "int") "int")
|
||||
"int"
|
||||
)
|
||||
"void"
|
||||
]
|
||||
"void"
|
||||
, StmtExprStmt
|
||||
(Assign
|
||||
(LocalOrFieldVar "i" "int")
|
||||
(Binary Add (LocalOrFieldVar "i" "int") (Integer 1 "int") "int")
|
||||
"int"
|
||||
)
|
||||
"void"
|
||||
]
|
||||
"void"
|
||||
)
|
||||
"void"
|
||||
]
|
||||
"void"
|
||||
, Return (Just (LocalOrFieldVar "s" "int")) "int"
|
||||
]
|
||||
"int"
|
||||
)
|
||||
, Method
|
||||
"boolean"
|
||||
"predicate"
|
||||
[("char", "c")]
|
||||
( Block
|
||||
[ Return
|
||||
( Just
|
||||
( Binary
|
||||
Or
|
||||
(Binary CompEqual (LocalOrFieldVar "c" "char") (Char 'a' "char") "boolean")
|
||||
(Binary CompEqual (LocalOrFieldVar "c" "char") (Char 'b' "char") "boolean")
|
||||
"boolean"
|
||||
)
|
||||
)
|
||||
"boolean"
|
||||
]
|
||||
"boolean"
|
||||
)
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,57 @@
|
||||
module Testsuite.TastFiles.ArithmeticTestTAST (expectedTAST) where
|
||||
|
||||
import Grammar.AST (BinaryOperator (..), UnaryOperator (..))
|
||||
import Grammar.TAST
|
||||
|
||||
expectedTAST =
|
||||
[ Class
|
||||
"ArithmeticTest"
|
||||
[]
|
||||
[ Method
|
||||
"int"
|
||||
"basic"
|
||||
[("int", "a"), ("int", "b"), ("int", "c")]
|
||||
( Block
|
||||
[ Return
|
||||
( Just
|
||||
( Binary
|
||||
Subtract
|
||||
(Binary Add (LocalOrFieldVar "a" "int") (LocalOrFieldVar "b" "int") "int")
|
||||
( Binary
|
||||
Modulo
|
||||
( Binary
|
||||
Divide
|
||||
(Binary Multiply (LocalOrFieldVar "c" "int") (LocalOrFieldVar "a" "int") "int")
|
||||
(LocalOrFieldVar "b" "int")
|
||||
"int"
|
||||
)
|
||||
(LocalOrFieldVar "c" "int")
|
||||
"int"
|
||||
)
|
||||
"int"
|
||||
)
|
||||
)
|
||||
"int"
|
||||
]
|
||||
"int"
|
||||
)
|
||||
, Method
|
||||
"boolean"
|
||||
"logic"
|
||||
[("boolean", "a"), ("boolean", "b"), ("boolean", "c")]
|
||||
( Block
|
||||
[ Return
|
||||
( Just
|
||||
( Binary
|
||||
And
|
||||
(Unary Not (LocalOrFieldVar "a" "boolean") "boolean")
|
||||
(Binary Or (LocalOrFieldVar "c" "boolean") (LocalOrFieldVar "b" "boolean") "boolean")
|
||||
"boolean"
|
||||
)
|
||||
)
|
||||
"boolean"
|
||||
]
|
||||
"boolean"
|
||||
)
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,45 @@
|
||||
module Testsuite.TastFiles.CombinedControlTestTAST (expectedTAST) where
|
||||
|
||||
import Grammar.AST (BinaryOperator (..), UnaryOperator (..))
|
||||
import Grammar.TAST
|
||||
|
||||
expectedTAST =
|
||||
[ Class
|
||||
"CombinedControlTest"
|
||||
[ Field "int" "field" Nothing
|
||||
]
|
||||
[ Method
|
||||
"void"
|
||||
"<init>"
|
||||
[("int", "v")]
|
||||
( Block
|
||||
[ StmtExprStmt (Assign (LocalOrFieldVar "field" "int") (LocalOrFieldVar "v" "int") "int") "void"
|
||||
]
|
||||
"void"
|
||||
)
|
||||
, Method
|
||||
"int"
|
||||
"compute"
|
||||
[]
|
||||
( Block
|
||||
[ LocalVarDecl "int" "i" "int"
|
||||
, LocalVarDecl "int" "acc" "int"
|
||||
, While
|
||||
(Binary CompLessThan (LocalOrFieldVar "i" "int") (LocalOrFieldVar "field" "int") "boolean")
|
||||
( Block
|
||||
[ If
|
||||
(Binary CompEqual (Binary Modulo (LocalOrFieldVar "i" "int") (Integer 2 "int") "int") (Integer 0 "int") "boolean")
|
||||
(Block [StmtExprStmt (Assign (LocalOrFieldVar "acc" "int") (Binary Add (LocalOrFieldVar "acc" "int") (LocalOrFieldVar "i" "int") "int") "int") "void"] "void")
|
||||
(Just (Block [StmtExprStmt (Assign (LocalOrFieldVar "acc" "int") (Binary Subtract (LocalOrFieldVar "acc" "int") (LocalOrFieldVar "i" "int") "int") "int") "void"] "void"))
|
||||
"void"
|
||||
, StmtExprStmt (Assign (LocalOrFieldVar "i" "int") (Binary Add (LocalOrFieldVar "i" "int") (Integer 1 "int") "int") "int") "void"
|
||||
]
|
||||
"void"
|
||||
)
|
||||
"void"
|
||||
, Return (Just (LocalOrFieldVar "acc" "int")) "int"
|
||||
]
|
||||
"int"
|
||||
)
|
||||
]
|
||||
]
|
||||
@@ -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") "int") "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 (Unary Negate (Integer 1 "int") "int"))]
|
||||
[ Method
|
||||
"void"
|
||||
"<init>"
|
||||
[("int", "initial_value")]
|
||||
(Block [StmtExprStmt (Assign (LocalOrFieldVar "a" "int") (LocalOrFieldVar "initial_value" "int") "int") "void"] "void")
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,10 @@
|
||||
module Testsuite.TastFiles.EmptyTestTAST (expectedTAST) where
|
||||
|
||||
import Grammar.TAST
|
||||
|
||||
expectedTAST =
|
||||
[ Class
|
||||
"EmptyTest"
|
||||
[]
|
||||
[]
|
||||
]
|
||||
@@ -0,0 +1,31 @@
|
||||
module Testsuite.TastFiles.ExpressionTestTAST (expectedTAST) where
|
||||
|
||||
import Grammar.AST (BinaryOperator (..), UnaryOperator (..))
|
||||
import Grammar.TAST
|
||||
|
||||
expectedTAST =
|
||||
[ Class
|
||||
"ExpressionTest"
|
||||
[]
|
||||
[ Method
|
||||
"boolean"
|
||||
"shortCircuit"
|
||||
[("int", "a"), ("int", "b")]
|
||||
( Block
|
||||
[ LocalVarDecl "boolean" "res" "boolean"
|
||||
, Return (Just (LocalOrFieldVar "res" "boolean")) "boolean"
|
||||
]
|
||||
"boolean"
|
||||
)
|
||||
, Method
|
||||
"char"
|
||||
"charArithmetic"
|
||||
[("char", "c"), ("int", "offset")]
|
||||
( Block
|
||||
[ LocalVarDecl "char" "d" "char"
|
||||
, Return (Just (LocalOrFieldVar "d" "char")) "char"
|
||||
]
|
||||
"char"
|
||||
)
|
||||
]
|
||||
]
|
||||
@@ -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"))
|
||||
]
|
||||
[]
|
||||
]
|
||||
@@ -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,78 @@
|
||||
module Testsuite.TastFiles.LoopTestTAST (expectedTAST) where
|
||||
|
||||
import Grammar.AST (BinaryOperator (..), UnaryOperator (..))
|
||||
import Grammar.TAST
|
||||
|
||||
expectedTAST =
|
||||
[ Class
|
||||
"LoopTest"
|
||||
[]
|
||||
[ Method
|
||||
"int"
|
||||
"factorial"
|
||||
[("int", "n")]
|
||||
( Block
|
||||
[ LocalVarDecl "int" "tally" "int"
|
||||
, Block
|
||||
[ LocalVarDecl "int" "i" "int"
|
||||
, While
|
||||
(Binary CompLessOrEqual (LocalOrFieldVar "i" "int") (LocalOrFieldVar "n" "int") "boolean")
|
||||
( Block
|
||||
[ Block
|
||||
[ StmtExprStmt
|
||||
(Assign
|
||||
(LocalOrFieldVar "tally" "int")
|
||||
(Binary Multiply (LocalOrFieldVar "tally" "int") (LocalOrFieldVar "i" "int") "int")
|
||||
"int"
|
||||
)
|
||||
"void"
|
||||
]
|
||||
"void"
|
||||
, StmtExprStmt
|
||||
(Assign
|
||||
(LocalOrFieldVar "i" "int")
|
||||
(Binary Add (LocalOrFieldVar "i" "int") (Integer 1 "int") "int")
|
||||
"int"
|
||||
)
|
||||
"void"
|
||||
]
|
||||
"void"
|
||||
)
|
||||
"void"
|
||||
]
|
||||
"void"
|
||||
, Return (Just (LocalOrFieldVar "tally" "int")) "int"
|
||||
]
|
||||
"int"
|
||||
)
|
||||
, Method
|
||||
"int"
|
||||
"weirdFor"
|
||||
[]
|
||||
( Block
|
||||
[ LocalVarDecl "int" "k" "int"
|
||||
, Block
|
||||
[ EmptyStmt "void"
|
||||
, While
|
||||
(Binary CompLessThan (LocalOrFieldVar "k" "int") (Integer 5 "int") "boolean")
|
||||
( Block
|
||||
[ Block [] "void"
|
||||
, StmtExprStmt
|
||||
(Assign
|
||||
(LocalOrFieldVar "k" "int")
|
||||
(Binary Add (LocalOrFieldVar "k" "int") (Integer 1 "int") "int")
|
||||
"int"
|
||||
)
|
||||
"void"
|
||||
]
|
||||
"void"
|
||||
)
|
||||
"void"
|
||||
]
|
||||
"void"
|
||||
, Return (Just (LocalOrFieldVar "k" "int")) "int"
|
||||
]
|
||||
"int"
|
||||
)
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,31 @@
|
||||
module Testsuite.TastFiles.MaliciousTestTAST (expectedTAST) where
|
||||
|
||||
import Grammar.AST (BinaryOperator (..), UnaryOperator (..))
|
||||
import Grammar.TAST
|
||||
|
||||
expectedTAST =
|
||||
[ Class
|
||||
"MaliciousTest"
|
||||
[]
|
||||
[ Method
|
||||
"int"
|
||||
"cursedFormatting"
|
||||
[("int", "n")]
|
||||
( Block
|
||||
[ If
|
||||
(Binary CompEqual (LocalOrFieldVar "n" "int") (Integer 0 "int") "boolean")
|
||||
(Block [Return (Just (Integer 0 "int")) "int"] "int")
|
||||
( Just
|
||||
( If
|
||||
(Binary CompEqual (LocalOrFieldVar "n" "int") (Integer 1 "int") "boolean")
|
||||
(Block [Return (Just (Integer 1 "int")) "int"] "int")
|
||||
(Just (Block [Return (Just (Integer 2 "int")) "int"] "int"))
|
||||
"int"
|
||||
)
|
||||
)
|
||||
"int"
|
||||
]
|
||||
"int"
|
||||
)
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,21 @@
|
||||
module Testsuite.TastFiles.MethodOverloadTestTAST (expectedTAST) where
|
||||
|
||||
import Grammar.AST (BinaryOperator (..), UnaryOperator (..))
|
||||
import Grammar.TAST
|
||||
|
||||
expectedTAST =
|
||||
[ Class
|
||||
"MethodOverloadTest"
|
||||
[]
|
||||
[ Method
|
||||
"int"
|
||||
"MethodOverload"
|
||||
[]
|
||||
(Block [Return (Just (Integer 42 "int")) "int"] "int"),
|
||||
Method
|
||||
"int"
|
||||
"MethodOverload"
|
||||
[("int", "a")]
|
||||
(Block [Return (Just (Binary Add (Integer 42 "int") (LocalOrFieldVar "a" "int") "int")) "int"] "int")
|
||||
]
|
||||
]
|
||||
@@ -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") "int") "void"] "void"),
|
||||
Method
|
||||
"int"
|
||||
"doubleIt"
|
||||
[]
|
||||
(Block [Return (Just (Binary Multiply (LocalOrFieldVar "v" "int") (Integer 2 "int") "int")) "int"] "int")
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,17 @@
|
||||
module Testsuite.TastFiles.MultipleClassesTestTAST (expectedTAST) where
|
||||
|
||||
import Grammar.AST (BinaryOperator (..), UnaryOperator (..))
|
||||
import Grammar.TAST
|
||||
|
||||
expectedTAST =
|
||||
[ Class
|
||||
"MultipleClassesTest"
|
||||
[ Field "AnotherTestClass" "a" (Just (StmtExprExpr (New "AnotherTestClass" [] "AnotherTestClass") "AnotherTestClass"))
|
||||
]
|
||||
[]
|
||||
, Class
|
||||
"AnotherTestClass"
|
||||
[ Field "int" "a" (Just (Integer 42 "int"))
|
||||
]
|
||||
[]
|
||||
]
|
||||
@@ -0,0 +1,110 @@
|
||||
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 "null"))
|
||||
]
|
||||
[ Method
|
||||
"void"
|
||||
"<init>"
|
||||
[("int", "n")]
|
||||
( Block
|
||||
[ StmtExprStmt (Assign (InstVar (This "RecursionTest") "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")
|
||||
(Block [Return (Just (LocalOrFieldVar "n" "int")) "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"
|
||||
]
|
||||
"int"
|
||||
)
|
||||
)
|
||||
"int"
|
||||
]
|
||||
"int"
|
||||
)
|
||||
, 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
|
||||
"int"
|
||||
, 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
|
||||
"int"
|
||||
, 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"
|
||||
)
|
||||
]
|
||||
]
|
||||
@@ -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,39 @@
|
||||
module Testsuite.TastFiles.ShenaniganceTestTAST (expectedTAST) where
|
||||
|
||||
import Grammar.AST (BinaryOperator (..), UnaryOperator (..))
|
||||
import Grammar.TAST
|
||||
|
||||
expectedTAST =
|
||||
[ Class
|
||||
"ShenaniganceTest"
|
||||
[]
|
||||
[ Method
|
||||
"int"
|
||||
"testAssignment"
|
||||
[]
|
||||
( Block
|
||||
[ LocalVarDecl "int" "x" "int"
|
||||
, LocalVarDecl "int" "y" "int"
|
||||
, Return (Just (LocalOrFieldVar "y" "int")) "int"
|
||||
]
|
||||
"int"
|
||||
)
|
||||
, Method
|
||||
"int"
|
||||
"divEqual"
|
||||
[]
|
||||
( Block
|
||||
[ LocalVarDecl "int" "x" "int"
|
||||
, StmtExprStmt
|
||||
(Assign
|
||||
(LocalOrFieldVar "x" "int")
|
||||
(Binary Divide (LocalOrFieldVar "x" "int") (Integer 4 "int") "int")
|
||||
"int"
|
||||
)
|
||||
"void"
|
||||
, Return (Just (LocalOrFieldVar "x" "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 "null") "boolean")
|
||||
(Block [StmtExprStmt (Assign (LocalOrFieldVar "instance" "SingletonTest") (StmtExprExpr (New "SingletonTest" [] "SingletonTest") "SingletonTest") "SingletonTest") "void"] "void")
|
||||
Nothing
|
||||
"void"
|
||||
, Return (Just (LocalOrFieldVar "instance" "SingletonTest")) "SingletonTest"
|
||||
]
|
||||
"SingletonTest"
|
||||
)
|
||||
]
|
||||
]
|
||||
@@ -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"
|
||||
)
|
||||
"void"
|
||||
, StmtExprStmt
|
||||
(Assign
|
||||
(LocalOrFieldVar "n" "int")
|
||||
(Binary Subtract (LocalOrFieldVar "n" "int") (Integer 1 "int") "int")
|
||||
"int"
|
||||
)
|
||||
"void"
|
||||
]
|
||||
"void"
|
||||
)
|
||||
"void"
|
||||
, 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.
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.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user