Toward template rendering.

This commit is contained in:
jmsgrogan 2022-10-20 09:00:39 +01:00
parent eb3b394bdf
commit 350c20efa6
9 changed files with 542 additions and 385 deletions

View file

@ -1,11 +1,15 @@
list(APPEND compiler_HEADERS list(APPEND compiler_HEADERS
Lexer.h Lexer.h
TemplatingEngine.h TemplatingEngine.h
TemplateFile.h
TemplateNodes.h
) )
list(APPEND compiler_LIB_INCLUDES list(APPEND compiler_LIB_INCLUDES
Lexer.cpp Lexer.cpp
TemplatingEngine.cpp TemplatingEngine.cpp
TemplateFile.cpp
TemplateNodes.cpp
) )
add_library(compiler SHARED ${compiler_LIB_INCLUDES} ${compiler_HEADERS}) add_library(compiler SHARED ${compiler_LIB_INCLUDES} ${compiler_HEADERS})

View file

@ -0,0 +1,187 @@
#include "TemplateFile.h"
#include <iostream>
TemplateFile::TemplateFile(const Path& path)
: mPath(path),
mRootNode(std::make_unique<TemplateNode>(nullptr))
{
}
std::vector<std::string> 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<TemplateTextBody>(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<TemplateTextBody>(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<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));
}
void TemplateFile::onFoundExpression(const std::string& expression_string)
{
auto expression = std::make_unique<TemplateExpression>(mWorkingNode, expression_string);
mWorkingNode->addChild(std::move(expression));
}
void TemplateFile::dumpContent()
{
auto content = mRootNode->getRawContent();
std::cout << content << std::endl;
}

View file

@ -0,0 +1,50 @@
#pragma once
#include "File.h"
#include "TemplateNodes.h"
#include <vector>
class TemplateFile
{
public:
TemplateFile(const Path& path);
std::vector<std::string> 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<std::string> args);
void onFoundEndBlock(const std::vector<std::string> args);
void onFoundExtends(const std::vector<std::string> 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<std::string> mRawContent;
std::vector<std::string> mProcessedContent;
std::unique_ptr<TemplateNode> mRootNode;
TemplateNode* mWorkingNode{ nullptr };
std::unique_ptr<TemplateTextBody> mWorkingTextBody;
};

View file

View file

@ -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<TemplateNode> 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<typename T>
T* getFirstChildShallow() const
{
for (const auto& child : mChildren)
{
if (auto ret = dynamic_cast<T*>(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<std::unique_ptr<TemplateNode> > mChildren;
TemplateNode* mParent{ nullptr };
TemplateNode* mExtensionParent{ nullptr };
};
using TemplateNodePtr = std::unique_ptr<TemplateNode>;
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<std::string> 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<std::string> mContent;
};

View file

@ -0,0 +1,82 @@
#include "TemplatingEngine.h"
#include "Directory.h"
#include <iostream>
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<TemplateFile>(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<TemplateExtends>())
{
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();
}

View file

@ -1,399 +1,28 @@
#pragma once #pragma once
#include "File.h" #include "File.h"
#include "Directory.h" #include "TemplateFile.h"
#include "StringUtils.h"
#include <vector> #include <vector>
#include <string> #include <string>
#include <sstream>
#include <iostream>
class TemplateNode
{
public:
TemplateNode(TemplateNode* parent)
: mParent(parent)
{
}
TemplateNode* getParent() const
{
return mParent;
}
virtual void addChild(std::unique_ptr<TemplateNode> 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<std::unique_ptr<TemplateNode> > mChildren;
TemplateNode* mParent{ nullptr };
};
using TemplateNodePtr = std::unique_ptr<TemplateNode>;
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<std::string> 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<std::string> mContent;
};
class TemplateFile
{
public:
TemplateFile(const Path& path)
: mPath(path),
mRootNode(std::make_unique<TemplateNode>(nullptr))
{
}
std::vector<std::string> 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<TemplateTextBody>(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<TemplateTextBody>(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<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 onFoundEndBlock(const std::vector<std::string> args)
{
mWorkingNode = mWorkingNode->getParent();
}
void 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));
}
void onFoundExpression(const std::string& expression_string)
{
auto expression = std::make_unique<TemplateExpression>(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<std::string> mRawContent;
std::vector<std::string> mProcessedContent;
std::unique_ptr<TemplateNode> mRootNode;
TemplateNode* mWorkingNode{ nullptr };
std::unique_ptr<TemplateTextBody> mWorkingTextBody;
};
class TemplatingEngine class TemplatingEngine
{ {
public: public:
TemplatingEngine(const Path& workingDirectory) TemplatingEngine(const Path& workingDirectory);
: mWorkingDirectory(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: private:
std::vector<TemplateFile> 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<std::unique_ptr<TemplateFile> > mTemplateFiles;
Path mWorkingDirectory; Path mWorkingDirectory;
std::string mTemplateExtension{ ".html" }; std::string mTemplateExtension{ ".html" };
}; };

View file

@ -76,7 +76,20 @@ FileFormat::Format File::InferFormat() const
void File::WriteText(const std::string& text) 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; (*mOutHandle) << text;
if (had_to_open)
{
Close();
}
} }
std::vector<std::string> File::readLines() std::vector<std::string> File::readLines()

View file

@ -1,5 +1,7 @@
#include "TemplatingEngine.h" #include "TemplatingEngine.h"
#include <File.h>
#include <filesystem> #include <filesystem>
#include <iostream> #include <iostream>
@ -9,8 +11,8 @@ int main()
auto engine = TemplatingEngine(data_loc); auto engine = TemplatingEngine(data_loc);
engine.loadTemplateFiles(); engine.loadTemplateFiles();
engine.processTemplate("index"); const auto content = engine.processTemplate("index");
engine.processTemplate("base");
File outfile("index.html");
outfile.WriteText(content);
} }