Skip to content

Temporal-range key binding on navigation stops, scope prefixes, and transitive leaves (lexer-path; follow-up to #74) #76

Description

@javierlores

Summary

Extend the temporal-range key-bracket binding (foo[t1...t2], foo[...t2], foo[t1...]) introduced for flat leaf keys in #74 to the remaining key shapes that route through the lexer-captured bracket path:

  • Navigation stopsa[t1...t2].b, a.foo[t1...]
  • Scope prefixesa[t1...].(foo = "X")
  • Transitive leavesfoo[t1...t2]*

This is the grammar/AST/parse side only (same as #74); server-side evaluation remains a separate Concourse concern.

Background — why these were deferred from #74

#74 implemented range bindings on flat leaf keys only. Those flow through the parser path (Key()OPEN_BRACKET KeyBracketParameter() CLOSE_BRACKET in grammar/grammar.jjt), where bracket content is tokenized in the default lexer state and interpreted by Parsing.applyKeyBracket(base, content).

The shapes above instead flow through the lexer-captured path: the whole key (e.g. a[t1...t2].b, foo[t1...t2]*) is captured as a single PERIOD_SEPARATED_STRING / NAVIGATION_SCOPE_OPEN / ASTERISK_SUFFIXED_STRING token via the #KEY_BRACKET fragment (grammar.jjt:310), and the bracket content is split out and parsed by hand in NavigationKeyStop.parseBracketContent (NavigationKeyStop.java:208-230). That method currently produces a single TimestampSymbol and is unaware of the ... range form.

Deferring kept #74 to one parse path, one new AST node (TemporalRangeKeySymbol), and left all existing single-timestamp navigation behavior untouched as a regression net.

Scope of work

  1. NavigationKeyStop must carry a range, not just an instant. Today it holds a single @Nullable TimestampSymbol timestamp (NavigationKeyStop.java:266). It needs to additionally carry an optional end endpoint (or a small range descriptor) and thread it through withTimestamp() (:323), segment()/baseSegment() (:364,386), and equals/hashCode (:285-302).
  2. parseBracketContent must detect .... Reuse the same ... split logic centralized in Parsing.applyKeyBracket (added in Temporal range key-bracket binding: foo[t1...t2], foo[...t2], foo[t1...] (grammar/AST/parse only) #74) rather than duplicating it, so the parser path and lexer path stay in lockstep.
  3. NavigationKeySymbol rendering/strip must reflect the range. joinStops/components/baseKey/stripParameters (:120-129,207-248) delegate to the stop's segment(), so they should follow once the stop renders the range; withTimestampOnLastStop (:92-112) and parseScopePrefix (:47-75) need to construct the range form when the content is a range.
  4. Lift the leaf-only guard. TemporalRangeKeySymbol's constructor currently rejects wrapping a NavigationKeySymbol ("temporal-range bindings on navigation keys are not yet supported"); navigation ranges live on the stop, not on this wrapper, so the navigation path will not construct TemporalRangeKeySymbol directly — confirm the guard stays correct.
  5. Canonical round-trip. Each new shape must survive tokenizetoString → re-parse to an equal AST (the assertRoundTrip helper in BracketTimestampMatrixTest), with the canonical all-micros bracket form.

Tests

Extend BracketTimestampMatrixTest (range rows on first stop, leaf stop, mid-chain, scope prefix, transitive), BracketTimestampCommandTest (accept on reads, reject on writes/range-history), and add navigation cases mirroring the existing N* and S* sections. Update CCL_REFERENCE.md §8.4 to remove the "not yet supported on navigation/scope/transitive" caveat once landed.

Notes

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions