diff --git a/CMakeLists.txt b/CMakeLists.txt
index 464b22a..8e04563 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.20)
include(cmake/color.cmake)
-set(BLT_VERSION 0.16.22)
+set(BLT_VERSION 0.16.23)
set(BLT_TEST_VERSION 0.0.1)
set(BLT_TARGET BLT)
diff --git a/include/blt/parse/templating.h b/include/blt/parse/templating.h
new file mode 100644
index 0000000..814140a
--- /dev/null
+++ b/include/blt/parse/templating.h
@@ -0,0 +1,270 @@
+#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 .
+ */
+
+#ifndef BLT_TEMPLATING_H
+#define BLT_TEMPLATING_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace blt
+{
+ template
+ 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, // $
+ 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 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]";
+ }
+ }
+
+ enum class template_tokenizer_failure_t
+ {
+ MISMATCHED_CURLY,
+ MISMATCHED_PAREN,
+ MISMATCHED_QUOTE,
+ };
+
+ enum class template_parser_failure_t
+ {
+ TOKENIZER_FAILURE,
+ NO_MATCHING_CURLY
+ };
+
+ 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
+ {
+ 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, template_token_data_t>
+ {
+ public:
+ explicit template_token_consumer_t(const std::vector& statement): template_consumer_base_t(statement)
+ {}
+
+ std::string_view from_last(std::string_view raw_string)
+ {
+ if (current_index == 0)
+ return "";
+ auto token = storage[getPreviousIndex()];
+ auto len = (&token.token.back() - &raw_string.front()) - last_read_index;
+ auto str = std::string_view(&raw_string[last_read_index], len);
+ last_read_index += len;
+ return str;
+ }
+
+ private:
+ size_t last_read_index = 0;
+ };
+
+ class template_parser_t
+ {
+ public:
+ using estring = blt::expected;
+ template_parser_t(blt::hashmap_t& substitutions, template_token_consumer_t& consumer):
+ substitutions(substitutions), consumer(consumer)
+ {}
+
+ estring parse()
+ {
+ consumer.advance(2);
+ auto str = statement();
+ if (!str)
+ return str;
+ // 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 str;
+ }
+
+ private:
+ estring statement()
+ {
+
+ }
+
+ blt::hashmap_t& substitutions;
+ template_token_consumer_t& consumer;
+ };
+
+ 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;
+ }
+
+ static blt::expected, template_tokenizer_failure_t> process_string(std::string_view str);
+
+ blt::expected evaluate(std::string_view str);
+
+ private:
+ blt::hashmap_t substitutions;
+ };
+
+}
+
+#endif //BLT_TEMPLATING_H
diff --git a/include/blt/std/expected.h b/include/blt/std/expected.h
index 737b02d..b2700af 100644
--- a/include/blt/std/expected.h
+++ b/include/blt/std/expected.h
@@ -75,7 +75,7 @@ namespace blt
}
template
- inline friend constexpr bool operator==(const unexpected& x, const unexpected & y)
+ inline friend constexpr bool operator==(const unexpected& x, const unexpected& y)
{
return x.error() == y.error();
}
@@ -122,31 +122,42 @@ namespace blt
template
inline static constexpr bool eight_insanity_v =
std::is_constructible_v&> || std::is_constructible_v> ||
- std::is_constructible_v&> || std::is_constructible_v> ||
- std::is_convertible_v&, T> || std::is_convertible_v, T> ||
- std::is_convertible_v&, T> || std::is_convertible_v, T>;
+ std::is_constructible_v&> || std::is_constructible_v> ||
+ std::is_convertible_v&, T> || std::is_convertible_v, T> ||
+ std::is_convertible_v&, T> || std::is_convertible_v, T>;
template
inline static constexpr bool four_insanity_v =
std::is_constructible_v, expected&> || std::is_constructible_v, expected> ||
- std::is_constructible_v, const expected&> || std::is_constructible_v, const expected>;
-
+ std::is_constructible_v, const expected&> || std::is_constructible_v, const expected>;
public:
template, bool> = true>
constexpr expected() noexcept: v(T())
{}
- constexpr expected(const expected& copy) = delete;
-
- constexpr expected(expected&& move) noexcept: v(move ? std::move(*move) : std::move(move.error()))
+// constexpr expected(const expected& copy) = delete;
+ constexpr expected(const expected& copy): expected::v(copy.v)
{}
+ expected& operator=(const expected& copy)
+ {
+ v = copy.v;
+ }
+
+ constexpr expected(expected&& move) noexcept: v(std::move(move.v))
+ {}
+
+ expected& operator=(expected&& move)
+ {
+ std::swap(v, move.v);
+ }
+
/*
* (4)...(5)
*/
template, class GF = const G&, std::enable_if_t<
(!std::is_convertible_v || !std::is_convertible_v) && (std::is_constructible_v || std::is_void_v) &&
- std::is_constructible_v && !eight_insanity_v < U, G>&& !four_insanity_v, bool> = true>
+ std::is_constructible_v && !eight_insanity_v && !four_insanity_v, bool> = true>
constexpr explicit expected(const expected& other):
v(other.has_value() ? std::forward(*other) : std::forward(other.error()))
@@ -154,7 +165,7 @@ namespace blt
template || !std::is_convertible_v) && (std::is_constructible_v || std::is_void_v) &&
- std::is_constructible_v && !eight_insanity_v < U, G>&& !four_insanity_v, bool> = true>
+ std::is_constructible_v && !eight_insanity_v && !four_insanity_v, bool> = true>
constexpr explicit expected(expected&& other):
v(other.has_value() ? std::forward(*other) : std::forward(other.error()))
@@ -162,7 +173,7 @@ namespace blt
template, class GF = const G&, std::enable_if_t<
(std::is_convertible_v && std::is_convertible_v) && (std::is_constructible_v || std::is_void_v) &&
- std::is_constructible_v && !eight_insanity_v < U, G>&& !four_insanity_v, bool> = true>
+ std::is_constructible_v && !eight_insanity_v && !four_insanity_v, bool> = true>
constexpr expected(const expected& other):
v(other.has_value() ? std::forward(*other) : std::forward(other.error()))
@@ -170,7 +181,7 @@ namespace blt
template && std::is_convertible_v) && (std::is_constructible_v || std::is_void_v) &&
- std::is_constructible_v && !eight_insanity_v < U, G>&& !four_insanity_v, bool> = true>
+ std::is_constructible_v && !eight_insanity_v && !four_insanity_v, bool> = true>
constexpr expected(expected&& other):
v(other.has_value() ? std::forward(*other) : std::forward(other.error()))
@@ -182,22 +193,22 @@ namespace blt
*/
template &&
- !std::is_same_v, void> &&
- !std::is_same_v, std::in_place_t> &&
- !std::is_same_v> &&
- std::is_constructible_v &&
- !std::is_same_v, unexpected> &&
- !std::is_same_v, expected>, bool> = true>
+ !std::is_same_v, void> &&
+ !std::is_same_v, std::in_place_t> &&
+ !std::is_same_v> &&
+ std::is_constructible_v &&
+ !std::is_same_v, unexpected> &&
+ !std::is_same_v, expected>, bool> = true>
constexpr explicit expected(U&& v): v(T(std::forward(v)))
{}
template &&
- !std::is_same_v, void> &&
- !std::is_same_v, std::in_place_t> &&
- !std::is_same_v> &&
- std::is_constructible_v &&
- !std::is_same_v, unexpected> &&
- !std::is_same_v, expected>, bool> = true>
+ !std::is_same_v, void> &&
+ !std::is_same_v, std::in_place_t> &&
+ !std::is_same_v> &&
+ std::is_constructible_v &&
+ !std::is_same_v, unexpected> &&
+ !std::is_same_v, expected>, bool> = true>
constexpr expected(U&& v): v(T(std::forward(v)))
{}
@@ -252,10 +263,6 @@ namespace blt
constexpr explicit expected(unexpect_t, std::initializer_list il, Args&& ... args): v(E(il, std::forward(args)...))
{}
- expected& operator=(const expected& copy) = delete;
-
- expected& operator=(expected&& move) = default;
-
[[nodiscard]] constexpr explicit operator bool() const noexcept
{
return std::holds_alternative(v);
@@ -362,15 +369,14 @@ namespace blt
};
template
- class expected : expected
+ class expected : public expected
{
public:
- using expected::expected;
+ using expected::expected;
- constexpr expected(const expected& copy): expected::v(copy ? *copy : copy.error())
- {}
+ constexpr expected(const expected& copy) = delete;
- expected& operator=(const expected& copy) = default;
+ expected& operator=(const expected& copy) = delete;
};
}
diff --git a/src/blt/parse/templating.cpp b/src/blt/parse/templating.cpp
new file mode 100644
index 0000000..ff6ad0b
--- /dev/null
+++ b/src/blt/parse/templating.cpp
@@ -0,0 +1,226 @@
+/*
+ *
+ * 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 .
+ */
+#include
+#include
+#include
+#include "blt/std/logging.h"
+
+namespace blt
+{
+
+ bool isNonStringNext(char c)
+ {
+ switch (c)
+ {
+ case '$':
+ case '{':
+ case '}':
+ case '(':
+ case ')':
+ case '"':
+ case '^':
+ case '!':
+ case '&':
+ case ';':
+ case ',':
+ case '.':
+ case '|':
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ blt::expected, template_tokenizer_failure_t> template_engine_t::process_string(std::string_view str)
+ {
+ std::vector tokens;
+
+ template_char_consumer_t consumer(str);
+
+ i64 start = -1;
+ size_t paren_level = 0;
+ size_t level = 0;
+ bool open = false;
+ while (consumer.hasNext())
+ {
+ i64 current_start = static_cast(consumer.getCurrentIndex());
+ char c = consumer.consume();
+ switch (c)
+ {
+ case '$':
+ tokens.emplace_back(template_token_t::IDENT, level, consumer.from(current_start, current_start + 1));
+ if (consumer.next() == '{')
+ {
+ paren_level = 0;
+ open = true;
+ }
+ continue;
+ case '{':
+ tokens.emplace_back(template_token_t::CURLY_OPEN, level, consumer.from(current_start, current_start + 1));
+ if (open)
+ level++;
+ continue;
+ case '}':
+ tokens.emplace_back(template_token_t::CURLY_CLOSE, level, consumer.from(current_start, current_start + 1));
+ if (open)
+ level--;
+ if (level == 0)
+ {
+ open = false;
+ if (paren_level != 0)
+ return blt::unexpected(template_tokenizer_failure_t::MISMATCHED_PAREN);
+ }
+ continue;
+ case '(':
+ tokens.emplace_back(template_token_t::PAR_OPEN, level, consumer.from(current_start, current_start + 1), paren_level);
+ paren_level++;
+ break;
+ case ')':
+ tokens.emplace_back(template_token_t::PAR_CLOSE, level, consumer.from(current_start, current_start + 1), paren_level);
+ paren_level--;
+ break;
+ case '"':
+ tokens.emplace_back(template_token_t::QUOTE, level, consumer.from(current_start, current_start + 1));
+ // if we just encountered a quote, we need to consume characters until we find its matching quote
+ // only if we are currently inside a template though...
+ if (open)
+ {
+ current_start = static_cast(consumer.getCurrentIndex());
+ while (consumer.hasNext() && consumer.next() != '"')
+ consumer.advance();
+ if (!consumer.hasNext())
+ return blt::unexpected(template_tokenizer_failure_t::MISMATCHED_QUOTE);
+ tokens.emplace_back(template_token_t::STRING, level, consumer.from(current_start, consumer.getCurrentIndex()));
+ consumer.advance();
+ current_start = static_cast(consumer.getCurrentIndex());
+ tokens.emplace_back(template_token_t::QUOTE, level, consumer.from(current_start, current_start + 1));
+ }
+ break;
+ case '^':
+ tokens.emplace_back(template_token_t::XOR, level, consumer.from(current_start, current_start + 1));
+ break;
+ case '!':
+ tokens.emplace_back(template_token_t::NOT, level, consumer.from(current_start, current_start + 1));
+ break;
+ case ';':
+ tokens.emplace_back(template_token_t::SEMI, level, consumer.from(current_start, current_start + 1));
+ break;
+ case ',':
+ tokens.emplace_back(template_token_t::COMMA, level, consumer.from(current_start, current_start + 1));
+ break;
+ case '.':
+ tokens.emplace_back(template_token_t::PERIOD, level, consumer.from(current_start, current_start + 1));
+ break;
+ case '~':
+ tokens.emplace_back(template_token_t::FUNCTION, level, consumer.from(current_start, current_start + 1));
+ break;
+ case '|':
+ if (consumer.hasNext() && consumer.next() == '|')
+ {
+ consumer.advance();
+ tokens.emplace_back(template_token_t::OR, level, consumer.from(current_start, current_start + 2));
+ continue;
+ }
+ start = current_start;
+ break;
+ case '&':
+ if (consumer.hasNext() && consumer.next() == '&')
+ {
+ consumer.advance();
+ tokens.emplace_back(template_token_t::AND, level, consumer.from(current_start, current_start + 2));
+ continue;
+ }
+ start = current_start;
+ break;
+ default:
+ // do not add whitespace to anything
+ if (std::isspace(c))
+ break;
+ if (start == -1)
+ start = current_start;
+ if (consumer.hasNext() && (isNonStringNext(consumer.next()) || std::isspace(consumer.next())))
+ {
+ tokens.emplace_back(template_token_t::STRING, level, consumer.from(start, consumer.getCurrentIndex()));
+ start = -1;
+ }
+ break;
+ }
+ }
+
+ if (start != -1)
+ tokens.emplace_back(template_token_t::STRING, level, consumer.from(start, consumer.getCurrentIndex()));
+
+ for (auto& token : tokens)
+ {
+ if (token.type == template_token_t::STRING && detail::identifiers.contains(token.token))
+ token.type = detail::identifiers.at(token.token);
+ }
+
+ if (level != 0)
+ return unexpected(template_tokenizer_failure_t::MISMATCHED_CURLY);
+
+ return tokens;
+ }
+
+ blt::expected template_engine_t::evaluate(std::string_view str)
+ {
+ auto tokens = process_string(str);
+
+ if (!tokens)
+ {
+ switch (tokens.error())
+ {
+ case template_tokenizer_failure_t::MISMATCHED_CURLY:
+ BLT_ERROR("Mismatched curly braces");
+ break;
+ case template_tokenizer_failure_t::MISMATCHED_PAREN:
+ BLT_ERROR("Mismatched parentheses");
+ break;
+ case template_tokenizer_failure_t::MISMATCHED_QUOTE:
+ BLT_ERROR("Mismatched quotes");
+ break;
+ }
+ return blt::unexpected(template_parser_failure_t::TOKENIZER_FAILURE);
+ }
+
+ std::string return_str;
+ return_str.reserve(str.size());
+
+ template_token_consumer_t consumer{tokens.value()};
+
+ template_parser_t parser(substitutions, consumer);
+
+ while (consumer.hasNext())
+ {
+ while (consumer.hasNext(2))
+ {
+ if (consumer.next().type == template_token_t::IDENT && consumer.next().type == template_token_t::CURLY_OPEN)
+ {
+ return_str += consumer.from_last(str);
+ break;
+ }
+ }
+ if (auto result = parser.parse())
+ return_str += result.value();
+ else
+ return result;
+ }
+
+ return return_str;
+ }
+}
\ No newline at end of file
diff --git a/tests/include/blt_tests.h b/tests/include/blt_tests.h
index b5efd9c..bc32d43 100644
--- a/tests/include/blt_tests.h
+++ b/tests/include/blt_tests.h
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
namespace blt::test
{
diff --git a/tests/include/templating_test.h b/tests/include/templating_test.h
new file mode 100644
index 0000000..0d13c80
--- /dev/null
+++ b/tests/include/templating_test.h
@@ -0,0 +1,27 @@
+#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 .
+ */
+
+#ifndef BLT_TEMPLATING_TEST_H
+#define BLT_TEMPLATING_TEST_H
+
+namespace blt::test
+{
+ void template_test();
+}
+
+#endif //BLT_TEMPLATING_TEST_H
diff --git a/tests/src/template_test.cpp b/tests/src/template_test.cpp
new file mode 100644
index 0000000..b5edc75
--- /dev/null
+++ b/tests/src/template_test.cpp
@@ -0,0 +1,89 @@
+/*
+ *
+ * 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 .
+ */
+#include
+#include
+#include
+#include
+
+const std::string shader_test_string = R"("
+#version 300 es
+precision mediump float;
+
+${LAYOUT_STRING} out vec4 FragColor;
+in vec2 uv;
+in vec2 pos;
+
+uniform sampler2D tex;
+
+vec4 linear_iter(vec4 i, vec4 p, float factor){
+ return (i + p) * factor;
+}
+
+void main() {
+ FragColor = texture(tex, uv);
+}
+
+")";
+
+void process_string(const std::string& str)
+{
+ BLT_DEBUG(str);
+ auto results = blt::template_engine_t::process_string(str);
+ if (results)
+ {
+ auto val = results.value();
+ for (auto& v : val)
+ {
+ BLT_TRACE_STREAM << (blt::template_token_to_string(v.type));
+ }
+ BLT_TRACE_STREAM << "\n";
+ for (auto& v : val)
+ {
+ BLT_TRACE("{%s: %s}", blt::template_token_to_string(v.type).c_str(), std::string(v.token).c_str());
+ }
+ } else
+ {
+ auto error = results.error();
+ switch (error)
+ {
+ case blt::template_tokenizer_failure_t::MISMATCHED_CURLY:
+ BLT_ERROR("Tokenizer Failure: Mismatched curly");
+ break;
+ case blt::template_tokenizer_failure_t::MISMATCHED_PAREN:
+ BLT_ERROR("Tokenizer Failure: Mismatched parenthesis");
+ break;
+ case blt::template_tokenizer_failure_t::MISMATCHED_QUOTE:
+ BLT_ERROR("Tokenizer Failure: Mismatched Quotes");
+ break;
+ }
+
+ }
+ BLT_DEBUG("--------------------------");
+}
+
+namespace blt::test
+{
+ void template_test()
+ {
+ process_string(shader_test_string);
+ process_string("~hello");
+ process_string("hello");
+ process_string("hello ${WORLD}");
+ process_string("layout (location = ${IF(LAYOUT_LOCATION) LAYOUT_LOCATION ELSE ~DISCARD})");
+ }
+}
\ No newline at end of file