Skip to content

Commit c1223af

Browse files
committed
feat: private data specs
1 parent 03b5b26 commit c1223af

File tree

5 files changed

+533
-36
lines changed

5 files changed

+533
-36
lines changed

include/mrdox/Dom/Value.hpp

+4
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ class MRDOX_DECL
7272
Value(Object obj) noexcept;
7373
Value(Function fn) noexcept;
7474

75+
template <std::integral T>
76+
requires (!std::same_as<T, bool>)
77+
Value(T v) noexcept : Value(std::int64_t(v)) {}
78+
7579
template<class Boolean>
7680
requires std::is_same_v<Boolean, bool>
7781
Value(Boolean const& b) noexcept

include/mrdox/Support/Handlebars.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ struct HandlebarsOptions
7474
This variable can be used to pass in an object to define custom
7575
private variables.
7676
*/
77-
dom::Value data = {nullptr};
77+
dom::Value data = nullptr;
7878
};
7979

8080
namespace detail {

src/lib/Support/Handlebars.cpp

+116-25
Original file line numberDiff line numberDiff line change
@@ -117,17 +117,36 @@ class OverlayObjectImpl : public dom::ObjectImpl
117117

118118
std::size_t size() const override {
119119
std::size_t n = parent_.size() + child_.size();
120-
for (auto const& [key, value] : child_) {
120+
for (auto const& [key, value] : child_)
121+
{
121122
if (parent_.exists(key))
122-
--n;
123+
{
124+
--n;
123125
}
126+
}
124127
return n;
125128
};
126129

127130
reference get(std::size_t i) const override {
128131
if (i < child_.size())
129-
return child_.get(i);
130-
return parent_.get(i - child_.size());
132+
{
133+
return child_.get(i);
134+
}
135+
MRDOX_ASSERT(i < size());
136+
std::size_t pi = i - child_.size();
137+
for (std::size_t j = 0; j < parent_.size(); ++j)
138+
{
139+
auto el = parent_.get(j);
140+
if (child_.exists(el.key))
141+
{
142+
++pi;
143+
}
144+
else if (j == pi)
145+
{
146+
return el;
147+
}
148+
}
149+
MRDOX_UNREACHABLE();
131150
};
132151

133152
dom::Value find(std::string_view key) const override {
@@ -328,6 +347,7 @@ namespace detail {
328347
dom::Object data;
329348
dom::Object blockValues;
330349
std::vector<dom::Value> compatStack;
350+
std::vector<dom::Object> dataStack;
331351
};
332352
}
333353

@@ -560,6 +580,9 @@ void
560580
checkPath(std::string_view path0, detail::RenderState const& state)
561581
{
562582
std::string_view path = path0;
583+
if (path.starts_with('@')) {
584+
path.remove_prefix(1);
585+
}
563586
std::string_view seg = popFirstSegment(path);
564587
bool areDotDots = seg == "..";
565588
seg = popFirstSegment(path);
@@ -1204,16 +1227,12 @@ render_to(
12041227
detail::RenderState state;
12051228
state.templateText0 = templateText;
12061229
state.templateText = templateText;
1207-
if (options.data.isNull())
1208-
{
1209-
state.data.set("root", context);
1210-
}
1211-
else if (options.data.isObject())
1212-
{
1230+
if (options.data.isObject()) {
12131231
state.data = options.data.getObject();
12141232
}
12151233
state.inlinePartials.emplace_back();
12161234
state.compatStack.emplace_back(context);
1235+
state.dataStack.emplace_back(state.data);
12171236
render_to(out, context, options, state);
12181237
}
12191238

@@ -1490,7 +1509,9 @@ appendContextPath(dom::Value const& contextPath, std::string_view id) {
14901509
bool
14911510
popContextSegment(std::string_view& contextPath) {
14921511
if (contextPath.empty())
1512+
{
14931513
return false;
1514+
}
14941515
auto pos = contextPath.find_last_of('.');
14951516
if (pos == std::string_view::npos)
14961517
{
@@ -1507,7 +1528,7 @@ popContextSegment(std::string_view& contextPath) {
15071528
std::pair<dom::Value, bool>
15081529
Handlebars::
15091530
evalExpr(
1510-
dom::Value const & context,
1531+
dom::Value const& context,
15111532
std::string_view expression,
15121533
detail::RenderState& state,
15131534
HandlebarsOptions const& opt,
@@ -1584,17 +1605,60 @@ evalExpr(
15841605
// ==============================================================
15851606
if (expression.starts_with('@'))
15861607
{
1608+
checkPath(expression, state);
15871609
expression.remove_prefix(1);
1588-
return lookupPropertyImpl(state.data, expression, state);
1610+
dom::Value data = state.data;
1611+
if (expression == "root" || expression.starts_with("root.") || expression.starts_with("root/"))
1612+
{
1613+
popFirstSegment(expression);
1614+
if (state.data.exists("root"))
1615+
{
1616+
data = state.data.find("root");
1617+
}
1618+
else if (!state.compatStack.empty())
1619+
{
1620+
MRDOX_ASSERT(!state.compatStack.empty());
1621+
data = state.compatStack.front();
1622+
}
1623+
}
1624+
else if (expression.starts_with("./") || expression.starts_with("../"))
1625+
{
1626+
auto rDataStack = std::ranges::views::reverse(state.dataStack);
1627+
auto dataIt = rDataStack.begin();
1628+
while (!expression.empty())
1629+
{
1630+
if (expression.starts_with("./"))
1631+
{
1632+
expression.remove_prefix(2);
1633+
continue;
1634+
}
1635+
if (expression.starts_with("../"))
1636+
{
1637+
expression.remove_prefix(3);
1638+
if (dataIt == rDataStack.end())
1639+
{
1640+
return {nullptr, false};
1641+
}
1642+
data = *dataIt;
1643+
++dataIt;
1644+
continue;
1645+
}
1646+
break;
1647+
}
1648+
}
1649+
return lookupPropertyImpl(data, expression, state);
15891650
}
15901651
// ==============================================================
15911652
// Dotdot context path
15921653
// ==============================================================
15931654
if (expression.starts_with("..")) {
1594-
auto contextPathV = state.data.find("contextPath");
1595-
if (!contextPathV.isString())
1596-
return {nullptr, false};
1597-
std::string_view contextPath = contextPathV.getString();
1655+
// Determine the context path, if any
1656+
dom::Value contextPathV = state.data.find("contextPath");
1657+
std::string_view contextPath;
1658+
if (contextPathV.isString())
1659+
{
1660+
contextPath = contextPathV.getString();
1661+
}
15981662
// Remove last segment if it's a literal integer
15991663
if (!contextPath.empty())
16001664
{
@@ -1609,8 +1673,7 @@ evalExpr(
16091673
}
16101674
}
16111675
while (expression.starts_with("..")) {
1612-
if (!popContextSegment(contextPath))
1613-
return {nullptr, false};
1676+
popContextSegment(contextPath);
16141677
expression.remove_prefix(2);
16151678
if (!expression.empty()) {
16161679
if (expression.front() != '/') {
@@ -1620,12 +1683,24 @@ evalExpr(
16201683
}
16211684
}
16221685
std::string absContextPath;
1623-
dom::Value root = state.data.find("root");
1686+
dom::Value root = nullptr;
1687+
if (state.data.exists("root"))
1688+
{
1689+
root = state.data.find("root");
1690+
}
1691+
else
1692+
{
1693+
MRDOX_ASSERT(!state.compatStack.empty());
1694+
root = state.compatStack.front();
1695+
}
16241696
do {
16251697
absContextPath = contextPath;
16261698
if (!expression.empty())
16271699
{
1628-
absContextPath += '.';
1700+
if (!absContextPath.empty())
1701+
{
1702+
absContextPath += '.';
1703+
}
16291704
absContextPath += expression;
16301705
}
16311706
auto [v, defined] = lookupPropertyImpl(root, absContextPath, state);
@@ -1909,19 +1984,19 @@ renderTag(
19091984
dom::Value const& context,
19101985
HandlebarsOptions const& opt,
19111986
detail::RenderState& state) const {
1912-
if (tag.type == '#' || tag.type == '^')
1987+
if ('#' == tag.type || '^' == tag.type)
19131988
{
19141989
renderBlock(tag.helper, tag, out, context, opt, state, false);
19151990
}
1916-
else if (tag.type == '>')
1991+
else if ('>' == tag.type)
19171992
{
19181993
renderPartial(tag, out, context, opt, state);
19191994
}
1920-
else if (tag.type == '*')
1995+
else if ('*' == tag.type)
19211996
{
19221997
renderDecorator(tag, out, context, opt, state);
19231998
}
1924-
else if (tag.type != '/' && tag.type != '!')
1999+
else if ('/' != tag.type && '!' != tag.type)
19252000
{
19262001
renderExpression(tag, out, context, opt, state);
19272002
}
@@ -1981,7 +2056,19 @@ renderExpression(
19812056
auto [v, defined] = evalExpr(context, helper_expr, state, opt, false);
19822057
if (defined)
19832058
{
1984-
format_to(out, v, opt2);
2059+
if (v.isFunction())
2060+
{
2061+
dom::Array args = dom::newArray<dom::DefaultArrayImpl>();
2062+
HandlebarsCallback cb;
2063+
cb.name_ = helper_expr;
2064+
setupArgs(tag.arguments, context, state, args, cb, opt);
2065+
auto v2 = v.getFunction().call(args).value();
2066+
format_to(out, v2, opt2);
2067+
}
2068+
else
2069+
{
2070+
format_to(out, v, opt2);
2071+
}
19852072
return;
19862073
}
19872074

@@ -2279,10 +2366,12 @@ renderPartial(
22792366
state.partialBlockLevel -= isPartialBlock;
22802367
out.setIndent(out.getIndent() + tag.standaloneIndent * !opt.preventIndent);
22812368
state.compatStack.emplace_back(context);
2369+
state.dataStack.emplace_back(state.data);
22822370
// Render
22832371
this->render_to(out, partialCtx, opt, state);
22842372
// Restore state
22852373
state.compatStack.pop_back();
2374+
state.dataStack.pop_back();
22862375
out.setIndent(out.getIndent() - tag.standaloneIndent * !opt.preventIndent);
22872376
state.partialBlockLevel += isPartialBlock;
22882377
state.templateText = templateText;
@@ -2467,6 +2556,7 @@ renderBlock(
24672556
// ==============================================================
24682557
state.inlinePartials.emplace_back();
24692558
state.compatStack.emplace_back(context);
2559+
state.dataStack.emplace_back(state.data);
24702560
auto [res, render] = fn(args, cb);
24712561
if (render == HelperBehavior::RENDER_RESULT) {
24722562
format_to(out, res, opt);
@@ -2477,6 +2567,7 @@ renderBlock(
24772567
}
24782568
state.inlinePartials.pop_back();
24792569
state.compatStack.pop_back();
2570+
state.dataStack.pop_back();
24802571
}
24812572

24822573
void

0 commit comments

Comments
 (0)