Skip to content

Expressions

Core Concepts: This Page · Expression Spec · Macros · Search Patterns · Protocols · Refiners · Styling

Every Alap link has a query — a short expression that says which menu items to show. Expressions go in the query attribute of an <alap-link> (or data-alap-linkitems for vanilla mode).

Live version with interactive examples: https://docs.alap.info/core-concepts/expressions

Syntax at a glance

SyntaxMeaningExample
item_idDirect item referencegolden_gate
.tagAll items with that tag.coffee
@macroExpand a named macro@favorites
@Expand macro matching the anchor's DOM ID@
,Separator (concatenate results)golden_gate, brooklyn
+AND (intersection).nyc + .bridge
|OR (union).nyc | .sf
-WITHOUT (subtraction).nyc - .tourist
(...)Grouping(.nyc + .bridge) | .sf
/key/Search pattern lookup/bridges/
/key/optsSearch with field overrides/bridges/lt
:protocol:args:Protocol query:time:30d:
*refiner:args*Result refiner*sort:label*, *limit:5*

Tags are the key idea

Every link in your library can have tags — simple labels like coffee, sf, nyc, bridge. One link can have many tags. Many links can share the same tag.

If you've used CSS classes, you already understand this. A CSS class groups elements so you can style them together. An Alap tag groups links so you can summon them together.

.coffee    → every link tagged "coffee"
.sf        → every link tagged "sf"
.bridge    → every link tagged "bridge"

The dot is intentional — it looks like a CSS class selector because it works like one.

Operators

Operators combine sets of links:

OperatorMeaningExampleResult
+AND.coffee + .sfCoffee shops in SF
|OR.nyc | .sfAnything in NYC or SF
-WITHOUT.nyc - .touristNYC spots, minus tourist traps

These read naturally. .coffee + .sf isn't code — it's a sentence: "things that are coffee and SF."

Try it

(AND) · (OR) · (WITHOUT)
html
<!-- Intersection: items tagged both "nyc" AND "bridge" -->
<alap-link query=".nyc + .bridge">NYC bridges</alap-link>

<!-- Union: items tagged "nyc" OR "sf" -->
<alap-link query=".nyc | .sf">NYC or SF</alap-link>

<!-- Subtraction: NYC items, excluding tourist spots -->
<alap-link query=".nyc - .tourist">local NYC</alap-link>

Left-to-right evaluation

Operators evaluate strictly left-to-right. There is no precedence — + does not bind tighter than |.

.nyc | .sf - .tourist

This means (.nyc | .sf) - .tourist, not .nyc | (.sf - .tourist). If you need different grouping, use parentheses.

Parentheses

Parentheses override left-to-right evaluation. They nest up to 32 levels deep.

html
<!-- Without parens: (.nyc | .sf) - .tourist -->
<alap-link query=".nyc | .sf - .tourist">cities (no tourists)</alap-link>

<!-- With parens: .nyc | (.sf - .tourist) -->
<alap-link query=".nyc | (.sf - .tourist)">NYC + non-tourist SF</alap-link>

A practical example — bridges in either NYC or SF:

html
<alap-link query="(.nyc + .bridge) | (.sf + .bridge)">bridges</alap-link>

Without parentheses, .nyc + .bridge | .sf + .bridge would evaluate as (((.nyc + .bridge) | .sf) + .bridge) — probably not what you want.

Commas

Commas separate independent expressions. Each segment is evaluated on its own, and the results are concatenated:

html
<!-- Three specific items -->
<alap-link query="brooklyn, golden_gate, highline">picks</alap-link>

<!-- Mix direct IDs with tag queries -->
<alap-link query="brooklyn, .coffee + .nyc">bridge + NYC cafes</alap-link>

A real example

Say you run a travel site. Your link library has 200 entries, each tagged by city, category, and vibe. A single expression can carve out exactly the menu you want:

(.nyc + .food) | (.sf + .food) - .chain

"Food in NYC or SF, but not chain restaurants." That's one attribute on one HTML element, producing a curated menu from a library of hundreds.

Grammar reference

The formal grammar the parser implements:

query   = segment (',' segment)*
segment = term (op term)*
op      = '+' | '|' | '-'
term    = '(' segment ')' | atom
atom    = ITEM_ID | CLASS | REGEX | MACRO | PROTOCOL | REFINER

Every expression is one or more comma-separated segments. Each segment is a chain of terms joined by operators. A term is either a parenthesized sub-expression or a single atom.

For the full formal specification with operator semantics and worked input/output examples, see the Expression Spec.

Beyond selection

Expressions can also include protocol filters for dimensional queries (time, location, live data sources) and refiners for shaping results (sort, limit, shuffle). Together, the three phases — selection, gathering, and refining — give you full control over what appears and in what order.

Next steps