Features, Symbols and Modifiers

Defining Features

Features are defined by a name, a list of values, and optionally a default value. Unlike other sound change engines, tadpole aims to be as unopinionated about the relationships of feature names and values as possible.

feature feat1 = fval1 +fval2 -fval3 *fval4 default fval1;

Feature names and values must be unique across all features, since feature values are referred to without the context of the feature they belong to. In addition to the rules for other names, feature names can begin with +, -, or * to better support common linguistic notation.

The default value is optional. If no default is defined, the feature will default to an empty value which may be more difficult to match against.

Defining Symbols

To practically use features, they need to be assigned to symbols. A symbol definition is a symbol string associated with a feature matrix:

symbol 'a' = [open front -rounded vowel];
symbol 'b' = [+voiced bilabial plosive consonant];

Features with no value specified in a matrix will use the default value. Listing more than one value of one feature does not currently cause an error but may not result in valid behavior.

Defining Modifiers

Modifiers attach to symbols and add to or modify their features. There are two types of symbols, hard and soft modifiers. These differ in how strongly they bind to the symbols: For a symbol to match a symbol matcher, they must have the same hard modifiers, but may differ in soft modifiers.

Modifiers also have a position parameter, which defines how they are shown when the symbol is output to a string. There are three positions:

A modifier is defined with its parameters, modifier string, and a feature matrix.

modifier (soft after) 'ː' = [+long];
modifier (hard at 1) '\u{0235}' = [-voiced];

Since modifiers often take the form of combining diacritics, they can display in confusing ways when placed in quotes. In this case, some people may prefer using the '\u{}' syntax for specifying characters by unicode values to keep the code clean.

The default type and position for modifiers is hard after. Default values can be omitted from the parameters, and if both parameters are default the parentheses can be omitted as well.

Using Symbols with Modifiers

Since by default no symbols or modifiers are defined, the default behavior of tadpole is to use symbols written in symbol matchers as-is. If you are using modifiers, this is not what you want since this will treat modifiers as part of the core symbols rather than as modifiers attached to it.

To allow symbol matchers to use the symbol and modifier library to properly handle modifiers, there is a configuration option that can be enabled by placing the directive

config validate_symbols = true;

before any rules using the symbols. This will however cause errors if any symbols present aren’t in the symbol library. Symbol validation can be temporarily disabled with a #{with config validate_symbols = false} annotation.

Configuration options will be discussed further in a later chapter.

Matching Symbols with Modifiers

By default, symbol matchers require hard modifiers to match and ignore soft modifiers. To change this behavior, you can use the strict and loose match operators. These are written as postfixed (!) and (~) respectively and affect all matchers nested within the matcher they are applied to. A strict matcher requires both soft and hard modifiers to match, while a loose matcher just requires the base symbol to match and ignores modifiers.

Stripping Modifiers

If a content binding needs to be inserted without soft modifiers, postfix the binding replacer with the (~) operator. To strip all modifiers, double the operator.

rule remove_vowel_mods = (vow)@s -> <s>(~)(~);

Feature Matchers

Feature matchers are written as feature matrices in square brackets.

rule feat_matcher = [+rounded] -> ();

This will delete all the symbols with the +rounded feature.

You can also match against feature values not being present with the ! operator:

rule not_feature = [!vowel] -> ();

This will delete all non-vowels.

Features and Bindings

Feature matrices can also be used as a replacer, however they need a new type of binding, a feature matrix binding, which is generated by feature matchers. A feature replacer will use the full feature matrix of the matched symbol (including any modifiers) as a basis and overwrite any values specified in its own feature matrix. The symbol library is then searched for a symbol (or combination of symbol and modifiers) that has the resulting feature matrix. If no symbol is found, tadpole asserts an error.

rule feature_replacer = [+rounded vowel]@f -> [-rounded]@f;

This example removes rounding from all vowels.

There is also a second kind of feature binding containing just one value, which can be created by binding to a feature name in the feature matrix. This kind of binding can be inserted into feature matrices similarly to how content bindings can be inserted into matchers and replacers.

rule feature_transfer = [-voiced place@f1]@m [-voiced <f1> manner@f2] -> [<f2>]@m;

This rule will match any two unvoiced sounds articulalted in the same place and replace them with a copy of the first sound, but with the manner of articulation of the second.

Feature matrix replacers can also accept symbol bindings. Used in this way, the feature matrix replacer attempts to find a set of modifiers to fulfil the requested features.

rule sym_features = vow@s -> [+long]@s in _ #;

This example will lengthen vowels at the end of the word.