tokens = tokenStream.getTokens();
-// System.out.println("-------------------- Scanner -> tokens --------------------");
-// for (Token token : tokens) {
-// String tokenType = SimpleJavaLexer.VOCABULARY.getSymbolicName(token.getType());
-// String tokenText = token.getText();
-// // System.out.println("Token Type: " + tokenType + ", Token Text: " +
-// // tokenText);
-// System.out.println(tokenType + " " + tokenText);
-// }
-// System.out.println();
-
- /* ------------------------- Parser -> Parsetree ------------------------- */
- SimpleJavaParser parser = new SimpleJavaParser(tokenStream);
- ParseTree parseTree = parser.program(); // parse the input
-
- // Printing the parse tree
-// System.out.println("-------------------- Parser -> Parsetree --------------------");
-// System.out.println(parseTree.toStringTree(parser));
-// printTree(parseTree, parser, 0);
-// System.out.println();
-
- /* ------------------------- AST builder -> AST ------------------------- */
- ASTBuilder astBuilder = new ASTBuilder();
- ProgramNode abstractSyntaxTree = (ProgramNode) astBuilder.visit(parseTree);
-
- // Printing the AST
-// System.out.println("-------------------- AST builder -> AST --------------------");
-// // System.out.println("AST: " + ast.toString());
-// printAST(abstractSyntaxTree, 0);
-// System.out.println();
-
- /*
- * ------------------------- Semantic Analyzer -> Tast -------------------------
- */
- SemanticAnalyzer semanticAnalyzer = new SemanticAnalyzer();
- ProgramNode typedAst = (ProgramNode) semanticAnalyzer.generateTast(abstractSyntaxTree);
-
- // Printing the Tast
- System.out.println("Tast generated");
-
- /*
- * ------------------------- Bytecode Generator -> Bytecode
- * -------------------------
- */
- ByteCodeGenerator byteCodeGenerator = new ByteCodeGenerator();
- //byteCodeGenerator.generateByteCode(abstractSyntaxTree);
- byteCodeGenerator.visit(typedAst);
- System.out.println("Bytecode generated");
-
- }
-
- /**
- * 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 printTree(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];
- System.out.println(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
- System.out.println(indentString + tree.getText());
- }
-
- // Recursively print the children of the current node, increasing the
- // indentation level
- for (int i = 0; i < tree.getChildCount(); i++) {
- printTree(tree.getChild(i), parser, indent + 1);
- }
- }
-
- public static void printAST(ASTNode node, int indent) {
- String indentString = " ".repeat(indent * 2);
- System.out.println(indentString + node.getClass().toString());
-
- // for (ASTNode child : node.) {
- // printAST(child, indent + 1);
- // }
- }
-
-}
\ No newline at end of file
diff --git a/src/main/java/bytecode/ByteCodeGenerator.java b/src/main/java/bytecode/ByteCodeGenerator.java
index 5494255..99cf5c1 100644
--- a/src/main/java/bytecode/ByteCodeGenerator.java
+++ b/src/main/java/bytecode/ByteCodeGenerator.java
@@ -6,10 +6,16 @@ import bytecode.visitor.ProgramVisitor;
public class ByteCodeGenerator implements ProgramVisitor {
+ private final String outputDirectoryPath;
+
+ public ByteCodeGenerator(String outputDirectoryPath) {
+ this.outputDirectoryPath = outputDirectoryPath;
+ }
+
@Override
public void visit(ProgramNode programNode) {
for (ClassNode classDeclarationNode : programNode.classes) {
- ClassCodeGen classCodeGen = new ClassCodeGen();
+ ClassCodeGen classCodeGen = new ClassCodeGen(outputDirectoryPath);
classDeclarationNode.accept(classCodeGen);
}
}
diff --git a/src/main/java/bytecode/ClassCodeGen.java b/src/main/java/bytecode/ClassCodeGen.java
index b0afa69..089b567 100644
--- a/src/main/java/bytecode/ClassCodeGen.java
+++ b/src/main/java/bytecode/ClassCodeGen.java
@@ -6,7 +6,9 @@ import ast.member.MemberNode;
import ast.member.MethodNode;
import ast.type.BaseTypeNode;
import bytecode.visitor.ClassVisitor;
+
import java.io.File;
+
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
@@ -17,8 +19,10 @@ import java.io.IOException;
public class ClassCodeGen implements ClassVisitor {
private Mapper mapper;
private ClassWriter classWriter;
+ private final String outputDirectoryPath;
- public ClassCodeGen() {
+ public ClassCodeGen(String outputDirectoryPath) {
+ this.outputDirectoryPath = outputDirectoryPath;
mapper = new Mapper();
}
@@ -45,20 +49,21 @@ public class ClassCodeGen implements ClassVisitor {
@Override
public void visit(FieldNode fieldNode) {
- if(fieldNode.type instanceof BaseTypeNode baseTypeNode){
- classWriter.visitField(mapper.mapAccessTypeToOpcode(fieldNode.accessTypeNode), fieldNode.identifier, mapper.getTypeChar(baseTypeNode.enumType), null, null );
+ if (fieldNode.type instanceof BaseTypeNode baseTypeNode) {
+ classWriter.visitField(mapper.mapAccessTypeToOpcode(fieldNode.accessTypeNode), fieldNode.identifier, mapper.getTypeChar(baseTypeNode.enumType), null, null);
}
classWriter.visitEnd();
}
private void printIntoClassFile(byte[] byteCode, String name) {
- String directoryPath = "src/main/java/classFileOutput";
- File directory = new File(directoryPath);
+ // String outputDirectoryPath = "src/main/resources/output";
+ // System.out.println("Output directory path: " + outputDirectoryPath);
+ File directory = new File(outputDirectoryPath);
if (!directory.exists()) {
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/visitor/ClassVisitor.java b/src/main/java/bytecode/visitor/ClassVisitor.java
index 98ef25c..725b075 100644
--- a/src/main/java/bytecode/visitor/ClassVisitor.java
+++ b/src/main/java/bytecode/visitor/ClassVisitor.java
@@ -5,6 +5,7 @@ import ast.member.FieldNode;
import org.objectweb.asm.ClassWriter;
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 522ae9e..0000000
Binary files a/src/main/java/classFileOutput/Example.class and /dev/null differ
diff --git a/src/main/java/classFileOutput/Test.class b/src/main/java/classFileOutput/Test.class
deleted file mode 100644
index 8ba4c23..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..8b858b4
--- /dev/null
+++ b/src/main/java/main/Main.java
@@ -0,0 +1,108 @@
+package main;
+
+import ast.ASTNode;
+import ast.ProgramNode;
+import parser.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/resources/CompilerInput.java b/src/main/resources/CompilerInput.java
deleted file mode 100644
index 1cbe5ca..0000000
--- a/src/main/resources/CompilerInput.java
+++ /dev/null
@@ -1,18 +0,0 @@
-public class Example {
-
- public int a;
-
- public static int testMethod(char 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 66%
rename from src/main/test/java/MainTest.java
rename to src/test/java/main/MainTest.java
index f9e848e..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 ast.ClassNode;
-import ast.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..d599632
--- /dev/null
+++ b/src/test/java/parser/ParserTest.java
@@ -0,0 +1,184 @@
+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.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