bytecode #10
@ -18,6 +18,7 @@ public class Main {
|
||||
TestLoop loop = new TestLoop();
|
||||
TestMethodOverload overload = new TestMethodOverload();
|
||||
TestShenanigance shenanigance = new TestShenanigance();
|
||||
TestOptionalParameter optionalParameter = new TestOptionalParameter();
|
||||
|
||||
// constructing a basic class works
|
||||
assert empty != null;
|
||||
@ -50,8 +51,14 @@ public class Main {
|
||||
{
|
||||
assert malicious.cursedFormatting(i) == i;
|
||||
}
|
||||
// other syntactic sugar
|
||||
assert shenanigance.testAssignment() == 5;
|
||||
assert shenanigance.divEqual() == 234_343_000 / 4;
|
||||
assert shenanigance.testIf(5);
|
||||
// optional parameters
|
||||
assert optionalParameter.oneOptional() == 1;
|
||||
assert optionalParameter.oneOptional(2) == 2;
|
||||
assert optionalParameter.normalAndOptional(1) == 6;
|
||||
assert optionalParameter.normalAndOptional(1, 0) == 4;
|
||||
}
|
||||
}
|
||||
|
10
Test/JavaSources/TestOptionalParameter.java
Normal file
10
Test/JavaSources/TestOptionalParameter.java
Normal file
@ -0,0 +1,10 @@
|
||||
class TestOptionalParameter {
|
||||
|
||||
int oneOptional(int p = 1) {
|
||||
return p;
|
||||
}
|
||||
|
||||
int normalAndOptional(int a, int b = 2, int c = 3) {
|
||||
return a + b + c;
|
||||
}
|
||||
}
|
Binary file not shown.
@ -16,6 +16,39 @@
|
||||
filecolor=magenta,
|
||||
urlcolor=cyan,
|
||||
}
|
||||
|
||||
%for code listings
|
||||
\usepackage{listings}
|
||||
\usepackage{xcolor}
|
||||
\definecolor{ListingBackground}{HTML}{F8F8F8}
|
||||
\lstdefinestyle{mystyle}{
|
||||
language=Java, % default language
|
||||
numbers=left, % position of line numbers (left, right)
|
||||
stepnumber=1, % set number to each line
|
||||
numbersep=5pt, % 5pt between number and source code
|
||||
numberstyle=\tiny, % letter size of numbers
|
||||
breaklines=true, % break lines if necessary (true, false)
|
||||
breakautoindent=true, % indenting after break line (true, false)
|
||||
postbreak=\space, % break line after space
|
||||
tabsize=2, % tabulator size
|
||||
basicstyle=\ttfamily\footnotesize, % font style
|
||||
showspaces=false, % show space (true, false)
|
||||
extendedchars=true, % show all Latin1 characters (true, false)
|
||||
captionpos=b, % sets the caption-position to bottom
|
||||
backgroundcolor=\color{ListingBackground}, % source code background
|
||||
xleftmargin=10pt, % margin left
|
||||
xrightmargin=5pt, % margin right
|
||||
frame=single, % border settings
|
||||
frameround=ffff,
|
||||
rulecolor=\color{darkgray}, % border color
|
||||
fillcolor=\color{ListingBackground},
|
||||
aboveskip=20pt,
|
||||
keywordstyle=\color[rgb]{0.133,0.133,0.6},
|
||||
commentstyle=\color[rgb]{0.133,0.545,0.133},
|
||||
stringstyle=\color[rgb]{0.627,0.126,0.941}
|
||||
}
|
||||
\lstset{style=mystyle}
|
||||
|
||||
\let\clearpage\relax
|
||||
|
||||
\begin{document}
|
||||
|
@ -15,7 +15,7 @@
|
||||
\item Binar-Operationen (\texttt{\&, |, \^})
|
||||
\item Pre/Post-Inkrement \& Dekrement
|
||||
\item Kontrollflussstrukturen:
|
||||
\begin{itemize}
|
||||
\begin{itemize}[noitemsep]
|
||||
\item If/Else
|
||||
\item While
|
||||
\item For
|
||||
@ -31,4 +31,5 @@
|
||||
\item Deklaration und Zuweisung in einer Anweisung
|
||||
\item Beliebig verschachtelte Blöcke
|
||||
\item Überladung von Methoden \& Konstruktoren
|
||||
\item Parameter mit Standardwerten
|
||||
\end{itemize}
|
@ -7,13 +7,14 @@ Die komplexeren Tokens haben Unittests, welche mit dem Testframework HUnit gesch
|
||||
|
||||
\subsection{Parser}
|
||||
|
||||
Der Parser wurde mit dem Happy tool implementiert. Er baut aus einer Liste von Tokens einen ungetypten AST. Wir haben bereits eine Grammatik bekommen und mussten diese noch in den AST umwandeln.
|
||||
Der Parser wurde mit dem Happy tool implementiert. Er baut aus einer Liste von Tokens einen ungetypten AST. Wir haben bereits eine Grammatik bekommen und mussten für die einzelnen Regeln noch die Rückgabewerte angeben.
|
||||
|
||||
Um den Parser aufzubauen wurde zuerst ein Großteil der Grammatik auskommentiert und Stück für Stück wurden die Umwandlungen hinzugefügt. Immer wenn ein neues Feature umgesetzt wurde, wurde dafür ein weiterer Unit Test geschrieben. Es gibt also für jede komplexe Ableitungsregel mindestens einen Unittest.
|
||||
Um den Parser aufzubauen wurde zuerst ein Großteil der Grammatik auskommentiert und Stück für Stück wurden die Rückgabewerte hinzugefügt. Immer wenn ein neues Feature umgesetzt wurde, wurde dafür ein weiterer Unit Test geschrieben. Es gibt also für jede komplexe Ableitungsregel mindestens einen Unittest.
|
||||
|
||||
\subsubsection{Klassenaufbau}
|
||||
Als erstes wurden leere Konstruktoren Methoden und Felder umgesetzt. Da in Java Konstruktoren, Methoden und Felder durcheinander vorkommen können geben die Ableitungsregeln einen Datentyp namens `MemberDeclaration` zurück. Die classbodydeclarations Regel baut dann einen 3-Tupel mit einer Liste aus Konstruktoren, einer aus Methoden und einer aus Feldern. Über pattern matching werden diese Listen dann erweitert und in der darüberliegenden Regel schließlich extrahiert.
|
||||
Als erstes wurden leere Konstruktoren Methoden und Felder umgesetzt. Da in Java Konstruktoren, Methoden und Felder durcheinander vorkommen können geben die Ableitungsregeln einen Datentyp namens MemberDeclaration zurück, welcher eines von den drei enthalten kann. Die \verb|classbodydeclarations| Regel baut dann einen 3-Tupel mit einer Liste aus Konstruktoren, einer aus Methoden und einer aus Feldern. Über Pattern Matching werden diese Listen dann erweitert und in der darüberliegenden Regel schließlich extrahiert.
|
||||
|
||||
\pagebreak
|
||||
Bei folgender Klasse:
|
||||
\begin{lstlisting}[language=Java]
|
||||
class TestClass {
|
||||
@ -41,12 +42,13 @@ Class "TestClass"
|
||||
\end{lstlisting}
|
||||
Das Nothing ist in diesem Fall ein Platzhalter für eine Zuweisung, da unser Compiler auch Zuweisung bei der Felddeklaration unterstützt.
|
||||
|
||||
In Java ist es möglich mehrere Variablen in einer Zeile zu deklarieren (Bsp.: `int x, y;`). Beim Parsen ergiebt sich dann die Schwierigkeit, dass man in dem Moment, wo man die Variable parst nicht weiß welchen Datentyp diese hat. Aus diesem Grund gibt es den Datentyp Declarator, welcher nur den Identifier und eventuell eine Zuweisung enthält. In den darüberliegenden Regeln fielddeclaration und localvariabledeclaration wird dann die Typinformation hinzugefügt mithilfe der Funktion convertDeclarator.
|
||||
|
||||
\subsubsection{Syntactic Sugar}
|
||||
|
||||
In Java ist es möglich mehrere Variablen in einer Zeile zu deklarieren (Bsp.: \verb|int x, y;|). Beim Parsen ergibt sich dann die Schwierigkeit, dass man in dem Moment, in dem man die Variable parst, nicht weiß welchen Datentyp diese hat. Aus diesem Grund gibt es den Datentyp Declarator, welcher nur den Identifier und eventuell eine Zuweisung enthält. In den darüberliegenden Regeln \verb|fielddeclaration| und \verb|localvariabledeclaration| wird dann die Typinformation hinzugefügt mithilfe der Funktion \verb|convertDeclarator|.
|
||||
|
||||
Für die Zuweisung wird auch die Kombination mit Rechenoperatoren unterstützt. Das ganze ist durch Syntactic Sugar im Parser umgesetzt. Wenn es einen Zuweisungsoperator gibt, dann wird der Ausdruck in eine Zuweisung und Rechnung aufgeteilt. Bsp.: \verb|x += 3;| wird umgewandelt in \verb|x = x + 3|.
|
||||
|
||||
For-Schleifen wurde auch rein im Parser durch Syntactic Sugar implementiert. Eine For-Schleife wird dabei in eine While-Schleife umgewandelt. Dafür wird zuerst ein Block erstellt, sodass die deklarierten Variablen auch nur für den Bereich der Schleife gültig sind. Die Bedingung der For-Schleife kann in die While-Schleife übernommen werden. Innerhalb der While-Schleife folgen zuerst die Statements, die im Block der For-Schleife waren und danach die Update-Statements.
|
||||
For-Schleifen wurden auch rein im Parser durch Syntactic Sugar implementiert. Eine For-Schleife wird dabei in eine While-Schleife umgewandelt. Dafür wird zuerst ein Block erstellt, sodass die deklarierten Variablen auch nur für den Bereich der Schleife gültig sind. Die Bedingung der For-Schleife kann in die While-Schleife übernommen werden. Innerhalb der While-Schleife folgen zuerst die Statements, die im Block der For-Schleife waren und danach die Update-Statements.
|
||||
|
||||
\begin{lstlisting}[language=Java]
|
||||
for (int i = 0; i < 9; i++) {
|
||||
@ -63,3 +65,24 @@ wird umgewandelt in:
|
||||
}
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
Es wurden auch Parameter mit Standardwerten im Parser implementiert. Dieses Feature ist in der aktuellen Java Version (Java 22) noch nicht implementiert. Der Parser macht sich dafür das Überladen von Methoden zunutze. Er generiert für jedes Parameter mit Standardwert eine weitere Funktion, welche die ursprüngliche Funktion mit einem Standardwert aufruft.
|
||||
|
||||
%\lstinputlisting[language=Java,firstline=7,lastline=9]{../Test/JavaSources/TestOptionalParameter.java}
|
||||
\begin{lstlisting}[language=Java]
|
||||
int normalAndOptional(int a, int b = 2, int c = 3) {
|
||||
return a + b + c;
|
||||
}
|
||||
\end{lstlisting}
|
||||
wird umgewandelt in:
|
||||
\begin{lstlisting}
|
||||
int normalAndOptional(int a) {
|
||||
return normalAndOptional(a, 2);
|
||||
}
|
||||
int normalAndOptional(int a, int b) {
|
||||
return normalAndOptional(a, b, 3);
|
||||
}
|
||||
int normalAndOptional(int a, int b, int c) {
|
||||
return a + b + c;
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
@ -252,10 +252,11 @@ assembleStatement (constants, ops, lvars) (TypedStatement _ (LocalVariableDeclar
|
||||
in
|
||||
(constants_init, ops_init ++ storeLocal, lvars ++ [name])
|
||||
|
||||
assembleStatement (constants, ops, lvars) (TypedStatement _ (StatementExpressionStatement expr)) = let
|
||||
assembleStatement (constants, ops, lvars) (TypedStatement dtype (StatementExpressionStatement expr)) = let
|
||||
(constants_e, ops_e, lvars_e) = assembleStatementExpression (constants, ops, lvars) expr
|
||||
in
|
||||
(constants_e, ops_e ++ [Oppop], lvars_e)
|
||||
in case dtype of
|
||||
"void" -> (constants_e, ops_e, lvars_e)
|
||||
_ -> (constants_e, ops_e ++ [Oppop], lvars_e)
|
||||
|
||||
assembleStatement _ stmt = error ("Unknown statement: " ++ show stmt)
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
module Parser.JavaParser (parse, parseStatement, parseExpression) where
|
||||
module Parser.JavaParser (parse, parseStatement, parseExpression, parseMethod) where
|
||||
import Ast
|
||||
import Parser.Lexer
|
||||
}
|
||||
@ -7,6 +7,7 @@ import Parser.Lexer
|
||||
%name parse
|
||||
%name parseStatement statement
|
||||
%name parseExpression expression
|
||||
%name parseMethod classbodydeclarations
|
||||
%tokentype { Token }
|
||||
%error { parseError }
|
||||
%errorhandlertype explist
|
||||
@ -104,13 +105,13 @@ modifiers : modifier { }
|
||||
classbodydeclarations : classbodydeclaration {
|
||||
case $1 of
|
||||
ConstructorDecl constructor -> ([constructor], [], [])
|
||||
MethodDecl method -> ([], [method], [])
|
||||
MethodDecl method -> ([], (convertMethodDeclarationWithOptionals method), [])
|
||||
FieldDecls fields -> ([], [], fields)
|
||||
}
|
||||
| classbodydeclarations classbodydeclaration {
|
||||
case ($1, $2) of
|
||||
((constructors, methods, fields), ConstructorDecl constructor) -> ((constructors ++ [constructor]), methods, fields)
|
||||
((constructors, methods, fields), MethodDecl method) -> (constructors, (methods ++ [method]), fields)
|
||||
((constructors, methods, fields), MethodDecl method) -> (constructors, (methods ++ (convertMethodDeclarationWithOptionals method)), fields)
|
||||
((constructors, methods, fields), FieldDecls newFields) -> (constructors, methods, (fields ++ newFields))
|
||||
}
|
||||
|
||||
@ -136,7 +137,7 @@ constructordeclaration : constructordeclarator constructorbody { case $1 of (ide
|
||||
fielddeclaration : type variabledeclarators SEMICOLON { FieldDecls $ map (convertDeclarator $1) $2 }
|
||||
| modifiers type variabledeclarators SEMICOLON { FieldDecls $ map (convertDeclarator $2) $3 }
|
||||
|
||||
methoddeclaration : methodheader methodbody { case $1 of (returnType, (name, parameters)) -> MethodDecl (MethodDeclaration returnType name parameters $2) }
|
||||
methoddeclaration : methodheader methodbody { case $1 of (returnType, (name, (parameters, optionalparameters))) -> MethodDecl (MethodDeclarationWithOptionals returnType name parameters optionalparameters $2) }
|
||||
|
||||
block : LBRACKET RBRACKET { Block [] }
|
||||
| LBRACKET blockstatements RBRACKET { Block $2 }
|
||||
@ -166,6 +167,10 @@ methodbody : block { $1 }
|
||||
blockstatements : blockstatement { $1 }
|
||||
| blockstatements blockstatement { $1 ++ $2}
|
||||
|
||||
formalandoptionalparameterlist : formalparameterlist { ($1, []) }
|
||||
| formalparameterlist COMMA optionalparameterlist { ($1, $3) }
|
||||
| optionalparameterlist { ([], $1) }
|
||||
|
||||
formalparameterlist : formalparameter { [$1] }
|
||||
| formalparameterlist COMMA formalparameter { $1 ++ [$3] }
|
||||
|
||||
@ -175,8 +180,13 @@ explicitconstructorinvocation : THIS LBRACE RBRACE SEMICOLON { }
|
||||
classtypelist : classtype { }
|
||||
| classtypelist COMMA classtype { }
|
||||
|
||||
methoddeclarator : IDENTIFIER LBRACE RBRACE { ($1, []) }
|
||||
| IDENTIFIER LBRACE formalparameterlist RBRACE { ($1, $3) }
|
||||
methoddeclarator : IDENTIFIER LBRACE RBRACE { ($1, ([], [])) }
|
||||
| IDENTIFIER LBRACE formalandoptionalparameterlist RBRACE { ($1, $3) }
|
||||
|
||||
optionalparameterlist : optionalparameter { [$1] }
|
||||
| optionalparameterlist COMMA optionalparameter { $1 ++ [$3] }
|
||||
|
||||
optionalparameter : type variabledeclaratorid ASSIGN variableinitializer { OptionalParameter $1 $2 $4 }
|
||||
|
||||
primitivetype : BOOLEAN { "boolean" }
|
||||
| numerictype { $1 }
|
||||
@ -386,9 +396,9 @@ multiplicativeexpression : unaryexpression { $1 }
|
||||
|
||||
{
|
||||
|
||||
data MemberDeclaration = MethodDecl MethodDeclaration
|
||||
data MemberDeclaration = MethodDecl MethodDeclarationWithOptionals
|
||||
| ConstructorDecl ConstructorDeclaration
|
||||
| FieldDecls [VariableDeclaration]
|
||||
| FieldDecls [VariableDeclaration] deriving (Show)
|
||||
|
||||
data Declarator = Declarator Identifier (Maybe Expression)
|
||||
|
||||
@ -399,6 +409,22 @@ extractFunctionName :: Expression -> (Expression, Identifier)
|
||||
extractFunctionName (BinaryOperation NameResolution exp (Reference functionname)) = (exp, functionname)
|
||||
extractFunctionName (Reference functionname) = ((Reference "this"), functionname)
|
||||
|
||||
data OptionalParameter = OptionalParameter DataType Identifier Expression deriving (Show)
|
||||
data MethodDeclarationWithOptionals = MethodDeclarationWithOptionals DataType Identifier [ParameterDeclaration] [OptionalParameter] Statement deriving (Show)
|
||||
convertMethodDeclarationWithOptionals :: MethodDeclarationWithOptionals -> [MethodDeclaration]
|
||||
convertMethodDeclarationWithOptionals (MethodDeclarationWithOptionals returnType id param [] stmt) = [MethodDeclaration returnType id param stmt]
|
||||
convertMethodDeclarationWithOptionals (MethodDeclarationWithOptionals returnType id param (opt : optRest) stmt) = generateHelperMethod returnType id param opt : convertMethodDeclarationWithOptionals (generateBaseMethod returnType id param opt optRest stmt)
|
||||
convertOptionalParameter :: OptionalParameter -> ParameterDeclaration
|
||||
convertOptionalParameter (OptionalParameter dtype id exp) = ParameterDeclaration dtype id
|
||||
generateHelperMethod :: DataType -> Identifier -> [ParameterDeclaration] -> OptionalParameter -> MethodDeclaration
|
||||
generateHelperMethod returnType methodName params (OptionalParameter dtype id exp) =
|
||||
let references = ((map (\(ParameterDeclaration paramType ident) -> (Reference ident)) params) ++ [exp])
|
||||
methodcall = (MethodCall (Reference "this") methodName references)
|
||||
lastStatement = if returnType == "void" then StatementExpressionStatement methodcall else Return $ Just $ StatementExpressionExpression methodcall
|
||||
in MethodDeclaration returnType methodName params $ Block [lastStatement]
|
||||
generateBaseMethod :: DataType -> Identifier -> [ParameterDeclaration] -> OptionalParameter -> [OptionalParameter] -> Statement -> MethodDeclarationWithOptionals
|
||||
generateBaseMethod returnType methodName params (OptionalParameter dtype id exp) optRest stmt = MethodDeclarationWithOptionals returnType methodName (params ++ [ParameterDeclaration dtype id]) optRest stmt
|
||||
|
||||
parseError :: ([Token], [String]) -> a
|
||||
parseError (errortoken, expected) = error ("parse error on token: " ++ show errortoken ++ "\nexpected one of: " ++ show expected)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user