A Close Look at Rule Application

This chapter will take a close look at the sequence of events that happens during rule application.

We will use a relatively common example that may show up in inputs and shows a few interesting concepts in action:

class vow = 'a' 'e' 'i' 'o' 'u';
feature length = +long -long default -long;
modifier (soft after) 'ː' = [+long];

rule long_vowels = (vow)@s <s> -> [+long]@s;

To make things more intersting, we will apply it to the word toooonai. That’s two double vowels in a row.

Matching

Class Match

First, the rule tries the matcher specification at each position in the input word.

The top level sequence matcher calls down to its inner matchers one after the other, the match binder ( )@s in turn calling down to the class matcher.

The first position fails immediately because t is not in class vow. At the second position, vow matches and produces a partial match result:

rule long_vowels = (vow)@s <s> -> [+long]@s;
//                  ^^^
// match result:
//     toooonai
//      ^
// binding table: empty

Binding Creation

The match binder uses the result of its inner matcher to create the binding:

rule long_vowels = (vow)@s <s> -> [+long]@s;
//                 ^---^^^
// match result:
//     toooonai
//      ^
// binding table:
//     "s": symbol('o')

Binding Inserter

Since its first inner matcher has returned a result, the outer sequence matcher tries to match the next inner element, the binding inserter. This successfully matches the second o, which is returned and combined with the previous result.

rule long_vowels = (vow)@s <s> -> [+long]@s;
//                 ------- ^^^
                           'o'
// match result:
//     toooonai
//      -^
// binding table:
//     "s": symbol('o')

Further Matches

The same sequence of events occurs at the next two positions in the word. Then n again fails to match the vowel. At position 7 (0-indexed), the first part of the sequence matcher succeeds, but the binding inserter does not match:

rule long_vowels = (vow)@s <s> -> [+long]@s;
//                 ------- ^^^
                           'a'
// match result:
//     toooonai
//           -x
// binding table:
//     "s": symbol('a')

Match Results

The matching process concludes with three results:

Result012
Match Span
toooonai
 ^^
toooonai
  ^^
toooonai
   ^^
Binding Table
"s": symbol('o')
"s": symbol('o')
"s": symbol('o')

Match Selection

The rule then steps through the match results and selects them as follows:

Replacement

For each match result the replacement is generated. Replacement generation does not receive information about match position, only the binding table.

rule long_vowels = (vow)@s <s> -> [+long]@s;
//                                ^^^^^^^^^
// binding table:
//     "s": symbol('o')

The feature replacer performs the following steps:

Note that this would not work for a sequence binding, since modifiers can only be applied to individual symbols, nor for index bindings since those do not contain any symbol information, nor to syllable bindings since the replacer is a symbol feature matrix, not a syllable feature matrix. A feature binding would work, provided we added the feature and symbol definitions to allow the symbol and feature library to construct 'oː' from the features in the binding.

The final replacement results are as follows:

Result01
Match Span
toooonai
 ^^
toooonai
   ^^
Replacement
'oː'
'oː'

Output Generation

Finally, the output word is generated by stepping through the input word and outputting the replacement if there is any, or the original symbols otherwise.

toooonai
   ^^-- 'oː'
 ^^---- 'oː'

toːoːnai

We are left with the final result with double vowels having been replaced by vowels with long vowel marks. note that though the output is the same number of characters, in tadpole terms it has fewer symbols than the input since 'o' 'o' was converted to 'oː'.