Extending Build-markup with repeat refinement

2020-03-31 07:13发布

问题:

I tried to add a repeat refinement to build-markup function using the previous answer: How to bind to foreach context?

build-markup: func [
    {Return markup text replacing <%tags%> with their evaluated results.} 
    content [string! file! url!] 
    /repeat block-fields block-values
    /quiet "Do not show errors in the output." 
    /local out eval value
][

  either not repeat [
      content: either string? content [copy content] [read content] 
      out: make string! 126 
      eval: func [val /local tmp] [
          either error? set/any 'tmp try [do val] [
              if not quiet [
                  tmp: disarm :tmp 
                  append out reform ["***ERROR" tmp/id "in:" val]
              ]
          ] [
              if not unset? get/any 'tmp [append out :tmp]
          ]
      ] 
      parse/all content [
          any [
              end break 
              | "<%" [copy value to "%>" 2 skip | copy value to end] (eval value) 
              | copy value [to "<%" | to end] (append out value)
          ]
      ]
    ][        
        probe :block-fields
        foreach :block-fields block-values [
          print get pick :block-fields 1
          print get pick :block-fields 2
        ]
    ] 
    out
]

c: [a b]
template: "<%a%> <%b%>"
build-markup/repeat template :c [1 2 3 4]

Output is not what I want:

>> c: [a b]
== [a b]
>> template: "<%a%> <%b%>"
== "<%a%> <%b%>"
>> build-markup/repeat template :c [1 2 3 4]
[a b]
1
1 a b
1
1 a b

whereas I would have expected

1
2
3
4

So how should I correct ?

回答1:

For:

words: [num]
vals: [1 2 3]

When you use foreach :words, you are creating a new context for which the repeat block will be bound to. The word! contents of :words are not actually bound to this new context. The values you are getting suggest 'a is globally set to 1 and 'b is set to [a b]. To illustrate:

>> num: 9                 
== 9
>> words: [num]           
== [num]
>> foreach :words vals [
[    probe get 'num         
[    probe get first :words 
[    ]                      
1
9
2
9
3
9
== 9

To work around this, try to picture that for each iteration of the loop, the block that is executed is 'bind-ed to the loop context. You can preempt the bind like this:

foreach :words vals probe compose/only [
    probe get first (words)
]

(probe left in for illustrative purposes)



回答2:

seems to work:

build-markup: func [
    {Return markup text replacing <%tags%> with their evaluated results.} 
    content [string! file! url!] 
    /repeat block-fields block-values
    /quiet "Do not show errors in the output." 
    /local out eval value
][

  out: make string! 126 

  either not repeat [
      content: either string? content [copy content] [read content] 

      eval: func [val /local tmp] [
          either error? set/any 'tmp try [do val] [
              if not quiet [
                  tmp: disarm :tmp 
                  append out reform ["***ERROR" tmp/id "in:" val]
              ]
          ] [
              if not unset? get/any 'tmp [append out :tmp]
          ]
      ] 
      parse/all content [
          any [
              end break 
              | "<%" [copy value to "%>" 2 skip | copy value to end] (eval value) 
              | copy value [to "<%" | to end] (append out value)
          ]
      ]
    ][        

        actions: compose/only [
            set in system/words (to-lit-word pick (block-fields) 1) get pick (block-fields) 1
            set in system/words (to-lit-word pick (block-fields) 2) get pick (block-fields) 2
            probe get in system/words (to-lit-word pick (block-fields) 1)
            probe get in system/words (to-lit-word pick (block-fields) 2)
            append out build-markup content

        ]
        foreach :block-fields block-values actions        
    ] 
    out
]

template1: {    <td><%a%></td><td><%b%></td>
}
template2: {  <tr>
<%build-markup/repeat template1 [a b] [1 2 3 4]%>
  </tr>
}
template3: {<table>
<%build-markup/repeat template2 [a b] [1 2 3 4 5 6]%>
</table>}
build-markup template3

output:

== {<table>
  <tr>
    <td>1</td><td>2</td>
    <td>3</td><td>4</td>

  </tr>
  <tr>
    <td>1</td><td>2</td>
    <td>3</td><td>4</...
>

>



标签: rebol