diff --git a/.gitignore b/.gitignore index 4a79f71..89f9ac0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1 @@ -/.vscode - -*.out \ No newline at end of file +out/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1aaeb5d --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +CC = g++ +TARGET_NAME = vm-translator +OUT_DIR = out +TARGET = $(OUT_DIR)/$(TARGET_NAME) +SRCS = src/main.cpp src/parser.cpp src/code.cpp src/utils.cpp +OBJS = $(SRCS:src/%.cpp=$(OUT_DIR)/%.o) + +$(TARGET): $(OBJS) + @mkdir -p $(OUT_DIR) + @$(CC) $^ -o $@ + @echo "Built $(TARGET_NAME): $@" + +$(OUT_DIR)/%.o: src/%.cpp + @mkdir -p $(OUT_DIR) + @$(CC) -c $< -o $@ + +clean: + @rm -rf out + @echo "Cleaned all builds." + +.PHONY: all run clean diff --git a/README.md b/README.md index 951cec0..e955684 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,3 @@ # Jack Compiler This is a compiler made as the an assignment for the [Nand To Tetris Course: Part 2](https://nand2tetris.org/project07). It assumes that the jack file does not have errors for now. - -To compile the program run the following command: -``` -g++ main.cpp -Isrc -std=c++17 -``` -To run the executable against jack vm files run the following command with the path of the file: -``` -./a.out -``` -The file will be written next to the source file. diff --git a/include/branching.h b/include/branching.h deleted file mode 100644 index c283830..0000000 --- a/include/branching.h +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include -#include - -using namespace std; - -string translateLabel(string label) -{ - stringstream output; - - output << "(" << label << ")" << endl; - - return output.str(); -} - -string translateGoto(string label) -{ - stringstream output; - - output << "@" << label << endl; - output << "0;JMP" << endl; - - return output.str(); -} - -string translateIfGoto(string label) -{ - stringstream output; - - output << "@SP" << endl; - output << "M=M-1" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - - output << "@" << label << endl; - output << "D;JNE" << endl; - - return output.str(); -} \ No newline at end of file diff --git a/include/code.h b/include/code.h deleted file mode 100644 index 87991e8..0000000 --- a/include/code.h +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include -#include -#include "types.h" -#include "utils.h" -#include "operations.h" -#include "memory.h" -#include "branching.h" -#include "functions.h" - -using namespace std; - -class Code -{ -private: - ofstream file; - string filename; - vector> commands; - - void closeFile() - { - file.close(); - } - -public: - Code(string path, vector> tokens, bool isNew) - { - size_t slashIndex = path.find_last_of('/'); - size_t dotIndex = path.find_last_of('.'); - - if (slashIndex != string::npos && dotIndex != string::npos) - { - filename = path.substr(slashIndex + 1, dotIndex - slashIndex - 1); - } - - file = ofstream(path, isNew ? ios_base::out : ios_base::app); - commands = tokens; - - if (isNew) { - file << "@256" << endl; - file << "D=A" << endl; - file << "@SP" << endl; - file << "M=D" << endl; - file << translateCall("Sys.init", 0); - } - } - - ~Code() - { - closeFile(); - } - - void translate() - { - for (const vector &vec : commands) - { - if (vec.size() == 3) - { - switch (determineTwoArgumentCommand(vec[0])) - { - case PUSH: - file << translatePush(filename, determineSegment(vec[1]), stoi(vec[2])); - break; - case FUNCTION: - file << translateFunction(vec[1], stoi(vec[2])); - break; - case CALL: - file << translateCall(vec[1], stoi(vec[2])); - break; - case POP: - default: - file << translatePop(filename, determineSegment(vec[1]), stoi(vec[2])); - } - } - else if (vec.size() == 2) - { - switch (determineOneArgumentCommand(vec[0])) - { - case LABEL: - file << translateLabel(vec[1]); - break; - case GOTO: - file << translateGoto(vec[1]); - break; - case IFGOTO: - default: - file << translateIfGoto(vec[1]); - break; - } - } - else if (vec.size() == 1) - { - switch (determineNoArgumentCommand(vec[0])) - { - case ADD: - file << translateAdd(); - break; - case SUB: - file << translateSub(); - break; - case NEG: - file << translateNeg(); - break; - case EQ: - file << translateEq(); - break; - case GT: - file << translateGt(); - break; - case LT: - file << translateLt(); - break; - case AND: - file << translateAnd(); - break; - case OR: - file << translateOr(); - break; - case NOT: - file << translateNot(); - break; - case RETURN: - file << translateReturn(); - break; - default: - file << translateNeg(); - break; - } - } - } - } -}; diff --git a/include/functions.h b/include/functions.h deleted file mode 100644 index e6512fc..0000000 --- a/include/functions.h +++ /dev/null @@ -1,182 +0,0 @@ -#include -#include -#include - -using namespace std; - -int callCounter = 1; - -string translateFunction(string name, int args) -{ - stringstream output; - - output << "(" << name << ")" << endl; - - for (int i = 0; i < args; ++i) - { - output << "@0" << endl; - output << "D=A" << endl; - output << "@SP" << endl; - output << "A=M" << endl; - output << "M=D" << endl; - output << "@SP" << endl; - output << "M=M+1" << endl; - } - - return output.str(); -} - -string translateCall(string name, int args) -{ - stringstream output; - string label = name + "$ret" + std::to_string(callCounter); - - callCounter++; - - output << "@" << label << endl; - output << "D=A" << endl; - output << "@SP" << endl; - output << "A=M" << endl; - output << "M=D" << endl; - output << "@SP" << endl; - output << "M=M+1" << endl; - - output << "@LCL" << endl; - output << "D=M" << endl; - output << "@SP" << endl; - output << "A=M" << endl; - output << "M=D" << endl; - output << "@SP" << endl; - output << "M=M+1" << endl; - - output << "@ARG" << endl; - output << "D=M" << endl; - output << "@SP" << endl; - output << "A=M" << endl; - output << "M=D" << endl; - output << "@SP" << endl; - output << "M=M+1" << endl; - - output << "@THIS" << endl; - output << "D=M" << endl; - output << "@SP" << endl; - output << "A=M" << endl; - output << "M=D" << endl; - output << "@SP" << endl; - output << "M=M+1" << endl; - - output << "@THAT" << endl; - output << "D=M" << endl; - output << "@SP" << endl; - output << "A=M" << endl; - output << "M=D" << endl; - output << "@SP" << endl; - output << "M=M+1" << endl; - - output << "@SP" << endl; - output << "D=M" << endl; - output << "@C" << endl; - output << "M=D" << endl; - output << "@5" << endl; - output << "D=A" << endl; - output << "@C" << endl; - output << "M=M-D" << endl; - output << "@" << args << endl; - output << "D=A" << endl; - output << "@C" << endl; - output << "M=M-D" << endl; - output << "D=M" << endl; - output << "@ARG" << endl; - output << "M=D" << endl; - - output << "@SP" << endl; - output << "D=M" << endl; - output << "@LCL" << endl; - output << "M=D" << endl; - - output << "@" << name << endl; - output << "0;JMP" << endl; - output << "(" << label << ")" << endl; - - return output.str(); -} - -string translateReturn() -{ - stringstream output; - - output << "@LCL" << endl; - output << "D=M" << endl; - output << "@EF" << endl; - output << "M=D" << endl; - - output << "@RA" << endl; - output << "M=D" << endl; - output << "@5" << endl; - output << "D=A" << endl; - output << "@RA" << endl; - output << "M=M-D" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - output << "@RA" << endl; - output << "M=D" << endl; - - output << "@ARG" << endl; - output << "D=M" << endl; - output << "@ADDR" << endl; - output << "M=D" << endl; - output << "@SP" << endl; - output << "M=M-1" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - output << "@ADDR" << endl; - output << "A=M" << endl; - output << "M=D" << endl; - - output << "@ARG" << endl; - output << "D=M+1" << endl; - output << "@SP" << endl; - output << "M=D" << endl; - - output << "@EF" << endl; - output << "D=M" << endl; - output << "@1" << endl; - output << "D=D-A" << endl; - output << "A=D" << endl; - output << "D=M" << endl; - output << "@THAT" << endl; - output << "M=D" << endl; - - output << "@EF" << endl; - output << "D=M" << endl; - output << "@2" << endl; - output << "D=D-A" << endl; - output << "A=D" << endl; - output << "D=M" << endl; - output << "@THIS" << endl; - output << "M=D" << endl; - - output << "@EF" << endl; - output << "D=M" << endl; - output << "@3" << endl; - output << "D=D-A" << endl; - output << "A=D" << endl; - output << "D=M" << endl; - output << "@ARG" << endl; - output << "M=D" << endl; - - output << "@EF" << endl; - output << "D=M" << endl; - output << "@4" << endl; - output << "D=D-A" << endl; - output << "A=D" << endl; - output << "D=M" << endl; - output << "@LCL" << endl; - output << "M=D" << endl; - - output << "@RA" << endl; - output << "D=M" << endl; - output << "D;JMP" << endl; - - return output.str(); -} \ No newline at end of file diff --git a/include/memory.h b/include/memory.h deleted file mode 100644 index 50845ca..0000000 --- a/include/memory.h +++ /dev/null @@ -1,139 +0,0 @@ -#include -#include -#include - -using namespace std; - -string translatePush(string filename, Segment segment, int index) -{ - stringstream output; - - switch (segment) - { - case LCL: - output << "@" << index << endl; - output << "D=A" << endl; - output << "@LCL" << endl; - output << "A=D+M" << endl; - output << "D=M" << endl; - break; - case ARG: - output << "@" << index << endl; - output << "D=A" << endl; - output << "@ARG" << endl; - output << "A=D+M" << endl; - output << "D=M" << endl; - break; - case THIS: - output << "@" << index << endl; - output << "D=A" << endl; - output << "@THIS" << endl; - output << "A=D+M" << endl; - output << "D=M" << endl; - break; - case THAT: - output << "@" << index << endl; - output << "D=A" << endl; - output << "@THAT" << endl; - output << "A=D+M" << endl; - output << "D=M" << endl; - break; - case POINTER: - output << (index == 0 ? "@THIS" : "@THAT") << endl; - output << "D=M" << endl; - break; - case STATIC: - output << "@" << filename << "." << index << endl; - output << "D=M" << endl; - break; - case TEMP: - output << "@" << index + 5 << endl; - output << "D=M" << endl; - break; - case CONSTANT: - default: - output << '@' << index << endl; - output << "D=A" << endl; - break; - } - - output << "@SP" << endl; - output << "A=M" << endl; - output << "M=D" << endl; - output << "@SP" << endl; - output << "M=M+1" << endl; - - return output.str(); -} - -string translatePop(string filename, Segment segment, int index) -{ - stringstream output; - - switch (segment) - { - case LCL: - output << "@" << index << endl; - output << "D=A" << endl; - output << "@LCL" << endl; - output << "D=D+M" << endl; - output << "@ADDR" << endl; - output << "M=D" << endl; - break; - case ARG: - output << "@" << index << endl; - output << "D=A" << endl; - output << "@ARG" << endl; - output << "D=D+M" << endl; - output << "@ADDR" << endl; - output << "M=D" << endl; - break; - case THIS: - output << "@" << index << endl; - output << "D=A" << endl; - output << "@THIS" << endl; - output << "D=D+M" << endl; - output << "@ADDR" << endl; - output << "M=D" << endl; - break; - case THAT: - output << "@" << index << endl; - output << "D=A" << endl; - output << "@THAT" << endl; - output << "D=D+M" << endl; - output << "@ADDR" << endl; - output << "M=D" << endl; - break; - case POINTER: - output << (index == 0 ? "@THIS" : "@THAT") << endl; - output << "D=A" << endl; - output << "@ADDR" << endl; - output << "M=D" << endl; - break; - case STATIC: - output << "@" << filename << "." << index << endl; - output << "D=A" << endl; - output << "@ADDR" << endl; - output << "M=D" << endl; - break; - case TEMP: - output << "@" << index + 5 << endl; - output << "D=A" << endl; - output << "@ADDR" << endl; - output << "M=D" << endl; - break; - - default: - break; - } - - output << "@SP" << endl; - output << "M=M-1" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - output << "@ADDR" << endl; - output << "A=M" << endl; - output << "M=D" << endl; - - return output.str(); -} diff --git a/include/operations.h b/include/operations.h deleted file mode 100644 index f8b7fda..0000000 --- a/include/operations.h +++ /dev/null @@ -1,328 +0,0 @@ -#include -#include -#include -#include - -using namespace std; - -string translateAdd() -{ - stringstream output; - - output << "@SP" << endl; - output << "M=M-1" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - - output << "@A" << endl; - output << "M=D" << endl; - - output << "@SP" << endl; - output << "M=M-1" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - - output << "@A" << endl; - output << "M=D+M" << endl; - output << "D=M" << endl; - - output << "@SP" << endl; - output << "A=M" << endl; - output << "M=D" << endl; - output << "@SP" << endl; - output << "M=M+1" << endl; - - return output.str(); -} - -string translateSub() -{ - stringstream output; - - output << "@SP" << endl; - output << "M=M-1" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - - output << "@S" << endl; - output << "M=D" << endl; - - output << "@SP" << endl; - output << "M=M-1" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - - output << "@S" << endl; - output << "M=D-M" << endl; - output << "D=M" << endl; - - output << "@SP" << endl; - output << "A=M" << endl; - output << "M=D" << endl; - output << "@SP" << endl; - output << "M=M+1" << endl; - - return output.str(); -} - -string translateNeg() -{ - stringstream output; - - output << "@SP" << endl; - output << "M=M-1" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - - output << "@N" << endl; - output << "M=D" << endl; - output << "M=M-D" << endl; - output << "M=M-D" << endl; - output << "D=M" << endl; - - output << "@SP" << endl; - output << "A=M" << endl; - output << "M=D" << endl; - output << "@SP" << endl; - output << "M=M+1" << endl; - - return output.str(); -} - -string translateEq() -{ - stringstream output; - - srand(static_cast(time(nullptr))); - - string LABEL = generateRandomLabel(8); - - output << "@SP" << endl; - output << "M=M-1" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - - output << "@E" << endl; - output << "M=D" << endl; - - output << "@SP" << endl; - output << "M=M-1" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - - output << "@E" << endl; - output << "M=D-M" << endl; - output << "D=M" << endl; - output << "@" << LABEL << "_TRUE" << endl; - output << "D;JEQ" << endl; - output << "@" << LABEL << "_FALSE" << endl; - output << "D;JNE" << endl; - - output << "(" << LABEL << "_TRUE)" << endl; - output << "@SP" << endl; - output << "A=M" << endl; - output << "M=-1" << endl; - output << "@SP" << endl; - output << "M=M+1" << endl; - output << "@" << LABEL << endl; - output << "0;JMP" << endl; - - output << "(" << LABEL << "_FALSE)" << endl; - output << "@SP" << endl; - output << "A=M" << endl; - output << "M=0" << endl; - output << "@SP" << endl; - output << "M=M+1" << endl; - output << "@" << LABEL << endl; - output << "0;JMP" << endl; - - output << "(" << LABEL << ")" << endl; - - return output.str(); -} - -string translateGt() -{ - stringstream output; - - srand(static_cast(time(nullptr))); - - string LABEL = generateRandomLabel(8); - - output << "@SP" << endl; - output << "M=M-1" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - - output << "@GT" << endl; - output << "M=D" << endl; - - output << "@SP" << endl; - output << "M=M-1" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - - output << "@GT" << endl; - output << "M=D-M" << endl; - output << "D=M" << endl; - output << "@" << LABEL << "_TRUE" << endl; - output << "D;JGT" << endl; - output << "@" << LABEL << "_FALSE" << endl; - output << "D;JLT" << endl; - output << "@" << LABEL << "_FALSE" << endl; - output << "D;JEQ" << endl; - - output << "(" << LABEL << "_TRUE)" << endl; - output << "@SP" << endl; - output << "A=M" << endl; - output << "M=-1" << endl; - output << "@SP" << endl; - output << "M=M+1" << endl; - output << "@" << LABEL << endl; - output << "0;JMP" << endl; - - output << "(" << LABEL << "_FALSE)" << endl; - output << "@SP" << endl; - output << "A=M" << endl; - output << "M=0" << endl; - output << "@SP" << endl; - output << "M=M+1" << endl; - output << "@" << LABEL << endl; - output << "0;JMP" << endl; - - output << "(" << LABEL << ")" << endl; - - return output.str(); -} - -string translateLt() -{ - stringstream output; - - srand(static_cast(time(nullptr))); - - string LABEL = generateRandomLabel(8); - - output << "@SP" << endl; - output << "M=M-1" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - - output << "@LT" << endl; - output << "M=D" << endl; - - output << "@SP" << endl; - output << "M=M-1" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - - output << "@LT" << endl; - output << "M=D-M" << endl; - output << "D=M" << endl; - output << "@" << LABEL << "_TRUE" << endl; - output << "D;JLT" << endl; - output << "@" << LABEL << "_FALSE" << endl; - output << "D;JGT" << endl; - output << "@" << LABEL << "_FALSE" << endl; - output << "D;JEQ" << endl; - - output << "(" << LABEL << "_TRUE)" << endl; - output << "@SP" << endl; - output << "A=M" << endl; - output << "M=-1" << endl; - output << "@SP" << endl; - output << "M=M+1" << endl; - output << "@" << LABEL << endl; - output << "0;JMP" << endl; - - output << "(" << LABEL << "_FALSE)" << endl; - output << "@SP" << endl; - output << "A=M" << endl; - output << "M=0" << endl; - output << "@SP" << endl; - output << "M=M+1" << endl; - output << "@" << LABEL << endl; - output << "0;JMP" << endl; - - output << "(" << LABEL << ")" << endl; - - return output.str(); -} - -string translateAnd() -{ - stringstream output; - - output << "@SP" << endl; - output << "M=M-1" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - - output << "@AND" << endl; - output << "M=D" << endl; - - output << "@SP" << endl; - output << "M=M-1" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - - output << "@AND" << endl; - output << "M=D&M" << endl; - output << "D=M" << endl; - - output << "@SP" << endl; - output << "A=M" << endl; - output << "M=D" << endl; - output << "@SP" << endl; - output << "M=M+1" << endl; - - return output.str(); -} - -string translateOr() -{ - stringstream output; - - output << "@SP" << endl; - output << "M=M-1" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - - output << "@OR" << endl; - output << "M=D" << endl; - - output << "@SP" << endl; - output << "M=M-1" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - - output << "@OR" << endl; - output << "M=D|M" << endl; - output << "D=M" << endl; - - output << "@SP" << endl; - output << "A=M" << endl; - output << "M=D" << endl; - output << "@SP" << endl; - output << "M=M+1" << endl; - - return output.str(); -} - -string translateNot() -{ - stringstream output; - - output << "@SP" << endl; - output << "M=M-1" << endl; - output << "A=M" << endl; - output << "D=M" << endl; - - output << "@SP" << endl; - output << "A=M" << endl; - output << "M=!D" << endl; - output << "@SP" << endl; - output << "M=M+1" << endl; - - return output.str(); -} diff --git a/include/parser.h b/include/parser.h deleted file mode 100644 index 4073a5f..0000000 --- a/include/parser.h +++ /dev/null @@ -1,99 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "types.h" -#include "utils.h" - -using namespace std; - -class Parser -{ -private: - ifstream file; - string vmCode; - - bool isEmptyLine(string text) - { - for (char c : text) - { - if (!isspace(c)) - return false; - } - - return true; - } - - void removeCommentsAndWhitespace() - { - string text; - smatch matched; - - while (getline(file, text)) - { - if (regex_search(text, matched, regex("^(.*)?(\\/\\/.*)")) || isEmptyLine(text)) - { - if (!isEmptyLine(matched[1])) - { - string command = matched[1]; - - trim(command); - vmCode.append(command + '\n'); - } - continue; - } - else - { - trim(text); - vmCode.append(text + '\n'); - } - } - } - - void closeFile() - { - file.close(); - } - -public: - Parser(string path) - { - file = ifstream(path); - - removeCommentsAndWhitespace(); - } - - vector> getCommands() - { - stringstream vmCodeStream(vmCode); - string text; - smatch matched; - vector> commands; - - while (getline(vmCodeStream, text, '\n')) - { - vector matchedVector; - - if (regex_search(text, matched, regex("^(.*) (.*) (.*)"))) - { - matchedVector.push_back(matched[1]); - matchedVector.push_back(matched[2]); - matchedVector.push_back(matched[3]); - } - else if (regex_search(text, matched, regex("^(.*) (.*)"))) - { - matchedVector.push_back(matched[1]); - matchedVector.push_back(matched[2]); - } - else if (regex_search(text, matched, regex("^(.*)"))) - { - matchedVector.push_back(matched[1]); - } - commands.push_back(matchedVector); - } - - return commands; - } -}; diff --git a/include/types.h b/include/types.h deleted file mode 100644 index 7c613f8..0000000 --- a/include/types.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -enum Segment -{ - LCL, - ARG, - THIS, - THAT, - STATIC, - CONSTANT, - TEMP, - POINTER, -}; - -enum NoArgumentCommand { - ADD, - SUB, - NEG, - EQ, - GT, - LT, - AND, - OR, - NOT, - RETURN, -}; - -enum OneArgumentCommand { - LABEL, - GOTO, - IFGOTO, -}; - -enum TwoArgumentCommand { - PUSH, - POP, - FUNCTION, - CALL -}; diff --git a/include/utils.h b/include/utils.h deleted file mode 100644 index 8c9cacc..0000000 --- a/include/utils.h +++ /dev/null @@ -1,112 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -using namespace std; - -inline void ltrim(string &str) { - str.erase(str.begin(), find_if(str.begin(), str.end(), [](unsigned char ch) { - return !isspace(ch); - })); -} - -inline void rtrim(string &str) { - str.erase(find_if(str.rbegin(), str.rend(), [](unsigned char ch) { - return !isspace(ch); - }).base(), str.end()); -} - -inline void trim(string &str) { - rtrim(str); - ltrim(str); -} - -Segment determineSegment(string segment) -{ - if (segment == "local") - return Segment::LCL; - if (segment == "argument") - return Segment::ARG; - if (segment == "this") - return Segment::THIS; - if (segment == "that") - return Segment::THAT; - if (segment == "pointer") - return Segment::POINTER; - if (segment == "static") - return Segment::STATIC; - if (segment == "temp") - return Segment::TEMP; - if (segment == "constant") - return Segment::CONSTANT; - - return Segment::CONSTANT; -} - -string generateRandomLabel(int length) { - random_device rd; - mt19937 gen(rd()); - uniform_int_distribution<> dis('A', 'Z'); - string label; - - for (int i = 0; i < length; ++i) { - label += static_cast(dis(gen)); - } - - return label; -} - -NoArgumentCommand determineNoArgumentCommand(string command) -{ - if (command == "add") - return NoArgumentCommand::ADD; - if (command == "sub") - return NoArgumentCommand::SUB; - if (command == "neg") - return NoArgumentCommand::NEG; - if (command == "eq") - return NoArgumentCommand::EQ; - if (command == "gt") - return NoArgumentCommand::GT; - if (command == "lt") - return NoArgumentCommand::LT; - if (command == "and") - return NoArgumentCommand::AND; - if (command == "or") - return NoArgumentCommand::OR; - if (command == "not") - return NoArgumentCommand::NOT; - if (command == "return") - return NoArgumentCommand::RETURN; - - return NoArgumentCommand::NEG; -} - -OneArgumentCommand determineOneArgumentCommand(string command) -{ - if (command == "label") - return OneArgumentCommand::LABEL; - if (command == "goto") - return OneArgumentCommand::GOTO; - if (command == "if-goto") - return OneArgumentCommand::IFGOTO; - - return OneArgumentCommand::IFGOTO; -} - -TwoArgumentCommand determineTwoArgumentCommand(string command) -{ - if (command == "push") - return TwoArgumentCommand::PUSH; - if (command == "pop") - return TwoArgumentCommand::POP; - if (command == "function") - return TwoArgumentCommand::FUNCTION; - if (command == "call") - return TwoArgumentCommand::CALL; - - return TwoArgumentCommand::POP; -} diff --git a/main.cpp b/main.cpp deleted file mode 100644 index ec9c8b5..0000000 --- a/main.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include -#include -#include -#include -#include "include/parser.h" -#include "include/code.h" - -using namespace std; -namespace fs = std::filesystem; - -string constructTranslatedFilePath(string path) -{ - size_t position = path.rfind(".vm"); - - return path.replace(position, 3, ".asm"); -} - -string constructTranslatedDirectoryPath(string path) -{ - string newPath = path; - - size_t position = path.rfind("/"); - - if (position != string::npos && position == path.size() - 1) - { - newPath = path.replace(position, 1, ""); - } - - size_t slashIndex = path.find_last_of('/'); - - newPath += "/" + path.substr(slashIndex + 1); - - return newPath + ".asm"; -} - -void processFile(string sourcePath) -{ - Parser parser(sourcePath); - - vector> commands = parser.getCommands(); - - string translatedPath = constructTranslatedFilePath(sourcePath); - - Code code(translatedPath, commands, false); - - code.translate(); -} - -void processDirectory(string sourcePath, vector files) -{ - string translatedPath = constructTranslatedDirectoryPath(sourcePath); - bool isNew = true; - - for (const auto &file : files) - { - Parser parser(file); - - vector> commands = parser.getCommands(); - - Code code(translatedPath, commands, isNew); - - code.translate(); - - isNew = false; - } -} - -int main(int argc, char *argv[]) -{ - if (!argv[1]) - { - cout << "You must specify a vm file path!" << endl; - return 1; - } - - string sourcePath = argv[1]; - - if (!regex_match(sourcePath, regex("^.+\\.vm"))) - { - if (!fs::is_directory(sourcePath)) - { - cerr << "Error: " << sourcePath << " is not a directory.\n"; - return 1; - } - - vector vmFiles; - - for (const auto &entry : fs::directory_iterator(sourcePath)) - { - if (entry.is_regular_file() && entry.path().extension() == ".vm") - { - vmFiles.push_back(entry.path().string()); - } - } - - if (vmFiles.empty()) - { - cout << "Directory does not contain vm files!" << sourcePath << "\n"; - } - else - { - processDirectory(sourcePath, vmFiles); - } - } - else - { - processFile(sourcePath); - } - - return 0; -} \ No newline at end of file diff --git a/src/linked-list.cpp b/src/linked-list.cpp new file mode 100644 index 0000000..d15a6ae --- /dev/null +++ b/src/linked-list.cpp @@ -0,0 +1,16 @@ +#include "linked-list.hpp" + +template int insertNode(LinkedList *list, T data) { + LinkedList *node = new LinkedList; + + node->data = data; + + if (list == nullptr) { + node->next = list; + list = node; + } else { + list->next = node; + } + + return 0; +} diff --git a/src/linked-list.hpp b/src/linked-list.hpp new file mode 100644 index 0000000..4586745 --- /dev/null +++ b/src/linked-list.hpp @@ -0,0 +1,8 @@ +template +struct LinkedList { + T data; + struct LinkedList* next; +}; + +template +int insertNode(LinkedList *, T); diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..ced9133 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include +#include + +#include "parser.hpp" +#include "utils.hpp" + +int process(std::string source) { + std::ifstream ifs(source); + std::string line; + + LinkedList *commands = nullptr; + + while (getline(ifs, line)) { + if (isComment(line) || isEmptyLine(line)) + continue; + + int parseResult; + + if ((parseResult = parseCommand(commands, line)) != 0) + return parseResult; + } + + return 0; +} + +int main(int argc, char **argv) { + if (argc != 2) { + std::cerr << "You must specify an argument for the vm file to translate!" + << std::endl; + exit(1); + } + + std::string source = argv[1]; + + if (!regex_match(source, std::regex("^.+\\.vm"))) { + std::cerr << "Source file is not a vm file!" << std::endl; + exit(1); + } + + return process(source); +} diff --git a/src/parser.cpp b/src/parser.cpp new file mode 100644 index 0000000..eb713ff --- /dev/null +++ b/src/parser.cpp @@ -0,0 +1,39 @@ +#include +#include +#include + +#include "parser.hpp" + +int parseCommand(LinkedList *commands, std::string line) { + std::smatch matched; + + if (regex_search(line, matched, std::regex("^(.*) (.*) (.*)"))) { + Command cmd; + int err; + + cmd.line = line; + cmd.type = commandTypes.at(matched[0]); + cmd.segment = segmentTypes.at(matched[1]); + cmd.index = std::stoi(matched[2]); + + if ((err = insertNode(commands, cmd)) != 0) { + std::cerr << "Unexpected error parsing vm command:" << line << std::endl; + return 1; + } + } + + if (regex_search(line, matched, std::regex("^(.*)"))) { + Command cmd; + int err; + + cmd.line = line; + cmd.type = matched[0]; + + if ((err = insertNode(commands, cmd)) != 0) { + std::cerr << "Unexpected error parsing vm command:" << line << std::endl; + return 1; + } + } + + return 0; +} diff --git a/src/parser.hpp b/src/parser.hpp new file mode 100644 index 0000000..ab00850 --- /dev/null +++ b/src/parser.hpp @@ -0,0 +1,6 @@ +#include + +#include "linked-list.hpp" +#include "types.hpp" + +int parseCommand(LinkedList *, std::string); diff --git a/src/types.hpp b/src/types.hpp new file mode 100644 index 0000000..0cba471 --- /dev/null +++ b/src/types.hpp @@ -0,0 +1,53 @@ +#include +#include + +enum CommandType { + ADD, + SUB, + NEG, + + EQ, + GT, + LT, + + AND, + OR, + NOT, + + PUSH, + POP, +}; + +enum SegmentType { + LCL, + ARG, + THIS, + THAT, + STATIC, + CONSTANT, + TEMP, + POINTER, +}; + +std::unordered_map const commandTypes = { + {"add", CommandType::ADD}, {"sub", CommandType::SUB}, + {"neg", CommandType::NEG}, {"eq", CommandType::EQ}, + {"gt", CommandType::GT}, {"lt", CommandType::LT}, + {"and", CommandType::AND}, {"or", CommandType::OR}, + {"not", CommandType::NOT}, {"push", CommandType::PUSH}, + {"pop", CommandType::POP}, +}; + +std::unordered_map const segmentTypes = { + {"local", SegmentType::LCL}, {"argument", SegmentType::ARG}, + {"this", SegmentType::THIS}, {"that", SegmentType::THAT}, + {"static", SegmentType::STATIC}, {"constant", SegmentType::CONSTANT}, + {"temp", SegmentType::TEMP}, {"pointer", SegmentType::POINTER}, +}; + +typedef struct { + std::string line; + std::string type; + std::string segment; + int index; +} Command; diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..4953966 --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,13 @@ +#include +#include + +bool isEmptyLine(std::string line) { + for (char c : line) { + if (!isspace(c)) + return false; + } + + return true; +} + +bool isComment(std::string line) { return line[0] == '/' && line[1] == '/'; } diff --git a/src/utils.hpp b/src/utils.hpp new file mode 100644 index 0000000..53cc254 --- /dev/null +++ b/src/utils.hpp @@ -0,0 +1,4 @@ +#include + +bool isEmptyLine(std::string); +bool isComment(std::string);