Liking cljdoc? Tell your friends :D

hive-system.pattern.construct.api

BOUNDARY: write a regex as data, get a Pattern the existing subsystem already knows how to run.

(api/->expr :java [:cat :start [:+ :digit] :end]) ;; => {:ok "^\d+$"}

(api/register! #:pattern{:id :audit/probe} [:cat "ready" [:? "?"]]) (pattern/match? :audit/probe "ready?")

A Construct projects INTO the existing Pattern value object rather than replacing it: :pattern/expr is filled from the construct, :pattern/construct records what it was built from, and everything downstream — the registry's validation, IPatternEngine, match?, scan — is untouched. A pattern written as a literal string keeps working exactly as before.

register! goes through pattern.registry/register-pattern! so a construct that projects to an invalid Pattern is refused at the same gate as every other pattern.

BOUNDARY: write a regex as data, get a Pattern the existing subsystem
already knows how to run.

  (api/->expr :java [:cat :start [:+ :digit] :end])
  ;; => {:ok "^\\d+$"}

  (api/register! #:pattern{:id :audit/probe} [:cat "ready" [:? "?"]])
  (pattern/match? :audit/probe "ready?")

A Construct projects INTO the existing `Pattern` value object rather than
replacing it: `:pattern/expr` is filled from the construct, `:pattern/construct`
records what it was built from, and everything downstream — the registry's
validation, `IPatternEngine`, `match?`, `scan` — is untouched. A pattern
written as a literal string keeps working exactly as before.

`register!` goes through `pattern.registry/register-pattern!` so a construct
that projects to an invalid Pattern is refused at the same gate as every
other pattern.
raw docstring

hive-system.pattern.construct.core

PROMOTE layer: lift authored regal syntax into a validated Construct, project it back, and say what a Construct requires of its target.

Everything here is pure and total — a malformed form yields a Result describing why, never an exception and never a silent repair.

(normalize [:cat "a" [:alt "b" "c"]]) => {:ok {…}} (->regal normalized) => [:cat "a" [:alt "b" "c"]] (required-capabilities normalized) => #{}

required-capabilities is what makes dialect support a set-difference decidable BEFORE emission rather than a try/catch around a compile.

PROMOTE layer: lift authored regal syntax into a validated Construct, project
it back, and say what a Construct requires of its target.

Everything here is pure and total — a malformed form yields a Result
describing why, never an exception and never a silent repair.

  (normalize [:cat "a" [:alt "b" "c"]])  => {:ok {…}}
  (->regal   normalized)                    => [:cat "a" [:alt "b" "c"]]
  (required-capabilities normalized)        => #{}

`required-capabilities` is what makes dialect support a set-difference
decidable BEFORE emission rather than a `try`/`catch` around a compile.
raw docstring

hive-system.pattern.construct.dialect

The DIP swap point: a regex target language as data, plus the one gate every emitter passes through.

The substitutability contract

For a Construct a dialect cannot express, emit returns (r/err :construct/unsupported …). It NEVER emits an approximation and never drops a node. A :lookahead quietly discarded for RE2 produces a DIFFERENT LANGUAGE that still compiles and still returns hits — the worst outcome available, because nothing reports it.

Why the gate is here and not in the emitter

emit is a plain function that checks capabilities and THEN calls -render. An implementation supplies rendering only; it cannot skip, weaken or forget the check, because it never sees it. One definition, N implementations.

The dialect profile is an ARGUMENT throughout — resolved from the registry at call time, never captured at wiring time.

The DIP swap point: a regex target language as data, plus the one gate every
emitter passes through.

## The substitutability contract

For a Construct a dialect cannot express, `emit` returns
`(r/err :construct/unsupported …)`. It NEVER emits an approximation and never
drops a node. A `:lookahead` quietly discarded for RE2 produces a DIFFERENT
LANGUAGE that still compiles and still returns hits — the worst outcome
available, because nothing reports it.

## Why the gate is here and not in the emitter

`emit` is a plain function that checks capabilities and THEN calls
`-render`. An implementation supplies rendering only; it cannot skip,
weaken or forget the check, because it never sees it. One definition, N
implementations.

The dialect profile is an ARGUMENT throughout — resolved from the registry
at call time, never captured at wiring time.
raw docstring

hive-system.pattern.construct.ere

POSIX ERE — the dialect grep -E speaks, rendered without regal.

Why this emitter is hand-written

regal has no ERE flavor, and its :java/:ecma output is not ERE: a nested alternation comes out as (?:a|b), which POSIX ERE has no syntax for. So this is the one dialect that renders from the Construct directly. It is small because ERE is small — literals, classes, alternation, greedy quantifiers, anchors, and one grouping syntax.

Why the gate matters more here than anywhere else

rg rejects what it cannot do: an unsupported pattern exits 2 with a message. GNU grep 3.11 does not. Measured:

grep -E '\d+' matches a literal d — no warning, exit 0 grep -E '(?:a|b)c' matches abc — a warning, exit 0 grep -E 'ab*?' greedy — silently not lazy grep -E '(?=x)' never matches — a warning, exit 1

Every one of those is a DIFFERENT LANGUAGE that still runs and still returns hits. For rg the capability gate converts a stderr parse into data; for grep it converts a silently wrong answer into a named refusal, which is the stronger reason the gate exists at all.

What :posix-ere therefore provides: nothing

Its capability set is empty. \d must be written [:class [\0 \9]], which renders [0-9]; a tab must be a literal tab inside a :literal, not :tab; a :capture is refused because ERE's only group is a capturing one, so any structural group this emitter needs would shift the very index :match/groups promises.

Everything the gate lets through is rendered exactly, or not at all.

POSIX ERE — the dialect `grep -E` speaks, rendered without regal.

## Why this emitter is hand-written

regal has no ERE flavor, and its `:java`/`:ecma` output is not ERE: a nested
alternation comes out as `(?:a|b)`, which POSIX ERE has no syntax for. So
this is the one dialect that renders from the Construct directly. It is
small because ERE is small — literals, classes, alternation, greedy
quantifiers, anchors, and one grouping syntax.

## Why the gate matters more here than anywhere else

`rg` rejects what it cannot do: an unsupported pattern exits 2 with a
message. GNU grep 3.11 does not. Measured:

  grep -E '\d+'      matches a literal `d`  — no warning, exit 0
  grep -E '(?:a|b)c'  matches `abc`          — a warning, exit 0
  grep -E 'ab*?'      greedy                 — silently not lazy
  grep -E '(?=x)'     never matches          — a warning, exit 1

Every one of those is a DIFFERENT LANGUAGE that still runs and still returns
hits. For rg the capability gate converts a stderr parse into data; for grep
it converts a silently wrong answer into a named refusal, which is the
stronger reason the gate exists at all.

## What `:posix-ere` therefore provides: nothing

Its capability set is empty. `\d` must be written `[:class [\0 \9]]`,
which renders `[0-9]`; a tab must be a literal tab inside a `:literal`, not
`:tab`; a `:capture` is refused because ERE's only group is a capturing one,
so any structural group this emitter needs would shift the very index
`:match/groups` promises.

Everything the gate lets through is rendered exactly, or not at all.
raw docstring

hive-system.pattern.construct.regal

COLLECT layer: an anti-corruption layer over lambdaisland/regal, and the three dialect profiles it can render.

Regal is the emitter we buy rather than build — it owns escaping, precedence and grouping, and it is the only code here that knows what a regex looks like. Its vocabulary does not cross into the domain: it throws, we return a Result; it names flavors, we name dialects.

What regal does not do, and this layer must

Measured on 0.1.175: regal does NOT gate on flavor. [:atomic "ab"] renders as (?>ab) under :ecma, where JavaScript has no atomic groups, and an unknown flavor is accepted silently. The capability gate in construct.dialect is therefore load-bearing, not ceremony.

Dialects shipped

:java every capability flavor :java9 :ecma no atomic groups flavor :ecma :re2 no lookaround, no atomic flavor :ecma (rg, sd, RE2, Rust regex)

:posix-ere is deliberately absent: ERE has no non-capturing group, so regal's (?:…) output is not ERE even after a capability gate. It needs a real second emitter, which this design declines to build.

COLLECT layer: an anti-corruption layer over lambdaisland/regal, and the
three dialect profiles it can render.

Regal is the emitter we buy rather than build — it owns escaping, precedence
and grouping, and it is the only code here that knows what a regex looks
like. Its vocabulary does not cross into the domain: it throws, we return a
Result; it names flavors, we name dialects.

## What regal does not do, and this layer must

Measured on 0.1.175: regal does NOT gate on flavor. `[:atomic "ab"]` renders
as `(?>ab)` under `:ecma`, where JavaScript has no atomic groups, and an
unknown flavor is accepted silently. The capability gate in
`construct.dialect` is therefore load-bearing, not ceremony.

## Dialects shipped

  :java  every capability            flavor :java9
  :ecma  no atomic groups            flavor :ecma
  :re2   no lookaround, no atomic    flavor :ecma  (rg, sd, RE2, Rust regex)

`:posix-ere` is deliberately absent: ERE has no non-capturing group, so
regal's `(?:…)` output is not ERE even after a capability gate. It needs a
real second emitter, which this design declines to build.
raw docstring

hive-system.pattern.construct.schema

Malli value objects for regex CONSTRUCTS — a regex expression reified as data.

Two forms, one language

A construct is AUTHORED in lambdaisland/regal's vector syntax — [:cat "a" [:alt "b" "c"]] — and NORMALIZED into the recursive map value object this namespace defines. The authored form is what callers write; the normalized form is what carries contracts, generators and tests. construct.core promotes one into the other and projects back.

The split is forced: malli refuses a :ref reachable inside a sequence schema (::m/potentially-recursive-seqex), and regal's head-plus-variadic vector IS a sequence schema.

Capabilities

A Capability names a regex feature whose availability differs across target languages. A Dialect declares the set it PROVIDES; a construct's required set is computed from its shape. Support is a set-difference, decidable before emission — never a try/catch around a compile.

These schemas are the single source: they drive the m/=> contracts and the synthesized property/mutation tests.

Malli value objects for regex CONSTRUCTS — a regex expression reified as data.

## Two forms, one language

A construct is AUTHORED in lambdaisland/regal's vector syntax — `[:cat "a"
[:alt "b" "c"]]` — and NORMALIZED into the recursive map value object this
namespace defines. The authored form is what callers write; the normalized
form is what carries contracts, generators and tests. `construct.core`
promotes one into the other and projects back.

The split is forced: malli refuses a `:ref` reachable inside a sequence
schema (`::m/potentially-recursive-seqex`), and regal's head-plus-variadic
vector IS a sequence schema.

## Capabilities

A `Capability` names a regex feature whose availability differs across target
languages. A `Dialect` declares the set it PROVIDES; a construct's required
set is computed from its shape. Support is a set-difference, decidable before
emission — never a `try`/`catch` around a compile.

These schemas are the single source: they drive the `m/=>` contracts and the
synthesized property/mutation tests.
raw docstring

hive-system.pattern.core

Facade for the pattern subsystem: name what you are looking for, get an answer, never mention an engine.

(pattern/register-pattern! #:pattern{:id :audit/probe :expr "ready\?"}) (pattern/match? :audit/probe source) (pattern/scan :audit/probe source)

The regex engine registers itself on load, so :regex is available without ceremony; *default-engine* selects what an engine-less Pattern uses.

Re-exports are DELEGATING fns, never def-aliases — a def alias of a protocol method freezes its method cache.

Facade for the pattern subsystem: name what you are looking for, get an
answer, never mention an engine.

  (pattern/register-pattern! #:pattern{:id :audit/probe :expr "ready\\?"})
  (pattern/match? :audit/probe source)
  (pattern/scan  :audit/probe source)

The regex engine registers itself on load, so `:regex` is available without
ceremony; `*default-engine*` selects what an engine-less Pattern uses.

Re-exports are DELEGATING fns, never def-aliases — a def alias of a protocol
method freezes its method cache.
raw docstring

hive-system.pattern.protocols

Protocol for the pattern subsystem.

ISP boundary

IPatternEngine is the DIP seam between consumers (audits, linters, log scanners) and concrete matching engines (java.util.regex today; a structural s-expression matcher, a glob matcher, or a tree-sitter query engine next).

Consumers depend on this protocol and on a Pattern value. They never name an engine, so swapping or adding one changes no call site.

Engine contract

An engine MUST:

  • Return Result from every operation — never throw for a bad pattern.
  • Reject a flag it cannot honour (:pattern/unsupported-flag) rather than ignore it: a silently dropped flag is a wrong answer.
  • Report matches in subject order, non-overlapping, :match/end exclusive, with (subs subject start end) equal to :match/text.
  • Use a stable, unique engine-id keyword.
Protocol for the pattern subsystem.

## ISP boundary

`IPatternEngine` is the DIP seam between consumers (audits, linters, log
scanners) and concrete matching engines (java.util.regex today; a structural
s-expression matcher, a glob matcher, or a tree-sitter query engine next).

Consumers depend on this protocol and on a Pattern value. They never name an
engine, so swapping or adding one changes no call site.

## Engine contract

An engine MUST:
- Return `Result` from every operation — never throw for a bad pattern.
- Reject a flag it cannot honour (`:pattern/unsupported-flag`) rather than
  ignore it: a silently dropped flag is a wrong answer.
- Report matches in subject order, non-overlapping, `:match/end` exclusive,
  with `(subs subject start end)` equal to `:match/text`.
- Use a stable, unique `engine-id` keyword.
raw docstring

hive-system.pattern.regex

java.util.regex adapter for IPatternEngine.

The default engine. Pure promoters (flags->bitmask, ->matches) carry the logic; the record is a thin Result-returning shell over them.

java.util.regex adapter for IPatternEngine.

The default engine. Pure promoters (`flags->bitmask`, `->matches`) carry the
logic; the record is a thin Result-returning shell over them.
raw docstring

hive-system.pattern.registry

Engine and named-pattern registries — the DIP swap points.

Two open sets, two registries:

engines id -> IPatternEngine ;; how to match patterns id -> Pattern ;; what to look for

A consumer names a pattern (:audit/readiness-probe) instead of carrying a literal expression, so one definition serves every call site and a fix lands in one place. resolve-pattern is PURE — the index is an argument, which is what makes it testable and what lets a caller run against a private index.

Engine and named-pattern registries — the DIP swap points.

Two open sets, two registries:

  engines   id -> IPatternEngine   ;; how to match
  patterns  id -> Pattern          ;; what to look for

A consumer names a pattern (`:audit/readiness-probe`) instead of carrying a
literal expression, so one definition serves every call site and a fix lands
in one place. `resolve-pattern` is PURE — the index is an argument, which is
what makes it testable and what lets a caller run against a private index.
raw docstring

hive-system.pattern.schema

Malli value objects for the pattern subsystem.

A Pattern is a portable, ENGINE-AGNOSTIC description of what to look for: an expression plus flags, optionally named by an id. A Match is one located occurrence — text plus its half-open [start end) offsets in the subject.

These schemas are the single source: they drive the m/=> contracts and the synthesized property/mutation tests.

Malli value objects for the pattern subsystem.

A Pattern is a portable, ENGINE-AGNOSTIC description of what to look for:
an expression plus flags, optionally named by an id. A Match is one located
occurrence — text plus its half-open [start end) offsets in the subject.

These schemas are the single source: they drive the `m/=>` contracts and the
synthesized property/mutation tests.
raw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close