Tests, Structure, More #10
6
.gitignore
vendored
6
.gitignore
vendored
@ -77,3 +77,9 @@ fabric.properties
|
|||||||
.idea/caches/build_file_checksums.ser
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
/target
|
/target
|
||||||
|
src/main/resources/logs/RaupenLog.log
|
||||||
|
src/main/resources/output/CompilerInput.class
|
||||||
|
src/test/resources/output/javac/CompilerInput$Test.class
|
||||||
|
src/test/resources/output/javac/CompilerInput.class
|
||||||
|
src/test/resources/output/raupenpiler/CompilerInput.class
|
||||||
|
src/test/resources/output/raupenpiler/CompilerInput$Test.class
|
||||||
|
32
pom.xml
32
pom.xml
@ -4,13 +4,14 @@
|
|||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<groupId>org.example</groupId>
|
<groupId>de.dhbw-stuttgart</groupId>
|
||||||
<artifactId>JavaCompiler</artifactId>
|
<artifactId>JavaCompiler</artifactId>
|
||||||
<version>1.0-SNAPSHOT</version>
|
<version>1.0</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>21</maven.compiler.source>
|
<java.version>22</java.version>
|
||||||
<maven.compiler.target>21</maven.compiler.target>
|
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||||
|
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
@ -44,7 +45,28 @@
|
|||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<version>3.0.0-M5</version> <!-- Change the version as needed -->
|
<version>3.0.0-M5</version> <!-- Change the version as needed -->
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>make-assembly</id> <!-- this is used for inheritance merges -->
|
||||||
|
<phase>package</phase> <!-- bind to the packaging phase -->
|
||||||
|
<goals>
|
||||||
|
<goal>single</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<mainClass>main.Main</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
<descriptorRefs>
|
||||||
|
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||||
|
</descriptorRefs>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -1,135 +0,0 @@
|
|||||||
import ast.ASTNode;
|
|
||||||
import org.antlr.v4.runtime.*;
|
|
||||||
import org.antlr.v4.runtime.Token;
|
|
||||||
import ast.ProgramNode;
|
|
||||||
import bytecode.ByteCodeGenerator;
|
|
||||||
import org.antlr.v4.runtime.CharStream;
|
|
||||||
import org.antlr.v4.runtime.CharStreams;
|
|
||||||
import org.antlr.v4.runtime.tree.ParseTree;
|
|
||||||
import org.antlr.v4.runtime.CommonTokenStream;
|
|
||||||
import parser.ASTBuilder;
|
|
||||||
import parser.generated.SimpleJavaLexer;
|
|
||||||
import parser.generated.SimpleJavaParser;
|
|
||||||
import semantic.SemanticAnalyzer;
|
|
||||||
import bytecode.ByteCodeGenerator;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class Main {
|
|
||||||
public static void main(String[] args) throws Exception {
|
|
||||||
if(args.length > 0) {
|
|
||||||
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
CharStream codeCharStream = CharStreams.fromPath(Paths.get("src/main/resources/CompilerInput.java"));
|
|
||||||
parseFile(codeCharStream);
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.err.println("Error reading the file: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void parseFile(CharStream codeCharStream) {
|
|
||||||
/* ------------------------- Scanner -> tokens ------------------------- */
|
|
||||||
SimpleJavaLexer lexer = new SimpleJavaLexer(codeCharStream);
|
|
||||||
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
|
|
||||||
|
|
||||||
// Printing the tokens
|
|
||||||
// tokenStream.fill();
|
|
||||||
// List<Token> 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);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -6,10 +6,16 @@ import bytecode.visitor.ProgramVisitor;
|
|||||||
|
|
||||||
public class ByteCodeGenerator implements ProgramVisitor {
|
public class ByteCodeGenerator implements ProgramVisitor {
|
||||||
|
|
||||||
|
private final String outputDirectoryPath;
|
||||||
|
|
||||||
|
public ByteCodeGenerator(String outputDirectoryPath) {
|
||||||
|
this.outputDirectoryPath = outputDirectoryPath;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ProgramNode programNode) {
|
public void visit(ProgramNode programNode) {
|
||||||
for (ClassNode classDeclarationNode : programNode.classes) {
|
for (ClassNode classDeclarationNode : programNode.classes) {
|
||||||
ClassCodeGen classCodeGen = new ClassCodeGen();
|
ClassCodeGen classCodeGen = new ClassCodeGen(outputDirectoryPath);
|
||||||
classDeclarationNode.accept(classCodeGen);
|
classDeclarationNode.accept(classCodeGen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,9 @@ import ast.member.MemberNode;
|
|||||||
import ast.member.MethodNode;
|
import ast.member.MethodNode;
|
||||||
import ast.type.BaseTypeNode;
|
import ast.type.BaseTypeNode;
|
||||||
import bytecode.visitor.ClassVisitor;
|
import bytecode.visitor.ClassVisitor;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import org.objectweb.asm.ClassWriter;
|
import org.objectweb.asm.ClassWriter;
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
@ -17,8 +19,10 @@ import java.io.IOException;
|
|||||||
public class ClassCodeGen implements ClassVisitor {
|
public class ClassCodeGen implements ClassVisitor {
|
||||||
private Mapper mapper;
|
private Mapper mapper;
|
||||||
private ClassWriter classWriter;
|
private ClassWriter classWriter;
|
||||||
|
private final String outputDirectoryPath;
|
||||||
|
|
||||||
public ClassCodeGen() {
|
public ClassCodeGen(String outputDirectoryPath) {
|
||||||
|
this.outputDirectoryPath = outputDirectoryPath;
|
||||||
mapper = new Mapper();
|
mapper = new Mapper();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,13 +56,14 @@ public class ClassCodeGen implements ClassVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void printIntoClassFile(byte[] byteCode, String name) {
|
private void printIntoClassFile(byte[] byteCode, String name) {
|
||||||
String directoryPath = "src/main/java/classFileOutput";
|
// String outputDirectoryPath = "src/main/resources/output";
|
||||||
File directory = new File(directoryPath);
|
// System.out.println("Output directory path: " + outputDirectoryPath);
|
||||||
|
File directory = new File(outputDirectoryPath);
|
||||||
if (!directory.exists()) {
|
if (!directory.exists()) {
|
||||||
directory.mkdirs();
|
directory.mkdirs();
|
||||||
}
|
}
|
||||||
|
|
||||||
String filePath = directoryPath + "/" + name + ".class";
|
String filePath = outputDirectoryPath + "/" + name + ".class";
|
||||||
try {
|
try {
|
||||||
FileOutputStream fileOutputStream = new FileOutputStream(filePath);
|
FileOutputStream fileOutputStream = new FileOutputStream(filePath);
|
||||||
fileOutputStream.write(byteCode);
|
fileOutputStream.write(byteCode);
|
||||||
|
@ -6,5 +6,6 @@ import org.objectweb.asm.ClassWriter;
|
|||||||
|
|
||||||
public interface ClassVisitor {
|
public interface ClassVisitor {
|
||||||
void visit(ClassNode classNode);
|
void visit(ClassNode classNode);
|
||||||
|
|
||||||
void visit(FieldNode fieldNode);
|
void visit(FieldNode fieldNode);
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Binary file not shown.
108
src/main/java/main/Main.java
Normal file
108
src/main/java/main/Main.java
Normal file
@ -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:
|
||||||
|
* <p> <code> cd .\src\test\ </code>
|
||||||
|
* <p> <code> make clean compile-raupenpiler </code>
|
||||||
|
* <p> Start Raupenpiler using jar:
|
||||||
|
* <p> <code> java.exe -jar path_to_jar\JavaCompiler-1.0-SNAPSHOT-jar-with-dependencies.jar 'path_to_input_file.java' 'path_to_output_directory' </code>
|
||||||
|
* <p> Example (jar needs to be in the target directory, compile with make or mvn package first):
|
||||||
|
* <code> java.exe -jar .\target\JavaCompiler-1.0-SNAPSHOT-jar-with-dependencies.jar 'src/main/resources/input/CompilerInput.java' 'src/main/resources/output' </code>
|
||||||
|
*/
|
||||||
|
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:
|
||||||
|
* <p>1. Scanner: It uses the SimpleJavaLexer to tokenize the input CharStream.
|
||||||
|
* <p>2. Parser: It uses the SimpleJavaParser to parse the tokens and generate a ParseTree.
|
||||||
|
* <p>3. AST Builder: It uses the ASTBuilder to visit the ParseTree and generate an Abstract Syntax Tree (AST).
|
||||||
|
* <p>4. Semantic Analyzer: It uses the SemanticAnalyzer to generate a typed AST.
|
||||||
|
* <p>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();
|
||||||
|
}
|
||||||
|
}
|
180
src/main/java/main/RaupenLogger.java
Normal file
180
src/main/java/main/RaupenLogger.java
Normal file
@ -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:
|
||||||
|
<p><code>logger.severe("Schwerwiegender Fehler");</code>
|
||||||
|
<p><code>logger.warning("Warnung");</code>
|
||||||
|
<p><code>logger.info("Information");</code>
|
||||||
|
<p><code>logger.config("Konfigurationshinweis");</code>
|
||||||
|
<p><code>logger.fine("Fein");</code>
|
||||||
|
<p><code>logger.finer("Feiner");</code>
|
||||||
|
<p><code>logger.finest("Am feinsten");</code>
|
||||||
|
<p>You may toggle the logging level of the console and file handlers by
|
||||||
|
changing the level ALL/OFF/etc. in the constructor.
|
||||||
|
<code>consoleHandler.setLevel(Level.OFF);</code>
|
||||||
|
<code>fileHandler.setLevel(Level.ALL);</code>
|
||||||
|
*/
|
||||||
|
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<Token> 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);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
@ -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){
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
16
src/main/resources/input/CompilerInput.java
Normal file
16
src/main/resources/input/CompilerInput.java
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Binary file not shown.
@ -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
|
|
29
src/test/Makefile
Normal file
29
src/test/Makefile
Normal file
@ -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
|
||||||
|
|
@ -1,5 +1,7 @@
|
|||||||
# Scanner
|
# Scanner
|
||||||
|
|
||||||
## Scanner Input
|
## Scanner Input
|
||||||
|
|
||||||
### Beispiel 1: Empty Class
|
### Beispiel 1: Empty Class
|
||||||
|
|
||||||
String empty class = "public class Name {}";
|
String empty class = "public class Name {}";
|
||||||
@ -15,6 +17,9 @@
|
|||||||
"}"
|
"}"
|
||||||
|
|
||||||
## Scanner Output
|
## Scanner Output
|
||||||
|
|
||||||
|
CommonTokenStream
|
||||||
|
|
||||||
### Beispiel 1: Empty Class
|
### Beispiel 1: Empty Class
|
||||||
|
|
||||||
Token Type; Token Text
|
Token Type; Token Text
|
||||||
@ -25,54 +30,63 @@ Type gibts nur bei Terminalen, Text bei allen
|
|||||||
Bsp von Ihm mal:
|
Bsp von Ihm mal:
|
||||||
[TokPublic,TokClass,TokIdentifier "Name",TokLeftBrace,TokRightBrace]
|
[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
|
||||||
|
|
||||||
## Parser Input
|
## Parser Input
|
||||||
|
|
||||||
|
CommonTokenStream
|
||||||
(Scanner Output)
|
(Scanner Output)
|
||||||
|
|
||||||
## Parser Output (AST)
|
## Parser Output (AST)
|
||||||
|
|
||||||
|
(program (classDeclaration (accessType public) class Name { }))
|
||||||
|
|
||||||
|
ParseTree
|
||||||
|
|
||||||
### Beispiel 1: Empty Class
|
### Beispiel 1: Empty Class
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Beispiel 2: Filled Class
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Semantische Analyse / Typcheck
|
# Semantische Analyse / Typcheck
|
||||||
|
|
||||||
## Typcheck Input
|
## Typcheck Input
|
||||||
|
|
||||||
(Parser Output = AST)
|
(Parser Output = AST)
|
||||||
|
|
||||||
## Typcheck Output
|
## Typcheck Output
|
||||||
|
|
||||||
### Beispiel 1: Empty Class
|
### Beispiel 1: Empty Class
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Beispiel 2: Filled Class
|
|
||||||
|
|
||||||
|
|
||||||
# Bytecodegenerierung
|
# Bytecodegenerierung
|
||||||
|
|
||||||
## Bytecodegenerierung Input
|
## Bytecodegenerierung Input
|
||||||
|
|
||||||
(Typcheck Output = vom Typcheck eventuell manipulierter AST)
|
(Typcheck Output = vom Typcheck eventuell manipulierter AST)
|
||||||
|
|
||||||
## Bytecodegenerierung Output
|
## Bytecodegenerierung Output
|
||||||
|
|
||||||
### Beispiel 1: Empty Class
|
### Beispiel 1: Empty Class
|
||||||
|
|
||||||
Compiled Classfile
|
Compiled Classfile
|
||||||
|
|
||||||
public class javaFileInput.Example {
|
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
|
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
|
@ -1,3 +1,5 @@
|
|||||||
|
package main;
|
||||||
|
|
||||||
public class EmptyClassExample {
|
public class EmptyClassExample {
|
||||||
private class Inner {
|
private class Inner {
|
||||||
}
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
package main;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
@ -15,17 +17,17 @@ import java.util.List;
|
|||||||
|
|
||||||
public class FailureTest {
|
public class FailureTest {
|
||||||
private static final List<String> TEST_FILES = Arrays.asList(
|
private static final List<String> TEST_FILES = Arrays.asList(
|
||||||
"src/main/test/resources/failureTests/TestClass1.java",
|
"src/main/test/resources/input/failureTests/TestClass1.java",
|
||||||
"src/main/test/resources/failureTests/TestClass2.java",
|
"src/main/test/resources/input/failureTests/TestClass2.java",
|
||||||
"src/main/test/resources/failureTests/TestClass3.java",
|
"src/main/test/resources/input/failureTests/TestClass3.java",
|
||||||
"src/main/test/resources/failureTests/TestClass4.java",
|
"src/main/test/resources/input/failureTests/TestClass4.java",
|
||||||
"src/main/test/resources/failureTests/TestClass5.java",
|
"src/main/test/resources/input/failureTests/TestClass5.java",
|
||||||
"src/main/test/resources/failureTests/TestClass6.java",
|
"src/main/test/resources/input/failureTests/TestClass6.java",
|
||||||
"src/main/test/resources/failureTests/TestClass7.java",
|
"src/main/test/resources/input/failureTests/TestClass7.java",
|
||||||
"src/main/test/resources/failureTests/TestClass8.java",
|
"src/main/test/resources/input/failureTests/TestClass8.java",
|
||||||
"src/main/test/resources/failureTests/TestClass9.java",
|
"src/main/test/resources/input/failureTests/TestClass9.java",
|
||||||
"src/main/test/resources/failureTests/TestClass10.java",
|
"src/main/test/resources/input/failureTests/TestClass10.java",
|
||||||
"src/main/test/resources/failureTests/TestClass11.java"
|
"src/main/test/resources/input/failureTests/TestClass11.java"
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,8 +64,8 @@ public class FailureTest {
|
|||||||
void typedASTTest() throws IOException {
|
void typedASTTest() throws IOException {
|
||||||
CharStream codeCharStream = null;
|
CharStream codeCharStream = null;
|
||||||
try {
|
try {
|
||||||
codeCharStream = CharStreams.fromPath(Paths.get("src/main/test/resources/EmptyClassExample.java"));
|
codeCharStream = CharStreams.fromPath(Paths.get("src/main/test/resources/main/EmptyClassExample.java"));
|
||||||
Main.parsefile(codeCharStream);
|
Main.compileFile(codeCharStream, "src/main/test/resources/output");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
System.err.println("Error reading the file: " + e.getMessage());
|
System.err.println("Error reading the file: " + e.getMessage());
|
||||||
}
|
}
|
@ -1,12 +1,8 @@
|
|||||||
|
package main;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.antlr.v4.runtime.CharStream;
|
import org.antlr.v4.runtime.CharStream;
|
||||||
import org.antlr.v4.runtime.CharStreams;
|
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.io.IOException;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@ -21,8 +17,8 @@ public class MainTest {
|
|||||||
void testEmptyClass() {
|
void testEmptyClass() {
|
||||||
CharStream codeCharStream = null;
|
CharStream codeCharStream = null;
|
||||||
try {
|
try {
|
||||||
codeCharStream = CharStreams.fromPath(Paths.get("src/main/test/resources/EmptyClassExample.java"));
|
codeCharStream = CharStreams.fromPath(Paths.get("src/main/test/resources/CompilerInput.java"));
|
||||||
Main.parsefile(codeCharStream);
|
Main.compileFile(codeCharStream, "src/main/test/resources/output");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
System.err.println("Error reading the file: " + e.getMessage());
|
System.err.println("Error reading the file: " + e.getMessage());
|
||||||
}
|
}
|
45
src/test/java/main/TestCompilerOutput.java
Normal file
45
src/test/java/main/TestCompilerOutput.java
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package main;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to test the output of the compiler.
|
||||||
|
*
|
||||||
|
* <p>Im gleichen Ordner wie diese Datei (main.TestCompilerOutput.java) muss die selbst kompilierte CompilerInput.class Datei sein.
|
||||||
|
* <br><strong>Hinweis:</strong> Diese muss man also vom Ordner <code> main/resources/output </code> in diesen Ordner hier (test/java) rein kopieren. (bis es eine bessere Lösung gibt)</p>
|
||||||
|
*
|
||||||
|
* <p>Die selbst kompilierte .class Datei wird dann hier drin geladen und eine Instanz von ihr erstellt, es können auch Methoden aufgerufen werden.
|
||||||
|
* <p>Diese main.TestCompilerOutput.java Datei wird dann in <code> \src\test\java> </code> mit <code>javac .\main.TestCompilerOutput.java</code> kompiliert und mit <code>java main.TestCompilerOutput</code> 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).</p>
|
||||||
|
*
|
||||||
|
* <p><strong>PROBLEM:</strong> Hier kommen Errors, was eigentlich heißt, dass der Compiler nicht funktioniert, der Test sollte eigentlich passen.
|
||||||
|
* <br><strong>DENN:</strong> Wenn ich statt unserem CompilerInput.class die CompilerInput.class von javac verwende (aus <code> src/test/resources/output/javac </code>), dann funktioniert es.</p>
|
||||||
|
*/
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
184
src/test/java/parser/ParserTest.java
Normal file
184
src/test/java/parser/ParserTest.java
Normal file
@ -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<Token> actualTokens = tokenStream.getTokens();
|
||||||
|
List<String> expectedTokens = Arrays.asList("public", "class", "Name", "{", "}", "<EOF>");
|
||||||
|
List<String> 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<String, Object> actualTreeStructure = buildTreeStructure(parseTree, parser);
|
||||||
|
Map<String, Object> 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<String, Object> buildTreeStructure(ParseTree tree, Parser parser) {
|
||||||
|
return buildTree(tree, parser, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, Object> buildTree(ParseTree tree, Parser parser, int indent) {
|
||||||
|
Map<String, Object> 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<Map<String, Object>> children = new ArrayList<>();
|
||||||
|
for (int i = 0; i < tree.getChildCount(); i++) {
|
||||||
|
children.add(buildTree(tree.getChild(i), parser, indent + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!children.isEmpty()) {
|
||||||
|
node.put("children", children);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers Variante 2.2
|
||||||
|
|
||||||
|
public static Map<String, Object> parseStringToTree(String input) {
|
||||||
|
input = input.trim();
|
||||||
|
if (input.startsWith("(") && input.endsWith(")")) {
|
||||||
|
input = input.substring(1, input.length() - 1).trim();
|
||||||
|
}
|
||||||
|
return parse(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, Object> parse(String input) {
|
||||||
|
Map<String, Object> node = new HashMap<>();
|
||||||
|
StringBuilder currentToken = new StringBuilder();
|
||||||
|
List<Map<String, Object>> children = new ArrayList<>();
|
||||||
|
|
||||||
|
int depth = 0;
|
||||||
|
boolean inToken = false;
|
||||||
|
for (char ch : input.toCharArray()) {
|
||||||
|
if (ch == '(') {
|
||||||
|
if (depth == 0) {
|
||||||
|
if (currentToken.length() > 0) {
|
||||||
|
node.put("node", currentToken.toString().trim());
|
||||||
|
currentToken.setLength(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentToken.append(ch);
|
||||||
|
}
|
||||||
|
depth++;
|
||||||
|
} else if (ch == ')') {
|
||||||
|
depth--;
|
||||||
|
if (depth == 0) {
|
||||||
|
children.add(parse(currentToken.toString().trim()));
|
||||||
|
currentToken.setLength(0);
|
||||||
|
} else {
|
||||||
|
currentToken.append(ch);
|
||||||
|
}
|
||||||
|
} else if (Character.isWhitespace(ch) && depth == 0) {
|
||||||
|
if (currentToken.length() > 0) {
|
||||||
|
node.put("node", currentToken.toString().trim());
|
||||||
|
currentToken.setLength(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentToken.append(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentToken.length() > 0) {
|
||||||
|
node.put("node", currentToken.toString().trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!children.isEmpty()) {
|
||||||
|
node.put("children", children);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package semantic;
|
package semantic;
|
||||||
|
|
||||||
|
|
||||||
import ast.*;
|
import ast.*;
|
||||||
import ast.expression.BinaryExpressionNode;
|
import ast.expression.BinaryExpressionNode;
|
||||||
import ast.expression.ExpressionNode;
|
import ast.expression.ExpressionNode;
|
||||||
@ -20,11 +19,10 @@ import ast.type.EnumTypeNode;
|
|||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import semantic.exeptions.AlreadyDeclearedException;
|
import semantic.exeptions.AlreadyDeclearedException;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
public class SemanticTest {
|
public class SemanticTest {
|
||||||
|
|
||||||
@ -35,9 +33,8 @@ public class SemanticTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void alreadyDeclaredLocalFieldVar() {
|
public void alreadyDeclaredLocalFieldVar() {
|
||||||
|
|
||||||
ProgramNode programNode = new ProgramNode();
|
ProgramNode programNode = new ProgramNode();
|
||||||
List<ClassNode> classList = new ArrayList<ClassNode>();
|
List<ClassNode> classList = new ArrayList<>();
|
||||||
AccessTypeNode accessTypeNode = new AccessTypeNode(EnumAccessTypeNode.PUBLIC);
|
AccessTypeNode accessTypeNode = new AccessTypeNode(EnumAccessTypeNode.PUBLIC);
|
||||||
ClassNode classNode = new ClassNode(accessTypeNode, "testClass");
|
ClassNode classNode = new ClassNode(accessTypeNode, "testClass");
|
||||||
|
|
||||||
@ -53,16 +50,14 @@ public class SemanticTest {
|
|||||||
ASTNode typedAst = SemanticAnalyzer.generateTast(programNode);
|
ASTNode typedAst = SemanticAnalyzer.generateTast(programNode);
|
||||||
|
|
||||||
assertEquals(1, SemanticAnalyzer.errors.size());
|
assertEquals(1, SemanticAnalyzer.errors.size());
|
||||||
assertEquals(true, SemanticAnalyzer.errors.get(0) instanceof AlreadyDeclearedException);
|
assertInstanceOf(AlreadyDeclearedException.class, SemanticAnalyzer.errors.getFirst());
|
||||||
assertEquals(null, typedAst);
|
assertNull(typedAst);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldWorkWithNoError() {
|
public void shouldWorkWithNoError() {
|
||||||
|
|
||||||
ProgramNode programNode = new ProgramNode();
|
ProgramNode programNode = new ProgramNode();
|
||||||
List<ClassNode> classList = new ArrayList<ClassNode>();
|
List<ClassNode> classList = new ArrayList<>();
|
||||||
AccessTypeNode accessTypeNode = new AccessTypeNode(EnumAccessTypeNode.PUBLIC);
|
AccessTypeNode accessTypeNode = new AccessTypeNode(EnumAccessTypeNode.PUBLIC);
|
||||||
ClassNode classNode = new ClassNode(accessTypeNode, "testClass");
|
ClassNode classNode = new ClassNode(accessTypeNode, "testClass");
|
||||||
|
|
||||||
@ -72,26 +67,7 @@ public class SemanticTest {
|
|||||||
MemberNode memberNode2 = new FieldNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar2");
|
MemberNode memberNode2 = new FieldNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar2");
|
||||||
classNode.members.add(memberNode2);
|
classNode.members.add(memberNode2);
|
||||||
|
|
||||||
List<ParameterNode> parameterNodeList = new ArrayList<ParameterNode>();
|
MemberNode memberNode3 = getMemberNode(accessTypeNode);
|
||||||
ParameterNode parameterNode1 = new ParameterNode(new BaseTypeNode(EnumTypeNode.INT), "param1");
|
|
||||||
parameterNodeList.add(parameterNode1);
|
|
||||||
ParameterListNode parameterListNode = new ParameterListNode(parameterNodeList);
|
|
||||||
|
|
||||||
List<StatementNode> statementNodeList = new ArrayList<StatementNode>();
|
|
||||||
|
|
||||||
ExpressionNode expressionNodeObjectVariableLeft = new IdentifierExpressionNode("this");
|
|
||||||
ExpressionNode expressionNodeObjectVariableRight = new IdentifierExpressionNode("objectVar");
|
|
||||||
|
|
||||||
ExpressionNode expressionNodeLeft = new BinaryExpressionNode(expressionNodeObjectVariableLeft, expressionNodeObjectVariableRight, ExpresssionOperator.DOT);
|
|
||||||
|
|
||||||
ExpressionNode expressionNodeRight = new LiteralNode(1);
|
|
||||||
|
|
||||||
BinaryExpressionNode expressionNode = new BinaryExpressionNode(expressionNodeLeft, expressionNodeRight, ExpresssionOperator.ASSIGNMENT);
|
|
||||||
|
|
||||||
StatementNode statementNode1 = new AssignmentStatementNode(expressionNode);
|
|
||||||
statementNodeList.add(statementNode1);
|
|
||||||
|
|
||||||
MemberNode memberNode3 = new MethodNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar2",parameterListNode, statementNodeList );
|
|
||||||
classNode.members.add(memberNode3);
|
classNode.members.add(memberNode3);
|
||||||
|
|
||||||
classList.add(classNode);
|
classList.add(classNode);
|
||||||
@ -101,7 +77,53 @@ public class SemanticTest {
|
|||||||
|
|
||||||
assertEquals(0, SemanticAnalyzer.errors.size());
|
assertEquals(0, SemanticAnalyzer.errors.size());
|
||||||
assertEquals(programNode, typedAst);
|
assertEquals(programNode, typedAst);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to create a MemberNode representing a method.
|
||||||
|
* It first creates a list of ParameterNodes and adds a ParameterNode to it.
|
||||||
|
* Then, it creates a ParameterListNode using the list of ParameterNodes.
|
||||||
|
* After that, it creates a list of StatementNodes and adds a StatementNode to it by calling the getStatementNode method.
|
||||||
|
* Finally, it creates a MethodNode using the provided AccessTypeNode, a BaseTypeNode representing the return type of the method,
|
||||||
|
* the method name, the ParameterListNode, and the list of StatementNodes, and returns this MethodNode.
|
||||||
|
*
|
||||||
|
* @param accessTypeNode The AccessTypeNode representing the access type of the method.
|
||||||
|
* @return The created MemberNode representing the method.
|
||||||
|
*/
|
||||||
|
private static MemberNode getMemberNode(AccessTypeNode accessTypeNode) {
|
||||||
|
List<ParameterNode> parameterNodeList = new ArrayList<>();
|
||||||
|
ParameterNode parameterNode1 = new ParameterNode(new BaseTypeNode(EnumTypeNode.INT), "param1");
|
||||||
|
parameterNodeList.add(parameterNode1);
|
||||||
|
ParameterListNode parameterListNode = new ParameterListNode(parameterNodeList);
|
||||||
|
|
||||||
|
List<StatementNode> statementNodeList = new ArrayList<>();
|
||||||
|
|
||||||
|
StatementNode statementNode1 = getStatementNode();
|
||||||
|
statementNodeList.add(statementNode1);
|
||||||
|
|
||||||
|
return new MethodNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar2", parameterListNode, statementNodeList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used to create a StatementNode for an assignment operation.
|
||||||
|
* It first creates two IdentifierExpressionNodes for 'this' and 'objectVar'.
|
||||||
|
* Then, it creates a BinaryExpressionNode to represent the operation 'this.objectVar'.
|
||||||
|
* After that, it creates a LiteralNode to represent the integer value 1.
|
||||||
|
* Finally, it creates another BinaryExpressionNode to represent the assignment operation 'this.objectVar = 1',
|
||||||
|
* and wraps this expression in an AssignmentStatementNode.
|
||||||
|
*
|
||||||
|
* @return The created AssignmentStatementNode representing the assignment operation 'this.objectVar = 1'.
|
||||||
|
*/
|
||||||
|
private static StatementNode getStatementNode() {
|
||||||
|
ExpressionNode expressionNodeObjectVariableLeft = new IdentifierExpressionNode("this");
|
||||||
|
ExpressionNode expressionNodeObjectVariableRight = new IdentifierExpressionNode("objectVar");
|
||||||
|
|
||||||
|
ExpressionNode expressionNodeLeft = new BinaryExpressionNode(expressionNodeObjectVariableLeft, expressionNodeObjectVariableRight, ExpresssionOperator.DOT);
|
||||||
|
|
||||||
|
ExpressionNode expressionNodeRight = new LiteralNode(1);
|
||||||
|
|
||||||
|
BinaryExpressionNode expressionNode = new BinaryExpressionNode(expressionNodeLeft, expressionNodeRight, ExpresssionOperator.ASSIGNMENT);
|
||||||
|
|
||||||
|
return new AssignmentStatementNode(expressionNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
16
src/test/resources/input/CompilerInput.java
Normal file
16
src/test/resources/input/CompilerInput.java
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user