Modeling Rules¶
As a reminder, this is how ordinary rules are usually structured:
- C is replaced by X (when L comes before C) (when C comes before R).
- C is called X (when L comes before C) (when C comes before R).
- X is inserted after L (when L comes before R).
- C does not accept rule Y (when L comes before C) (when C comes before X).
We can rewrite these templates into a more general form:
When we see some context window W, perform some operation O.
where W is an arbitrary set of contexts and O is an abstraction for some arbitrary change, such as:
- replacing C with X
- calling C by the name of X
- inserting X after L
- blocking rule Y on C
With this general form in mind, we can decompose a rule model into two parts:
Or in other words: filters test and operators transform.
Filters¶
A Filter is a callable object that accepts a state
and index, performs some test on state[index], and returns True or
False as appropriate. For example, the samjna
filter returns whether or not state[index] has some particular samjna.
If all of a rule’s filters return True, then the rule has scope to apply.
In older version of the code base, filters were functions that accepted an
Upadesha and returned True or False. This
approach changed for two reasons:
- A few filters require global access to the state. If they accept just a single term, there`s no way to get information on the rest of the state. So filters were changed to accept state-index pairs.
- Usually, a rule`s filter is a combination of two other filters. One nice
way to do this is to use Python’s unary operators (e.g.
&,|). But custom operators are supported only for class instances. So filters were changed to class instances.
Parameterized filters¶
Parameterized filters group filters into families and make it easier to create a lot of related filters. Specifically, they are classes that can be instantiated (parameterized) by passing arguments.
For example, the al class tests whether a term
has a particular final letter:
ac = al('ac')
ak = al('ak')
hal = al('hal')
Note
Parameterized filters have lowercase names for historical reasons. Also,
they better match the names for unparameterized filters, e.g.
al('i') & ~samyogapurva.
Combining filters¶
We can create new filters by using Python’s unary operators.
We can invert a filter (“not”):
# ekac: having one vowel
anekac = ~ekac
take the intersection of two filters (“and”):
# samyoga: ending in a conjunct consonant
# samjna('dhatu'): having 'dhatu' samjna
samyoga_dhatu = samyoga & samjna('dhatu')
and take the union of two filters (“or”):
# raw('Snu'): raw value is the 'nu' of e.g. 'sunute', 'Apnuvanti'
# samjna('dhatu'): having 'dhatu' samjna
# raw('BrU'): raw value is 'BrU'
snu_dhatu_bhru = raw('Snu') | samjna('dhatu') | raw('BrU')
Operators¶
An Operator is a callable object that accepts a
state and index, performs some operation, and returns the result. For example,
the guna operator applies guna to
state[index] and returns a new state.
Parameterized operators¶
Parameterized operators group operators into families and make it easier to create a lot of related operators. Specifically, they are classes that can be instantiated (parameterized) by passing arguments.
For example, the al_tasya class does arbitrary
letter substitution:
# ku h: k, kh, g, gh, ṅ, h
# cu: c, ch, j, jh, ñ
kuhos_cu = al_tasya('ku h', 'cu')
# f: ṛ, ṝ
# at: a
ur_at = al_tasya('f', 'at')
Note
Parameterized operators have lowercase names for historical reasons. Also, they better match the names for unparameterized operators.