mirror of
https://github.com/pantor/inja.git
synced 2026-03-05 00:36:26 +00:00
add string_view polyfill
This commit is contained in:
@@ -4,6 +4,13 @@ sudo: required
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env: COMPILER=g++-6
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test']
|
||||
packages: g++-6
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env:
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
#ifndef PANTOR_INJA_BYTECODE_HPP
|
||||
#define PANTOR_INJA_BYTECODE_HPP
|
||||
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "string_view.hpp"
|
||||
|
||||
|
||||
namespace inja {
|
||||
|
||||
@@ -120,7 +121,7 @@ struct Bytecode {
|
||||
|
||||
Bytecode(): args(0), flags(0) {}
|
||||
explicit Bytecode(Op op, unsigned int args = 0): op(op), args(args), flags(0) {}
|
||||
explicit Bytecode(Op op, std::string_view str, unsigned int flags): op(op), args(0), flags(flags), str(str) {}
|
||||
explicit Bytecode(Op op, nonstd::string_view str, unsigned int flags): op(op), args(0), flags(flags), str(str) {}
|
||||
explicit Bytecode(Op op, json&& value, unsigned int flags): op(op), args(0), flags(flags), value(std::move(value)) {}
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "string_view.hpp"
|
||||
|
||||
|
||||
namespace inja {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -14,6 +13,7 @@
|
||||
#include "parser.hpp"
|
||||
#include "polyfill.hpp"
|
||||
#include "renderer.hpp"
|
||||
#include "string_view.hpp"
|
||||
#include "template.hpp"
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ class Environment {
|
||||
}
|
||||
|
||||
|
||||
Template parse(std::string_view input) {
|
||||
Template parse(nonstd::string_view input) {
|
||||
Parser parser(m_impl->parser_config, m_impl->lexer_config, m_impl->included_templates);
|
||||
return parser.parse(input);
|
||||
}
|
||||
@@ -92,7 +92,7 @@ class Environment {
|
||||
return parser.parse_template(m_impl->input_path + static_cast<std::string>(filename));
|
||||
}
|
||||
|
||||
std::string render(std::string_view input, const json& data) {
|
||||
std::string render(nonstd::string_view input, const json& data) {
|
||||
return render(parse(input), data);
|
||||
}
|
||||
|
||||
@@ -166,14 +166,14 @@ class Environment {
|
||||
/*!
|
||||
@brief render with default settings to a string
|
||||
*/
|
||||
inline std::string render(std::string_view input, const json& data) {
|
||||
inline std::string render(nonstd::string_view input, const json& data) {
|
||||
return Environment().render(input, data);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief render with default settings to the given output stream
|
||||
*/
|
||||
inline void render_to(std::ostream& os, std::string_view input, const json& data) {
|
||||
inline void render_to(std::ostream& os, nonstd::string_view input, const json& data) {
|
||||
Environment env;
|
||||
env.render_to(os, env.parse(input), data);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#ifndef PANTOR_INJA_FUNCTION_STORAGE_HPP
|
||||
#define PANTOR_INJA_FUNCTION_STORAGE_HPP
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include "bytecode.hpp"
|
||||
#include "string_view.hpp"
|
||||
|
||||
|
||||
namespace inja {
|
||||
@@ -15,24 +14,24 @@ using CallbackFunction = std::function<json(Arguments& args)>;
|
||||
|
||||
class FunctionStorage {
|
||||
public:
|
||||
void add_builtin(std::string_view name, unsigned int num_args, Bytecode::Op op) {
|
||||
void add_builtin(nonstd::string_view name, unsigned int num_args, Bytecode::Op op) {
|
||||
auto& data = get_or_new(name, num_args);
|
||||
data.op = op;
|
||||
}
|
||||
|
||||
void add_callback(std::string_view name, unsigned int num_args, const CallbackFunction& function) {
|
||||
void add_callback(nonstd::string_view name, unsigned int num_args, const CallbackFunction& function) {
|
||||
auto& data = get_or_new(name, num_args);
|
||||
data.function = function;
|
||||
}
|
||||
|
||||
Bytecode::Op find_builtin(std::string_view name, unsigned int num_args) const {
|
||||
Bytecode::Op find_builtin(nonstd::string_view name, unsigned int num_args) const {
|
||||
if (auto ptr = get(name, num_args)) {
|
||||
return ptr->op;
|
||||
}
|
||||
return Bytecode::Op::Nop;
|
||||
}
|
||||
|
||||
CallbackFunction find_callback(std::string_view name, unsigned int num_args) const {
|
||||
CallbackFunction find_callback(nonstd::string_view name, unsigned int num_args) const {
|
||||
if (auto ptr = get(name, num_args)) {
|
||||
return ptr->function;
|
||||
}
|
||||
@@ -46,7 +45,7 @@ class FunctionStorage {
|
||||
CallbackFunction function; // for callbacks
|
||||
};
|
||||
|
||||
FunctionData& get_or_new(std::string_view name, unsigned int num_args) {
|
||||
FunctionData& get_or_new(nonstd::string_view name, unsigned int num_args) {
|
||||
auto &vec = m_map[static_cast<std::string>(name)];
|
||||
for (auto &i: vec) {
|
||||
if (i.num_args == num_args) return i;
|
||||
@@ -56,7 +55,7 @@ class FunctionStorage {
|
||||
return vec.back();
|
||||
}
|
||||
|
||||
const FunctionData* get(std::string_view name, unsigned int num_args) const {
|
||||
const FunctionData* get(nonstd::string_view name, unsigned int num_args) const {
|
||||
auto it = m_map.find(static_cast<std::string>(name));
|
||||
if (it == m_map.end()) return nullptr;
|
||||
for (auto &&i: it->second) {
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "environment.hpp"
|
||||
#include "string_view.hpp"
|
||||
#include "template.hpp"
|
||||
#include "parser.hpp"
|
||||
#include "renderer.hpp"
|
||||
|
||||
@@ -25,14 +25,14 @@ class Lexer {
|
||||
} m_state;
|
||||
|
||||
const LexerConfig& m_config;
|
||||
std::string_view m_in;
|
||||
nonstd::string_view m_in;
|
||||
size_t m_tok_start;
|
||||
size_t m_pos;
|
||||
|
||||
public:
|
||||
explicit Lexer(const LexerConfig& config) : m_config(config) {}
|
||||
|
||||
void start(std::string_view in) {
|
||||
void start(nonstd::string_view in) {
|
||||
m_in = in;
|
||||
m_tok_start = 0;
|
||||
m_pos = 0;
|
||||
@@ -50,7 +50,7 @@ class Lexer {
|
||||
case State::Text: {
|
||||
// fast-scan to first open character
|
||||
size_t open_start = m_in.substr(m_pos).find_first_of(m_config.open_chars);
|
||||
if (open_start == std::string_view::npos) {
|
||||
if (open_start == nonstd::string_view::npos) {
|
||||
// didn't find open, return remaining text as text token
|
||||
m_pos = m_in.size();
|
||||
return make_token(Token::Kind::Text);
|
||||
@@ -58,15 +58,15 @@ class Lexer {
|
||||
m_pos += open_start;
|
||||
|
||||
// try to match one of the opening sequences, and get the close
|
||||
std::string_view open_str = m_in.substr(m_pos);
|
||||
if (string_view::starts_with(open_str, m_config.expression_open)) {
|
||||
nonstd::string_view open_str = m_in.substr(m_pos);
|
||||
if (inja::string_view::starts_with(open_str, m_config.expression_open)) {
|
||||
m_state = State::ExpressionStart;
|
||||
} else if (string_view::starts_with(open_str, m_config.statement_open)) {
|
||||
} else if (inja::string_view::starts_with(open_str, m_config.statement_open)) {
|
||||
m_state = State::StatementStart;
|
||||
} else if (string_view::starts_with(open_str, m_config.comment_open)) {
|
||||
} else if (inja::string_view::starts_with(open_str, m_config.comment_open)) {
|
||||
m_state = State::CommentStart;
|
||||
} else if ((m_pos == 0 || m_in[m_pos - 1] == '\n') &&
|
||||
string_view::starts_with(open_str, m_config.line_statement)) {
|
||||
inja::string_view::starts_with(open_str, m_config.line_statement)) {
|
||||
m_state = State::LineStart;
|
||||
} else {
|
||||
m_pos += 1; // wasn't actually an opening sequence
|
||||
@@ -104,7 +104,7 @@ class Lexer {
|
||||
case State::CommentBody: {
|
||||
// fast-scan to comment close
|
||||
size_t end = m_in.substr(m_pos).find(m_config.comment_close);
|
||||
if (end == std::string_view::npos) {
|
||||
if (end == nonstd::string_view::npos) {
|
||||
m_pos = m_in.size();
|
||||
return make_token(Token::Kind::Eof);
|
||||
}
|
||||
@@ -119,7 +119,7 @@ class Lexer {
|
||||
const LexerConfig& get_config() const { return m_config; }
|
||||
|
||||
private:
|
||||
Token scan_body(std::string_view close, Token::Kind closeKind) {
|
||||
Token scan_body(nonstd::string_view close, Token::Kind closeKind) {
|
||||
again:
|
||||
// skip whitespace (except for \n as it might be a close)
|
||||
if (m_tok_start >= m_in.size()) return make_token(Token::Kind::Eof);
|
||||
@@ -130,7 +130,7 @@ class Lexer {
|
||||
}
|
||||
|
||||
// check for close
|
||||
if (string_view::starts_with(m_in.substr(m_tok_start), close)) {
|
||||
if (inja::string_view::starts_with(m_in.substr(m_tok_start), close)) {
|
||||
m_state = State::Text;
|
||||
m_pos = m_tok_start + close.size();
|
||||
return make_token(closeKind);
|
||||
|
||||
@@ -128,7 +128,7 @@ class Parser {
|
||||
}
|
||||
|
||||
bool parse_expression_datum(Template& tmpl) {
|
||||
std::string_view json_first;
|
||||
nonstd::string_view json_first;
|
||||
size_t bracket_level = 0;
|
||||
size_t brace_level = 0;
|
||||
|
||||
@@ -253,13 +253,13 @@ class Parser {
|
||||
|
||||
returnJson:
|
||||
// bridge across all intermediate tokens
|
||||
std::string_view json_text(json_first.data(), m_tok.text.data() - json_first.data() + m_tok.text.size());
|
||||
nonstd::string_view json_text(json_first.data(), m_tok.text.data() - json_first.data() + m_tok.text.size());
|
||||
tmpl.bytecodes.emplace_back(Bytecode::Op::Push, json::parse(json_text), Bytecode::Flag::ValueImmediate);
|
||||
get_next_token();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parse_statement(Template& tmpl, std::string_view path) {
|
||||
bool parse_statement(Template& tmpl, nonstd::string_view path) {
|
||||
if (m_tok.kind != Token::Kind::Id) return false;
|
||||
|
||||
if (m_tok.text == "if") {
|
||||
@@ -352,7 +352,7 @@ class Parser {
|
||||
if (!key_token.text.empty()) {
|
||||
tmpl.bytecodes.back().value = key_token.text;
|
||||
}
|
||||
tmpl.bytecodes.back().str = value_token.text;
|
||||
tmpl.bytecodes.back().str = static_cast<std::string>(value_token.text);
|
||||
} else if (m_tok.text == "endfor") {
|
||||
get_next_token();
|
||||
if (m_loop_stack.empty()) {
|
||||
@@ -409,7 +409,7 @@ class Parser {
|
||||
tmpl.bytecodes.emplace_back(op, num_args);
|
||||
}
|
||||
|
||||
void append_callback(Template& tmpl, std::string_view name, unsigned int num_args) {
|
||||
void append_callback(Template& tmpl, nonstd::string_view name, unsigned int num_args) {
|
||||
// we can merge with back-to-back push value (not lookup)
|
||||
if (!tmpl.bytecodes.empty()) {
|
||||
Bytecode& last = tmpl.bytecodes.back();
|
||||
@@ -417,17 +417,17 @@ class Parser {
|
||||
(last.flags & Bytecode::Flag::ValueMask) == Bytecode::Flag::ValueImmediate) {
|
||||
last.op = Bytecode::Op::Callback;
|
||||
last.args = num_args;
|
||||
last.str = name;
|
||||
last.str = static_cast<std::string>(name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise just add it to the end
|
||||
tmpl.bytecodes.emplace_back(Bytecode::Op::Callback, num_args);
|
||||
tmpl.bytecodes.back().str = name;
|
||||
tmpl.bytecodes.back().str = static_cast<std::string>(name);
|
||||
}
|
||||
|
||||
void parse_into(Template& tmpl, std::string_view path) {
|
||||
void parse_into(Template& tmpl, nonstd::string_view path) {
|
||||
m_lexer.start(tmpl.content);
|
||||
|
||||
for (;;) {
|
||||
@@ -480,28 +480,28 @@ class Parser {
|
||||
}
|
||||
}
|
||||
|
||||
Template parse(std::string_view input, std::string_view path) {
|
||||
Template parse(nonstd::string_view input, nonstd::string_view path) {
|
||||
Template result;
|
||||
result.content = input;
|
||||
result.content = static_cast<std::string>(input);
|
||||
parse_into(result, path);
|
||||
return result;
|
||||
}
|
||||
|
||||
Template parse(std::string_view input) {
|
||||
Template parse(nonstd::string_view input) {
|
||||
return parse(input, "./");
|
||||
}
|
||||
|
||||
Template parse_template(std::string_view filename) {
|
||||
Template parse_template(nonstd::string_view filename) {
|
||||
Template result;
|
||||
result.content = load_file(filename);
|
||||
|
||||
std::string_view path = filename.substr(0, filename.find_last_of("/\\") + 1);
|
||||
nonstd::string_view path = filename.substr(0, filename.find_last_of("/\\") + 1);
|
||||
// StringRef path = sys::path::parent_path(filename);
|
||||
Parser(m_config, m_lexer.get_config(), m_included_templates).parse_into(result, path);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string load_file(std::string_view filename) {
|
||||
std::string load_file(nonstd::string_view filename) {
|
||||
std::ifstream file(static_cast<std::string>(filename));
|
||||
std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
return text;
|
||||
|
||||
@@ -13,15 +13,15 @@
|
||||
|
||||
namespace inja {
|
||||
|
||||
inline std::string_view convert_dot_to_json_pointer(std::string_view dot, std::string& out) {
|
||||
inline nonstd::string_view convert_dot_to_json_pointer(nonstd::string_view dot, std::string& out) {
|
||||
out.clear();
|
||||
do {
|
||||
std::string_view part;
|
||||
nonstd::string_view part;
|
||||
std::tie(part, dot) = string_view::split(dot, '.');
|
||||
out.push_back('/');
|
||||
out.append(part.begin(), part.end());
|
||||
} while (!dot.empty());
|
||||
return std::string_view(out.data(), out.size());
|
||||
return nonstd::string_view(out.data(), out.size());
|
||||
}
|
||||
|
||||
class Renderer {
|
||||
@@ -60,7 +60,7 @@ class Renderer {
|
||||
|
||||
const json* get_imm(const Bytecode& bc) {
|
||||
std::string ptr_buffer;
|
||||
std::string_view ptr;
|
||||
nonstd::string_view ptr;
|
||||
switch (bc.flags & Bytecode::Flag::ValueMask) {
|
||||
case Bytecode::Flag::ValuePop:
|
||||
return nullptr;
|
||||
@@ -136,8 +136,8 @@ class Renderer {
|
||||
std::vector<json> m_stack;
|
||||
|
||||
struct LoopLevel {
|
||||
std::string_view key_name; // variable name for keys
|
||||
std::string_view value_name; // variable name for values
|
||||
nonstd::string_view key_name; // variable name for keys
|
||||
nonstd::string_view value_name; // variable name for values
|
||||
json data; // data with loop info added
|
||||
|
||||
json values; // values to iterate over
|
||||
@@ -148,7 +148,7 @@ class Renderer {
|
||||
size_t size; // length of list
|
||||
|
||||
// loop over map
|
||||
using KeyValue = std::pair<std::string_view, json*>;
|
||||
using KeyValue = std::pair<nonstd::string_view, json*>;
|
||||
using MapValues = std::vector<KeyValue>;
|
||||
MapValues map_values; // values to iterate over
|
||||
MapValues::iterator map_it; // iterator over values
|
||||
|
||||
1320
include/inja/string_view.hpp
Normal file
1320
include/inja/string_view.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
#ifndef PANTOR_INJA_TOKEN_HPP
|
||||
#define PANTOR_INJA_TOKEN_HPP
|
||||
|
||||
#include <string_view>
|
||||
#include "string_view.hpp"
|
||||
|
||||
|
||||
namespace inja {
|
||||
@@ -38,10 +38,10 @@ struct Token {
|
||||
Eof
|
||||
} kind {Kind::Unknown};
|
||||
|
||||
std::string_view text;
|
||||
nonstd::string_view text;
|
||||
|
||||
constexpr Token() = default;
|
||||
constexpr Token(Kind kind, std::string_view text): kind(kind), text(text) {}
|
||||
constexpr Token(Kind kind, nonstd::string_view text): kind(kind), text(text) {}
|
||||
|
||||
std::string describe() const {
|
||||
switch (kind) {
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
#define PANTOR_INJA_UTILS_HPP
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
|
||||
#include "string_view.hpp"
|
||||
|
||||
|
||||
namespace inja {
|
||||
@@ -12,21 +13,21 @@ inline void inja_throw(const std::string& type, const std::string& message) {
|
||||
}
|
||||
|
||||
namespace string_view {
|
||||
inline std::string_view slice(std::string_view view, size_t start, size_t end) {
|
||||
inline nonstd::string_view slice(nonstd::string_view view, size_t start, size_t end) {
|
||||
start = std::min(start, view.size());
|
||||
end = std::min(std::max(start, end), view.size());
|
||||
return view.substr(start, end - start); // StringRef(Data + Start, End - Start);
|
||||
}
|
||||
|
||||
inline std::pair<std::string_view, std::string_view> split(std::string_view view, char Separator) {
|
||||
inline std::pair<nonstd::string_view, nonstd::string_view> split(nonstd::string_view view, char Separator) {
|
||||
size_t idx = view.find(Separator);
|
||||
if (idx == std::string_view::npos) {
|
||||
return std::make_pair(view, std::string_view());
|
||||
if (idx == nonstd::string_view::npos) {
|
||||
return std::make_pair(view, nonstd::string_view());
|
||||
}
|
||||
return std::make_pair(slice(view, 0, idx), slice(view, idx + 1, std::string_view::npos));
|
||||
return std::make_pair(slice(view, 0, idx), slice(view, idx + 1, nonstd::string_view::npos));
|
||||
}
|
||||
|
||||
inline bool starts_with(std::string_view view, std::string_view prefix) {
|
||||
inline bool starts_with(nonstd::string_view view, nonstd::string_view prefix) {
|
||||
return (view.size() >= prefix.size() && view.compare(0, prefix.size(), prefix) == 0);
|
||||
}
|
||||
} // namespace string
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -261,12 +261,12 @@ TEST_CASE("callbacks") {
|
||||
data["age"] = 28;
|
||||
|
||||
env.add_callback("double", 1, [](inja::Arguments& args) {
|
||||
int number = args.at(0)->get<double>();
|
||||
int number = args.at(0)->get<int>();
|
||||
return 2 * number;
|
||||
});
|
||||
|
||||
env.add_callback("half", 1, [](inja::Arguments args) {
|
||||
int number = args.at(0)->get<double>();
|
||||
int number = args.at(0)->get<int>();
|
||||
return number / 2;
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user