diff --git a/.gitignore b/.gitignore
index f4669f3..3112014 100644
--- a/.gitignore
+++ b/.gitignore
@@ -88,3 +88,4 @@ fabric.properties
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
+/.metadata/
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..a55e7a1
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/XPPCompiler.xml b/.idea/runConfigurations/XPPCompiler.xml
new file mode 100644
index 0000000..af8592a
--- /dev/null
+++ b/.idea/runConfigurations/XPPCompiler.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..11eba31
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,23 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+
+
+ {
+ "type": "java",
+ "name": "Debug (Launch) - Current File",
+ "request": "launch",
+ "mainClass": "${file}"
+ },
+ {
+ "type": "java",
+ "name": "Debug (Launch)-Main",
+ "request": "launch",
+ "mainClass": "Main",
+ "projectName": "java"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/bin/test b/bin/test
new file mode 100644
index 0000000..59392a8
--- /dev/null
+++ b/bin/test
@@ -0,0 +1,10 @@
+//this is a simple comment to test
+
+x = /*fucking comment */ 10;
+int x = 10; //será que ignora?
+int y = 10;
+batata = 20;
+triangulo = 70;
+tri%%@ 43566¨
+
+=== 1/**/2
\ No newline at end of file
diff --git a/bin/test_preprocessed.txt b/bin/test_preprocessed.txt
new file mode 100644
index 0000000..a0bd0c5
--- /dev/null
+++ b/bin/test_preprocessed.txt
@@ -0,0 +1,11 @@
+
+
+x = 10;
+int x = 10;
+int y = 10;
+batata = 20;
+triangulo = 70;
+tri%%@ 43566¨
+
+=== 12
+$
diff --git a/src/java/Parser.java b/src/java/Parser.java
deleted file mode 100644
index aa7e4e0..0000000
--- a/src/java/Parser.java
+++ /dev/null
@@ -1,3 +0,0 @@
-public class Parser {
-
-}
\ No newline at end of file
diff --git a/src/java/Token.java b/src/java/Token.java
deleted file mode 100644
index c80884b..0000000
--- a/src/java/Token.java
+++ /dev/null
@@ -1,3 +0,0 @@
-public class Token {
-
-}
diff --git a/src/java/TokenGenerator.java b/src/java/TokenGenerator.java
deleted file mode 100644
index d9fbe4a..0000000
--- a/src/java/TokenGenerator.java
+++ /dev/null
@@ -1,3 +0,0 @@
-public class TokenGenerator {
-
-}
diff --git a/src/java/TokenType.java b/src/java/TokenType.java
deleted file mode 100644
index 52e0a8a..0000000
--- a/src/java/TokenType.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * Enum used to group all possible token types.
- * @author Gabriel Chiquetto, Leonardo-Rocha
-*/
-public enum TokenType {
- /** Undefined - used when doesn't match any type of token. */
- UNDEF,
- /** A sequence of letters, numbers and underscores. */
- IDENTIFIER,
- /** A sequence of decimal digits. */
- INTEGER_LITERAL,
- /** Relational operator '<'. */
- LESSTHAN,
- /** Relational operator '>'. */
- GREATER_THAN,
- /** Relational operator '<='. */
- LESS_OR_EQUAL,
- /** Relational operator '>='. */
- GREATER_OR_EQUAL,
- /** Relational operator '=='. */
- EQUAL,
- /** Relational operator '!='. */
- NOT_EQUAL,
- /** Addition operator '+'. */
- PLUS,
- /** Subtraction operator '-'. */
- MINUS,
- /** Multiplication operator '*'. */
- TIMES,
- /** Division operator '/'. */
- DIV,
- /** Modulo operator '%'. */
- MOD,
- /** Attribution operator '='. */
- ATTRIB,
- /** Left parenthesis separator '('. */
- LPAREN,
- /** Right parenthesis separator ')'. */
- RPAREN,
- /** Left bracket separator '['. */
- LBRACKET,
- /** Right bracket separator ']'. */
- RBRACKET,
- /** Left brace separator '{'. */
- LBRACE,
- /** Right brace separator '}'. */
- RBRACE,
- /** End of instruction indicator ';'. */
- SEMICOLON,
- /** Floating point number or object field accessor '.'. */
- DOT,
- /** Array element separator ',' - e.g. String[2] test = {"test1", "test2"}. */
- COMMA,
- /** String literal delimitation - e.g. String test = "teste". */
- DOUBLE_QUOTATION,
- /** Cpp-style single-line comment using "//". */
- LINE_COMMENT,
- /** C-style multi-line comment start using "/*".*/
- LBLOCK_COMMENT,
- /** C-style multi-line comment end using "*/".*/
- RBLOCK_COMMENT,
- /** End of file indicator. */
- EOF
-}
diff --git a/src/java/core/CompilerMain.java b/src/java/core/CompilerMain.java
new file mode 100644
index 0000000..f2b8f87
--- /dev/null
+++ b/src/java/core/CompilerMain.java
@@ -0,0 +1,76 @@
+package core;
+
+import utils.LexicalError;
+import utils.Preprocessor;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ * core.CompilerMain class.
+ *
+ * @author Leonardo-Rocha, Gabriel Chiquetto.
+ */
+public class CompilerMain {
+
+ public static void main(String[] args) throws IOException {
+
+ if (args.length > 0) {
+ File filePath = openFile(args[0]);
+
+ filePath = openFile(preprocessFile(filePath));
+
+ runTest(filePath);
+
+ LexicalError.computeErrorLog();
+
+ System.out.println("Process terminated.");
+ } else {
+ System.out.println("Please, insert a valid file path.");
+ }
+ }
+
+ /**
+ * @param path path of the file to be open.
+ * @return open file reference.
+ */
+ private static File openFile(String path) {
+ return new File(path);
+ }
+
+ /**
+ * Runs a test with the TokenGenerator to visually check if it's working.
+ *
+ * @param source file to run the token generator.
+ * @throws IOException if an error occurs during getNextToken().
+ */
+ private static void runTest(File source) throws IOException {
+ TokenGenerator tokenizer = new TokenGenerator(source);
+ Token currentToken = tokenizer.getNextToken();
+ while (!currentToken.equalsTokenType(TokenType.EOF)) {
+ currentToken.showCase();
+ currentToken = tokenizer.getNextToken();
+ }
+
+ }
+
+ /**
+ * @param rawSource source to preprocess.
+ * @return preprocessed file path.
+ */
+ private static String preprocessFile(File rawSource) {
+ System.out.println("Test file: " + rawSource.getAbsolutePath());
+ System.out.println("Pre-processing test file...");
+ try {
+ Preprocessor preprocessor = new Preprocessor();
+ preprocessor.preProcess(rawSource);
+ System.out.println("Preprocess successful.");
+ } catch (FileNotFoundException ex) {
+ System.out.println("Unable to open file '" + rawSource + "'");
+ } catch (IOException ex) {
+ System.out.println("Error reading file '" + rawSource + "'");
+ }
+ return rawSource + "_preprocessed.txt";
+ }
+}
\ No newline at end of file
diff --git a/src/java/core/Parser.java b/src/java/core/Parser.java
new file mode 100644
index 0000000..9941dc5
--- /dev/null
+++ b/src/java/core/Parser.java
@@ -0,0 +1,47 @@
+package core;
+
+import java.io.File;
+import java.io.IOException;
+
+public class Parser {
+
+ private TokenGenerator tokenGenerator;
+
+ private Token currentToken;
+
+ private void advanceToken() throws IOException{
+ currentToken = this.tokenGenerator.getNextToken();
+ }
+
+ public Parser(File sourceCode) throws IOException {
+ tokenGenerator = new TokenGenerator(sourceCode);
+ advanceToken();
+ }
+
+ public void match(TokenType type){
+ //if(type != currentToken.getAttribute())
+ }
+
+ public void program(){
+ if(currentToken.equalsTokenType(TokenType.CLASS)){
+ classList();
+ }
+ }
+
+ private void classList(){
+ classDecl();
+ classListLinha();
+ }
+
+ private void classListLinha(){
+ if(currentToken.equalsTokenType(TokenType.CLASS)){
+ classList();
+ }
+ }
+
+ private void classDecl(){
+ //match(CLASS);
+ //match(IDENTIFIER);
+ //classDeclLinha();
+ }
+}
\ No newline at end of file
diff --git a/src/java/core/Token.java b/src/java/core/Token.java
new file mode 100644
index 0000000..59b6079
--- /dev/null
+++ b/src/java/core/Token.java
@@ -0,0 +1,111 @@
+package core;
+
+/**
+ * This class represents every recognized single unit(token) of the source code.
+ * A token is a Pair and the attribute is optional.
+ *
+ * @author Gabriel Chiquetto, Leonardo-Rocha
+ */
+class Token {
+ /**
+ * Abstract symbol that represents a type of lexical unit.
+ */
+ private final TokenType tokenType;
+ /**
+ * Optional value of a token.
+ */
+ private TokenType attribute;
+ /**
+ * Sequence of characters that represents the token in the source code.
+ */
+ private String lexeme;
+
+ /**
+ * Initializes a token with the main fields.
+ *
+ * @param tokenType type of the generated token.
+ * @param attribute value of the token.
+ */
+ public Token(TokenType tokenType, TokenType attribute) {
+ this.tokenType = tokenType;
+ this.attribute = attribute;
+ }
+
+ /**
+ * Initializes a token without an attribute.
+ *
+ * @param tokenType type of the generated token.
+ */
+ public Token(TokenType tokenType) {
+ this(tokenType, TokenType.UNDEF);
+ }
+
+ /**
+ * Override of equals method.
+ * @param obj Object to evaluate.
+ * @return true if they're equal.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ Token token = (Token) obj;
+ return this.getTokenType() == token.getTokenType();
+ }
+
+ /**
+ * Compare token types.
+ * @param tokentype
+ * @return true if the type of this token equals the given token type.
+ */
+ public boolean equalsTokenType(TokenType tokentype){
+ return this.getTokenType() == tokentype;
+ }
+
+ /**
+ * Print token informations for debuggin purposes.
+ */
+ public void showCase() {
+ System.out.print("lexeme:" + this.getLexeme());
+ System.out.print(", core.TokenType: " + getTokenType());
+ System.out.println(", Attribute: " + getAttribute() + '.');
+ }
+
+ /**
+ *
+ * @return type of the token.
+ */
+ public TokenType getTokenType() {
+ return tokenType;
+ }
+
+ /**
+ *
+ * @return attribute of the token.
+ */
+ public TokenType getAttribute() {
+ return attribute;
+ }
+
+ /**
+ * Set the value of the field attribute.
+ * @param attribute value to set.
+ */
+ public void setAttribute(TokenType attribute){
+ this.attribute = attribute;
+ }
+
+ /**
+ *
+ * @return lexeme of the token.
+ */
+ public String getLexeme() {
+ return lexeme;
+ }
+
+ /**
+ * Should only be used once.
+ * @param lexeme string to set.
+ */
+ void setLexeme(String lexeme) {
+ this.lexeme = lexeme;
+ }
+}
diff --git a/src/java/core/TokenGenerator.java b/src/java/core/TokenGenerator.java
new file mode 100644
index 0000000..64f34a6
--- /dev/null
+++ b/src/java/core/TokenGenerator.java
@@ -0,0 +1,294 @@
+package core;
+
+import utils.LexicalError;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.LineNumberReader;
+
+import static java.lang.Character.*;
+
+/**
+ * Also known as Scanner or Lexer. This class does the lexical analysis and returns the tokens to the parser.
+ *
+ * @author Leonardo-Rocha, Gabriel Chiquetto.
+ */
+class TokenGenerator {
+ /**
+ * Scanner to parse the input.
+ */
+ private LineNumberReader lineNumberReader;
+
+ /**
+ * Line being evaluated.
+ */
+ private String currentLine;
+
+ /**
+ * Char at the current position.
+ */
+ private char currentChar;
+
+ /**
+ * Current position.
+ */
+ private int currentLinePosition;
+
+ private int lexemeStartPosition;
+
+ private char lastLineEndChar;
+
+ /**
+ * Position of the last returned token.
+ */
+ private int lastTokenEndPosition;
+
+ /**
+ * Lexeme of the last token returned.
+ */
+ private String lastLexeme;
+
+ private static final String OpString = "+-/%*";
+
+ /**
+ * Constructor.
+ *
+ * @param sourceCode File reference.
+ * @throws IOException if an error occurs during bufferedReader readline.
+ */
+ TokenGenerator(File sourceCode) throws IOException {
+ try {
+ FileReader fileReader = new FileReader(sourceCode);
+ lineNumberReader = new LineNumberReader(fileReader);
+
+ assert lineNumberReader != null;
+ currentLine = lineNumberReader.readLine();
+ currentLinePosition = 0;
+ lastTokenEndPosition = 0;
+ updateCurrentChar();
+ } catch (FileNotFoundException ex) {
+ System.out.println("Unable to open file '" + sourceCode + "'");
+ } catch (NullPointerException e) {
+ currentChar = '$';
+ }
+ }
+
+ /**
+ * @return next core.Token in the input.
+ * @throws IOException if an error occurs during advanceInput().
+ */
+ public Token getNextToken() throws IOException {
+
+ Token token = null;
+
+ while(isWhitespace(currentChar)){
+ advanceInput();
+ }
+ lexemeStartPosition = currentLinePosition;
+
+ if (isLetter(currentChar) || currentChar == '_') {
+ advanceInput();
+ while (isLetterOrDigit(currentChar) || currentChar == '_')
+ advanceInput();
+
+ token = new Token(TokenType.IDENTIFIER);
+ } else if (isDigit(currentChar)) {
+ advanceInput();
+ while (isDigit(currentChar)) {
+ advanceInput();
+ }
+
+ token = new Token(TokenType.INTEGER_LITERAL);
+ } else if (currentChar == '<') {
+ advanceInput();
+ if (currentChar == '=') {
+ advanceInput();
+
+ token = new Token(TokenType.REL_OP, TokenType.LESS_OR_EQUAL);
+ }
+ else{
+ token = new Token(TokenType.REL_OP, TokenType.LESS_THAN);
+ }
+
+ } else if (currentChar == '>') {
+ advanceInput();
+ if (currentChar == '=') {
+ advanceInput();
+
+ token = new Token(TokenType.REL_OP, TokenType.GREATER_OR_EQUAL);
+ }
+ else {
+ token = new Token(TokenType.REL_OP, TokenType.GREATER_THAN);
+ }
+ } else if (currentChar == '=') {
+ advanceInput();
+ if (currentChar == '=') {
+ advanceInput();
+
+ token = new Token(TokenType.REL_OP, TokenType.EQUAL);
+ } else {
+ token = new Token(TokenType.ATTRIB);
+ }
+ } else if (currentChar == '!') {
+ advanceInput();
+ if (currentChar == '=') {
+ advanceInput();
+
+ token = new Token(TokenType.NOT_EQUAL);
+ } else {
+ LexicalError.expectedChar('=', lineNumberReader.getLineNumber(), currentLinePosition);
+ }
+ } else if (currentChar == '+') {
+ advanceInput();
+
+ token = new Token(TokenType.PLUS);
+ } else if (currentChar == '-') {
+ advanceInput();
+
+ token = new Token(TokenType.MINUS);
+ } else if (currentChar == '*') {
+ advanceInput();
+
+ token = new Token(TokenType.TIMES);
+ } else if (currentChar == '/') {
+ advanceInput();
+
+ token = new Token(TokenType.DIV);
+ } else if (currentChar == '%') {
+ advanceInput();
+
+ token = new Token(TokenType.MOD);
+ } else if (currentChar == '(') {
+ advanceInput();
+
+ token = new Token(TokenType.LPAREN);
+ } else if (currentChar == ')') {
+ advanceInput();
+
+ token = new Token(TokenType.RPAREN);
+ } else if (currentChar == '{') {
+ advanceInput();
+
+ token = new Token(TokenType.LBRACE);
+ } else if (currentChar == '}') {
+ advanceInput();
+
+ token = new Token(TokenType.RBRACE);
+ } else if (currentChar == '[') {
+ advanceInput();
+
+ token = new Token(TokenType.LBRACKET);
+ } else if (currentChar == ']') {
+ advanceInput();
+
+ token = new Token(TokenType.RBRACKET);
+ } else if (currentChar == ';') {
+ advanceInput();
+ token = new Token(TokenType.SEMICOLON);
+ } else if (currentChar == '.') {
+ advanceInput();
+ token = new Token(TokenType.DOT);
+ } else if (currentChar == ',') {
+ advanceInput();
+
+ token = new Token(TokenType.COMMA);
+ } else if (currentChar == '"') {
+ advanceInput();
+
+ token = new Token(TokenType.DOUBLE_QUOTATION);
+ } else if (currentChar == '$') {
+ token = new Token(TokenType.EOF);
+ } else{
+ LexicalError.unexpectedChar(currentChar, lineNumberReader.getLineNumber(), currentLinePosition);
+ advanceInput();
+ token = new Token(TokenType.UNDEF);
+ }
+ updateLexeme();
+ token.setLexeme(lastLexeme);
+ if (token.equalsTokenType(TokenType.IDENTIFIER)) {
+ verifyKeywords(token);
+ }
+ return token;
+ }
+
+ /**
+ * Update lexeme according to the last valid token position.
+ */
+ private void updateLexeme() {
+ if(currentLinePosition != 0){
+ lastTokenEndPosition = currentLinePosition;
+ lastLexeme = currentLine.substring(lexemeStartPosition, lastTokenEndPosition);
+ }
+ else
+ lastLexeme = "" + lastLineEndChar;
+
+ }
+
+ /**
+ * Advance on input incrementing the line position and updating the current
+ * char.
+ *
+ * @throws IOException if an error occurs during bufferedReader readline.
+ */
+ private void advanceInput() throws IOException {
+ if (currentLinePosition + 1 != currentLine.length()) {
+ currentLinePosition++;
+ } else {
+ lastLineEndChar = currentChar;
+ currentLine = lineNumberReader.readLine();
+ currentLinePosition = 0;
+ }
+ updateCurrentChar();
+ }
+
+ private void updateCurrentChar() {
+ if (currentLine != null && currentLine.isEmpty()) {
+ currentChar = ' ';
+ } else {
+ currentChar = currentLine.charAt(currentLinePosition);
+ }
+ }
+
+ /**
+ * @param expectedOp char to evaluate.
+ * @return true if the expectedOp is an operator..
+ */
+ private boolean isOperator(char expectedOp) {
+ return OpString.contains("" + expectedOp);
+ }
+
+ private void verifyKeywords(Token identifier){
+ String lexeme = identifier.getLexeme();
+ switch(lexeme){
+ case "class":
+ identifier.setAttribute(TokenType.CLASS);
+ case "extends":
+ identifier.setAttribute(TokenType.EXTENDS);
+ case "int":
+ identifier.setAttribute(TokenType.INT);
+ case "String":
+ identifier.setAttribute(TokenType.STRING);
+ case "break":
+ identifier.setAttribute(TokenType.BREAK);
+ case "print":
+ identifier.setAttribute(TokenType.PRINT);
+ case "read":
+ identifier.setAttribute(TokenType.READ);
+ case "return":
+ identifier.setAttribute(TokenType.SUPER);
+ case "if":
+ identifier.setAttribute(TokenType.IF);
+ case "else":
+ identifier.setAttribute(TokenType.ELSE);
+ case "for":
+ identifier.setAttribute(TokenType.FOR);
+ case "new":
+ identifier.setAttribute(TokenType.NEW);
+ case "constructor":
+ identifier.setAttribute(TokenType.CONSTRUCTOR);
+ default:
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/java/core/TokenType.java b/src/java/core/TokenType.java
new file mode 100644
index 0000000..1e73904
--- /dev/null
+++ b/src/java/core/TokenType.java
@@ -0,0 +1,173 @@
+package core;
+
+/**
+ * Enum used to group all possible token types.
+ *
+ * @author Gabriel Chiquetto, Leonardo-Rocha
+ */
+enum TokenType {
+ /**
+ * Undefined - used when doesn't match any type of token.
+ */
+ UNDEF,
+ /**
+ * A sequence of letters, numbers and underscores.
+ */
+ IDENTIFIER,
+ /**
+ * Used for class declarations.
+ */
+ CLASS,
+ /**
+ * Used for class inheritance.
+ */
+ EXTENDS,
+ /**
+ * Primitive type declaration "integer".
+ */
+ INT,
+ /**
+ * String reference declaration.
+ */
+ STRING,
+ /**
+ * Terminates execution of current scope.
+ */
+ BREAK,
+ /**
+ * Show a message in the standard output.
+ */
+ PRINT,
+ /**
+ * Read from standard input.
+ */
+ READ,
+ /**
+ * It causes program control to transfer back to the caller of the method.
+ */
+ RETURN,
+ /**
+ * References the immediate parent of a class.
+ */
+ SUPER,
+ /**
+ * Conditional statement that executes a block when the expression is true.
+ */
+ IF,
+ /**
+ * Executes when the Boolean expression of the matching "if" is false.
+ */
+ ELSE,
+ /**
+ * Iterative Loop control structure.
+ */
+ FOR,
+ /**
+ * Memory allocation for a new object.
+ */
+ NEW,
+ /**
+ * Method called when the object is allocated.
+ */
+ CONSTRUCTOR,
+ /**
+ * A sequence of decimal digits.
+ */
+ INTEGER_LITERAL,
+ /**
+ * Relational operator attribute.
+ */
+ REL_OP,
+ /**
+ * Relational operator '<'.
+ */
+ LESS_THAN,
+ /**
+ * Relational operator '>'.
+ */
+ GREATER_THAN,
+ /**
+ * Relational operator '<='.
+ */
+ LESS_OR_EQUAL,
+ /**
+ * Relational operator '>='.
+ */
+ GREATER_OR_EQUAL,
+ /**
+ * Relational operator '=='.
+ */
+ EQUAL,
+ /**
+ * Relational operator '!='.
+ */
+ NOT_EQUAL,
+ /**
+ * Addition operator '+'.
+ */
+ PLUS,
+ /**
+ * Subtraction operator '-'.
+ */
+ MINUS,
+ /**
+ * Multiplication operator '*'.
+ */
+ TIMES,
+ /**
+ * Division operator '/'.
+ */
+ DIV,
+ /**
+ * Modulo operator '%'.
+ */
+ MOD,
+ /**
+ * Attribution operator '='.
+ */
+ ATTRIB,
+ /**
+ * Left parenthesis separator '('.
+ */
+ LPAREN,
+ /**
+ * Right parenthesis separator ')'.
+ */
+ RPAREN,
+ /**
+ * Left bracket separator '['.
+ */
+ LBRACKET,
+ /**
+ * Right bracket separator ']'.
+ */
+ RBRACKET,
+ /**
+ * Left brace separator '{'.
+ */
+ LBRACE,
+ /**
+ * Right brace separator '}'.
+ */
+ RBRACE,
+ /**
+ * End of instruction indicator ';'.
+ */
+ SEMICOLON,
+ /**
+ * Floating point number or object field accessor '.'.
+ */
+ DOT,
+ /**
+ * Array element separator ',' - e.g. String[2] test = {"test1", "test2"}.
+ */
+ COMMA,
+ /**
+ * String literal delimitation - e.g. String test = "test".
+ */
+ DOUBLE_QUOTATION,
+ /**
+ * End of file indicator.
+ */
+ EOF
+}
diff --git a/src/java/gui/GUIController.java b/src/java/gui/GUIController.java
new file mode 100644
index 0000000..d599915
--- /dev/null
+++ b/src/java/gui/GUIController.java
@@ -0,0 +1,131 @@
+package gui;
+
+import core.CompilerMain;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.scene.control.Menu;
+import javafx.scene.control.MenuItem;
+import javafx.scene.control.TextArea;
+import javafx.stage.FileChooser;
+import javafx.stage.Stage;
+
+import javax.swing.*;
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+public class GUIController {
+
+ @FXML
+ private MenuItem openFileButton;
+ @FXML
+ private MenuItem newFileButton;
+ @FXML
+ private MenuItem saveFileButton;
+ @FXML
+ private Menu compileButton;
+ @FXML
+ private TextArea editorTextArea;
+ @FXML
+ private TextArea consoleTextArea;
+
+ private FileChooser fileChooser;
+ private Stage stage;
+ private File currentFile;
+
+ public void actionOpenFile(ActionEvent actionEvent) throws IOException {
+ System.out.println("Opening file...");
+
+ if (fileChooser == null || stage == null)
+ return;
+
+ currentFile = fileChooser.showOpenDialog(stage);
+ openCurrentFile();
+ }
+
+ public void actionNewFile(ActionEvent actionEvent) throws IOException {
+ System.out.println("Creating a new file...");
+ if (fileChooser == null || stage == null)
+ return;
+
+ currentFile = fileChooser.showSaveDialog(stage);
+ openCurrentFile();
+ }
+
+ public void actionSaveFile(ActionEvent actionEvent) throws IOException {
+ System.out.println("Saving file...");
+ String buffer = editorTextArea.getText();
+ try {
+ writeStringToFile(currentFile, buffer);
+ JOptionPane.showMessageDialog(null, "File saved successfully!");
+ }
+ catch (IOException e) {
+ JOptionPane.showMessageDialog(null, "Failed to save file.");
+ }
+ }
+
+ public void writeStringToFile(File file, String text) throws IOException {
+ if (file == null)
+ throw new IllegalArgumentException("File cannot be null.");
+
+ if (text == null)
+ throw new IllegalArgumentException("Text cannot be null.");
+
+ String filePath = file.getAbsolutePath();
+
+ try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(filePath))) {
+ writer.write(text);
+ }
+ }
+
+ public void actionCompileProgram(ActionEvent actionEvent) {
+ System.out.println("Compiling...");
+ try {
+ String[] parameters = {currentFile.getAbsolutePath()};
+ CompilerMain.main(parameters);
+ }
+ catch (IOException e) {
+ System.out.println("Invalid file.");
+ }
+ }
+
+ private void openCurrentFile() throws IOException {
+ if (currentFile == null)
+ throw new IllegalArgumentException("File cannot be null.");
+
+ editorTextArea.setText("");
+ LineNumberReader lineNumberReader = new LineNumberReader(new FileReader(currentFile));
+ String currentLine;
+ while ((currentLine = lineNumberReader.readLine()) != null) {
+ editorTextArea.appendText(currentLine + "\n");
+ }
+ }
+
+ public MenuItem getOpenFileButton() {
+ return openFileButton;
+ }
+
+ public MenuItem getNewFileButton() {
+ return newFileButton;
+ }
+
+ public MenuItem getSaveFileButton() {
+ return saveFileButton;
+ }
+
+ public Menu getCompileButton() {
+ return compileButton;
+ }
+
+ public FileChooser getFileChooser() {
+ return fileChooser;
+ }
+
+ public void setFileChooser(FileChooser fileChooser) {
+ this.fileChooser = fileChooser;
+ }
+
+ public void setStage(Stage stage) {
+ this.stage = stage;
+ }
+}
diff --git a/src/java/gui/MainGUI.java b/src/java/gui/MainGUI.java
new file mode 100644
index 0000000..051f2b7
--- /dev/null
+++ b/src/java/gui/MainGUI.java
@@ -0,0 +1,32 @@
+package gui;
+
+import javafx.application.Application;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.stage.FileChooser;
+import javafx.stage.Stage;
+
+public class MainGUI extends Application {
+
+ @Override
+ public void start(Stage primaryStage) throws Exception{
+
+ FXMLLoader loader = new FXMLLoader(getClass().getResource("/gui/xpp-compiler-gui.fxml"));
+ Parent root = loader.load();
+ primaryStage.setTitle("XPP Compiler");
+ primaryStage.setScene(new Scene(root, 300, 275));
+
+ FileChooser fileChooser = new FileChooser();
+
+ GUIController controller = loader.getController();
+ controller.setFileChooser(fileChooser);
+ controller.setStage(primaryStage);
+
+ primaryStage.show();
+ }
+
+ public static void main(String[] args) {
+ launch(args);
+ }
+}
diff --git a/src/java/gui/xpp-compiler-gui.css b/src/java/gui/xpp-compiler-gui.css
new file mode 100644
index 0000000..1f4e77b
--- /dev/null
+++ b/src/java/gui/xpp-compiler-gui.css
@@ -0,0 +1,45 @@
+.root {
+ -fx-background-color: #3c3f41;
+}
+.label {
+ -fx-font-size: 12;
+ -fx-text-fill: white;
+}
+
+.border-pane {
+ -fx-background-color: #313335;
+ -fx-border-color: #ffff;
+}
+
+.text-area .content {
+ -fx-background-color: #2b2b2b;
+}
+
+.text-area {
+ -fx-font-size: 12;
+ -fx-text-fill: white;
+}
+
+.scroll-pane {
+ -fx-background-color: #2b2b2b;
+ -fx-fill: #2b2b2b;
+}
+
+.separator {
+ -fx-color-label-visible: #3c3f41;
+}
+
+.anchor-pane {
+ -fx-background-color: #2b2b2b;
+ -fx-fill: #2b2b2b;
+}
+
+.menu-bar {
+ -fx-background-color: #3c3f41;
+ -fx-border-color: #ffff;
+}
+
+.menu-item {
+ -fx-background-color: #3c3f41;
+ -fx-text-fill: #bbbbbb;
+}
\ No newline at end of file
diff --git a/src/java/gui/xpp-compiler-gui.fxml b/src/java/gui/xpp-compiler-gui.fxml
new file mode 100644
index 0000000..a2fd6ee
--- /dev/null
+++ b/src/java/gui/xpp-compiler-gui.fxml
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/java/java.iml b/src/java/java.iml
index c90834f..b16c4e5 100644
--- a/src/java/java.iml
+++ b/src/java/java.iml
@@ -4,8 +4,10 @@
+
+
\ No newline at end of file
diff --git a/src/java/utils/LexicalError.java b/src/java/utils/LexicalError.java
new file mode 100644
index 0000000..1983079
--- /dev/null
+++ b/src/java/utils/LexicalError.java
@@ -0,0 +1,37 @@
+package utils;
+
+public class LexicalError {
+ /**
+ * Indicates the error state.
+ */
+ private static boolean errorState = false;
+
+ /**
+ * Error log message.
+ */
+ private static String errorLog = "";
+
+ /**
+ * Unexpected char error.
+ *
+ * @param unexpectedChar char to display why is an error.
+ */
+ public static void unexpectedChar(char unexpectedChar, int line, int position) {
+ errorLog = errorLog + ("Unexpected char found: '" + unexpectedChar + "'in line: "+ line + ":" + position + "\n");
+ errorState = true;
+ }
+
+ public static void expectedChar(char expectedChar, int line, int position) {
+ errorLog = errorLog + ("expected char missing: '" + expectedChar + "'in line: "+ line + ":" + position + "\n");
+ errorState = true;
+ }
+
+ /**
+ * Log error message.
+ */
+ public static void computeErrorLog() {
+ if (errorState) {
+ System.out.println(errorLog);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/java/utils/Preprocessor.java b/src/java/utils/Preprocessor.java
new file mode 100644
index 0000000..9c40344
--- /dev/null
+++ b/src/java/utils/Preprocessor.java
@@ -0,0 +1,224 @@
+package utils;
+
+import java.io.*;
+
+/**
+ * Class to Preprocess files.
+ *
+ * @author Leonardo-Rocha.
+ */
+public class Preprocessor {
+ /**
+ * Definition of End Of File Marker.
+ */
+ private static final char EOF = '$';
+
+ /**
+ * Maximum number of lines in the source code.
+ */
+ private static final int MAX_BUFFER_SIZE = 8192;
+
+ /**
+ * Preprocessed output.
+ */
+ private String[] output;
+
+ /**
+ * Buffered reader containing the source code.
+ */
+ private LineNumberReader lineNumberReader;
+
+ /**
+ * Current line being read.
+ */
+ private String currentLine;
+
+ /**
+ * PreProcess file.
+ *
+ * @param filePath path of the file to be preprocessed.
+ * @throws IOException if something goes wrong during removeCommentsAndAddEOF.
+ */
+ public void preProcess(File filePath) throws IOException {
+ output = new String[MAX_BUFFER_SIZE];
+ FileReader fileReader = new FileReader(filePath);
+ lineNumberReader = new LineNumberReader(fileReader);
+
+ removeCommentsAndAddEOF();
+
+ lineNumberReader.close();
+
+ printOutputAndWriteToFile(filePath);
+ }
+
+ /**
+ * Remove single-line/multi-line comments and add EOF to the end of the file.
+ *
+ * @throws IOException if something goes wrong during buffer line reading.
+ */
+ private void removeCommentsAndAddEOF() throws IOException {
+
+ int outputLineIndex = 0;
+
+ while ((currentLine = lineNumberReader.readLine()) != null) {
+ if (isBeginMultiLineComment()) {
+ outputLineIndex = handleMultiLineComments(outputLineIndex);
+ } else if (isSingleLineComment()) {
+ outputLineIndex = handleSingleLineComments(outputLineIndex);
+ } else {
+ output[outputLineIndex++] = currentLine;
+ }
+ }
+ output[outputLineIndex] = "" + EOF;
+ }
+
+ /**
+ * @param outputLineIndex last valid output line index.
+ * @return outputLineIndex.
+ * @throws IOException if something goes wrong during splitMultiLineComment.
+ */
+ private int handleMultiLineComments(int outputLineIndex) throws IOException {
+ String[] validCode = splitMultiLineComment();
+ for (String line : validCode) {
+ if (isValidLine(line))
+ output[outputLineIndex++] = line;
+ }
+ return outputLineIndex;
+ }
+
+ /**
+ * @param outputLineIndex last valid output line index.
+ * @return outputLineIndex.
+ */
+ private int handleSingleLineComments(int outputLineIndex) {
+ String[] splitStrings;
+ String processedLine = "";
+ if (currentLine.contains("//")){
+ splitStrings = currentLine.split("//");
+ processedLine = splitStrings[0];
+ }
+ output[outputLineIndex++] = processedLine;
+
+ return outputLineIndex;
+ }
+
+ /**
+ * Print preprocessed code and write in a new file with the same name + _preprocessed.txt.
+ *
+ * @param filePath original file path.
+ */
+ private void printOutputAndWriteToFile(File filePath) throws IOException {
+
+ BufferedWriter outputWriter;
+ outputWriter = new BufferedWriter(new FileWriter(filePath + "_preprocessed.txt"));
+
+ for (String line : getOutput()) {
+ System.out.println(line);
+ if (isValidLine(line)) {
+ outputWriter.write(line);
+ outputWriter.newLine();
+ }
+ if (line.equals("" + EOF))
+ break;
+ }
+ outputWriter.close();
+ }
+
+ /**
+ * @param line line to be evaluated.
+ * @return true if the line is valid.
+ */
+ private boolean isValidLine(String line) {
+ return line != null;
+ }
+
+ /**
+ * @return true if the current line begins a Multi-line comment.
+ */
+ private boolean isBeginMultiLineComment() {
+ return currentLine != null && currentLine.contains("/*");
+ }
+
+ /**
+ * @return true if the current line NOT ends a Multi-line comment
+ */
+ private boolean isNotEndMultiLineComment() {
+ String EOF = "" + Preprocessor.EOF;
+ boolean NotEndMultiLineComment = currentLine != null && !currentLine.contains("*/");
+ return NotEndMultiLineComment && !currentLine.contains(EOF);
+ }
+
+ /**
+ * @return true if the current line is a single-line comment.
+ */
+ private boolean isSingleLineComment() {
+ return currentLine != null && currentLine.contains("//");
+ }
+
+ /**
+ * @return processed String[] without Multi-line comment.
+ * @throws IOException if something goes wrong during buffer line reading or string splitting..
+ */
+ private String[] splitMultiLineComment() throws IOException {
+ int linesDistance = 0;
+ String[] output = new String[2];
+
+ String[] splitStrings = currentLine.split("/\\*");
+
+ output[0] = getValidStatementAtPosition(splitStrings, 0);
+ linesDistance = findEndMultiLineComment(linesDistance);
+
+ if (currentLine == null) {
+ output[1] = "";
+ } else if (linesDistance == 0) {
+ splitStrings = currentLine.split("\\*/");
+ output[0] += getValidStatementAtPosition(splitStrings, 1);
+ } else {
+ splitStrings = currentLine.split("\\*/");
+ output[1] = getValidStatementAtPosition(splitStrings, 1);
+ }
+
+ return output;
+ }
+
+ /**
+ * @param linesDistance Distance between the initial read line and the line where the end Multi-line is found.
+ * @return linesDistance after the search.
+ * @throws IOException if an error occurs during buffer line reading.
+ */
+ private int findEndMultiLineComment(int linesDistance) throws IOException {
+ while (isNotEndMultiLineComment()) {
+ currentLine = lineNumberReader.readLine();
+ linesDistance++;
+ }
+ return linesDistance;
+ }
+
+ /**
+ * Check the array length and access the given position.
+ *
+ * @param splitStrings array of strings to be filtered.
+ * @param position array position.
+ * @return validStatement string.
+ */
+ private static String getValidStatementAtPosition(String[] splitStrings, int position) {
+ String validStatement;
+
+ if (splitStrings != null && splitStrings.length > position)
+ validStatement = splitStrings[position];
+ else
+ validStatement = "";
+
+ return validStatement;
+ }
+
+ /**
+ * Get the preprocessed output.
+ *
+ * @return output.
+ */
+ @SuppressWarnings("WeakerAccess")
+ public String[] getOutput() {
+ return output;
+ }
+}