日本語 · English

Kairos Language Specification — 5. Grammar and Symbols

Translated from the canonical Japanese chapter spec/40-grammar.md. The source_sha above records the source revision; a consistency check flags this page when the Japanese original changes.

5.1 Symbol table

Symbol Layer / role Meaning
\|> Both layers Stage connection (body layer: stream → stream / premise layer: premise → premise)
. Both layers Premise qualification (Gregorian.month)
\| Both layers Combinator: union
& Both layers Combinator: intersection
\ Both layers Combinator: difference (U+005C — not the yen sign ¥)
= Both layers Binding (definition)
== != < <= > >= Value expr. Comparison
+ - * / mod div Value expr. Arithmetic (mod = remainder and div = integer division are words)
and or not Value expr. Logic (words; symbols are reserved for the combinators)
? : Value expr. Ternary conditional
in Value expr. List-membership predicate
[…] / l[i] Value expr. List literal / indexing (0-based). A list of instant elements is a time-stream constant (§3.8)
"…" Value expr. String literal (no newlines, no escapes; the values of TZs and provenance. ADR-32)
.. Value expr. Date range (sugar expanding to consecutive days)
=> Both layers Lambda binding (distinct from the type notation ->)
@name Preamble Lightweight reference to a premise bundle
# Both layers Line comment

5.2 Signatures of the body-layer core family

5.3 Signatures of the premise layer

Window-generating words of primitive definitions (names final; §5.4):

Derived definitions:

Sugar definitions (no dedicated syntax = the existing = binding):

5.4 Naming status

Settled (2026-07-07, as of RC2, plus ADR-31/32): the window-generating words grid/span/split/cycle; the arguments anchor:/phase:/by:/from: (mandatory in the stride family); the member epoch: (epoch); the string literal "…"; derivation’s with (all RC1 — no cracks in the 40-examples expressiveness verification). The base’s lexical name chronos (day = chronos grid 1d; the type name is likewise Chronos) and the operation axis’s axis: — the ambiguity between the two was dissolved in ADR-29, and both were confirmed as final names. The projection family’s ordinalIn/epochOrdinal/snapTo and the attachment/data-side label:/labels:/covering: — confirmed at RC2, since the internal-design confirmation (ADR-30) and the crack hunting (40-examples/04-projections.md, F34–F42) surfaced no cracks in the names. The body layer’s symbols and operator names (|>, within, segmentBy, roll, shift, stride, strideBy, filter, the selectors, the combinators) are final as well. The lexis of date and width literals was fixed in ADR-28 (§5.5).

Settled (2026-07-14, ADR-46 = designer ruling): the external-supply declaration external (the rival candidate socket was rejected because it would be the same spelling with a different sense as the “socket” metaphor of ADR-15 in the English-language documents) and the kind values dates/instants (consistent in feel with everyInstant and the date literal; a sweep confirmed no collisions with existing identifiers).

Settled (2026-07-09, the F51 batch confirmation = designer ruling): nonWorking (the reserved public word for entities; §3.9), coincides (the window-membership predicate; §4.9 — the rival candidates hits/anyIn/sharesWindow were rejected), rebase (re-anchoring; §4.4 — the rival candidates relabel/sameDate were rejected), and bizOpen/bizClose/isOpen (the standard derivations; §3.9.1) are confirmed as final names unchanged. The business-hours supply pair is renamed and finalized as sessionOpens/sessionCloses (formerly the placeholders opens/closes — ordinary English words with a wide collision surface against accidental same-name bindings; ADR-41 revised). All have seen live use (16–35 files) with no cracks in the names.

Final (2026-07-26, designer ruling — the last placeholder resolved): the window-boundary phase-shift sugar is rephase (formerly the placeholder shiftBoundary). The name matches the expansion’s substance (replacing span’s phase:) and is symmetric with rebase (re-anchor) and anchor: (the real day that pins a phase) in the vocabulary family. Rejected candidates: promoting shiftBoundary (same stem as the point transform shift despite being a different thing; 13 characters), shiftPhase (the stem confusion remains), offsetBoundary/moveBoundary (new stems). The word boundary is reserved for a possible future operator handling variable-k pairs (§3.7, out of scope). With this, every word bears its official name — no placeholders remain (the history is CHANGELOG addendum 11).

5.5 Lexis (ADR-28)

5.6 Grammar (EBNF)

The structure and the vocabulary are both final (§5.4 — no placeholders remain). Sugar (the a..b range, the point-free shorthand) disappears into core on expansion.

(* ---- Program ---- *)
program        = { statement } ;
statement      = premise-def | preamble | binding | stream-expr ;

(* ---- Premise layer ---- *)
premise-def    = "premise" , name , ( premise-block | "=" , premise-expr ) ;
premise-expr   = name , [ "with" , premise-block ] , { "|>" , stage } ;
premise-block  = "{" , { member | binding } , "}" ;
member         = member-key , ":" , value-expr ;
member-key     = "calendar-system" | "calendar" | "axis" | "roll" | "granularity"
               | "tz" | "wkst" | "asof" | "source" | "epoch" ;
                 (* epoch is exclusive to primitive-definition blocks
                    (not allowed in user-side preambles. ADR-31) *)

(* Preamble (independent statement; governs what follows until the next preamble. §3.2) *)
preamble       = ( "@" , name , { member } )            (* lightweight form + postfix folding *)
               | ( "premise" , premise-block )          (* full inline form *)
               | ( "@" , name , "{" , { statement } , "}" ) ;   (* block form (explicit extent) *)

(* ---- Bindings (public words, sugar, and value functions share one mechanism) ---- *)
binding        = name , [ "(" , params , ")" ] , "=" , rhs ,
                 [ "covering" , ":" , covering-list ] ;  (* postfix on a binding = explicit coverage
                                                            claim for a composition (ADR-37 decision 5).
                                                            When the rhs is a bare table literal, read
                                                            as a table attribute (the canonical parse.
                                                            ADR-45) *)
params         = param , { "," , param } ;
param          = name | named-param ;
named-param    = param-key , ":" , name ;
rhs            = lambda | stream-expr | gen-expr | value-expr ;

(* ---- Body layer ---- *)
stream-expr    = pipe-expr , { combine-op , pipe-expr } ;     (* one precedence level, left-assoc. *)
combine-op     = "|" | "&" | "\" ;
pipe-expr      = stream-atom , { "|>" , stage } ;
stream-atom    = table-literal | ( name | qualified ) , [ "(" , args , ")" ] | "(" , stream-expr , ")" ;
                 (* qualified application = head-position application of a qualification pin
                    (Gregorian.year(2020). ADR-42) *)
stage          = ( name | qualified ) , [ "(" , args , ")" ] ;
qualified      = name , "." , name ;
args           = arg , { "," , arg } ;
arg            = named-arg | lambda | stream-expr | value-expr ;
named-arg      = param-key , ":" , ( lambda | stream-expr | value-expr | cycle-labels ) ;
                                       (* cycle-labels only in the value position of segmentBy's
                                          labels: (ADR-47) *)
cycle-labels   = "cycle" , ( list-literal | name ) , "anchor" , ":" , date-literal ;
param-key      = "on" | "unit" | "of" | "from" | "edges" | "empties"
               | "by" | "anchor" | "phase" | "covering" | "label" | "labels"
               | "kind" | "source" ;                     (* kind:/source: are external's (ADR-46) *)

(* ---- Premise-layer window generation (infix) ---- *)
gen-expr       = operand , gen-word , gen-arg , { named-arg } ;
operand        = name ;                                  (* chronos, day, month, year, etc. *)
gen-word       = "grid" | "span" | "split" | "cycle" ;
gen-arg        = width-literal | lambda | list-literal | name | "(" , lambda , ")" ;

(* ---- Value expressions ---- *)
value-expr     = ternary ;
ternary        = or-expr , [ "?" , value-expr , ":" , value-expr ] ;
or-expr        = and-expr , { "or" , and-expr } ;
and-expr       = unary-not , { "and" , unary-not } ;
unary-not      = [ "not" ] , comparison ;                (* the function form not(x) is also allowed *)
comparison     = additive , [ comp-op , additive ] ;
comp-op        = "==" | "!=" | "<" | "<=" | ">" | ">=" | "in" ;
additive       = multiplicative , { ( "+" | "-" ) , multiplicative } ;
multiplicative = unary , { ( "*" | "/" | "mod" | "div" ) , unary } ;
unary          = [ "-" ] , postfix ;
postfix        = atom , { "[" , value-expr , "]" | "(" , args , ")" } ;
atom           = number | date-literal | width-literal | string-literal | name | qualified
               | list-literal | "(" , value-expr , ")" ;
lambda         = ( name | "_" | "(" , params , ")" ) , "=>" , ( value-expr | stream-expr ) ;

(* ---- Lists and tables ---- *)
list-literal   = "[" , [ list-elem , { "," , list-elem } ] , "]" ;
list-elem      = value-expr | date-range ;
date-range     = date-literal , ".." , date-literal ;    (* sugar expanded into consecutive days *)
table-literal  = list-literal , { "covering" , ":" , covering-list
                                | "labels" , ":" , list-literal } ; (* postfixes in either order, once
                                                            each (a duplicate is a static error;
                                                            RC5 addendum 9). A stream constant when the
                                                            elements are instants (§3.8). Zero elements
                                                            are a stream constant only with a covering:
                                                            postfix (the empty table. ADR-45). labels:
                                                            is a parallel
                                                            label sequence (ADR-30) *)
covering-list  = covering-range , { "," , covering-range } ;
                 (* interval list = declaring interior gaps (ADR-37) *)
covering-range = [ covering-edge ] , ".." , [ covering-edge ] ; (* omitted edge = open end (completeness
                                                                   claim). Bare ".." = complete everywhere.
                                                                   ADR-37 decision 9 *)
covering-edge  = date-literal | digit4 ;                 (* year-only shorthand allowed *)

(* ---- Lexis (§5.5) ---- *)
date-literal   = digit4 , "-" , digit2 , "-" , digit2 ,
                 [ "T" , digit2 , ":" , digit2 , [ ":" , digit2 , [ "." , digits ] ] ] ;
width-literal  = civil-width | elapsed-width ;
civil-width    = digits , "d" ;
elapsed-width  = [ digits , "h" ] , [ digits , "m" ] , [ digits , [ "." , digits ] , "s" ] ;
number         = digits , [ "." , digits ] ;
string-literal = '"' , { ? any character except " and newline ? } , '"' ;   (* no escapes. ADR-32 *)
name           = letter , { letter | digit } ;           (* letter = any Unicode letter (kanji allowed) *)
comment        = "#" , { ? any character up to end of line ? } ;

Note: enumeration labels (Mon, , Following) are lexically identical to name and are distinguished by semantics (resolution against the in-scope premise). table-literal and list-literal share one syntax, and the type is decided by the elements (ADR-26. Zero elements are decided by the presence of covering: — with covering = an empty table, without = an empty value list. ADR-45). The label sequence of labels: has the same length as the instant sequence (a semantic check. ADR-30; for the empty table only labels: [] is legal). Operator precedence is defined exactly by the nesting of the production rules above (the combinators share one precedence level and are left-associative; a check imposes the convention that mixing & requires parentheses. §4.5). A preamble is grammatically an independent statement, and bindings and body expressions may be listed under it (the holiday cascade of §7.5 is an example). Its effect lasts until the next preamble (§3.2) — a scoping rule, not grammar. A body expression that reaches evaluation with a dangerous member unresolved is a static error (§3.3 — likewise a governance check, not grammar).

5.7 Open questions

The canonical record of open questions and homework is ../../design/90-open-questions.md (syntax-level details in ../../design/30-syntax/00-syntax-draft.md §4). Within the scope of this specification, a separate operator for the out-of-scope variable-k pairs (introduced only if needed; the word boundary is reserved for it) and anonymous-window labeling for of: remain open (naming is final for every word = the rephase ruling of 2026-07-26, §5.4; the external-supply declaration is final as external = ADR-46, §3.8). Neither changes the semantics (additive extension; the DoD classification is at the head of ../../design/90-open-questions.md).