Compare commits
11 Commits
6b4b9b496d
...
0a53ea14cf
Author | SHA1 | Date | |
---|---|---|---|
|
0a53ea14cf | ||
|
4c5dbd16f9 | ||
d0bf34d331 | |||
fe4ef2614f | |||
2154f8fd62 | |||
504e26dcdd | |||
f09e6ad09e | |||
3e18efc097 | |||
|
cb462b5e75 | ||
|
3275b045cb | ||
3d352035ee |
13
Test/JavaSources/TestConstructorOverload.java
Normal file
13
Test/JavaSources/TestConstructorOverload.java
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
public class TestConstructorOverload {
|
||||||
|
|
||||||
|
TestConstructorOverload() {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestConstructorOverload(int a) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void test() {
|
||||||
|
int a = 3;
|
||||||
|
TestConstructorOverload test = new TestConstructorOverload(a);
|
||||||
|
}
|
||||||
|
}
|
14
Test/JavaSources/TestMethodOverload.java
Normal file
14
Test/JavaSources/TestMethodOverload.java
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
public class TestMethodOverload {
|
||||||
|
|
||||||
|
public void MethodOverload() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MethodOverload(int a) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void test() {
|
||||||
|
int a = 3;
|
||||||
|
MethodOverload(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
15
Test/JavaSources/TestSingleton.java
Normal file
15
Test/JavaSources/TestSingleton.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
public class TestSingleton {
|
||||||
|
|
||||||
|
TestSingleton instance;
|
||||||
|
|
||||||
|
TestSingleton() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestSingleton getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new TestSingleton();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -7,56 +7,56 @@ import Ast
|
|||||||
|
|
||||||
|
|
||||||
testSingleEmptyClass = TestCase $
|
testSingleEmptyClass = TestCase $
|
||||||
assertEqual "expect single empty class hello" [Class "Hello" [] []] $
|
assertEqual "expect single empty class hello" [Class "Hello" [] [] []] $
|
||||||
parse [CLASS, IDENTIFIER "Hello", LBRACKET, RBRACKET]
|
parse [CLASS, IDENTIFIER "Hello", LBRACKET, RBRACKET]
|
||||||
testTwoEmptyClasses = TestCase $
|
testTwoEmptyClasses = TestCase $
|
||||||
assertEqual "expect two empty classes" [Class "Class1" [] [], Class "Class2" [] []] $
|
assertEqual "expect two empty classes" [Class "Class1" [] [] [], Class "Class2" [] [] []] $
|
||||||
parse [CLASS,IDENTIFIER "Class1",LBRACKET,RBRACKET,CLASS,IDENTIFIER "Class2",LBRACKET,RBRACKET]
|
parse [CLASS,IDENTIFIER "Class1",LBRACKET,RBRACKET,CLASS,IDENTIFIER "Class2",LBRACKET,RBRACKET]
|
||||||
testBooleanField = TestCase $
|
testBooleanField = TestCase $
|
||||||
assertEqual "expect class with boolean field" [Class "WithBool" [] [VariableDeclaration "boolean" "value" Nothing]] $
|
assertEqual "expect class with boolean field" [Class "WithBool" [] [] [VariableDeclaration "boolean" "value" Nothing]] $
|
||||||
parse [CLASS,IDENTIFIER "WithBool",LBRACKET,BOOLEAN,IDENTIFIER "value",SEMICOLON,RBRACKET]
|
parse [CLASS,IDENTIFIER "WithBool",LBRACKET,BOOLEAN,IDENTIFIER "value",SEMICOLON,RBRACKET]
|
||||||
testIntField = TestCase $
|
testIntField = TestCase $
|
||||||
assertEqual "expect class with int field" [Class "WithInt" [] [VariableDeclaration "int" "value" Nothing]] $
|
assertEqual "expect class with int field" [Class "WithInt" [] [] [VariableDeclaration "int" "value" Nothing]] $
|
||||||
parse [CLASS,IDENTIFIER "WithInt",LBRACKET,INT,IDENTIFIER "value",SEMICOLON,RBRACKET]
|
parse [CLASS,IDENTIFIER "WithInt",LBRACKET,INT,IDENTIFIER "value",SEMICOLON,RBRACKET]
|
||||||
testCustomTypeField = TestCase $
|
testCustomTypeField = TestCase $
|
||||||
assertEqual "expect class with foo field" [Class "WithFoo" [] [VariableDeclaration "Foo" "value" Nothing]] $
|
assertEqual "expect class with foo field" [Class "WithFoo" [] [] [VariableDeclaration "Foo" "value" Nothing]] $
|
||||||
parse [CLASS,IDENTIFIER "WithFoo",LBRACKET,IDENTIFIER "Foo",IDENTIFIER "value",SEMICOLON,RBRACKET]
|
parse [CLASS,IDENTIFIER "WithFoo",LBRACKET,IDENTIFIER "Foo",IDENTIFIER "value",SEMICOLON,RBRACKET]
|
||||||
testMultipleDeclarationSameLine = TestCase $
|
testMultipleDeclarationSameLine = TestCase $
|
||||||
assertEqual "expect class with two int fields" [Class "TwoInts" [] [VariableDeclaration "int" "num1" Nothing, VariableDeclaration "int" "num2" Nothing]] $
|
assertEqual "expect class with two int fields" [Class "TwoInts" [] [] [VariableDeclaration "int" "num1" Nothing, VariableDeclaration "int" "num2" Nothing]] $
|
||||||
parse [CLASS,IDENTIFIER "TwoInts",LBRACKET,INT,IDENTIFIER "num1",COMMA,IDENTIFIER "num2",SEMICOLON,RBRACKET]
|
parse [CLASS,IDENTIFIER "TwoInts",LBRACKET,INT,IDENTIFIER "num1",COMMA,IDENTIFIER "num2",SEMICOLON,RBRACKET]
|
||||||
testMultipleDeclarations = TestCase $
|
testMultipleDeclarations = TestCase $
|
||||||
assertEqual "expect class with int and char field" [Class "Multiple" [] [VariableDeclaration "int" "value" Nothing, VariableDeclaration "char" "letter" Nothing]] $
|
assertEqual "expect class with int and char field" [Class "Multiple" [] [] [VariableDeclaration "int" "value" Nothing, VariableDeclaration "char" "letter" Nothing]] $
|
||||||
parse [CLASS,IDENTIFIER "Multiple",LBRACKET,INT,IDENTIFIER "value",SEMICOLON,CHAR,IDENTIFIER "letter",SEMICOLON,RBRACKET]
|
parse [CLASS,IDENTIFIER "Multiple",LBRACKET,INT,IDENTIFIER "value",SEMICOLON,CHAR,IDENTIFIER "letter",SEMICOLON,RBRACKET]
|
||||||
testWithModifier = TestCase $
|
testWithModifier = TestCase $
|
||||||
assertEqual "expect class with int field" [Class "WithInt" [] [VariableDeclaration "int" "value" Nothing]] $
|
assertEqual "expect class with int field" [Class "WithInt" [] [] [VariableDeclaration "int" "value" Nothing]] $
|
||||||
parse [ABSTRACT,CLASS,IDENTIFIER "WithInt",LBRACKET,PUBLIC,INT,IDENTIFIER "value",SEMICOLON,RBRACKET]
|
parse [ABSTRACT,CLASS,IDENTIFIER "WithInt",LBRACKET,PUBLIC,INT,IDENTIFIER "value",SEMICOLON,RBRACKET]
|
||||||
|
|
||||||
testEmptyMethod = TestCase $
|
testEmptyMethod = TestCase $
|
||||||
assertEqual "expect class with method" [Class "WithMethod" [MethodDeclaration "int" "foo" [] (Block [])] []] $
|
assertEqual "expect class with method" [Class "WithMethod" [] [MethodDeclaration "int" "foo" [] (Block [])] []] $
|
||||||
parse [CLASS,IDENTIFIER "WithMethod",LBRACKET,INT,IDENTIFIER "foo",LBRACE,RBRACE,SEMICOLON,RBRACKET]
|
parse [CLASS,IDENTIFIER "WithMethod",LBRACKET,INT,IDENTIFIER "foo",LBRACE,RBRACE,SEMICOLON,RBRACKET]
|
||||||
testEmptyPrivateMethod = TestCase $
|
testEmptyPrivateMethod = TestCase $
|
||||||
assertEqual "expect class with method" [Class "WithMethod" [MethodDeclaration "int" "foo" [] (Block [])] []] $
|
assertEqual "expect class with method" [Class "WithMethod" [] [MethodDeclaration "int" "foo" [] (Block [])] []] $
|
||||||
parse [CLASS,IDENTIFIER "WithMethod",LBRACKET,PRIVATE,INT,IDENTIFIER "foo",LBRACE,RBRACE,LBRACKET,RBRACKET,RBRACKET]
|
parse [CLASS,IDENTIFIER "WithMethod",LBRACKET,PRIVATE,INT,IDENTIFIER "foo",LBRACE,RBRACE,LBRACKET,RBRACKET,RBRACKET]
|
||||||
testEmptyVoidMethod = TestCase $
|
testEmptyVoidMethod = TestCase $
|
||||||
assertEqual "expect class with method" [Class "WithMethod" [MethodDeclaration "void" "foo" [] (Block [])] []] $
|
assertEqual "expect class with method" [Class "WithMethod" [] [MethodDeclaration "void" "foo" [] (Block [])] []] $
|
||||||
parse [CLASS,IDENTIFIER "WithMethod",LBRACKET,VOID,IDENTIFIER "foo",LBRACE,RBRACE,LBRACKET,RBRACKET,RBRACKET]
|
parse [CLASS,IDENTIFIER "WithMethod",LBRACKET,VOID,IDENTIFIER "foo",LBRACE,RBRACE,LBRACKET,RBRACKET,RBRACKET]
|
||||||
testEmptyMethodWithParam = TestCase $
|
testEmptyMethodWithParam = TestCase $
|
||||||
assertEqual "expect class with method with param" [Class "WithParam" [MethodDeclaration "void" "foo" [ParameterDeclaration "int" "param"] (Block [])] []] $
|
assertEqual "expect class with method with param" [Class "WithParam" [] [MethodDeclaration "void" "foo" [ParameterDeclaration "int" "param"] (Block [])] []] $
|
||||||
parse [CLASS,IDENTIFIER "WithParam",LBRACKET,VOID,IDENTIFIER "foo",LBRACE,INT,IDENTIFIER "param",RBRACE,SEMICOLON,RBRACKET]
|
parse [CLASS,IDENTIFIER "WithParam",LBRACKET,VOID,IDENTIFIER "foo",LBRACE,INT,IDENTIFIER "param",RBRACE,SEMICOLON,RBRACKET]
|
||||||
testEmptyMethodWithParams = TestCase $
|
testEmptyMethodWithParams = TestCase $
|
||||||
assertEqual "expect class with multiple params" [Class "WithParams" [MethodDeclaration "void" "foo" [ParameterDeclaration "int" "p1",ParameterDeclaration "Custom" "p2"] (Block [])] []] $
|
assertEqual "expect class with multiple params" [Class "WithParams" [] [MethodDeclaration "void" "foo" [ParameterDeclaration "int" "p1",ParameterDeclaration "Custom" "p2"] (Block [])] []] $
|
||||||
parse [CLASS,IDENTIFIER "WithParams",LBRACKET,VOID,IDENTIFIER "foo",LBRACE,INT,IDENTIFIER "p1",COMMA,IDENTIFIER "Custom",IDENTIFIER "p2",RBRACE,SEMICOLON,RBRACKET]
|
parse [CLASS,IDENTIFIER "WithParams",LBRACKET,VOID,IDENTIFIER "foo",LBRACE,INT,IDENTIFIER "p1",COMMA,IDENTIFIER "Custom",IDENTIFIER "p2",RBRACE,SEMICOLON,RBRACKET]
|
||||||
testClassWithMethodAndField = TestCase $
|
testClassWithMethodAndField = TestCase $
|
||||||
assertEqual "expect class with method and field" [Class "WithMethodAndField" [MethodDeclaration "void" "foo" [] (Block []), MethodDeclaration "int" "bar" [] (Block [])] [VariableDeclaration "int" "value" Nothing]] $
|
assertEqual "expect class with method and field" [Class "WithMethodAndField" [] [MethodDeclaration "void" "foo" [] (Block []), MethodDeclaration "int" "bar" [] (Block [])] [VariableDeclaration "int" "value" Nothing]] $
|
||||||
parse [CLASS,IDENTIFIER "WithMethodAndField",LBRACKET,VOID,IDENTIFIER "foo",LBRACE,RBRACE,LBRACKET,RBRACKET,INT,IDENTIFIER "value",SEMICOLON,INT,IDENTIFIER "bar",LBRACE,RBRACE,SEMICOLON,RBRACKET]
|
parse [CLASS,IDENTIFIER "WithMethodAndField",LBRACKET,VOID,IDENTIFIER "foo",LBRACE,RBRACE,LBRACKET,RBRACKET,INT,IDENTIFIER "value",SEMICOLON,INT,IDENTIFIER "bar",LBRACE,RBRACE,SEMICOLON,RBRACKET]
|
||||||
testClassWithConstructor = TestCase $
|
testClassWithConstructor = TestCase $
|
||||||
assertEqual "expect class with constructor" [Class "WithConstructor" [MethodDeclaration "void" "<init>" [] (Block [])] []] $
|
assertEqual "expect class with constructor" [Class "WithConstructor" [ConstructorDeclaration "WithConstructor" [] (Block [])] [] []] $
|
||||||
parse [CLASS,IDENTIFIER "WithConstructor",LBRACKET,IDENTIFIER "WithConstructor",LBRACE,RBRACE,LBRACKET,RBRACKET,RBRACKET]
|
parse [CLASS,IDENTIFIER "WithConstructor",LBRACKET,IDENTIFIER "WithConstructor",LBRACE,RBRACE,LBRACKET,RBRACKET,RBRACKET]
|
||||||
testConstructorWithParams = TestCase $
|
testConstructorWithParams = TestCase $
|
||||||
assertEqual "expect constructor with params" [Class "WithParams" [MethodDeclaration "void" "<init>" [ParameterDeclaration "int" "p1"] (Block [])] []] $
|
assertEqual "expect constructor with params" [Class "WithParams" [ConstructorDeclaration "WithParams" [ParameterDeclaration "int" "p1"] (Block [])] [] []] $
|
||||||
parse [CLASS,IDENTIFIER "WithParams",LBRACKET,IDENTIFIER "WithParams",LBRACE,INT,IDENTIFIER "p1",RBRACE,LBRACKET,RBRACKET,RBRACKET]
|
parse [CLASS,IDENTIFIER "WithParams",LBRACKET,IDENTIFIER "WithParams",LBRACE,INT,IDENTIFIER "p1",RBRACE,LBRACKET,RBRACKET,RBRACKET]
|
||||||
testConstructorWithStatements = TestCase $
|
testConstructorWithStatements = TestCase $
|
||||||
assertEqual "expect constructor with statement" [Class "WithConstructor" [MethodDeclaration "void" "<init>" [] (Block [Return Nothing])] []] $
|
assertEqual "expect constructor with statement" [Class "WithConstructor" [ConstructorDeclaration "WithConstructor" [] (Block [Return Nothing])] [] []] $
|
||||||
parse [CLASS,IDENTIFIER "WithConstructor",LBRACKET,IDENTIFIER "WithConstructor",LBRACE,RBRACE,LBRACKET,RETURN,SEMICOLON,RBRACKET,RBRACKET]
|
parse [CLASS,IDENTIFIER "WithConstructor",LBRACKET,IDENTIFIER "WithConstructor",LBRACE,RBRACE,LBRACKET,RETURN,SEMICOLON,RBRACKET,RBRACKET]
|
||||||
|
|
||||||
|
|
||||||
@ -78,13 +78,13 @@ testExpressionIntLiteral = TestCase $
|
|||||||
assertEqual "expect IntLiteral" (IntegerLiteral 3) $
|
assertEqual "expect IntLiteral" (IntegerLiteral 3) $
|
||||||
parseExpression [INTEGERLITERAL 3]
|
parseExpression [INTEGERLITERAL 3]
|
||||||
testFieldWithInitialization = TestCase $
|
testFieldWithInitialization = TestCase $
|
||||||
assertEqual "expect Class with initialized field" [Class "WithInitField" [] [VariableDeclaration "int" "number" $ Just $ IntegerLiteral 3]] $
|
assertEqual "expect Class with initialized field" [Class "WithInitField" [] [] [VariableDeclaration "int" "number" $ Just $ IntegerLiteral 3]] $
|
||||||
parse [CLASS,IDENTIFIER "WithInitField",LBRACKET,INT,IDENTIFIER "number",ASSIGN,INTEGERLITERAL 3,SEMICOLON,RBRACKET]
|
parse [CLASS,IDENTIFIER "WithInitField",LBRACKET,INT,IDENTIFIER "number",ASSIGN,INTEGERLITERAL 3,SEMICOLON,RBRACKET]
|
||||||
testLocalBoolWithInitialization = TestCase $
|
testLocalBoolWithInitialization = TestCase $
|
||||||
assertEqual "expect block with with initialized local var" [Block [LocalVariableDeclaration $ VariableDeclaration "boolean" "b" $ Just $ BooleanLiteral False]] $
|
assertEqual "expect block with with initialized local var" [Block [LocalVariableDeclaration $ VariableDeclaration "boolean" "b" $ Just $ BooleanLiteral False]] $
|
||||||
parseStatement [LBRACKET,BOOLEAN,IDENTIFIER "b",ASSIGN,BOOLLITERAL False,SEMICOLON,RBRACKET]
|
parseStatement [LBRACKET,BOOLEAN,IDENTIFIER "b",ASSIGN,BOOLLITERAL False,SEMICOLON,RBRACKET]
|
||||||
testFieldNullWithInitialization = TestCase $
|
testFieldNullWithInitialization = TestCase $
|
||||||
assertEqual "expect Class with initialized field" [Class "WithInitField" [] [VariableDeclaration "Object" "bar" $ Just NullLiteral]] $
|
assertEqual "expect Class with initialized field" [Class "WithInitField" [] [] [VariableDeclaration "Object" "bar" $ Just NullLiteral]] $
|
||||||
parse [CLASS,IDENTIFIER "WithInitField",LBRACKET,IDENTIFIER "Object",IDENTIFIER "bar",ASSIGN,NULLLITERAL,SEMICOLON,RBRACKET]
|
parse [CLASS,IDENTIFIER "WithInitField",LBRACKET,IDENTIFIER "Object",IDENTIFIER "bar",ASSIGN,NULLLITERAL,SEMICOLON,RBRACKET]
|
||||||
testReturnVoid = TestCase $
|
testReturnVoid = TestCase $
|
||||||
assertEqual "expect block with return nothing" [Block [Return Nothing]] $
|
assertEqual "expect block with return nothing" [Block [Return Nothing]] $
|
||||||
|
29
doc/features.md
Normal file
29
doc/features.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Sprach-Features
|
||||||
|
- Klassen
|
||||||
|
- Felder
|
||||||
|
- Methoden (mit Parametern)
|
||||||
|
- Konstruktoren (mit Parametern)
|
||||||
|
- Standardkonstruktoren
|
||||||
|
- Lokale Variablen
|
||||||
|
- Zuweisungen (Feld- und lokale Variablen)
|
||||||
|
- Arithmetik (+, -, *, /, %, Klammern, Korrekte Operator Precedence)
|
||||||
|
- Arithmetische Zuweisungen (+=, -=, *=, /=, %=, &=, |=, ^=)
|
||||||
|
- Vergleichsoperationen (<, >, <=, >=, ==, !=)
|
||||||
|
- Boolsche Operationen (||, &&)
|
||||||
|
- Unäre Operationen (-, ~)
|
||||||
|
- Binar-Operationen (&, |, ^)
|
||||||
|
- Pre/Post-Inkrement & Dekrement
|
||||||
|
- Kontrollflussstrukturen:
|
||||||
|
- If/Else
|
||||||
|
- While
|
||||||
|
- For
|
||||||
|
- Return (mit/ohne Rückgabewert)
|
||||||
|
- Default-Werte für alle Klassenfelder
|
||||||
|
- Methodenaufrufe (mit Parametern), auch über Klassengrenzen
|
||||||
|
- Mehrere Klassen in einer Datei
|
||||||
|
- implizites "this"
|
||||||
|
- Beliebig verschachtelte Namensketten
|
||||||
|
- Beliebige Deklarationsreihenfolge
|
||||||
|
- Literale für Integer und Characters
|
||||||
|
- Deklaration und Zuweisung in einer Anweisung
|
||||||
|
- Beliebig verschachtelte Blöcke
|
19
doc/parser.md
Normal file
19
doc/parser.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Lexer
|
||||||
|
(Marvin Schlegel)
|
||||||
|
|
||||||
|
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.: `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.
|
||||||
|
|
||||||
|
# Parser
|
||||||
|
(Marvin Schlegel)
|
||||||
|
|
||||||
|
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 `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 `<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.: `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.: `x += 3;` wird umgewandelt in `x = x + 3`.
|
@ -1,10 +1,11 @@
|
|||||||
# Typcheck (Fabian Noll)
|
|
||||||
|
# Typecheck (Fabian Noll)
|
||||||
|
|
||||||
## Überblick und Struktur
|
## Überblick und Struktur
|
||||||
|
|
||||||
Die Typprüfung beginnt mit der Funktion `typeCheckCompilationUnit`, die eine Kompilationseinheit als Eingabe erhält. Diese Kompilationseinheit besteht aus einer Liste von Klassen. Jede Klasse wird einzeln durch die Funktion `typeCheckClass` überprüft. Innerhalb dieser Funktion wird eine Symboltabelle erstellt, die den Namen der Klasse als Typ und `this` als Identifier enthält. Diese Symboltabelle wird verwendet, um Typinformationen nach dem Lokalitätsprinzip während der Typprüfung zugänglich zu machen und zu verwalten.
|
Die Typprüfung beginnt mit der Funktion `typeCheckCompilationUnit`, die eine Kompilationseinheit als Eingabe erhält. Diese Kompilationseinheit besteht aus einer Liste von Klassen. Jede Klasse wird einzeln durch die Funktion `typeCheckClass` überprüft. Innerhalb dieser Funktion wird eine Symboltabelle erstellt, die den Namen der Klasse als Typ und `this` als Identifier enthält. Diese Symboltabelle wird verwendet, um Typinformationen nach dem Lokalitätsprinzip während der Typprüfung zugänglich zu machen und zu verwalten.
|
||||||
|
|
||||||
Die Typprüfung einer Klasse umfasst die Überprüfung aller Methoden und Felder. Die Methode `typeCheckMethodDeclaration` ist für die Typprüfung einzelner Methodendeklarationen verantwortlich. Sie überprüft den Rückgabetyp der Methode, die Parameter und den Methodenrumpf. Der Methodenrumpf wird durch rekursive Aufrufe von `typeCheckStatement` überprüft, die verschiedene Arten von Anweisungen wie If-Anweisungen, While-Schleifen, Rückgabeanweisungen und Blockanweisungen behandelt.
|
Die Typprüfung einer Klasse umfasst die Überprüfung aller Konstruktoren, Methoden und Felder. Die Methode `typeCheckConstructorDeclaration` ist für die Typprüfung einzelner Konstruktordeklarationen verantwortlich, während `typeCheckMethodDeclaration` für die Typprüfung einzelner Methodendeklarationen zuständig ist. Beide Funktionen überprüfen die Parameter und den Rumpf der jeweiligen Konstruktoren bzw. Methoden. Der Rumpf wird durch rekursive Aufrufe von `typeCheckStatement` überprüft, die verschiedene Arten von Anweisungen wie If-Anweisungen, While-Schleifen, Rückgabeanweisungen und Blockanweisungen behandelt.
|
||||||
|
|
||||||
## Ablauf und Symboltabellen
|
## Ablauf und Symboltabellen
|
||||||
|
|
||||||
@ -15,6 +16,9 @@ Eine zentrale Komponente des Typecheckers ist die Symboltabelle (symtab), die In
|
|||||||
- **Klassenkontext**:
|
- **Klassenkontext**:
|
||||||
Beim Typcheck einer Klasse wird eine initiale Symboltabelle erstellt, die die `this`-Referenz enthält. Dies geschieht in der Funktion `typeCheckClass`.
|
Beim Typcheck einer Klasse wird eine initiale Symboltabelle erstellt, die die `this`-Referenz enthält. Dies geschieht in der Funktion `typeCheckClass`.
|
||||||
|
|
||||||
|
- **Konstruktorkontext**:
|
||||||
|
Innerhalb eines Konstruktors wird die Symboltabelle um die Parameter des Konstruktors erweitert. Dies geschieht in `typeCheckConstructorDeclaration`. Der Rückgabetyp eines Konstruktors ist implizit `void`, was überprüft wird, um sicherzustellen, dass kein Wert zurückgegeben wird.
|
||||||
|
|
||||||
- **Methodenkontext**:
|
- **Methodenkontext**:
|
||||||
Innerhalb einer Methode wird die Symboltabelle um die Parameter der Methode erweitert sowie den Rückgabetyp der Methode, um die einzelnen Returns dagegen zu prüfen. Dies geschieht in `typeCheckMethodDeclaration`.
|
Innerhalb einer Methode wird die Symboltabelle um die Parameter der Methode erweitert sowie den Rückgabetyp der Methode, um die einzelnen Returns dagegen zu prüfen. Dies geschieht in `typeCheckMethodDeclaration`.
|
||||||
|
|
||||||
@ -30,7 +34,7 @@ Bei der Typprüfung von Referenzen (`typeCheckExpression` für Reference) wird z
|
|||||||
Ein zentraler Aspekt des Typecheckers ist die Fehlerbehandlung. Bei Typinkonsistenzen oder ungültigen Operationen werden aussagekräftige Fehlermeldungen generiert. Beispiele für solche Fehlermeldungen sind:
|
Ein zentraler Aspekt des Typecheckers ist die Fehlerbehandlung. Bei Typinkonsistenzen oder ungültigen Operationen werden aussagekräftige Fehlermeldungen generiert. Beispiele für solche Fehlermeldungen sind:
|
||||||
|
|
||||||
- **Typinkonsistenzen**:
|
- **Typinkonsistenzen**:
|
||||||
Wenn der Rückgabetyp einer Methode nicht mit dem deklarierten Rückgabetyp übereinstimmt. Oder aber auch die Anzahl der Parameter nicht übereinstimmt.
|
Wenn der Rückgabetyp einer Methode nicht mit dem deklarierten Rückgabetyp übereinstimmt oder die Anzahl der Parameter nicht übereinstimmt.
|
||||||
|
|
||||||
- **Ungültige Operationen**:
|
- **Ungültige Operationen**:
|
||||||
Wenn eine arithmetische Operation auf inkompatiblen Typen durchgeführt wird.
|
Wenn eine arithmetische Operation auf inkompatiblen Typen durchgeführt wird.
|
||||||
@ -53,3 +57,23 @@ Die Typprüfung eines Blocks erfolgt in `typeCheckStatement` für Block. Jede An
|
|||||||
### Rückgabeanweisungen
|
### Rückgabeanweisungen
|
||||||
|
|
||||||
Die Typprüfung einer Rückgabeanweisung (`typeCheckStatement` für Return) überprüft, ob der Rückgabewert der Anweisung mit dem deklarierten Rückgabetyp der Methode übereinstimmt. Dafür wurde zu Beginn der Methodentypprüfung der Rückgabetyp der Methode in die Symboltabelle eingetragen. Wenn der Rückgabewert `null` ist, wird überprüft, ob der deklarierte Rückgabetyp ein Objekttyp ist. Dies stellt sicher, dass Methoden immer den korrekten Typ zurückgeben. Generell wird bei der Prüfung nach dem UpperBound geschaut und ebenfalls wird nachgeschaut, ob, wenn der Rückgabetyp `Object` ist, der Return-Wert auch eine tatsächlich existierende Klasse ist, indem in die Klassentabelle geschaut wird.
|
Die Typprüfung einer Rückgabeanweisung (`typeCheckStatement` für Return) überprüft, ob der Rückgabewert der Anweisung mit dem deklarierten Rückgabetyp der Methode übereinstimmt. Dafür wurde zu Beginn der Methodentypprüfung der Rückgabetyp der Methode in die Symboltabelle eingetragen. Wenn der Rückgabewert `null` ist, wird überprüft, ob der deklarierte Rückgabetyp ein Objekttyp ist. Dies stellt sicher, dass Methoden immer den korrekten Typ zurückgeben. Generell wird bei der Prüfung nach dem UpperBound geschaut und ebenfalls wird nachgeschaut, ob, wenn der Rückgabetyp `Object` ist, der Return-Wert auch eine tatsächlich existierende Klasse ist, indem in die Klassentabelle geschaut wird.
|
||||||
|
|
||||||
|
### Konstruktorüberladung und -prüfung
|
||||||
|
|
||||||
|
Die Typprüfung unterstützt Konstruktorüberladung. Bei der Typprüfung von Konstruktoraufrufen (`typeCheckStatementExpression` für `ConstructorCall`) wird überprüft, ob es mehrere Konstruktoren mit derselben Anzahl von Parametern gibt. Falls mehrere passende Konstruktoren gefunden werden, wird ein Fehler gemeldet.
|
||||||
|
|
||||||
|
- **Parameterabgleich**:
|
||||||
|
Die Parameter eines Konstruktors werden gegen die Argumente des Aufrufs abgeglichen. Dies umfasst die Prüfung der Typen und, falls es sich um `null` handelt, die Überprüfung, ob der Parameter ein Objekttyp ist.
|
||||||
|
|
||||||
|
- **Fehlerbehandlung**:
|
||||||
|
Wenn kein passender Konstruktor gefunden wird, wird eine detaillierte Fehlermeldung generiert, die die erwarteten Signaturen und die tatsächlichen Argumenttypen anzeigt. Wenn mehrere passende Konstruktoren gefunden werden, wird ebenfalls ein Fehler gemeldet.
|
||||||
|
|
||||||
|
### Methodenüberladung und -prüfung
|
||||||
|
|
||||||
|
Die Typprüfung unterstützt auch Methodenüberladung. Bei der Typprüfung von Methodenaufrufen (`typeCheckStatementExpression` für `MethodCall`) wird überprüft, ob es mehrere Methoden mit demselben Namen, aber unterschiedlichen Parametertypen gibt.
|
||||||
|
|
||||||
|
- **Parameterabgleich**:
|
||||||
|
Die Parameter einer Methode werden gegen die Argumente des Aufrufs abgeglichen. Dies umfasst die Prüfung der Typen und, falls es sich um `null` handelt, die Überprüfung, ob der Parameter ein Objekttyp ist.
|
||||||
|
|
||||||
|
- **Fehlerbehandlung**:
|
||||||
|
Wenn keine passende Methode gefunden wird, wird eine detaillierte Fehlermeldung generiert, die die erwarteten Signaturen und die tatsächlichen Argumenttypen anzeigt. Wenn mehrere passende Methoden gefunden werden, wird ebenfalls ein Fehler gemeldet.
|
||||||
|
@ -42,10 +42,4 @@ test-suite tests
|
|||||||
Parser.JavaParser,
|
Parser.JavaParser,
|
||||||
Ast,
|
Ast,
|
||||||
TestLexer,
|
TestLexer,
|
||||||
TestParser,
|
TestParser
|
||||||
ByteCode.Util,
|
|
||||||
ByteCode.ByteUtil,
|
|
||||||
ByteCode.ClassFile,
|
|
||||||
ByteCode.Assembler,
|
|
||||||
ByteCode.Builder,
|
|
||||||
ByteCode.Constants
|
|
||||||
|
@ -6,8 +6,9 @@ type Identifier = String
|
|||||||
|
|
||||||
data ParameterDeclaration = ParameterDeclaration DataType Identifier deriving (Show, Eq)
|
data ParameterDeclaration = ParameterDeclaration DataType Identifier deriving (Show, Eq)
|
||||||
data VariableDeclaration = VariableDeclaration DataType Identifier (Maybe Expression) 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 MethodDeclaration = MethodDeclaration DataType Identifier [ParameterDeclaration] Statement deriving (Show, Eq)
|
||||||
data Class = Class DataType [MethodDeclaration] [VariableDeclaration] deriving (Show, Eq)
|
data ConstructorDeclaration = ConstructorDeclaration Identifier [ParameterDeclaration] Statement deriving (Show, Eq)
|
||||||
|
|
||||||
data Statement
|
data Statement
|
||||||
= If Expression Statement (Maybe Statement)
|
= If Expression Statement (Maybe Statement)
|
||||||
|
@ -92,10 +92,10 @@ qualifiedname : name DOT IDENTIFIER { BinaryOperation NameResolution $1 (Ref
|
|||||||
|
|
||||||
simplename : IDENTIFIER { $1 }
|
simplename : IDENTIFIER { $1 }
|
||||||
|
|
||||||
classdeclaration : CLASS IDENTIFIER classbody { case $3 of (methods, fields) -> Class $2 methods fields }
|
classdeclaration : CLASS IDENTIFIER classbody { case $3 of (constructors, methods, fields) -> Class $2 constructors methods fields }
|
||||||
| modifiers CLASS IDENTIFIER classbody { case $4 of (methods, fields) -> Class $3 methods fields }
|
| modifiers CLASS IDENTIFIER classbody { case $4 of (constructors, methods, fields) -> Class $3 constructors methods fields }
|
||||||
|
|
||||||
classbody : LBRACKET RBRACKET { ([], []) }
|
classbody : LBRACKET RBRACKET { ([], [], []) }
|
||||||
| LBRACKET classbodydeclarations RBRACKET { $2 }
|
| LBRACKET classbodydeclarations RBRACKET { $2 }
|
||||||
|
|
||||||
modifiers : modifier { }
|
modifiers : modifier { }
|
||||||
@ -103,13 +103,15 @@ modifiers : modifier { }
|
|||||||
|
|
||||||
classbodydeclarations : classbodydeclaration {
|
classbodydeclarations : classbodydeclaration {
|
||||||
case $1 of
|
case $1 of
|
||||||
MethodDecl method -> ([method], [])
|
ConstructorDecl constructor -> ([constructor], [], [])
|
||||||
FieldDecls fields -> ([], fields)
|
MethodDecl method -> ([], [method], [])
|
||||||
|
FieldDecls fields -> ([], [], fields)
|
||||||
}
|
}
|
||||||
| classbodydeclarations classbodydeclaration {
|
| classbodydeclarations classbodydeclaration {
|
||||||
case ($1, $2) of
|
case ($1, $2) of
|
||||||
((methods, fields), MethodDecl method) -> ((methods ++ [method]), fields)
|
((constructors, methods, fields), ConstructorDecl constructor) -> ((constructors ++ [constructor]), methods, fields)
|
||||||
((methods, fields), FieldDecls newFields) -> (methods, (fields ++ newFields))
|
((constructors, methods, fields), MethodDecl method) -> (constructors, (methods ++ [method]), fields)
|
||||||
|
((constructors, methods, fields), FieldDecls newFields) -> (constructors, methods, (fields ++ newFields))
|
||||||
}
|
}
|
||||||
|
|
||||||
modifier : PUBLIC { }
|
modifier : PUBLIC { }
|
||||||
@ -128,8 +130,8 @@ classorinterfacetype : simplename { $1 }
|
|||||||
classmemberdeclaration : fielddeclaration { $1 }
|
classmemberdeclaration : fielddeclaration { $1 }
|
||||||
| methoddeclaration { $1 }
|
| methoddeclaration { $1 }
|
||||||
|
|
||||||
constructordeclaration : constructordeclarator constructorbody { MethodDecl $ MethodDeclaration "void" "<init>" $1 $2 }
|
constructordeclaration : constructordeclarator constructorbody { case $1 of (identifier, parameters) -> ConstructorDecl $ ConstructorDeclaration identifier parameters $2 }
|
||||||
| modifiers constructordeclarator constructorbody { MethodDecl $ MethodDeclaration "void" "<init>" $2 $3 }
|
| modifiers constructordeclarator constructorbody { case $2 of (identifier, parameters) -> ConstructorDecl $ ConstructorDeclaration identifier parameters $3 }
|
||||||
|
|
||||||
fielddeclaration : type variabledeclarators SEMICOLON { FieldDecls $ map (convertDeclarator $1) $2 }
|
fielddeclaration : type variabledeclarators SEMICOLON { FieldDecls $ map (convertDeclarator $1) $2 }
|
||||||
| modifiers type variabledeclarators SEMICOLON { FieldDecls $ map (convertDeclarator $2) $3 }
|
| modifiers type variabledeclarators SEMICOLON { FieldDecls $ map (convertDeclarator $2) $3 }
|
||||||
@ -139,8 +141,8 @@ methoddeclaration : methodheader methodbody { case $1 of (returnType, (name, par
|
|||||||
block : LBRACKET RBRACKET { Block [] }
|
block : LBRACKET RBRACKET { Block [] }
|
||||||
| LBRACKET blockstatements RBRACKET { Block $2 }
|
| LBRACKET blockstatements RBRACKET { Block $2 }
|
||||||
|
|
||||||
constructordeclarator : simplename LBRACE RBRACE { [] }
|
constructordeclarator : simplename LBRACE RBRACE { ($1, []) }
|
||||||
| simplename LBRACE formalparameterlist RBRACE { $3 }
|
| simplename LBRACE formalparameterlist RBRACE { ($1, $3) }
|
||||||
|
|
||||||
constructorbody : LBRACKET RBRACKET { Block [] }
|
constructorbody : LBRACKET RBRACKET { Block [] }
|
||||||
-- | LBRACKET explicitconstructorinvocation RBRACKET { }
|
-- | LBRACKET explicitconstructorinvocation RBRACKET { }
|
||||||
@ -385,6 +387,7 @@ multiplicativeexpression : unaryexpression { $1 }
|
|||||||
{
|
{
|
||||||
|
|
||||||
data MethodOrFieldDeclaration = MethodDecl MethodDeclaration
|
data MethodOrFieldDeclaration = MethodDecl MethodDeclaration
|
||||||
|
| ConstructorDecl ConstructorDeclaration
|
||||||
| FieldDecls [VariableDeclaration]
|
| FieldDecls [VariableDeclaration]
|
||||||
|
|
||||||
data Declarator = Declarator Identifier (Maybe Expression)
|
data Declarator = Declarator Identifier (Maybe Expression)
|
||||||
|
156
src/Typecheck.hs
156
src/Typecheck.hs
@ -3,18 +3,35 @@ import Data.List (find)
|
|||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Ast
|
import Ast
|
||||||
|
|
||||||
|
|
||||||
typeCheckCompilationUnit :: CompilationUnit -> CompilationUnit
|
typeCheckCompilationUnit :: CompilationUnit -> CompilationUnit
|
||||||
typeCheckCompilationUnit classes = map (`typeCheckClass` classes) classes
|
typeCheckCompilationUnit classes = map (`typeCheckClass` classes) classes
|
||||||
|
|
||||||
typeCheckClass :: Class -> [Class] -> Class
|
typeCheckClass :: Class -> [Class] -> Class
|
||||||
typeCheckClass (Class className methods fields) classes =
|
typeCheckClass (Class className constructors methods fields) classes =
|
||||||
let
|
let
|
||||||
-- Fields and methods dont need to be added to the symtab because they are looked upon automatically under "this"
|
-- 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.
|
-- if its not a declared local variable. Also shadowing wouldnt be possible then.
|
||||||
initalSymTab = [("this", className)]
|
initalSymTab = [("this", className)]
|
||||||
|
checkedConstructors = map (\constructor -> typeCheckConstructorDeclaration constructor initalSymTab classes) constructors
|
||||||
checkedMethods = map (\method -> typeCheckMethodDeclaration method initalSymTab classes) methods
|
checkedMethods = map (\method -> typeCheckMethodDeclaration method initalSymTab classes) methods
|
||||||
checkedFields = map (\field -> typeCheckVariableDeclaration field initalSymTab classes) fields
|
checkedFields = map (\field -> typeCheckVariableDeclaration field initalSymTab classes) fields
|
||||||
in Class className checkedMethods checkedFields
|
in Class className checkedConstructors checkedMethods checkedFields
|
||||||
|
|
||||||
|
typeCheckConstructorDeclaration :: ConstructorDeclaration -> [(Identifier, DataType)] -> [Class] -> ConstructorDeclaration
|
||||||
|
typeCheckConstructorDeclaration (ConstructorDeclaration name params body) symtab classes =
|
||||||
|
let
|
||||||
|
constructorParams = [(identifier, dataType) | ParameterDeclaration dataType identifier <- params]
|
||||||
|
initialSymtab = symtab ++ constructorParams
|
||||||
|
className = fromMaybe (error "Constructor Declaration: 'this' not found in symtab") (lookup "this" symtab)
|
||||||
|
checkedBody = typeCheckStatement body initialSymtab classes
|
||||||
|
bodyType = getTypeFromStmt checkedBody
|
||||||
|
in if name == className
|
||||||
|
then if bodyType == "void"
|
||||||
|
then ConstructorDeclaration 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 -> [(Identifier, DataType)] -> [Class] -> MethodDeclaration
|
||||||
typeCheckMethodDeclaration (MethodDeclaration retType name params body) symtab classes =
|
typeCheckMethodDeclaration (MethodDeclaration retType name params body) symtab classes =
|
||||||
@ -63,9 +80,9 @@ typeCheckExpression (Reference id) symtab classes =
|
|||||||
Nothing ->
|
Nothing ->
|
||||||
case lookup "this" symtab of
|
case lookup "this" symtab of
|
||||||
Just className ->
|
Just className ->
|
||||||
let classDetails = find (\(Class name _ _) -> name == className) classes
|
let classDetails = find (\(Class name _ _ _) -> name == className) classes
|
||||||
in case classDetails of
|
in case classDetails of
|
||||||
Just (Class _ _ fields) ->
|
Just (Class _ _ _ fields) ->
|
||||||
let fieldTypes = [dt | VariableDeclaration dt fieldId _ <- fields, fieldId == id]
|
let fieldTypes = [dt | VariableDeclaration dt 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
|
-- 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
|
in case fieldTypes of
|
||||||
@ -139,70 +156,70 @@ typeCheckStatementExpression (Assignment ref expr) symtab classes =
|
|||||||
|
|
||||||
|
|
||||||
typeCheckStatementExpression (ConstructorCall className args) symtab classes =
|
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."
|
Nothing -> error $ "Class '" ++ className ++ "' not found."
|
||||||
Just (Class _ methods _) ->
|
Just (Class _ constructors _ _) ->
|
||||||
-- Find constructor matching the class name with void return type
|
let
|
||||||
case find (\(MethodDeclaration _ name params _) -> name == "<init>") methods of
|
matchParams (ParameterDeclaration paramType _) arg =
|
||||||
-- If no constructor is found, assume standard constructor with no parameters
|
let argTyped = typeCheckExpression arg symtab classes
|
||||||
Nothing ->
|
argType = getTypeFromExpr argTyped
|
||||||
if null args then
|
in if argType == "null" && isObjectType paramType
|
||||||
TypedStatementExpression className (ConstructorCall className args)
|
then Just (TypedExpression paramType NullLiteral)
|
||||||
else
|
else if argType == paramType
|
||||||
error $ "No valid constructor found for class '" ++ className ++ "', but arguments were provided."
|
then Just argTyped
|
||||||
Just (MethodDeclaration _ _ params _) ->
|
else Nothing
|
||||||
let args' = zipWith
|
|
||||||
(\arg (ParameterDeclaration paramType _) ->
|
matchConstructor (ConstructorDeclaration name params _) =
|
||||||
let argTyped = typeCheckExpression arg symtab classes
|
let matchedArgs = sequence $ zipWith matchParams params args
|
||||||
in if getTypeFromExpr argTyped == "null" && isObjectType paramType
|
in fmap (\checkedArgs -> (params, checkedArgs)) matchedArgs
|
||||||
then TypedExpression paramType NullLiteral
|
|
||||||
else argTyped
|
validConstructors = filter (\(params, _) -> length params == length args) $ mapMaybe matchConstructor constructors
|
||||||
) args params
|
|
||||||
expectedTypes = [dataType | ParameterDeclaration dataType _ <- params]
|
expectedSignatures = [ map (\(ParameterDeclaration t _) -> t) params | ConstructorDeclaration _ params _ <- constructors ]
|
||||||
argTypes = map getTypeFromExpr args'
|
actualSignature = map (\arg -> getTypeFromExpr (typeCheckExpression arg symtab classes)) args
|
||||||
typeMatches = zipWith
|
mismatchDetails = "Constructor not found for class '" ++ className ++ "' with given arguments.\n" ++
|
||||||
(\expType argType -> (expType == argType || (argType == "null" && isObjectType expType), expType, argType))
|
"Expected signatures:\n" ++ show expectedSignatures ++
|
||||||
expectedTypes argTypes
|
"\nActual arguments:" ++ show actualSignature
|
||||||
mismatches = filter (not . fst3) typeMatches
|
|
||||||
fst3 (a, _, _) = a
|
in case validConstructors of
|
||||||
in
|
[(_, checkedArgs)] ->
|
||||||
if null mismatches && length args == length params then
|
TypedStatementExpression className (ConstructorCall className checkedArgs)
|
||||||
TypedStatementExpression className (ConstructorCall className args')
|
[] -> error mismatchDetails
|
||||||
else if not (null mismatches) then
|
_ -> error $ "Multiple matching constructors found for class '" ++ className ++ "' with given arguments."
|
||||||
error $ unlines $ ("Type mismatch in constructor arguments for class '" ++ className ++ "':")
|
|
||||||
: [ "Expected: " ++ expType ++ ", Found: " ++ argType | (_, expType, argType) <- mismatches ]
|
|
||||||
else
|
|
||||||
error $ "Incorrect number of arguments for constructor of class '" ++ className ++ "'. Expected " ++ show (length expectedTypes) ++ ", found " ++ show (length args) ++ "."
|
|
||||||
|
|
||||||
typeCheckStatementExpression (MethodCall expr methodName args) symtab classes =
|
typeCheckStatementExpression (MethodCall expr methodName args) symtab classes =
|
||||||
let objExprTyped = typeCheckExpression expr symtab classes
|
let objExprTyped = typeCheckExpression expr symtab classes
|
||||||
in case objExprTyped of
|
in case objExprTyped of
|
||||||
TypedExpression objType _ ->
|
TypedExpression objType _ ->
|
||||||
case find (\(Class className _ _) -> className == objType) classes of
|
case find (\(Class className _ _ _) -> className == objType) classes of
|
||||||
Just (Class _ methods _) ->
|
Just (Class _ _ methods _) ->
|
||||||
case find (\(MethodDeclaration retType name params _) -> name == methodName) methods of
|
let matchParams (ParameterDeclaration paramType _) arg =
|
||||||
Just (MethodDeclaration retType _ params _) ->
|
let argTyped = typeCheckExpression arg symtab classes
|
||||||
let args' = zipWith
|
argType = getTypeFromExpr argTyped
|
||||||
(\arg (ParameterDeclaration paramType _) ->
|
in if argType == "null" && isObjectType paramType
|
||||||
let argTyped = typeCheckExpression arg symtab classes
|
then Just (TypedExpression paramType NullLiteral)
|
||||||
in if getTypeFromExpr argTyped == "null" && isObjectType paramType
|
else if argType == paramType
|
||||||
then TypedExpression paramType NullLiteral
|
then Just argTyped
|
||||||
else argTyped
|
else Nothing
|
||||||
) args params
|
|
||||||
expectedTypes = [dataType | ParameterDeclaration dataType _ <- params]
|
matchMethod (MethodDeclaration retType name params _) =
|
||||||
argTypes = map getTypeFromExpr args'
|
let matchedArgs = sequence $ zipWith matchParams params args
|
||||||
typeMatches = zipWith
|
in fmap (\checkedArgs -> (MethodDeclaration retType name params (Block []), checkedArgs)) matchedArgs
|
||||||
(\expType argType -> (expType == argType || (argType == "null" && isObjectType expType), expType, argType))
|
|
||||||
expectedTypes argTypes
|
validMethods = filter (\(MethodDeclaration _ name params _, _) -> name == methodName && length params == length args) $ mapMaybe matchMethod methods
|
||||||
mismatches = filter (not . fst3) typeMatches
|
|
||||||
fst3 (a, _, _) = a
|
expectedSignatures = [ map (\(ParameterDeclaration t _) -> t) params | MethodDeclaration _ name params _ <- methods, name == methodName ]
|
||||||
in if null mismatches && length args == length params
|
actualSignature = map (\arg -> getTypeFromExpr (typeCheckExpression arg symtab classes)) args
|
||||||
then TypedStatementExpression retType (MethodCall objExprTyped methodName args')
|
mismatchDetails = "Method not found for class '" ++ objType ++ "' with given arguments.\n" ++
|
||||||
else if not (null mismatches)
|
"Expected signatures for method '" ++ methodName ++ "':\n" ++ unlines (map show expectedSignatures) ++
|
||||||
then error $ unlines $ ("Argument type mismatches for method '" ++ methodName ++ "':")
|
"Actual arguments:\n" ++ show actualSignature
|
||||||
: [ "Expected: " ++ expType ++ ", Found: " ++ argType | (_, expType, argType) <- mismatches ]
|
|
||||||
else error $ "Incorrect number of arguments for method '" ++ methodName ++ "'. Expected " ++ show (length expectedTypes) ++ ", found " ++ show (length args) ++ "."
|
in case validMethods of
|
||||||
Nothing -> error $ "Method '" ++ methodName ++ "' not found in class '" ++ objType ++ "'."
|
[(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."
|
Nothing -> error $ "Class for object type '" ++ objType ++ "' not found."
|
||||||
_ -> error "Invalid object type for method call. Object must have a class type."
|
_ -> error "Invalid object type for method call. Object must have a class type."
|
||||||
|
|
||||||
@ -355,7 +372,7 @@ isSubtype subType superType classes
|
|||||||
| otherwise = False
|
| otherwise = False
|
||||||
|
|
||||||
isUserDefinedClass :: DataType -> [Class] -> Bool
|
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 :: DataType -> Bool
|
||||||
isObjectType dt = dt /= "int" && dt /= "boolean" && dt /= "char"
|
isObjectType dt = dt /= "int" && dt /= "boolean" && dt /= "char"
|
||||||
@ -408,9 +425,14 @@ checkComparisonOperation op expr1' expr2' type1 type2
|
|||||||
|
|
||||||
checkEqualityOperation :: BinaryOperator -> Expression -> Expression -> DataType -> DataType -> Expression
|
checkEqualityOperation :: BinaryOperator -> Expression -> Expression -> DataType -> DataType -> Expression
|
||||||
checkEqualityOperation op expr1' expr2' type1 type2
|
checkEqualityOperation op expr1' expr2' type1 type2
|
||||||
| type1 == type2 =
|
| type1 == type2 || (type1 == "null" && isObjectType type2) || (type2 == "null" && isObjectType type1) =
|
||||||
TypedExpression "boolean" (BinaryOperation op expr1' expr2')
|
TypedExpression "boolean" (BinaryOperation op expr1' expr2')
|
||||||
| otherwise = error $ "Equality operation " ++ show op ++ " requires operands of the same type"
|
| type1 /= type2 =
|
||||||
|
error $ "Equality operation " ++ show op ++ " requires operands of the same type. Found types: " ++ type1 ++ " and " ++ type2
|
||||||
|
| (type1 == "null" && not (isObjectType type2)) || (type2 == "null" && not (isObjectType type1)) =
|
||||||
|
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 :: BinaryOperator -> Expression -> Expression -> DataType -> DataType -> Expression
|
||||||
checkLogicalOperation op expr1' expr2' type1 type2
|
checkLogicalOperation op expr1' expr2' type1 type2
|
||||||
@ -422,8 +444,8 @@ resolveNameResolution :: Expression -> Expression -> [(Identifier, DataType)] ->
|
|||||||
resolveNameResolution expr1' (Reference ident2) symtab classes =
|
resolveNameResolution expr1' (Reference ident2) symtab classes =
|
||||||
case getTypeFromExpr expr1' of
|
case getTypeFromExpr expr1' of
|
||||||
objType ->
|
objType ->
|
||||||
case find (\(Class className _ _) -> className == objType) classes of
|
case find (\(Class className _ _ _) -> className == objType) classes of
|
||||||
Just (Class _ _ fields) ->
|
Just (Class _ _ _ fields) ->
|
||||||
let fieldTypes = [dt | VariableDeclaration dt id _ <- fields, id == ident2]
|
let fieldTypes = [dt | VariableDeclaration dt id _ <- fields, id == ident2]
|
||||||
in case fieldTypes of
|
in case fieldTypes of
|
||||||
[resolvedType] -> TypedExpression resolvedType (BinaryOperation NameResolution expr1' (TypedExpression resolvedType (FieldVariable ident2)))
|
[resolvedType] -> TypedExpression resolvedType (BinaryOperation NameResolution expr1' (TypedExpression resolvedType (FieldVariable ident2)))
|
||||||
|
Loading…
Reference in New Issue
Block a user