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).freqtri.(roll \ redirect).countbi.akp.map((d ^ 2) ?? 0).sumType 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).sumLiterals 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).sumbi.kp.(stretch >= 1u).countWeighting 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).sumbi.kp.map(s).compact.sumReport sources#
Unigram#
uni
One resolved keystroke event.
uni.keys(events:press,finger:LI)Bigram#
bi
Two consecutive keystrokes.
bi.same_fingerTrigram#
tri
Three consecutive keystrokes.
tri.alternationFour keystrokes#
quad
Four consecutive keystrokes.
quad.endpoints.same_keyFive keystrokes#
quint
Five consecutive keystrokes.
quint.endpoints.same_keyWindow 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_fingerWindow 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_keySame 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_fingersFinger repeat#
finger_repeat
Matches three-keystroke rhythms that repeat a finger without repeating a physical key.
Aliases: fr.
tri.finger_repeatKey repeat#
key_repeat
Matches three-keystroke rhythms that repeat a physical key.
Aliases: kr.
tri.key_repeatAlternation#
alternation
Matches three-keystroke rhythms that alternate hands, including repeated-finger and repeated-key forms.
Aliases: alt.
tri.alternationRoll#
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.rollTwo-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.roll2Three-step roll#
roll3
Matches rolls where both movements continue in the same direction.
Aliases: rol3.
tri.roll3Redirect#
redirect
Matches three-keystroke rhythms that reverse direction on one hand, including supported repeated-finger and repeated-key forms.
Aliases: red.
tri.redirectStrong redirect#
redirect_strong
Matches redirects graded strong by the fingers involved.
Aliases: sred.
tri.redirect_strongStrongish redirect#
redirect_strongish
Matches redirects graded strongish by the fingers involved.
Aliases: strongish, sired.
tri.redirect_strongishWeakish redirect#
redirect_weakish
Matches distinct-finger redirects graded weakish by the fingers involved.
Aliases: weakish, wired.
tri.redirect_weakishWeak redirect#
redirect_weak
Matches redirects graded weak by the fingers involved.
Aliases: weak, wred.
tri.redirect_weakSame-finger trigram#
same_finger_trigram
Matches any three-keystroke same-finger pattern, including same-key forms.
Aliases: sft.
tri.same_finger_trigramThree-key same-finger trigram#
three_key_sft
Matches three-keystroke same-finger patterns using three different physical keys.
tri.three_key_sftSame-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_sftSame-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_sftSame-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_sftInterrupted rhythm#
interrupted
Matches three-keystroke rhythms interrupted by a chord.
Aliases: int.
tri.interruptedInward#
inward
Matches pairs or rolls moving inward. Repeated-finger and repeated-key two-step rolls have no direction and are excluded.
Aliases: in.
tri.inwardOutward#
outward
Matches pairs or rolls moving outward. Repeated-finger and repeated-key two-step rolls have no direction and are excluded.
Aliases: out.
tri.outwardWindow quantifiers#
Every keystroke#
every(condition)
Matches when every keystroke satisfies the singleton condition.
tri.every(!thumb).rollEvery 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).rollAny 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.rollKeystroke 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.countFrequency#
collection.freq
Divides selected occurrence mass by the complete source population of the same width.
bi.sf.freqMapped 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).sumPhysical 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).sumPhysical 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.countOccurrence weight#
weight
The exact retained occurrence weight of the current mapped item; mapped sums also apply this weight implicitly.
bi.sf.map(weight).sumCorpus 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.graphemesArithmetic#
number + number
Bounded finite arithmetic; exponentiation binds before unary minus and associates to the right.
100 * bi.sf.map(1).sum / corpus.graphemesdistance#
distance
Euclidean physical key distance in key units.
Aliases: d.
bi.kp.map(d ^ 2).sumstretch#
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).countdx#
dx
Signed horizontal physical displacement in key units.
bi.kp.map(dx).sumdy#
dy
Signed vertical physical displacement in key units.
bi.kp.map(dy).sumFirst event#
first
Selects the first event of the current local window.
bi.map(first.keys(events:press).count).sumSecond event#
second
Selects the second event of the current local window.
bi.map(second.keys(events:press).count).sumLast event#
last
Selects the last event of the current local window.
bi.map(last.keys(events:press).count).sumLocal 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).sumLocal 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).sumPresent values#
numbers.compact
Drops only nil values from a mapped numeric collection.
bi.kp.map(s).compact.sumNumber 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).sumNumber 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).sumFallback#
value ?? fallback
Evaluates the right operand only if the left is nil.
bi.kp.map(s ?? 0).sumLayout 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.5Layout 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.