[Class]
rewrite

Generates elements according to user specified rewrite rules. Rewrite rules are expressed in terms of nodes and node identifiers. Two different styles of rule specification are supported:

  1. Context-free rules

    Context-free rules are rules that depend only on the nodes they are associated with in the pattern. The element is specified as a node list similar to the graph pattern:

    (element {property value}+)

    where element is the element to return from the pattern followed by one or more property value pairs:

    :id datum
    Specifies a unique identifier for the node in the pattern. If omitted, the identifier defaults to the element itself. It is good practice to provide each node with an explicit id.
    :-> {id | ({id}+) | pattern | false}
    Sets the rewrite expression for the node. The value may be a single identifier, a list of identifiers, a pattern or false. If the value is false the node is terminal, i.e.. it produces no successor(s) in the pattern's next generation. If the value is a pattern then the pattern is read to produce a successor term each time the node is rewritten. Otherwise the value should be an id or list of ids that identify successor node(s) in the next generation.
  2. Context-sensitive rules

    Context-sensitive rules are associated with the entire pattern rather than with the nodes in the pattern. This means that each rule may reference more than one node in its left-hand side and there may be more (or fewer) rules than there are nodes. The list of rules is interpreted as an ordered set: to produce a new generation, nodes in the current generation are matched against the rules to find the first rule whose left-hand side is true (matches). This rule is then "triggered" and the id(s) in its right-hand side are added to the next generation.

    Node specification in a context sensitive rewrite pattern are simlar to the context-free pattern except that:

    • the :-> marker appears in rules, not in node descriptions
    • if the node's :id value is the same as the element then it can be specified in place of a node list

    Each rule in a context-sensitive patter is a list of the form:

    ({id}+ :-> {id}*)

    The :-> marker divides each rule into two sides (Table 1). The left-hand side of the rule defines the "matching target" and the right-hand side defines the rewrite succession. Either or both sides may contain more than one id. If the left-hand side of the rule is a single id then the rule matches any node with the same id. If the left-hand side has more than one id (a context-sensitive rule) then the rule matches if the "strict predecessor" in the left-hand side matches the current node and the ids around the strict predecessor match match the nodes around the current node. The strict predecessor id is marked in the left-hand side by making it a single element list. Every context rule must contain exactly one strict predecessor in its left hand side.

    Table 1.Three examples of context sensitive rules: Note that the right-hand side may be empty and that the left-hand side may use the wild card * to matches any single element in the current generation.

    RuleDescription
    (1 (1) 2 :-> 3) 1 rewrites to 3 wherever it is preceded by itself and followed by 2
    (1 * (2) :-> 1 2) 2 rewrites to 1 2 whenever 1 occurs two positions earlier
    (5 (3) 3 4 :->) 3 rewrites to nothing if preceded by 5 and followed by itself and 4

rewrite supports the following slot initializations:

:of list
The list of nodes constituting the pattern data. For context-free patterns each element in the list must be proper node list as decribed above. For context-sensitive patterns a node list only has to be provided for those nodes containing patterns as elements, otherwise only the node id needs to be specified.
:intially list
A list of ids that consititute the initial generation to return from the pattern. The default generation contains only first node listed in the pattern's data.
:rules list
Sets the rewrite rules of a context-sensitive pattern to list.
:generations number
Sets the number of generations to generate. After number of generations the pattern simply reuses the final generation's elements as if the pattern were a cycle.

See generic pattern initializations for documentation on additional keyword initializations to the pattern.

Examples:

Example 1. Context free melody.

(define pat1 (new rewrite :of '(( 2 :-> ( 2 -2 2))
                                (-2 :-> (-2 -2 2)))))

(define (stepper pat end rate from)
  (let ((pat (new range :initially (keynum from) 
                  :stepping pat)))
    (process while (< (now) end)
             output (new midi :time (now)
                         :keynum (next pat)
                         :duration rate)
             wait rate))

(events (list (stepper 20 (rhythm 's 100) 'c5)
              (stepper 20 (rhythm 'te 100) 'f4)
              (stepper 20 (rhythm 'e 100) 'bf3)
              (stepper 20 (rhythm 'tq 100) 'ef3))
        "test.mid")
 "test.mid"

Example 2. Context sensitive harmony.

(define pat1
  (new rewrite :of '(((0 4 7) :id f)
                     ((0 3 7) :id g)
                     ( 1 :id +)
                     (-1 :id -))
                   :initially '(f + f - g - f)
                   :rules '((f :-> f f - g - - g - f)
                            (g :-> g f + f)))

(define (lsystem-harmony lsys len rhy dur)
  (let ((term #f)
        (knum 60))
    (process repeat len
             do
             (set! term #f)
             (loop until term
                   for x = (next lsys)
                   do
                   (if (list? x)
                     (set! term x)
                     (set! knum (fit (+ knum x) 48 72))))
             each c in (transpose term knum)
             output (new midi :time (now)
                         :keynum c
                         :duration dur
                         :amplitude .7)
             wait rhy)))

(events (lsystem-harmony 200 .2 .35)
        "test.mid")
 "test.mid"

See also: