Add raw templater

This commit is contained in:
Jan 2022-09-05 23:25:11 +02:00
parent 4ef38264c8
commit 1464329245
21 changed files with 996 additions and 7 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
local/ local/
build/ build/
.vscode
user*.* user*.*

View File

@ -100,6 +100,7 @@ include "src/Common.lua"
include "src/Crypto.lua" include "src/Crypto.lua"
include "src/Linker.lua" include "src/Linker.lua"
include "src/Parser.lua" include "src/Parser.lua"
include "src/RawTemplater.lua"
include "src/Unlinker.lua" include "src/Unlinker.lua"
include "src/Utils.lua" include "src/Utils.lua"
include "src/ZoneCode.lua" include "src/ZoneCode.lua"
@ -130,11 +131,16 @@ group "Components"
ObjWriting:project() ObjWriting:project()
group "" group ""
-- Tools group: All projects that compile into the final tools
group "BuildTools"
RawTemplater:project()
ZoneCodeGenerator:project()
group ""
-- Tools group: All projects that compile into the final tools -- Tools group: All projects that compile into the final tools
group "Tools" group "Tools"
Linker:project() Linker:project()
Unlinker:project() Unlinker:project()
ZoneCodeGenerator:project()
group "" group ""
group "Raw" group "Raw"

View File

@ -0,0 +1,14 @@
#pragma options TEST(asdf, bla)
#pragma switch TEST_SWITCH
#ifdef TEST_SWITCH
#define SVAL "1"
#else
#define SVAL "0"
#endif
#pragma filename "lemao_" + TEST + SVAL + ".txt"
HAHA TEST
#ifdef TEST_SWITCH
kekw
#endif

View File

@ -8,7 +8,7 @@ DefinesExpressionMatchers::DefinesExpressionMatchers()
} }
DefinesExpressionMatchers::DefinesExpressionMatchers(const DefinesDirectiveParsingState* state) DefinesExpressionMatchers::DefinesExpressionMatchers(const DefinesDirectiveParsingState* state)
: SimpleExpressionMatchers(false, false, true, true, false), : SimpleExpressionMatchers(true, false, true, true, false),
m_state(state) m_state(state)
{ {
} }

View File

@ -281,13 +281,13 @@ bool DefinesStreamProxy::MatchUndefDirective(const ParserLine& line, const unsig
std::unique_ptr<ISimpleExpression> DefinesStreamProxy::ParseExpression(const std::string& expressionString) const std::unique_ptr<ISimpleExpression> DefinesStreamProxy::ParseExpression(const std::string& expressionString) const
{ {
std::istringstream ss(expressionString); std::istringstream ss(expressionString);
ParserSingleInputStream inputStream(ss, "#if expression"); ParserSingleInputStream inputStream(ss, "defines directive expression");
SimpleLexer::Config lexerConfig; SimpleLexer::Config lexerConfig;
lexerConfig.m_emit_new_line_tokens = false; lexerConfig.m_emit_new_line_tokens = false;
lexerConfig.m_read_integer_numbers = true; lexerConfig.m_read_integer_numbers = true;
lexerConfig.m_read_floating_point_numbers = true; lexerConfig.m_read_floating_point_numbers = true;
lexerConfig.m_read_strings = false; lexerConfig.m_read_strings = true;
SimpleExpressionMatchers().ApplyTokensToLexerConfig(lexerConfig); SimpleExpressionMatchers().ApplyTokensToLexerConfig(lexerConfig);
SimpleLexer lexer(&inputStream, std::move(lexerConfig)); SimpleLexer lexer(&inputStream, std::move(lexerConfig));

View File

@ -77,7 +77,6 @@ private:
static void ExtractParametersFromDefineUsage(const ParserLine& line, unsigned parameterStart, unsigned& parameterEnd, std::vector<std::string>& parameterValues); static void ExtractParametersFromDefineUsage(const ParserLine& line, unsigned parameterStart, unsigned& parameterEnd, std::vector<std::string>& parameterValues);
bool FindDefineForWord(const ParserLine& line, unsigned wordStart, unsigned wordEnd, Define*& value); bool FindDefineForWord(const ParserLine& line, unsigned wordStart, unsigned wordEnd, Define*& value);
void ExpandDefines(ParserLine& line);
public: public:
explicit DefinesStreamProxy(IParserLineStream* stream); explicit DefinesStreamProxy(IParserLineStream* stream);
@ -85,6 +84,8 @@ public:
void AddDefine(Define define); void AddDefine(Define define);
void Undefine(const std::string& name); void Undefine(const std::string& name);
void ExpandDefines(ParserLine& line);
_NODISCARD std::unique_ptr<ISimpleExpression> ParseExpression(const std::string& expressionString) const; _NODISCARD std::unique_ptr<ISimpleExpression> ParseExpression(const std::string& expressionString) const;
ParserLine NextLine() override; ParserLine NextLine() override;

View File

@ -61,5 +61,5 @@ bool ParserSingleInputStream::IsOpen() const
bool ParserSingleInputStream::Eof() const bool ParserSingleInputStream::Eof() const
{ {
return !m_stream.eof(); return m_stream.eof();
} }

50
src/RawTemplater.lua Normal file
View File

@ -0,0 +1,50 @@
RawTemplater = {}
function RawTemplater:include(includes)
if includes:handle(self:name()) then
includedirs {
path.join(ProjectFolder(), "RawTemplater")
}
Utils:include(includes)
end
end
function RawTemplater:link(links)
end
function RawTemplater:use()
dependson(self:name())
end
function RawTemplater:name()
return "RawTemplater"
end
function RawTemplater:project()
local folder = ProjectFolder()
local includes = Includes:create()
local links = Links:create()
project(self:name())
targetdir(TargetDirectoryBin)
location "%{wks.location}/src/%{prj.name}"
kind "ConsoleApp"
language "C++"
files {
path.join(folder, "RawTemplater/**.h"),
path.join(folder, "RawTemplater/**.cpp")
}
vpaths {
["*"] = {
path.join(folder, "RawTemplater"),
}
}
self:include(includes)
Parser:include(includes)
links:linkto(Parser)
links:linkall()
end

View File

@ -0,0 +1,68 @@
#include "RawTemplater.h"
#include <fstream>
#include <iostream>
#include <filesystem>
#include "RawTemplaterArguments.h"
#include "Templating/Templater.h"
namespace fs = std::filesystem;
class RawTemplater::Impl
{
RawTemplaterArguments m_args;
_NODISCARD bool GenerateCode(const std::string& filename) const
{
std::ifstream file(filename);
if (!file.is_open())
{
std::cerr << "Failed to open file \"" << filename << "\"\n";
return false;
}
templating::Templater templater(file, filename);
if (!m_args.m_output_directory.empty())
return templater.TemplateToDirectory(m_args.m_output_directory);
const fs::path filePath(filename);
const auto parentPath = filePath.parent_path();
return templater.TemplateToDirectory(parentPath.string());
}
public:
Impl()
= default;
int Run(const int argc, const char** argv)
{
if (!m_args.Parse(argc, argv))
return 1;
for(const auto& inputFile : m_args.m_input_files)
{
if (!GenerateCode(inputFile))
return 1;
}
return 0;
}
};
RawTemplater::RawTemplater()
{
m_impl = new Impl();
}
RawTemplater::~RawTemplater()
{
delete m_impl;
m_impl = nullptr;
}
int RawTemplater::Run(const int argc, const char** argv) const
{
return m_impl->Run(argc, argv);
}

View File

@ -0,0 +1,17 @@
#pragma once
class RawTemplater
{
class Impl;
Impl* m_impl;
public:
RawTemplater();
~RawTemplater();
RawTemplater(const RawTemplater& other) = delete;
RawTemplater(RawTemplater&& other) noexcept = default;
RawTemplater& operator=(const RawTemplater& other) = delete;
RawTemplater& operator=(RawTemplater&& other) noexcept = default;
int Run(int argc, const char** argv) const;
};

View File

@ -0,0 +1,105 @@
#include "RawTemplaterArguments.h"
#include "Utils/Arguments/CommandLineOption.h"
#include "Utils/Arguments/UsageInformation.h"
const CommandLineOption* const OPTION_HELP = CommandLineOption::Builder::Create()
.WithShortName("?")
.WithLongName("help")
.WithDescription("Displays usage information.")
.Build();
const CommandLineOption* const OPTION_VERBOSE = CommandLineOption::Builder::Create()
.WithShortName("v")
.WithLongName("verbose")
.WithDescription("Outputs a lot more and more detailed messages.")
.Build();
const CommandLineOption* const OPTION_OUTPUT_FOLDER = CommandLineOption::Builder::Create()
.WithShortName("o")
.WithLongName("output")
.WithDescription("Specify the folder to save the generated files. Defaults to the current directory.")
.WithParameter("outputPath")
.Build();
const CommandLineOption* const OPTION_DEFINE = CommandLineOption::Builder::Create()
.WithShortName("d")
.WithLongName("define")
.WithDescription("Adds a define for the templating process. Can be of format define or define=value.")
.WithParameter("defineValue")
.Reusable()
.Build();
const CommandLineOption* const COMMAND_LINE_OPTIONS[]
{
OPTION_HELP,
OPTION_VERBOSE,
OPTION_OUTPUT_FOLDER,
OPTION_DEFINE
};
RawTemplaterArguments::RawTemplaterArguments()
: m_argument_parser(COMMAND_LINE_OPTIONS, std::extent_v<decltype(COMMAND_LINE_OPTIONS)>),
m_verbose(false)
{
}
void RawTemplaterArguments::PrintUsage()
{
UsageInformation usage("RawTemplater.exe");
for (const auto* commandLineOption : COMMAND_LINE_OPTIONS)
{
usage.AddCommandLineOption(commandLineOption);
}
usage.Print();
}
bool RawTemplaterArguments::Parse(const int argc, const char** argv)
{
if (!m_argument_parser.ParseArguments(argc - 1, &argv[1]))
{
PrintUsage();
return false;
}
// Check if the user requested help
if (m_argument_parser.IsOptionSpecified(OPTION_HELP))
{
PrintUsage();
return false;
}
m_input_files = m_argument_parser.GetArguments();
if (m_input_files.empty())
{
PrintUsage();
return false;
}
// -v; --verbose
m_verbose = m_argument_parser.IsOptionSpecified(OPTION_VERBOSE);
// -o; --output
if (m_argument_parser.IsOptionSpecified(OPTION_OUTPUT_FOLDER))
m_output_directory = m_argument_parser.GetValueForOption(OPTION_OUTPUT_FOLDER);
else
m_output_directory = ".";
// -d; --define
if (m_argument_parser.IsOptionSpecified(OPTION_DEFINE))
{
for (const auto& arg : m_argument_parser.GetParametersForOption(OPTION_DEFINE))
{
const auto separator = arg.find('=');
if (separator != std::string::npos)
m_defines.emplace_back(std::make_pair(arg.substr(0, separator), arg.substr(separator + 1)));
else
m_defines.emplace_back(std::make_pair(arg, std::string()));
}
}
return true;
}

View File

@ -0,0 +1,29 @@
#pragma once
#include <string>
#include <utility>
#include <vector>
#include "Utils/Arguments/ArgumentParser.h"
class RawTemplaterArguments
{
ArgumentParser m_argument_parser;
/**
* \brief Prints a command line usage help text for the RawTemplater tool to stdout.
*/
static void PrintUsage();
public:
bool m_verbose;
std::vector<std::string> m_input_files;
std::string m_output_directory;
std::vector<std::pair<std::string, std::string>> m_defines;
RawTemplaterArguments();
bool Parse(int argc, const char** argv);
};

View File

@ -0,0 +1,38 @@
#include "DirectiveEscapeStreamProxy.h"
using namespace templating;
DirectiveEscapeStreamProxy::DirectiveEscapeStreamProxy(IParserLineStream* stream)
: m_stream(stream)
{
}
ParserLine DirectiveEscapeStreamProxy::NextLine()
{
auto line = m_stream->NextLine();
if (line.m_line.size() >= 2 && line.m_line[0] == '#' && line.m_line[1] == '#')
line.m_line = line.m_line.substr(1);
return line;
}
bool DirectiveEscapeStreamProxy::IncludeFile(const std::string& filename)
{
return m_stream->IncludeFile(filename);
}
void DirectiveEscapeStreamProxy::PopCurrentFile()
{
m_stream->PopCurrentFile();
}
bool DirectiveEscapeStreamProxy::IsOpen() const
{
return m_stream->IsOpen();
}
bool DirectiveEscapeStreamProxy::Eof() const
{
return m_stream->Eof();
}

View File

@ -0,0 +1,20 @@
#pragma once
#include "Parsing/Impl/AbstractDirectiveStreamProxy.h"
namespace templating
{
class DirectiveEscapeStreamProxy final : public AbstractDirectiveStreamProxy
{
public:
explicit DirectiveEscapeStreamProxy(IParserLineStream* stream);
ParserLine NextLine() override;
bool IncludeFile(const std::string& filename) override;
void PopCurrentFile() override;
_NODISCARD bool IsOpen() const override;
_NODISCARD bool Eof() const override;
private:
IParserLineStream* const m_stream;
};
}

View File

@ -0,0 +1,354 @@
#include "Templater.h"
#include <unordered_map>
#include <vector>
#include <filesystem>
#include <fstream>
#include <iostream>
#include "Utils/ClassUtils.h"
#include "DirectiveEscapeStreamProxy.h"
#include "TemplatingStreamProxy.h"
#include "Parsing/ParsingException.h"
#include "Parsing/Impl/DefinesStreamProxy.h"
#include "Parsing/Impl/ParserSingleInputStream.h"
using namespace templating;
namespace fs = std::filesystem;
namespace templating
{
class TemplatingPass
{
public:
TemplatingPass()
: m_stream(nullptr)
{
}
TemplatingPass(std::istream& stream, const std::string& fileName, ITemplaterControl* templaterControl)
{
m_base_stream = std::make_unique<ParserSingleInputStream>(stream, fileName);
m_templating_proxy = std::make_unique<TemplatingStreamProxy>(m_base_stream.get(), templaterControl);
m_defines_proxy = std::make_unique<DefinesStreamProxy>(m_templating_proxy.get());
m_directive_escape_proxy = std::make_unique<DirectiveEscapeStreamProxy>(m_defines_proxy.get());
m_templating_proxy->SetDefinesProxy(m_defines_proxy.get());
m_stream = m_directive_escape_proxy.get();
}
std::unique_ptr<IParserLineStream> m_base_stream;
std::unique_ptr<TemplatingStreamProxy> m_templating_proxy;
std::unique_ptr<DefinesStreamProxy> m_defines_proxy;
std::unique_ptr<DirectiveEscapeStreamProxy> m_directive_escape_proxy;
IParserLineStream* m_stream;
};
enum class TemplatingVariationType
{
OPTIONS,
SWITCH
};
class ITemplatingVariation
{
protected:
ITemplatingVariation() = default;
public:
virtual ~ITemplatingVariation() = default;
ITemplatingVariation(const ITemplatingVariation& other) = default;
ITemplatingVariation(ITemplatingVariation&& other) noexcept = default;
ITemplatingVariation& operator=(const ITemplatingVariation& other) = default;
ITemplatingVariation& operator=(ITemplatingVariation&& other) noexcept = default;
virtual const std::string& GetName() = 0;
virtual void Advance() = 0;
virtual void Apply(DefinesStreamProxy* definesProxy) = 0;
_NODISCARD virtual bool IsFinished() const = 0;
_NODISCARD virtual TemplatingVariationType GetVariationType() const = 0;
};
class SwitchVariation final : public ITemplatingVariation
{
public:
explicit SwitchVariation(std::string name)
: m_name(std::move(name)),
m_should_define(true),
m_finished(false)
{
}
const std::string& GetName() override
{
return m_name;
}
void Advance() override
{
if (!m_should_define)
m_finished = true;
else
m_should_define = false;
}
void Apply(DefinesStreamProxy* definesProxy) override
{
if (m_should_define)
definesProxy->AddDefine(DefinesStreamProxy::Define(m_name, "1"));
}
_NODISCARD bool IsFinished() const override
{
return m_finished;
}
_NODISCARD TemplatingVariationType GetVariationType() const override
{
return TemplatingVariationType::SWITCH;
}
std::string m_name;
bool m_should_define;
bool m_finished;
};
class OptionsVariation final : public ITemplatingVariation
{
public:
OptionsVariation(std::string name, std::vector<std::string> values)
: m_name(std::move(name)),
m_values(std::move(values)),
m_value_offset(0u)
{
}
const std::string& GetName() override
{
return m_name;
}
void Advance() override
{
m_value_offset++;
}
void Apply(DefinesStreamProxy* definesProxy) override
{
if (m_value_offset < m_values.size())
definesProxy->AddDefine(DefinesStreamProxy::Define(m_name, m_values[m_value_offset]));
}
_NODISCARD bool IsFinished() const override
{
return m_value_offset >= m_values.size();
}
_NODISCARD TemplatingVariationType GetVariationType() const override
{
return TemplatingVariationType::OPTIONS;
}
std::string m_name;
std::vector<std::string> m_values;
size_t m_value_offset;
};
class TemplaterControlImpl final : ITemplaterControl
{
public:
TemplaterControlImpl(std::istream& stream, std::string filename, const std::string& outputDirectory)
: m_stream(stream),
m_filename(std::move(filename)),
m_output_directory(outputDirectory),
m_first_line(true),
m_write_output_to_file(false)
{
fs::path filenamePath(m_filename);
m_default_output_file = (m_output_directory / filenamePath.replace_extension()).string();
}
bool RunNextPass()
{
m_stream.clear();
m_stream.seekg(0, std::ios::beg);
m_output_file = m_default_output_file;
m_current_pass = TemplatingPass(m_stream, m_filename, this);
for (const auto& activeVariation : m_active_variations)
activeVariation->Apply(m_current_pass.m_defines_proxy.get());
while (!m_current_pass.m_stream->Eof())
{
auto nextLine = m_current_pass.m_stream->NextLine();
if (m_write_output_to_file)
{
if (m_first_line)
m_first_line = false;
else
m_output_stream << '\n';
m_output_stream << nextLine.m_line;
}
else
{
if (m_first_line)
m_first_line = false;
else
m_output_cache << '\n';
m_output_cache << nextLine.m_line;
}
}
if (!m_write_output_to_file)
{
m_output_stream = std::ofstream(m_output_file);
if (!m_output_stream.is_open())
{
std::cerr << "Failed to open output file \"" << m_output_file << "\"\n";
return false;
}
const auto cachedData = m_output_cache.str();
if (!cachedData.empty())
m_output_stream << cachedData;
}
m_first_line = true;
m_write_output_to_file = false;
m_output_cache.clear();
m_output_cache.str(std::string());
m_output_stream.close();
return true;
}
void AdvanceActiveVariations()
{
while (!m_active_variations.empty())
{
const auto& lastVariation = m_active_variations[m_active_variations.size() - 1];
lastVariation->Advance();
if (lastVariation->IsFinished())
{
m_active_variations_by_name.erase(lastVariation->GetName());
m_active_variations.pop_back();
}
else
break;
}
}
_NODISCARD bool HasActiveVariations() const
{
return !m_active_variations.empty();
}
protected:
bool AddSwitch(std::string switchName) override
{
const auto existingVariation = m_active_variations_by_name.find(switchName);
if (existingVariation != m_active_variations_by_name.end())
return existingVariation->second->GetVariationType() == TemplatingVariationType::SWITCH;
auto switchVariation = std::make_unique<SwitchVariation>(std::move(switchName));
if (m_current_pass.m_defines_proxy)
switchVariation->Apply(m_current_pass.m_defines_proxy.get());
m_active_variations_by_name.emplace(switchVariation->m_name, switchVariation.get());
m_active_variations.emplace_back(std::move(switchVariation));
return true;
}
bool AddOptions(std::string optionsName, std::vector<std::string> optionValues) override
{
const auto existingVariation = m_active_variations_by_name.find(optionsName);
if (existingVariation != m_active_variations_by_name.end())
return existingVariation->second->GetVariationType() == TemplatingVariationType::SWITCH;
auto optionsVariation = std::make_unique<OptionsVariation>(std::move(optionsName), std::move(optionValues));
if (m_current_pass.m_defines_proxy)
optionsVariation->Apply(m_current_pass.m_defines_proxy.get());
m_active_variations_by_name.emplace(optionsVariation->m_name, optionsVariation.get());
m_active_variations.emplace_back(std::move(optionsVariation));
return true;
}
bool SetFileName(const std::string& fileName) override
{
if (m_write_output_to_file)
return false;
m_output_file = fileName;
m_output_stream = std::ofstream(m_output_file);
if (!m_output_stream.is_open())
{
std::cerr << "Failed to open output file \"" << m_output_file << "\"\n";
return false;
}
m_write_output_to_file = true;
const auto cachedData = m_output_cache.str();
if (!cachedData.empty())
m_output_stream << cachedData;
m_output_cache.clear();
m_output_cache.str(std::string());
return true;
}
private:
std::vector<std::unique_ptr<ITemplatingVariation>> m_active_variations;
std::unordered_map<std::string, ITemplatingVariation*> m_active_variations_by_name;
TemplatingPass m_current_pass;
std::istream& m_stream;
std::string m_filename;
std::string m_output_file;
std::string m_default_output_file;
const fs::path m_output_directory;
bool m_first_line;
bool m_write_output_to_file;
std::ofstream m_output_stream;
std::ostringstream m_output_cache;
};
}
Templater::Templater(std::istream& stream, std::string fileName)
: m_stream(stream),
m_file_name(std::move(fileName))
{
}
bool Templater::TemplateToDirectory(const std::string& outputDirectory)
{
TemplaterControlImpl control(m_stream, m_file_name, outputDirectory);
try
{
if (!control.RunNextPass())
return false;
control.AdvanceActiveVariations();
while (control.HasActiveVariations())
{
if (!control.RunNextPass())
return false;
control.AdvanceActiveVariations();
}
}
catch (ParsingException& e)
{
std::cerr << "Error: " << e.FullMessage() << std::endl;
return false;
}
return true;
}

View File

@ -0,0 +1,19 @@
#pragma once
#include <string>
#include "Parsing/IParserLineStream.h"
namespace templating
{
class Templater
{
public:
Templater(std::istream& stream, std::string fileName);
bool TemplateToDirectory(const std::string& outputDirectory);
private:
std::istream& m_stream;
std::string m_file_name;
};
}

View File

@ -0,0 +1,196 @@
#include "TemplatingStreamProxy.h"
#include <iostream>
#include "Parsing/ParsingException.h"
using namespace templating;
TemplatingStreamProxy::TemplatingStreamProxy(IParserLineStream* stream, ITemplaterControl* templaterControl)
: m_stream(stream),
m_templater_control(templaterControl),
m_defines_proxy(nullptr)
{
}
void TemplatingStreamProxy::SetDefinesProxy(DefinesStreamProxy* definesProxy)
{
m_defines_proxy = definesProxy;
}
bool TemplatingStreamProxy::MatchSwitchDirective(const ParserLine& line, const unsigned directiveStartPosition, const unsigned directiveEndPosition) const
{
auto currentPosition = directiveStartPosition;
if (directiveEndPosition - directiveStartPosition != std::char_traits<char>::length(PRAGMA_DIRECTIVE)
|| !MatchString(line, currentPosition, PRAGMA_DIRECTIVE, std::char_traits<char>::length(PRAGMA_DIRECTIVE)))
{
return false;
}
if (!SkipWhitespace(line, currentPosition))
return false;
if (!MatchString(line, currentPosition, SWITCH_PRAGMA_COMMAND, std::char_traits<char>::length(SWITCH_PRAGMA_COMMAND)))
return false;
if (!SkipWhitespace(line, currentPosition))
throw ParsingException(CreatePos(line, currentPosition), "Invalid switch directive.");
const auto nameStartPosition = currentPosition;
if (!ExtractIdentifier(line, currentPosition))
throw ParsingException(CreatePos(line, currentPosition), "Invalid switch directive.");
auto name = line.m_line.substr(nameStartPosition, currentPosition - nameStartPosition);
std::cout << "Switch: \"" << name << "\"\n";
m_templater_control->AddSwitch(std::move(name));
return true;
}
bool TemplatingStreamProxy::MatchOptionsDirective(const ParserLine& line, const unsigned directiveStartPosition, const unsigned directiveEndPosition) const
{
auto currentPosition = directiveStartPosition;
if (directiveEndPosition - directiveStartPosition != std::char_traits<char>::length(PRAGMA_DIRECTIVE)
|| !MatchString(line, currentPosition, PRAGMA_DIRECTIVE, std::char_traits<char>::length(PRAGMA_DIRECTIVE)))
{
return false;
}
if (!SkipWhitespace(line, currentPosition))
return false;
if (!MatchString(line, currentPosition, OPTIONS_PRAGMA_COMMAND, std::char_traits<char>::length(OPTIONS_PRAGMA_COMMAND)))
return false;
if (!SkipWhitespace(line, currentPosition))
throw ParsingException(CreatePos(line, currentPosition), "Invalid options directive.");
const auto nameStartPosition = currentPosition;
if (!ExtractIdentifier(line, currentPosition))
throw ParsingException(CreatePos(line, currentPosition), "Invalid options directive.");
auto name = line.m_line.substr(nameStartPosition, currentPosition - nameStartPosition);
if (!MatchNextCharacter(line, currentPosition, '('))
throw ParsingException(CreatePos(line, currentPosition), "Invalid options directive.");
std::vector<std::string> options;
if (!SkipWhitespace(line, currentPosition))
throw ParsingException(CreatePos(line, currentPosition), "Invalid options directive.");
bool firstArg = true;
while (!MatchNextCharacter(line, currentPosition, ')'))
{
if (!firstArg && !MatchNextCharacter(line, currentPosition, ','))
throw ParsingException(CreatePos(line, currentPosition), "Invalid options directive.");
if (!SkipWhitespace(line, currentPosition))
throw ParsingException(CreatePos(line, currentPosition), "Invalid options directive.");
const auto optionStartPosition = currentPosition;
if (!ExtractIdentifier(line, currentPosition))
throw ParsingException(CreatePos(line, currentPosition), "Invalid options directive.");
std::ostringstream optionValueBuilder;
optionValueBuilder << '"' << line.m_line.substr(optionStartPosition, currentPosition - optionStartPosition) << '"';
options.emplace_back(optionValueBuilder.str());
firstArg = false;
}
std::cout << "Options: \"" << name << "\" with values:\n";
for (const auto& option : options)
std::cout << " Value: " << option << "\n";
m_templater_control->AddOptions(std::move(name), std::move(options));
return true;
}
bool TemplatingStreamProxy::MatchFilenameDirective(const ParserLine& line, const unsigned directiveStartPosition, const unsigned directiveEndPosition) const
{
auto currentPosition = directiveStartPosition;
if (directiveEndPosition - directiveStartPosition != std::char_traits<char>::length(PRAGMA_DIRECTIVE)
|| !MatchString(line, currentPosition, PRAGMA_DIRECTIVE, std::char_traits<char>::length(PRAGMA_DIRECTIVE)))
{
return false;
}
if (!SkipWhitespace(line, currentPosition))
return false;
if (!MatchString(line, currentPosition, FILENAME_PRAGMA_COMMAND, std::char_traits<char>::length(FILENAME_PRAGMA_COMMAND)))
return false;
if (!SkipWhitespace(line, currentPosition))
throw ParsingException(CreatePos(line, currentPosition), "Invalid options directive.");
const auto expressionString = line.m_line.substr(currentPosition, line.m_line.size() - currentPosition);
if (expressionString.empty())
throw ParsingException(CreatePos(line, currentPosition), "Cannot pragma filename without an expression.");
ParserLine expressionStringAsLine(line.m_filename, line.m_line_number, expressionString);
m_defines_proxy->ExpandDefines(expressionStringAsLine);
const auto expression = m_defines_proxy->ParseExpression(expressionStringAsLine.m_line);
if (!expression)
throw ParsingException(CreatePos(line, currentPosition), "Failed to parse pragma filename expression");
if (!expression->IsStatic())
throw ParsingException(CreatePos(line, currentPosition), "pragma filename expression must be static");
const auto value = expression->EvaluateStatic();
if (value.m_type != SimpleExpressionValue::Type::STRING)
throw ParsingException(CreatePos(line, currentPosition), "pragma filename expression must evaluate to string");
std::cout << "Filename: \"" << *value.m_string_value << "\"\n";
m_templater_control->SetFileName(*value.m_string_value);
return true;
}
bool TemplatingStreamProxy::MatchDirectives(const ParserLine& line)
{
unsigned directiveStartPos, directiveEndPos;
if (!FindDirective(line, directiveStartPos, directiveEndPos))
return false;
directiveStartPos++;
return MatchSwitchDirective(line, directiveStartPos, directiveEndPos)
|| MatchOptionsDirective(line, directiveStartPos, directiveEndPos)
|| MatchFilenameDirective(line, directiveStartPos, directiveEndPos);
}
ParserLine TemplatingStreamProxy::NextLine()
{
auto line = m_stream->NextLine();
while (MatchDirectives(line))
line = m_stream->NextLine();
return line;
}
bool TemplatingStreamProxy::IncludeFile(const std::string& filename)
{
return m_stream->IncludeFile(filename);
}
void TemplatingStreamProxy::PopCurrentFile()
{
m_stream->PopCurrentFile();
}
bool TemplatingStreamProxy::IsOpen() const
{
return m_stream->IsOpen();
}
bool TemplatingStreamProxy::Eof() const
{
return m_stream->Eof();
}

View File

@ -0,0 +1,54 @@
#pragma once
#include "Utils/ClassUtils.h"
#include "Parsing/Impl/AbstractDirectiveStreamProxy.h"
#include "Parsing/Impl/DefinesStreamProxy.h"
namespace templating
{
class ITemplaterControl
{
protected:
ITemplaterControl() = default;
public:
virtual ~ITemplaterControl() = default;
ITemplaterControl(const ITemplaterControl& other) = default;
ITemplaterControl(ITemplaterControl&& other) noexcept = default;
ITemplaterControl& operator=(const ITemplaterControl& other) = default;
ITemplaterControl& operator=(ITemplaterControl&& other) noexcept = default;
virtual bool AddSwitch(std::string switchName) = 0;
virtual bool AddOptions(std::string optionsName, std::vector<std::string> optionValues) = 0;
virtual bool SetFileName(const std::string& fileName) = 0;
};
class TemplatingStreamProxy final : public AbstractDirectiveStreamProxy
{
public:
TemplatingStreamProxy(IParserLineStream* stream, ITemplaterControl* templaterControl);
void SetDefinesProxy(DefinesStreamProxy* definesProxy);
ParserLine NextLine() override;
bool IncludeFile(const std::string& filename) override;
void PopCurrentFile() override;
_NODISCARD bool IsOpen() const override;
_NODISCARD bool Eof() const override;
private:
static constexpr const char* PRAGMA_DIRECTIVE = "pragma";
static constexpr const char* SWITCH_PRAGMA_COMMAND = "switch";
static constexpr const char* OPTIONS_PRAGMA_COMMAND = "options";
static constexpr const char* FILENAME_PRAGMA_COMMAND = "filename";
_NODISCARD bool MatchSwitchDirective(const ParserLine& line, unsigned directiveStartPosition, unsigned directiveEndPosition) const;
_NODISCARD bool MatchOptionsDirective(const ParserLine& line, unsigned directiveStartPosition, unsigned directiveEndPosition) const;
_NODISCARD bool MatchFilenameDirective(const ParserLine& line, unsigned directiveStartPosition, unsigned directiveEndPosition) const;
bool MatchDirectives(const ParserLine& line);
IParserLineStream* const m_stream;
ITemplaterControl* const m_templater_control;
DefinesStreamProxy* m_defines_proxy;
};
}

View File

@ -0,0 +1,7 @@
#include "RawTemplater.h"
int main(const int argc, const char** argv)
{
const RawTemplater rawTemplater;
return rawTemplater.Run(argc, argv);
}

View File

@ -39,7 +39,7 @@ function Raw:project()
} }
} }
filter "files:**" filter "files:not **/*.template"
buildmessage 'Copying rawfile %{file.relpath}' buildmessage 'Copying rawfile %{file.relpath}'
buildcommands { buildcommands {
-- Relpath contains two .. so build/raw is getting reverted in the target path -- Relpath contains two .. so build/raw is getting reverted in the target path
@ -50,4 +50,14 @@ function Raw:project()
"%{cfg.targetdir}/build/raw/%{file.relpath}" "%{cfg.targetdir}/build/raw/%{file.relpath}"
} }
filter {} filter {}
filter "files:**/*.template"
buildmessage 'Templating %{file.relpath}'
buildcommands {
"echo \"%{cfg.targetdir}/build/raw/%{file.reldirectory}\""
}
buildoutputs {
"%{cfg.targetdir}/build/raw/%{file.relpath}"
}
filter {}
end end