!!id:"https://tson.io/2026/39/m/meta.tn?sha256=c9b069b606f7cc8a043ebabe3fe91e4aaa35a9ffd814b68be0ba4bbe6d02bfb5" @doc:""" TSON meta — the pre-loaded meta kernel (2026 Revision 39 draft). The kernel holds the series' structural vocabulary: the one wait cycle a processor closes by pre-loading, the one holding `meta.record` ([TSON-SCHEMA] §3.4), every name its declarations reference, so it resolves and links on its own, and the concepts that belong with them — the map, tuple and choice constructors, held template bodies, the `meta.ref` reference, the one-element tuple templates, the octet-sequence constructor, and the classifying annotations the data-format libraries state their types with (`atom.ordering`, `atom.bounded`, `atom.exact`, `atom.numeric`). Its root holds the vocabulary an author writes: `void`, `boolean`, `integer`, `int64`, `text`, `bytes`, `iri`, `iri_reference`, `regex`, `doc`, the `enum` constructor, the `set` template and `schema`, the tag of a schema map. Everything else is machinery, a member of one of six schema maps nested in this document's root, each member named by its path from the root: meta the type system: the kinds and constructors, fields, collections, references, templates, aliases, `meta.schema`, the name families (`meta.identifier`, `meta.type_name`, `meta.field_name`, `meta.param_name`), `meta.disjoint` and the one-element tuples atom the datatype properties of ISO/IEC 11404 (`@atom.ordering`, `@atom.bounded`, `@atom.exact`, `@atom.numeric`) and the `atom.specification` mixin numeric the integer constructor `numeric.integer_type`, its facet vocabulary, and `numeric.count` unicode the text constructors `unicode.text_type`, `unicode.identifier_type` and `unicode.regex_type`, and their facet vocabulary net the IRI constructor `net.iri_type` and its schemes binary the octet-sequence constructor `binary.bytes_type` and its encodings A schema whose import closure holds this document reserves those six map names and the thirteen root names ([TSON-SCHEMA] §2.2.3). Every name resolves from the root, so an entry here naming a member of a map writes its path, as an importer does. Every map is open: any schema may add a member to it. A schema map declared by several documents of one closure is the union of their members, and a member path two of them declare collides as any name does ([TSON-SCHEMA] §2.2.3). The kernel imports nothing and is never resolved from a document: it is pre-loaded by special resolution ([TSON-SCHEMA] §3.4), and must be the revision the processor was built for. Its root tag and the tag of each map it nests, `!schema`, name its own `schema`, an alias of `meta.schema`, as every schema document's root tag does. Implementations ship the kernel's resolved structure, and this document describes it. The data-format libraries build on it, each importing this document alone and declaring its constructors under the map named for it, numeric.tn and net.tn adding to this document's `numeric` and `net`, and its instances at its root: numeric.tn (`number`, `int32`, `float64`, `numeric.float_type`), temporal.tn (`date`, `datetime`, `duration`, `temporal.date_type`) and net.tn (`uuid`, `uri`, `ipv4`, `net.uuid_type`). core.tn imports the three, so a schema importing it has every bundled type, and declares the author annotations (`@title`, `@deprecated`, …). policy.tn declares the processor policy's vocabulary. Hash pins are real digests: sha256 over every byte of a document past its `!!id` line ([TSON-DATA] §2.2.1), which is what lets an `!!id` carry the digest of the document it identifies. Each library pins this file and core.tn pins the libraries, so an edit here moves every pin above it. References: ISO/IEC 11404:2007 General Purpose Datatypes (the classifying annotations) RFC 3987 Internationalized Resource Identifiers (IRIs) RFC 4648 Base encodings (text spellings of `bytes`) RFC 9485 I-Regexp, the interoperable regular expression CommonMark 0.31.2 The text of `doc` """ !schema { numeric => !schema { @doc:""" Fixed-width integer representation: a bit width paired with its two's-complement signedness. A width above zero is a schema-load coherence check: zero or a negative `bits` is a schema-load error. """ integer_size => { bits: integer signed: boolean } @doc:""" The members of an integer_type. Member identity is [TSON-DATA] §4.3's, so 80 and 0x50 are one member and a duplicate rather than two. """ integer_member_set => !meta.set_type { element_type: integer min_items: 1 } @doc:""" Integer constraint vocabulary. Atom constructor whose instance, `integer`, is the arbitrary-precision integer. Its field types name `integer`, the constructor's own instance; a name at a type position orders nothing, so the mutual reference closes. Both reach every schema whose import closure includes this document. Bounds: each side is a field group ([TSON-SCHEMA] §5.11) — the inclusive `min` or the exclusive `exclusive_min`; likewise `max` or `exclusive_max` — so at most one bound per side holds by shape. That the lower bound not exceed the upper (across whichever forms are present) is a schema-load coherence check. Fixed width: `size` gives a fixed representation width; when absent the integer is unbounded arbitrary precision (the base `integer`). Width and signedness derive the representable range — signed n-bit is [-2^(n-1), 2^(n-1)-1], unsigned n-bit is [0, 2^n-1] — so `size` and the bound groups are equivalent ways to state a bound. The two compose: bounds and `multiple_of` narrow within the width-derived range, and a bound outside that range is a resolver error. Signedness is two's complement. `size` names the target representation a consumer decodes into, not the source form. Resolver output stores `size` as written and does not expand it to bounds. Step: `multiple_of` is strictly positive; zero or a negative value is a schema-load error. The test ignores the sign of the value, so -30 is a multiple of 15. A refinement may only tighten `multiple_of` to an integer multiple of the inherited value (15 under 5 is a tightening; 10 under 15 is an error). It composes with every other facet present: a value must satisfy all of them. Enumeration: `members` lists the admitted values outright — the spelling for numeric codes, an enum's members being text (`!integer ^ { members: [1 2 3] }`, not `!enum [1 2 3]`). Every member must itself satisfy the bounds, the width and `multiple_of` present on the same body — a member outside them is a schema-load error — and a refinement may only shrink the set (the member-set rule of the tightening table). A value must be a member and satisfy the other facets; with a coherent set the second test is implied by the first. """ integer_type => meta.atom & { size?: numeric.integer_size ( min: integer | exclusive_min: integer )? ( max: integer | exclusive_max: integer )? multiple_of?: integer members?: numeric.integer_member_set } @doc:""" The type of every facet that counts something: lengths, item counts, digit counts, prefix lengths, fractional-second precision. It is the non-negative half of `int64`, 0 to 2^63-1, so every implementation holds a count in a native type (JavaScript in a BigInt). """ count => !int64 ^ { min: 0 } } unicode => !schema { @doc:""" The members of a text_type. The set's own uniqueness compares the text as written; each member is then put into the constrained type's normalization form, and two that are one value there are a duplicate, as text_type says. """ text_member_set => !meta.set_type { element_type: text min_items: 1 } @doc:""" Text constraint vocabulary. Atom constructor whose instance, `text`, is the kernel's Unicode code point sequence type. Lengths count code points. `members` lists the admitted strings outright. Every member must satisfy the other facets on the same body, the pattern included. `normalization` is the form a value is put into. A value is its token's text, unquoted, unescaped and then put into that form; every other facet judges the value and never the spelling, and so does every comparison of two values. A member is a value too, and two members that are one value are refused. `pattern` and `members` are each settable once: a refinement may set one the source left unset, or restate the source's own, and never change it. `normalization` is fixed where the type is constructed: a refinement restates it or leaves it, since changing it would change what a token means rather than narrow the values admitted. """ text_type => meta.atom & { min_length?: numeric.count max_length?: numeric.count length?: numeric.count pattern?: regex members?: unicode.text_member_set normalization?: unicode.normalization ~ NONE } @doc:""" The base a UAX #31 Start or Continue set is drawn from: XID_Start / XID_Continue, ID_Start / ID_Continue, their ASCII members (Start A-Z a-z, Continue adding 0-9 and "_"), or the empty set. """ identifier_base => !enum [XID ID ASCII NONE] @doc:""" The form a text value is put into. NONE keeps the text as written. NFC and NFKC are Unicode's normalization forms. NFKC_CASEFOLD is toNFKC_Casefold, UAX #31's form for names compared without case: over ASCII it lowercases, so Content-Type and content-type are one value. ASCII_CASEFOLD maps A-Z to a-z and changes nothing else, the comparison of case-insensitive ASCII names such as URI schemes and HTTP field names. """ normalization => !enum [NONE NFC NFKC NFKC_CASEFOLD ASCII_CASEFOLD] @doc:""" Identifier constraint vocabulary: a UAX #31 identifier profile, with the text facets applied inside it. It composes text_type's record shape, so it takes lengths, a pattern such as a naming convention, and members for a closed vocabulary of names. The profile is UAX #31's R1 default identifier syntax: meta.identifier := Start Continue* (Medial Continue+)* Start := (start ∪ start_add) − exclude Continue := (continue ∪ continue_add) − exclude Medial := medial where start and continue name the base each set is drawn from, and each of start_add, continue_add, medial and exclude is the set of code points its text holds. A medial character stands only between two others and never beside another medial, so medial must be disjoint from Start and Continue. The profile judges the value, which is the text put into the normalization form; the form defaults to NFC here. A join control (U+200C, U+200D) is admitted only in the contexts UTS #39 §3.1.1.1 permits, whatever the profile. The profile facets are fixed where the profile is constructed: a refinement restates each or leaves it, and narrows only the text facets. A different profile is a fresh `!unicode.identifier_type`. identifier is [TSON-DATA] §7.7's instance, and the type of every naming position in the series — type names, field names and parameter names, through the three roles below. A rule stated here reaches all of them. An identifier is a name, not a lexeme: it is the decoded text of a token, after unquoting, escape processing and normalisation, and it is constrained however it was spelled. Its profile is stated beside the unquoted-token profile of [TSON-DATA] §7.1: Start = XID_Start Continue = XID_Continue + "-" and its value is the text in NFC. The profile is the token profile minus the extensions the number grammar requires, so an identifier never begins with a digit or a sign, which subsumes the rule that numbers are not declarable names. "+" is dropped from Continue, and "." is reserved as a future identifier separator. Everything else follows from XID membership: whitespace, control characters, emoji, unassigned code points and every format character but the two join controls are none of them XID_Continue. Every part of identifier's profile lies inside the unquoted-token profile, so every identifier is a well-formed unquoted token and none ever needs quoting. Another profile carries no such guarantee, and a value under it may need quoting. A schema that types a value or a map key as a name of another family declares its own identifier_type instance under a name of its own, such as `slug => !unicode.identifier_type { continue_add: "-" }`. """ identifier_type => unicode.text_type & atom.specification & { spec?: = "https://www.unicode.org/reports/tr31/" start?: unicode.identifier_base ~ XID continue?: unicode.identifier_base ~ XID start_add?: text continue_add?: text medial?: text exclude?: text normalization?: unicode.normalization ~ NFC } @doc:""" Regex constraint vocabulary. Same shape pattern as iri_type, pinned to I-Regexp. A regex is its text as written: putting it into another form would change what it matches. """ regex_type => unicode.text_type & atom.specification & { spec?: = "https://www.rfc-editor.org/rfc/rfc9485" normalization?: = NONE } } net => !schema { @doc:"The schemes an iri_type or uri_type admits." scheme_set => !meta.set_type { element_type: net.scheme_name min_items: 1 } @doc:""" A URI scheme (RFC 3986 §3.1): a letter, then letters, digits, "+", "-" and ".". A scheme compares without case, so its value is the text folded. """ scheme_name => !unicode.identifier_type { start: ASCII continue: ASCII continue_add: "+-." exclude: "_" normalization: ASCII_CASEFOLD } @doc:""" IRI constraint vocabulary per RFC 3987. Composes with the text_type constructor to inherit its record shape (length and pattern fields) plus the atom.specification mixin's spec field plus iri-specific fields. `schemes` lists the admitted schemes as net.scheme_name values, which fold case as RFC 3986 §3.1 compares a scheme, and a value's scheme is compared in that form; a value with no scheme is outside it. `allow_relative` admits a relative reference beside an IRI, so its default is an IRI-reference; withdrawing it requires a scheme. `allow_fragment` admits a fragment; withdrawing it refuses one. An IRI's characters beyond US-ASCII are RFC 3987's `ucschar`, and `iprivate` within the query, and an IRI is judged through the URI it maps to (RFC 3987 §3.1). `uri_type` is the US-ASCII family with the same facets. An IRI is its text as written: RFC 3986 says which of its parts compare without case, and a form over the whole text would fold a path or a query that is case-sensitive. """ iri_type => unicode.text_type & atom.specification & { spec?: = "https://www.rfc-editor.org/rfc/rfc3987" schemes?: net.scheme_set allow_relative?: boolean ~ true allow_fragment?: boolean ~ true normalization?: = NONE } } atom => !schema { @doc:""" Mixin record contributing the `spec` field to specification-bound atoms. """ specification => { spec: iri } @doc:""" Ordering classification (ISO/IEC 11404 / XSD fundamental facet): whether the value space has an order relation. Not a container's `ordered` facet, which says whether element order is part of a value. """ ordering => @meta.annotation !enum [NONE PARTIAL TOTAL] @doc:""" Boundedness classification (ISO/IEC 11404 / XSD fundamental facet): true when the value space has a finite least and greatest value. """ bounded => @meta.annotation boolean @doc:""" Exactness classification (ISO/IEC 11404 characterizing property): true for exact types (values preserved as written), false for approximate types (values rounded to a representation grid). """ exact => @meta.annotation boolean @doc:"Numeric type marker annotation." numeric => @meta.annotation void } @doc:"The type system, each member named by its path, `meta.record`." meta => !schema { @doc:""" The structural root: every constructor IS-A `top`. Every constructor transitively IS-A `atom`, `product` or `sum`, except `alias` and `template`, which compose with `top` directly; `atom`, `product` and `sum` IS-A `type`, which IS-A `top`. Construction transfers kind, not IS-A, so instances and fresh records sit under the kinds with empty chains. `top` and the kinds beneath it are abstract: none has instances of its own, so a direct application of one is refused as any abstract record's is, naming the subtypes that would satisfy it. A schema map's entries are types or values (`schema`). An entry whose value is built by a constructor IS-A `type` is a type; one whose value is built by `alias` or `template` is an alias or an open entry; and one whose value is an instance of an ordinary type, `orders => !api { ... }` or `create_order => !operation { ... }`, is a value, marked by nothing but its tag. """ top => abstract {} @doc:""" Where an entry sits in the IS-A order. `parents` is what was written: the parents of a record template, one of which may be an application (` result & { ... }`), kept on the template entry once they resolve; a closed instantiation keeps none. It is the only list that ever holds an application. `supertypes` is the closed chain: every entry this one IS-A, complete to a fixed point through applied parents ([TSON-SCHEMA] §5.8), each a name without arguments, sorted, and empty for a fresh record, an instance and a subtracted record alike. `subtypes` is the inverse, over the entries of this map only ([TSON-SCHEMA] §3.3.4). """ type_chain => { parents?: [meta.type_ref] supertypes?: [meta.type_ref] subtypes?: [meta.type_ref] } @doc:""" The kind of kinds that describe the shape of a data value: `atom`, `product` and `sum` IS-A `type`, and nothing else does. An entry whose value is a body whose constructor IS-A `type` is a type; an `alias` or a `template` is not, composing `top`, and an entry whose value is an instance of an ordinary type is a value, never a type. A type carries its IS-A chain. A declaration never writes `chain`, and a constructor never declares a field of that name: the resolver computes the chain from composition and refinement and the linker inverts it. On ingest it is read as stated and never recomputed, because a flat body and its chain cannot be recomputed from each other. """ type => abstract meta.top & { chain?: meta.type_chain } @doc:""" The kind of a scalar: a value written as one token, which its atom family reads into a value. """ atom => abstract meta.type & {} @doc:""" The kind of a compound value of parts: a record, an array, a map or a tuple. `access_pattern` says whether a part is reached by position or by name, and `size_type` whether the number of parts is fixed by the type. """ product => abstract meta.type & { access_pattern: meta.product_access_type size_type: meta.product_size_type } @doc:""" The kind of a value that is one of several types: a choice or a scope. """ sum => abstract meta.type & {} @doc:""" Aliasing body — the entry `target` names: a type, or a constructor, since an alias of a constructor is applied as its target is ([TSON-SCHEMA] §3.3.1), a constructor's value being a record. A partial application ([TSON-SCHEMA] §5.10, `uuid_pair => pair`) is an open entry, and its target is the reference as written, inside the `template.template` text: `!meta.alias { target: pair }`. An alias is a hop, not a rewrite: resolved output states the chain as the author wrote it, and a use site names whatever entry it names. A processor collapses the chain when it compiles readers, after linking, once per entry. A walk stops at an argument-bearing target, which is an application rather than a further hop. """ alias => meta.top & { target: meta.type_ref } @doc:""" One parameter of an open entry: its name, and the type an argument for it is read as — the declared type of the slot it stands in, in the vocabulary of the constructor the held body applies. It is derived from the body and never required of the author: a type slot gives `meta.type_ref`; a scalar slot of the applied constructor gives that slot's declared type (`min_items: N` gives `numeric.count`); a routed default or fixed value gives the field's own declared type (`w?: int32 ~ N` gives `int32`); and a parameter passed to another template takes that template's recorded type for the position, with `meta.type_ref` where nothing grounds it. A parameter is a type parameter exactly when its `type` is `meta.type_ref`. A `type` may name an earlier parameter of the same template, where a routed default's field is typed by one. A declaration may narrow what it derives by writing a type after the name, ``, read by the kind the positions give. On a value parameter it narrows `type` itself and must IS-A the derived type. On a type parameter it is `bound`: an argument must name a type that IS-A it. `bound` is present only where `type` is `meta.type_ref`, and a parameter passed to another template inherits that template's bound for the position, so an unannotated parameter is bounded as narrowly as its uses require. """ template_param => { name: meta.param_name type: meta.type_ref bound?: meta.type_ref } @doc:""" Held body — the body of an entry that declares type parameters and applies any constructor but `record` ([TSON-SCHEMA] §5.10), a record template being a `record_template`. `template` is the constructor application as written, unread until materialisation substitutes the parameters away, and `parameters` lists what it binds, each a `meta.template_param`. The text is how a held body is written in resolved output; a processor holds the parsed form, which this vocabulary cannot represent. A held body is not a value of this vocabulary: a parameter stands wherever a token stands — `min_items: N` as readily as `element_type: T` — so a body carrying one is not typed by any constructor's own record shape until it closes. An open entry's value is this, built by a constructor IS-A `top`. What is compared is the parsed form and never the text: an open synthetic's identity is derived from its held binding record, so two spellings of one form must reduce to one entry, and whitespace is free. Composes `top` alone: it is not a type, its open form denotes nothing, and naming it at a type position is an error. An application is its expansion, and carries whatever chain the expansion's own `^` or `&` gives it. """ template => meta.top & { parameters: [meta.template_param] template: text } @doc:""" Void primitive. The instance of void_type, an atom constructor with no constraint vocabulary whose parsing contract admits only the void sentinel `_`. It binds no host value. void is the target type for bare annotations (`@T` with no value); the resolver fills the implicit `_` and validates against this contract. See [TSON-SCHEMA] §6. """ void_type => meta.atom & {} @doc:"The default identifier type used for types/fields/params" identifier => !unicode.identifier_type { continue_add: "-" } @doc:"A type's name: a schema-map key, and a segment of a reference." type_name => meta.identifier @doc:"A field's name, in a record or a field group." field_name => meta.identifier @doc:"A template parameter's name." param_name => meta.identifier @doc:"How a product's parts are reached: by position, or by name." product_access_type => !enum [INDEX NAMED] @doc:"Whether a product's number of parts is fixed by its type." product_size_type => !enum [FIXED VARIABLE] @doc:""" What a record field's `value` is for: FREE has none, DEFAULT is injected when the field is omitted and may be overridden, and FIXED is the only value the field admits. """ field_role => !enum [FREE DEFAULT FIXED] @doc:""" How a record may be realised ([TSON-SCHEMA] §5.2). OPEN is the ordinary case and the default: the record has instances of its own and may be composed onto or refined. ABSTRACT has no instances of its own, so a value at a position typed by it is a value of some subtype. FINAL has instances and admits no subtype: no composition or refinement may name it as a source. Subtraction stays admissible against a FINAL record — a subtracted record's chain is empty, so it mints no IS-A edge, which is the only thing FINAL constrains. **How a subtype is selected is no part of this enum.** An ABSTRACT record naming no `discriminators` is selected by the tag; one naming them is selected by reading those fields. Extensibility is never inherited. A subtype of an ABSTRACT record is OPEN unless it says otherwise. **A field written `=?` implies ABSTRACT.** Writing `abstract` beside a selector asserts what the body already says and is admitted; `final` beside one is refused. **A family is open across schemas.** [TSON-SCHEMA] §3.3.4 makes `subtypes` open, so an importing schema may declare a further member. A host language's own closed-set construct, generated from a family, is therefore relative to one closure and not a promise the schema makes. The member is written as a word at the declaration — `pet => abstract { … }`, [TSON-SCHEMA] §12.1 — and OPEN is the absence of one. """ record_extension_type => !enum [ABSTRACT FINAL OPEN] @doc:""" The annotation type marker: a type carrying it is meant as annotation metadata ([TSON-SCHEMA] §6). """ annotation => void @doc:""" Resolver-attached and derived: discarded and recomputed on ingest ([TSON-SCHEMA] §6, §8.1), and marks a resolver-materialised synthetic entry at its schema-map key (§8.2). """ synthetic => @meta.annotation void @doc:""" Resolver-attached and derived: on an entry's schema-map key, the application the entry is the instantiation of ([TSON-SCHEMA] §8.2). An entry of any kind can be one, so it is a fact about the entry and not about a type: the resolver writes it wherever an entry closes an application — a record template's member, an instance closed from a templated construction, the alias a template's application closes to. On ingest it is read as stated, unlike `synthetic`, which is recomputed: it is the one fact the entry's value does not state, and what lets an imported schema's instantiations unify with a later schema's. A declaration never writes it, as it never writes `chain`. Its value is the application as the reference written, nested arguments unreduced, `@meta.application:foo>`. """ application => @meta.annotation meta.type_ref @doc:""" Supporting records. A record field is the four facts a reader consults: `optional`, that the key may be missing; `voidable`, that the value may be void, the void sentinel `_` written in its place; `role`, what `value` is for; and `value`, a concrete default or fixed value read against the field's declared type — for a `meta.type_ref` slot, a type name. `value` is typed `type!`, a dependent field: each record field's `value` is read against the type its own `type` names. Its name is not among them: a field is a value of `record.fields`, keyed by its name. Each fact is one mark of the spelling `name?: type? ~ value`: the name's `?`, the type's `?`, and `~` or `=` with its value. What omission yields is derived, never stored: a missing field that is not optional is an error, and a missing optional one with a value has it injected — except at a field group's member, whose presence is the group's and which is never supplied. A record field is closed: its `type` names a type or a sibling, and its `value` is a literal, never a parameter. A field that may carry one is a `meta.record_template_field`, which only a record template holds. A field whose `type` is a sibling reference, `kind!`, is a dependent field ([TSON-SCHEMA] §5.2): each instance's value is read against the type that instance's `kind` names. The sibling is declared before it, is typed by a reference whose `of` lies within `meta.type`, is never voidable, and is there to read whenever the dependent field is: required, defaulted, pinned, or a member of a field group with the dependent field optional, a group's own member counting as optional. Its default or pin names a type a value can be read against. A member is absent when another option is chosen, and a dependent field written without its sibling is refused where it is read. A dependent field is never voidable, carries no value of its own, and is restated only as itself; pinning its sibling fixes its type. Which fields a member-dispatched family dispatches on is the enclosing record's own statement, `record.discriminators`, and no field says it of itself. A field it names is required and not voidable, no member of a field group, typed by an atom or an enum, and carries no value of its own; each subtype restates it FIXED, the pins pairwise distinct under the field type's own equality. The two facts are the series' two kinds of nothing, and each has one name wherever it applies: a slot that may be missing is `optional`, on a record field and a field group; a slot whose value may be void is `voidable`, on a record field, an array's element, a map's value and a tuple position. A container's part is never missing, so a container has no `optional`. """ record_field => { type: meta.field_type_ref optional?: boolean ~ false voidable?: boolean ~ false role?: meta.field_role ~ FREE value?: type! } @doc:""" Field group — a presence rule over fields of a record ([TSON-SCHEMA] §5.11). `members` lists the group's options in source order, each the fields it holds; a group has at least one option, and an option at least one field. Every member is declared in the enclosing record's `fields`, `optional` there and carrying no value, and sits in one option of one group. An option is chosen when any of its members is present. `optional_members` names the members that may be missing once their option is chosen, and never the only member of an option; a chosen option holds every member it does not name. `optional` says whether the group may be missing as a whole, as an optional field's key may: without it exactly one option is chosen, with it at most one. A group of one option is either not optional with every member named in `optional_members`, so at least one is present, or optional with at least two members and one not named. The flattened fields-plus-groups encoding is canonical. """ field_group => { members: [[text; 1..]; 1..] optional_members?: [text; 1..] optional?: boolean ~ false } @doc:""" The fields of a record ([TSON-SCHEMA] §5.2): each field's name mapped to its facts, in declaration order, which is part of the value. The map's own key uniqueness compares the text as written; each key is then read as a value of the record's `name_type`, and two that are one value under that type's equality are a duplicate, a resolver error. A field name is text within its own record and is no namespace key: no qualified name selects a field declaration. """ field_map => !meta.map { key_type: text value_type: meta.record_field ordered: true } @doc:""" A field of a record template: a record field's four facts, where the type and the value may each be a parameter of the enclosing template, and the label says which. `type_param` names a parameter standing for the whole type, `value_param` one standing for the whole value; `type` and `value` are as on a record field. A parameter may also stand as an argument of an application in `type`, `list`, recognised there because its text resolves into the template's `parameters` ([TSON-SCHEMA] §5.10's shadowing rule). An application substitutes every parameter away, and each of its fields is then a `meta.record_field`: `type_param` becomes `type` and `value_param` becomes `value`, carrying the argument, and no other fact changes. A literal `value` needs a closed type, so a field whose type is a parameter, or an application over one, takes `value_param` or nothing. A field of a closed type takes either. `value` is typed `type!`, as on a record field: beside `type_param`, with `type` absent, it has no type to be read against and is refused where it is read. A parametric modifier takes the facts its literal spelling takes ([TSON-SCHEMA] §5.7): `w?: T ~ P` is an optional DEFAULT field and `w?: T = P` an optional FIXED one, `w: T = P` is a FIXED field that is not optional, and `w: T ~ P` is refused. A field the template's `discriminators` names may be FIXED to a parameter, optional or not as its name is marked (`type?: text = T`): each application supplies its pin, and the base reads the field required with that pin dropped. Its type is never a parameter, so the base reads it without an application. """ record_template_field => { ( type: meta.field_type_ref | type_param: meta.param_name ) optional?: boolean ~ false voidable?: boolean ~ false role?: meta.field_role ~ FREE ( value: type! | value_param: meta.param_name )? } @doc:""" The fields of a record template, as `meta.field_map` is a record's: each field's name mapped to its facts, in declaration order. """ record_template_field_map => !meta.map { key_type: text value_type: meta.record_template_field ordered: true } @doc:""" A record: named fields, fixed by the type, with the field groups over them. A record is closed — no field carries a parameter — and it is what readers and writers are built for; a record with parameters is a `meta.record_template`. `extension` states how the record may be realised, and is OPEN unless the record says otherwise. Of the constructors, only `record` and `record_template` carry it. Its chain is `type`'s. `fields` is flattened: a composition's fields are its operands' and its own, merged, so a consumer that reads values against it never repeats the merge. `discriminators` names the fields this record's members are selected by, in the order their pins are compared as a tuple, and its presence is what makes the family member-dispatched rather than tag-dispatched. Every name must name a field the enclosing record declares, and the record must be ABSTRACT. The author writes `=?` on the field, which is what lowers into this list ([TSON-SCHEMA] §5.2). `name_type` names the family this record's field names belong to, as an enum_type's names its members': every name in `fields`, `discriminators` and the groups is a value of it, and so is every field name a data value of this record writes. It names an identifier family, a member of `meta.identifier_ref`, so every field name is a name under its profile, and the field name slots are therefore `text`, each judged by this type. A record's name type is set where it is declared and a subtype cannot change it: absent, it is its supertypes' one name type, and `meta.identifier`, which this document names `meta.field_name`, for a record with none; stated beside supertypes, it is theirs. Supertypes with different name types do not compose. """ record => meta.product & { access_pattern?: meta.product_access_type = NAMED size_type?: meta.product_size_type = FIXED fields: meta.field_map groups?: [meta.field_group] extension?: meta.record_extension_type ~ OPEN discriminators?: [text; 1..] name_type?: meta.identifier_ref } @doc:""" A record template ([TSON-SCHEMA] §5.10): a record body over `parameters`, whose fields are `meta.record_template_field`s and may carry them. Each application substitutes its arguments for the parameters and closes to a `meta.record`, its member. A record template is a **family base**: a type a position may name, ABSTRACT by construction, a value there being a value of one of its members, each IS-A it. It takes part in IS-A as any abstract parent does, and nothing reads or writes a value against it, so no reader or writer is built for it: a reader at a position it types dispatches to a member and reads against the member's closed record. That is why it is its own constructor and not a record with an optional list of parameters. A parameter stands only in a field's `type_param` or `value_param`, or as an argument of an application in a field's `type` or in a parent; the rest of the template is what every application shares. Its type and bound are derived from where it stands, as `meta.template_param` says. `extension` states how each application is realised, the template's own being ABSTRACT whatever it says, so `result => abstract { ... }` makes `result` abstract over its own members. `discriminators` is the family's, as on an ABSTRACT record, and every field it names is typed by no parameter, so the base reads each one without an application. `groups` and `name_type` are each application's, unchanged by substitution. Its chain is `type`'s. A parent may still be open — ` result & { ... }` — and it is substituted and closed with every other reference the template holds, so `ok` reaches an IS-A edge to `result` and not to `result`. `fields` is flattened, as a record's is. """ record_template => meta.product & { access_pattern?: meta.product_access_type = NAMED size_type?: meta.product_size_type = FIXED fields: meta.record_template_field_map groups?: [meta.field_group] extension?: meta.record_extension_type ~ OPEN discriminators?: [text; 1..] name_type?: meta.identifier_ref parameters: [meta.template_param; 1..] } @doc:""" A sequence of elements of one type. The container constructors carry no parameters: a type slot (array's element_type, map's key_type and value_type) is an ordinary required field typed `meta.type_ref`, filled by the construction or by a sugar form's desugaring like any other required field, and a closed application carries the bound reference as the field's value (`!meta.array { element_type: person }`), its binding record being ordinary data of this vocabulary. Inside a held template body ([TSON-SCHEMA] §5.10) any slot — type, scalar, or collection — may hold a parameter token, substituted away before the body is read against this vocabulary. `voidable` says whether an element may be void, spelled `[T?]`. `ordered` says whether order is part of a container's value: true for an array unless it says otherwise, false for a set. It never changes what a document may write, and output keeps the order written either way ([TSON-SCHEMA] §7.5) — the facet says whether two values differing only in order are one value. """ array => meta.product & { access_pattern?: meta.product_access_type = INDEX size_type?: meta.product_size_type = VARIABLE element_type: meta.type_ref voidable?: boolean ~ false ordered?: boolean ~ true unique_items?: boolean ~ false min_items?: numeric.count max_items?: numeric.count } @doc:""" The unordered, unique refinement of array. Its bounds are array's: the empty set is a set, and a set that must be non-empty says `min_items: 1`. The template an author writes is the root's `set`, `set => !meta.set_type { element_type: T }`, so a field reads `tags: set`. """ set_type => meta.array ^ { voidable?: = false ordered?: = false unique_items?: = true } @doc:""" Entries keyed by distinct values of one type. `voidable` says whether an entry's value may be void, spelled `{K => V?}`, as array's says it of an element; a key is never void. `ordered` is false unless the map says otherwise, as for a JSON object: two maps holding the same entries in different orders are one value. An ordered map, `!meta.map { … ordered: true }`, is one whose entry order is part of its value, so those two are distinct, and a host binds it to a map that keeps its order. Either way output keeps the order written ([TSON-SCHEMA] §7.5). """ map => meta.product & { access_pattern?: meta.product_access_type = NAMED size_type?: meta.product_size_type = VARIABLE key_type: meta.type_ref value_type: meta.type_ref voidable?: boolean ~ false ordered?: boolean ~ false min_items?: numeric.count max_items?: numeric.count } @doc:""" A map keyed by names: the constructor of a namespace type, a refinement of `map` whose `key_type` narrows from `meta.type_ref` to `meta.identifier_ref`, which is IS-A it, and whose entries are never void. A namespace type is an instance, as a map type is one of `map`: `api => !meta.namespace { key_type: meta.identifier value_type: method }`, and an entry holding a value of it, `orders => !api { ... }`, is an ordinary value of the type `api`. Its keys are names, of an identifier family, which is what a qualified name's segment is, and a segment selects a key of a namespace value and of no other map. `key_type` is the family every key is read and judged under. `ordered` and the size bounds are map's. The schema map (`schema`) is the kernel's one. """ namespace => meta.map ^ { key_type: meta.identifier_ref voidable?: = false } @doc:""" One position of a tuple: its type, and whether its value may be void. A position is never missing, so `[A, B?]` admits `[a _]` and refuses `[a]`. """ tuple_element => { element_type: meta.type_ref voidable?: boolean ~ false } @doc:"A fixed sequence of positions, each with a type of its own." tuple => meta.product & { access_pattern?: meta.product_access_type = INDEX size_type?: meta.product_size_type = FIXED elements: [meta.tuple_element] } @doc:""" A tuple of exactly one element: `meta.tuple1` admits `[a]` and refuses `[]`, `[a b]` and `[_]`. `[T]` is an array, and the tuple sugar takes two or more positions. """ tuple1 => !meta.tuple { elements: [{ element_type: T }] } @doc:""" A tuple of exactly one voidable element: as `meta.tuple1`, and the element may be void, so `meta.voidable_tuple1` admits `[a]` and `[_]`. """ voidable_tuple1 => !meta.tuple { elements: [{ element_type: T voidable: true }] } @doc:""" The members of an enum_type. The set's own uniqueness compares the text as written; each member is then read as a value of the enum's `name_type`, and two that are one value under that type's equality are a duplicate. """ enum_set => !meta.set_type { element_type: text min_items: 1 } @doc:""" An enumeration: a closed set of names, and the type they are drawn from ([TSON-SCHEMA] §7.4), as a record's `name_type` is its field names'. `name_type` names an identifier family — a member of `meta.identifier_ref`, a type built with identifier_type — and each member must be a value of it, distinct from the others under its equality, and is a name under its profile ([TSON-DATA] §8.2). A closed set of text that is no name is the text family's own `members` facet, `!text ^ { members: [...] }`; an enumeration of names a profile admits beyond the kernel's identifier, such as words separated by single spaces, is an enum_type over the author's own identifier_type. A value `name_type` takes from a constructor resolves where that constructor is declared; one an author writes resolves in the author's own namespace, so a schema may enumerate names of a naming vocabulary it declares itself. `name_type` is fixed once an enum is constructed: a refinement narrows the members and never the type. """ enum_type => meta.atom & { name_type: meta.identifier_ref members: meta.enum_set } @doc:""" Closed sum. `disjoint` is a resolver-derived index over the variant list, parallel to `subtypes`: true or false by discrimination-class distinctness ([TSON-SCHEMA] §5.4). Declarations never set it; on ingest it MUST be discarded and recomputed. It carries the encoding-independent fact each encoding's discrimination rules consume ([TSON-SCHEMA] §5.4, §7.2). """ choice => meta.sum & { variants: [meta.type_ref] disjoint?: boolean } @doc:""" Author's assertion that a choice's variants are distinguishable by an encoding's form resolution — discrimination-class distinctness, not disjointness of inhabitance — checked against the resolver's derived `disjoint` fact in the `!meta.choice` body ([TSON-SCHEMA] §5.4, §8.1). The fact is class-stable: a variant whose forms straddle classes (an approximate atom still admitting NaN or the infinities, a map with a compound key) has no class, so the untagged values of a choice are one set in every encoding. A void-targeted presence marker like `@atom.numeric`: written bare, and presence is the assertion. Carries no decode force — the resolver computes disjointness whether or not the annotation is present. The derivation is total: present and refuted is a schema-load error; present and verified, or absent, is silent. """ disjoint => @meta.annotation void @doc:""" Constructor for the open sum over the governing closure: the value names its own type by its tag, among the types of the closure ([TSON-SCHEMA] §2.2.3), and is validated in full against it. A value naming no type is invalid. Where `choice` is the closed sum, enumerating its variants, this is the open one, drawing them from the closure; `disjoint` is absent, as on every non-choice sum. """ declared_type => meta.sum & {} @doc:""" A value of a type the closure declares, chosen by the value: the tag names the type, the name resolves in the closure exactly as it does anywhere else, and the value is validated in full against it. What a schema map's entries are. """ declared => !meta.declared_type {} @doc:""" A name held in a slot: an atom whose values are names, each naming an entry whose value's type, past the entry's alias hops ([TSON-SCHEMA] §8.3), is `of` or IS-A it. Every entry is a name and a value, and the value has a type, its tag: `order => { ... }` holds a value of `record`, `int32 => !integer ^ { ... }` a value of `integer_type`, and `orders => !api { ... }` a value of `api`. A field's type position, a `meta.ref` slot and a `meta.ref` slot all admit a name by that one predicate, and differ only in `of`. `of` names any type or constructor of the writer's namespace ([TSON-SCHEMA] §3.3.1), never a value or an open entry. Where it is a constructor, `top` or IS-A it, the values it admits are types, and a slot holds a type's name, which takes part in structural identity ([TSON-SCHEMA] §8.2). A record written in braces is built with `record`, and so is `top` itself and a constructor whose declaration is a record, so `meta.ref` holds all of them; a record template is built with `record_template` and is not among them. A template over another constructor is no type, and an application of one is the type it expands to. Where `of` is any other type, the values it admits are values, and a slot holds a value's qualified name: the first segment resolves in the writer's namespace and names an entry, and each further segment selects a key of a namespace value or a field of a record value. A map that is no namespace is not entered. A type's name is its entry's path from the document's root: `order`, or `geo.address` for a member of a schema map nested as `geo => !schema { ... }`, and every name resolves from the root. A value is the name, never a value of the type it names. In a data document it resolves in the namespace of the governing schema and is judged there. In a schema document its grammar is read where it is written, and the name is judged when that schema links: the set of names a reference admits is the closure's, derived and never written. A value may carry an argument list, `geo.address`: after its qualified name, an adjacent `<`, one or more arguments, and `>`. It is data grammar ([TSON-DATA] §7.4), written unquoted wherever a value stands. An argument is a reference or a token, positional against the head's parameters, and typed where it lands: a type argument is judged as the parameter's `bound` says, and a value argument is a token of the parameter's type ([TSON-SCHEMA] §5.10). The canonical text has no whitespace but one space between arguments: `pair`, `maxint<10>`. Identity is the parsed form ([TSON-SCHEMA] §8.2), so whitespace inside an argument list is free. Every application denotes an entry: a closed one lifts to the entry it denotes and is named bare where it stands ([TSON-SCHEMA] §5.3), so a value carries arguments only where an application is still open, in a record template's `parents` and field types, in held text, and in the `application` annotation. A refinement narrows `of`: the refined `of` is the source's or IS-A it, so every name the refinement admits the source admits too, and the refinement is IS-A its source. `meta.identifier_ref` is `meta.type_ref` narrowed to the identifier families. A value may instead be a sibling reference, `kind!` ([TSON-DATA] §7.4): one identifier with an adjacent `!` followed by whitespace, naming the field `kind` of the same record instance, whose value names the type ([TSON-SCHEMA] §5.2). A reference is a name, an application or a sibling reference, and `allow_sibling` says whether the last is admitted: as `allow_nan` is of `numeric.float_type`, it is a permission, true by default, and a refinement may withdraw it and never grant it back. It is true only at a record field's type, `meta.field_type_ref`; every other reference sets it false and refuses a sibling reference. Resolved output writes a sibling reference as it is written. """ reference_type => meta.atom & { of: meta.type_ref allow_sibling?: boolean ~ true } @doc:""" The spelling of a reference: `meta.ref` is `!meta.reference_type { of: T allow_sibling: false }`. Where `T` is a constructor it holds the names of the types built with it, `meta.ref`, and otherwise the names of values of `T`, `meta.ref`. It never holds a sibling reference. """ ref => !meta.reference_type { of: T allow_sibling: false } @doc:""" The type of a record field: the name of a type a value can have, or a sibling reference, `kind!`, naming the field of the same record whose value names the field's type ([TSON-SCHEMA] §5.2). `record_field.type` and `record_template_field.type` are typed by it, and no other slot is. """ field_type_ref => !meta.reference_type { of: meta.type } @doc:""" The names of the types a value can have: `meta.field_type_ref` without the sibling reference, a refinement of it, so a type reference stands wherever a field's type can. """ type_ref => !meta.field_type_ref ^ { allow_sibling: false } @doc:""" The names of the identifier families, which a `name_type` names: a refinement of `meta.type_ref`, an identifier family being a type. """ identifier_ref => !meta.type_ref ^ { of: unicode.identifier_type } @doc:""" Resolver output: a schema map, from each name to the value its entry resolves to, a type or a value, each naming its type by its tag. Resolved output and source are one grammar — output is source with sugar expanded, bodies flattened and chains computed, and an entry written as a constructor application resolves to itself. A value of `schema` nested as an entry, `geo => !schema { ... }`, declares as the root does: its members are entries of the document, each named by its path from the root, `geo.address`. A map of one path declared by several documents of a closure is one map, the union of their members, and a member's identity is its declaring document and its path; a member path two of them declare collides as any name does ([TSON-SCHEMA] §2.2.3). The annotations written on a map's entry are its declaring document's. """ schema => !meta.namespace { key_type: meta.type_name value_type: meta.declared } } binary => !schema { @doc:""" The base encodings of RFC 4648 that a text encoding may spell a `bytes` value in (HEX is §8's base16). """ bytes_encoding => !enum [BASE64 BASE64URL BASE32 HEX] @doc:""" Octet-sequence constraint vocabulary. Instance is `bytes`, at the root. The value is the octets: equality, identity, content addressing and the length facets are all over octets, never over a spelling — `length: 32` is a 32-byte digest whether it arrives as 64 hex characters, 44 base64 characters, or 32 raw bytes — so a round trip through any encoding preserves the value. `encoding` is a selector ([TSON-SCHEMA] §5.7): it picks which RFC 4648 alphabet the text class of encodings — TSON text, JSON, and any encoding whose values are character sequences — spells the octets in, and an encoding whose values are octets ignores it. A selector picks a spelling, so it never changes what two values compare as. It defaults to BASE64 (§4, padded), so a `bytes` position is base64 in every text encoding. `encoding` is not refinable. Another alphabet is another type, declared as its own instance: `hexdigest => !binary.bytes_type { encoding: HEX length: 4 }`. Refining for length inherits the alphabet: `sha256 => !bytes ^ { length: 32 }` is a base64 sha256. """ bytes_type => meta.atom & { encoding?: binary.bytes_encoding ~ BASE64 length?: numeric.count min_length?: numeric.count max_length?: numeric.count } } void => !meta.void_type {} @doc:"The two-valued type of a constraint field that is a switch." boolean => !enum [true false] @atom.ordering:TOTAL @atom.bounded:false @atom.exact:true @atom.numeric integer => !numeric.integer_type {} @doc:"Signed 64-bit integer." @atom.ordering:TOTAL @atom.bounded:true @atom.exact:true @atom.numeric int64 => !integer ^ { size: { bits: 64 signed: true } } @atom.ordering:NONE @atom.bounded:false text => !unicode.text_type {} @doc:""" Octet sequence. The universal binary type, and the only one: a field is `avatar: bytes`, a digest `sha256 => !bytes ^ { length: 32 }`. Equality and the length facets are over octets; the type says nothing about spelling. The text class of encodings spells `bytes` in base64 (RFC 4648 §4). Another alphabet is another type, declared as its own instance of the constructor (`hexdigest => !binary.bytes_type { encoding: HEX length: 4 }`) — never as a refinement of this one. An encoding whose values are octets writes them raw. """ @atom.ordering:NONE @atom.bounded:false bytes => !binary.bytes_type {} @doc:""" An IRI-reference (RFC 3987 §2.2): an IRI or a relative reference, its characters drawn from RFC 3987's grammar, beyond US-ASCII included. """ @atom.ordering:NONE @atom.bounded:false iri_reference => !net.iri_type {} @doc:"An IRI (RFC 3987 §2.2): the scheme is required." @atom.ordering:NONE @atom.bounded:false iri => !iri_reference ^ { allow_relative: false } @atom.ordering:NONE @atom.bounded:false regex => !unicode.regex_type {} @doc:""" Documentation for a declaration, a field or a document. The text is CommonMark 0.31.2 (https://spec.commonmark.org/0.31.2/) with no extensions. Every string is valid CommonMark, so this refuses nothing and says only how the text is displayed. Raw HTML in it is never executed, and a renderer need not render it. """ doc => @meta.annotation text @doc:""" An enumeration of identifiers, the enumeration constructor an author writes bare: the positional `!enum [OPEN DONE]`. """ enum => meta.enum_type ^ { name_type?: = meta.identifier } @doc:""" The set spelling an author writes: `set` where the bare construction is `!meta.set_type { element_type: text }`. A §5.10 template, not a constructor: it composes with nothing and so is not IS-A `top`. """ set => !meta.set_type { element_type: T } @doc:""" The tag of a schema map, an alias of `meta.schema`: a schema document's root, `!schema { … }`, and a map nested as an entry, `geo => !schema { … }`. """ schema => meta.schema }