Skip to content

Query Language Reference

Complete metric-query and layout-query syntax, types, arguments, aliases, and examples.

Metric query grammar#

A report source starts a pipeline. Dot stages filter, project, or reduce the current collection. Parenthesized filter stages accept Boolean expressions over the current item.

Collection union uses |. Intersection uses &. Difference uses \. Prefix ! negates a condition, and parentheses group it. Unary expressions bind first; & and \ bind before |.

Numeric precedence from lowest to highest is ??, |, &, comparisons, + and -, * and /, then ^. Coalescing and exponentiation associate to the right. Exponentiation binds before unary minus and logical not.

Spaces and line breaks may separate tokens. Metric queries do not have comment syntax.

tri.(roll | redirect).freq
tri.(roll \ redirect).count
bi.akp.map((d ^ 2) ?? 0).sum

Type legend#

input type → output type

start is the beginning of a report pipeline. Window1 through Window5 are ordered windows containing exactly that many consecutive resolved keystrokes; Window accepts any supported width. same-as-input means a filter preserves the input collection and its width.

Keystroke is one resolved action within a window. Event is a selected window position such as first or last. KeyCollection contains physical keys selected from an event. KeyPair is one ordered physical-key pair, and KeyPairCollection contains those pairs. NumberCollection contains mapped numeric values. map means the current mapped-item scope.

Number and Boolean are scalar results. Nil is an absent numeric or Boolean value. Length is a distance in key units. numeric-expression accepts a complete numeric expression; length-condition is the distance-comparison form used by positional predicates.

finger-set, finger-pair, and finger-pair-set describe accepted fingers or ordered finger combinations. participation-set selects event roles. trigger-kind-set distinguishes keys from chords. ergonomic-direction selects concordant or discordant reach. singleton-expression is a condition checked against one keystroke. enum is the closed value list shown by that argument, and boolean accepts true or false.

Current item and scope#

Filter and map bodies use the current window, event, key pair, or mapped item implicitly; the language has no arrow-function syntax. Local cost expressions likewise run against one implicit candidate window and omit a report source.

Report sources cannot appear inside local costs or nested map scopes. weight is available only inside a map and is unavailable to local costs. corpus.graphemes is also unavailable to local costs.

first, second, and last select events from the current local window. Pair properties require key_pairs, active_key_pairs, or cartesian_product to produce physical pairs first.

Named parameters and shorthand#

function(name:value, ...)

Named parameters use name:value and commas separate parameters. finger has the alias f; finger_pair has the alias fp. Row distance may be positional or named dy:. Stretch may be positional or named stretch:, with s: as its shorthand. concordant and discordant are positional row-change conditions, with conc and disc as shorthands.

first, second, sustained, events, kind, and home have no shorter parameter names. A | inside a parameter forms a set of accepted values.

bi.rc(fp:EPR | ERP, discordant, dy:abs(dy) >= 1u)
bi.sf(f:LI | RI)

The two keys forms#

keystroke.keys(...) | event.keys(...)

On a keystroke, keys(...) is a Boolean condition: it flattens the selected trigger groups to physical keys and tests whether one key has all requested properties. It deliberately discards key-versus-chord trigger identity; use triggers(...) when that identity matters.

After first, second, or last, keys(...) is a numeric projection that returns a local key collection for count, cartesian_product, or another numeric operation. In both forms, omitted events mean press, release, acquire, and sustain.

uni.keys(finger:LH)
bi.map(first.keys().count).sum

Literals and named values#

Numeric expressions accept decimal numbers, true, false, and nil. Predicate distance conditions also accept key-unit lengths such as 1.5u. Finger sets, finger pairs, participation roles, trigger kinds, sustained-key policies, and ergonomic directions use the cataloged named values.

Named arguments use name:value. A | inside an argument value forms a value set, while | between complete conditions forms a Boolean or collection union according to the surrounding type.

Geometry and key units#

distance | dx | dy | stretch

One u is one standard key width. Decimal values are accepted. distance is Euclidean key-to-key distance. dx and dy are signed as the second key position minus the first: positive x points right and positive y points toward the lower keyboard rows.

stretch is absolute horizontal key separation minus the natural horizontal spacing between the assigned fingers’ authored home positions. It is nil for cross-hand pairs, same-finger pairs, or pairs whose finger homes are unavailable.

bi.kp.map(distance).sum
bi.kp.(stretch >= 1u).count

Weighting and aggregation#

count is an unweighted count of selected rows or projected pairs. freq divides selected occurrence mass by the complete source population of the same width.

map(expression).sum evaluates the expression for each retained item and applies that item’s occurrence weight once. Pairs projected from a report collection retain the source window weight. Pairs made inside an outer window map have unit local weight before the outer map applies its weight.

at(index) reads one number from a mapped numeric collection using a zero-based index; an index beyond the end produces nil. slice(start,end) keeps a range from the zero-based start through the exclusive end. Slice bounds beyond the collection are clamped, and an equal start and end produces an empty collection. Indices must be finite nonnegative integers, and a slice start cannot exceed its end.

corpus.graphemes is the shared corpus-grapheme denominator used by normalized statistics.

Missing numeric values#

nil represents an absent numeric or Boolean value. The ?? operator evaluates its right side only when the left side is nil. compact removes only nil values from a mapped numeric collection; it does not remove zero or false.

Arithmetic and ordered comparisons involving nil remain nil. false & nil is false, true | nil is true, and other Boolean combinations preserve the unknown result. nil equals nil and differs from present values. A mapped sum is nil when an unhandled nil remains; use ?? for a fallback or compact to omit absent items.

bi.kp.map(s ?? 0).sum
bi.kp.map(s).compact.sum

Report sources#

Unigram#

uni

One resolved keystroke event.

uni.keys(events:press,finger:LI)

Bigram#

bi

Two consecutive keystrokes.

bi.same_finger

Trigram#

tri

Three consecutive keystrokes.

tri.alternation

Four keystrokes#

quad

Four consecutive keystrokes.

quad.endpoints.same_key

Five keystrokes#

quint

Five consecutive keystrokes.

quint.endpoints.same_key

Window projections#

Endpoints#

endpoints

Selects the first and last keystrokes while retaining the original source window. Earlier filters can examine intervening keystrokes; a following key_pairs or keys selector chooses participating physical keys and held-key policy.

Aliases: ends.

tri.endpoints.same_finger

Window predicates#

Same finger#

same_finger(finger?: finger-set)

Matches when a key participating in each keystroke uses the same finger. Includes repeated presses of the same key.

Aliases: sf.

bi.same_finger(finger: RP)

Same finger: Finger#

finger

Restricts the repeated participating finger. For same finger, that finger must participate in both keystrokes; repeated presses of one key still count.

Accepted values: LP, LR, LM, LI, LT, RP, RR, RM, RI, RT, P, R, M, I, LH, RH.

Same finger, different key#

same_finger_different_key(finger?: finger-set)

Matches different keys operated by the same finger. Excludes repeated presses of the same key.

Aliases: sfdk.

bi.same_finger_different_key(finger: I)

Same finger, different key: Finger#

finger

Restricts the repeated participating finger. That finger must participate in both keystrokes, and the two physical keys must be different; repeated presses of the same key are excluded.

Accepted values: LP, LR, LM, LI, LT, RP, RR, RM, RI, RT, P, R, M, I, LH, RH.

Same key#

same_key(finger?: finger-set)

Matches when the same physical key participates in both keystrokes.

Aliases: sk.

bi.same_key

Same key: Finger#

finger

Restricts the repeated participating finger. For same finger, that finger must participate in both keystrokes; repeated presses of one key still count.

Accepted values: LP, LR, LM, LI, LT, RP, RR, RM, RI, RT, P, R, M, I, LH, RH.

Row change#

row_change(finger_pair?: finger-pair-set, ergonomic?, condition?: length-condition)

Matches different non-thumb fingers on one hand when their keys are on different rows. dy is the second key's row minus the first key's row.

Aliases: rc.

bi.row_change(fp: EPR | ERP, 1u < abs(dy) < 1.5u)

Row change: Finger pair#

finger_pair

Restricts the ordered pair of distinct participating fingers.

Accepted values: EPR, ERP.

Row change: Row distance condition#

condition

An optional positional condition on signed row distance in key units.

Accepted values: dy, abs, u.

Row change: Ergonomic direction#

ergonomic

Restricts the reach relationship without depending on keystroke order.

Accepted values: concordant, discordant.

Lateral stretch#

lateral_stretch(finger_pair?: finger-pair-set, condition?: length-condition)

Matches distinct fingers on one hand, including thumbs, when horizontal key separation exceeds the assigned fingers' authored home spacing. Missing homes do not match.

Aliases: ls.

bi.lateral_stretch(s > 1u)

Lateral stretch: Finger pair#

finger_pair

Restricts the ordered pair of distinct participating fingers.

Accepted values: EPR, ERP.

Lateral stretch: Stretch condition#

condition

An optional condition on horizontal key separation beyond the assigned fingers' home spacing.

Accepted values: stretch, s, abs, u.

Distinct fingers#

distinct_fingers

Matches three keystrokes using three different fingers. Chorded keystrokes are excluded.

Aliases: df.

tri.distinct_fingers

Finger repeat#

finger_repeat

Matches three-keystroke rhythms that repeat a finger without repeating a physical key.

Aliases: fr.

tri.finger_repeat

Key repeat#

key_repeat

Matches three-keystroke rhythms that repeat a physical key.

Aliases: kr.

tri.key_repeat

Alternation#

alternation

Matches three-keystroke rhythms that alternate hands, including repeated-finger and repeated-key forms.

Aliases: alt.

tri.alternation

Roll#

roll

Matches two-step and three-step rolls. Repeated-finger and repeated-key two-step rolls are included but have no inward or outward direction.

Aliases: rol.

tri.roll

Two-step roll#

roll2

Matches two-step rolls. Repeated-finger and repeated-key forms are included but have no inward or outward direction.

Aliases: rol2.

tri.roll2

Three-step roll#

roll3

Matches rolls where both movements continue in the same direction.

Aliases: rol3.

tri.roll3

Redirect#

redirect

Matches three-keystroke rhythms that reverse direction on one hand, including supported repeated-finger and repeated-key forms.

Aliases: red.

tri.redirect

Strong redirect#

redirect_strong

Matches redirects graded strong by the fingers involved.

Aliases: sred.

tri.redirect_strong

Strongish redirect#

redirect_strongish

Matches redirects graded strongish by the fingers involved.

Aliases: strongish, sired.

tri.redirect_strongish

Weakish redirect#

redirect_weakish

Matches distinct-finger redirects graded weakish by the fingers involved.

Aliases: weakish, wired.

tri.redirect_weakish

Weak redirect#

redirect_weak

Matches redirects graded weak by the fingers involved.

Aliases: weak, wred.

tri.redirect_weak

Same-finger trigram#

same_finger_trigram

Matches any three-keystroke same-finger pattern, including same-key forms.

Aliases: sft.

tri.same_finger_trigram

Three-key same-finger trigram#

three_key_sft

Matches three-keystroke same-finger patterns using three different physical keys.

tri.three_key_sft

Same-key adjacent same-finger trigram#

same_key_bigram_sft

Matches same-finger trigrams where an adjacent pair repeats a physical key.

tri.same_key_bigram_sft

Same-key skip same-finger trigram#

same_key_skipgram_sft

Matches same-finger trigrams where the first and last keystrokes repeat a physical key.

tri.same_key_skipgram_sft

Same-key same-finger trigram#

same_key_trigram_sft

Matches same-finger trigrams where all three keystrokes use the same physical key.

tri.same_key_trigram_sft

Interrupted rhythm#

interrupted

Matches three-keystroke rhythms interrupted by a chord.

Aliases: int.

tri.interrupted

Inward#

inward

Matches pairs or rolls moving inward. Repeated-finger and repeated-key two-step rolls have no direction and are excluded.

Aliases: in.

tri.inward

Outward#

outward

Matches pairs or rolls moving outward. Repeated-finger and repeated-key two-step rolls have no direction and are excluded.

Aliases: out.

tri.outward

Window quantifiers#

Every keystroke#

every(condition)

Matches when every keystroke satisfies the singleton condition.

tri.every(!thumb).roll

Every keystroke: Condition#

condition

A condition checked separately against each keystroke.

Accepted values: thumb, finger.

Any keystroke#

any(condition)

Matches when at least one keystroke satisfies the singleton condition.

tri.any(thumb).roll

Any keystroke: Condition#

condition

A condition checked separately against each keystroke.

Accepted values: thumb, finger.

No thumbs#

no_thumbs

Matches when no key pressed, acquired, or released by any keystroke uses a thumb. Keys only held over from an earlier keystroke are ignored.

tri.no_thumbs.roll

Keystroke conditions#

Thumb#

thumb

A key action assigned to either thumb.

tri.any(thumb)

Finger#

finger(set)

A key action assigned to a selected finger or finger set.

tri.every(finger(LH \ LT))

Finger: Finger#

finger

The finger or finger set to match.

Accepted values: LP, LR, LM, LI, LT, RP, RR, RM, RI, RT, P, R, M, I, LH, RH.

Structured trigger keys#

triggers(events:press|acquire|release|sustain, kind:key|chord, finger:set, home:true|false)

Matches when one key belonging to a selected native key or chord trigger has all requested properties. Press, acquire, and release select the active event; sustain selects the carried trigger context.

uni.triggers(events:press,kind:chord,finger:LI,home:false)

Structured trigger keys: Participation#

events

The active event categories or sustained context to inspect.

Accepted values: press, acquire, release, sustain.

Structured trigger keys: Trigger kind#

kind

Retains native key-versus-chord trigger identity.

Accepted values: key, chord.

Structured trigger keys: Finger#

finger

The resolved finger of the same matching physical key.

Accepted values: LP, LR, LM, LI, LT, RP, RR, RM, RI, RT, P, R, M, I, LH, RH.

Structured trigger keys: Home#

home

Whether that same physical key carries the authored home flag.

Accepted values: true, false.

Participating keys#

keys(events:press|release|acquire|sustain, finger:set, home:true|false)

Flattens the selected event and sustained trigger groups to physical keys, then matches one key with all requested properties. Omitted events include press, release, acquire, and sustain. Trigger identity is intentionally omitted.

uni.keys(finger:LH,home:false)

Participating keys: Participation#

events

Event categories or sustained context to flatten; omitted selects press, release, acquire, and sustain.

Accepted values: press, release, acquire, sustain.

Participating keys: Finger#

finger

The resolved finger of the same matching physical key.

Accepted values: LP, LR, LM, LI, LT, RP, RR, RM, RI, RT, P, R, M, I, LH, RH.

Participating keys: Home#

home

Whether that same physical key carries the authored home flag.

Accepted values: true, false.

Collection operators#

Either#

left | right

Matches when either expression matches.

tri.(alternation | roll)

Both#

left & right

Matches when both expressions match.

tri.(roll & inward)

Except#

left \ right

Matches the left expression except where the right expression matches.

tri.(roll \ key_repeat)

Not#

!expression

Matches when the following expression does not match.

tri.(!key_repeat)

Group#

(expression)

Groups an expression so its operators are applied together.

tri.(alternation | roll)

Filter#

.(expression)

Keeps windows that satisfy the expression in parentheses.

tri.(alternation | roll)

Comparison#

left < right

Compares a distance with a length in key units.

bi.row_change(1u < abs(dy) < 1.5u)

Predicate values#

Left pinky#

LP

The left pinky.

bi.sf(f:LP)

Left ring#

LR

The left ring finger.

bi.sf(f:LR)

Left middle#

LM

The left middle finger.

bi.sf(f:LM)

Left index#

LI

The left index finger.

bi.sf(f:LI)

Left thumb#

LT

The left thumb.

bi.sf(f:LT)

Right pinky#

RP

The right pinky.

bi.sf(f:RP)

Right ring#

RR

The right ring finger.

bi.sf(f:RR)

Right middle#

RM

The right middle finger.

bi.sf(f:RM)

Right index#

RI

The right index finger.

bi.sf(f:RI)

Right thumb#

RT

The right thumb.

bi.sf(f:RT)

Pinkies#

P

Both pinkies.

bi.sf(f:P)

Ring fingers#

R

Both ring fingers.

bi.sf(f:R)

Middle fingers#

M

Both middle fingers.

bi.sf(f:M)

Index fingers#

I

Both index fingers.

bi.sf(f:I)

Left hand#

LH

Every finger and thumb on the left hand.

bi.sf(f:LH)

Right hand#

RH

Every finger and thumb on the right hand.

bi.sf(f:RH)

Concordant#

concordant

The longer finger reaches farther from the typist; ring-to-index pairs count regardless of keystroke order.

Aliases: conc.

bi.row_change(concordant)

Discordant#

discordant

The shorter finger reaches farther from the typist, regardless of keystroke order.

Aliases: disc.

bi.row_change(discordant)

Ordered finger pair#

LPLR

An ordered pair such as LPLR for left pinky followed by left ring.

bi.row_change(fp: LPLR)

Either-hand finger pair#

EPR

The same ordered pair on either hand, such as EPR for pinky followed by ring.

bi.row_change(fp: EPR | ERP)

Key unit#

1.5u

A keyboard distance in key-width units; decimals are allowed.

Aliases: U.

bi.row_change(1u < abs(dy) < 1.5u)

Row distance#

dy

The second key's row minus the first key's row, in key units.

bi.row_change(dy > 1u)

Absolute value#

abs(length)

Uses distance magnitude without direction.

bi.row_change(abs(dy) > 1u)

Numeric queries#

Count#

collection.count

Counts selected rows or projected physical action pairs without weighting.

bi.sf.count

Frequency#

collection.freq

Divides selected occurrence mass by the complete source population of the same width.

bi.sf.freq

Mapped sum#

collection.map(number).sum

Evaluates a scalar for every selected weighted item and sums each mapped value times its occurrence weight.

bi.sf.map(1).sum

Physical key pairs#

window.key_pairs(first:press|release,second:press|acquire,sustained:all|transient)

Combines selected physical keys from two ordered events. The first defaults to press or release, and the second to press or acquire.

Aliases: kp.

bi.kp.sf.map(d ^ 2).sum

Physical key pairs: First roles#

first

Event roles for the first event.

Accepted values: press, release, acquire, sustain.

Physical key pairs: Second roles#

second

Event roles for the second event.

Accepted values: press, release, acquire, sustain.

Physical key pairs: Held keys#

sustained

Whether to include carried triggers unchanged throughout the original source window.

Accepted values: all, transient.

Active key pairs#

window.active_key_pairs

Combines keys actively involved in some movement within the window. Persistent held keys are excluded; held keys that enter or leave the window are included.

Aliases: akp.

bi.akp.sf.count

Occurrence weight#

weight

The exact retained occurrence weight of the current mapped item; mapped sums also apply this weight implicitly.

bi.sf.map(weight).sum

Corpus graphemes#

corpus.graphemes

The total grapheme count in the corpus, available as an explicit denominator for custom formulas. It is separate from freq: an n-gram freq divides by the weighted source population for that same width.

100 * bi.sf.map(1).sum / corpus.graphemes

Arithmetic#

number + number

Bounded finite arithmetic; exponentiation binds before unary minus and associates to the right.

100 * bi.sf.map(1).sum / corpus.graphemes

distance#

distance

Euclidean physical key distance in key units.

Aliases: d.

bi.kp.map(d ^ 2).sum

stretch#

stretch

Horizontal key separation minus assigned-finger home separation; absent for cross-hand pairs, same fingers, or missing homes.

Aliases: s.

bi.kp.ls(s > 1u).count

dx#

dx

Signed horizontal physical displacement in key units.

bi.kp.map(dx).sum

dy#

dy

Signed vertical physical displacement in key units.

bi.kp.map(dy).sum

First event#

first

Selects the first event of the current local window.

bi.map(first.keys(events:press).count).sum

Second event#

second

Selects the second event of the current local window.

bi.map(second.keys(events:press).count).sum

Last event#

last

Selects the last event of the current local window.

bi.map(last.keys(events:press).count).sum

Local event keys#

event.keys(events:press|release|acquire|sustain)

Selects physical keys by event role while retaining resolved fingers and native trigger identities. Omitted events include press, release, acquire, and sustain.

bi.map(first.keys().count).sum

Local event keys: Events#

events

Event roles to select; omitted selects press, release, acquire, and sustain.

Accepted values: press, release, acquire, sustain.

Local event keys: Held keys#

sustained

Whether to include carried triggers unchanged throughout the original window.

Accepted values: all, transient.

Cartesian product#

keys.cartesian_product(keys)

Combines two explicit local key collections into ordered physical pairs.

Aliases: cp.

bi.map(first.keys(events:press|release).cp(second.keys(events:press|acquire)).count).sum

Present values#

numbers.compact

Drops only nil values from a mapped numeric collection.

bi.kp.map(s).compact.sum

Number at index#

numbers.at(index)

Returns the number at a zero-based index, or nil when the index is beyond the collection.

bi.map(weight).at(0)

Number at index: Index#

index

A finite nonnegative integer index.

Number range#

numbers.slice(start,end)

Keeps a zero-based range of numbers through an exclusive end, clamping bounds to the collection length.

bi.map(weight).slice(0,10).sum

Number range: Start#

start

The finite nonnegative integer start index.

Number range: End#

end

The finite nonnegative integer exclusive end index.

Absent value#

nil

An explicitly absent numeric or boolean value.

bi.kp.map(s ?? 0).sum

Fallback#

value ?? fallback

Evaluates the right operand only if the left is nil.

bi.kp.map(s ?? 0).sum

Layout query grammar#

Layout queries are complete Boolean expressions over metadata, authored feature fields, and saved metric results. They are parsed separately from metric queries.

Precedence from lowest to highest is |, &, comparisons, + and -, * and /, unary ! and -, then right-associative ^. Parentheses override precedence. Boolean & and | short-circuit from left to right.

String = and != compare exact values; ~ tests whether the left string contains the right string. Ordered comparisons and arithmetic require numbers. Boolean fields are used directly rather than compared with =.

Saved metric value#

metric('stable-id')

Reads the numeric result of a saved metric by its stable definition ID. The ID must be a nonempty single-quoted string; display names are not identifiers.

metric('saved-definition-id') > 2.5

Layout query literals#

"text" | 1.5 | true | false

Double-quoted strings, finite decimal numbers, true, and false are literals. Strings accept escaped quotes, backslashes, newlines, carriage returns, and tabs.

Layout query fields#

Layout queries filter candidate layouts before analysis. This is a separate language from metric queries: its fields describe layout metadata and authored behavior, while explicit metric references supply numeric values.

name#

name

Matches the display name of the layout.

author#

author

Matches the layout author.

year#

year

Matches the four-digit year when the layout was first published.

family#

family

Matches the layout family identifier.

id#

id

Matches the independent layout identifier.

origin#

origin

Matches whether the layout is published or user-owned.

Accepted values: published, own.

draft#

draft

Matches whether the candidate is a draft.

has_magic#

has_magic

Matches layouts with at least one nonempty magic rule.

Aliases: magic.

has_repeat#

has_repeat

Matches layouts using any supported repeat behavior.

Aliases: repeat.

has_repeat_output#

has_repeat_output

Matches layouts using repeat-output behavior.

Aliases: ro.

has_skip_repeat_output#

has_skip_repeat_output

Matches layouts using skip-repeat-output behavior.

Aliases: sro.

has_repeat_keystroke#

has_repeat_keystroke

Matches layouts using repeat-keystroke behavior.

Aliases: rk.

has_skip_repeat_keystroke#

has_skip_repeat_keystroke

Matches layouts using skip-repeat-keystroke behavior.

Aliases: srk.

has_duplicate_output#

has_duplicate_output

Matches layouts where two different physical keys can produce the same nonempty literal output.

Aliases: dup.

has_chords#

has_chords

Matches layouts with at least one authored chord.

Aliases: chords.

has_multi_grapheme_output#

has_multi_grapheme_output

Matches layouts with a literal output containing more than one extended grapheme.

Aliases: multi.

Layout query operators#

These are the complete operators accepted by the layout-query parser. Parentheses group expressions. Boolean operators combine filters; comparison and arithmetic operators work with compatible field or metric values.

!#

!

Negates a boolean expression.

&#

&

Requires both boolean expressions to match.

|#

|

Requires either boolean expression to match.

=#

=

Matches equal values.

!=#

!=

Matches unequal values.

~#

~

Matches when the left text contains the right text.

<#

<

Matches when the left numeric value is smaller.

<=#

<=

Matches when the left numeric value is no larger.

>#

>

Matches when the left numeric value is larger.

>=#

>=

Matches when the left numeric value is no smaller.

+#

+

Adds numeric metric values.

-#

-

Subtracts numeric metric values.

*#

*

Multiplies numeric metric values.

/#

/

Divides numeric metric values.

^#

^

Raises a numeric value to a power.