diff --git a/.gitignore b/.gitignore
index 3943bed..be655f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@
/build/*
parser
/.vscode
+/*.html
diff --git a/Makefile b/Makefile
index 9f77ed3..4380f7b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
# Define the C++ compiler and flags
CXX = g++
-CXXFLAGS = -Wall -g
+CXXFLAGS = -Wall -g -fPIE
# Directories
BUILD_DIR = build
@@ -29,15 +29,15 @@ $(BUILD_DIR):
mkdir -p $(BUILD_DIR)
$(TARGET): $(OBJECTS)
- $(CXX) $(CXXFLAGS) $(INCLUDES) $^ -o $@
+ $(CXX) $(CXXFLAGS) $(INCLUDES) $^ -o $@ -pie
# Generic rule for all .cpp files in the src/ directory
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
- $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
+ $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ -pie
# Generic rule for all .cpp files in the lib/ directory
$(BUILD_DIR)/%.o: $(LIB_DIR)/%.cpp
- $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
+ $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ -pie
test: all
./$(TARGET)
diff --git a/input.md b/input.md
index 77ed442..c031c06 100644
--- a/input.md
+++ b/input.md
@@ -4,7 +4,7 @@
The goal is to create a program that reads a file containing text formatted in a simple version of
Markdown and converts it into a valid HTML file. The program will need to identify and translate
-specific syntax (e.g., `# Heading` to `
Heading
`, `*text*` to `text`).
+specific syntax (e.g., ` Heading` to `\Heading\
`, `*text*` to `\text\`).
### Implementation Requirements (Generated by Gemini)
@@ -42,8 +42,8 @@ that all tags are correctly opened and closed. Your presentation can visually de
with a stack diagram.
Hash Map or Map: A hash map (std::unordered_map) or a map (std::map) can be used to efficiently store
-and retrieve the HTML equivalent for each Markdown tag. For example, you could map `#` to ``or `*`
-to ``. This provides O(1) average-case lookup time.
+and retrieve the HTML equivalent for each Markdown tag. For example, you could map `#` to `\`or `*`
+to `\`. This provides O(1) average-case lookup time.
### Contribution Policy
diff --git a/lib/fileSystem.cpp b/lib/fileSystem.cpp
new file mode 100644
index 0000000..3742b7a
--- /dev/null
+++ b/lib/fileSystem.cpp
@@ -0,0 +1,62 @@
+#include "fileSystem.h"
+#include "util.h"
+
+#include
+#include
+#include
+#include
+
+FileSystem::FileSystem(string input_file_path, string output_file_path) {
+ removeWhitespace(input_file_path);
+ removeWhitespace(output_file_path);
+
+ if (input_file_path.empty())
+ throw std::runtime_error("input_file_path cannot be empty");
+
+ this->input_file_path = input_file_path;
+
+ this->output_file_path = output_file_path;
+ if (this->output_file_path.empty())
+ GenerateOutputFilePath();
+};
+
+void FileSystem::GenerateOutputFilePath() {
+ if (this->input_file_path.empty())
+ throw std::runtime_error("Cannot generate output path from empty input.");
+
+ int ext_idx = this->input_file_path.find_last_of('.');
+ string output_cleaned = this->input_file_path.substr(0, ext_idx) + ".html";
+ this->output_file_path = output_cleaned;
+}
+
+std::string FileSystem::ReadInputFile() {
+ // Cannot read file if the path does not exist
+ if (this->input_file_path.empty())
+ throw std::runtime_error("Cannot open file: path was not provided.");
+
+ std::ifstream input_file(this->input_file_path);
+
+ if (!input_file.is_open())
+ throw std::runtime_error("Failed to open input file.");
+
+ // Read the file into a single string using a string stream
+ std::stringstream buffer;
+ buffer << input_file.rdbuf();
+ input_file.close();
+
+ return buffer.str();
+}
+
+void FileSystem::WriteOutputFile(std::string content) {
+ // Cannot write to file if the path does not exist
+ if (this->output_file_path.empty())
+ throw std::runtime_error("Cannot open file: path was not provided.");
+
+ std::ofstream output_file(this->output_file_path);
+
+ if (!output_file.is_open())
+ throw std::runtime_error("Failed to open output file.");
+
+ output_file << content;
+ output_file.close();
+}
diff --git a/lib/fileSystem.h b/lib/fileSystem.h
new file mode 100644
index 0000000..b1e5ac2
--- /dev/null
+++ b/lib/fileSystem.h
@@ -0,0 +1,71 @@
+#ifndef FILESYSTEM_H
+#define FILESYSTEM_H
+
+#include
+
+using std::string;
+
+class FileSystem {
+public:
+ FileSystem(string input_file_path, string output_file_path = "");
+
+ /**
+ * @brief Read the input file and return its content.
+ *
+ * This method will read the file at the input_file_path and create a single
+ * output string. Each line will be delimited by either `\n` (Unix) or `\r\n`
+ * (Windows). If the file path does not exist OR the file fails to open, this
+ * method will throw a run time error.
+ *
+ * @return File contents as a single string.
+ *
+ * @author Hayden Hargreaves (hhargreaves2006@gmail.com)
+ */
+ string ReadInputFile();
+
+ /**
+ * @brief Write the provided string to the output file.
+ *
+ * This method will attempt to open the output file and write the content
+ * provided to the method in the file. If the file does not exist, it will be
+ * created. If the file path does not exist OR the file fails to open, this
+ * method will throw a run time error.
+ *
+ * @author Hayden Hargreaves (hhargreaves2006@gmail.com)
+ */
+ void WriteOutputFile(string content);
+
+protected:
+ /**
+ * @brief Input file path.
+ *
+ * Must be provided by the user.
+ *
+ * @author Hayden Hargreaves (hhargreaves2006@gmail.com)
+ */
+ string input_file_path;
+
+ /**
+ * @brief Output file path.
+ *
+ * If not provided, will be generated using the `input_file_path` by removing
+ * the extension and appending `.html`.
+ *
+ * @author Hayden Hargreaves (hhargreaves2006@gmail.com)
+ */
+ string output_file_path;
+
+private:
+ /**
+ * @brief Generate an output file path.
+ *
+ * If the user does not provide an output file path, this method can be
+ * used to generate the path. This is done by simply swapping the `.md`
+ * with `.html`.
+ *
+ * @author Hayden Hargreaves (hhargreaves2006@gmail.com)
+ */
+ void GenerateOutputFilePath();
+};
+
+#endif
diff --git a/lib/parser.cpp b/lib/parser.cpp
index 51bde62..86ab32d 100644
--- a/lib/parser.cpp
+++ b/lib/parser.cpp
@@ -1,49 +1,19 @@
#include "parser.h"
+#include "fileSystem.h"
#include "inlineNode.h"
#include "structureNode.h"
-#include "util.h"
#include
#include
-#include
#include
-#include
-#include
#include
using std::string;
using std::vector;
-Parser::Parser(string input_file_path, string output_file_path) {
- // NOTE: Remove any white space AROUND the inputs
- removeWhitespace(input_file_path);
- removeWhitespace(output_file_path);
-
- if (input_file_path == "") {
- throw std::runtime_error("input_file_path cannot be empty");
- }
-
- this->input_file_path = input_file_path;
-
- // NOTE: If the user does not provide an output file, then we should construct
- // one using the input file with .md swapped with the extension.
- if (output_file_path == "") {
- int ext_idx = input_file_path.find_last_of('.');
- string output_cleaned = input_file_path.substr(0, ext_idx) + ".html";
- this->output_file_path = output_cleaned;
- return;
- }
-
- this->output_file_path = output_file_path;
-}
-
void Parser::Inspect() {
- std::cout << "std::string input_file_path: " << this->input_file_path
- << std::endl;
- std::cout << "std::string output_file_path: " << this->output_file_path
- << std::endl;
+ std::cerr << "Parser::Inspect() is not yet implemented." << std::endl;
}
-// replace '\r\n' with '\n'
void Parser::NormalizeInputStream() {
if (this->content.empty())
return;
@@ -60,22 +30,23 @@ void Parser::NormalizeInputStream() {
this->content.end());
}
-void Parser::ParseDocument() {
- // Open the input file
- std::ifstream input_file(this->input_file_path);
+void Parser::WriteOutput() {
+ if (this->DOM == nullptr)
+ throw std::runtime_error(
+ "Cannot write output, output DOM tree does not exist. Please run the "
+ "Parser::ParserDocument method first.");
- if (!input_file.is_open()) {
- throw std::runtime_error("Failed to open input file.");
+ this->filesystem.WriteOutputFile(this->DOM->ToHtml());
+}
+
+void Parser::ParseDocument() {
+ try {
+ this->content = this->filesystem.ReadInputFile();
+ } catch (const std::runtime_error &e) {
+ std::cerr << "Caught an error: " << e.what() << std::endl;
return;
}
- // Read the file into a single string
- std::stringstream buffer;
- buffer << input_file.rdbuf();
- this->content = buffer.str();
-
- input_file.close();
-
// Remove the windows BS
NormalizeInputStream();
@@ -88,8 +59,6 @@ void Parser::ParseDocument() {
if (block != nullptr)
this->DOM->AddChild(std::move(block));
}
-
- std::cout << this->DOM->ToHtml();
}
// All this does is pick which subparser to call
diff --git a/lib/parser.h b/lib/parser.h
index ce7590d..cd24c74 100644
--- a/lib/parser.h
+++ b/lib/parser.h
@@ -1,6 +1,7 @@
#ifndef PARSER_H
#define PARSER_H
+#include "fileSystem.h"
#include "node.h"
#include
#include
@@ -25,7 +26,8 @@ using std::vector;
*/
class Parser {
public:
- Parser(string input_file_path, string output_file_path = "");
+ Parser(string input_file_path, string output_file_path = "")
+ : filesystem(input_file_path, output_file_path) {};
/**
* @brief Inspect (view) contents of the class.
@@ -37,7 +39,6 @@ public:
void Inspect();
/**
- *
* @brief Parse an entire document.
*
* This function will be called to yield the result. This is the entry point
@@ -53,25 +54,27 @@ public:
*/
void ParseDocument();
+ /**
+ * @brief Write the output to the file.
+ *
+ * Once the tree is generated, this method should be called to actually
+ * write the output. Having this functionality separate allows for more
+ * portability.
+ *
+ * @author Hayden Hargreaves (hhargreaves2006@gmail.com)
+ */
+ void WriteOutput();
+
protected:
/**
- * @brief Input file path.
+ * @brief File system module to handle file I/O.
+ *
+ * Anything requiring file I/O operations will be handled by this module.
*
- * Must be provided by the user.
*
* @author Hayden Hargreaves (hhargreaves2006@gmail.com)
*/
- string input_file_path;
-
- /**
- * @brief Output file path.
- *
- * If not provided, will be generated using the `input_file_path` by removing
- * the extension and appending `.html`.
- *
- * @author Hayden Hargreaves (hhargreaves2006@gmail.com)
- */
- string output_file_path;
+ FileSystem filesystem;
/**
* @brief Parser generated tree.
@@ -84,11 +87,16 @@ protected:
*/
std::unique_ptr DOM;
- // NOTE: We need a stack, just not sure what goes in it yet
- // std::stack stack;
-
private:
- // windows... >:(
+ /**
+ * @brief Normalize the input stream.
+ *
+ * Replaces all `\r\n` with just `\n` since that is what the parser expects.
+ * Then removes any left over `\r` elements in the stream. If the stream is
+ * empty this method does nothing.
+ *
+ * @author Hayden Hargreaves (hhargreaves2006@gmail.com)
+ */
void NormalizeInputStream();
/**
@@ -97,7 +105,7 @@ private:
* How does this function work...
* This is where the magic happens.
*
- * @return DOMNode, once exists
+ * @return Node, to be appended to the callers children.
*
* @author Hayden Hargreaves (hhargreaves2006@gmail.com)
*/
diff --git a/src/main.cpp b/src/main.cpp
index 5fc78ed..cf4be4c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -79,9 +79,10 @@ void test_input(int argc, char **argv) {
}
int main(int argc, char **argv) {
- Parser p("input.md");
+ Parser p("syntax.md");
p.ParseDocument();
+ p.WriteOutput();
- Parser p2("README.md");
- p2.ParseDocument();
+ // Parser p2("README.md");
+ // p2.ParseDocument();
}