7
\$\begingroup\$

I have a block with an undefined data and a pair of REP [code]:

arr: [
    REP ["code1"]
    | 'something
     "f"
    | [ f | 3 ]
    REP [40 + 2]
    ]

The pair can be everywhere in the arr.
I replace every occurrence of a pair REP [code] with a value from evaluating the code.

t: true

while [t == true] [
    f: find arr 'REP
    either f [
        t: true
        change/part f do first next f 2
        ][
        t: false
        ]
]

Does this code follow common best practices? Does it have some security issues? Is there any problems with a performance using this code?

\$\endgroup\$

2 Answers 2

7
\$\begingroup\$

Does it have some security issues?

Well, you're doing an evaluation of code. If that code comes from a foreign source somehow, that could be a problem. If it's your code, it's no more dangerous than anything else.

But as you wrote it, it does have the property that it can infinite loop within itself. Because after your substitution you start at the beginning again:

arr: [REP [quote REP]]

That's a stable state, but you could keep growing without bound too.

My answers will assume you didn't want this.

Does this code follow common best practices?

It's C-like. Rebol is a great language for writing bad C code. :-)


Using COMPOSE is the obvious answer here:

>> compose [
    ("code1")
    | 'something
    "f"
    | [ f | 3 ]
    (40 + 2)
]
== ["code1" | 'something "f" | [ f | 3 ] 42]

And if you're worried your content contains parens that you want to leave in place, you can quote them:

>> compose [1 (quote (2 3)) 4]
== [1 (2 3) 4]

But if you want to work with the input as written, there's a lot of approaches.


The most "sensible" way--that the wiki regarding features in R3-Alpha suggests would work (but doesn't seem to)--would be:

>> parse arr [
    any [
        change ['REP blk: block!] (do blk/1)
        |
        skip
    ]
]

Which would more or less capture the essence of the task. Any number of times, try and match a pattern of REP followed by a BLOCK!, marking the position prior to the block. Then substitute the execution of do blk/1 for that code". Otherwise skip and keep looking.

It gets almost there but misses the execution. So it splices in do blk/1 literally:

== [do blk/1 | 'something "f" | [f | 3] do blk/1]

Try reporting a bug to...whoever works on that. Or maybe to Red. :-)


I've seen you mention you don't like UNTIL, so here's a FORALL-based solution...

forall arr [
    if arr/1 = 'REP [
        remove arr
        arr: back change arr do arr/1
    ]
]

(Note: I think forall should be called FOR-NEXT, because all it does is advance the argument until the end via NEXT. You can modify it during the loop, and then it will run NEXT again. It will be reset at the end.)


As a talking point on your code: Note that there are two FALSE? values (NONE! and LOGIC! false), and everything else is TRUE? (except unset which is neither). So it's not that common to work with literal true and false. NONE is almost always better as a contrast to some value you are interested in.

Just to show what kinds of tricks are available in an "imperative-series-assembly-language" with a WHILE approach: if we are walking along the array replacing, we might use the position back from FIND not being NONE! to cue continuation:

head while [arr: find arr 'REP] [
    arr: change/part arr do arr/2 2
]

It's able to work because change is designed for chaining, so it returns the point after the replacement. Also because WHILE returns the last evaluation of the body. So even though the condition wipes out the value of arr when the find comes back with NONE!, it can be recovered because the last time the body evaluated it was the end position of the CHANGE...and you can seek back to the head from that.


So there are a few possibilities.

\$\endgroup\$
2
  • \$\begingroup\$ Isn't forall very similar to while? After removing 1 element (let's say 1) and after inserting 3 elements (2 REP []) it will be on a position just before REP - so it can create infinite loop too. \$\endgroup\$ Dec 25, 2015 at 10:59
  • \$\begingroup\$ @DarekNędza The forall was buggy, sorry about that, you do need to update the arr position (in this case, one step back so it bumps to the next when it hits the loop) \$\endgroup\$ Dec 25, 2015 at 11:07
5
\$\begingroup\$

To add to hostilefork's great answer, my preference with such things is to use Parse.

As hostilefork as pointed out Rebol 3 Parse has the Change keyword. Parse's Change will take a word argument as a replacement value:

parse b: [x y] [(value: [1 2 3]) change 'x value] ?? b
b: [1 2 3 y]
== [1 2 3 y]

But note that it has only changed the first value not the whole matched pattern. While Parse's Change has a mode for ONLY, I do not think it has a mode for PART. So I think that you cannot use Change for your example.

Parse does have Remove and Insert however.

You didn't say if nested blocks should also be modified. If so, you would need to use the INTO keyword and call the rule recursively. Putting it all together:

rule: [
    any [
        and block!
        into rule
        |
        remove ['REP blk: block! (value: do blk/1)]
        insert only value
        |
        skip
    ]
]

parse arr rule

For Rebol 2, Parse does not have the Change, Remove, Insert keywords. In Rebol 2, INTO assumes any-block!. So this would be the alternative for Rebol 2:

rule: [
    any [
        into rule
        |
        position: 'REP block! (
            position: change/only/part position do position/2 2
        ) :position
        |
        skip
    ]
]
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.