Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 22de50b77e | |||
| e84550025c | |||
| 8470ab2d94 | |||
| 1ad8aface9 | |||
| daa741a090 | |||
| e81ceb454f | |||
| 87db9f6e23 | |||
| a447fad905 | |||
| b964bdda1b | |||
| 2f908d1df1 | |||
| 40e84e45f6 | |||
| 56d43bac38 | |||
| 9230042b25 | |||
| 76c4cb677c |
@@ -31,3 +31,4 @@ cabal.project.local~
|
||||
hie.yaml
|
||||
src/Grammar/Scanner.hs
|
||||
src/Grammar/Parser.hs
|
||||
src/Grammar/Parser.info
|
||||
|
||||
+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,130 @@
|
||||
module Codegen.ClassFile where
|
||||
|
||||
import Data.Word (Word16, Word32, Word8)
|
||||
|
||||
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 CP_Infos = [CP_Info]
|
||||
|
||||
type Interfaces = [Word16] -- constant pool indices
|
||||
|
||||
type Field_Infos = [Field_Info]
|
||||
|
||||
type Method_Infos = [Method_Info]
|
||||
|
||||
type Attribute_Infos = [Attribute_Info]
|
||||
|
||||
type Index = Int -- constant pool index (1-based)
|
||||
|
||||
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)
|
||||
|
||||
-- Duplicate field names fixed by prefixing per constructor
|
||||
data CP_Info
|
||||
= Class_Info
|
||||
{ci_nameIndex :: Index}
|
||||
| FieldRef_Info
|
||||
{ fr_classIndex :: Index,
|
||||
fr_nameAndTypeIndex :: Index
|
||||
}
|
||||
| MethodRef_Info
|
||||
{ mr_classIndex :: Index,
|
||||
mr_nameAndTypeIndex :: Index
|
||||
}
|
||||
| InterfaceMethodRef_Info
|
||||
{ imr_classIndex :: Index,
|
||||
imr_nameAndTypeIndex :: Index
|
||||
}
|
||||
| String_Info
|
||||
{si_stringIndex :: Index}
|
||||
| Integer_Info
|
||||
{ii_value :: Int}
|
||||
| Float_Info
|
||||
{fi_value :: Float}
|
||||
| Long_Info
|
||||
{li_value :: Integer} -- Long fits in Haskell Integer
|
||||
| Double_Info
|
||||
{di_value :: Double}
|
||||
| NameAndType_Info
|
||||
{ nt_nameIndex :: Index,
|
||||
nt_descriptorIndex :: Index
|
||||
}
|
||||
| Utf8_Info
|
||||
{u8_value :: String}
|
||||
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)
|
||||
@@ -0,0 +1,54 @@
|
||||
module ConstantPool where
|
||||
data Index_Constant_Pool
|
||||
|
||||
data CPInfo =
|
||||
ClassInfo
|
||||
{ tag_cp :: Tag
|
||||
, index_cp :: Index_Constant_Pool
|
||||
, desc :: String
|
||||
}
|
||||
| FieldRefInfo
|
||||
{ tag_cp :: Tag
|
||||
, index_name_cp :: Index_Constant_Pool
|
||||
, index_nameandtype_cp :: Index_Constant_Pool
|
||||
, desc :: String
|
||||
}
|
||||
| MethodRefInfo
|
||||
{ tag_cp :: Tag
|
||||
, index_name_cp :: Index_Constant_Pool
|
||||
, index_nameandtype_cp :: Index_Constant_Pool
|
||||
, desc :: String
|
||||
}
|
||||
| StringInfo
|
||||
{ tag_cp :: Tag
|
||||
, index_cp :: Index_Constant_Pool
|
||||
, desc :: String
|
||||
}
|
||||
| IntegerInfo
|
||||
{ tag_cp :: Tag
|
||||
, numi_cp :: Int
|
||||
, desc :: String
|
||||
}
|
||||
| NameAndTypeInfo
|
||||
{ tag_cp :: Tag
|
||||
, index_name_cp :: Index_Constant_Pool
|
||||
, index_descr_cp :: Index_Constant_Pool
|
||||
, desc :: String
|
||||
}
|
||||
| Utf8Info
|
||||
{ tag_cp :: Tag
|
||||
, tam_cp :: Int
|
||||
, cad_cp :: String
|
||||
, desc :: String
|
||||
}
|
||||
deriving ()
|
||||
|
||||
data Tag =
|
||||
TagClass
|
||||
| TagFieldRef
|
||||
| TagMethodRef
|
||||
| TagString
|
||||
| TagInteger
|
||||
| TagNameAndType
|
||||
| TagUtf8
|
||||
deriving (Eq, Ord, Show)
|
||||
@@ -0,0 +1,138 @@
|
||||
module Codegen.Lowerer where
|
||||
|
||||
import Codegen.ClassFile
|
||||
import Data.Word (Word8)
|
||||
import Numeric (showHex)
|
||||
|
||||
data Instruction
|
||||
= ALoad Int
|
||||
| ALoad0
|
||||
| ILoad Int
|
||||
| AStore Int
|
||||
| IStore Int
|
||||
| GetField Int
|
||||
| PutField Int
|
||||
| InvokeSpecial Int
|
||||
| InvokeVirtual Int
|
||||
| InvokeStatic Int
|
||||
| IConst Int
|
||||
| AConstNull
|
||||
| Ldc Int
|
||||
| IAdd
|
||||
| ISub
|
||||
| IMul
|
||||
| IfEq Int
|
||||
| IfNe Int
|
||||
| Goto Int
|
||||
| Return
|
||||
| IReturn
|
||||
| AReturn
|
||||
deriving (Show, Eq)
|
||||
|
||||
data BtcLine = BtcLine
|
||||
{ lineNumber :: Integer,
|
||||
instruction :: Instruction
|
||||
}
|
||||
deriving (Show, Eq)
|
||||
|
||||
newtype BtcProgram = BtcProgram [BtcLine]
|
||||
deriving (Show, Eq)
|
||||
|
||||
-- Lower a full ClassFile into a BtcProgram per method
|
||||
-- Returns one BtcProgram per method, paired with its name
|
||||
lowerClassFile :: ClassFile -> [(String, BtcProgram)]
|
||||
lowerClassFile cf =
|
||||
[ (methodName cf m, lowerMethod m)
|
||||
| m <- methods cf
|
||||
]
|
||||
|
||||
-- Extract method name from the constant pool
|
||||
methodName :: ClassFile -> Method_Info -> String
|
||||
methodName cf m =
|
||||
case lookupPool (constantPool cf) (methodNameIndex m) of
|
||||
Just (Utf8_Info name) -> name
|
||||
_ -> "<unknown>"
|
||||
|
||||
-- Lower a single method's Code attribute into a BtcProgram
|
||||
lowerMethod :: Method_Info -> BtcProgram
|
||||
lowerMethod m =
|
||||
case findCodeAttr (methodAttributes m) of
|
||||
Just attr -> lowerCode attr
|
||||
Nothing -> BtcProgram [] -- abstract/native method, no body
|
||||
|
||||
findCodeAttr :: Attribute_Infos -> Maybe Attribute_Info
|
||||
findCodeAttr = foldr step Nothing
|
||||
where
|
||||
step attr@Code_Attribute {} _ = Just attr
|
||||
step _ acc = acc
|
||||
|
||||
-- Reconstruct BtcProgram from raw code bytes
|
||||
-- This is the tricky direction — bytes → instructions
|
||||
lowerCode :: Attribute_Info -> BtcProgram
|
||||
lowerCode attr = BtcProgram (decodeBytes 0 (codeBody attr))
|
||||
|
||||
decodeBytes :: Int -> [Word8] -> [BtcLine]
|
||||
decodeBytes _ [] = []
|
||||
decodeBytes pos (b : bs) = case b of
|
||||
0x2A -> line ALoad0 pos 1 bs
|
||||
0x2B -> line (ALoad 1) pos 1 bs
|
||||
0x2C -> line (ALoad 2) pos 1 bs
|
||||
0x2D -> line (ALoad 3) pos 1 bs
|
||||
0x19 -> withByte bs pos $ \n rest -> line (ALoad n) pos 2 rest
|
||||
0x1A -> line (ILoad 0) pos 1 bs
|
||||
0x1B -> line (ILoad 1) pos 1 bs
|
||||
0x1C -> line (ILoad 2) pos 1 bs
|
||||
0x1D -> line (ILoad 3) pos 1 bs
|
||||
0x15 -> withByte bs pos $ \n rest -> line (ILoad n) pos 2 rest
|
||||
0x4B -> line (AStore 0) pos 1 bs
|
||||
0x4C -> line (AStore 1) pos 1 bs
|
||||
0x4D -> line (AStore 2) pos 1 bs
|
||||
0x4E -> line (AStore 3) pos 1 bs
|
||||
0x3A -> withByte bs pos $ \n rest -> line (AStore n) pos 2 rest
|
||||
0x3B -> line (IStore 0) pos 1 bs
|
||||
0x3C -> line (IStore 1) pos 1 bs
|
||||
0x3D -> line (IStore 2) pos 1 bs
|
||||
0x3E -> line (IStore 3) pos 1 bs
|
||||
0x36 -> withByte bs pos $ \n rest -> line (IStore n) pos 2 rest
|
||||
0xB4 -> withIndex bs pos $ \i rest -> line (GetField i) pos 3 rest
|
||||
0xB5 -> withIndex bs pos $ \i rest -> line (PutField i) pos 3 rest
|
||||
0xB7 -> withIndex bs pos $ \i rest -> line (InvokeSpecial i) pos 3 rest
|
||||
0xB6 -> withIndex bs pos $ \i rest -> line (InvokeVirtual i) pos 3 rest
|
||||
0xB8 -> withIndex bs pos $ \i rest -> line (InvokeStatic i) pos 3 rest
|
||||
0x02 -> line (IConst (-1)) pos 1 bs
|
||||
0x03 -> line (IConst 0) pos 1 bs
|
||||
0x04 -> line (IConst 1) pos 1 bs
|
||||
0x05 -> line (IConst 2) pos 1 bs
|
||||
0x06 -> line (IConst 3) pos 1 bs
|
||||
0x07 -> line (IConst 4) pos 1 bs
|
||||
0x08 -> line (IConst 5) pos 1 bs
|
||||
0x01 -> line AConstNull pos 1 bs
|
||||
0x12 -> withByte bs pos $ \i rest -> line (Ldc i) pos 2 rest
|
||||
0x13 -> withIndex bs pos $ \i rest -> line (Ldc i) pos 3 rest
|
||||
0x60 -> line IAdd pos 1 bs
|
||||
0x64 -> line ISub pos 1 bs
|
||||
0x68 -> line IMul pos 1 bs
|
||||
0x99 -> withIndex bs pos $ \i rest -> line (IfEq i) pos 3 rest
|
||||
0x9A -> withIndex bs pos $ \i rest -> line (IfNe i) pos 3 rest
|
||||
0xA7 -> withIndex bs pos $ \i rest -> line (Goto i) pos 3 rest
|
||||
0xB1 -> line Return pos 1 bs
|
||||
0xAC -> line IReturn pos 1 bs
|
||||
0xB0 -> line AReturn pos 1 bs
|
||||
unknown -> error $ "Unknown opcode 0x" ++ showHex unknown "" ++ " at position " ++ show pos
|
||||
|
||||
-- Helpers
|
||||
line :: Instruction -> Int -> Int -> [Word8] -> [BtcLine]
|
||||
line instr pos size rest = BtcLine (toInteger pos) instr : decodeBytes (pos + size) rest
|
||||
|
||||
withByte :: [Word8] -> Int -> (Int -> [Word8] -> [BtcLine]) -> [BtcLine]
|
||||
withByte (b : rest) _ f = f (fromIntegral b) rest
|
||||
withByte [] pos _ = error $ "Unexpected end of bytecode at " ++ show pos
|
||||
|
||||
withIndex :: [Word8] -> Int -> (Int -> [Word8] -> [BtcLine]) -> [BtcLine]
|
||||
withIndex (hi : lo : rest) _ f = f (fromIntegral hi * 256 + fromIntegral lo) rest
|
||||
withIndex _ pos _ = error $ "Unexpected end of bytecode at " ++ show pos
|
||||
|
||||
lookupPool :: CP_Infos -> Index -> Maybe CP_Info
|
||||
lookupPool pool i
|
||||
| i < 1 || i > length pool = Nothing
|
||||
| otherwise = Just (pool !! (i - 1)) -- pool is 1-indexed
|
||||
@@ -0,0 +1,79 @@
|
||||
module Codegen.Serializer where
|
||||
|
||||
import Data.Bits (shiftR, (.&.))
|
||||
import Data.Word (Word8)
|
||||
|
||||
-- Split a 16-bit Int into two bytes (big-endian), as JVM expects
|
||||
indexBytes :: Int -> [Word8]
|
||||
indexBytes n =
|
||||
[ fromIntegral (n `shiftR` 8 .&. 0xFF),
|
||||
fromIntegral (n .&. 0xFF)
|
||||
]
|
||||
|
||||
serializeInstruction :: Instruction -> [Word8]
|
||||
serializeInstruction instr = case instr of
|
||||
-- aload_<n>: 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
|
||||
AConstNull -> [0x01]
|
||||
-- ldc: 1-byte index (use ldc_w 0x13 for large pool indices > 255)
|
||||
Ldc i
|
||||
| i <= 255 -> [0x12, fromIntegral i]
|
||||
| otherwise -> 0x13 : indexBytes i -- ldc_w
|
||||
|
||||
-- Arithmetic
|
||||
IAdd -> [0x60]
|
||||
ISub -> [0x64]
|
||||
IMul -> [0x68]
|
||||
-- Control flow: 2-byte signed branch offset
|
||||
IfEq offset -> 0x99 : indexBytes offset
|
||||
IfNe offset -> 0x9A : indexBytes offset
|
||||
Goto offset -> 0xA7 : indexBytes offset
|
||||
-- Return
|
||||
Return -> [0xB1]
|
||||
IReturn -> [0xAC]
|
||||
AReturn -> [0xB0]
|
||||
|
||||
-- Serialize a full program — concatenate all instruction bytes
|
||||
serializeProgram :: BtcProgram -> [Word8]
|
||||
serializeProgram (BtcProgram lines) =
|
||||
concatMap (serializeInstruction . instruction) lines
|
||||
+1
-1
@@ -19,7 +19,7 @@ data Expr
|
||||
| Integer Integer
|
||||
| Bool Bool
|
||||
| Char Char
|
||||
| Jnull
|
||||
| Null
|
||||
| StmtExprExpr StmtExpr
|
||||
deriving (Show, Eq)
|
||||
|
||||
|
||||
+26
-8
@@ -1,13 +1,31 @@
|
||||
module Grammar.TAST where
|
||||
|
||||
import Grammar.AST
|
||||
import Grammar.AST (BinaryOperator, Type, UnaryOperator)
|
||||
|
||||
data TypedExpr = TypedExpr Expr 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 TypedStmtExpr = TypedStmtExpr StmtExpr Type
|
||||
data TypedStmtExpr
|
||||
= Assign TypedExpr TypedExpr Type
|
||||
| New Type [TypedExpr] Type
|
||||
| MethodCall TypedExpr String [TypedExpr] Type
|
||||
deriving (Show, Eq)
|
||||
|
||||
data TypedStmt = TypedStmt Stmt Type
|
||||
|
||||
data TypedBinaryOperator = TypedBinaryOperator BinaryOperator Type
|
||||
|
||||
data TypedUnaryOperator = TypedUnaryOperator UnaryOperator Type
|
||||
data TypedStmt
|
||||
= Block [TypedStmt] Type
|
||||
| Return (Maybe TypedExpr) Type
|
||||
| While TypedExpr TypedStmt Type
|
||||
| LocalVarDecl Type String Type
|
||||
| If TypedExpr TypedStmt (Maybe TypedStmt) Type
|
||||
| StmtExprStmt TypedStmtExpr Type
|
||||
deriving (Show, Eq)
|
||||
|
||||
@@ -1,46 +1,57 @@
|
||||
module Typecheck.SemanticChecker where
|
||||
|
||||
import Grammar.AST
|
||||
import Grammar.TAST
|
||||
import Grammar.AST as AST
|
||||
import Grammar.TAST as TAST
|
||||
|
||||
--------------------------------------------------
|
||||
-- Helper functions
|
||||
--------------------------------------------------
|
||||
getExpr :: TypedExpr -> Expr
|
||||
getExpr (TypedExpr e _) = e
|
||||
getTypeFromTypedExpr :: TypedExpr -> Type
|
||||
getTypeFromTypedExpr (TAST.This t) = t
|
||||
getTypeFromTypedExpr (TAST.LocalOrFieldVar _ t) = t
|
||||
getTypeFromTypedExpr (TAST.InstVar _ _ t) = t
|
||||
getTypeFromTypedExpr (TAST.Unary _ _ t) = t
|
||||
getTypeFromTypedExpr (TAST.Binary _ _ _ t) = t
|
||||
getTypeFromTypedExpr (TAST.Integer _ t) = t
|
||||
getTypeFromTypedExpr (TAST.Bool _ t) = t
|
||||
getTypeFromTypedExpr (TAST.Char _ t) = t
|
||||
getTypeFromTypedExpr (TAST.Null t) = t
|
||||
getTypeFromTypedExpr (TAST.StmtExprExpr _ t) = t
|
||||
|
||||
getStmt :: TypedStmt -> Stmt
|
||||
getStmt (TypedStmt s _) = s
|
||||
|
||||
getTypeFromExpr :: TypedExpr -> Type
|
||||
getTypeFromExpr (TypedExpr _ typ) = typ
|
||||
getTypeFromTypedStmtExpr :: TypedStmtExpr -> Type
|
||||
getTypeFromTypedStmtExpr (TAST.Assign _ _ t) = t
|
||||
getTypeFromTypedStmtExpr (TAST.New _ _ t) = t
|
||||
getTypeFromTypedStmtExpr (TAST.MethodCall _ _ _ t) = t
|
||||
|
||||
getTypeFromTypedStmt :: TypedStmt -> Type
|
||||
getTypeFromTypedStmt (TAST.Block _ t) = t
|
||||
getTypeFromTypedStmt (TAST.Return _ t) = t
|
||||
getTypeFromTypedStmt (TAST.While _ _ t) = t
|
||||
getTypeFromTypedStmt (TAST.LocalVarDecl _ _ t) = t
|
||||
getTypeFromTypedStmt (TAST.If _ _ _ t) = t
|
||||
getTypeFromTypedStmt (TAST.StmtExprStmt _ t) = t
|
||||
|
||||
getTypeFromStmt :: TypedStmt -> Type
|
||||
getTypeFromStmt (TypedStmt _ typ) = typ
|
||||
|
||||
upperBound :: Type -> Type -> Type
|
||||
upperBound t1 t2
|
||||
| t1 == t2 = t1
|
||||
| otherwise = "Object" -- // TODO: implement proper class hierarchy and find the least common ancestor of t1 and t2 in that hierarchy
|
||||
| otherwise = "Object" -- // Or: Throw an error because types don't match
|
||||
|
||||
--------------------------------------------------
|
||||
-- Statement Typechecking
|
||||
--------------------------------------------------
|
||||
typeCheckStmt :: Stmt -> [(String, Type)] -> [Class] -> TypedStmt
|
||||
-- If Statement
|
||||
typeCheckStmt (If cond body Nothing) symtab cls =
|
||||
let checkedCond = typeCheckExpr cond symtab cls
|
||||
checkedBody = typeCheckStmt body symtab cls
|
||||
in if getTypeFromExpr checkedCond == "boolean"
|
||||
then TypedStmt (If checkedCond checkedBody Nothing) (getTypeFromStmt checkedBody)
|
||||
else error "Condition in if statement must be of type boolean"
|
||||
-- If-Else Statement
|
||||
typeCheckStmt (If cond body (Just elseBranch)) symtab cls =
|
||||
typeCheckStmt (AST.If cond body elseBranch) symtab cls =
|
||||
let checkedCond = typeCheckExpr cond symtab cls
|
||||
checkedBody = typeCheckStmt body symtab cls
|
||||
checkedElse = fmap (\e -> typeCheckStmt e symtab cls) elseBranch
|
||||
in if getTypeFromExpr checkedCond == "boolean"
|
||||
then TypedStmt (If checkedCond checkedBody checkedElse) (upperBound (getTypeFromStmt checkedBody) (getTypeFromStmt checkedElse))
|
||||
typ = case checkedElse of
|
||||
Nothing -> getTypeFromTypedStmt checkedBody
|
||||
Just ce -> upperBound (getTypeFromTypedStmt checkedBody) (getTypeFromTypedStmt ce)
|
||||
in if getTypeFromTypedExpr checkedCond == "boolean"
|
||||
then TAST.If checkedCond checkedBody checkedElse typ
|
||||
else error "Condition in if statement must be of type boolean"
|
||||
|
||||
--------------------------------------------------
|
||||
@@ -48,13 +59,13 @@ typeCheckStmt (If cond body (Just elseBranch)) symtab cls =
|
||||
--------------------------------------------------
|
||||
typeCheckExpr :: Expr -> [(String, Type)] -> [Class] -> TypedExpr
|
||||
-- boolean literals
|
||||
typeCheckExpr (Bool boolean) symtbl cls =
|
||||
TypedExpr (Bool boolean) "boolean"
|
||||
typeCheckExpr (AST.Bool value) symtbl cls =
|
||||
TAST.Bool value "boolean"
|
||||
-- integer literals
|
||||
typeCheckExpr (Integer integer) symtbl cls =
|
||||
TypedExpr (Integer integer) "int"
|
||||
typeCheckExpr (AST.Integer value) symtbl cls =
|
||||
TAST.Integer value "int"
|
||||
-- variable references
|
||||
typeCheckExpr (LocalOrFieldVar varName) symtbl cls =
|
||||
typeCheckExpr (AST.LocalOrFieldVar varName) symtbl cls =
|
||||
case lookup varName symtbl of
|
||||
Just t -> TypedExpr (LocalOrFieldVar varName) t
|
||||
Just t -> TAST.LocalOrFieldVar varName t
|
||||
Nothing -> error $ "Undefined variable: " ++ varName
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
public class AllSyntaxTest {
|
||||
public int x;
|
||||
private static int counter = 0;
|
||||
|
||||
public AllSyntaxTest(int initial) {
|
||||
x = initial;
|
||||
counter = counter + 1;
|
||||
}
|
||||
|
||||
public int inc() {
|
||||
x = x + 1;
|
||||
return x;
|
||||
}
|
||||
|
||||
public int inc(int delta) {
|
||||
x = x + delta;
|
||||
return x;
|
||||
}
|
||||
|
||||
public static int sumUpTo(int n) {
|
||||
int s = 0;
|
||||
for (int i = 1; i <= n; i = i + 1) {
|
||||
s = s + i;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public boolean predicate(char c) {
|
||||
return c == 'a' || c == 'b';
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
AllSyntaxTest a = new AllSyntaxTest(0);
|
||||
System.out.println(a.inc()); // 1
|
||||
System.out.println(a.inc(4)); // 5
|
||||
System.out.println(AllSyntaxTest.sumUpTo(5)); // 15
|
||||
System.out.println(a.predicate('a') ? 1 : 0); // ternary used to produce int
|
||||
System.out.println(counter); // static field access
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
public class ArithmeticTest {
|
||||
public int basic(int a, int b, int c)
|
||||
{
|
||||
return a + b - c * a / b % c;
|
||||
}
|
||||
|
||||
public boolean logic(boolean a, boolean b, boolean c)
|
||||
{
|
||||
return !a && (c || b);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
module Main where
|
||||
|
||||
import Codegen.Assemble (assembleProgram)
|
||||
import Codegen.ClassFile (ClassFile(..), ClassFileMethod(..))
|
||||
import Codegen.Errors (CodegenError(..))
|
||||
import Codegen.IR
|
||||
( ClassDef(..)
|
||||
, Expr(..)
|
||||
, Literal(..)
|
||||
, MethodDef(..)
|
||||
, Program(..)
|
||||
, Stmt(..)
|
||||
, Type(..)
|
||||
)
|
||||
import Data.List (isInfixOf)
|
||||
import System.Exit (exitFailure)
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
testVerticalSliceReturnInt
|
||||
testUnsupportedStatementFails
|
||||
putStrLn "Codegen smoke tests passed."
|
||||
|
||||
testVerticalSliceReturnInt :: IO ()
|
||||
testVerticalSliceReturnInt =
|
||||
case assembleProgram program of
|
||||
Left err -> failTest ("Expected successful assemble, got error: " ++ show err)
|
||||
Right [classFile] -> do
|
||||
assertEqual "class name" "Main" (cfClassName classFile)
|
||||
case cfMethods classFile of
|
||||
[method] -> do
|
||||
assertEqual "method name" "constSeven" (cfmName method)
|
||||
assertEqual "method descriptor" "()I" (cfmDescriptor method)
|
||||
assertEqual "method instructions" ["iconst 7", "ireturn"] (cfmCode method)
|
||||
_ -> failTest "Expected exactly one method in generated class"
|
||||
Right _ -> failTest "Expected exactly one generated class"
|
||||
where
|
||||
program =
|
||||
Program
|
||||
[ ClassDef
|
||||
{ className = "Main"
|
||||
, classFields = []
|
||||
, classMethods =
|
||||
[ MethodDef
|
||||
{ methodName = "constSeven"
|
||||
, methodParams = []
|
||||
, methodReturnType = IntType
|
||||
, methodBody = Return (Literal (IntLit 7))
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
testUnsupportedStatementFails :: IO ()
|
||||
testUnsupportedStatementFails =
|
||||
case assembleProgram program of
|
||||
Left (LoweringError msg)
|
||||
| "Unsupported statement" `isInfixOf` msg -> pure ()
|
||||
| otherwise -> failTest ("Lowering failed with unexpected message: " ++ msg)
|
||||
Left err -> failTest ("Expected LoweringError, got: " ++ show err)
|
||||
Right _ -> failTest "Expected lowering to fail for unsupported statement"
|
||||
where
|
||||
program =
|
||||
Program
|
||||
[ ClassDef
|
||||
{ className = "Main"
|
||||
, classFields = []
|
||||
, classMethods =
|
||||
[ MethodDef
|
||||
{ methodName = "badMethod"
|
||||
, methodParams = []
|
||||
, methodReturnType = IntType
|
||||
, methodBody = While (Literal (BoolLit True)) (Return (Literal (IntLit 0)))
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
assertEqual :: (Eq a, Show a) => String -> a -> a -> IO ()
|
||||
assertEqual label expected actual
|
||||
| expected == actual = pure ()
|
||||
| otherwise =
|
||||
failTest
|
||||
( "Assertion failed for "
|
||||
++ label
|
||||
++ ": expected "
|
||||
++ show expected
|
||||
++ ", got "
|
||||
++ show actual
|
||||
)
|
||||
|
||||
failTest :: String -> IO ()
|
||||
failTest message = do
|
||||
putStrLn ("[FAIL] " ++ message)
|
||||
exitFailure
|
||||
@@ -0,0 +1,27 @@
|
||||
public class CombinedControlTest {
|
||||
int field;
|
||||
|
||||
public CombinedControlTest(int v) {
|
||||
field = v;
|
||||
}
|
||||
|
||||
public int compute() {
|
||||
int i = 0;
|
||||
int acc = 0;
|
||||
while (i < field) {
|
||||
if (i % 2 == 0) {
|
||||
acc = acc + i;
|
||||
} else {
|
||||
acc = acc - i;
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
CombinedControlTest t = new CombinedControlTest(6);
|
||||
// Computation: 0 -1 +2 -3 +4 -5 = -3
|
||||
System.out.println(t.compute());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
public class ConstructorOverloadTest {
|
||||
|
||||
public int a = 42;
|
||||
|
||||
ConstructorOverloadTest() {
|
||||
// nothing here, so a will assume the default value 42.
|
||||
}
|
||||
|
||||
ConstructorOverloadTest(int a) {
|
||||
this.a = a;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
public class ConstructorTest
|
||||
{
|
||||
public int a = -1;
|
||||
|
||||
public ConstructorTest(int initial_value)
|
||||
{
|
||||
a = initial_value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
public class EmptyTest
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
public class ExpressionTest {
|
||||
public static boolean shortCircuit(int a, int b) {
|
||||
// short-circuit: when a==0 the right side must not be evaluated
|
||||
boolean res = (a != 0) && ((10 / a) > b);
|
||||
return res;
|
||||
}
|
||||
|
||||
public static char charArithmetic(char c, int offset) {
|
||||
char d = (char)(c + offset);
|
||||
return d;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(shortCircuit(2, 1)); // true
|
||||
System.out.println(shortCircuit(0, 1)); // false (right side not evaluated)
|
||||
System.out.println(charArithmetic('A', 2)); // 'C'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
public class FieldsTest
|
||||
{
|
||||
public int a;
|
||||
public int b = 42;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
public class IfTest {
|
||||
public static boolean ifElseTest(int x) {
|
||||
if (x < 0) {
|
||||
return false;
|
||||
} else if (x == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return x > 10;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(ifElseTest(-1)); // false
|
||||
System.out.println(ifElseTest(0)); // true
|
||||
System.out.println(ifElseTest(11)); // true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
public class LoopTest {
|
||||
public int factorial(int n)
|
||||
{
|
||||
int tally = 1;
|
||||
for(int i = 1; i <= n; i++)
|
||||
{
|
||||
tally *= i;
|
||||
}
|
||||
|
||||
return tally;
|
||||
}
|
||||
|
||||
int weirdFor() {
|
||||
int k = 0;
|
||||
for (; k < 5; k++) {
|
||||
}
|
||||
return k;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// compile all test files using:
|
||||
// ls Test/JavaSources/*.java | grep -v ".*Main.java" | xargs -I {} cabal run compiler {}
|
||||
// compile (in project root) using:
|
||||
// pushd Test/JavaSources; javac -g:none Main.java; popd
|
||||
// afterwards, run using
|
||||
// java -ea -cp Test/JavaSources/ Main
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args)
|
||||
{
|
||||
EmptyTest empty = new EmptyTest();
|
||||
FieldsTest fields = new FieldsTest();
|
||||
ConstructorTest constructor = new ConstructorTest(42);
|
||||
ArithmeticTest arithmetic = new ArithmeticTest();
|
||||
MultipleClassesTest multipleClasses = new MultipleClassesTest();
|
||||
RecursionTest recursion = new RecursionTest(10);
|
||||
MaliciousTest malicious = new MaliciousTest();
|
||||
LoopTest loop = new LoopTest();
|
||||
MethodOverloadTest overload = new MethodOverloadTest();
|
||||
ShenaniganceTest shenanigance = new ShenaniganceTest();
|
||||
|
||||
// constructing a basic class works
|
||||
System.out.println("test empty non-null. Expected: non-null, Real: " + (empty != null));
|
||||
// initializers (and default initializers to 0/null) work
|
||||
System.out.println("test fields initializers. Expected: a==0 and b==42, Real: a=" + fields.a + " and b=" + fields.b);
|
||||
// constructor parameters override initializers
|
||||
System.out.println("test constructor a. Expected result: 42, Real result: " + constructor.a);
|
||||
// basic arithmetics
|
||||
System.out.println("test arithmetic basic(1,2,3). Expected: 2, Real: " + arithmetic.basic(1, 2, 3));
|
||||
// we have boolean logic as well
|
||||
System.out.println("test arithmetic logic(false,false,true). Expected: true, Real: " + arithmetic.logic(false, false, true));
|
||||
// multiple classes within one file work. Referencing another classes fields/methods works.
|
||||
System.out.println("test multiple classes field. Expected: 42, Real: " + multipleClasses.a.a);
|
||||
// self-referencing classes work.
|
||||
System.out.println("test nested child value. Expected: 5, Real: " + recursion.child.child.child.child.child.value);
|
||||
// self-referencing methods work.
|
||||
System.out.println("test recursion fibonacci(15). Expected: 610, Real: " + recursion.fibonacci(15));
|
||||
System.out.println("test factorial(5). Expected: 120, Real: " + loop.factorial(5));
|
||||
System.out.println("test weirdFor(). Expected: 5, Real: " + loop.weirdFor());
|
||||
// methods with the same name but different parameters work
|
||||
System.out.println("test MethodOverload(). Expected: 42, Real: " + overload.MethodOverload());
|
||||
System.out.println("test MethodOverload(15). Expected: 42+15, Real: " + overload.MethodOverload(15));
|
||||
// constructor overloading works, too.
|
||||
System.out.println("test ctor overload default. Expected: 42, Real: " + (new ConstructorOverloadTest()).a);
|
||||
System.out.println("test ctor overload with arg. Expected: 12, Real: " + (new ConstructorOverloadTest(12)).a);
|
||||
// intentionally dodgy expressions work
|
||||
System.out.println("test assignNegativeIncrement(42). Expected: -42, Real: " + malicious.assignNegativeIncrement(42));
|
||||
System.out.println("test tripleAddition(1,2,3). Expected: 6, Real: " + malicious.tripleAddition(1, 2, 3));
|
||||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
System.out.println("test cursedFormatting i=" + i + ". Expected: " + i + ", Real: " + malicious.cursedFormatting(i));
|
||||
}
|
||||
// other syntactic sugar
|
||||
System.out.println("test shenanigance.testAssignment(). Expected: 5, Real: " + shenanigance.testAssignment());
|
||||
System.out.println("test shenanigance.divEqual(). Expected: " + (234_343_000 / 4) + ", Real: " + shenanigance.divEqual());
|
||||
System.out.println("test shenanigance.testIf(5). Expected: true, Real: " + shenanigance.testIf(5));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
public class MaliciousTest {
|
||||
public int assignNegativeIncrement(int n)
|
||||
{
|
||||
return n=-++n+1;
|
||||
}
|
||||
|
||||
public int tripleAddition(int a, int b, int c)
|
||||
{
|
||||
return a+++b+++c++;
|
||||
}
|
||||
|
||||
public int cursedFormatting(int n)
|
||||
{
|
||||
if
|
||||
|
||||
|
||||
(n == 0)
|
||||
|
||||
{
|
||||
|
||||
return ((((0))));
|
||||
}
|
||||
|
||||
else
|
||||
|
||||
|
||||
if(n ==
|
||||
|
||||
1)
|
||||
{
|
||||
return
|
||||
|
||||
|
||||
1;
|
||||
}else {
|
||||
return
|
||||
2
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
public class MethodOverloadTest {
|
||||
|
||||
public int MethodOverload() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
public int MethodOverload(int a) {
|
||||
return 42 + a;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
public class MultiClassTest {
|
||||
public static void main(String[] args) {
|
||||
Helper h = new Helper(3);
|
||||
System.out.println(h.doubleIt()); // expect 6
|
||||
}
|
||||
}
|
||||
|
||||
class Helper {
|
||||
int v;
|
||||
Helper(int v0) { v = v0; }
|
||||
int doubleIt() { return v * 2; }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
public class MultipleClassesTest
|
||||
{
|
||||
public AnotherTestClass a = new AnotherTestClass();
|
||||
}
|
||||
|
||||
class AnotherTestClass
|
||||
{
|
||||
public int a = 42;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
public class RecursionTest {
|
||||
|
||||
public int value = 0;
|
||||
public RecursionTest child = null;
|
||||
|
||||
public RecursionTest(int n)
|
||||
{
|
||||
this.value = n;
|
||||
|
||||
if(n > 0)
|
||||
{
|
||||
child = new RecursionTest(n - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public int fibonacci(int n)
|
||||
{
|
||||
if(n < 2)
|
||||
{
|
||||
return n;
|
||||
}
|
||||
else
|
||||
{
|
||||
return fibonacci(n - 1) + this.fibonacci(n - 2);
|
||||
}
|
||||
}
|
||||
|
||||
public int ackermann(int m, int n)
|
||||
{
|
||||
if (m == 0) return n + 1;
|
||||
if (n == 0) return ackermann(m - 1, 1);
|
||||
return ackermann(m - 1, ackermann(m, n - 1));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
class ShenaniganceTest {
|
||||
|
||||
int testAssignment() {
|
||||
int x = 1;
|
||||
int y = x = 5;
|
||||
return y;
|
||||
}
|
||||
|
||||
int divEqual() {
|
||||
int x = 234_343_000;
|
||||
x /= 4;
|
||||
return x;
|
||||
}
|
||||
|
||||
boolean testIf(int x) {
|
||||
if (true && x < 8) {
|
||||
char f = 'c';
|
||||
return f > x ;
|
||||
}
|
||||
ifn't {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
public class SingletonTest {
|
||||
|
||||
SingletonTest instance;
|
||||
|
||||
SingletonTest() {
|
||||
}
|
||||
|
||||
public SingletonTest getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new SingletonTest();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
public class WhileTest {
|
||||
public static int whileLoopTest(int n) {
|
||||
int sum = 0;
|
||||
while (n > 0) {
|
||||
sum = sum + n;
|
||||
n = n - 1;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Expect 1+2+3+4+5 = 15
|
||||
System.out.println(whileLoopTest(5));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
class Main {
|
||||
int main() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
class ClassWithInt {
|
||||
Integer i;
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user