expressions = new ArrayList<>();
public MethodCallStatementExpressionNode(TargetNode target, String identifier) {
this.target = target;
@@ -21,9 +24,13 @@ public class MethodCallStatementExpressionNode implements ASTNode {
chainedMethods.add(chainedMethode);
}
- public void addExpression(ExpressionNode expression) {
+ public void addExpression(IExpressionNode expression) {
expressions.add(expression);
}
+ @Override
+ public TypeCheckResult accept(SemanticVisitor visitor) {
+ return visitor.analyze(this);
+ }
}
diff --git a/src/main/java/ast/statement/statementexpression/methodcallstatementnexpression/TargetNode.java b/src/main/java/ast/statement/statementexpression/methodcallstatementnexpression/TargetNode.java
index d8c0c54..7a18125 100644
--- a/src/main/java/ast/statement/statementexpression/methodcallstatementnexpression/TargetNode.java
+++ b/src/main/java/ast/statement/statementexpression/methodcallstatementnexpression/TargetNode.java
@@ -5,10 +5,10 @@ import ast.expression.unaryexpression.MemberAccessNode;
import ast.statement.statementexpression.NewDeclarationStatementExpressionNode;
public class TargetNode implements ASTNode {
- Boolean thisTar;
- MemberAccessNode memberAccess;
- NewDeclarationStatementExpressionNode newDeclaration;
- String identifier;
+ public Boolean thisTar;
+ public MemberAccessNode memberAccess;
+ public NewDeclarationStatementExpressionNode newDeclaration;
+ public String identifier;
public TargetNode(Boolean thisTar) {
this.thisTar = thisTar;
diff --git a/src/main/java/ast/type/EnumTypeNode.java b/src/main/java/ast/type/EnumTypeNode.java
deleted file mode 100644
index 98c38ea..0000000
--- a/src/main/java/ast/type/EnumTypeNode.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package ast.type;
-
-public enum EnumTypeNode {
- INT, BOOLEAN, CHAR, IDENTIFIER
-}
diff --git a/src/main/java/ast/type/TypeNode.java b/src/main/java/ast/type/TypeNode.java
deleted file mode 100644
index 8a831cc..0000000
--- a/src/main/java/ast/type/TypeNode.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package ast.type;
-
-public class TypeNode {
- public EnumTypeNode type;
-
- public TypeNode(String type) {
- setType(type);
- }
-
- private void setType(String type) {
- switch(type) {
- case "int":
- this.type = EnumTypeNode.INT;
- break;
- case "boolean":
- this.type = EnumTypeNode.BOOLEAN;
- break;
- case "char":
- this.type = EnumTypeNode.CHAR;
- break;
- default:
- this.type = EnumTypeNode.IDENTIFIER;
- }
- }
-}
diff --git a/src/main/java/ast/type/ValueNode.java b/src/main/java/ast/type/ValueNode.java
index d188006..b51f799 100644
--- a/src/main/java/ast/type/ValueNode.java
+++ b/src/main/java/ast/type/ValueNode.java
@@ -3,8 +3,8 @@ package ast.type;
import ast.ASTNode;
public class ValueNode implements ASTNode {
- EnumValueNode valueType;
- String value;
+ public EnumValueNode valueType;
+ public String value;
public ValueNode(EnumValueNode valueType, String value) {
this.valueType = valueType;
diff --git a/src/main/java/ast/type/type/BaseType.java b/src/main/java/ast/type/type/BaseType.java
new file mode 100644
index 0000000..fdfb293
--- /dev/null
+++ b/src/main/java/ast/type/type/BaseType.java
@@ -0,0 +1,30 @@
+package ast.type.type;
+
+public class BaseType implements ITypeNode {
+
+ private TypeEnum typeEnum;
+
+ public BaseType(TypeEnum typeEnum) {
+ this.typeEnum = typeEnum;
+ }
+
+ public TypeEnum getTypeEnum() {
+ return typeEnum;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ BaseType other = (BaseType) obj;
+ if (typeEnum != other.typeEnum)
+ return false;
+ return true;
+ }
+
+
+}
diff --git a/src/main/java/ast/type/type/ITypeNode.java b/src/main/java/ast/type/type/ITypeNode.java
new file mode 100644
index 0000000..e449e3c
--- /dev/null
+++ b/src/main/java/ast/type/type/ITypeNode.java
@@ -0,0 +1,5 @@
+package ast.type.type;
+
+public interface ITypeNode {
+
+}
diff --git a/src/main/java/ast/type/type/ReferenceType.java b/src/main/java/ast/type/type/ReferenceType.java
new file mode 100644
index 0000000..2292046
--- /dev/null
+++ b/src/main/java/ast/type/type/ReferenceType.java
@@ -0,0 +1,33 @@
+package ast.type.type;
+
+public class ReferenceType implements ITypeNode{
+
+ private String identifier;
+
+ public ReferenceType(String identifier) {
+ this.identifier = identifier;
+ }
+
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ReferenceType other = (ReferenceType) obj;
+ if (identifier == null) {
+ if (other.identifier != null)
+ return false;
+ } else if (!identifier.equals(other.identifier))
+ return false;
+ return true;
+ }
+
+
+}
diff --git a/src/main/java/ast/type/type/TypeEnum.java b/src/main/java/ast/type/type/TypeEnum.java
new file mode 100644
index 0000000..d46fac3
--- /dev/null
+++ b/src/main/java/ast/type/type/TypeEnum.java
@@ -0,0 +1,9 @@
+package ast.type.type;
+
+public enum TypeEnum {
+ VOID,
+ INT,
+ CHAR,
+ BOOL;
+
+}
diff --git a/src/main/java/bytecode/ClassCodeGen.java b/src/main/java/bytecode/ClassCodeGen.java
index 79011b8..7f544d9 100644
--- a/src/main/java/bytecode/ClassCodeGen.java
+++ b/src/main/java/bytecode/ClassCodeGen.java
@@ -9,7 +9,6 @@ import bytecode.visitor.ClassVisitor;
import java.io.File;
import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.Opcodes;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -30,8 +29,8 @@ public class ClassCodeGen implements ClassVisitor {
@Override
public void visit(ClassNode classNode) {
classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
- classWriter.visit(Opcodes.V1_5, mapper.mapAccessTypeToOpcode(classNode.accessType), classNode.identifier, null,
- "java/lang/Object", null);
+// classWriter.visit(Opcodes.V1_5, mapper.mapAccessTypeToOpcode(classNode.accessType), classNode.identifier, null,
+// "java/lang/Object", null);
for (MemberNode memberNode : classNode.members) {
if (memberNode instanceof FieldNode) {
@@ -62,7 +61,7 @@ public class ClassCodeGen implements ClassVisitor {
directory.mkdirs();
}
- String filePath = directoryPath + "/" + name + ".class";
+ String filePath = outputDirectoryPath + "/" + name + ".class";
try {
FileOutputStream fileOutputStream = new FileOutputStream(filePath);
fileOutputStream.write(byteCode);
diff --git a/src/main/java/bytecode/Mapper.java b/src/main/java/bytecode/Mapper.java
index aff0c5e..49ad62b 100644
--- a/src/main/java/bytecode/Mapper.java
+++ b/src/main/java/bytecode/Mapper.java
@@ -1,6 +1,5 @@
package bytecode;
-import ast.parameter.ParameterNode;
import ast.type.*;
import org.objectweb.asm.Opcodes;
@@ -31,19 +30,19 @@ public class Mapper {
return descriptor;
}
- public String getTypeChar(EnumTypeNode enumTypeNode) {
- String typeChar = "";
- switch (enumTypeNode) {
- case EnumTypeNode.INT:
- typeChar = "I";
- break;
- case EnumTypeNode.CHAR:
- typeChar = "C";
- break;
- case EnumTypeNode.BOOLEAN:
- typeChar = "Z";
- break;
- }
- return typeChar;
- }
+// public String getTypeChar(TypeEnum enumTypeNode) {
+// String typeChar = "";
+// switch (enumTypeNode) {
+// case TypeEnum.INT:
+// typeChar = "I";
+// break;
+// case TypeEnum.CHAR:
+// typeChar = "C";
+// break;
+// case TypeEnum.BOOLEAN:
+// typeChar = "Z";
+// break;
+// }
+// return typeChar;
+// }
}
diff --git a/src/main/java/bytecode/visitor/ClassVisitor.java b/src/main/java/bytecode/visitor/ClassVisitor.java
index 903775a..078f211 100644
--- a/src/main/java/bytecode/visitor/ClassVisitor.java
+++ b/src/main/java/bytecode/visitor/ClassVisitor.java
@@ -4,6 +4,7 @@ import ast.ClassNode;
import ast.member.FieldNode;
public interface ClassVisitor {
- void visit(ClassNode classNode);
- void visit(FieldNode fieldNode);
+ void visit(ClassNode classNode);
+
+ void visit(FieldNode fieldNode);
}
diff --git a/src/main/java/classFileOutput/Example.class b/src/main/java/classFileOutput/Example.class
deleted file mode 100644
index e69de29..0000000
diff --git a/src/main/java/classFileOutput/Test.class b/src/main/java/classFileOutput/Test.class
deleted file mode 100644
index 98f2799..0000000
Binary files a/src/main/java/classFileOutput/Test.class and /dev/null differ
diff --git a/src/main/java/main/Main.java b/src/main/java/main/Main.java
new file mode 100644
index 0000000..69de2bc
--- /dev/null
+++ b/src/main/java/main/Main.java
@@ -0,0 +1,108 @@
+package main;
+
+import ast.ASTNode;
+import ast.ProgramNode;
+import parser.astBuilder.ASTBuilder;
+import parser.generated.SimpleJavaLexer;
+import parser.generated.SimpleJavaParser;
+import semantic.SemanticAnalyzer;
+import bytecode.ByteCodeGenerator;
+
+import org.antlr.v4.runtime.*;
+import org.antlr.v4.runtime.tree.ParseTree;
+
+import java.io.IOException;
+import java.nio.file.Paths;
+
+
+/**
+ * Start Raupenpiler using make:
+ * cd .\src\test\
+ *
make clean compile-raupenpiler
+ *
Start Raupenpiler using jar:
+ *
java.exe -jar path_to_jar\JavaCompiler-1.0-SNAPSHOT-jar-with-dependencies.jar 'path_to_input_file.java' 'path_to_output_directory'
+ *
Example (jar needs to be in the target directory, compile with make or mvn package first):
+ * java.exe -jar .\target\JavaCompiler-1.0-SNAPSHOT-jar-with-dependencies.jar 'src/main/resources/input/CompilerInput.java' 'src/main/resources/output'
+ */
+public class Main {
+
+
+ public static void main(String[] args) throws Exception {
+ if (args.length == 2) {
+ // args[0] is the input file path
+ // args[1] is the output directory path
+ String inputFilePath = args[0];
+ String outputDirectoryPath = args[1];
+ System.out.println("Compiling file: " + inputFilePath);
+ try {
+ CharStream inputCharStream = CharStreams.fromPath(Paths.get(inputFilePath));
+ compileFile(inputCharStream, outputDirectoryPath);
+ } catch (IOException e) {
+ System.err.println("Error reading the file: " + e.getMessage());
+ }
+ }
+ /* !!! Else Branch (main ohne args starten) ist nicht zur Verwendung vorgesehen, immer mit args starten !!!
+ else {
+ try {
+ CharStream codeCharStream = CharStreams.fromPath(Paths.get("src/main/resources/input/CompilerInput.java"));
+ compileFile(codeCharStream);
+ } catch (IOException e) {
+ System.err.println("Error reading the file: " + e.getMessage());
+ }
+ }
+ */
+ }
+
+ /**
+ * This method is used to compile a file from a given CharStream and output the bytecode to a specified directory.
+ * It goes through the following steps:
+ *
1. Scanner: It uses the SimpleJavaLexer to tokenize the input CharStream.
+ *
2. Parser: It uses the SimpleJavaParser to parse the tokens and generate a ParseTree.
+ *
3. AST Builder: It uses the ASTBuilder to visit the ParseTree and generate an Abstract Syntax Tree (AST).
+ *
4. Semantic Analyzer: It uses the SemanticAnalyzer to generate a typed AST.
+ *
5. Bytecode Generator: It uses the ByteCodeGenerator to generate bytecode from the typed AST and output it to the specified directory.
+ *
+ * @param inputCharStream The CharStream representing the input file to be compiled.
+ * @param outputDirectoryPath The path of the directory where the output bytecode should be written.
+ */
+ static void compileFile(CharStream inputCharStream, String outputDirectoryPath) {
+ // Initialize the logger
+ new RaupenLogger();
+
+ /* ------------------------- Scanner -> tokens ------------------------- */
+ // Use the SimpleJavaLexer to tokenize the input CharStream
+ SimpleJavaLexer lexer = new SimpleJavaLexer(inputCharStream);
+ CommonTokenStream tokenStream = new CommonTokenStream(lexer);
+ tokenStream.fill();
+ // Log the tokens
+ RaupenLogger.logScanner(tokenStream);
+
+ /*------------------------- Parser -> Parsetree -------------------------*/
+ // Use the SimpleJavaParser to parse the tokens and generate a ParseTree
+ SimpleJavaParser parser = new SimpleJavaParser(tokenStream);
+ ParseTree parseTree = parser.program(); // parse the input
+ // Log the ParseTree
+ RaupenLogger.logParser(parseTree, parser);
+
+ /*------------------------- AST builder -> AST -------------------------*/
+ // Use the ASTBuilder to visit the ParseTree and generate an Abstract Syntax Tree (AST)
+ ASTBuilder astBuilder = new ASTBuilder();
+ ASTNode abstractSyntaxTree = astBuilder.visit(parseTree);
+ // Log the AST
+ RaupenLogger.logAST(abstractSyntaxTree);
+
+ /*------------------------- Semantic Analyzer -> typed AST -------------------------*/
+ // Use the SemanticAnalyzer to generate a typed AST
+ ASTNode typedAst = SemanticAnalyzer.generateTast(abstractSyntaxTree);
+ // Log the typed AST
+ RaupenLogger.logSemanticAnalyzer(typedAst);
+
+ /*------------------------- Bytecode Generator -> Bytecode -------------------------*/
+ // Use the ByteCodeGenerator to generate bytecode from the typed AST and output it to the specified directory
+ ByteCodeGenerator byteCodeGenerator = new ByteCodeGenerator(outputDirectoryPath);
+ assert typedAst != null;
+ byteCodeGenerator.visit((ProgramNode) typedAst);
+ // Log the bytecode generation
+ RaupenLogger.logBytecodeGenerator();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/main/RaupenLogger.java b/src/main/java/main/RaupenLogger.java
new file mode 100644
index 0000000..4ea499f
--- /dev/null
+++ b/src/main/java/main/RaupenLogger.java
@@ -0,0 +1,180 @@
+package main;
+
+import ast.ASTNode;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.Parser;
+import org.antlr.v4.runtime.RuleContext;
+import org.antlr.v4.runtime.Token;
+import org.antlr.v4.runtime.tree.ParseTree;
+import parser.generated.SimpleJavaLexer;
+import parser.generated.SimpleJavaParser;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.logging.*;
+
+/**
+ Beispiel für Logging-Arten:
+
logger.severe("Schwerwiegender Fehler");
+
logger.warning("Warnung");
+
logger.info("Information");
+
logger.config("Konfigurationshinweis");
+
logger.fine("Fein");
+
logger.finer("Feiner");
+
logger.finest("Am feinsten");
+
You may toggle the logging level of the console and file handlers by
+ changing the level ALL/OFF/etc. in the constructor.
+ consoleHandler.setLevel(Level.OFF);
+ fileHandler.setLevel(Level.ALL);
+ */
+public class RaupenLogger {
+
+ static Logger logger = Logger.getLogger("RaupenLogs");
+
+ public RaupenLogger() {
+ // ------------------------- Logging -------------------------
+ logger.setLevel(Level.ALL);
+ logger.getParent().getHandlers()[0].setLevel(Level.ALL);
+ logger.setUseParentHandlers(false);
+
+ // Custom formatter class
+ class CustomFormatter extends Formatter {
+ private final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss dd-MM-yyyy");
+
+ @Override
+ public String format(LogRecord record) {
+ return formatMessage(record) + System.lineSeparator();
+ }
+
+ @Override
+ public String getHead(Handler h) {
+ Date now = new Date();
+ String dateTime = dateFormat.format(now);
+ return "Log Start Time: " + dateTime + "\n"
+ + "Logger Name: " + h.getFormatter().getClass().getName() + "\n\n";
+ }
+ }
+
+ try {
+ // Configure console handler
+ Handler consoleHandler = new ConsoleHandler();
+ // Toggle console logging on/off
+ consoleHandler.setLevel(Level.OFF);
+ consoleHandler.setFormatter(new CustomFormatter());
+ logger.addHandler(consoleHandler);
+
+ // Configure file handler
+ Handler fileHandler = new FileHandler("src/main/resources/logs/RaupenLog.log");
+ // Toggle file logging on/off
+ fileHandler.setLevel(Level.ALL);
+ fileHandler.setFormatter(new CustomFormatter());
+ logger.addHandler(fileHandler);
+
+ } catch (SecurityException | IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void logScanner(CommonTokenStream tokenStream) {
+ // Printing the tokens
+ logger.info("-------------------- Scanner -> Tokens --------------------");
+ List tokens = tokenStream.getTokens();
+ for (Token token : tokens) {
+ String tokenType =
+ SimpleJavaLexer.VOCABULARY.getSymbolicName(token.getType());
+ String tokenText = token.getText();
+ // logger.info("Token Type: " + tokenType + ", Token Text: " + tokenText);
+ logger.info(tokenType + " " + tokenText);
+ }
+ logger.info("\n");
+ }
+
+ public static void logParser(ParseTree parseTree, SimpleJavaParser parser) {
+ // Printing the parse tree
+ logger.info("-------------------- Parser -> Parsetree --------------------");
+ logger.info(parseTree.toStringTree(parser)); //one line representation
+ logTree(parseTree, parser, 0);
+ logger.info("\n");
+ }
+
+ public static void logAST(ASTNode abstractSyntaxTree) {
+ // Printing the AST
+ logger.info("-------------------- AST builder -> AST --------------------");
+ // logger.info("AST: " + ast.toString());
+ logAST(abstractSyntaxTree, 0);
+ logger.info("\n");
+ }
+
+ public static void logSemanticAnalyzer(ASTNode typedAst) {
+ // Printing the typed AST
+ logger.info("-------------------- Semantic Analyzer -> typed AST --------------------");
+ logAST(typedAst, 0);
+ logger.info("\n");
+ }
+
+ public static void logBytecodeGenerator() {
+ // Printing the bytecode
+ logger.info("-------------------- Bytecode Generator -> Bytecode --------------------");
+ logger.info("Bytecode generated");
+ logger.info("\n");
+ }
+
+
+
+
+
+
+
+
+
+ /* ------------------------- Printing methods ------------------------- */
+
+ /**
+ * This method is used to print the parse tree in a structured format.
+ * It recursively traverses the tree and prints the rule names and text of the
+ * nodes.
+ *
+ * @param tree The parse tree to be printed.
+ * @param parser The parser used to parse the input. It's used to get the rule
+ * names.
+ * @param indent The current indentation level. It's used to format the output.
+ */
+ public static void logTree(ParseTree tree, Parser parser, int indent) {
+ // Create an indentation string based on the current indentation level
+ String indentString = " ".repeat(indent * 2);
+
+ // If the tree node is an instance of RuleContext (i.e., it's an internal node),
+ // print the rule name
+ if (tree instanceof RuleContext) {
+ int ruleIndex = ((RuleContext) tree).getRuleIndex();
+ String ruleName = parser.getRuleNames()[ruleIndex];
+ logger.info(indentString + ruleName);
+ } else {
+ // If the tree node is not an instance of RuleContext (i.e., it's a leaf node),
+ // print the text of the node
+ logger.info(indentString + tree.getText());
+ }
+
+ // Recursively print the children of the current node, increasing the
+ // indentation level
+ for (int i = 0; i < tree.getChildCount(); i++) {
+ logTree(tree.getChild(i), parser, indent + 1);
+ }
+ }
+
+ // TODO: Fix this method
+ public static void logAST(ASTNode abstractSyntaxTree, int indent) {
+ if (abstractSyntaxTree == null) {
+ logger.severe("AST is null !!!");
+ return;
+ }
+ String indentString = " ".repeat(indent * 2);
+ logger.info(indentString + abstractSyntaxTree.getClass());
+
+ // for (ASTNode child : node.) {
+ // printAST(child, indent + 1);
+ // }
+ }
+}
diff --git a/src/main/java/parser/astBuilder/ASTBuilder.java b/src/main/java/parser/astBuilder/ASTBuilder.java
index 7760b5c..95ac93c 100644
--- a/src/main/java/parser/astBuilder/ASTBuilder.java
+++ b/src/main/java/parser/astBuilder/ASTBuilder.java
@@ -10,11 +10,9 @@ import ast.expression.binaryexpression.NonCalculationExpressionNode;
import ast.expression.unaryexpression.MemberAccessNode;
import ast.expression.unaryexpression.NotExpressionNode;
import ast.expression.unaryexpression.UnaryExpressionNode;
-import ast.member.MethodNode;
+import ast.member.*;
import ast.statement.ifstatement.ElseStatementNode;
import ast.statement.ifstatement.IfElseStatementNode;
-import ast.member.ConstructorNode;
-import ast.member.MemberNode;
import ast.parameter.ParameterNode;
import ast.statement.*;
import ast.statement.ifstatement.IfStatementNode;
@@ -28,6 +26,7 @@ import ast.statement.statementexpression.methodcallstatementnexpression.ChainedM
import ast.statement.statementexpression.methodcallstatementnexpression.MethodCallStatementExpressionNode;
import ast.statement.statementexpression.methodcallstatementnexpression.TargetNode;
import ast.type.*;
+import ast.type.type.*;
import org.antlr.v4.runtime.tree.TerminalNode;
import parser.generated.*;
@@ -53,7 +52,7 @@ public class ASTBuilder extends SimpleJavaBaseVisitor {
@Override
public ASTNode visitConstructorDeclaration(SimpleJavaParser.ConstructorDeclarationContext ctx) {
- ConstructorNode constructorNode = new ConstructorNode((AccessModifierNode) visit(ctx.AccessModifier()), ctx.Identifier().getText(), (BlockNode) visit(ctx.block()));
+ ConstructorNode constructorNode = new ConstructorNode(ctx.AccessModifier().getText(), ctx.Identifier().getText(), (BlockNode) visit(ctx.block()));
for(SimpleJavaParser.ParameterContext parameter : ctx.parameterList().parameter()) {
constructorNode.addParameter((ParameterNode) visit(parameter));
}
@@ -63,16 +62,16 @@ public class ASTBuilder extends SimpleJavaBaseVisitor {
@Override
public ASTNode visitMethodDeclaration(SimpleJavaParser.MethodDeclarationContext ctx) {
if(ctx.MainMethodDeclaration() != null) {
- return new MethodNode((BlockNode) visit(ctx.block()));
+ return new MainMethodNode((BlockNode) visit(ctx.block()));
} else {
if(ctx.type() != null) {
- MethodNode methodNode = new MethodNode((AccessModifierNode) visit(ctx.AccessModifier()), (TypeNode) visit(ctx.type()), false, ctx.Identifier().getText(), (BlockNode) visit(ctx.block()));
+ MethodNode methodNode = new MethodNode(ctx.AccessModifier().getText(), createTypeNode(ctx.type().getText()), false, ctx.Identifier().getText(), (BlockNode) visit(ctx.block()));
for(SimpleJavaParser.ParameterContext parameter : ctx.parameterList().parameter()) {
methodNode.addParameter((ParameterNode) visit(parameter));
}
return methodNode;
} else {
- MethodNode methodNode = new MethodNode((AccessModifierNode) visit(ctx.AccessModifier()), null, true, ctx.Identifier().getText(), (BlockNode) visit(ctx.block()));
+ MethodNode methodNode = new MethodNode(ctx.AccessModifier().getText(), null, true, ctx.Identifier().getText(), (BlockNode) visit(ctx.block()));
for(SimpleJavaParser.ParameterContext parameter : ctx.parameterList().parameter()) {
methodNode.addParameter((ParameterNode) visit(parameter));
}
@@ -81,9 +80,14 @@ public class ASTBuilder extends SimpleJavaBaseVisitor {
}
}
+ @Override
+ public ASTNode visitFieldDeclaration(SimpleJavaParser.FieldDeclarationContext ctx) {
+ return new FieldNode(new AccessModifierNode(ctx.AccessModifier().getText()), createTypeNode(ctx.type().getText()), ctx.Identifier().getText());
+ }
+
@Override
public ASTNode visitParameter(SimpleJavaParser.ParameterContext ctx) {
- return new ParameterNode(new TypeNode(ctx.type().getText()), ctx.Identifier().getText());
+ return new ParameterNode(createTypeNode(ctx.type().getText()), ctx.Identifier().getText());
}
@Override
@@ -108,34 +112,37 @@ public class ASTBuilder extends SimpleJavaBaseVisitor {
@Override
public ASTNode visitReturnStatement(SimpleJavaParser.ReturnStatementContext ctx) {
- return new ReturnStatementNode((ExpressionNode) visit(ctx.expression()));
+ return new ReturnStatementNode((IExpressionNode) visit(ctx.expression()));
}
@Override
public ASTNode visitLocalVariableDeclaration(SimpleJavaParser.LocalVariableDeclarationContext ctx) {
- return new LocalVariableDeclarationNode(new TypeNode(ctx.type().getText()), ctx.Identifier().getText(), ctx.Assign().getText(), (ExpressionNode) visit(ctx.expression()));
+ return new LocalVariableDeclarationNode(createTypeNode(ctx.type().getText()), ctx.Identifier().getText(), ctx.Assign().getText(), (IExpressionNode) visit(ctx.expression()));
}
@Override
public ASTNode visitBlock(SimpleJavaParser.BlockContext ctx) {
BlockNode blockNode = new BlockNode();
for(SimpleJavaParser.StatementContext statement : ctx.statement()) {
- blockNode.addStatement((StatementNode) visit(statement));
+ blockNode.addStatement((IStatementNode) visit(statement));
+ }
+ if(!blockNode.hasReturnStatement) {
+ blockNode.addStatement(new ReturnStatementNode(null));
}
return blockNode;
}
@Override
public ASTNode visitWhileStatement(SimpleJavaParser.WhileStatementContext ctx) {
- return new WhileStatementNode((ExpressionNode) visit(ctx.expression()), (BlockNode) visit(ctx.block()));
+ return new WhileStatementNode((IExpressionNode) visit(ctx.expression()), (BlockNode) visit(ctx.block()));
}
@Override
public ASTNode visitForStatement(SimpleJavaParser.ForStatementContext ctx) {
if(ctx.statementExpression(0) != null) {
- return new ForStatementNode((ExpressionNode) visit(ctx.statementExpression(0)), (ExpressionNode) visit(ctx.expression()), (ExpressionNode) visit(ctx.statementExpression(1)));
+ return new ForStatementNode((IExpressionNode) visit(ctx.statementExpression(0)), (IExpressionNode) visit(ctx.expression()), (IExpressionNode) visit(ctx.statementExpression(1)));
} else if(ctx.localVariableDeclaration() != null) {
- return new ForStatementNode((StatementNode) visit(ctx.localVariableDeclaration()), (ExpressionNode) visit(ctx.expression()), (ExpressionNode) visit(ctx.statementExpression(1)));
+ return new ForStatementNode((IStatementNode) visit(ctx.localVariableDeclaration()), (IExpressionNode) visit(ctx.expression()), (IExpressionNode) visit(ctx.statementExpression(1)));
}
return null;
}
@@ -151,7 +158,7 @@ public class ASTBuilder extends SimpleJavaBaseVisitor {
@Override
public ASTNode visitIfStatement(SimpleJavaParser.IfStatementContext ctx) {
- return new IfStatementNode((ExpressionNode) visit(ctx.expression()), (BlockNode) visit(ctx.block()));
+ return new IfStatementNode((IExpressionNode) visit(ctx.expression()), (BlockNode) visit(ctx.block()));
}
@Override
@@ -175,14 +182,14 @@ public class ASTBuilder extends SimpleJavaBaseVisitor {
@Override
public ASTNode visitAssign(SimpleJavaParser.AssignContext ctx) {
- return new AssignStatementExpressionNode((AssignableExpressionNode) visit(ctx.assignableExpression()), (ExpressionNode) visit(ctx.expression()));
+ return new AssignStatementExpressionNode((AssignableExpressionNode) visit(ctx.assignableExpression()), (IExpressionNode) visit(ctx.expression()));
}
@Override
public ASTNode visitNewDeclaration(SimpleJavaParser.NewDeclarationContext ctx) {
NewDeclarationStatementExpressionNode newDeclarationStatementExpressionNode = new NewDeclarationStatementExpressionNode(ctx.Identifier().getText());
for(SimpleJavaParser.ExpressionContext expression : ctx.argumentList().expression()) {
- newDeclarationStatementExpressionNode.addExpression((ExpressionNode) visit(expression));
+ newDeclarationStatementExpressionNode.addExpression((IExpressionNode) visit(expression));
}
return newDeclarationStatementExpressionNode;
}
@@ -194,7 +201,7 @@ public class ASTBuilder extends SimpleJavaBaseVisitor {
methodCallStatementExpressionNode.addChainedMethod((ChainedMethodNode) visit(chainedMethod));
}
for(SimpleJavaParser.ExpressionContext expression : ctx.argumentList().expression()) {
- methodCallStatementExpressionNode.addExpression((ExpressionNode) visit(expression));
+ methodCallStatementExpressionNode.addExpression((IExpressionNode) visit(expression));
}
return methodCallStatementExpressionNode;
}
@@ -217,7 +224,7 @@ public class ASTBuilder extends SimpleJavaBaseVisitor {
public ASTNode visitChainedMethod(SimpleJavaParser.ChainedMethodContext ctx) {
ChainedMethodNode chainedMethodNode = new ChainedMethodNode(ctx.Identifier().getText());
for(SimpleJavaParser.ExpressionContext expression : ctx.argumentList().expression()) {
- chainedMethodNode.addExpression((ExpressionNode) visit(expression));
+ chainedMethodNode.addExpression((IExpressionNode) visit(expression));
}
return chainedMethodNode;
}
@@ -295,9 +302,9 @@ public class ASTBuilder extends SimpleJavaBaseVisitor {
} else if(ctx.notExpression() != null) {
return new UnaryExpressionNode((NotExpressionNode) visitNotExpression(ctx.notExpression()));
} else if(ctx.statementExpression() != null) {
- return new UnaryExpressionNode((StatementNode) visitStatementExpression(ctx.statementExpression()));
+ return new UnaryExpressionNode((IStatementNode) visitStatementExpression(ctx.statementExpression()));
} else if(ctx.expression() != null) {
- return new UnaryExpressionNode((ExpressionNode) visitExpression(ctx.expression()));
+ return new UnaryExpressionNode((IExpressionNode) visitExpression(ctx.expression()));
}
return null;
}
@@ -332,7 +339,7 @@ public class ASTBuilder extends SimpleJavaBaseVisitor {
@Override
public ASTNode visitNotExpression(SimpleJavaParser.NotExpressionContext ctx) {
- return new NotExpressionNode((ExpressionNode) visitExpression(ctx.expression()));
+ return new NotExpressionNode((IExpressionNode) visitExpression(ctx.expression()));
}
@@ -382,7 +389,7 @@ public class ASTBuilder extends SimpleJavaBaseVisitor {
@Override
public ASTNode visitNonCalculationExpression(SimpleJavaParser.NonCalculationExpressionContext ctx) {
- return new NonCalculationExpressionNode((UnaryExpressionNode) visit(ctx.unaryExpression()), ctx.nonCalculationOperator().getText(), (ExpressionNode) visit(ctx.expression()));
+ return new NonCalculationExpressionNode((UnaryExpressionNode) visit(ctx.unaryExpression()), ctx.nonCalculationOperator().getText(), (IExpressionNode) visit(ctx.expression()));
}
@Override
@@ -394,4 +401,15 @@ public class ASTBuilder extends SimpleJavaBaseVisitor {
}
return null;
}
+
+ public ITypeNode createTypeNode(String identifier){
+ return switch (identifier) {
+ case "int" -> new BaseType(TypeEnum.INT);
+ case "boolean" -> new BaseType(TypeEnum.BOOL);
+ case "char" -> new BaseType(TypeEnum.CHAR);
+ case "void" -> new BaseType(TypeEnum.VOID);
+ default -> new ReferenceType(identifier);
+ };
+ }
+
}
diff --git a/src/main/java/semantic/Scope.java b/src/main/java/semantic/Scope.java
index 60e9d2b..39dd2f8 100644
--- a/src/main/java/semantic/Scope.java
+++ b/src/main/java/semantic/Scope.java
@@ -1,27 +1,35 @@
package semantic;
-import oldAst.type.TypeNode;
+import ast.type.type.*;
+import semantic.exeptions.AlreadyDeclearedException;
import java.util.HashMap;
import java.util.Stack;
public class Scope {
- private Stack> localVars;
+ private Stack> localVars;
public Scope() {
- localVars = new Stack>();
+ localVars = new Stack>();
}
- public void addLocalVar(String name, TypeNode type) {
+ public void addLocalVar(String name, ITypeNode type) {
if (this.contains(name)) {
- throw new RuntimeException("Variable " + name + " already exists in this scope");
+ throw new AlreadyDeclearedException("Variable " + name + " already exists in this scope");
}
localVars.peek().put(name, type);
}
+ public ITypeNode getLocalVar(String name) {
+ for (HashMap map : localVars) {
+ return map.get(name);
+ }
+ return null;
+ }
+
public boolean contains(String name) {
- for (HashMap map : localVars) {
+ for (HashMap map : localVars) {
if (map.containsKey(name)) {
return true;
}
@@ -30,7 +38,7 @@ public class Scope {
}
public void pushScope() {
- localVars.push(new HashMap());
+ localVars.push(new HashMap());
}
public void popScope() {
diff --git a/src/main/java/semantic/SemanticAnalyzer.java b/src/main/java/semantic/SemanticAnalyzer.java
index 3bce422..c409c0d 100644
--- a/src/main/java/semantic/SemanticAnalyzer.java
+++ b/src/main/java/semantic/SemanticAnalyzer.java
@@ -1,40 +1,44 @@
package semantic;
-import oldAst.*;
-import oldAst.expression.*;
-import oldAst.member.FieldNode;
-import oldAst.member.MemberNode;
-
-import oldAst.member.MethodNode;
-import oldAst.parameter.ParameterListNode;
-import oldAst.parameter.ParameterNode;
-import oldAst.statement.*;
-import oldAst.type.ReferenceTypeNode;
-import oldAst.expression.This;
-
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
-import oldAst.type.BaseTypeNode;
-import oldAst.type.TypeNode;
+import ast.*;
+import ast.block.BlockNode;
+import ast.expression.IExpressionNode;
+import ast.expression.binaryexpression.*;
+import ast.expression.unaryexpression.UnaryExpressionNode;
+import ast.member.*;
+import ast.parameter.ParameterNode;
+import ast.statement.*;
+import ast.statement.ifstatement.ElseStatementNode;
+import ast.statement.ifstatement.IfElseStatementNode;
+import ast.statement.ifstatement.IfStatementNode;
+import ast.statement.statementexpression.AssignStatementExpressionNode;
+import ast.statement.statementexpression.AssignableExpressionNode;
+import ast.statement.statementexpression.NewDeclarationStatementExpressionNode;
+import ast.statement.statementexpression.crementExpression.DecrementExpressionNode;
+import ast.statement.statementexpression.crementExpression.IncrementExpressionNode;
+import ast.statement.statementexpression.methodcallstatementnexpression.MethodCallStatementExpressionNode;
+import ast.type.type.*;
import semantic.context.Context;
-import semantic.exeptions.AlreadyDeclearedException;
-import semantic.exeptions.NotDeclearedException;
-import semantic.exeptions.TypeMismatchException;
+import semantic.exeptions.*;
import typechecker.TypeCheckResult;
public class SemanticAnalyzer implements SemanticVisitor {
- private static HashMap currentFields = new HashMap<>();
+ private static HashMap currentFields = new HashMap<>();
public static ArrayList errors = new ArrayList<>();
- private Context context;
+ private static Context context;
private static Scope currentScope;
private static ClassNode currentClass;
+ private static ITypeNode currentNullType;
+ private ITypeNode currentMethodReturnType;
public static ASTNode generateTast(ASTNode node) {
SemanticAnalyzer semanticCheck = new SemanticAnalyzer();
@@ -50,7 +54,7 @@ public class SemanticAnalyzer implements SemanticVisitor {
return null;
}
- public static void clearAnalyzier(){
+ public static void clearAnalyzer() {
currentFields.clear();
errors.clear();
currentScope = null;
@@ -87,13 +91,10 @@ public class SemanticAnalyzer implements SemanticVisitor {
valid = valid && result.isValid();
} else if (memberNode instanceof MethodNode methodNode) {
//Methods
- for(MethodNode methode : currentClass.getMethods()){
- if(methode.equals(methodNode))
+ for (MethodNode methode : currentClass.getMethods()) {
+ if (methode.equals(methodNode))
break;
- if(methode.isSame(methodNode)){
- errors.add(new AlreadyDeclearedException("This method has already been declared"));
- valid = false;
- }
+
}
var result = methodNode.accept(this);
valid = valid && result.isValid();
@@ -106,38 +107,59 @@ public class SemanticAnalyzer implements SemanticVisitor {
@Override
public TypeCheckResult analyze(MethodNode methodNode) {
- var valid = true;
+ if (methodNode instanceof ConstructorNode) {
+ return new TypeCheckResult(true, new BaseType(TypeEnum.VOID));
+ } else {
- currentScope.pushScope();
+ var valid = true;
- //Parameter
- ParameterListNode parameterListNode = methodNode.parameters;
- if (parameterListNode != null) {
- List parameters = parameterListNode.parameters;
- for (ParameterNode parameter : parameters) {
- if (currentScope.contains(parameter.identifier)) {
- errors.add(new AlreadyDeclearedException("Duplicated Parameter " + parameter.identifier));
- return new TypeCheckResult(false, null);
- } else {
- currentScope.addLocalVar(parameter.identifier, parameter.type);
+ for (var otherMethod : currentClass.getMethods()) {
+ if (Objects.equals(otherMethod, methodNode))
+ break;
+ if (otherMethod.isSame(methodNode)) {
+ errors.add(new AlreadyDeclearedException(
+ "Method " + methodNode.getIdentifier() + " is already defined in class "
+ + currentClass.identifier));
+ valid = false;
}
}
- }
- //Statements
- List statements = methodNode.statements;
- for (StatementNode statement : statements) {
- if (statement instanceof AssignmentStatementNode assignmentStatementNode) {
- var result = assignmentStatementNode.accept(this);
- valid = valid && result.isValid();
- } else if (statement instanceof VariableDeclarationStatementNode variableDeclarationStatementNode) {
- var result = variableDeclarationStatementNode.accept(this);
+ currentScope.pushScope();
+ for (var parameter : methodNode.getParameters()) {
+ var result = parameter.accept(this);
valid = valid && result.isValid();
+ try {
+ currentScope.addLocalVar(parameter.identifier, parameter.type);
+ } catch (AlreadyDeclearedException e) {
+ errors.add(new AlreadyDeclearedException(parameter.identifier));
+ }
+
}
- }
+ // Check if this method is already declared
- currentScope.popScope();
- return new TypeCheckResult(valid, null);
+ currentMethodReturnType = methodNode.getType();
+ currentNullType = currentMethodReturnType; // Solange nicht in einem Assign oder Methoden-Aufruf dieser Typ
+
+ ITypeNode resultType = new BaseType(TypeEnum.VOID);
+
+ // gesetzt ist, ist dieser der Rückgabewert der Methode
+ var result = methodNode.block.accept(this);
+ valid = valid && result.isValid();
+ currentScope.popScope();
+ resultType = result.getType();
+
+ if (resultType == null) {
+ resultType = new BaseType(TypeEnum.VOID);
+ }
+ if (!resultType.equals(methodNode.getType())) {
+ errors.add(new TypeMismatchException("Method-Declaration " + methodNode.getIdentifier() + " with type "
+ + methodNode.getType() + " has at least one Mismatching return Type:"));
+ valid = false;
+ }
+
+ return new TypeCheckResult(valid, resultType);
+
+ }
}
@Override
@@ -152,87 +174,20 @@ public class SemanticAnalyzer implements SemanticVisitor {
}
@Override
- public TypeCheckResult analyze(AssignmentStatementNode assignmentStatementNode) {
- boolean valid = true;
- ExpressionNode expressionNodeLeft = assignmentStatementNode.expressionLeft;
- var resultLeft = expressionNodeLeft.accept(this);
- valid = valid && resultLeft.isValid();
- ExpressionNode expressionNodeRight = assignmentStatementNode.expressionRight;
- var resultRight = expressionNodeRight.accept(this);
- valid = valid && resultRight.isValid();
-
- if(Objects.equals(resultLeft.getType(), resultRight.getType())){
- System.out.println("SAME TYPE");
- } else {
- errors.add(new TypeMismatchException("Type mismatch"));
- valid = false;
- }
-
- return new TypeCheckResult(valid, null);
- }
-
- @Override
- public TypeCheckResult analyze(BinaryExpressionNode toCheck) {
- boolean valid = true;
- ExpressionNode left = toCheck.left;
- var resultLeft = left.accept(this);
- ExpressionNode right = toCheck.right;
- var resultRight = right.accept(this);
-
- switch (toCheck.operator) {
- case ASSIGNMENT:
- if(Objects.equals(resultRight.getType(), resultLeft.getType())){
- System.out.println("Correct Type");
- } else {
- valid = false;
- errors.add(new TypeMismatchException("Type Mismatch " + resultLeft.getType() + " and " + resultRight.getType()));
- }
- break;
- case DOT:
- return new TypeCheckResult(true, resultRight.getType());
- default:
- throw new RuntimeException("Unexpected operator: " + toCheck.operator);
- }
-
- return new TypeCheckResult(valid, null);
- }
-
- @Override
- public TypeCheckResult analyze(IdentifierExpressionNode toCheck) {
- if(toCheck.name == "this"){
- return new TypeCheckResult(true, null);
- } else if (currentFields.get(toCheck.name) == null) {
- errors.add(new AlreadyDeclearedException("Not declared " + toCheck.name + " in this scope"));
- return new TypeCheckResult(false, null);
- } else {
- return new TypeCheckResult(false, currentFields.get(toCheck.name));
- }
- }
-
- @Override
- public TypeCheckResult analyze(UnaryExpressionNode toCheck) {
- return null;
- }
-
- @Override
- public TypeCheckResult analyze(VariableDeclarationStatementNode toCheck) {
- if (currentScope.contains(toCheck.identifier)) {
- errors.add(new AlreadyDeclearedException("Already declared " + toCheck.identifier + " in this scope"));
- return new TypeCheckResult(false, null);
- } else {
- currentScope.addLocalVar(toCheck.identifier, toCheck.type);
- }
+ public TypeCheckResult analyze(IfStatementNode toCheck) {
return new TypeCheckResult(true, null);
}
- @Override
- public TypeCheckResult analyze(IfStatementNode toCheck) {
- return null;
- }
-
@Override
public TypeCheckResult analyze(ReturnStatementNode toCheck) {
- return null;
+
+ if(toCheck.expression != null){
+ var result = toCheck.expression.accept(this);
+ return new TypeCheckResult(true, result.getType());
+ } else {
+ return new TypeCheckResult(false, null);
+ }
+
}
@Override
@@ -241,39 +196,175 @@ public class SemanticAnalyzer implements SemanticVisitor {
}
@Override
- public TypeCheckResult analyze(LiteralNode toCheck) {
- return new TypeCheckResult(true, toCheck.getType());
+ public TypeCheckResult analyze(ParameterNode toCheck) {
+
+
+ return new TypeCheckResult(true, null);
}
@Override
- public TypeCheckResult analyze(InstVar toCheck) {
- boolean valid = true;
-
- var result = toCheck.expression.accept(this);
-
- if(result.getType() instanceof BaseTypeNode){
- throw new RuntimeException("BaseType has no Methods or Fields");
- } else {
- //Get typ of Field
-
- var type = (ReferenceTypeNode)result.getType();
- var classContext = context.getClass(type.getIdentifier());
-
- if(classContext == null){
- errors.add(new NotDeclearedException("Not declared " + type.getIdentifier() + " in this scope"));
- return new TypeCheckResult(false, null);
- } else {
- var field = classContext.getField(toCheck.identifier);
-
- return new TypeCheckResult(valid, field.getType());
+ public TypeCheckResult analyze(BlockNode blockNode) {
+ ITypeNode blockReturnType = null;
+ for (IStatementNode statementNode : blockNode.statements) {
+ var result = statementNode.accept(this);
+ if(result.getType() != null){
+ if(blockReturnType == null){
+ blockReturnType = result.getType();
+ } else {
+ errors.add(new MultipleReturnTypes("There are multiple Return types"));
+ }
}
}
+ return new TypeCheckResult(true, blockReturnType);
+ }
+
+ @Override
+ public TypeCheckResult analyze(AssignableExpressionNode toCheck) {
+ return new TypeCheckResult(true, currentFields.get(toCheck.identifier));
}
@Override
- public TypeCheckResult analyze(This toCheck) {
- return new TypeCheckResult(true, toCheck.getType());
+ public TypeCheckResult analyze(ElseStatementNode toCheck) {
+ return null;
+ }
+
+ @Override
+ public TypeCheckResult analyze(ForStatementNode toCheck) {
+ return null;
+ }
+
+ @Override
+ public TypeCheckResult analyze(AssignStatementExpressionNode toCheck) {
+ AssignableExpressionNode assignable = toCheck.assignable;
+ var oldNullType = currentNullType;
+ currentNullType = currentFields.get(toCheck.assignable.identifier);
+ IExpressionNode rExpression = toCheck.expression;
+ currentNullType = oldNullType;
+ var valid = true;
+
+ // This check currently handles things like :
+ /**
+ * private int i;
+ * void foo(int i){
+ * i = i;
+ * }
+ */
+ if (assignable.equals(rExpression)) {
+ errors.add(new TypeMismatchException("Cannot assign to self"));
+ valid = false;
+ }
+
+ var lResult = assignable.accept(this);
+ currentNullType = lResult.getType();
+ var rResult = rExpression.accept(this);
+
+ if (!Objects.equals(currentScope.getLocalVar(toCheck.assignable.identifier), rExpression.getType())) {
+ errors.add(new TypeMismatchException(
+ "Mismatch types in Assign-Statement: cannot convert from \"" + lResult.getType() + "\" to \""
+ + rResult.getType() + "\""));
+ valid = false;
+ }
+// else {
+// toCheck.setType(assignable.getType());
+// }
+ valid = valid && lResult.isValid() && rResult.isValid();
+ currentNullType = null;
+ return new TypeCheckResult(valid, null); // return type is null to get the return type sufficently
+ }
+
+ @Override
+ public TypeCheckResult analyze(DecrementExpressionNode toCheck) {
+ return null;
+ }
+
+ @Override
+ public TypeCheckResult analyze(IfElseStatementNode toCheck) {
+ return new TypeCheckResult(true, null);
+ }
+
+ @Override
+ public TypeCheckResult analyze(MethodCallStatementExpressionNode toCheck) {
+ return null;
+ }
+
+ @Override
+ public TypeCheckResult analyze(LocalVariableDeclarationNode localVarDecl) {
+ var valid = true;
+
+ if (localVarDecl.expression != null) {
+ TypeCheckResult result = localVarDecl.expression.accept(this);
+
+ var resultType = localVarDecl.expression.getType();
+ valid = result.isValid() && valid;
+
+ if (!Objects.equals(resultType, localVarDecl.type)) {
+ errors.add(new TypeMismatchException(
+ "Type mismatch: cannot convert from " + resultType + " to " + localVarDecl.type));
+ valid = false;
+ }
+
+ }
+
+ try {
+ currentScope.addLocalVar(localVarDecl.identifier, localVarDecl.type);
+ } catch (AlreadyDefinedException e) {
+ errors.add(new AlreadyDefinedException(e.getMessage()));
+ valid = false;
+ }
+ return new TypeCheckResult(valid, null);
+ }
+
+ @Override
+ public TypeCheckResult analyze(NewDeclarationStatementExpressionNode toCheck) {
+ return null;
+ }
+
+ @Override
+ public TypeCheckResult analyze(IncrementExpressionNode toCheck) {
+ return null;
+ }
+
+ @Override
+ public TypeCheckResult analyze(BinaryExpressionNode toCheck) {
+ return null;
+ }
+
+ @Override
+ public TypeCheckResult analyze(CalculationExpressionNode calcNode) {
+ if (calcNode.calculationExpression != null) {
+ calcNode.calculationExpression.accept(this);
+ }
+ return null;
+ }
+
+ @Override
+ public TypeCheckResult analyze(DotExpressionNode toCheck) {
+ return null;
+ }
+
+ @Override
+ public TypeCheckResult analyze(DotSubstractionExpressionNode toCheck) {
+ return null;
+ }
+
+ @Override
+ public TypeCheckResult analyze(NonCalculationExpressionNode toCheck) {
+ return null;
+ }
+
+ @Override
+ public TypeCheckResult analyze(UnaryExpressionNode unary) {
+ var valid = true;
+
+ if (currentScope.contains(unary.identifier)) {
+ return new TypeCheckResult(valid, currentScope.getLocalVar(unary.identifier));
+ } else if(currentFields.get(unary.identifier) != null) {
+ return new TypeCheckResult(valid, currentFields.get(unary.identifier));
+ } else {
+ errors.add(new NotDeclearedException("Var is not Decleared"));
+ }
+ return new TypeCheckResult(valid, null);
}
}
\ No newline at end of file
diff --git a/src/main/java/semantic/SemanticVisitor.java b/src/main/java/semantic/SemanticVisitor.java
index 7139261..6b6942f 100644
--- a/src/main/java/semantic/SemanticVisitor.java
+++ b/src/main/java/semantic/SemanticVisitor.java
@@ -1,18 +1,20 @@
package semantic;
-import ast.ClassNode;
-import ast.expression.LiteralNode;
-import ast.ProgramNode;
-import ast.expression.BinaryExpressionNode;
-import ast.expression.IdentifierExpressionNode;
-import ast.expression.InstVar;
+import ast.*;
+import ast.block.BlockNode;
+import ast.expression.binaryexpression.*;
import ast.expression.unaryexpression.UnaryExpressionNode;
-import ast.member.FieldNode;
-import ast.member.MethodNode;
+import ast.member.*;
+import ast.parameter.ParameterNode;
import ast.statement.*;
-import ast.expression.This;
-import ast.statement.ifstatement.IfStatementNode;
+import ast.statement.ifstatement.*;
+import ast.statement.statementexpression.AssignStatementExpressionNode;
+import ast.statement.statementexpression.AssignableExpressionNode;
+import ast.statement.statementexpression.NewDeclarationStatementExpressionNode;
+import ast.statement.statementexpression.crementExpression.DecrementExpressionNode;
+import ast.statement.statementexpression.crementExpression.IncrementExpressionNode;
+import ast.statement.statementexpression.methodcallstatementnexpression.MethodCallStatementExpressionNode;
import typechecker.TypeCheckResult;
public interface SemanticVisitor {
@@ -25,25 +27,46 @@ public interface SemanticVisitor {
TypeCheckResult analyze(FieldNode toCheck);
- TypeCheckResult analyze(AssignmentStatementNode toCheck);
-
- TypeCheckResult analyze(BinaryExpressionNode toCheck);
-
- TypeCheckResult analyze(IdentifierExpressionNode toCheck);
-
- TypeCheckResult analyze(UnaryExpressionNode toCheck);
-
- TypeCheckResult analyze(VariableDeclarationStatementNode toCheck);
-
TypeCheckResult analyze(IfStatementNode toCheck);
TypeCheckResult analyze(ReturnStatementNode toCheck);
TypeCheckResult analyze(WhileStatementNode toCheck);
- TypeCheckResult analyze(LiteralNode toCheck);
+ TypeCheckResult analyze(ParameterNode toCheck);
- TypeCheckResult analyze(InstVar toCheck);
+ TypeCheckResult analyze(BlockNode toCheck);
+
+ TypeCheckResult analyze(AssignableExpressionNode toCheck);
+
+ TypeCheckResult analyze(ElseStatementNode toCheck);
+
+ TypeCheckResult analyze(ForStatementNode toCheck);
+
+ TypeCheckResult analyze(AssignStatementExpressionNode toCheck);
+
+ TypeCheckResult analyze(DecrementExpressionNode toCheck);
+
+ TypeCheckResult analyze(IfElseStatementNode toCheck);
+
+ TypeCheckResult analyze(MethodCallStatementExpressionNode toCheck);
+
+ TypeCheckResult analyze(LocalVariableDeclarationNode toCheck);
+
+ TypeCheckResult analyze(NewDeclarationStatementExpressionNode toCheck);
+
+ TypeCheckResult analyze(IncrementExpressionNode toCheck);
+
+ TypeCheckResult analyze(BinaryExpressionNode toCheck);
+
+ TypeCheckResult analyze(CalculationExpressionNode toCheck);
+
+ TypeCheckResult analyze(DotExpressionNode toCheck);
+
+ TypeCheckResult analyze(DotSubstractionExpressionNode toCheck);
+
+ TypeCheckResult analyze(NonCalculationExpressionNode toCheck);
+
+ TypeCheckResult analyze(UnaryExpressionNode toCheck);
- TypeCheckResult analyze(This toCheck);
}
\ No newline at end of file
diff --git a/src/main/java/semantic/context/ClassContext.java b/src/main/java/semantic/context/ClassContext.java
index 6b0d386..982866f 100644
--- a/src/main/java/semantic/context/ClassContext.java
+++ b/src/main/java/semantic/context/ClassContext.java
@@ -1,7 +1,7 @@
package semantic.context;
-import oldAst.ClassNode;
-import oldAst.member.FieldNode;
+import ast.ClassNode;
+import ast.member.FieldNode;
import java.util.HashMap;
public class ClassContext {
diff --git a/src/main/java/semantic/context/Context.java b/src/main/java/semantic/context/Context.java
index 31ba3de..d6431ef 100644
--- a/src/main/java/semantic/context/Context.java
+++ b/src/main/java/semantic/context/Context.java
@@ -1,6 +1,6 @@
package semantic.context;
-import oldAst.ProgramNode;
+import ast.ProgramNode;
import java.util.HashMap;
public class Context {
diff --git a/src/main/java/semantic/context/FieldContext.java b/src/main/java/semantic/context/FieldContext.java
index 2dad262..18e8cf7 100644
--- a/src/main/java/semantic/context/FieldContext.java
+++ b/src/main/java/semantic/context/FieldContext.java
@@ -1,20 +1,20 @@
package semantic.context;
-import oldAst.member.FieldNode;
-import oldAst.type.AccessTypeNode;
-import oldAst.type.TypeNode;
+import ast.member.FieldNode;
+import ast.type.*;
+import ast.type.type.*;
public class FieldContext {
- private AccessTypeNode accessModifier;
- private TypeNode type;
+ private AccessModifierNode accessModifier;
+ private ITypeNode type;
public FieldContext(FieldNode field) {
accessModifier = field.accessTypeNode;
type = field.type;
}
- public TypeNode getType() {
+ public ITypeNode getType() {
return type;
}
diff --git a/src/main/java/semantic/exeptions/AlreadyDefinedException.java b/src/main/java/semantic/exeptions/AlreadyDefinedException.java
new file mode 100644
index 0000000..441a5d9
--- /dev/null
+++ b/src/main/java/semantic/exeptions/AlreadyDefinedException.java
@@ -0,0 +1,9 @@
+package semantic.exeptions;
+
+public class AlreadyDefinedException extends RuntimeException {
+
+ public AlreadyDefinedException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/main/java/semantic/exeptions/MultipleReturnTypes.java b/src/main/java/semantic/exeptions/MultipleReturnTypes.java
new file mode 100644
index 0000000..59f92fa
--- /dev/null
+++ b/src/main/java/semantic/exeptions/MultipleReturnTypes.java
@@ -0,0 +1,9 @@
+package semantic.exeptions;
+
+public class MultipleReturnTypes extends RuntimeException {
+
+ public MultipleReturnTypes(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/main/java/typechecker/TypeCheckResult.java b/src/main/java/typechecker/TypeCheckResult.java
index a06d359..f52818d 100644
--- a/src/main/java/typechecker/TypeCheckResult.java
+++ b/src/main/java/typechecker/TypeCheckResult.java
@@ -1,14 +1,14 @@
package typechecker;
-import oldAst.type.TypeNode;
+import ast.type.type.ITypeNode;
public class TypeCheckResult {
private boolean valid;
- private TypeNode type;
+ private ITypeNode type;
- public TypeCheckResult(boolean valid, TypeNode type) {
+ public TypeCheckResult(boolean valid, ITypeNode type) {
this.valid = valid;
this.type = type;
}
@@ -17,7 +17,7 @@ public class TypeCheckResult {
return valid;
}
- public TypeNode getType() {
+ public ITypeNode getType() {
return type;
}
}
\ No newline at end of file
diff --git a/src/main/resources/CompilerInput.java b/src/main/resources/CompilerInput.java
deleted file mode 100644
index 0f8297b..0000000
--- a/src/main/resources/CompilerInput.java
+++ /dev/null
@@ -1,22 +0,0 @@
-public class Example {
-
- public int a;
-
- public static int testMethod(char x){
-
- a = 12;
-
- a = x;
-
- }
-
-}
-
-public class Test {
-
- public static int testMethod(char x, int a){
-
-
-
- }
-}
\ No newline at end of file
diff --git a/src/main/resources/input/CompilerInput.java b/src/main/resources/input/CompilerInput.java
new file mode 100644
index 0000000..d850a3e
--- /dev/null
+++ b/src/main/resources/input/CompilerInput.java
@@ -0,0 +1,16 @@
+public class CompilerInput {
+
+ public int a;
+
+ public static int testMethod(char x){
+ return 0;
+ }
+
+ public class Test {
+
+ public static int testMethod(char x, int a){
+ return 0;
+ }
+ }
+}
+
diff --git a/src/main/test/java/EmptyClassExample.class b/src/main/test/java/EmptyClassExample.class
deleted file mode 100644
index 7a73bad..0000000
Binary files a/src/main/test/java/EmptyClassExample.class and /dev/null differ
diff --git a/src/main/test/java/Tester.java b/src/main/test/java/Tester.java
deleted file mode 100644
index 777c50b..0000000
--- a/src/main/test/java/Tester.java
+++ /dev/null
@@ -1,11 +0,0 @@
-public class Tester {
- public static void main(String[] args) {
- new EmptyClassExample();
- // cp mitgeben
- }
-}
-// java -jar pfadtocompiler.jar EmptyClass.java
-//mit bash scipt ode rmakefile test automatisieren
-//mvn package
-// javac tester // tester compilen
-// java tester // tester ausführen
\ No newline at end of file
diff --git a/src/main/test/java/make.md b/src/main/test/java/make.md
deleted file mode 100644
index e69de29..0000000
diff --git a/src/test/Makefile b/src/test/Makefile
new file mode 100644
index 0000000..e3e7d75
--- /dev/null
+++ b/src/test/Makefile
@@ -0,0 +1,29 @@
+# Makefile
+### IntelliJs play buttons do not work. Run in "src/test" folder with "make" command to run all
+### Or run only parts with "make compile-javac", "make clean" etc.
+
+all: compile-javac compile-raupenpiler
+
+compile-javac:
+ javac -d .\resources\output\javac .\resources\input\CompilerInput.java
+
+compile-raupenpiler:
+ cd ../.. ; mvn -DskipTests install
+ cd ../.. ; mvn exec:java -Dexec.mainClass="main.Main" -Dexec.args="'src/main/resources/input/CompilerInput.java' 'src/main/resources/output' "
+
+test: test-javac test-raupenpiler
+
+test-javac:
+ #compile-javac
+ #java -cp .\resources\output\javac CompilerInput
+
+test-raupenpiler:
+ #java -cp .\resources\output\raupenpiler CompilerInput
+
+clean:
+ rm -f ./resources/output/javac/*.class
+ rm -f ./resources/output/raupenpiler/*.class
+ rm -f ./java/*.class
+ rm -f ../main/resources/output/*.class
+ rm -f ../main/resources/logs/*.log
+
diff --git a/src/main/test/TestSpecs.md b/src/test/TestSpecs.md
similarity index 58%
rename from src/main/test/TestSpecs.md
rename to src/test/TestSpecs.md
index 6fd5a41..0911765 100644
--- a/src/main/test/TestSpecs.md
+++ b/src/test/TestSpecs.md
@@ -1,5 +1,7 @@
# Scanner
+
## Scanner Input
+
### Beispiel 1: Empty Class
String empty class = "public class Name {}";
@@ -15,64 +17,76 @@
"}"
## Scanner Output
+
+CommonTokenStream
+
### Beispiel 1: Empty Class
Token Type; Token Text
Type gibts nur bei Terminalen, Text bei allen
-
+
[null "public", null "class", IDENTIFIER "Name", null "{", null "}", EOF ""]
Bsp von Ihm mal:
[TokPublic,TokClass,TokIdentifier "Name",TokLeftBrace,TokRightBrace]
-### Beispiel 2: Filled Class
-
- [TokClass,TokIdentifier "javaFileInput.Example",TokLeftBrace]
- [TokIf,TokLeftParen,TokIdentifier "x",TokLessThan,TokNumber 5,TokRightParen,TokLeftBrace]
- [TokFor,TokLeftParen,TokIdentifier "int",TokIdentifier "i",TokAssign,TokNumber 0,TokSemicolon,TokIdentifier "i",TokLessThan,TokNumber 10,TokSemicolon,TokIdentifier "i",TokPlus,TokPlus,TokRightParen,TokLeftBrace]
- [TokWhile,TokLeftParen,TokIdentifier "true",TokRightParen,TokLeftBrace]
- [TokIdentifier "x",TokAssign,TokNumber 5,TokSemicolon]
- [TokRightBrace]
-
# Parser
-## Parser Input
+
+## Parser Input
+
+CommonTokenStream
(Scanner Output)
## Parser Output (AST)
+
+(program (classDeclaration (accessType public) class Name { }))
+
+ParseTree
+
### Beispiel 1: Empty Class
-
-
-### Beispiel 2: Filled Class
-
-
-
# Semantische Analyse / Typcheck
-## Typcheck Input
+
+## Typcheck Input
+
(Parser Output = AST)
## Typcheck Output
### Beispiel 1: Empty Class
-
-
-### Beispiel 2: Filled Class
-
-
# Bytecodegenerierung
-## Bytecodegenerierung Input
+
+## Bytecodegenerierung Input
+
(Typcheck Output = vom Typcheck eventuell manipulierter AST)
## Bytecodegenerierung Output
### Beispiel 1: Empty Class
+
Compiled Classfile
public class javaFileInput.Example {
}
+## E2E Tests:
+- Testdatei mit Main ausführen/kompilieren
+- Testdatei mit "javac -d output .\CompilerInput.java" kompilieren
+- -> Dateien mit javap vergleichen
-### Beispiel 2: Filled Class
-
\ No newline at end of file
+wenn beides erfolgreich
+
+- Ergebnis vom eigenen Compiler mithilfe von main.TestCompilerOutput ausführen
+- (Ergebnis von javac mithilfe von main.TestCompilerOutput ausführen)
+
+### Andis Tipps:
+
+- cp mitgeben
+- makefile
+- java -jar pfadtocompiler.jar EmptyClass.java
+- mvn package
+- javac tester // tester compilen
+- java tester // tester ausführen
+- -> tester ist in unserem Fall main.TestCompilerOutput.java
\ No newline at end of file
diff --git a/src/main/test/java/EmptyClassExample.java b/src/test/java/main/EmptyClassExample.java
similarity index 65%
rename from src/main/test/java/EmptyClassExample.java
rename to src/test/java/main/EmptyClassExample.java
index 9f54ec6..e7715be 100644
--- a/src/main/test/java/EmptyClassExample.java
+++ b/src/test/java/main/EmptyClassExample.java
@@ -1,3 +1,5 @@
+package main;
+
public class EmptyClassExample {
private class Inner {
}
diff --git a/src/main/test/java/FailureTest.java b/src/test/java/main/FailureTest.java
similarity index 68%
rename from src/main/test/java/FailureTest.java
rename to src/test/java/main/FailureTest.java
index bc63d91..5a2e1c8 100644
--- a/src/main/test/java/FailureTest.java
+++ b/src/test/java/main/FailureTest.java
@@ -1,3 +1,5 @@
+package main;
+
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -15,17 +17,17 @@ import java.util.List;
public class FailureTest {
private static final List TEST_FILES = Arrays.asList(
- "src/main/test/resources/failureTests/TestClass1.java",
- "src/main/test/resources/failureTests/TestClass2.java",
- "src/main/test/resources/failureTests/TestClass3.java",
- "src/main/test/resources/failureTests/TestClass4.java",
- "src/main/test/resources/failureTests/TestClass5.java",
- "src/main/test/resources/failureTests/TestClass6.java",
- "src/main/test/resources/failureTests/TestClass7.java",
- "src/main/test/resources/failureTests/TestClass8.java",
- "src/main/test/resources/failureTests/TestClass9.java",
- "src/main/test/resources/failureTests/TestClass10.java",
- "src/main/test/resources/failureTests/TestClass11.java"
+ "src/main/test/resources/input/failureTests/TestClass1.java",
+ "src/main/test/resources/input/failureTests/TestClass2.java",
+ "src/main/test/resources/input/failureTests/TestClass3.java",
+ "src/main/test/resources/input/failureTests/TestClass4.java",
+ "src/main/test/resources/input/failureTests/TestClass5.java",
+ "src/main/test/resources/input/failureTests/TestClass6.java",
+ "src/main/test/resources/input/failureTests/TestClass7.java",
+ "src/main/test/resources/input/failureTests/TestClass8.java",
+ "src/main/test/resources/input/failureTests/TestClass9.java",
+ "src/main/test/resources/input/failureTests/TestClass10.java",
+ "src/main/test/resources/input/failureTests/TestClass11.java"
);
/**
@@ -62,8 +64,8 @@ public class FailureTest {
void typedASTTest() throws IOException {
CharStream codeCharStream = null;
try {
- codeCharStream = CharStreams.fromPath(Paths.get("src/main/test/resources/EmptyClassExample.java"));
- Main.parsefile(codeCharStream);
+ codeCharStream = CharStreams.fromPath(Paths.get("src/main/test/resources/main/EmptyClassExample.java"));
+ Main.compileFile(codeCharStream, "src/main/test/resources/output");
} catch (IOException e) {
System.err.println("Error reading the file: " + e.getMessage());
}
diff --git a/src/main/test/java/MainTest.java b/src/test/java/main/MainTest.java
similarity index 65%
rename from src/main/test/java/MainTest.java
rename to src/test/java/main/MainTest.java
index d4899fd..363fa35 100644
--- a/src/main/test/java/MainTest.java
+++ b/src/test/java/main/MainTest.java
@@ -1,12 +1,8 @@
+package main;
+
import org.junit.jupiter.api.Test;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
-import org.antlr.v4.runtime.CommonTokenStream;
-import org.antlr.v4.runtime.tree.ParseTree;
-import parser.ASTBuilder;
-import oldAst.ClassNode;
-import oldAst.ProgramNode;
-import bytecode.ByteCodeGenerator;
import java.io.IOException;
import java.nio.file.Paths;
@@ -21,8 +17,8 @@ public class MainTest {
void testEmptyClass() {
CharStream codeCharStream = null;
try {
- codeCharStream = CharStreams.fromPath(Paths.get("src/main/test/resources/EmptyClassExample.java"));
- Main.parsefile(codeCharStream);
+ codeCharStream = CharStreams.fromPath(Paths.get("src/main/test/resources/CompilerInput.java"));
+ Main.compileFile(codeCharStream, "src/main/test/resources/output");
} catch (IOException e) {
System.err.println("Error reading the file: " + e.getMessage());
}
diff --git a/src/test/java/main/TestCompilerOutput.java b/src/test/java/main/TestCompilerOutput.java
new file mode 100644
index 0000000..6a30e14
--- /dev/null
+++ b/src/test/java/main/TestCompilerOutput.java
@@ -0,0 +1,45 @@
+package main;
+
+/**
+ * This class is used to test the output of the compiler.
+ *
+ * Im gleichen Ordner wie diese Datei (main.TestCompilerOutput.java) muss die selbst kompilierte CompilerInput.class Datei sein.
+ *
Hinweis: Diese muss man also vom Ordner main/resources/output
in diesen Ordner hier (test/java) rein kopieren. (bis es eine bessere Lösung gibt)
+ *
+ * Die selbst kompilierte .class Datei wird dann hier drin geladen und eine Instanz von ihr erstellt, es können auch Methoden aufgerufen werden.
+ *
Diese main.TestCompilerOutput.java Datei wird dann in \src\test\java>
mit javac .\main.TestCompilerOutput.java
kompiliert und mit java main.TestCompilerOutput
ausgeführt.
+ * Wenn unser Compiler funktioniert, sollten keine Errors kommen (sondern nur die Ausgaben, die wir in der CompilerInput.java Datei gemacht haben,
+ * oder Methoden, die wir hier aufrufen).
+ *
+ * PROBLEM: Hier kommen Errors, was eigentlich heißt, dass der Compiler nicht funktioniert, der Test sollte eigentlich passen.
+ *
DENN: Wenn ich statt unserem CompilerInput.class die CompilerInput.class von javac verwende (aus src/test/resources/output/javac
), dann funktioniert es.
+ */
+public class TestCompilerOutput {
+ public static void main(String[] args) {
+ try {
+ // Try to load the class named "CompilerInput"
+ Class> cls = Class.forName("CompilerInput");
+ // Print a success message if the class is loaded successfully
+ System.out.println("Class loaded successfully: " + cls.getName());
+
+ // Try to create an instance of the loaded class
+ Object instance = cls.getDeclaredConstructor().newInstance();
+ // Print a success message if the instance is created successfully
+ System.out.println("Instance created: " + instance);
+
+
+ // If the class has a main method, you can invoke it
+ // cls.getMethod("main", String[].class).invoke(null, (Object) new String[]{});
+
+ // If the class has other methods, you can invoke them as well
+ // Example: cls.getMethod("someMethod").invoke(instance);
+
+ } catch (ClassNotFoundException e) {
+ // Print an error message if the class is not found
+ System.err.println("Class not found: " + e.getMessage());
+ } catch (Exception e) {
+ // Print an error message if any other exception occurs during class loading or instance creation
+ System.err.println("Error during class loading or execution: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/test/java/parser/ParserTest.java b/src/test/java/parser/ParserTest.java
new file mode 100644
index 0000000..4c39828
--- /dev/null
+++ b/src/test/java/parser/ParserTest.java
@@ -0,0 +1,185 @@
+package parser;
+
+import org.antlr.v4.runtime.*;
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import parser.astBuilder.ASTBuilder;
+import parser.generated.SimpleJavaLexer;
+import parser.generated.SimpleJavaParser;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.*;
+
+public class ParserTest {
+ @BeforeEach
+ public void init() { // noch nicht benötigt
+ String inputFilePath = "src/main/resources/input/CompilerInput.java";
+ String outputDirectoryPath = "src/main/resources/output";
+ }
+
+ /**
+ * This test method is used to test the scanner functionality of the SimpleJavaLexer.
+ * It creates a CharStream from a string representing a simple Java class declaration,
+ * and uses the SimpleJavaLexer to tokenize this input.
+ * It then compares the actual tokens and their types produced by the lexer to the expected tokens and their types.
+ */
+ @Test
+ public void scannerTest() {
+ // Create a CharStream from a string representing a simple Java class declaration
+ CharStream inputCharStream = CharStreams.fromString("public class Name {}");
+
+ // Use the SimpleJavaLexer to tokenize the input
+ SimpleJavaLexer lexer = new SimpleJavaLexer(inputCharStream);
+ CommonTokenStream tokenStream = new CommonTokenStream(lexer);
+ tokenStream.fill();
+
+ // Prepare the expected results
+ List actualTokens = tokenStream.getTokens();
+ List expectedTokens = Arrays.asList("public", "class", "Name", "{", "}", "");
+ List expectedTokenTypes = Arrays.asList(null, null, "IDENTIFIER", null, null, "EOF");
+
+ // Compare the actual tokens and their types to the expected tokens and their types
+ assertEquals(expectedTokens.size(), actualTokens.size());
+ for (int i = 0; i < expectedTokens.size(); i++) {
+ assertEquals(expectedTokens.get(i), actualTokens.get(i).getText());
+ assertEquals(expectedTokenTypes.get(i), SimpleJavaLexer.VOCABULARY.getSymbolicName(actualTokens.get(i).getType()));
+ }
+ }
+
+
+ @Test
+ public void parserTest() {
+ // init
+ CharStream inputCharStream = CharStreams.fromString("public class Name {}");
+ SimpleJavaLexer lexer = new SimpleJavaLexer(inputCharStream);
+ CommonTokenStream tokenStream = new CommonTokenStream(lexer);
+ tokenStream.fill();
+
+
+ /* Parser -> Parsetree */
+ SimpleJavaParser parser = new SimpleJavaParser(tokenStream);
+ ParseTree parseTree = parser.program(); // parse the input
+
+ //Variante 1 (geht)
+ String actualParseTreeAsString = parseTree.toStringTree(parser);
+ String expectedParseTreeAsString = "(program (classDeclaration (accessType public) class Name { }))";
+
+ assertEquals(actualParseTreeAsString, expectedParseTreeAsString);
+
+ //Variante 2 (geht nicht)
+ // - Sollte es gehen und es liegt am Parser? (keine Ahnung) -> Bitte Fehler (actual und expected) durchlesen
+ Map actualTreeStructure = buildTreeStructure(parseTree, parser);
+ Map expectedTreeStructure = parseStringToTree(expectedParseTreeAsString);
+
+ assertEquals(actualTreeStructure, expectedTreeStructure);
+
+
+ }
+
+ @Test
+ public void astBuilderTest() {
+ // TODO: Implement this test method
+
+
+
+
+ /* AST builder -> AST */
+ ASTBuilder astBuilder = new ASTBuilder();
+ // ProgramNode abstractSyntaxTree = (ProgramNode) astBuilder.visit(parseTree);
+
+ //String actualASTasString = new ASTBuilder().visit(parseTree).toString();
+
+ // ProgramNode actualAST = new ASTBuilder().visit(parseTree);
+ // ProgramNode expectedAST = new ProgramNode();
+ // expectedAST.add(new ProgramNode.ClassNode("Name", new ProgramNode()));
+ }
+
+
+ // Helpers Variante 2.1
+
+ public static Map buildTreeStructure(ParseTree tree, Parser parser) {
+ return buildTree(tree, parser, 0);
+ }
+
+ private static Map buildTree(ParseTree tree, Parser parser, int indent) {
+ Map node = new HashMap<>();
+
+ if (tree instanceof RuleContext) {
+ int ruleIndex = ((RuleContext) tree).getRuleIndex();
+ String ruleName = parser.getRuleNames()[ruleIndex];
+ node.put("rule", ruleName);
+ } else {
+ node.put("text", tree.getText());
+ }
+
+ List