Skip to content

Commit e35f106

Browse files
authored
Reintroduce json pointer syntax (#6)
* allow json pointer syntax * revert unwanted formatting changes * revert unwanted formatting changes * don't run push CI * put push back to match upstream and reduce complexity
1 parent c2ad85d commit e35f106

File tree

7 files changed

+77
-6
lines changed

7 files changed

+77
-6
lines changed

include/inja/config.hpp

+6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
namespace inja {
1010

11+
enum class ElementNotation { Dot, Pointer };
12+
1113
/*!
1214
* \brief Class for lexer configuration.
1315
*/
@@ -28,6 +30,8 @@ struct LexerConfig {
2830
std::string comment_close_force_rstrip {"-#}"};
2931
std::string open_chars {"#{"};
3032

33+
ElementNotation notation {ElementNotation::Dot};
34+
3135
bool trim_blocks {false};
3236
bool lstrip_blocks {false};
3337

@@ -64,6 +68,8 @@ struct LexerConfig {
6468
* \brief Class for parser configuration.
6569
*/
6670
struct ParserConfig {
71+
ElementNotation notation {ElementNotation::Dot};
72+
6773
bool search_included_templates_in_files {true};
6874

6975
std::function<Template(const std::string&, const std::string&)> include_callback;

include/inja/environment.hpp

+5
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ class Environment {
8282
void set_lstrip_blocks(bool lstrip_blocks) {
8383
lexer_config.lstrip_blocks = lstrip_blocks;
8484
}
85+
/// Sets the element notation syntax
86+
void set_element_notation(ElementNotation notation) {
87+
parser_config.notation = notation;
88+
lexer_config.notation = notation;
89+
}
8590

8691
/// Sets the element notation syntax
8792
void set_search_included_templates_in_files(bool search_in_files) {

include/inja/lexer.hpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class Lexer {
8181
}
8282

8383
pos = tok_start + 1;
84-
if (std::isalpha(ch)) {
84+
if (std::isalpha(ch) || ch == '~') {
8585
minus_state = MinusState::Operator;
8686
return scan_id();
8787
}
@@ -177,12 +177,13 @@ class Lexer {
177177
}
178178

179179
Token scan_id() {
180+
bool isDotNotation = config.notation == ElementNotation::Dot;
180181
for (;;) {
181182
if (pos >= m_in.size()) {
182183
break;
183184
}
184185
const char ch = m_in[pos];
185-
if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-') {
186+
if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-' && (isDotNotation || ch != '~')) {
186187
break;
187188
}
188189
pos += 1;

include/inja/node.hpp

+18
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010

1111
namespace inja {
1212

13+
enum NotationFlag {
14+
Dot = 0x00,
15+
Pointer = 0x01,
16+
};
17+
1318
class NodeVisitor;
1419
class BlockNode;
1520
class TextNode;
@@ -110,6 +115,15 @@ class DataNode : public ExpressionNode {
110115
const std::string name;
111116
const json::json_pointer ptr;
112117

118+
static std::string get_ptr(std::string_view ptr_name, NotationFlag notation) {
119+
auto ptr = notation == NotationFlag::Dot ? convert_dot_to_ptr(ptr_name) : ptr_name.data();
120+
if(ptr.substr(0,1) != "/") {
121+
ptr = "/" + ptr;
122+
}
123+
124+
return ptr;
125+
}
126+
113127
static std::string convert_dot_to_ptr(std::string_view ptr_name) {
114128
std::string result;
115129
do {
@@ -123,6 +137,10 @@ class DataNode : public ExpressionNode {
123137

124138
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))) {}
125139

140+
explicit DataNode(std::string_view ptr_name, size_t pos, NotationFlag notation)
141+
: ExpressionNode(pos), name(ptr_name),
142+
ptr(json::json_pointer(get_ptr(ptr_name, notation))) {}
143+
126144
void accept(NodeVisitor& v) const {
127145
v.visit(*this);
128146
}

include/inja/parser.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,8 @@ class Parser {
237237

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

243244
// Operators

single_include/inja/inja.hpp

+34-3
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,11 @@ inline void replace_substring(std::string& s, const std::string& f, const std::s
348348

349349
namespace inja {
350350

351+
enum NotationFlag {
352+
Dot = 0x00,
353+
Pointer = 0x01,
354+
};
355+
351356
class NodeVisitor;
352357
class BlockNode;
353358
class TextNode;
@@ -448,6 +453,15 @@ class DataNode : public ExpressionNode {
448453
const std::string name;
449454
const json::json_pointer ptr;
450455

456+
static std::string get_ptr(std::string_view ptr_name, NotationFlag notation) {
457+
auto ptr = notation == NotationFlag::Dot ? convert_dot_to_ptr(ptr_name) : ptr_name.data();
458+
if(ptr.substr(0,1) != "/") {
459+
ptr = "/" + ptr;
460+
}
461+
462+
return ptr;
463+
}
464+
451465
static std::string convert_dot_to_ptr(std::string_view ptr_name) {
452466
std::string result;
453467
do {
@@ -461,6 +475,10 @@ class DataNode : public ExpressionNode {
461475

462476
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))) {}
463477

478+
explicit DataNode(std::string_view ptr_name, size_t pos, NotationFlag notation)
479+
: ExpressionNode(pos), name(ptr_name),
480+
ptr(json::json_pointer(get_ptr(ptr_name, notation))) {}
481+
464482
void accept(NodeVisitor& v) const {
465483
v.visit(*this);
466484
}
@@ -816,6 +834,8 @@ using TemplateStorage = std::map<std::string, Template>;
816834

817835
namespace inja {
818836

837+
enum class ElementNotation { Dot, Pointer };
838+
819839
/*!
820840
* \brief Class for lexer configuration.
821841
*/
@@ -836,6 +856,8 @@ struct LexerConfig {
836856
std::string comment_close_force_rstrip {"-#}"};
837857
std::string open_chars {"#{"};
838858

859+
ElementNotation notation {ElementNotation::Dot};
860+
839861
bool trim_blocks {false};
840862
bool lstrip_blocks {false};
841863

@@ -872,6 +894,8 @@ struct LexerConfig {
872894
* \brief Class for parser configuration.
873895
*/
874896
struct ParserConfig {
897+
ElementNotation notation {ElementNotation::Dot};
898+
875899
bool search_included_templates_in_files {true};
876900

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

10681092
pos = tok_start + 1;
1069-
if (std::isalpha(ch)) {
1093+
if (std::isalpha(ch) || ch == '~') {
10701094
minus_state = MinusState::Operator;
10711095
return scan_id();
10721096
}
@@ -1162,12 +1186,13 @@ class Lexer {
11621186
}
11631187

11641188
Token scan_id() {
1189+
bool isDotNotation = config.notation == ElementNotation::Dot;
11651190
for (;;) {
11661191
if (pos >= m_in.size()) {
11671192
break;
11681193
}
11691194
const char ch = m_in[pos];
1170-
if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-') {
1195+
if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-' && (isDotNotation || ch != '~')) {
11711196
break;
11721197
}
11731198
pos += 1;
@@ -1649,7 +1674,8 @@ class Parser {
16491674

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

16551681
// Operators
@@ -2771,6 +2797,11 @@ class Environment {
27712797
void set_lstrip_blocks(bool lstrip_blocks) {
27722798
lexer_config.lstrip_blocks = lstrip_blocks;
27732799
}
2800+
/// Sets the element notation syntax
2801+
void set_element_notation(ElementNotation notation) {
2802+
parser_config.notation = notation;
2803+
lexer_config.notation = notation;
2804+
}
27742805

27752806
/// Sets the element notation syntax
27762807
void set_search_included_templates_in_files(bool search_in_files) {

test/test-renderer.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ TEST_CASE("types") {
1818
data["relatives"]["brother"] = "Chris";
1919
data["relatives"]["sister"] = "Jenny";
2020
data["vars"] = {2, 3, 4, 0, -1, -2, -3};
21+
data["json_pointers"]["example.com"] = "online";
22+
data["json_pointers"]["and/or"] = "slash";
23+
data["json_pointers"]["and~or"] = "tilde";
2124

2225
SUBCASE("basic") {
2326
CHECK(env.render("", data) == "");
@@ -39,6 +42,12 @@ TEST_CASE("types") {
3942
CHECK(env.render("{{ @name }}", data) == "@name");
4043
CHECK(env.render("{{ $name }}", data) == "$name");
4144

45+
env.set_element_notation(inja::ElementNotation::Pointer);
46+
CHECK(env.render("{{ json_pointers/example.com }}", data) == "online");
47+
CHECK(env.render("{{ json_pointers/and~1or }}", data) == "slash");
48+
CHECK(env.render("{{ json_pointers/and~0or }}", data) == "tilde");
49+
env.set_element_notation(inja::ElementNotation::Dot);
50+
4251
CHECK_THROWS_WITH(env.render("{{unknown}}", data), "[inja.exception.render_error] (at 1:3) variable 'unknown' not found");
4352
}
4453

0 commit comments

Comments
 (0)