我有一个看起来本质上像这样的宏:
#macro( surround $x )
surround:$x
$bodyContent
/surround:$x
#end
调用#@surround("A")bunch o' stuff#end
产生"环绕:一堆o'东西/围绕:a"预期的。调用#@surround("A")#@surround("B")more stuff#end#end
产生周围:A环绕:B更多的东西/环绕:B/环绕:A正是我想要的。
,但现在我想用另一个宏
向上堆积#macro( annotated-surround $x $y )
#@surround( $x )
annotate:$y
$bodyContent
#end
#end
#annotated-surround( "C" "note" ) stuff #end
的预期扩展为 环绕:C注释:注意事项/环境:C
...但这不起作用;我得到了带注释的体系的可怕的半无限扩展内容。
我已经在速度模板宏中读到了闭合的答案,但仍然不太知道我想做什么。
我愿意在#surround
的定义和 #annotated-surround
,但我不希望这些宏的用户看到任何复杂性。这整个想法是简化他们的生活。
只要我有您的耳朵:设置macro.provide.scope.control=true
应该是"宏中的本地名称空间"。这是什么意思?提供的名称空间是否独立于默认上下文,但是在所有宏的所有调用中共享一个这样的空间?还是为每个宏调用提供了一个单独的上下文,甚至是递归的?由于$macro.parent
,它必须是后者,对吗?
还有另一个问题。考虑以下宏:
#macro( recursive $x )
#if($x == 0)
zero
#else
$x before . . .
#set($xMinusOne = $x - 1)
#recursive($xMinusOne)
. . . $x after
#end
#end
#recursive( 4 )
屈服:
4之前。。。3之前。。。2之前。。。1之前。。。零 。。。0之后。。。0之后。。。0之后。。。4之后
现在,我了解所有这些出现的" 0":只有一个全局$ x,所以分配给它递归调用将其砸碎,并且无法恢复。但是地球到底是最后的" 4"来自?就此而言,我的第一个"环绕"宏是如何进行任意深度的?它的最终$ x怎么不会被内部呼叫砸碎?
很抱歉成为prolix,但是我无法在此问题上找到清晰的文档。
问题是全局变量,名称碰撞和懒惰渲染的组合。
让我们浏览#@annotated-surround( "x" "y" )content#end
的渲染过程:
- 渲染进入
annotated-surround
宏。上下文图包含:-
$x
=字符串x
-
$y
=字符串y
-
$bodyContent
=渲染content
-请注意,尚未评估其字符串输出。
-
- 第一行的渲染进入
surround
宏。这将上下文映射更新为:- 新
$x
=旧$x
=字符串x
-
$y
=字符串y
-
$bodyContent
=可渲染的annotate:$yn$bodyContent
-请注意,该字符串输出尚未评估,它仍然是模板代码。
- 新
- 渲染输出
surround
的第一行,产生字符串surround:x
。 - 渲染开始评估
surround
的第二行,该行引用了$bodyContent
。- 渲染
$bodyContent
的第一行产生字符串annotate:y
。 - 渲染开始评估
$bodyContent
的第二行,该行引用了$bodyContent
。- 渲染
$bodyContent
的第一行产生字符串annotate:y
。 - 渲染开始评估
$bodyContent
的第二行,该行引用了$bodyContent
。- 等。
- 渲染
- 渲染
解决方案是删除问题组合的一部分。全球变量和懒惰渲染是速度工作原理的基本部分,因此您无法触摸这些速度。那留下了碰撞的名字。您需要的是每个宏的$bodyContent
,以不同的名称引用。通过在调用任何其他宏之前将其分配给具有唯一名称的新变量,并在任何调用宏的身体中使用新变量,可以很容易地实现这一点:
#macro( surround $x )
surround:$x
$bodyContent
/surround:$x
#end
#macro( annotated-surround $x $y )
#set( $annotated-surround-content = $bodyContent )
#@surround( $x )
annotate:$y
$annotated-surround-content
#end
#end
此版本的渲染如下:
- 渲染进入
annotated-surround
宏。上下文图包含:-
$x
=字符串x
-
$y
=字符串y
-
$bodyContent
=渲染content
-请注意,尚未评估其字符串输出。
-
- 第一行的渲染执行
#set
指令,在上下文映射中添加一个变量:$annotated-surround-content
=当前$bodyContent
=渲染content
。 - 第二行的渲染进入
surround
宏。这将上下文映射更新为:- 新
$x
=旧$x
=字符串x
-
$y
=字符串y
-
$annotated-surround-content
=旧$bodyContent
=渲染content
-
$bodyContent
=渲染annotate:$yn$annotated-surround-content
- 新
- 渲染输出
surround
的第一行,产生字符串surround:x
。 - 渲染开始评估
surround
的第二行,该行引用了$bodyContent
。- 渲染
$bodyContent
的第一行产生字符串annotate:y
。 - 渲染开始评估
$bodyContent
的第二行,该行参考$annotated-surround-content
。- 渲染
$annotated-surround-content
产生字符串content
。
- 渲染
- 渲染
- 渲染输出
surround
的第三行,产生字符串/surround:x
。
最终渲染输出为surround:x annotate:y content /surround:x
。可以通过将此类替换应用于另一个宏调用内容中的所有发生的$bodyContent
的出现,每次使用从宏名称派生的变量名称来确保唯一性,可以将这种方法推广。它不适合递归宏,而没有额外的东西来区分每个嵌套的调用。
涉及范围设置,所做的只是将$macro
对象添加到上下文中,该对象是每个宏调用所独有的,可以用作映射。如果将$macro.myVar
设置为两个嵌套宏调用中的每个呼叫中的每一个,则外部宏的值将在内部完成时不变。但是,这对$bodyContent
问题无济于事,因为呈现宏的$bodyContent
内的任何引用$macro
的任何引用将在渲染时解决到最内向的宏。
关于#recursive( 4 )
的最后4个,这是来自具有本地范围并通过名称传递的宏观参数的组合。对于#recursive
的最外部调用以外,参数$x
是对全局上下文变量$xMinusOne
的引用 - 当它们渲染after
线时,$x
的使用实际上可以解决全局上下文中$xMinusOne
的当前值。对于最外面的调用,它是常数值4
,并且在完成时,内部调用的参数不在范围,因此,当最外部的一个人到达最终线时,它又回到了4
。
从最简单的macro.provide.scope.control = true开始,肯定会为每个 em> acro>宏调用创建一个单独的$ acro范围对象。否则,正如您指出的那样,$ acro.parent会是胡说八道。"范围控件"的全部要点是为所讨论的VTL块类型提供一个明确的名称空间。您甚至可以进行abtrovide.scope.control = true以自动创建一个$环绕范围。
在您的第一个问题上,我对正在发生的事情有些困惑。呼叫#@Annotate-surround和对#@围绕的嵌套调用都将使$ BodyContent参考可用。我是对的,这是使用"错误" $ BODYCONTENT正在使用?$ BODYCONTENT参考应属于最近的块宏调用。要引用内部宏内的外部宏的$ bodycontent,您可能需要#set($ acro.bodycontent = $ bodycontent),然后在内部,通过$ macro.parent.body.bodycontent
使用它至于#Recursive怪异,我不知道副手,现在必须继续进行其他工作。我在现在的机器上没有速度检查也无济于事,所以我无法快速尝试一下。