REBOL [
    Title: "RSP Preprocessor"
    Date: 25-Jul-2010
    Author: "Christopher Ross-Gill"
    Notes: "Extracted from QM"
    Type: 'module
    File: %rsp.r
    Exports: [load-rsp render render-each]
]


load-rsp: use [prototype to-set-block][
    prototype: context [
        out*: "" prin: func [val][repend out* val]
        print: func [val][prin val prin newline]
    ]

    to-set-block: func [block [block! object!] /local word][
        either object? block [block: third block][
            parse copy block [
                (block: copy [])
                any [set word word! (repend block [to-set-word word get/any word])]
            ]
        ]
        block
    ]

    func [[catch] body [string!] /local code mk][
        code: make string! length? body

        append code "^/out*: make string! {}^/"
        parse/all body [
            any [
                end (append code "out*") break
                | "<%" [
                    "=" copy mk to "%>" (repend code ["prin (" mk "^/)^/"])
                    | [#":" | #"!"] copy mk to "%>" (repend code ["prin build-tag [" mk "^/]^/"])
                    | copy mk to "%>" (repend code [mk newline])
                    | (throw make error! "Expected '%>'")
                ] 2 skip
                | copy mk [to "<%" | to end] (repend code ["prin " mold mk "^/"])
            ]
        ]

        func [args [block! object!]] compose/only [
            args: make prototype to-set-block args
            do bind/copy (throw-on-error [load code]) args
        ]
    ]
]

render: use [depth*][
    depth*: 0 ;-- to break recursion

    func [
        [catch] rsp [file! url! string!]
        /with locals [block! object!]
    ][
        if depth* > 20 [return ""]
        depth*: depth* + 1

        rsp: case/all [
            file? rsp [rsp: read rsp]
            url? rsp [rsp: read rsp]
            string? rsp [
                throw-on-error [rsp: load-rsp rsp]
                throw-on-error [rsp any [locals []]]
            ]
        ]

        depth*: depth* - 1
        rsp
    ]
]

render-each: func [
    'items [word! block!]
    source [series!]
    body [file! url! string!]
    /with locals /local out
][
    out: copy ""
    locals: append any [locals []] items: compose [(items)]
    foreach :items source compose/only [
        append out render/with body (locals)
    ]
    return out
]