速度:是否可以嵌套使用#@和$ bodycontent的宏



我有一个看起来本质上像这样的宏:

#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的渲染过程:

  1. 渲染进入annotated-surround宏。上下文图包含:
    1. $x =字符串x
    2. $y =字符串y
    3. $bodyContent =渲染content-请注意,尚未评估其字符串输出。
  2. 第一行的渲染进入surround宏。这将上下文映射更新为:
    1. $x =旧$x =字符串x
    2. $y =字符串y
    3. $bodyContent =可渲染的annotate:$yn$bodyContent-请注意,该字符串输出尚未评估,它仍然是模板代码。
  3. 渲染输出surround的第一行,产生字符串surround:x
  4. 渲染开始评估surround的第二行,该行引用了$bodyContent
    1. 渲染$bodyContent的第一行产生字符串annotate:y
    2. 渲染开始评估$bodyContent的第二行,该行引用了$bodyContent
      1. 渲染$bodyContent的第一行产生字符串annotate:y
      2. 渲染开始评估$bodyContent的第二行,该行引用了$bodyContent
        1. 等。

解决方案是删除问题组合的一部分。全球变量和懒惰渲染是速度工作原理的基本部分,因此您无法触摸这些速度。那留下了碰撞的名字。您需要的是每个宏的$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

此版本的渲染如下:

  1. 渲染进入annotated-surround宏。上下文图包含:
    1. $x =字符串x
    2. $y =字符串y
    3. $bodyContent =渲染content-请注意,尚未评估其字符串输出。
  2. 第一行的渲染执行#set指令,在上下文映射中添加一个变量:$annotated-surround-content =当前$bodyContent =渲染content
  3. 第二行的渲染进入surround宏。这将上下文映射更新为:
    1. $x =旧$x =字符串x
    2. $y =字符串y
    3. $annotated-surround-content =旧$bodyContent =渲染content
    4. $bodyContent =渲染annotate:$yn$annotated-surround-content
  4. 渲染输出surround的第一行,产生字符串surround:x
  5. 渲染开始评估surround的第二行,该行引用了$bodyContent
    1. 渲染$bodyContent的第一行产生字符串annotate:y
    2. 渲染开始评估$bodyContent的第二行,该行参考$annotated-surround-content
      1. 渲染$annotated-surround-content产生字符串content
  6. 渲染输出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怪异,我不知道副手,现在必须继续进行其他工作。我在现在的机器上没有速度检查也无济于事,所以我无法快速尝试一下。

最新更新