(INIT): Project init
On-boarding will begin now...
This commit is contained in:
parent
0c6f32df33
commit
7f6a3c6312
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*.o
|
||||||
|
/build
|
||||||
|
/build/*
|
||||||
|
parser
|
||||||
46
Makefile
Normal file
46
Makefile
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Define the C++ compiler and flags
|
||||||
|
CXX = g++
|
||||||
|
CXXFLAGS = -Wall -g
|
||||||
|
|
||||||
|
# Directories
|
||||||
|
BUILD_DIR = build
|
||||||
|
SRC_DIR = src
|
||||||
|
LIB_DIR = lib
|
||||||
|
|
||||||
|
# Executable name
|
||||||
|
TARGET = parser
|
||||||
|
|
||||||
|
SRC_FILES := $(wildcard $(SRC_DIR)/*.cpp)
|
||||||
|
LIB_FILES := $(wildcard $(LIB_DIR)/*.cpp)
|
||||||
|
ALL_SOURCES = $(SRC_FILES) $(LIB_FILES)
|
||||||
|
|
||||||
|
# Generate object file paths in the build directory
|
||||||
|
OBJECTS := $(patsubst %.cpp, $(BUILD_DIR)/%.o, $(notdir $(ALL_SOURCES)))
|
||||||
|
|
||||||
|
# Include directories
|
||||||
|
INCLUDES = -I$(LIB_DIR) -I$(SRC_DIR)
|
||||||
|
|
||||||
|
.PHONY: all clean test
|
||||||
|
|
||||||
|
all: $(BUILD_DIR) $(TARGET)
|
||||||
|
|
||||||
|
$(BUILD_DIR):
|
||||||
|
mkdir -p $(BUILD_DIR)
|
||||||
|
|
||||||
|
$(TARGET): $(OBJECTS)
|
||||||
|
$(CXX) $(CXXFLAGS) $(INCLUDES) $^ -o $@
|
||||||
|
|
||||||
|
$(BUILD_DIR)/main.o: $(SRC_DIR)/main.cpp $(LIB_DIR)/parser.h
|
||||||
|
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
|
||||||
|
|
||||||
|
$(BUILD_DIR)/parser.o: $(LIB_DIR)/parser.cpp $(LIB_DIR)/parser.h
|
||||||
|
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
|
||||||
|
|
||||||
|
test: all
|
||||||
|
./$(TARGET)
|
||||||
|
./$(TARGET) ' '
|
||||||
|
./$(TARGET) ./test/input.md
|
||||||
|
./$(TARGET) ./test/input.md ./test/output.html
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(BUILD_DIR) $(TARGET)
|
||||||
30
README.md
30
README.md
@ -1,3 +1,31 @@
|
|||||||
# MarkdownToHtmlCompiler
|
# MarkdownToHtmlCompiler
|
||||||
|
|
||||||
This compiler will convert a Markdown file into an HTML output.
|
This compiler will convert a Markdown file into an HTML output.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
Recursive Descent Parser: This is the primary algorithm you'll use. It's a top-down parsing technique
|
||||||
|
where a set of recursive functions "descend" through the grammar of your simple Markdown language.
|
||||||
|
For example, a parse_document() function would call parse_line(), which in turn might call parse_bold_text()
|
||||||
|
or parse_italic_text(). This method is intuitive and easy to implement for a simple grammar.
|
||||||
|
|
||||||
|
Stack: A stack is essential for handling nested elements. For instance, if you allow bold text inside
|
||||||
|
italic text (_This is *bold and italic* text_), you can push the _ token onto the stack and then push
|
||||||
|
the * token. When you encounter the closing *, you check if the top of the stack matches. This ensures
|
||||||
|
that all tags are correctly opened and closed. Your presentation can visually demonstrate this process
|
||||||
|
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 `<h1>` or "_"
|
||||||
|
to `<em>`. This provides O(1) average-case lookup time.
|
||||||
|
|
||||||
|
### Targets
|
||||||
|
|
||||||
|
- [-] Convert a .md file to an .html output
|
||||||
|
|
||||||
|
### Reaches
|
||||||
|
- [ ] Hot reload?
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
61
flake.lock
generated
Normal file
61
flake.lock
generated
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1760284886,
|
||||||
|
"narHash": "sha256-TK9Kr0BYBQ/1P5kAsnNQhmWWKgmZXwUQr4ZMjCzWf2c=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "cf3f5c4def3c7b5f1fc012b3d839575dbe552d43",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
42
flake.nix
Normal file
42
flake.nix
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
description = "Blank development flake. Adjust as needed";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils, ... }:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# Define the development shell.
|
||||||
|
# When you run `nix develop` (or direnv activates), you'll enter this shell.
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
# List all the development tools you need available in this shell's PATH.
|
||||||
|
packages = with pkgs; [
|
||||||
|
gcc
|
||||||
|
gdb
|
||||||
|
stdenv
|
||||||
|
];
|
||||||
|
|
||||||
|
# Define the shell that will be executed.
|
||||||
|
# Here, we explicitly use zsh.
|
||||||
|
# Note: pkgs.zsh needs to be included in `packages` or `nativeBuildInputs`
|
||||||
|
# for it to be found in the shell's environment. `inherit pkgs.zsh;` is concise.
|
||||||
|
inherit (pkgs) zsh;
|
||||||
|
|
||||||
|
# Environment variables and commands to run when the shell starts.
|
||||||
|
shellHook = ''
|
||||||
|
# Add any exports, hooks, aliases, or anything else here
|
||||||
|
|
||||||
|
# Exec zsh to replace the current shell process with zsh.
|
||||||
|
# This ensures your prompt and zsh configurations load correctly.
|
||||||
|
exec zsh
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
43
lib/parser.cpp
Normal file
43
lib/parser.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#include "./parser.h"
|
||||||
|
#include <cctype>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
|
||||||
|
void removeWhitespace(string &input) {
|
||||||
|
size_t end = input.find_last_not_of(" \t\n\r\f\v");
|
||||||
|
if (end != std::string::npos) {
|
||||||
|
input.erase(end + 1);
|
||||||
|
} else {
|
||||||
|
input.clear(); // String contains only whitespace
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t start = input.find_first_not_of(" \t\n\r\f\v");
|
||||||
|
if (start != std::string::npos) {
|
||||||
|
input.erase(0, start);
|
||||||
|
} else {
|
||||||
|
input.clear(); // String contains only whitespace
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Parser::Parser(string input_file_path, string output_file_path) {
|
||||||
|
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 == "") {
|
||||||
|
std::cout << "CLEANING" << std::endl;
|
||||||
|
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;
|
||||||
|
} else {
|
||||||
|
this->output_file_path = output_file_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
lib/parser.h
Normal file
21
lib/parser.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef PARSER_H
|
||||||
|
#define PARSER_H
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class Parser {
|
||||||
|
private:
|
||||||
|
std::string input_file_path;
|
||||||
|
std::string output_file_path;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Parser(std::string input_file_path, std::string output_file_path = "");
|
||||||
|
|
||||||
|
inline void Print() {
|
||||||
|
std::cout << this->input_file_path << " -> " << this->output_file_path
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
25
src/main.cpp
Normal file
25
src/main.cpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#include "../lib/parser.h"
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
if (argc <= 1) {
|
||||||
|
std::cerr << "Usage: <input_file> <?output_file>" << std::endl;
|
||||||
|
return 0; // TODO: Should return 1?
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (argc >= 3) {
|
||||||
|
Parser p(argv[1], argv[2]);
|
||||||
|
p.Print();
|
||||||
|
} else {
|
||||||
|
Parser p(argv[1]);
|
||||||
|
p.Print();
|
||||||
|
}
|
||||||
|
} catch (const std::runtime_error &e) {
|
||||||
|
std::cout << "Caught an error: " << e.what() << std::endl;
|
||||||
|
} catch (...) {
|
||||||
|
std::cout << "Caught an error: UNKNOWN" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
38
syntax.md
Normal file
38
syntax.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
Reference [here](https://www.markdownguide.org/basic-syntax/)
|
||||||
|
|
||||||
|
Headings, h# tags
|
||||||
|
|
||||||
|
|
||||||
|
# Header Level 1 -> <h1> Content </h1>
|
||||||
|
## Header Level 2 -> <h2> Content </h2>
|
||||||
|
### Header Level 3 -> <h3> Content </h3>
|
||||||
|
#### Header Level 4 -> <h4> Content </h4>
|
||||||
|
##### Header Level 5 -> <h5> Content </h5>
|
||||||
|
###### Header Level 6 -> <h6> Content </h6>
|
||||||
|
|
||||||
|
|
||||||
|
Alternate syntax (n number of =/-)
|
||||||
|
|
||||||
|
Header Level 1 -> <h1> Content </h1>
|
||||||
|
================
|
||||||
|
|
||||||
|
|
||||||
|
Header Level 2 -> <h2> Content </h2>
|
||||||
|
----------------
|
||||||
|
|
||||||
|
|
||||||
|
Paragraph tags
|
||||||
|
|
||||||
|
Hello world -> <p> Hello world </p>
|
||||||
|
|
||||||
|
This is also
|
||||||
|
a paragraph -> <p> this is also a paragraph regardless </p>
|
||||||
|
regardless
|
||||||
|
|
||||||
|
However
|
||||||
|
this is a break, because it ends with two spaces -> <p> However <br> this is a break, because it ends with two spaces </p>
|
||||||
|
|
||||||
|
Double returns also
|
||||||
|
|
||||||
|
yield line breaks -> <p> Double returns also <br> yield line breaks </p>
|
||||||
17
test/input.md
Normal file
17
test/input.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
|
||||||
|
# Hello world in an h1 tag
|
||||||
|
|
||||||
|
|
||||||
|
## This is a h2 tag
|
||||||
|
|
||||||
|
|
||||||
|
### h3
|
||||||
|
|
||||||
|
|
||||||
|
#### h4
|
||||||
|
|
||||||
|
##### h5
|
||||||
|
|
||||||
|
###### h6
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user