# 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](page:eve/compacta) 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](page: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.

```text
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:

```text
  spic(WHOLE)
  PLURAL
```

Use `(ROOT)` when you will attach endings or trailing groups:

```text
  ni(ROOT)
  suffix(g OR ga OR gga OR gger OR gl LETTER t)
  PLURAL
```

!!! warning "Every 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](#gap-and-letter)) 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](#alias-alternate-spellings) and [skeletons](#skeleton-exact-spellings).

## 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.

```text
  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:

```text
  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](#gap-and-letter)):

```text
  suffix(g OR ga OR gga OR gger OR gl LETTER t)
```

```text
  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.

```text
  dyke(WHOLE)
  PLURAL
```

`PLURAL` reads the class named `s`. The shipping English filter declares it as:

```text
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 ys
```

An 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](page:eve/compacta#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.

```text
  wop(WHOLE)
  PLURAL
  NEVER(doowop OR doo wop OR doowap OR doo wap OR dowap OR do wap OR do wop)
```

```text
  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.

```text
  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:

```text
  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.

```text
  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](page:eve/compacta#plus-groups-floating) 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:

```text
  moon(ROOT)
  GAP
  suffix(cricket)
  PLURAL
```

This 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:

```text
  suffix(m LETTER GAP OR NONE)
```

`LETTERS` matches a run of letters only, so it stops at a space:

```text
  my(ROOT)
  GAP
  LETTERS
  GAP
  suffix(hit)
```

!!! note "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:

```text
LET function(quantifier) BE NONE OR some OR any OR a OR an OR the
```

## BEFORE 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:...}` |

```text
  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](page:eve/compacta#precheck-and-post-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.

```text
  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](page:eve/compacta#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.

```text
  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.

```text
  SKELETON(r[e,i]t[a,e,i]rd)
```

```text
  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](page:eve/language#let-defining-a-function-named-class)). 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:

```text
  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:

```text
  ALIAS(function(genderword))
  internal
  GAP
  condom(WHOLE)
```

Inside a `suffix`, `NEVER`, `ALSO`, `BEFORE`, or `AFTER` list, where the members join the alternatives:

```text
  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 `(#*)` |

```text
  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)
  PLURAL
```

Each 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.

```text
  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.

```text
  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](page:eve/language#define-declaring-flag-ids)), or the document fails to load. Under the hood a flag wraps the pattern in a conditional gate; see [Flag wrappers](page:eve/compacta#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:

```text
  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.

```text
  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](page:eve/compacta#segmented-roots) for exactly which alternatives are generated.

## Putting it together

A realistic rule combines several constructs:

```text
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](page:eve/compacta) to see how each of these lowers and matches.
