Contents:

1. Introduction

Despite the huge power of the parse function, and the ability to define rules that can be reused inside other rules (they are just blocks), there is a lack of generic, reusable rules that solve very common problems. The main reason for this is the inability of passing parameters to rules, which means that only the most simple rules can really be made reusable across different "callers". This module proposes a possible solution.

2. Overview

Basically, we create a "global" stack, and use it to pass arguments to rules. In other words, we add a stack to the parse dialect so that it can be used to pass arguments around.

Overview

stack: [ ]
push-arguments: func [
 "Push values into the arguments/results stack"
 values [block!]
] [
 repend stack values
]
push-result: func [
 "Push one value into the arguments/results stack"
 value
] [
 append/only stack :value
]
pop-arguments: func [
 {Pop and set values from the arguments/results stack} [catch]
 names [block!] "Words to set to the values from the stack"
] [
 if greater? length? names length? stack [
  throw make error! "Not enough arguments in the stack"
 ]
 set names names: skip tail stack negate length? names
 clear names
]
pop-result: func [
 "Pop one value from the arguments/results stack" [catch]
] [
 throw-on-error [
  also last stack
   remove back tail stack
 ]
]

Then, make-rule is just a shortcut for the common case of defining a rule with some arguments:

Overview +≡

make-rule: func [
 {Create a rule that takes arguments (via PUSH-ARGUMENTS)}
 spec [block!] "Argument spec (similar to function spec)"
 body [block!] "PARSE rule"

 /local make-rule's locals
] [
 Create a rule taking the arguments defined in spec
]

Warning!

The body argument is modified, so if you plan to reuse it you must use copy.

3. Examples

Say we have the rule:

Examples

[#"<" copy name to #">" skip]

It would be nice to reuse this in a number of places; however, this means that name has to be global so that it is available to all rules that call it. If we could provide a word as argument... now we can!

Examples +≡

make-rule [word /local name] [
 #"<" copy name to #">" skip (set word name)
]

You can then call Examples this way:

(push-arguments ['tag-name]) Examples (if tag-name = "a" [...])

4. Create a rule taking the arguments defined in spec

Create a rule taking the arguments defined in spec

ctx: clear [ ]
local?: no
args: copy [ ]
parse spec [
 some [
  set word word! (
   append ctx to set-word! word
   unless local? [append args word]
  )
  |
  /local (local?: yes)
  |
  skip
 ]
]
ctx: context append ctx none
bind args ctx
bind body ctx
head insert/only body to paren! reduce ['pop-arguments args]

4.1 make-rule's locals

make-rule's locals

ctx args word local?