Tests, Structure, More #10

Merged
i22005 merged 15 commits from Tests into main 2024-06-21 16:16:54 +00:00
4 changed files with 229 additions and 101 deletions
Showing only changes of commit 102961bccc - Show all commits

View File

@ -1,4 +1,3 @@
import ast.ASTNode;
import ast.ProgramNode;
import parser.ASTBuilder;
import parser.generated.SimpleJavaLexer;
@ -11,7 +10,7 @@ import org.antlr.v4.runtime.tree.ParseTree;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;
/**
* Start Raupenpiler with the following commands:
i22005 marked this conversation as resolved Outdated

Warum muss der compiler mit einem Makefile gestartet werden? Es reicht doch wenn wir die jar mit
Java compiler fileIn fileOut ausführt.

Warum muss der compiler mit einem Makefile gestartet werden? Es reicht doch wenn wir die jar mit Java compiler fileIn fileOut ausführt.
@ -19,6 +18,8 @@ import java.util.List;
* <p> <code> make clean compile-raupenpiler </code>
*/
public class Main {
public static void main(String[] args) throws Exception {
if (args.length > 0) {
// args[0] is the input file path
@ -45,105 +46,56 @@ public class Main {
*/
}
static void compileFile(CharStream inputCharStream, String outputDirectoryPath) {
/* ------------------------- Scanner -> tokens ------------------------- */
SimpleJavaLexer lexer = new SimpleJavaLexer(inputCharStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
tokenStream.fill();
// Printing the tokens
System.out.println("-------------------- Scanner -> Tokens --------------------");
List<Token> tokens = tokenStream.getTokens();
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)); //one line representation
// 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 -> typed AST -------------------------*/
ProgramNode typedAst = (ProgramNode) SemanticAnalyzer.generateTast(abstractSyntaxTree);
// Printing the typed AST
System.out.println("-------------------- Semantic Analyzer -> typed AST --------------------");
printAST(typedAst, 0);
System.out.println();
/*------------------------- Bytecode Generator -> Bytecode -------------------------*/
ByteCodeGenerator byteCodeGenerator = new ByteCodeGenerator(outputDirectoryPath);
byteCodeGenerator.visit(typedAst);
System.out.println("Bytecode generated");
}
/* ------------------------- 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 printTree(ParseTree tree, Parser parser, int indent) {
// Create an indentation string based on the current indentation level
String indentString = " ".repeat(indent * 2);
* 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.
i22011 marked this conversation as resolved Outdated

Können wir die ganze Ausgabe in ne extra Klasse auzslagern? Des macht die Main Klasse bissle arg unübersichtlich. Außerdem gehört des nacher nicht in den Endgültigen Compiler.

Können wir die ganze Ausgabe in ne extra Klasse auzslagern? Des macht die Main Klasse bissle arg unübersichtlich. Außerdem gehört des nacher nicht in den Endgültigen Compiler.
* <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 MyLogger();
// 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());
}
/* ------------------------- 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
MyLogger.logScanner(tokenStream);
i22011 marked this conversation as resolved Outdated

Same Here

Same Here
// 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);
}
}
/*------------------------- Parser -> Parse tree -------------------------*/
// 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
MyLogger.logParser(parseTree, parser);
// TODO: Fix this method
public static void printAST(ASTNode node, int indent) {
if (node == null) {
System.out.println("null");
return;
}
String indentString = " ".repeat(indent * 2);
System.out.println(indentString + node.getClass().toString());
/*------------------------- AST builder -> AST -------------------------*/
// Use the ASTBuilder to visit the ParseTree and generate an Abstract Syntax Tree (AST)
i22011 marked this conversation as resolved Outdated

Same Here

Same Here
ASTBuilder astBuilder = new ASTBuilder();
ProgramNode abstractSyntaxTree = (ProgramNode) astBuilder.visit(parseTree);
// Log the AST
MyLogger.logAST(abstractSyntaxTree);
// for (ASTNode child : node.) {
// printAST(child, indent + 1);
// }
}
/*------------------------- Semantic Analyzer -> typed AST -------------------------*/
// Use the SemanticAnalyzer to generate a typed AST
ProgramNode typedAst = (ProgramNode) SemanticAnalyzer.generateTast(abstractSyntaxTree);
i22011 marked this conversation as resolved Outdated

Same Here

Same Here
// Log the typed AST
MyLogger.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(typedAst);
// Log the bytecode generation
MyLogger.logBytecodeGenerator();
}
}

178
src/main/java/MyLogger.java Normal file
View File

@ -0,0 +1,178 @@
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 MyLogger {
static Logger logger = Logger.getLogger("RaupenLogs");
public MyLogger() {
// ------------------------- 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 node) {
// Printing the AST
logger.info("-------------------- AST builder -> AST --------------------");
// logger.info("AST: " + ast.toString());
logAST(node, 0);
logger.info("\n");
}
public static void logSemanticAnalyzer(ASTNode node) {
// Printing the typed AST
logger.info("-------------------- Semantic Analyzer -> typed AST --------------------");
logAST(node, 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 node, int indent) {
if (node == null) {
System.out.println("null");
return;
}
String indentString = " ".repeat(indent * 2);
logger.info(indentString + node.getClass().toString());
// for (ASTNode child : node.) {
// printAST(child, indent + 1);
// }
}
}

View File

@ -1,6 +1,6 @@
# 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 delete" etc.
### Or run only parts with "make compile-javac", "make clean" etc.
all: compile-javac compile-raupenpiler
@ -25,4 +25,5 @@ clean:
rm -f ./resources/output/raupenpiler/*.class
rm -f ./java/*.class
rm -f ../main/resources/output/*.class
rm -f ../main/resources/logs/*.log

View File

@ -1,6 +1,5 @@
package parser;
import ast.ProgramNode;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.jupiter.api.BeforeEach;
@ -10,9 +9,7 @@ import parser.generated.SimpleJavaParser;
import static org.junit.jupiter.api.Assertions.*;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.BooleanSupplier;
public class ParserTest {
@BeforeEach