ExpandaEVE Authoring Reference
ExpandaEVE is the readable form of the EVE language. It is what you write in a .eve filter file. Every construct here is translated into CompactaEVE and then compiled. This page documents each construct with real examples drawn from the shipping filter files.
For the document-level directives (DEFINE, LET, REALM, the rule statements) see The EVE Language. This page is about the body of a fuzzy rule: the pattern itself.
The shape of a rule body
A fuzzy rule body always has a root and may add any number of constructs around it. The root is the stem word the rule is built on. Everything else refines it: suffixes, plural handling, exclusions, context, and so on.
HEAR rule
AS
fuck(WHOLE)
suffix(NONE OR er OR ers OR ing OR ed OR s)
FLAG(pf YES)Here fuck(WHOLE) is the root, suffix(...) adds allowed endings, and FLAG(pf YES) tags the category. The constructs may appear in any order that reads naturally; the translator recognizes each by keyword.
The root and its boundary markers
The root is the anchor of a rule. Exactly one root marker sets how strictly the match is bounded at the root:
| Marker | Meaning |
|---|---|
(WHOLE) |
The root must match as a whole word. The candidate begins with the root stem and the whole route runs to a word boundary. Written [^^] in CompactaEVE. |
(ROOT) |
The root is a stem that suffixes and groups build on. It need not be a whole word by itself. Written [*^] in CompactaEVE. |
Use (WHOLE) when the bare word is itself the offence:
spic(WHOLE)
PLURALUse (ROOT) when you will attach endings or trailing groups:
ni(ROOT)
suffix(g OR ga OR gga OR gger OR gl LETTER t)
PLURALEvery fuzzy rule needs a root marker
A rule body with no (WHOLE) or (ROOT) marker cannot compile. The marker is what separates the root stem from the rest of the pattern.
Fuzzy matching is automatic
A fuzzy root is not a literal string test. The matcher already tolerates common evasion: vowels are interchangeable through the vowel set, letters may be stretched, and gaps (see GAP) let inserted characters through where you allow them. You do not enable this; it is how fuzzy rules match. To catch a wider spread of deliberate misspellings, add aliases and skeletons.
suffix: allowed endings
suffix(...) lists endings the word may take after the root. The arms are separated by OR. Only one suffix group is needed, but you may stack several; each adds an optional segment in order.
bitch(WHOLE)
suffix(NONE OR es OR ed OR ing OR y OR ier OR iest OR fest OR fests)NONE means the ending is optional: the word may end right at the root. Without a NONE arm the suffix is required.
Suffix groups compose left to right, so several in a row build a chain of optional segments:
fa(ROOT)
suffix(g OR gg OR NONE)
suffix(ggot OR NONE)
suffix(ggie OR NONE)A suffix arm may itself contain GAP or LETTER (see GAP and LETTER):
suffix(g OR ga OR gga OR gger OR gl LETTER t) suffix(my GAP OR NONE)PLURAL
PLURAL adds a rich set of plural and inflection endings in one word. It expands to the built-in s class of endings, so a rule tagged PLURAL also catches -s, -z, -es, -ing, -ed, -er, -y, and the other members declared in the s function.
dyke(WHOLE)
PLURALPLURAL reads the class named s. The shipping English filter declares it as:
LET function(s) BE s OR z OR es OR ez OR ies OR iez OR y>ies OR y>iez OR ing OR ings OR ed OR er OR ers OR y OR ysAn arm written from>to is a rewrite ending: it matches a word that ends in to by testing the root with the from ending instead. y>ies catches a word ending in ies as though it ended in y, which is how baby and babies are both reached from one root. See Rewrite suffixes for the mechanics.
NEVER: exclusions
NEVER(...) lists phrases that must not appear in the surrounding line. If any listed phrase is present, the rule does not fire, even if the pattern otherwise matches. This is how an aggressive root is kept from catching innocent words.
wop(WHOLE)
PLURAL
NEVER(doowop OR doo wop OR doowap OR doo wap OR dowap OR do wap OR do wop) gook(WHOLE)
PLURAL
NEVER(600k OR 900k OR 60ok OR 90ok OR 6ook OR 9ook OR 600ks OR 900ks)A NEVER check is a plain substring test against the whole line, lowercased. If the phrase is anywhere in the message, the rule is vetoed.
ALIAS: alternate spellings
ALIAS(...) adds an alternate spelling of the root. Each alias is a separate root variant that reuses the rest of the rule (its suffixes, groups, exclusions). List one alias per line to cover deliberate misspellings and letter swaps.
ALIAS(phuck)
ALIAS(fuk)
ALIAS(fukk)
ALIAS(fucc)
ALIAS(fck)
ALIAS(fk)
fuck(WHOLE)
suffix(NONE OR er OR ers OR ing OR ed OR s)Every alias here matches with the same suffix set as fuck. An alias may carry its own (WHOLE) or (ROOT) marker; without one it inherits the root's boundary.
An alias may reference a function, in which case each member of that function becomes an alias:
ALIAS(function(selfharmverb))
kill(ROOT)An alias may also be a segmented word or a multi-part fragment; it is normalized the same way the root is.
ALSO: additive fragments
ALSO(...) adds a fragment that may appear anywhere after the root rather than in fixed order. It is used when a required extra word can float relative to the stem.
kill(ROOT)
GAP
suffix(yo OR ur OR your OR yor)
suffix(self)
ALSO(wrists OR wrist OR throat)ALSO lowers to a floating group that attaches at any reachable position after the root and normal groups. See Plus groups for how a floating group is matched.
GAP and LETTER
These shorthands control what may sit between parts of a pattern.
| Construct | Meaning | CompactaEVE |
|---|---|---|
GAP |
An optional run of any characters (letters or non-letters), so inserted spaces, punctuation, or filler is tolerated. | (#*) |
LETTERS |
An optional run of letters only. | ($^) |
LETTER |
Exactly one character of word material. | @ |
GAP between a root and a suffix lets a phrase spread across words:
moon(ROOT)
GAP
suffix(cricket)
PLURALThis catches moon cricket, mooncricket, and spellings with filler between the two parts. LETTER matches a single character, useful inside a suffix arm to allow one wildcard position:
suffix(m LETTER GAP OR NONE)LETTERS matches a run of letters only, so it stops at a space:
my(ROOT)
GAP
LETTERS
GAP
suffix(hit)What counts as a letter
Word material is an ASCII letter or any byte of a non-ASCII glyph, so an emoji or symbol run also counts as word material for boundaries and the LETTER wildcard. Plain ASCII text is unaffected.
OR and NONE
OR separates alternatives inside a construct such as suffix(...), NEVER(...), ALSO(...), BEFORE(...), and AFTER(...), and it separates class members in a LET declaration. It must stand as a whole word.
NONE is an empty alternative. In a suffix it makes the ending optional; in a function member list it lets the class contribute an empty match:
LET function(quantifier) BE NONE OR some OR any OR a OR an OR theBEFORE and AFTER: context gating
These gate a rule on text elsewhere in the line. They are used to make an otherwise broad or ambiguous term fire only in a telling context.
| Construct | Meaning | CompactaEVE |
|---|---|---|
BEFORE(...) |
A precheck. The rule only activates when one of the listed phrases appears in the surrounding line. | {pre:...} |
AFTER(...) |
A post condition. Allowed endings that let the rule fire on the word itself even without a precheck phrase present. | {post:...} |
BEFORE(function(seeking))
cp
AFTER(mega OR pack OR packs OR nz OR db OR dropbox OR folder OR vids OR pics)
(WHOLE)A BEFORE phrase is a plain substring check against the whole line, lowercased. When a rule has a BEFORE gate it stays inert unless one of the phrases is present, or unless the word satisfies the AFTER post condition on its own. AFTER accepts a trailing plural s on its endings. See Gating for the exact rule.
SWAP: inline substitution set
SWAP(...) matches any one of several small fragments at a position, with control over word boundaries. It is used where a short interchangeable piece sits inside a phrase.
kill(ROOT)
GAP
SWAP(my* OR me*)
suffix(self)Each arm may carry asterisks that free a boundary:
- A leading
*frees the start (the arm need not begin at a word boundary). - A trailing
*frees the end. - No asterisk, or a double leading
**, anchors both sides to a word boundary.
So my* matches my with a required boundary before it but not necessarily after, letting it run into self. See Swaps for the boundary rules in full.
SKELETON: exact spellings
SKELETON(...) lists exact-word shapes the rule also matches directly, bypassing the fuzzy matcher. It is used to nail down specific obfuscated spellings using inline choice lists.
ni(ROOT)
suffix(g OR ga OR gga OR gger OR gl LETTER t)
PLURAL
NEVER(snigger OR sniggers OR sniggered)
SKELETON(n[a,e,i,o,u]gg[a,e,i,o,u]r OR n[a,e,i,o,u]gg[a,e,i,o,u]h)A skeleton is expanded into the full set of concrete words it can spell, and any candidate equal to one of those words matches immediately. The expansion takes the cross product of every choice, so n[a,e,i,o,u]gg[a,e,i,o,u]r produces naggar, neggar, niggar, and so on for every vowel pair.
Inline choice lists
Inside a skeleton, [a,e,i,o,u] is an inline choice list: one character position that may be any of the comma-separated members. Members may be longer than one character. Whitespace around a member is trimmed. A skeleton may also reference a function with &name, which expands to that function's members.
SKELETON(r[e,i]t[a,e,i]rd) SKELETON(m[a,e,i,o,u]st[a,e,i,o,u]rb[a,e,i,o,u]t OR m[a,e,i,o,u]st[a,e,i,o,u]rb[a,e,i,o,u]t[a,e,i,o,u])The result is lowercased. Skeletons match the exact spelling, so they are complementary to the fuzzy root, which handles the looser variants.
FUNCTION: named classes
A function is a named class declared with LET (see The EVE Language). It is referenced with function(name), and the reference expands to the class members. Functions have several roles depending on where they appear.
As a whole group of alternatives in a rule body:
groom(ROOT)
suffix(NONE OR ing OR ed)
GAP
MAYBE function(declarative)
GAP
function(vulnerable)As an alias, where each member becomes an alternate root:
ALIAS(function(genderword))
internal
GAP
condom(WHOLE)Inside a suffix, NEVER, ALSO, BEFORE, or AFTER list, where the members join the alternatives:
suffix(function(article) OR box of OR a box of OR internal OR function(genderword) OR NONE)Function modifiers
A function(...) reference may carry modifiers, joined with +:
| Modifier | Effect | CompactaEVE |
|---|---|---|
ADDITIVE |
The reference is a floating addition to the match rather than a fixed-order requirement. | wraps as a plus group [+[...]] |
GAP |
The reference matches loosely, allowing a gap after it. | appends (#*) |
function(subject + ADDITIVE + GAP)
function(desire + ADDITIVE + GAP)
function(quantifier + ADDITIVE + GAP)
rap(ROOT)
suffix(e OR ist OR ed OR ing OR eable OR able)
PLURALEach additive function here floats into the match wherever it fits, so the subject, desire, and quantifier words may appear in varying order before the root.
MAYBE FUNCTION: optional function
MAYBE FUNCTION(name) makes a function reference optional: the match succeeds whether or not one of its members is present.
rap(ROOT)
suffix(NONE OR e OR es OR ed OR ing OR eing OR in OR ist OR ists)
GAP
MAYBE function(declarative)
GAP
function(vulnerable)Here function(declarative) (words like a, the, my) may or may not appear between the verb and the vulnerable-person word.
FLAG: conditional rules and category tagging
FLAG(name YES) tags the rule with a declared flag. When the rule fires, that flag is set, which is how the filter reports a category such as self-harm or abuse rather than an ordinary block.
kms(WHOLE)
FLAG(sh YES)The value may be written YES, Y, or TRUE for on, and anything else for off. The flag name must be declared with DEFINE (see The EVE Language), or the document fails to load. Under the hood a flag wraps the pattern in a conditional gate; see Flag wrappers.
NOTE: comments
NOTE(...) attaches an inline comment. It is removed before compilation and does not affect matching, so it is a way to annotate a pattern in place. Whole-line comments still use a leading #.
ALIAS with WHOLE or ROOT markers
An alias may end in (WHOLE) or (ROOT) to set its own boundary, independent of the root's:
ALIAS(dike)
dyke(WHOLE)Without a trailing marker the alias inherits the root's boundary marker.
Segmented roots
A root may be written as several bracketed and bare pieces to mark part of it as inflectable. When at least two pieces are present and at least one is bracketed, they fold into a single root with alternate entry points.
re[tard] (WHOLE)
suffix(NONE OR d OR ded OR ted OR t OR der OR ders OR ing OR ed OR s)The bracketed piece marks the inflectable segment. See Segmented roots for exactly which alternatives are generated.
Putting it together
A realistic rule combines several constructs:
HEAR rule
AS
ALIAS(masterb)
masturb(ROOT)
suffix(ate OR ating OR ation OR ated OR ates OR at)
SKELETON(m[a,e,i,o,u]st[a,e,i,o,u]rb[a,e,i,o,u]t OR m[a,e,i,o,u]st[a,e,i,o,u]rb[a,e,i,o,u]t[a,e,i,o,u])This rule has an alias for a common misspelling, a root with a fixed suffix set, and skeletons for the leetspeak spellings the fuzzy matcher might miss. Read CompactaEVE to see how each of these lowers and matches.