BLT/include/blt/parse/templating.h

563 lines
20 KiB
C
Raw Normal View History

2024-05-09 13:51:25 -04:00
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_TEMPLATING_H
#define BLT_TEMPLATING_H
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include <utility>
#include <blt/std/hashmap.h>
#include <blt/std/types.h>
#include <blt/std/expected.h>
2024-05-10 01:53:50 -04:00
#include <blt/std/logging.h>
2024-05-09 13:51:25 -04:00
#include <variant>
namespace blt
{
2024-05-09 21:53:08 -04:00
2024-05-10 12:56:48 -04:00
class template_engine_t;
2024-05-09 13:51:25 -04:00
template<typename Storage, typename Consumable>
class template_consumer_base_t
{
public:
explicit template_consumer_base_t(Storage storage): storage(std::move(storage))
{}
[[nodiscard]] Consumable next(size_t offset = 0) const
{
return storage[current_index + offset];
}
void advance(size_t offset = 1)
{
current_index += offset;
}
[[nodiscard]] bool hasNext(size_t offset = 1) const
{
return (current_index + (offset - 1)) < storage.size();
}
[[nodiscard]] Consumable consume()
{
Consumable c = next();
advance();
return c;
}
[[nodiscard]] size_t getCurrentIndex() const
{
return current_index;
}
[[nodiscard]] size_t getPreviousIndex() const
{
return current_index - 1;
}
protected:
size_t current_index = 0;
Storage storage;
};
enum class template_token_t
{
//STRING, // A string of characters not $ { or }
IDENT, // $
2024-05-09 21:53:08 -04:00
ADD, // +
2024-05-09 13:51:25 -04:00
CURLY_OPEN, // {
CURLY_CLOSE, // }
IF, // IF
ELSE, // ELSE
PAR_OPEN, // (
PAR_CLOSE, // )
OR, // ||
AND, // &&
XOR, // ^
NOT, // !
QUOTE, // "
SEMI, // ;
COMMA, // ,
PERIOD, // .
FUNCTION, // ~
STRING // variable name
};
namespace detail
{
inline const blt::hashmap_t<std::string_view, template_token_t> identifiers = {
{"IF", template_token_t::IF},
{"ELSE", template_token_t::ELSE}
};
}
inline std::string template_token_to_string(template_token_t token)
{
switch (token)
{
case template_token_t::IDENT:
return "[Template Identifier]";
case template_token_t::CURLY_OPEN:
return "[Curly Open]";
case template_token_t::CURLY_CLOSE:
return "[Curly Close]";
case template_token_t::IF:
return "[IF]";
case template_token_t::ELSE:
return "[ELSE]";
case template_token_t::PAR_OPEN:
return "[Par Open]";
case template_token_t::PAR_CLOSE:
return "[Par Close]";
case template_token_t::OR:
return "[OR]";
case template_token_t::AND:
return "[AND]";
case template_token_t::XOR:
return "[XOR]";
case template_token_t::NOT:
return "[NOT]";
case template_token_t::QUOTE:
return "[QUOTE]";
case template_token_t::FUNCTION:
return "[FUNC]";
case template_token_t::STRING:
return "[STR]";
case template_token_t::SEMI:
return "[SEMI]";
case template_token_t::COMMA:
return "[COMMA]";
case template_token_t::PERIOD:
return "[PERIOD]";
2024-05-09 21:53:08 -04:00
case template_token_t::ADD:
return "[ADD]";
2024-05-09 13:51:25 -04:00
}
}
enum class template_tokenizer_failure_t
{
MISMATCHED_CURLY,
MISMATCHED_PAREN,
MISMATCHED_QUOTE,
};
enum class template_parser_failure_t
{
2024-05-09 21:53:08 -04:00
SUBSTITUTION_NOT_FOUND,
2024-05-09 13:51:25 -04:00
TOKENIZER_FAILURE,
2024-05-09 21:53:08 -04:00
NO_MATCHING_CURLY,
MISSING_IDENT_BRACES,
FUNCTION_EXPECTED_STRING,
FUNCTION_NOT_FOUND,
FUNCTION_DISCARD,
STRING_EXPECTED_CONCAT,
IF_EXPECTED_PAREN,
BOOL_EXPECTED_PAREN,
2024-05-10 12:56:48 -04:00
BOOL_TYPE_NOT_FOUND,
2024-05-09 21:53:08 -04:00
UNKNOWN_STATEMENT_ERROR,
UNKNOWN_ERROR
2024-05-09 13:51:25 -04:00
};
struct template_token_data_t
{
template_token_t type;
size_t level;
std::string_view token;
size_t paren_level = 0;
template_token_data_t(template_token_t type, size_t level, const std::string_view& token): type(type), level(level), token(token)
{}
template_token_data_t(template_token_t type, size_t level, const std::string_view& token, size_t parenLevel):
type(type), level(level), token(token), paren_level(parenLevel)
{}
};
class template_char_consumer_t : public template_consumer_base_t<std::string_view, char>
{
public:
explicit template_char_consumer_t(std::string_view statement): template_consumer_base_t(statement)
{}
[[nodiscard]] std::string_view from(size_t begin, size_t end)
{
return std::string_view{&storage[begin], end - begin};
}
};
class template_token_consumer_t : public template_consumer_base_t<std::vector<template_token_data_t>, template_token_data_t>
{
public:
2024-05-10 12:56:48 -04:00
explicit template_token_consumer_t(const std::vector<template_token_data_t>& statement, std::string_view raw_string):
template_consumer_base_t(statement), raw_string(raw_string)
2024-05-09 13:51:25 -04:00
{}
2024-05-10 12:56:48 -04:00
void set_marker()
2024-05-09 13:51:25 -04:00
{
2024-05-10 12:56:48 -04:00
// when setting the marker, we need to go from the last closing brace
auto index = storage.begin() + getCurrentIndex();
while (index->type != template_token_t::CURLY_CLOSE)
index--;
last_read_index = ((&index->token.front() + index->token.size()) - &raw_string[last_read_index]);
}
std::string_view from_last()
{
if (!hasNext())
return std::string_view(&raw_string[last_read_index], raw_string.size() - last_read_index);
auto token = storage[getCurrentIndex()];
auto len = ((&token.token.back()) - &raw_string[last_read_index]);
2024-05-09 21:53:49 -04:00
auto str = std::string_view(&raw_string[last_read_index], len);
2024-05-09 13:51:25 -04:00
return str;
}
2024-05-09 21:53:08 -04:00
void back()
{
current_index--;
}
2024-05-09 13:51:25 -04:00
private:
2024-05-10 12:56:48 -04:00
std::string_view raw_string;
2024-05-09 13:51:25 -04:00
size_t last_read_index = 0;
};
2024-05-10 12:56:48 -04:00
class template_engine_t
{
public:
inline std::string& operator[](const std::string& key)
{
return substitutions[key];
}
inline std::string& operator[](std::string_view key)
{
return substitutions[key];
}
inline template_engine_t& set(std::string_view key, std::string_view replacement)
{
substitutions[key] = replacement;
return *this;
}
inline bool contains(std::string_view token)
{
return substitutions.contains(token);
}
inline auto get(std::string_view token)
{
return evaluate(substitutions[token]);
}
static blt::expected<std::vector<template_token_data_t>, template_tokenizer_failure_t> process_string(std::string_view str);
blt::expected<std::string, template_parser_failure_t> evaluate(std::string_view str);
private:
blt::hashmap_t<std::string, std::string> substitutions;
};
2024-05-09 13:51:25 -04:00
class template_parser_t
{
public:
using estring = blt::expected<std::string, template_parser_failure_t>;
2024-05-09 21:53:08 -04:00
using ebool = blt::expected<bool, template_parser_failure_t>;
2024-05-10 12:56:48 -04:00
template_parser_t(template_engine_t& engine, template_token_consumer_t& consumer):
engine(engine), consumer(consumer)
2024-05-09 13:51:25 -04:00
{}
estring parse()
{
2024-05-09 21:53:08 -04:00
auto next = consumer.consume();
if (next.type == template_token_t::IDENT && consumer.next().type == template_token_t::CURLY_OPEN)
{
consumer.advance();
auto str = statement();
consumer.advance();
2024-05-09 13:51:25 -04:00
return str;
2024-05-09 21:53:08 -04:00
}
return blt::unexpected(template_parser_failure_t::MISSING_IDENT_BRACES);
2024-05-09 13:51:25 -04:00
}
private:
estring statement()
{
2024-05-09 21:53:08 -04:00
auto next = consumer.consume();
if (next.type == template_token_t::STRING || next.type == template_token_t::QUOTE)
{
consumer.back();
return string();
} else if (next.type == template_token_t::FUNCTION)
{
return function();
} else if (next.type == template_token_t::IDENT && consumer.hasNext() && consumer.next().type == template_token_t::CURLY_OPEN)
{
consumer.advance();
auto stmt = statement();
// should never occur
if (consumer.hasNext() && consumer.next().type != template_token_t::CURLY_CLOSE)
return blt::unexpected(template_parser_failure_t::NO_MATCHING_CURLY);
consumer.advance();
return stmt;
} else if (next.type == template_token_t::IF)
{
return if_func();
}
return blt::unexpected(template_parser_failure_t::UNKNOWN_STATEMENT_ERROR);
}
estring function()
{
auto str = consumer.consume();
if (str.type != template_token_t::STRING)
return blt::unexpected(template_parser_failure_t::FUNCTION_EXPECTED_STRING);
if (str.token == "DISCARD")
return blt::unexpected(template_parser_failure_t::FUNCTION_DISCARD);
return blt::unexpected(template_parser_failure_t::FUNCTION_NOT_FOUND);
}
2024-05-09 13:51:25 -04:00
2024-05-09 21:53:08 -04:00
estring if_func()
{
// IF(
if (consumer.consume().type != template_token_t::PAR_OPEN)
return blt::unexpected(template_parser_failure_t::IF_EXPECTED_PAREN);
// (statement)
auto bool_eval = bool_statement();
if (!bool_eval)
return blt::unexpected(bool_eval.error());
if (consumer.consume().type != template_token_t::PAR_CLOSE)
return blt::unexpected(template_parser_failure_t::IF_EXPECTED_PAREN);
auto true_statement = statement();
estring false_statement = blt::unexpected(template_parser_failure_t::UNKNOWN_ERROR);
if (consumer.next().type == template_token_t::ELSE)
{
consumer.advance();
false_statement = statement();
}
if (bool_eval.value())
{
return true_statement;
} else
{
if (false_statement)
return false_statement;
return "";
}
}
estring string()
{
auto next = consumer.consume();
if (next.type == template_token_t::STRING)
{
2024-05-10 12:56:48 -04:00
if (!engine.contains(next.token))
2024-05-09 21:53:08 -04:00
return blt::unexpected(template_parser_failure_t::SUBSTITUTION_NOT_FOUND);
2024-05-10 12:56:48 -04:00
if (consumer.next().type == template_token_t::SEMI || consumer.next().type == template_token_t::ELSE ||
consumer.next().type == template_token_t::CURLY_CLOSE)
2024-05-09 21:53:08 -04:00
{
consumer.advance();
2024-05-10 12:56:48 -04:00
return engine.get(next.token);
2024-05-09 21:53:08 -04:00
}
2024-05-10 01:53:50 -04:00
2024-05-09 21:53:08 -04:00
if (consumer.next().type != template_token_t::ADD)
return blt::unexpected(template_parser_failure_t::STRING_EXPECTED_CONCAT);
consumer.advance();
auto str = string();
2024-05-10 12:56:48 -04:00
if (!str)
2024-05-09 21:53:08 -04:00
return str;
2024-05-10 12:56:48 -04:00
auto sub = engine.get(next.token);
if (!sub)
return sub;
return sub.value() + str.value();
2024-05-09 21:53:08 -04:00
} else
{
if (consumer.next().type == template_token_t::SEMI)
{
consumer.advance();
return std::string(next.token);
}
auto str = string();
if (str)
return std::string(next.token) + str.value();
else
return str;
}
}
ebool bool_statement()
{
auto next = consumer.next();
if (next.type == template_token_t::PAR_OPEN)
{
consumer.advance();
auto b = bool_statement();
if (consumer.consume().type != template_token_t::PAR_CLOSE)
return blt::unexpected(template_parser_failure_t::BOOL_EXPECTED_PAREN);
2024-05-10 12:56:48 -04:00
consumer.advance();
2024-05-09 21:53:08 -04:00
return b;
}
return bool_expression();
}
2024-05-10 12:56:48 -04:00
ebool bool_value()
2024-05-09 21:53:08 -04:00
{
2024-05-10 12:56:48 -04:00
bool b1;
2024-05-09 21:53:08 -04:00
auto next = consumer.next();
if (next.type == template_token_t::PAR_OPEN)
{
auto b = bool_statement();
2024-05-10 12:56:48 -04:00
if (!b)
2024-05-09 21:53:08 -04:00
return b;
2024-05-10 12:56:48 -04:00
b1 = b.value();
} else
2024-05-09 21:53:08 -04:00
{
2024-05-10 12:56:48 -04:00
bool invert = false;
// prefixes
if (next.type == template_token_t::NOT)
2024-05-09 21:53:08 -04:00
{
2024-05-10 12:56:48 -04:00
invert = true;
consumer.advance();
}
if (consumer.next().type == template_token_t::PAR_OPEN)
2024-05-09 21:53:08 -04:00
{
2024-05-10 12:56:48 -04:00
auto b = bool_statement();
if (!b)
return b;
b1 = b.value();
} else
2024-05-09 21:53:08 -04:00
{
2024-05-10 12:56:48 -04:00
auto b = statement();
if (!b)
return blt::unexpected(b.error());
b1 = !b.value().empty();
2024-05-09 21:53:08 -04:00
}
2024-05-10 12:56:48 -04:00
if (invert)
b1 = !b1;
2024-05-09 21:53:08 -04:00
}
2024-05-10 12:56:48 -04:00
return b1;
2024-05-09 13:51:25 -04:00
}
2024-05-10 12:56:48 -04:00
ebool bool_expression()
2024-05-09 13:51:25 -04:00
{
2024-05-10 12:56:48 -04:00
// this whole thing is just bad. please redo. TODO
std::vector<int> values;
while (consumer.next().type != template_token_t::PAR_CLOSE)
{
auto next = consumer.next();
auto bv = bool_value();
if (!bv)
return bv;
values.push_back(bv.value());
if (values.size() == 2)
{
auto b1 = values[0];
auto b2 = values[1];
values.pop_back();
values.pop_back();
switch (next.type)
{
case template_token_t::AND:
values.push_back(b1 && b2);
break;
case template_token_t::OR:
values.push_back(b1 || b2);
break;
case template_token_t::XOR:
values.push_back(b1 ^ b2);
break;
default:
return blt::unexpected(template_parser_failure_t::BOOL_TYPE_NOT_FOUND);
}
}
next = consumer.next();
if (next.type == template_token_t::PAR_CLOSE)
break;
consumer.advance();
// bv = bool_value();
// if (!bv)
// return bv;
// values.push_back(bv.value());
//
// switch (next.type)
// {
// case template_token_t::AND:
// ret =
// case template_token_t::OR:
// break;
// case template_token_t::XOR:
// break;
// default:
// return blt::unexpected(template_parser_failure_t::BOOL_TYPE_NOT_FOUND);
// }
}
if (values.empty())
BLT_WARN("This is not possible!");
return values[0];
// if (next.type == template_token_t::NOT)
// {
// auto b = bool_statement();
// if (b)
// return !b.value();
// else
// return b;
// } else if (next.type == template_token_t::STRING)
// {
// auto bool_val = next.token.empty();
// next = consumer.next();
// if (next.type == template_token_t::PAR_CLOSE)
// return bool_val;
// consumer.advance();
// if (next.type == template_token_t::AND)
// {
// auto other_val = bool_expression();
// if (!other_val)
// return other_val;
// return bool_val && other_val.value();
// } else if (next.type == template_token_t::OR)
// {
// auto other_val = bool_expression();
// if (!other_val)
// return other_val;
// return bool_val || other_val.value();
// } else if (next.type == template_token_t::XOR)
// {
// auto other_val = bool_expression();
// if (!other_val)
// return other_val;
// return bool_val ^ other_val.value();
// }
// }
// return unexpected(template_parser_failure_t::UNKNOWN_ERROR);
2024-05-09 13:51:25 -04:00
}
2024-05-10 12:56:48 -04:00
template_engine_t& engine;
template_token_consumer_t& consumer;
2024-05-09 13:51:25 -04:00
};
}
#endif //BLT_TEMPLATING_H