From 350c20efa64a9161d9a60000084df81d3cc40ddc Mon Sep 17 00:00:00 2001 From: jmsgrogan Date: Thu, 20 Oct 2022 09:00:39 +0100 Subject: [PATCH] Toward template rendering. --- src/compiler/CMakeLists.txt | 4 + src/compiler/TemplateFile.cpp | 187 ++++++++++++ src/compiler/TemplateFile.h | 50 ++++ src/compiler/TemplateNodes.cpp | 0 src/compiler/TemplateNodes.h | 190 ++++++++++++ src/compiler/TemplatingEngine.cpp | 82 ++++++ src/compiler/TemplatingEngine.h | 393 +------------------------ src/core/file_utilities/File.cpp | 13 + test/compiler/TestTemplatingEngine.cpp | 8 +- 9 files changed, 542 insertions(+), 385 deletions(-) create mode 100644 src/compiler/TemplateFile.cpp create mode 100644 src/compiler/TemplateFile.h create mode 100644 src/compiler/TemplateNodes.cpp create mode 100644 src/compiler/TemplateNodes.h diff --git a/src/compiler/CMakeLists.txt b/src/compiler/CMakeLists.txt index 04c667c..6879715 100644 --- a/src/compiler/CMakeLists.txt +++ b/src/compiler/CMakeLists.txt @@ -1,11 +1,15 @@ list(APPEND compiler_HEADERS Lexer.h TemplatingEngine.h + TemplateFile.h + TemplateNodes.h ) list(APPEND compiler_LIB_INCLUDES Lexer.cpp TemplatingEngine.cpp + TemplateFile.cpp + TemplateNodes.cpp ) add_library(compiler SHARED ${compiler_LIB_INCLUDES} ${compiler_HEADERS}) diff --git a/src/compiler/TemplateFile.cpp b/src/compiler/TemplateFile.cpp new file mode 100644 index 0000000..e6fd90c --- /dev/null +++ b/src/compiler/TemplateFile.cpp @@ -0,0 +1,187 @@ +#include "TemplateFile.h" + +#include + +TemplateFile::TemplateFile(const Path& path) + : mPath(path), + mRootNode(std::make_unique(nullptr)) +{ + +} + +std::vector TemplateFile::getProcessedContent() const +{ + return mProcessedContent; +} + +std::string TemplateFile::getName() const +{ + return mPath.stem().string(); +} + +void TemplateFile::loadContent() +{ + std::cout << "Trying to load file at: " << mPath << std::endl; + mRawContent = File(mPath).readLines(); + mWorkingLine = 0; + mWorkingNode = mRootNode.get(); + mWorkingTextBody = std::make_unique(mWorkingNode); + for (const auto& line : mRawContent) + { + processLine(line); + mWorkingLine++; + } + onTextBodyFinished(); +} + +void TemplateFile::onTextBodyFinished(std::string working_string) +{ + if (!working_string.empty()) + { + mWorkingTextBody->addLine(working_string); + } + + if (mWorkingTextBody->hasContent()) + { + mWorkingNode->addChild(std::move(mWorkingTextBody)); + mWorkingTextBody = std::make_unique(mWorkingNode); + } +} + +void TemplateFile::processLine(const std::string& line) +{ + bool last_was_ldelimiter{ false }; + bool last_was_statement_rdelimiter{ false }; + bool last_was_expression_rdelimiter{ false }; + bool in_statement{ false }; + bool in_expression{ false }; + std::string working_string; + std::string last_working_string; + for (auto c : line) + { + if (c == '{') + { + if (last_was_ldelimiter) + { + in_expression = true; + last_was_ldelimiter = false; + working_string = ""; + } + else + { + last_was_ldelimiter = true; + } + } + else if (c == '%' && last_was_ldelimiter) + { + last_was_ldelimiter = false; + in_statement = true; + last_working_string = working_string; + working_string = ""; + } + else if (c == '%' && in_statement) + { + last_was_statement_rdelimiter = true; + } + else if (c == '}' && (last_was_statement_rdelimiter || in_expression || last_was_expression_rdelimiter)) + { + if (last_was_statement_rdelimiter) + { + onTextBodyFinished(last_working_string); + onFoundStatement(working_string); + last_was_statement_rdelimiter = false; + working_string = ""; + } + else if (in_expression) + { + last_was_expression_rdelimiter = true; + in_expression = false; + } + else + { + onTextBodyFinished(); + onFoundExpression(working_string); + last_was_expression_rdelimiter = false; + working_string = ""; + } + } + else if (last_was_ldelimiter && (!in_statement && !in_expression)) + { + last_was_ldelimiter = false; + working_string += '{'; + working_string += c; + } + else + { + working_string += c; + } + } + + if (!working_string.empty()) + { + mWorkingTextBody->addLine(working_string); + } +} + +void TemplateFile::onFoundStatement(const std::string& statement_string) +{ + const auto statement_elements = StringUtils::split(statement_string); + + if (statement_elements.size() == 0) + { + return; + } + const auto statement_type = statement_elements[0]; + if (statement_type == "block" && statement_elements.size() == 2) + { + onFoundBlock(statement_elements); + } + else if (statement_type == "endblock") + { + onFoundEndBlock(statement_elements); + } + else if (statement_type == "extends") + { + onFoundExtends(statement_elements); + } + +} + +void TemplateFile::onFoundBlock(const std::vector args) +{ + if (args.size() != 2) + { + return; + } + auto block = std::make_unique(mWorkingNode, args[1]); + auto temp = block.get(); + mWorkingNode->addChild(std::move(block)); + mWorkingNode = temp; +} + +void TemplateFile::onFoundEndBlock(const std::vector args) +{ + mWorkingNode = mWorkingNode->getParent(); +} + +void TemplateFile::onFoundExtends(const std::vector args) +{ + if (args.size() != 2) + { + return; + } + auto extends = std::make_unique(mWorkingNode, args[1]); + mWorkingNode->addChild(std::move(extends)); +} + +void TemplateFile::onFoundExpression(const std::string& expression_string) +{ + auto expression = std::make_unique(mWorkingNode, expression_string); + mWorkingNode->addChild(std::move(expression)); +} + +void TemplateFile::dumpContent() +{ + auto content = mRootNode->getRawContent(); + std::cout << content << std::endl; +} \ No newline at end of file diff --git a/src/compiler/TemplateFile.h b/src/compiler/TemplateFile.h new file mode 100644 index 0000000..cd55ebc --- /dev/null +++ b/src/compiler/TemplateFile.h @@ -0,0 +1,50 @@ +#pragma once + +#include "File.h" + +#include "TemplateNodes.h" + +#include + +class TemplateFile +{ +public: + TemplateFile(const Path& path); + + std::vector getProcessedContent() const; + + std::string getName() const; + + void loadContent(); + + void onTextBodyFinished(std::string working_string = {}); + + void processLine(const std::string& line); + + void onFoundStatement(const std::string& statement_string); + + void onFoundBlock(const std::vector args); + + void onFoundEndBlock(const std::vector args); + + void onFoundExtends(const std::vector args); + + void onFoundExpression(const std::string& expression_string); + + void dumpContent(); + + TemplateNode* getContent() const + { + return mRootNode.get(); + } + +private: + Path mPath; + unsigned mWorkingLine{ 0 }; + std::string mParentName; + std::vector mRawContent; + std::vector mProcessedContent; + std::unique_ptr mRootNode; + TemplateNode* mWorkingNode{ nullptr }; + std::unique_ptr mWorkingTextBody; +}; \ No newline at end of file diff --git a/src/compiler/TemplateNodes.cpp b/src/compiler/TemplateNodes.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/compiler/TemplateNodes.h b/src/compiler/TemplateNodes.h new file mode 100644 index 0000000..2c9991f --- /dev/null +++ b/src/compiler/TemplateNodes.h @@ -0,0 +1,190 @@ +#pragma once + +class TemplateNode +{ +public: + + TemplateNode(TemplateNode* parent) + : mParent(parent) + { + + } + + TemplateNode* getParent() const + { + return mParent; + } + + virtual void addChild(std::unique_ptr child) + { + mChildren.push_back(std::move(child)); + } + + std::size_t getNumChildren() const + { + return mChildren.size(); + } + + TemplateNode* getChild(std::size_t index) const + { + return mChildren[index].get(); + } + + virtual std::string getRawContent() const + { + std::string content; + for (const auto& child : mChildren) + { + content += child->getRawContent() + "\n"; + } + return content; + } + + template + T* getFirstChildShallow() const + { + for (const auto& child : mChildren) + { + if (auto ret = dynamic_cast(child.get())) + { + return ret; + } + } + return nullptr; + } + + void setExtensionParent(TemplateNode* parent) + { + mExtensionParent = parent; + } + + virtual std::string render() + { + std::string content; + for (size_t idx = 0; idx < mChildren.size(); idx++) + { + content += mChildren[idx]->render(); + } + return content; + } + +protected: + std::vector > mChildren; + TemplateNode* mParent{ nullptr }; + TemplateNode* mExtensionParent{ nullptr }; +}; + +using TemplateNodePtr = std::unique_ptr; + +class TemplateExtends : public TemplateNode +{ +public: + TemplateExtends(TemplateNode* parent, const std::string& path) + : TemplateNode(parent), + mPath(path) + { + + }; + + std::string getRawContent() const override + { + return "TemplateExtends: " + mPath; + } + + std::string getPath() const + { + return mPath; + } + +private: + std::string mPath; +}; + +class TemplateBlock : public TemplateNode +{ +public: + TemplateBlock(TemplateNode* parent, const std::string& name) + : TemplateNode(parent), + mName(name) + { + + } + + void addLine(const std::string& line) + { + mBody.push_back(line); + } + + std::string getRawContent() const override + { + std::string content = "TemplateBlock: " + mName + "\n"; + content += TemplateNode::getRawContent(); + return content; + } + +private: + std::string mName; + std::vector mBody; +}; + +class TemplateExpression : public TemplateNode +{ +public: + TemplateExpression(TemplateNode* parent, const std::string& content) + : TemplateNode(parent), + mContent(content) + { + + } + + std::string getRawContent() const override + { + return "TemplateExpression: " + mContent; + } + +private: + std::string mContent; +}; + +class TemplateTextBody : public TemplateNode +{ +public: + TemplateTextBody(TemplateNode* parent) + : TemplateNode(parent) + { + + } + + void addLine(const std::string& content) + { + mContent.push_back(content); + } + + bool hasContent() const + { + return !mContent.empty(); + } + + std::string render() override + { + std::string content; + for (const auto& line : mContent) + { + content += line; + } + return content; + } + + std::string getRawContent() const override + { + std::string content; + for (const auto& line : mContent) + { + content += "TemplateBody: " + line + "\n"; + } + return content; + } + +private: + std::vector mContent; +}; \ No newline at end of file diff --git a/src/compiler/TemplatingEngine.cpp b/src/compiler/TemplatingEngine.cpp index e69de29..5c99ef5 100644 --- a/src/compiler/TemplatingEngine.cpp +++ b/src/compiler/TemplatingEngine.cpp @@ -0,0 +1,82 @@ +#include "TemplatingEngine.h" + +#include "Directory.h" + +#include + +TemplatingEngine::TemplatingEngine(const Path& workingDirectory) + : mWorkingDirectory(workingDirectory) +{ + +} + +void TemplatingEngine::loadTemplateFiles() +{ + const auto files = Directory::getFilesWithExtension(mWorkingDirectory, mTemplateExtension); + for (const auto& file : files) + { + mTemplateFiles.push_back(std::make_unique(file)); + } +} + +std::string TemplatingEngine::processTemplate(const std::string& name) +{ + if (auto file = getTemplateFile(name)) + { + return processTemplate(file); + } + return {}; +} + +TemplateFile* TemplatingEngine::getTemplateFile(const Path& path) +{ + return getTemplateFile(path.stem().string()); +} + +TemplateFile* TemplatingEngine::getTemplateFile(const std::string& name) +{ + std::cout << "Looking for template file with name: " << name << std::endl; + for (const auto& file : mTemplateFiles) + { + if (file->getName() == name) + { + return file.get(); + } + } + return nullptr; +} + +std::string TemplatingEngine::processTemplate(TemplateFile* file, TemplateNode* parent) +{ + file->loadContent(); + file->dumpContent(); + + auto content = file->getContent(); + if (parent) + { + content->setExtensionParent(parent); + } + + if (auto extension_node = content->getFirstChildShallow()) + { + std::cout << "Found extension node" << std::endl; + if (auto extension_template = getTemplateFile(Path(extension_node->getPath()))) + { + std::cout << "Found extension template" << std::endl; + return processTemplate(extension_template, parent); + } + else + { + return render(content); + } + } + else + { + return render(content); + } +} + +std::string TemplatingEngine::render(TemplateNode* content) +{ + return content->render(); +} \ No newline at end of file diff --git a/src/compiler/TemplatingEngine.h b/src/compiler/TemplatingEngine.h index 548da3f..b9e31ab 100644 --- a/src/compiler/TemplatingEngine.h +++ b/src/compiler/TemplatingEngine.h @@ -1,399 +1,28 @@ #pragma once #include "File.h" -#include "Directory.h" -#include "StringUtils.h" +#include "TemplateFile.h" #include #include -#include -#include - -class TemplateNode -{ -public: - - TemplateNode(TemplateNode* parent) - : mParent(parent) - { - - } - - TemplateNode* getParent() const - { - return mParent; - } - - virtual void addChild(std::unique_ptr child) - { - mChildren.push_back(std::move(child)); - } - - std::size_t getNumChildren() const - { - return mChildren.size(); - } - - TemplateNode* getChild(std::size_t index) const - { - return mChildren[index].get(); - } - - virtual std::string getRawContent() const - { - std::string content; - for (const auto& child : mChildren) - { - content += child->getRawContent() + "\n"; - } - return content; - } - -protected: - std::vector > mChildren; - TemplateNode* mParent{ nullptr }; -}; - -using TemplateNodePtr = std::unique_ptr; - -class TemplateExtends : public TemplateNode -{ -public: - TemplateExtends(TemplateNode* parent, const std::string& path) - : TemplateNode(parent), - mPath(path) - { - - }; - - std::string getRawContent() const override - { - return "TemplateExtends: " + mPath; - } - -private: - std::string mPath; -}; - -class TemplateBlock : public TemplateNode -{ -public: - TemplateBlock(TemplateNode* parent, const std::string& name) - : TemplateNode(parent), - mName(name) - { - - } - - void addLine(const std::string& line) - { - mBody.push_back(line); - } - - std::string getRawContent() const override - { - std::string content = "TemplateBlock: " + mName + "\n"; - content += TemplateNode::getRawContent(); - return content; - } - -private: - std::string mName; - std::vector mBody; -}; - -class TemplateExpression : public TemplateNode -{ -public: - TemplateExpression(TemplateNode* parent, const std::string& content) - : TemplateNode(parent), - mContent(content) - { - - } - - std::string getRawContent() const override - { - return "TemplateExpression: " + mContent; - } - -private: - std::string mContent; -}; - -class TemplateTextBody : public TemplateNode -{ -public: - TemplateTextBody(TemplateNode* parent) - : TemplateNode(parent) - { - - } - - void addLine(const std::string& content) - { - mContent.push_back(content); - } - - bool hasContent() const - { - return !mContent.empty(); - } - - std::string getRawContent() const override - { - std::string content; - for (const auto& line : mContent) - { - content += "TemplateBody: " + line + "\n"; - } - return content; - } - -private: - std::vector mContent; -}; - -class TemplateFile -{ -public: - TemplateFile(const Path& path) - : mPath(path), - mRootNode(std::make_unique(nullptr)) - { - - } - - std::vector getProcessedContent() const - { - return mProcessedContent; - } - - std::string getName() const - { - return mPath.stem().string(); - } - - void loadContent() - { - std::cout << "Trying to load file at: " << mPath << std::endl; - mRawContent = File(mPath).readLines(); - mWorkingLine = 0; - mWorkingNode = mRootNode.get(); - mWorkingTextBody = std::make_unique(mWorkingNode); - for (const auto& line : mRawContent) - { - processLine(line); - mWorkingLine++; - } - onTextBodyFinished(); - } - - void onTextBodyFinished(std::string working_string = {}) - { - if (!working_string.empty()) - { - mWorkingTextBody->addLine(working_string); - } - - if (mWorkingTextBody->hasContent()) - { - mWorkingNode->addChild(std::move(mWorkingTextBody)); - mWorkingTextBody = std::make_unique(mWorkingNode); - } - } - - void processLine(const std::string& line) - { - bool last_was_ldelimiter{ false }; - bool last_was_statement_rdelimiter{ false }; - bool last_was_expression_rdelimiter{ false }; - bool in_statement{ false }; - bool in_expression{ false }; - std::string working_string; - std::string last_working_string; - for (auto c : line) - { - if (c == '{') - { - if (last_was_ldelimiter) - { - in_expression = true; - last_was_ldelimiter = false; - working_string = ""; - } - else - { - last_was_ldelimiter = true; - } - } - else if (c == '%' && last_was_ldelimiter) - { - last_was_ldelimiter = false; - in_statement = true; - last_working_string = working_string; - working_string = ""; - } - else if (c == '%' && in_statement) - { - last_was_statement_rdelimiter = true; - } - else if (c == '}' && (last_was_statement_rdelimiter || in_expression || last_was_expression_rdelimiter)) - { - if (last_was_statement_rdelimiter) - { - onTextBodyFinished(last_working_string); - onFoundStatement(working_string); - last_was_statement_rdelimiter = false; - working_string = ""; - } - else if (in_expression) - { - last_was_expression_rdelimiter = true; - in_expression = false; - } - else - { - onTextBodyFinished(); - onFoundExpression(working_string); - last_was_expression_rdelimiter = false; - working_string = ""; - } - } - else if (last_was_ldelimiter && (!in_statement && !in_expression)) - { - last_was_ldelimiter = false; - working_string += '{'; - working_string += c; - } - else - { - working_string += c; - } - } - - if (!working_string.empty()) - { - mWorkingTextBody->addLine(working_string); - } - } - - void onFoundStatement(const std::string& statement_string) - { - const auto statement_elements = StringUtils::split(statement_string); - - if (statement_elements.size() == 0) - { - return; - } - const auto statement_type = statement_elements[0]; - if (statement_type == "block" && statement_elements.size() == 2) - { - onFoundBlock(statement_elements); - } - else if (statement_type == "endblock") - { - onFoundEndBlock(statement_elements); - } - else if (statement_type == "extends") - { - onFoundExtends(statement_elements); - } - - } - - void onFoundBlock(const std::vector args) - { - if (args.size() != 2) - { - return; - } - auto block = std::make_unique(mWorkingNode, args[1]); - auto temp = block.get(); - mWorkingNode->addChild(std::move(block)); - mWorkingNode = temp; - } - - void onFoundEndBlock(const std::vector args) - { - mWorkingNode = mWorkingNode->getParent(); - } - - void onFoundExtends(const std::vector args) - { - if (args.size() != 2) - { - return; - } - auto extends = std::make_unique(mWorkingNode, args[1]); - mWorkingNode->addChild(std::move(extends)); - } - - void onFoundExpression(const std::string& expression_string) - { - auto expression = std::make_unique(mWorkingNode, expression_string); - mWorkingNode->addChild(std::move(expression)); - } - - void dumpContent() - { - auto content = mRootNode->getRawContent(); - std::cout << content << std::endl; - } - -private: - Path mPath; - unsigned mWorkingLine{ 0 }; - std::string mParentName; - std::vector mRawContent; - std::vector mProcessedContent; - std::unique_ptr mRootNode; - TemplateNode* mWorkingNode{ nullptr }; - std::unique_ptr mWorkingTextBody; -}; class TemplatingEngine { public: - TemplatingEngine(const Path& workingDirectory) - : mWorkingDirectory(workingDirectory) - { + TemplatingEngine(const Path& workingDirectory); - } - - void loadTemplateFiles() - { - const auto files = Directory::getFilesWithExtension(mWorkingDirectory, mTemplateExtension); - for (const auto& file : files) - { - mTemplateFiles.push_back(TemplateFile(file)); - } - } - - void processTemplate(const std::string& name) - { - for (auto& file : mTemplateFiles) - { - const auto filename = file.getName(); - if (filename == name) - { - processTemplate(file); - break; - } - } - } - - void processTemplate(TemplateFile& file) - { - file.loadContent(); - file.dumpContent(); - } - - + void loadTemplateFiles(); + std::string processTemplate(const std::string& name); private: - std::vector mTemplateFiles; + TemplateFile* getTemplateFile(const std::string& name); + TemplateFile* getTemplateFile(const Path& path); + std::string render(TemplateNode* content); + + std::string processTemplate(TemplateFile* file, TemplateNode* parent = nullptr); + + std::vector > mTemplateFiles; Path mWorkingDirectory; std::string mTemplateExtension{ ".html" }; }; \ No newline at end of file diff --git a/src/core/file_utilities/File.cpp b/src/core/file_utilities/File.cpp index e0271f6..74202ab 100644 --- a/src/core/file_utilities/File.cpp +++ b/src/core/file_utilities/File.cpp @@ -76,7 +76,20 @@ FileFormat::Format File::InferFormat() const void File::WriteText(const std::string& text) { + bool had_to_open{ false }; + if (!mOutHandle) + { + had_to_open = true; + SetAccessMode(File::AccessMode::Write); + Open(); + } + (*mOutHandle) << text; + + if (had_to_open) + { + Close(); + } } std::vector File::readLines() diff --git a/test/compiler/TestTemplatingEngine.cpp b/test/compiler/TestTemplatingEngine.cpp index ee62273..bcdc028 100644 --- a/test/compiler/TestTemplatingEngine.cpp +++ b/test/compiler/TestTemplatingEngine.cpp @@ -1,5 +1,7 @@ #include "TemplatingEngine.h" +#include + #include #include @@ -9,8 +11,8 @@ int main() auto engine = TemplatingEngine(data_loc); engine.loadTemplateFiles(); - engine.processTemplate("index"); - engine.processTemplate("base"); - + const auto content = engine.processTemplate("index"); + File outfile("index.html"); + outfile.WriteText(content); } \ No newline at end of file