Skip to content

Commit

Permalink
vmtranslator: Extract class AssemblyFile to output module
Browse files Browse the repository at this point in the history
- Better logging and code comments
  • Loading branch information
lucas89901 committed Jan 19, 2024
1 parent 5776a20 commit 0a033d8
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 48 deletions.
12 changes: 12 additions & 0 deletions projects/08/vmtranslator/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ target_link_libraries(
commands
)

add_library(
output
output.cpp
)
target_link_libraries(
output
absl::check
absl::log
commands
)

add_executable(
vmtranslator
main.cpp
Expand All @@ -48,6 +59,7 @@ target_link_libraries(
absl::strings
absl::str_format
commands
output
parser
)

Expand Down
53 changes: 6 additions & 47 deletions projects/08/vmtranslator/main.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#include <filesystem>
#include <iostream>
#include <vector>

#include "absl/flags/flag.h"
Expand All @@ -9,60 +8,16 @@
#include "absl/log/log.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/strip.h"

#include "commands.h"
#include "output.h"
#include "parser.h"

ABSL_FLAG(bool, v, false, "verbose output, print assembly output to console");
ABSL_FLAG(bool, d, false,
"debug mode, write VM source lines as comments in assembly output");

class AssemblyFile {
public:
AssemblyFile(std::string_view path, bool source_is_multi_file)
: file_(path.data()), source_is_multi_file_(source_is_multi_file) {
QCHECK(file_.is_open()) << "Could not open output file: " << path;
LOG(INFO) << "Output: " << path;
LOG(INFO) << "Source is multi-file: " << source_is_multi_file;

// Bootstrap code.
file_ << "@256\n"
<< "D=A\n"
<< "@SP\n"
<< "M=D\n";

if (source_is_multi_file_) {
file_ << CallCommand("Sys.init", 0, "END").ToAssembly();
// Although `Sys.init` is expected to enter an infinite loop, we still add
// an infinite loop in case `Sys.init` returns.
file_ << "@END\n"
<< "(END)\n"
<< "0;JMP\n";
}
}

~AssemblyFile() {
if (!source_is_multi_file_) {
file_ << "@END\n"
<< "(END)\n"
<< "0;JMP\n";
}
file_.close();
}

template <class T>
AssemblyFile &operator<<(const T &value) {
file_ << value;
return *this;
}

private:
std::ofstream file_;
bool source_is_multi_file_ = false;
};

void Translate(VmFile &vm_file, AssemblyFile &asm_file) {
LOG(INFO) << "Processing VM file: " << vm_file.path();
while (vm_file.command()) {
if (absl::GetFlag(FLAGS_v)) {
LOG(INFO) << vm_file.line() << " ->\n" << vm_file.command()->ToAssembly();
Expand All @@ -87,6 +42,7 @@ int main(int argc, char *argv[]) {
<< positional_args[1];
LOG(INFO) << "Source: " << source.path().string();

// Determine program name and output path.
std::string program_name;
std::filesystem::path asm_path;
if (source.is_directory()) {
Expand All @@ -103,8 +59,10 @@ int main(int argc, char *argv[]) {
CHECK(!program_name.empty());
LOG(INFO) << "Program: " << program_name;

// Translate.
AssemblyFile asm_file(asm_path.string(), source.is_directory());
if (source.is_directory()) {
LOG(INFO) << "Multi-file source mode";
for (const std::filesystem::directory_entry &entry :
std::filesystem::directory_iterator(source)) {
if (entry.path().extension() == ".vm") {
Expand All @@ -113,6 +71,7 @@ int main(int argc, char *argv[]) {
}
}
} else {
LOG(INFO) << "Single-file source mode";
VmFile vm_file(positional_args[1]);
Translate(vm_file, asm_file);
}
Expand Down
38 changes: 38 additions & 0 deletions projects/08/vmtranslator/output.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include "output.h"

#include <string_view>

#include "absl/log/check.h"
#include "absl/log/log.h"

#include "commands.h"

AssemblyFile::AssemblyFile(std::string_view path, bool source_is_multi_file)
: file_(path.data()), source_is_multi_file_(source_is_multi_file) {
QCHECK(file_.is_open()) << "Could not open output file: " << path;
LOG(INFO) << "Output: " << path;

// Bootstrap code.
file_ << "@256\n"
<< "D=A\n"
<< "@SP\n"
<< "M=D\n";

if (source_is_multi_file_) {
file_ << CallCommand("Sys.init", 0, "END").ToAssembly();
// Although `Sys.init` is expected to enter an infinite loop, we still add
// an infinite loop in case `Sys.init` returns.
file_ << "@END\n"
<< "(END)\n"
<< "0;JMP\n";
}
}

AssemblyFile::~AssemblyFile() {
if (!source_is_multi_file_) {
file_ << "@END\n"
<< "(END)\n"
<< "0;JMP\n";
}
file_.close();
}
23 changes: 23 additions & 0 deletions projects/08/vmtranslator/output.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifndef NAND2TETRIS_VMTRANSLATOR_OUTPUT_H_
#define NAND2TETRIS_VMTRANSLATOR_OUTPUT_H_

#include <fstream>
#include <string_view>

class AssemblyFile {
public:
AssemblyFile(std::string_view path, bool source_is_multi_file);
~AssemblyFile();

template <class T>
AssemblyFile &operator<<(const T &value) {
file_ << value;
return *this;
}

private:
std::ofstream file_;
bool source_is_multi_file_ = false;
};

#endif // NAND2TETRIS_VMTRANSLATOR_OUTPUT_H_
1 change: 0 additions & 1 deletion projects/08/vmtranslator/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ VmFile::VmFile(std::string_view path)
filename_(std::filesystem::path(path_).stem().string()),
function_(absl::StrCat(filename_, ".GLOBAL")) {
QCHECK(file_.is_open()) << "Could not open file: " << path;
LOG(INFO) << "Processing VM file: " << path;
Advance();
}

Expand Down

0 comments on commit 0a033d8

Please sign in to comment.