Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow json pointer syntax #272

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions include/inja/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

namespace inja {

enum class ElementNotation { Dot, Pointer };

/*!
* \brief Class for lexer configuration.
*/
Expand All @@ -28,6 +30,8 @@ struct LexerConfig {
std::string comment_close_force_rstrip {"-#}"};
std::string open_chars {"#{"};

ElementNotation notation {ElementNotation::Dot};

bool trim_blocks {false};
bool lstrip_blocks {false};

Expand Down Expand Up @@ -64,6 +68,8 @@ struct LexerConfig {
* \brief Class for parser configuration.
*/
struct ParserConfig {
ElementNotation notation {ElementNotation::Dot};

bool search_included_templates_in_files {true};

std::function<Template(const std::string&, const std::string&)> include_callback;
Expand Down
5 changes: 5 additions & 0 deletions include/inja/environment.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ class Environment {
void set_lstrip_blocks(bool lstrip_blocks) {
lexer_config.lstrip_blocks = lstrip_blocks;
}
/// Sets the element notation syntax
void set_element_notation(ElementNotation notation) {
parser_config.notation = notation;
lexer_config.notation = notation;
}

/// Sets the element notation syntax
void set_search_included_templates_in_files(bool search_in_files) {
Expand Down
5 changes: 3 additions & 2 deletions include/inja/lexer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class Lexer {
}

pos = tok_start + 1;
if (std::isalpha(ch)) {
if (std::isalpha(ch) || ch == '~') {
minus_state = MinusState::Operator;
return scan_id();
}
Expand Down Expand Up @@ -177,12 +177,13 @@ class Lexer {
}

Token scan_id() {
bool isDotNotation = config.notation == ElementNotation::Dot;
for (;;) {
if (pos >= m_in.size()) {
break;
}
const char ch = m_in[pos];
if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-') {
if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-' && (isDotNotation || ch != '~')) {
break;
}
pos += 1;
Expand Down
18 changes: 18 additions & 0 deletions include/inja/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@

namespace inja {

enum NotationFlag {
Dot = 0x00,
Pointer = 0x01,
};

class NodeVisitor;
class BlockNode;
class TextNode;
Expand Down Expand Up @@ -110,6 +115,15 @@ class DataNode : public ExpressionNode {
const std::string name;
const json::json_pointer ptr;

static std::string get_ptr(std::string_view ptr_name, NotationFlag notation) {
auto ptr = notation == NotationFlag::Dot ? convert_dot_to_ptr(ptr_name) : ptr_name.data();
if(ptr.substr(0,1) != "/") {
ptr = "/" + ptr;
}

return ptr;
}

static std::string convert_dot_to_ptr(std::string_view ptr_name) {
std::string result;
do {
Expand All @@ -123,6 +137,10 @@ class DataNode : public ExpressionNode {

explicit DataNode(std::string_view ptr_name, size_t pos): ExpressionNode(pos), name(ptr_name), ptr(json::json_pointer(convert_dot_to_ptr(ptr_name))) {}

explicit DataNode(std::string_view ptr_name, size_t pos, NotationFlag notation)
: ExpressionNode(pos), name(ptr_name),
ptr(json::json_pointer(get_ptr(ptr_name, notation))) {}

void accept(NodeVisitor& v) const {
v.visit(*this);
}
Expand Down
3 changes: 2 additions & 1 deletion include/inja/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,8 @@ class Parser {

// Variables
} else {
arguments.emplace_back(std::make_shared<DataNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
auto notation = this->config.notation == ElementNotation::Dot ? NotationFlag::Dot : NotationFlag::Pointer;
arguments.emplace_back(std::make_shared<DataNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str(), notation));
}

// Operators
Expand Down
37 changes: 34 additions & 3 deletions single_include/inja/inja.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,11 @@ inline void replace_substring(std::string& s, const std::string& f, const std::s

namespace inja {

enum NotationFlag {
Dot = 0x00,
Pointer = 0x01,
};

class NodeVisitor;
class BlockNode;
class TextNode;
Expand Down Expand Up @@ -448,6 +453,15 @@ class DataNode : public ExpressionNode {
const std::string name;
const json::json_pointer ptr;

static std::string get_ptr(std::string_view ptr_name, NotationFlag notation) {
auto ptr = notation == NotationFlag::Dot ? convert_dot_to_ptr(ptr_name) : ptr_name.data();
if(ptr.substr(0,1) != "/") {
ptr = "/" + ptr;
}

return ptr;
}

static std::string convert_dot_to_ptr(std::string_view ptr_name) {
std::string result;
do {
Expand All @@ -461,6 +475,10 @@ class DataNode : public ExpressionNode {

explicit DataNode(std::string_view ptr_name, size_t pos): ExpressionNode(pos), name(ptr_name), ptr(json::json_pointer(convert_dot_to_ptr(ptr_name))) {}

explicit DataNode(std::string_view ptr_name, size_t pos, NotationFlag notation)
: ExpressionNode(pos), name(ptr_name),
ptr(json::json_pointer(get_ptr(ptr_name, notation))) {}

void accept(NodeVisitor& v) const {
v.visit(*this);
}
Expand Down Expand Up @@ -816,6 +834,8 @@ using TemplateStorage = std::map<std::string, Template>;

namespace inja {

enum class ElementNotation { Dot, Pointer };

/*!
* \brief Class for lexer configuration.
*/
Expand All @@ -836,6 +856,8 @@ struct LexerConfig {
std::string comment_close_force_rstrip {"-#}"};
std::string open_chars {"#{"};

ElementNotation notation {ElementNotation::Dot};

bool trim_blocks {false};
bool lstrip_blocks {false};

Expand Down Expand Up @@ -872,6 +894,8 @@ struct LexerConfig {
* \brief Class for parser configuration.
*/
struct ParserConfig {
ElementNotation notation {ElementNotation::Dot};

bool search_included_templates_in_files {true};

std::function<Template(const std::string&, const std::string&)> include_callback;
Expand Down Expand Up @@ -1066,7 +1090,7 @@ class Lexer {
}

pos = tok_start + 1;
if (std::isalpha(ch)) {
if (std::isalpha(ch) || ch == '~') {
minus_state = MinusState::Operator;
return scan_id();
}
Expand Down Expand Up @@ -1162,12 +1186,13 @@ class Lexer {
}

Token scan_id() {
bool isDotNotation = config.notation == ElementNotation::Dot;
for (;;) {
if (pos >= m_in.size()) {
break;
}
const char ch = m_in[pos];
if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-') {
if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-' && (isDotNotation || ch != '~')) {
break;
}
pos += 1;
Expand Down Expand Up @@ -1649,7 +1674,8 @@ class Parser {

// Variables
} else {
arguments.emplace_back(std::make_shared<DataNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
auto notation = this->config.notation == ElementNotation::Dot ? NotationFlag::Dot : NotationFlag::Pointer;
arguments.emplace_back(std::make_shared<DataNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str(), notation));
}

// Operators
Expand Down Expand Up @@ -2771,6 +2797,11 @@ class Environment {
void set_lstrip_blocks(bool lstrip_blocks) {
lexer_config.lstrip_blocks = lstrip_blocks;
}
/// Sets the element notation syntax
void set_element_notation(ElementNotation notation) {
parser_config.notation = notation;
lexer_config.notation = notation;
}

/// Sets the element notation syntax
void set_search_included_templates_in_files(bool search_in_files) {
Expand Down
9 changes: 9 additions & 0 deletions test/test-renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ TEST_CASE("types") {
data["relatives"]["brother"] = "Chris";
data["relatives"]["sister"] = "Jenny";
data["vars"] = {2, 3, 4, 0, -1, -2, -3};
data["json_pointers"]["example.com"] = "online";
data["json_pointers"]["and/or"] = "slash";
data["json_pointers"]["and~or"] = "tilde";

SUBCASE("basic") {
CHECK(env.render("", data) == "");
Expand All @@ -39,6 +42,12 @@ TEST_CASE("types") {
CHECK(env.render("{{ @name }}", data) == "@name");
CHECK(env.render("{{ $name }}", data) == "$name");

env.set_element_notation(inja::ElementNotation::Pointer);
CHECK(env.render("{{ json_pointers/example.com }}", data) == "online");
CHECK(env.render("{{ json_pointers/and~1or }}", data) == "slash");
CHECK(env.render("{{ json_pointers/and~0or }}", data) == "tilde");
env.set_element_notation(inja::ElementNotation::Dot);

CHECK_THROWS_WITH(env.render("{{unknown}}", data), "[inja.exception.render_error] (at 1:3) variable 'unknown' not found");
}

Expand Down