Rule Annotations

Annotations are written as #{/* annotation content */} before rules and change the way rules are executed or the context in which they run.

Annotations can be stacked and combined with the rule combinators in the previous section.

Filter Annotation

The filter annotation applies the rule only to parts of the input word which match a given matcher.

rule filtered = #{filter vow} 'a' -> () in _ #;

This example removes the last vowel in the word if it is an a, regardless of whether any consonants follow. For example aback becomes abck, but abeck remains unchanged.

When using filter rules, you should avoid using matchers that can match multiple adjacent symbols should be avoided. If a match covers multiple symbols that aren’t adjacent in the unfiltered word, there is no clear way to map the replacement back, and tadpole considers this an error.

Per-Word Annotation

The word annotation is only relevant for inputs with mutiple words in one and applies its inner rules to each of them separately. This is mostly relevannt in the presence of fallback rules.

rule per_word = #{word} {
  'a' -> 'b';
  else
  'b' -> 'c';
}

This will map the input ab bc to bb cc.

Converging Annotation

The converging annotation applies repeatedly until an application finds no matches.

rule expand = #{converging} 'a' -> 'b' in 'b' _ or _ 'b';

This will transform aaabaaa to bbbbbbb, attempting to apply the rule four times and finding no matches on the fourth.

When using converging rules, care must be taken to prevent an infinite loop where the rule itself ensures that it encounters a match in the next cycle. For example, the following rule would cause an infinite loop for any inputs containing a or b:

rule looping = #{converging} {
  'a' -> 'b';
  'b' -> 'a';
}

Tadpole will abort and raise an error if a converging rule runs more than 1000 times.

Directional Annotations

Directional annotations also repeatedly apply a rule, but do so by stepping through the input one symbol at a time and only searching for matches at the current position. Two of these annotations are available, #{ltr} running left-to-right and #{rtl} running right-to-left.

rule expandl = #{ltr} 'a' -> 'b' in 'b' _ or _ 'b';
rule expandr = #{rtl} 'a' -> 'b' in 'b' _ or _ 'b';

Th examples above transform aaabaaa to aabbbbb and bbbbbaa respectively.

Scope Annotation

The scope annotation allows adding additional definitions that are only valid for this one rule. So far, we have only discussed class and segmentation definitions, so the use of this is limited, but with more definitions discussed later you may see more applications of this. It can be particularly useful with configuration directives.

rule local_class = #{with
  class local = 'a' 'b'; // class local has not been defined elsewhere
} {
  local -> ();
}