@@ -117,17 +117,36 @@ class OverlayObjectImpl : public dom::ObjectImpl
117
117
118
118
std::size_t size () const override {
119
119
std::size_t n = parent_.size () + child_.size ();
120
- for (auto const & [key, value] : child_) {
120
+ for (auto const & [key, value] : child_)
121
+ {
121
122
if (parent_.exists (key))
122
- --n;
123
+ {
124
+ --n;
123
125
}
126
+ }
124
127
return n;
125
128
};
126
129
127
130
reference get (std::size_t i) const override {
128
131
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 ();
131
150
};
132
151
133
152
dom::Value find (std::string_view key) const override {
@@ -328,6 +347,7 @@ namespace detail {
328
347
dom::Object data;
329
348
dom::Object blockValues;
330
349
std::vector<dom::Value> compatStack;
350
+ std::vector<dom::Object> dataStack;
331
351
};
332
352
}
333
353
560
580
checkPath (std::string_view path0, detail::RenderState const & state)
561
581
{
562
582
std::string_view path = path0;
583
+ if (path.starts_with (' @' )) {
584
+ path.remove_prefix (1 );
585
+ }
563
586
std::string_view seg = popFirstSegment (path);
564
587
bool areDotDots = seg == " .." ;
565
588
seg = popFirstSegment (path);
@@ -1204,16 +1227,12 @@ render_to(
1204
1227
detail::RenderState state;
1205
1228
state.templateText0 = templateText;
1206
1229
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 ()) {
1213
1231
state.data = options.data .getObject ();
1214
1232
}
1215
1233
state.inlinePartials .emplace_back ();
1216
1234
state.compatStack .emplace_back (context);
1235
+ state.dataStack .emplace_back (state.data );
1217
1236
render_to (out, context, options, state);
1218
1237
}
1219
1238
@@ -1490,7 +1509,9 @@ appendContextPath(dom::Value const& contextPath, std::string_view id) {
1490
1509
bool
1491
1510
popContextSegment (std::string_view& contextPath) {
1492
1511
if (contextPath.empty ())
1512
+ {
1493
1513
return false ;
1514
+ }
1494
1515
auto pos = contextPath.find_last_of (' .' );
1495
1516
if (pos == std::string_view::npos)
1496
1517
{
@@ -1507,7 +1528,7 @@ popContextSegment(std::string_view& contextPath) {
1507
1528
std::pair<dom::Value, bool >
1508
1529
Handlebars::
1509
1530
evalExpr (
1510
- dom::Value const & context,
1531
+ dom::Value const & context,
1511
1532
std::string_view expression,
1512
1533
detail::RenderState& state,
1513
1534
HandlebarsOptions const & opt,
@@ -1584,17 +1605,60 @@ evalExpr(
1584
1605
// ==============================================================
1585
1606
if (expression.starts_with (' @' ))
1586
1607
{
1608
+ checkPath (expression, state);
1587
1609
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);
1589
1650
}
1590
1651
// ==============================================================
1591
1652
// Dotdot context path
1592
1653
// ==============================================================
1593
1654
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
+ }
1598
1662
// Remove last segment if it's a literal integer
1599
1663
if (!contextPath.empty ())
1600
1664
{
@@ -1609,8 +1673,7 @@ evalExpr(
1609
1673
}
1610
1674
}
1611
1675
while (expression.starts_with (" .." )) {
1612
- if (!popContextSegment (contextPath))
1613
- return {nullptr , false };
1676
+ popContextSegment (contextPath);
1614
1677
expression.remove_prefix (2 );
1615
1678
if (!expression.empty ()) {
1616
1679
if (expression.front () != ' /' ) {
@@ -1620,12 +1683,24 @@ evalExpr(
1620
1683
}
1621
1684
}
1622
1685
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
+ }
1624
1696
do {
1625
1697
absContextPath = contextPath;
1626
1698
if (!expression.empty ())
1627
1699
{
1628
- absContextPath += ' .' ;
1700
+ if (!absContextPath.empty ())
1701
+ {
1702
+ absContextPath += ' .' ;
1703
+ }
1629
1704
absContextPath += expression;
1630
1705
}
1631
1706
auto [v, defined] = lookupPropertyImpl (root, absContextPath, state);
@@ -1909,19 +1984,19 @@ renderTag(
1909
1984
dom::Value const & context,
1910
1985
HandlebarsOptions const & opt,
1911
1986
detail::RenderState& state) const {
1912
- if (tag. type == ' #' || tag.type == ' ^' )
1987
+ if (' #' == tag.type || ' ^' == tag. type )
1913
1988
{
1914
1989
renderBlock (tag.helper , tag, out, context, opt, state, false );
1915
1990
}
1916
- else if (tag. type == ' > ' )
1991
+ else if (' > ' == tag. type )
1917
1992
{
1918
1993
renderPartial (tag, out, context, opt, state);
1919
1994
}
1920
- else if (tag. type == ' * ' )
1995
+ else if (' * ' == tag. type )
1921
1996
{
1922
1997
renderDecorator (tag, out, context, opt, state);
1923
1998
}
1924
- else if (tag. type != ' /' && tag.type != ' !' )
1999
+ else if (' /' != tag.type && ' !' != tag. type )
1925
2000
{
1926
2001
renderExpression (tag, out, context, opt, state);
1927
2002
}
@@ -1981,7 +2056,19 @@ renderExpression(
1981
2056
auto [v, defined] = evalExpr (context, helper_expr, state, opt, false );
1982
2057
if (defined)
1983
2058
{
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
+ }
1985
2072
return ;
1986
2073
}
1987
2074
@@ -2279,10 +2366,12 @@ renderPartial(
2279
2366
state.partialBlockLevel -= isPartialBlock;
2280
2367
out.setIndent (out.getIndent () + tag.standaloneIndent * !opt.preventIndent );
2281
2368
state.compatStack .emplace_back (context);
2369
+ state.dataStack .emplace_back (state.data );
2282
2370
// Render
2283
2371
this ->render_to (out, partialCtx, opt, state);
2284
2372
// Restore state
2285
2373
state.compatStack .pop_back ();
2374
+ state.dataStack .pop_back ();
2286
2375
out.setIndent (out.getIndent () - tag.standaloneIndent * !opt.preventIndent );
2287
2376
state.partialBlockLevel += isPartialBlock;
2288
2377
state.templateText = templateText;
@@ -2467,6 +2556,7 @@ renderBlock(
2467
2556
// ==============================================================
2468
2557
state.inlinePartials .emplace_back ();
2469
2558
state.compatStack .emplace_back (context);
2559
+ state.dataStack .emplace_back (state.data );
2470
2560
auto [res, render] = fn (args, cb);
2471
2561
if (render == HelperBehavior::RENDER_RESULT) {
2472
2562
format_to (out, res, opt);
@@ -2477,6 +2567,7 @@ renderBlock(
2477
2567
}
2478
2568
state.inlinePartials .pop_back ();
2479
2569
state.compatStack .pop_back ();
2570
+ state.dataStack .pop_back ();
2480
2571
}
2481
2572
2482
2573
void
0 commit comments