Compare commits
13 Commits
fa285a4af2
...
typechecke
Author | SHA1 | Date | |
---|---|---|---|
ab7077d8f0 | |||
53061fb73d | |||
bac2a534b6 | |||
574628d3f7 | |||
30288731d3 | |||
f7d135cdd6 | |||
c05aa302a4 | |||
36d9b74df4 | |||
1348cea93f | |||
|
d648e5dd05 | ||
2b45d66ac1 | |||
ae45251189 | |||
94ed7b5056 |
@@ -17,6 +17,8 @@ public class Main {
|
||||
TestMalicious malicious = new TestMalicious();
|
||||
TestLoop loop = new TestLoop();
|
||||
TestMethodOverload overload = new TestMethodOverload();
|
||||
TestShenanigance shenanigance = new TestShenanigance();
|
||||
TestOptionalParameter optionalParameter = new TestOptionalParameter();
|
||||
|
||||
// constructing a basic class works
|
||||
assert empty != null;
|
||||
@@ -35,6 +37,7 @@ public class Main {
|
||||
// self-referencing methods work.
|
||||
assert recursion.fibonacci(15) == 610;
|
||||
assert loop.factorial(5) == 120;
|
||||
assert loop.weirdFor() == 5;
|
||||
// methods with the same name but different parameters work
|
||||
assert overload.MethodOverload() == 42;
|
||||
assert overload.MethodOverload(15) == 42 + 15;
|
||||
@@ -48,5 +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;
|
||||
}
|
||||
}
|
||||
|
@@ -9,4 +9,11 @@ public class TestLoop {
|
||||
|
||||
return tally;
|
||||
}
|
||||
|
||||
int weirdFor() {
|
||||
int k = 0;
|
||||
for (; k < 5; k++) {
|
||||
}
|
||||
return k;
|
||||
}
|
||||
}
|
||||
|
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;
|
||||
}
|
||||
}
|
25
Test/JavaSources/TestShenanigance.java
Normal file
25
Test/JavaSources/TestShenanigance.java
Normal file
@@ -0,0 +1,25 @@
|
||||
class TestShenanigance {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
@@ -1,35 +1,65 @@
|
||||
\section{Lexer \& Parser}
|
||||
\subsection{Lexer}
|
||||
|
||||
Der Lexer wurde mit dem Alex tool implementiert. Dieser ist dafür zuständig den langen String in einzelne Tokens umzuwandeln.
|
||||
In der Alex Datei gibt es für jedes Token einen regulären Ausdruck. Bei den meisten Tokens ist das einfach das Schlüsselwort.
|
||||
Etwas komplexer waren Identifier, Integerliterale Strings und Chars.
|
||||
Für die Definition wurde sich eng an die offizielle Java Language Specification gehalten.
|
||||
Es ist beispielsweise auch möglich Unterstriche in Integerliterale einzubauen (Bsp.: \texttt{234\_343\_000}).
|
||||
Es sind fast alle Schlüsselwörter von Java im Lexer implementiert, auch wenn nicht alle davon vom Parser geparst werden können.
|
||||
Whitespace und Kommentare werden direkt ignoriert und verworfen.
|
||||
Für Charliterale und Integerliterale gibt es auch spezielle Fehlermeldungen. Die meisten Tokens haben nur die Information, zu welchem Keyword sie gehören.
|
||||
Eine Ausnahme bilden der Identifier und die Literale. Für den Identifier wird noch der Name gespeichert und für die Literale der entsprechende Wert.
|
||||
Mit der Funktion alexScanTokens kann dann ein beliebiger String in Tokens umgewandelt werden.
|
||||
Die komplexeren Tokens haben Unittests, welche mit dem Testframework HUnit geschrieben wurden.
|
||||
Es gibt Tests für Kommentare, Identifier, Literale und ein paar weitere Tokens.
|
||||
Der Lexer wurde mit dem Alex tool implementiert. Dieser ist dafür zuständig den langen String in einzelne Tokens umzuwandeln. In der Alex Datei gibt es für jedes Token einen regulären Ausdruck. Bei den meisten Tokens ist das einfach das Schlüsselwort. Etwas komplexer waren Identifier, Integerliterale Strings und Chars. Für die Definition wurde sich eng an die offizielle Java Language Specification gehalten. Es ist beispielsweise auch möglich Unterstriche in Integerliterale einzubauen (Bsp.: \verb|234_343_000|) Es sind fast alle Schlüsselwörter von Java im Lexer implementiert, auch wenn nicht alle davon vom Parser geparst werden können. Whitespace und Kommentare werden direkt ignoriert und verworfen. Für Charliterale und Integerliterale gibt es auch spezielle Fehlermeldungen. Die meisten Tokens haben nur die Information, zu welchem Keyword sie gehören. Eine Ausnahme bilden der Identifier und die Literale. Für den Identifier wird noch der Name gespeichert und für die Literale der entsprechende Wert. Mit der Funktion alexScanTokens kann dann ein beliebiger String in Tokens umgewandelt werden.
|
||||
|
||||
Die komplexeren Tokens haben Unittests, welche mit dem Testframework HUnit geschrieben wurden. Es gibt Tests für Kommentare, Identifier, Literale und ein paar weitere Tokens.
|
||||
|
||||
\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.
|
||||
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. Als erstes wurden leere Methoden und Felder umgesetzt.
|
||||
Da in Java Methoden und Felder durcheinander vorkommen können geben die Ableitungsregeln einen Datentype namens \texttt{MethodOrFieldDeclaration} zurück.
|
||||
Über Pattern Matching baut die classbodydeclarations Regel dann eine Tupel mit einer Liste aus Methoden und einer aus Feldern.
|
||||
Über pattern matching werden diese Listen dann erweitert und in der darüberliegenden Regel schließlich extrahiert.
|
||||
Die Konstruktoren sind in diesem Fall auch normale Methoden mit dem Rückgabewert `void` und dem Namen \texttt{<init>}.
|
||||
Auf diese Weise müssen sie nicht mehr vom Typcheck oder vom Bytecode verändert werden.
|
||||
In Java ist es möglich mehrere Variablen in einer Zeile zu deklarieren (Bsp.: \texttt{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.
|
||||
Für die Zuweisung wird auch die Kombination mit Rechenoperatoren unterstützt. Das Ganze ist als syntactic sugar im Parser umgesetzt.
|
||||
Wenn es einen Zuweisungsoperator gibt, dann wird der Ausdruck in eine Zuweisung und Rechnung aufgeteilt.
|
||||
Bsp.: \texttt{x += 3;} wird umgewandelt zu \texttt{x = x + 3}.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
\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.
|
||||
|
||||
Bei folgender Klasse:
|
||||
\begin{lstlisting}[language=Java]
|
||||
class TestClass {
|
||||
int field;
|
||||
|
||||
TestClass() {}
|
||||
|
||||
void foo() {}
|
||||
}
|
||||
\end{lstlisting}
|
||||
würde die Regel folgendes Tupel zurückgeben:
|
||||
\begin{lstlisting}[language=Haskell]
|
||||
(
|
||||
[ConstructorDeclaration "TestClass" [] (Block [])],
|
||||
[MethodDeclaration "void" "foo" [] (Block [])],
|
||||
[VariableDeclaration "int" "field" Nothing]
|
||||
)
|
||||
\end{lstlisting}
|
||||
und folgende Klasse wird erstellt
|
||||
\begin{lstlisting}[language=Haskell]
|
||||
Class "TestClass"
|
||||
[ConstructorDeclaration "TestClass" [] (Block [])]
|
||||
[MethodDeclaration "void" "foo" [] (Block [])]
|
||||
[VariableDeclaration "int" "field" Nothing]
|
||||
\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}
|
||||
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.
|
||||
|
||||
\begin{lstlisting}[language=Java]
|
||||
for (int i = 0; i < 9; i++) {
|
||||
foo();
|
||||
}
|
||||
\end{lstlisting}
|
||||
wird umgewandelt in:
|
||||
\begin{lstlisting}[language=Java]
|
||||
{
|
||||
int i = 0;
|
||||
while (i < 9) {
|
||||
foo();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
@@ -3,12 +3,13 @@ module Ast where
|
||||
type CompilationUnit = [Class]
|
||||
type DataType = String
|
||||
type Identifier = String
|
||||
type Modifier = String
|
||||
|
||||
data ParameterDeclaration = ParameterDeclaration DataType Identifier deriving (Show, Eq)
|
||||
data VariableDeclaration = VariableDeclaration DataType Identifier (Maybe Expression) deriving (Show, Eq)
|
||||
data Class = Class DataType [ConstructorDeclaration] [MethodDeclaration] [VariableDeclaration] deriving (Show, Eq)
|
||||
data MethodDeclaration = MethodDeclaration DataType Identifier [ParameterDeclaration] Statement deriving (Show, Eq)
|
||||
data ConstructorDeclaration = ConstructorDeclaration Identifier [ParameterDeclaration] Statement deriving (Show, Eq)
|
||||
data VariableDeclaration = VariableDeclaration DataType Modifier Identifier (Maybe Expression) deriving (Show, Eq)
|
||||
data Class = Class DataType Modifier [ConstructorDeclaration] [MethodDeclaration] [VariableDeclaration] deriving (Show, Eq)
|
||||
data MethodDeclaration = MethodDeclaration DataType Modifier Identifier [ParameterDeclaration] Statement deriving (Show, Eq)
|
||||
data ConstructorDeclaration = ConstructorDeclaration Modifier Identifier [ParameterDeclaration] Statement deriving (Show, Eq)
|
||||
|
||||
data Statement
|
||||
= If Expression Statement (Maybe Statement)
|
||||
|
@@ -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
|
||||
@@ -92,33 +93,33 @@ qualifiedname : name DOT IDENTIFIER { BinaryOperation NameResolution $1 (Ref
|
||||
|
||||
simplename : IDENTIFIER { $1 }
|
||||
|
||||
classdeclaration : CLASS IDENTIFIER classbody { case $3 of (constructors, methods, fields) -> Class $2 constructors methods fields }
|
||||
| modifiers CLASS IDENTIFIER classbody { case $4 of (constructors, methods, fields) -> Class $3 constructors methods fields }
|
||||
classdeclaration : CLASS IDENTIFIER classbody { case $3 of (constructors, methods, fields) -> Class $2 "public" constructors methods fields }
|
||||
| modifiers CLASS IDENTIFIER classbody { case $4 of (constructors, methods, fields) -> Class $3 (head $1) constructors methods fields }
|
||||
|
||||
classbody : LBRACKET RBRACKET { ([], [], []) }
|
||||
| LBRACKET classbodydeclarations RBRACKET { $2 }
|
||||
|
||||
modifiers : modifier { }
|
||||
| modifiers modifier { }
|
||||
modifiers : modifier { [$1] }
|
||||
| modifiers modifier { $1 ++ [$2] }
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
modifier : PUBLIC { }
|
||||
| PROTECTED { }
|
||||
| PRIVATE { }
|
||||
| STATIC { }
|
||||
| ABSTRACT { }
|
||||
modifier : PUBLIC { "public" }
|
||||
| PROTECTED { "protected" }
|
||||
| PRIVATE { "private" }
|
||||
| STATIC { "static" }
|
||||
| ABSTRACT { "abstract" }
|
||||
|
||||
classtype : classorinterfacetype { $1 }
|
||||
|
||||
@@ -130,13 +131,13 @@ classorinterfacetype : simplename { $1 }
|
||||
classmemberdeclaration : fielddeclaration { $1 }
|
||||
| methoddeclaration { $1 }
|
||||
|
||||
constructordeclaration : constructordeclarator constructorbody { case $1 of (identifier, parameters) -> ConstructorDecl $ ConstructorDeclaration identifier parameters $2 }
|
||||
| modifiers constructordeclarator constructorbody { case $2 of (identifier, parameters) -> ConstructorDecl $ ConstructorDeclaration identifier parameters $3 }
|
||||
constructordeclaration : constructordeclarator constructorbody { case $1 of (identifier, parameters) -> ConstructorDecl $ ConstructorDeclaration "public" identifier parameters $2 }
|
||||
| modifiers constructordeclarator constructorbody { case $2 of (identifier, parameters) -> ConstructorDecl $ ConstructorDeclaration (head $1) identifier parameters $3 }
|
||||
|
||||
fielddeclaration : type variabledeclarators SEMICOLON { FieldDecls $ map (convertDeclarator $1) $2 }
|
||||
| modifiers type variabledeclarators SEMICOLON { FieldDecls $ map (convertDeclarator $2) $3 }
|
||||
fielddeclaration : type variabledeclarators SEMICOLON { FieldDecls $ map (convertDeclarator "public" $1) $2 }
|
||||
| modifiers type variabledeclarators SEMICOLON { FieldDecls $ map (convertDeclarator (head $1) $2) $3 }
|
||||
|
||||
methoddeclaration : methodheader methodbody { case $1 of (returnType, (name, parameters)) -> MethodDecl (MethodDeclaration returnType name parameters $2) }
|
||||
methoddeclaration : methodheader methodbody { case $1 of (returnType, modifier, (name, (parameters, optionalparameters))) -> MethodDecl (MethodDeclarationWithOptionals returnType modifier name parameters optionalparameters $2) }
|
||||
|
||||
block : LBRACKET RBRACKET { Block [] }
|
||||
| LBRACKET blockstatements RBRACKET { Block $2 }
|
||||
@@ -149,10 +150,10 @@ constructorbody : LBRACKET RBRACKET { Block [] }
|
||||
| LBRACKET blockstatements RBRACKET { Block $2 }
|
||||
-- | LBRACKET explicitconstructorinvocation blockstatements RBRACKET { }
|
||||
|
||||
methodheader : type methoddeclarator { ($1, $2) }
|
||||
| modifiers type methoddeclarator { ($2, $3) }
|
||||
| VOID methoddeclarator { ("void", $2) }
|
||||
| modifiers VOID methoddeclarator { ("void", $3)}
|
||||
methodheader : type methoddeclarator { ($1, "public", $2) }
|
||||
| modifiers type methoddeclarator { ($2, head $1, $3) }
|
||||
| VOID methoddeclarator { ("void", "public", $2) }
|
||||
| modifiers VOID methoddeclarator { ("void", head $1, $3) }
|
||||
|
||||
type : primitivetype { $1 }
|
||||
| referencetype { $1 }
|
||||
@@ -164,7 +165,11 @@ methodbody : block { $1 }
|
||||
| SEMICOLON { Block [] }
|
||||
|
||||
blockstatements : blockstatement { $1 }
|
||||
| blockstatements blockstatement { $1 ++ $2}
|
||||
| blockstatements blockstatement { $1 ++ $2 }
|
||||
|
||||
formalandoptionalparameterlist : formalparameterlist { ($1, []) }
|
||||
| formalparameterlist COMMA optionalparameterlist { ($1, $3) }
|
||||
| optionalparameterlist { ([], $1) }
|
||||
|
||||
formalparameterlist : formalparameter { [$1] }
|
||||
| formalparameterlist COMMA formalparameter { $1 ++ [$3] }
|
||||
@@ -175,15 +180,19 @@ 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 }
|
||||
|
||||
referencetype : classorinterfacetype { $1 }
|
||||
|
||||
|
||||
variabledeclarator : variabledeclaratorid { Declarator $1 Nothing }
|
||||
| variabledeclaratorid ASSIGN variableinitializer { Declarator $1 (Just $3) }
|
||||
|
||||
@@ -215,7 +224,8 @@ expression : assignmentexpression { $1 }
|
||||
integraltype : INT { "int" }
|
||||
| CHAR { "char" }
|
||||
|
||||
localvariabledeclaration : type variabledeclarators { map LocalVariableDeclaration $ map (convertDeclarator $1) $2 }
|
||||
localvariabledeclaration : type variabledeclarators { map (LocalVariableDeclaration . convertDeclarator "public" $1) $2 }
|
||||
| modifiers type variabledeclarators { map (LocalVariableDeclaration . convertDeclarator (unwords $1) $2) $3 }
|
||||
|
||||
statementwithouttrailingsubstatement : block { [$1] }
|
||||
| emptystatement { [] }
|
||||
@@ -386,19 +396,40 @@ multiplicativeexpression : unaryexpression { $1 }
|
||||
|
||||
{
|
||||
|
||||
data MethodOrFieldDeclaration = MethodDecl MethodDeclaration
|
||||
data MemberDeclaration = MethodDecl MethodDeclarationWithOptionals
|
||||
| ConstructorDecl ConstructorDeclaration
|
||||
| FieldDecls [VariableDeclaration]
|
||||
| FieldDecls [VariableDeclaration] deriving (Show)
|
||||
|
||||
data Declarator = Declarator Identifier (Maybe Expression)
|
||||
|
||||
convertDeclarator :: DataType -> Declarator -> VariableDeclaration
|
||||
convertDeclarator dataType (Declarator id assigment) = VariableDeclaration dataType id assigment
|
||||
convertDeclarator :: Modifier -> DataType -> Declarator -> VariableDeclaration
|
||||
convertDeclarator modifier dataType (Declarator id assignment) = VariableDeclaration dataType modifier id assignment
|
||||
|
||||
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 String Identifier [ParameterDeclaration] [OptionalParameter] Statement deriving (Show)
|
||||
|
||||
convertMethodDeclarationWithOptionals :: MethodDeclarationWithOptionals -> [MethodDeclaration]
|
||||
convertMethodDeclarationWithOptionals (MethodDeclarationWithOptionals returnType modifier id param [] stmt) = [MethodDeclaration returnType modifier id param stmt]
|
||||
convertMethodDeclarationWithOptionals (MethodDeclarationWithOptionals returnType modifier id param (opt : optRest) stmt) = generateHelperMethod returnType modifier id param opt : convertMethodDeclarationWithOptionals (generateBaseMethod returnType modifier id param opt optRest stmt)
|
||||
|
||||
convertOptionalParameter :: OptionalParameter -> ParameterDeclaration
|
||||
convertOptionalParameter (OptionalParameter dtype id exp) = ParameterDeclaration dtype id
|
||||
|
||||
generateHelperMethod :: DataType -> Modifier -> Identifier -> [ParameterDeclaration] -> OptionalParameter -> MethodDeclaration
|
||||
generateHelperMethod returnType modifier 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 modifier methodName params $ Block [lastStatement]
|
||||
|
||||
generateBaseMethod :: DataType -> Modifier -> Identifier -> [ParameterDeclaration] -> OptionalParameter -> [OptionalParameter] -> Statement -> MethodDeclarationWithOptionals
|
||||
generateBaseMethod returnType modifier methodName params (OptionalParameter dtype id exp) optRest stmt = MethodDeclarationWithOptionals returnType modifier 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)
|
||||
|
||||
|
140
src/Typecheck.hs
140
src/Typecheck.hs
@@ -1,19 +1,19 @@
|
||||
module Typecheck where
|
||||
|
||||
import Data.List (find)
|
||||
import Data.Maybe
|
||||
import Ast
|
||||
|
||||
|
||||
typeCheckCompilationUnit :: CompilationUnit -> CompilationUnit
|
||||
typeCheckCompilationUnit classes =
|
||||
let
|
||||
-- Helper function to add a default constructor if none are present
|
||||
ensureDefaultConstructor :: Class -> Class
|
||||
ensureDefaultConstructor (Class className constructors methods fields) =
|
||||
ensureDefaultConstructor (Class className modifier constructors methods fields) =
|
||||
let
|
||||
defaultConstructor = ConstructorDeclaration className [] (Block [])
|
||||
defaultConstructor = ConstructorDeclaration modifier className [] (Block [])
|
||||
constructorsWithDefault = if null constructors then [defaultConstructor] else constructors
|
||||
in Class className constructorsWithDefault methods fields
|
||||
in Class className modifier constructorsWithDefault methods fields
|
||||
|
||||
-- Inject default constructors into all classes
|
||||
classesWithDefaultConstructors = map ensureDefaultConstructor classes
|
||||
@@ -21,7 +21,7 @@ typeCheckCompilationUnit classes =
|
||||
in map (`typeCheckClass` classesWithDefaultConstructors) classesWithDefaultConstructors
|
||||
|
||||
typeCheckClass :: Class -> [Class] -> Class
|
||||
typeCheckClass (Class className constructors methods fields) classes =
|
||||
typeCheckClass (Class className modifier constructors methods fields) classes =
|
||||
let
|
||||
-- Fields and methods dont need to be added to the symtab because they are looked upon automatically under "this"
|
||||
-- if its not a declared local variable. Also shadowing wouldnt be possible then.
|
||||
@@ -29,10 +29,10 @@ typeCheckClass (Class className constructors methods fields) classes =
|
||||
checkedConstructors = map (\constructor -> typeCheckConstructorDeclaration constructor initalSymTab classes) constructors
|
||||
checkedMethods = map (\method -> typeCheckMethodDeclaration method initalSymTab classes) methods
|
||||
checkedFields = map (\field -> typeCheckVariableDeclaration field initalSymTab classes) fields
|
||||
in Class className checkedConstructors checkedMethods checkedFields
|
||||
in Class className modifier checkedConstructors checkedMethods checkedFields
|
||||
|
||||
typeCheckConstructorDeclaration :: ConstructorDeclaration -> [(Identifier, DataType)] -> [Class] -> ConstructorDeclaration
|
||||
typeCheckConstructorDeclaration (ConstructorDeclaration name params body) symtab classes =
|
||||
typeCheckConstructorDeclaration (ConstructorDeclaration modifier name params body) symtab classes =
|
||||
let
|
||||
constructorParams = [(identifier, dataType) | ParameterDeclaration dataType identifier <- params]
|
||||
initialSymtab = symtab ++ constructorParams
|
||||
@@ -41,24 +41,23 @@ typeCheckConstructorDeclaration (ConstructorDeclaration name params body) symtab
|
||||
bodyType = getTypeFromStmt checkedBody
|
||||
in if name == className
|
||||
then if bodyType == "void"
|
||||
then ConstructorDeclaration name params checkedBody
|
||||
then ConstructorDeclaration modifier name params checkedBody
|
||||
else error $ "Constructor Declaration: Return type mismatch in constructor " ++ name ++ ": expected void, found " ++ bodyType
|
||||
else error $ "Constructor Declaration: Constructor name " ++ name ++ " does not match class name " ++ className
|
||||
|
||||
|
||||
typeCheckMethodDeclaration :: MethodDeclaration -> [(Identifier, DataType)] -> [Class] -> MethodDeclaration
|
||||
typeCheckMethodDeclaration (MethodDeclaration retType name params body) symtab classes =
|
||||
typeCheckMethodDeclaration (MethodDeclaration retType modifier name params body) symtab classes =
|
||||
let
|
||||
methodParams = [(identifier, dataType) | ParameterDeclaration dataType identifier <- params]
|
||||
initialSymtab = ("thisMeth", retType) : symtab ++ methodParams
|
||||
checkedBody = typeCheckStatement body initialSymtab classes
|
||||
bodyType = getTypeFromStmt checkedBody
|
||||
in if bodyType == retType || (bodyType == "void" && retType == "void") || (bodyType == "null" && isObjectType retType) || isSubtype bodyType retType classes
|
||||
then MethodDeclaration retType name params checkedBody
|
||||
then MethodDeclaration retType modifier name params checkedBody
|
||||
else error $ "Method Declaration: Return type mismatch in method " ++ name ++ ": expected " ++ retType ++ ", found " ++ bodyType
|
||||
|
||||
typeCheckVariableDeclaration :: VariableDeclaration -> [(Identifier, DataType)] -> [Class] -> VariableDeclaration
|
||||
typeCheckVariableDeclaration (VariableDeclaration dataType identifier maybeExpr) symtab classes =
|
||||
typeCheckVariableDeclaration (VariableDeclaration dataType modifier identifier maybeExpr) symtab classes =
|
||||
let
|
||||
-- Ensure the type is valid (either a primitive type or a valid class name)
|
||||
validType = dataType `elem` ["int", "boolean", "char"] || isUserDefinedClass dataType classes
|
||||
@@ -74,11 +73,10 @@ typeCheckVariableDeclaration (VariableDeclaration dataType identifier maybeExpr)
|
||||
(False, _, _) -> error $ "Type '" ++ dataType ++ "' is not a valid type for variable '" ++ identifier ++ "'"
|
||||
(_, True, _) -> error $ "Variable '" ++ identifier ++ "' is redefined in the same scope"
|
||||
(_, _, Just t)
|
||||
| t == "null" && isObjectType dataType -> VariableDeclaration dataType identifier checkedExprWithType
|
||||
| t == "null" && isObjectType dataType -> VariableDeclaration dataType modifier identifier checkedExprWithType
|
||||
| t /= dataType -> error $ "Type mismatch in declaration of '" ++ identifier ++ "': expected " ++ dataType ++ ", found " ++ t
|
||||
| otherwise -> VariableDeclaration dataType identifier checkedExprWithType
|
||||
(_, _, Nothing) -> VariableDeclaration dataType identifier checkedExprWithType
|
||||
|
||||
| otherwise -> VariableDeclaration dataType modifier identifier checkedExprWithType
|
||||
(_, _, Nothing) -> VariableDeclaration dataType modifier identifier checkedExprWithType
|
||||
|
||||
-- ********************************** Type Checking: Expressions **********************************
|
||||
|
||||
@@ -93,10 +91,10 @@ typeCheckExpression (Reference id) symtab classes =
|
||||
Nothing ->
|
||||
case lookup "this" symtab of
|
||||
Just className ->
|
||||
let classDetails = find (\(Class name _ _ _) -> name == className) classes
|
||||
let classDetails = find (\(Class name _ _ _ _) -> name == className) classes
|
||||
in case classDetails of
|
||||
Just (Class _ _ _ fields) ->
|
||||
let fieldTypes = [dt | VariableDeclaration dt fieldId _ <- fields, fieldId == id]
|
||||
Just (Class _ _ _ _ fields) ->
|
||||
let fieldTypes = [dt | VariableDeclaration dt modifier fieldId _ <- fields, fieldId == id]
|
||||
-- this case only happens when its a field of its own class so the implicit this will be converted to explicit this
|
||||
in case fieldTypes of
|
||||
[fieldType] -> TypedExpression fieldType (BinaryOperation NameResolution (TypedExpression className (LocalVariable "this")) (TypedExpression fieldType (FieldVariable id)))
|
||||
@@ -167,35 +165,36 @@ typeCheckStatementExpression (Assignment ref expr) symtab classes =
|
||||
else
|
||||
error $ "Type mismatch in assignment to variable: expected " ++ type'' ++ ", found " ++ typeToAssign
|
||||
|
||||
|
||||
typeCheckStatementExpression (ConstructorCall className args) symtab classes =
|
||||
case find (\(Class name _ _ _) -> name == className) classes of
|
||||
case find (\(Class name _ _ _ _) -> name == className) classes of
|
||||
Nothing -> error $ "Class '" ++ className ++ "' not found."
|
||||
Just (Class _ constructors _ _) ->
|
||||
let
|
||||
matchParams (ParameterDeclaration paramType _) arg =
|
||||
let argTyped = typeCheckExpression arg symtab classes
|
||||
argType = getTypeFromExpr argTyped
|
||||
in if argType == "null" && isObjectType paramType
|
||||
then Just (TypedExpression paramType NullLiteral)
|
||||
else if argType == paramType
|
||||
then Just argTyped
|
||||
else Nothing
|
||||
Just (Class _ modifier constructors _ _) ->
|
||||
let matchParams (ParameterDeclaration paramType _) arg =
|
||||
let argTyped = typeCheckExpression arg symtab classes
|
||||
argType = getTypeFromExpr argTyped
|
||||
in if argType == "null" && isObjectType paramType
|
||||
then Just (TypedExpression paramType NullLiteral)
|
||||
else if argType == paramType
|
||||
then Just argTyped
|
||||
else Nothing
|
||||
|
||||
matchConstructor (ConstructorDeclaration name params _) =
|
||||
let matchedArgs = sequence $ zipWith matchParams params args
|
||||
in fmap (\checkedArgs -> (params, checkedArgs)) matchedArgs
|
||||
matchConstructor (ConstructorDeclaration constructorModifier name params _)
|
||||
| constructorModifier == "public" = fmap (\checkedArgs -> (ConstructorDeclaration constructorModifier name params (Block []), checkedArgs)) (sequence $ zipWith matchParams params args)
|
||||
| constructorModifier == "private" = if checkAccess className (lookup "this" symtab)
|
||||
then fmap (\checkedArgs -> (ConstructorDeclaration constructorModifier name params (Block []), checkedArgs)) (sequence $ zipWith matchParams params args)
|
||||
else Nothing
|
||||
| otherwise = Nothing
|
||||
|
||||
validConstructors = filter (\(params, _) -> length params == length args) $ mapMaybe matchConstructor constructors
|
||||
validConstructors = filter (\(ConstructorDeclaration _ _ params _, _) -> length params == length args) $ mapMaybe matchConstructor constructors
|
||||
|
||||
expectedSignatures = [ map (\(ParameterDeclaration t _) -> t) params | ConstructorDeclaration _ params _ <- constructors ]
|
||||
actualSignature = map (\arg -> getTypeFromExpr (typeCheckExpression arg symtab classes)) args
|
||||
mismatchDetails = "Constructor not found for class '" ++ className ++ "' with given arguments.\n" ++
|
||||
"Expected signatures:\n" ++ show expectedSignatures ++
|
||||
"\nActual arguments:" ++ show actualSignature
|
||||
expectedSignatures = [ map (\(ParameterDeclaration t _) -> t) params | ConstructorDeclaration _ _ params _ <- constructors ]
|
||||
actualSignature = map (\arg -> getTypeFromExpr (typeCheckExpression arg symtab classes)) args
|
||||
mismatchDetails = "Constructor not found for class '" ++ className ++ "' with given arguments.\n" ++
|
||||
"Expected signatures:\n" ++ show expectedSignatures ++
|
||||
"\nActual arguments:" ++ show actualSignature
|
||||
|
||||
in case validConstructors of
|
||||
[(_, checkedArgs)] ->
|
||||
[(ConstructorDeclaration _ _ params _, checkedArgs)] ->
|
||||
TypedStatementExpression className (ConstructorCall className checkedArgs)
|
||||
[] -> error mismatchDetails
|
||||
_ -> error $ "Multiple matching constructors found for class '" ++ className ++ "' with given arguments."
|
||||
@@ -204,8 +203,8 @@ typeCheckStatementExpression (MethodCall expr methodName args) symtab classes =
|
||||
let objExprTyped = typeCheckExpression expr symtab classes
|
||||
in case objExprTyped of
|
||||
TypedExpression objType _ ->
|
||||
case find (\(Class className _ _ _) -> className == objType) classes of
|
||||
Just (Class _ _ methods _) ->
|
||||
case find (\(Class className _ _ _ _) -> className == objType) classes of
|
||||
Just (Class className _ _ methods _) ->
|
||||
let matchParams (ParameterDeclaration paramType _) arg =
|
||||
let argTyped = typeCheckExpression arg symtab classes
|
||||
argType = getTypeFromExpr argTyped
|
||||
@@ -215,27 +214,31 @@ typeCheckStatementExpression (MethodCall expr methodName args) symtab classes =
|
||||
then Just argTyped
|
||||
else Nothing
|
||||
|
||||
matchMethod (MethodDeclaration retType name params _) =
|
||||
let matchedArgs = sequence $ zipWith matchParams params args
|
||||
in fmap (\checkedArgs -> (MethodDeclaration retType name params (Block []), checkedArgs)) matchedArgs
|
||||
matchMethod (MethodDeclaration retType modifier name params _)
|
||||
| modifier == "public" = fmap (\checkedArgs -> (MethodDeclaration retType modifier name params (Block []), checkedArgs)) (sequence $ zipWith matchParams params args)
|
||||
| modifier == "private" = if checkAccess className (lookup "this" symtab)
|
||||
then fmap (\checkedArgs -> (MethodDeclaration retType modifier name params (Block []), checkedArgs)) (sequence $ zipWith matchParams params args)
|
||||
else Nothing
|
||||
| otherwise = Nothing
|
||||
|
||||
validMethods = filter (\(MethodDeclaration _ name params _, _) -> name == methodName && length params == length args) $ mapMaybe matchMethod methods
|
||||
validMethods = filter (\(MethodDeclaration _ _ name params _, _) -> name == methodName && length params == length args) $ mapMaybe matchMethod methods
|
||||
|
||||
expectedSignatures = [ map (\(ParameterDeclaration t _) -> t) params | MethodDeclaration _ name params _ <- methods, name == methodName ]
|
||||
expectedSignatures = [ map (\(ParameterDeclaration t _) -> t) params | MethodDeclaration _ _ name params _ <- methods, name == methodName ]
|
||||
actualSignature = map (\arg -> getTypeFromExpr (typeCheckExpression arg symtab classes)) args
|
||||
mismatchDetails = "Method not found for class '" ++ objType ++ "' with given arguments.\n" ++
|
||||
"Expected signatures for method '" ++ methodName ++ "':\n" ++ unlines (map show expectedSignatures) ++
|
||||
"Actual arguments:\n" ++ show actualSignature
|
||||
|
||||
in case validMethods of
|
||||
[(MethodDeclaration retType _ params _, checkedArgs)] ->
|
||||
[(MethodDeclaration retType _ _ params _, checkedArgs)] ->
|
||||
TypedStatementExpression retType (MethodCall objExprTyped methodName checkedArgs)
|
||||
[] -> error mismatchDetails
|
||||
_ -> error $ "Multiple matching methods found for class '" ++ objType ++ "' and method '" ++ methodName ++ "' with given arguments."
|
||||
|
||||
Nothing -> error $ "Class for object type '" ++ objType ++ "' not found."
|
||||
_ -> error "Invalid object type for method call. Object must have a class type."
|
||||
|
||||
|
||||
|
||||
typeCheckStatementExpression (PostIncrement expr) symtab classes =
|
||||
let expr' = typeCheckExpression expr symtab classes
|
||||
type' = getTypeFromExpr expr'
|
||||
@@ -292,8 +295,7 @@ typeCheckStatement (If cond thenStmt elseStmt) symtab classes =
|
||||
then TypedStatement ifType (If cond' thenStmt' elseStmt')
|
||||
else error "If condition must be of type boolean"
|
||||
|
||||
|
||||
typeCheckStatement (LocalVariableDeclaration (VariableDeclaration dataType identifier maybeExpr)) symtab classes =
|
||||
typeCheckStatement (LocalVariableDeclaration (VariableDeclaration dataType modifier identifier maybeExpr)) symtab classes =
|
||||
-- Check for redefinition in the current scope
|
||||
if any ((== identifier) . snd) symtab
|
||||
then error $ "Variable '" ++ identifier ++ "' is redefined in the same scope"
|
||||
@@ -307,11 +309,10 @@ typeCheckStatement (LocalVariableDeclaration (VariableDeclaration dataType ident
|
||||
in case exprType of
|
||||
Just t
|
||||
| t == "null" && isObjectType dataType ->
|
||||
TypedStatement dataType (LocalVariableDeclaration (VariableDeclaration dataType identifier checkedExprWithType))
|
||||
TypedStatement dataType (LocalVariableDeclaration (VariableDeclaration dataType modifier identifier checkedExprWithType))
|
||||
| t /= dataType -> error $ "Type mismatch in declaration of '" ++ identifier ++ "': expected " ++ dataType ++ ", found " ++ t
|
||||
| otherwise -> TypedStatement dataType (LocalVariableDeclaration (VariableDeclaration dataType identifier checkedExprWithType))
|
||||
Nothing -> TypedStatement dataType (LocalVariableDeclaration (VariableDeclaration dataType identifier checkedExpr))
|
||||
|
||||
| otherwise -> TypedStatement dataType (LocalVariableDeclaration (VariableDeclaration dataType modifier identifier checkedExprWithType))
|
||||
Nothing -> TypedStatement dataType (LocalVariableDeclaration (VariableDeclaration dataType modifier identifier checkedExpr))
|
||||
|
||||
typeCheckStatement (While cond stmt) symtab classes =
|
||||
let cond' = typeCheckExpression cond symtab classes
|
||||
@@ -326,7 +327,7 @@ typeCheckStatement (Block statements) symtab classes =
|
||||
let
|
||||
processStatements (accSts, currentSymtab, types) stmt =
|
||||
case stmt of
|
||||
LocalVariableDeclaration (VariableDeclaration dataType identifier maybeExpr) ->
|
||||
LocalVariableDeclaration (VariableDeclaration dataType modifier identifier maybeExpr) ->
|
||||
let
|
||||
alreadyDefined = any (\(id, _) -> id == identifier) currentSymtab
|
||||
newSymtab = if alreadyDefined
|
||||
@@ -355,7 +356,6 @@ typeCheckStatement (Block statements) symtab classes =
|
||||
|
||||
in TypedStatement blockType (Block checkedStatements)
|
||||
|
||||
|
||||
typeCheckStatement (Return expr) symtab classes =
|
||||
let methodReturnType = fromMaybe (error "Method return type not found in symbol table") (lookup "thisMeth" symtab)
|
||||
expr' = case expr of
|
||||
@@ -369,7 +369,6 @@ typeCheckStatement (Return expr) symtab classes =
|
||||
then TypedStatement returnType (Return expr')
|
||||
else error $ "Return: Return type mismatch: expected " ++ methodReturnType ++ ", found " ++ returnType
|
||||
|
||||
|
||||
typeCheckStatement (StatementExpressionStatement stmtExpr) symtab classes =
|
||||
let stmtExpr' = typeCheckStatementExpression stmtExpr symtab classes
|
||||
in TypedStatement (getTypeFromStmtExpr stmtExpr') (StatementExpressionStatement stmtExpr')
|
||||
@@ -385,7 +384,7 @@ isSubtype subType superType classes
|
||||
| otherwise = False
|
||||
|
||||
isUserDefinedClass :: DataType -> [Class] -> Bool
|
||||
isUserDefinedClass dt classes = dt `elem` map (\(Class name _ _ _) -> name) classes
|
||||
isUserDefinedClass dt classes = dt `elem` map (\(Class name _ _ _ _) -> name) classes
|
||||
|
||||
isObjectType :: DataType -> Bool
|
||||
isObjectType dt = dt /= "int" && dt /= "boolean" && dt /= "char"
|
||||
@@ -446,7 +445,6 @@ checkEqualityOperation op expr1' expr2' type1 type2
|
||||
error $ "Equality operation " ++ show op ++ " requires that null can only be compared with object types. Found types: " ++ type1 ++ " and " ++ type2
|
||||
| otherwise = error $ "Equality operation " ++ show op ++ " encountered unexpected types: " ++ type1 ++ " and " ++ type2
|
||||
|
||||
|
||||
checkLogicalOperation :: BinaryOperator -> Expression -> Expression -> DataType -> DataType -> Expression
|
||||
checkLogicalOperation op expr1' expr2' type1 type2
|
||||
| type1 == "boolean" && type2 == "boolean" =
|
||||
@@ -457,12 +455,22 @@ resolveNameResolution :: Expression -> Expression -> [(Identifier, DataType)] ->
|
||||
resolveNameResolution expr1' (Reference ident2) symtab classes =
|
||||
case getTypeFromExpr expr1' of
|
||||
objType ->
|
||||
case find (\(Class className _ _ _) -> className == objType) classes of
|
||||
Just (Class _ _ _ fields) ->
|
||||
let fieldTypes = [dt | VariableDeclaration dt id _ <- fields, id == ident2]
|
||||
in case fieldTypes of
|
||||
[resolvedType] -> TypedExpression resolvedType (BinaryOperation NameResolution expr1' (TypedExpression resolvedType (FieldVariable ident2)))
|
||||
case find (\(Class className _ _ _ _) -> className == objType) classes of
|
||||
Just (Class _ _ _ _ fields) ->
|
||||
let fieldDetails = [(dt, mod) | VariableDeclaration dt mod id _ <- fields, id == ident2]
|
||||
className = objType
|
||||
in case fieldDetails of
|
||||
[(resolvedType, mod)] ->
|
||||
if mod == "public" || (mod == "private" && checkAccess objType (lookup "this" symtab))
|
||||
then TypedExpression resolvedType (BinaryOperation NameResolution expr1' (TypedExpression resolvedType (FieldVariable ident2)))
|
||||
else error $ "Field '" ++ ident2 ++ "' is private and cannot be accessed outside its class."
|
||||
[] -> error $ "Field '" ++ ident2 ++ "' not found in class '" ++ objType ++ "'"
|
||||
_ -> error $ "Ambiguous reference to field '" ++ ident2 ++ "' in class '" ++ objType ++ "'"
|
||||
Nothing -> error $ "Class '" ++ objType ++ "' not found"
|
||||
resolveNameResolution _ _ _ _ = error "Name resolution requires object reference and field name"
|
||||
|
||||
|
||||
checkAccess :: DataType -> Maybe DataType -> Bool
|
||||
checkAccess objType (Just thisClass) = objType == thisClass
|
||||
checkAccess _ Nothing = False
|
||||
|
||||
|
Reference in New Issue
Block a user