Clean project structure.

This commit is contained in:
jmsgrogan 2023-01-17 10:13:25 +00:00
parent 78a4fa99ff
commit 947bf937fd
496 changed files with 206 additions and 137 deletions

View file

@ -0,0 +1,28 @@
set(MODULE_NAME compiler)
list(APPEND HEADERS
Lexer.h
template_engine/TemplatingEngine.h
template_engine/TemplateFile.h
template_engine/TemplateNode.h
template_engine/TemplateElements.h
)
list(APPEND SOURCES
Lexer.cpp
template_engine/TemplatingEngine.cpp
template_engine/TemplateFile.cpp
template_engine/TemplateNode.cpp
template_engine/TemplateElements.cpp
)
add_library(${MODULE_NAME} SHARED ${HEADERS} ${SOURCES})
target_include_directories(${MODULE_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/template_engine
)
target_link_libraries( ${MODULE_NAME} PUBLIC core)
set_target_properties( ${MODULE_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER src/base)

View file

@ -0,0 +1,72 @@
#include "Lexer.h"
bool Lexer::matchPattern(const std::string& pattern, const std::string& checkString, char delimiter, std::vector<std::string>& hitSequence)
{
if (checkString.empty())
{
return false;
}
if (pattern.empty())
{
return false;
}
bool found_pattern = true;
unsigned check_idx = 0;
unsigned pattern_idx = 0;
std::vector<std::string> hits;
std::string working_hit;
while(check_idx < checkString.size())
{
if (pattern_idx == pattern.size())
{
break;
}
auto check_char = checkString[check_idx];
auto pattern_char = pattern[pattern_idx];
if (pattern_char == delimiter)
{
if (pattern_idx + 1 < pattern.size())
{
if (check_char == pattern[pattern_idx + 1])
{
hits.push_back(working_hit);
working_hit.clear();
pattern_idx++;
}
else
{
working_hit+=check_char;
check_idx++;
}
}
else
{
working_hit+=check_char;
check_idx++;
}
}
else
{
if (check_char == pattern_char)
{
check_idx++;
pattern_idx++;
}
else
{
found_pattern = false;
break;
}
}
}
if (found_pattern)
{
hitSequence = hits;
}
return found_pattern;
}

11
src/base/compiler/Lexer.h Normal file
View file

@ -0,0 +1,11 @@
#pragma once
#include <string>
#include <vector>
class Lexer
{
public:
// e.g. Pattern [@](@) returns <source, tag> for input: [source](tag) and delimiter @
static bool matchPattern(const std::string& pattern, const std::string& checkString, char delimiter, std::vector<std::string>& hitSequence);
};

View file

@ -0,0 +1,172 @@
#include "TemplateElements.h"
#include "StringUtils.h"
#include "TemplateSubstitutionContext.h"
#include <iostream>
TemplateExtends::TemplateExtends(TemplateNode* parent, const std::string& path)
: TemplateNode(parent)
{
mPath = StringUtils::stripQuotes(path);
};
std::string TemplateExtends::getRawContent() const
{
return "TemplateExtends: " + mPath;
}
std::string TemplateExtends::getPath() const
{
return mPath;
}
TemplateBlock::TemplateBlock(TemplateNode* parent, const std::string& name)
: TemplateNode(parent),
mName(name)
{
}
void TemplateBlock::addLine(const std::string& line)
{
mBody.push_back(line);
}
std::string TemplateBlock::getRawContent() const
{
std::string content = "TemplateBlock: " + mName + " - Start \n";
content += TemplateNode::getRawContent();
content += "TemplateBlock: " + mName + " - End";
return content;
}
std::string TemplateBlock::getIdentifier() const
{
return mName;
}
std::string TemplateBlock::renderAsParent(TemplateSubstitutionContext* substitutions, TemplateBlock* base)
{
std::string content;
for (auto& child : mChildren)
{
if (child->getType() == Type::EXPRESSION)
{
auto expression = dynamic_cast<TemplateExpression*>(child.get());
if (expression->getContent() == "super()")
{
content += base->render(substitutions, nullptr);
}
}
else if(child->getType() == Type::TEXT_BODY)
{
content += child->render(substitutions, nullptr);
}
}
return content;
}
std::string TemplateBlock::renderAsLeaf(TemplateSubstitutionContext* substitutions)
{
std::string content;
for (auto& child : mChildren)
{
if(child->getType() == Type::TEXT_BODY)
{
content += child->render(substitutions, nullptr);
}
}
return content;
}
std::string TemplateBlock::render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext)
{
std::string content;
if (parentContext)
{
if (auto parent_node = parentContext->getFirstChildShallow(Type::BLOCK, getIdentifier()))
{
content = dynamic_cast<TemplateBlock*>(parent_node)->renderAsParent(substitutions, this);
}
else
{
content= renderAsLeaf(substitutions);
}
}
else
{
content= renderAsLeaf(substitutions);
}
return content;
}
TemplateExpression::TemplateExpression(TemplateNode* parent, const std::string& content)
: TemplateNode(parent),
mContent(content)
{
}
const std::string& TemplateExpression::getContent() const
{
return mContent;
}
std::string TemplateExpression::render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext)
{
if (substitutions && substitutions->hasSubstitution(mContent))
{
return substitutions->getSubstitution(mContent);
}
return {};
}
std::string TemplateExpression::getRawContent() const
{
return "TemplateExpression: " + mContent;
}
TemplateTextBody::TemplateTextBody(TemplateNode* parent)
: TemplateNode(parent)
{
}
void TemplateTextBody::addLine(const std::string& content)
{
mContent.push_back(content);
}
bool TemplateTextBody::hasContent() const
{
return !mContent.empty();
}
std::string TemplateTextBody::render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext)
{
std::string content;
for(unsigned idx=0; idx<mContent.size(); idx++)
{
content += mContent[idx];
if(idx != mContent.size() - 1)
{
content += '\n';
}
}
return content;
}
std::string TemplateTextBody::getRawContent() const
{
std::string content;
for(unsigned idx=0; idx<mContent.size(); idx++)
{
content += "Template Body L-" + std::to_string(idx) + ": " + mContent[idx];
if(idx != mContent.size() - 1)
{
content += '\n';
}
}
return content;
}

View file

@ -0,0 +1,96 @@
#pragma once
#include "TemplateNode.h"
class TemplateExtends : public TemplateNode
{
public:
TemplateExtends(TemplateNode* parent, const std::string& path);
virtual ~TemplateExtends() = default;
std::string getRawContent() const override;
std::string getPath() const;
Type getType() const override
{
return Type::EXTENDS;
}
private:
std::string mPath;
};
class TemplateBlock : public TemplateNode
{
public:
TemplateBlock(TemplateNode* parent, const std::string& name);
virtual ~TemplateBlock() = default;
void addLine(const std::string& line);
std::string getRawContent() const override;
std::string getIdentifier() const override;
Type getType() const override
{
return Type::BLOCK;
}
std::string renderAsParent(TemplateSubstitutionContext* substitutions, TemplateBlock* base);
std::string renderAsLeaf(TemplateSubstitutionContext* substitutions);
std::string render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext) override;
private:
std::string mName;
std::vector<std::string> mBody;
};
class TemplateExpression : public TemplateNode
{
public:
TemplateExpression(TemplateNode* parent, const std::string& content);
virtual ~TemplateExpression() = default;
std::string getRawContent() const override;
const std::string& getContent() const;
Type getType() const override
{
return Type::EXPRESSION;
}
std::string render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext) override;
private:
std::string mContent;
};
class TemplateTextBody : public TemplateNode
{
public:
TemplateTextBody(TemplateNode* parent);
virtual ~TemplateTextBody() = default;
void addLine(const std::string& content);
std::string getRawContent() const override;
bool hasContent() const;
std::string render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext) override;
Type getType() const override
{
return Type::TEXT_BODY;
}
private:
std::vector<std::string> mContent;
};

View file

@ -0,0 +1,207 @@
#include "TemplateFile.h"
#include "TemplateElements.h"
#include "TemplateNode.h"
#include "Lexer.h"
#include "File.h"
#include "StringUtils.h"
TemplateFile::TemplateFile(const Path& path)
: mPath(path),
mRootNode(std::make_unique<TemplateNode>(nullptr))
{
}
TemplateFile::~TemplateFile()
{
}
std::string TemplateFile::getName() const
{
return mPath.stem().string();
}
bool TemplateFile::hasLoaded() const
{
return mHasLoaded;
}
TemplateNode* TemplateFile::getContent() const
{
return mRootNode.get();
}
void TemplateFile::loadContent()
{
if (mHasLoaded)
{
return;
}
mRawContent = File(mPath).readLines();
mWorkingNode = mRootNode.get();
mWorkingTextSpan = std::make_unique<TemplateTextBody>(mWorkingNode);
for (const auto& line : mRawContent)
{
processLine(line);
}
onTextSpanFinished();
mHasLoaded = true;
}
std::string TemplateFile::dumpContent()
{
return mRootNode->getRawContent();
}
void TemplateFile::onTextSpanFinished()
{
if (!mWorkingLineContent.empty())
{
mWorkingTextSpan->addLine(mWorkingLineContent);
}
if (mWorkingTextSpan->hasContent())
{
mWorkingNode->addChild(std::move(mWorkingTextSpan));
mWorkingTextSpan = std::make_unique<TemplateTextBody>(mWorkingNode);
}
mWorkingLineContent.clear();
}
std::size_t TemplateFile::checkForStatement(const std::string& lineSection)
{
if (lineSection.empty())
{
return 0;
}
std::vector<std::string> hits;
std::size_t hit_size{0};
if (Lexer::matchPattern("{%@%}", lineSection, '@', hits))
{
if (hits.size() == 1)
{
auto content = hits[0];
onTextSpanFinished();
onFoundStatement(content);
hit_size = 4 + content.size();
}
}
return hit_size;
}
std::size_t TemplateFile::checkForExpression(const std::string& lineSection)
{
if (lineSection.empty())
{
return 0;
}
std::vector<std::string> hits;
std::size_t hit_size{0};
if (Lexer::matchPattern("{{@}}", lineSection, '@', hits))
{
if (hits.size() == 1)
{
auto content = hits[0];
onTextSpanFinished();
onFoundExpression(content);
hit_size = 4 + content.size();
}
}
return hit_size;
}
void TemplateFile::processLine(const std::string& line)
{
std::size_t line_position = 0;
mWorkingLineContent.clear();
while(line_position < line.size())
{
const auto remaining = line.substr(line_position, line.size() - line_position);
if(auto length = checkForStatement(remaining))
{
line_position += length;
}
else if(auto length = checkForExpression(remaining))
{
line_position += length;
}
else
{
mWorkingLineContent += line[line_position];
line_position++;
}
}
if (!mWorkingLineContent.empty())
{
mWorkingTextSpan->addLine(mWorkingLineContent);
mWorkingLineContent.clear();
}
}
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::onFoundExpression(const std::string& expression_string)
{
const auto stripped = StringUtils::stripSurroundingWhitepsace(expression_string);
auto expression = std::make_unique<TemplateExpression>(mWorkingNode, stripped);
mWorkingNode->addChild(std::move(expression));
}
void TemplateFile::onFoundBlock(const std::vector<std::string> args)
{
if (args.size() != 2)
{
return;
}
auto block = std::make_unique<TemplateBlock>(mWorkingNode, args[1]);
auto temp = block.get();
mWorkingNode->addChild(std::move(block));
mWorkingNode = temp;
}
void TemplateFile::onFoundEndBlock(const std::vector<std::string> args)
{
mWorkingNode = mWorkingNode->getParent();
}
void TemplateFile::onFoundExtends(const std::vector<std::string> args)
{
if (args.size() != 2)
{
return;
}
auto extends = std::make_unique<TemplateExtends>(mWorkingNode, args[1]);
mWorkingNode->addChild(std::move(extends));
}

View file

@ -0,0 +1,58 @@
#pragma once
#include <vector>
#include <string>
#include <filesystem>
class TemplateNode;
class TemplateTextBody;
using Path = std::filesystem::path;
class TemplateFile
{
public:
TemplateFile(const Path& path);
~TemplateFile();
std::string dumpContent();
std::string getName() const;
TemplateNode* getContent() const;
bool hasLoaded() const;
void loadContent();
private:
std::size_t checkForStatement(const std::string& lineSection);
std::size_t checkForExpression(const std::string& lineSection);
void onTextSpanFinished();
void onFoundStatement(const std::string& statement_string);
void onFoundExpression(const std::string& expression_string);
void onFoundBlock(const std::vector<std::string> args);
void onFoundEndBlock(const std::vector<std::string> args);
void onFoundExtends(const std::vector<std::string> args);
void processLine(const std::string& line);
Path mPath;
std::string mParentName;
std::vector<std::string> mRawContent;
bool mHasLoaded{false};
std::unique_ptr<TemplateNode> mRootNode;
TemplateNode* mWorkingNode{ nullptr };
std::string mWorkingLineContent;
std::unique_ptr<TemplateTextBody> mWorkingTextSpan;
};

View file

@ -0,0 +1,101 @@
#include "TemplateNode.h"
#include "TemplateSubstitutionContext.h"
TemplateNode::TemplateNode(TemplateNode* parent)
: mParent(parent)
{
}
TemplateNode* TemplateNode::getParent() const
{
return mParent;
}
void TemplateNode::addChild(std::unique_ptr<TemplateNode> child)
{
mChildren.push_back(std::move(child));
}
std::size_t TemplateNode::getNumChildren() const
{
return mChildren.size();
}
TemplateNode* TemplateNode::getFirstChildShallow(Type type, const std::string& identifier) const
{
for (const auto& child : mChildren)
{
if (child->getType() == type)
{
if (!identifier.empty())
{
if (child->getIdentifier() == identifier)
{
return child.get();
}
}
else
{
return child.get();
}
}
}
return nullptr;
}
TemplateNode::Type TemplateNode::getType() const
{
return Type::NONE;
}
std::string TemplateNode::getIdentifier() const
{
return {};
}
TemplateNode* TemplateNode::getChild(std::size_t index) const
{
return mChildren[index].get();
}
std::string TemplateNode::getRawContent() const
{
std::string content;
for (const auto& child : mChildren)
{
content += child->getRawContent() + "\n";
}
return content;
}
void TemplateNode::setExtensionParent(TemplateNode* parent)
{
mExtensionParent = parent;
}
void TemplateNode::setExtensionBase(TemplateNode* base)
{
mExtensionBase = base;
}
std::string TemplateNode::render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext)
{
if (mExtensionBase)
{
return mExtensionBase->render(substitutions, this);
}
if (!parentContext && mExtensionParent)
{
parentContext = mExtensionParent;
}
std::string content;
for (size_t idx = 0; idx < mChildren.size(); idx++)
{
content += mChildren[idx]->render(substitutions, parentContext);
}
return content;
}

View file

@ -0,0 +1,54 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
class TemplateSubstitutionContext;
class TemplateNode
{
public:
enum class Type
{
NONE,
BLOCK,
EXPRESSION,
EXTENDS,
TEXT_BODY
};
TemplateNode(TemplateNode* parent);
virtual ~TemplateNode() = default;
virtual void addChild(std::unique_ptr<TemplateNode> child);
TemplateNode* getChild(std::size_t index) const;
TemplateNode* getFirstChildShallow(Type type, const std::string& identifier = {}) const;
virtual std::string getIdentifier() const;
std::size_t getNumChildren() const;
TemplateNode* getParent() const;
virtual std::string getRawContent() const;
virtual Type getType() const;
virtual std::string render(TemplateSubstitutionContext* substitutions, TemplateNode* parentContext = nullptr);
void setExtensionParent(TemplateNode* parent);
void setExtensionBase(TemplateNode* base);
protected:
std::vector<std::unique_ptr<TemplateNode> > mChildren;
TemplateNode* mParent{ nullptr };
TemplateNode* mExtensionParent{ nullptr };
TemplateNode* mExtensionBase{ nullptr };
};
using TemplateNodePtr = std::unique_ptr<TemplateNode>;

View file

@ -0,0 +1,35 @@
#pragma once
#include <string>
#include <unordered_map>
class TemplateSubstitutionContext
{
public:
void addSubstitution(const std::string& key, const std::string& value)
{
mSubstitutions[key] = value;
}
bool hasSubstitution(const std::string& key) const
{
return mSubstitutions.find(key) != mSubstitutions.end();
}
std::string getSubstitution(const std::string& key) const
{
auto iter = mSubstitutions.find(key);
if(iter != mSubstitutions.end())
{
return iter->second;
}
else
{
return {};
}
}
private:
std::unordered_map<std::string, std::string> mSubstitutions;
};

View file

@ -0,0 +1,80 @@
#include "TemplatingEngine.h"
#include "Directory.h"
#include "FileLogger.h"
#include "TemplateElements.h"
#include "TemplateSubstitutionContext.h"
#include <iostream>
TemplatingEngine::TemplatingEngine(const Path& workingDirectory)
: mWorkingDirectory(workingDirectory)
{
}
std::string TemplatingEngine::renderTemplate(const std::string& name, TemplateSubstitutionContext* substitutionContext)
{
if (mTemplateFiles.empty())
{
loadTemplateFiles();
}
if (auto file = getTemplateFile(name))
{
if (!file->hasLoaded())
{
file->loadContent();
//std::cout << file->dumpContent();
processTemplate(file, nullptr);
}
return file->getContent()->render(substitutionContext, nullptr);
}
else
{
return {};
}
}
void TemplatingEngine::loadTemplateFiles()
{
const auto files = Directory::getFilesWithExtension(mWorkingDirectory, mTemplateExtension);
for (const auto& path : files)
{
mTemplateFiles[path.stem().string()] = std::make_unique<TemplateFile>(path);
}
MLOG_INFO("Found: " << mTemplateFiles.size() << " templates in " << mWorkingDirectory);
}
TemplateFile* TemplatingEngine::getTemplateFile(const Path& path)
{
return getTemplateFile(path.stem().string());
}
TemplateFile* TemplatingEngine::getTemplateFile(const std::string& name)
{
return mTemplateFiles[name].get();
}
void TemplatingEngine::processTemplate(TemplateFile* file, TemplateNode* parent)
{
auto content = file->getContent();
if (parent)
{
content->setExtensionParent(parent);
parent->setExtensionBase(content);
}
if (auto extension_node = dynamic_cast<TemplateExtends*>(content->getFirstChildShallow(TemplateNode::Type::EXTENDS)))
{
if (auto extension_template = getTemplateFile(Path(extension_node->getPath())))
{
extension_template->loadContent();
//std::cout << extension_template->dumpContent();
processTemplate(extension_template, content);
}
}
}

View file

@ -0,0 +1,30 @@
#pragma once
#include "File.h"
#include "TemplateFile.h"
#include <vector>
#include <string>
#include <memory>
#include <unordered_map>
class TemplateSubstitutionContext;
class TemplatingEngine
{
public:
TemplatingEngine(const Path& workingDirectory);
std::string renderTemplate(const std::string& name, TemplateSubstitutionContext* substitutionContext);
private:
TemplateFile* getTemplateFile(const std::string& name);
TemplateFile* getTemplateFile(const Path& path);
void loadTemplateFiles();
void processTemplate(TemplateFile* file, TemplateNode* parent = nullptr);
std::unordered_map<std::string, std::unique_ptr<TemplateFile> > mTemplateFiles;
Path mWorkingDirectory;
std::string mTemplateExtension{ ".html" };
};