Composition via Replacement/Substitution


#1

I recently closed a Rebol issue where I was pitching COMPOSE, as I feel that the main needs are now served by SPACED, UNSPACED, and DELIMIT.

But Ladislav linked to a wiki article which contains some stuff which might be worth discussing:

Replacement.wiki

It mentions INLINE, BUILD, and SUBSTITUTE. I’d previously heard of BUILD, but never used it. The link is down, but here is %build.r from the Internet Archive:

Rebol [
    Title: "Build"
    Author: ["Ladislav Mecir" "Brian Hawley"]
    File: %build.r
    Date: 9-Apr-2007/23:51:01+2:00
    History: [
        7/apr/2003/19:02 {using INSERT and ONLY keywords}
        30/Oct/2004/12:55 {intermediate version - alpha}
        31/Oct/2004/9:49 {intermediate - word - insert/only, :word - insert}
        4/Nov/2004/6:55 {word - insert and evaluate, :word - insert/only}
        4/Nov/2004/15:00 {/with - refinement name}
        2-May-2006/10:51:09+2:00 {
            lit-path preservation,
            'ENCL and 'DTTO made local
            examples added
        }
        2-May-2006/13:08:57+2:00 {one more lit-path related change by Brian}
        2-May-2006/13:36:38+2:00 {example correction by Brian}
        9-Apr-2007/23:51:01+2:00 {function description improved}
    ]
    Purpose: {Build a block comfortably}
]

comment [
    ; Usage

    ; This dialect looks very human-readable, doesn't it?
    ; (I found a lot of possible usages for it).

    build/with [x (y) [z] :u v] [
        x: 1 y: 1 + 1 z: 1 + 2 u: [contents of u] v: [contents of v]
    ] ; == [1 (2) [3] [contents of u] contents of v]

    ; You can easily "stack" it like:

    build/with [let's see what] build/with [what: [the rise of the (y)]] [
        y: 'sun
    ] ; == [let's see the rise of the (sun)]
    
    ; or use the evaluation feature like:
    
    build [ins 2 + 2] ; == [4]
    build [only reduce [3 * 4 5 + 6]] ; == [[12 11]]
    
    ; you can even define and use your own functions the same way
    ; using the /WITH refinement
]

use [encl dtto spc] [
    ; patch the IN function if needed
    spc: find/only third :in reduce [word!]
    if spc [change spc/1 any-word!]

    encl: func [
        {"a value in a block" function}
        value [any-type!]
    ] [head insert/only copy [] get/any 'value]

    dtto: func [
        {a "pass the value over" function}
        value [any-type!]
    ] [return get/any 'value]

    build: func [
        {
            Build a block comfortably.
            Using 'INS and 'ONLY "keywords" by default.
            INS inserts the following value (similar to INSERT)
            ONLY inserts a series as one item (similar to INSERT/ONLY)
        }
        block [block! paren! path! set-path! lit-path!]
        /with {use the "keywords" given below}
        keywords [block!]
        /local context inner
    ] [
        keywords: any [keywords [only: :encl ins: :dtto]]
        context: make object! keywords
        inner: func [block /local item item' pos result] [
            result: make :block length? :block
            parse :block [
                any [
                    pos: set item word! (
                        either all [item': in context item item <> 'self] [
                            change pos item'
                            set/any [item pos] do/next pos
                            insert tail :result get/any 'item
                        ] [insert tail :result item pos: next pos]
                    ) :pos | set item get-word! (
                        either all [item': in context item item <> 'self] [
                            insert/only tail :result get/any item'
                        ] [insert tail :result item]
                    ) | set item [
                        block! | paren! | path! | set-path! | lit-path!
                    ] (
                        insert/only tail :result inner :item
                    ) | set item skip (insert/only tail :result get/any 'item)
                ]
            ]
            :result
        ]
        inner :block
    ]
]