@toon-format/spec 1.3.3 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ All notable changes to the TOON specification will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.4] - 2025-11-05
9
+
10
+ ### Changed
11
+
12
+ - Removed JavaScript-specific normalization details from specification; replaced with language-agnostic requirements (Section 3)
13
+ - Defined canonical number format for encoders: no exponent notation, no trailing zeros, no leading zeros except "0" (Section 2)
14
+ - Clarified decoder handling of exponent notation and out-of-range numbers (Section 2)
15
+ - Expanded `\w` regex notation to explicit character class `[A-Za-z0-9_]` for cross-language clarity (Section 7.3)
16
+ - Clarified non-strict mode tab handling as implementation-defined (Section 12)
17
+
18
+ ### Added
19
+
20
+ - Appendix G: Host Type Normalization Examples with guidance for Go, JavaScript, Python, and Rust implementations
21
+
8
22
  ## [1.3] - 2025-10-31
9
23
 
10
24
  ### Added
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # TOON Format Specification
2
2
 
3
- [![SPEC v1.3](https://img.shields.io/badge/spec-v1.3-lightgrey)](./SPEC.md)
4
- [![Tests](https://img.shields.io/badge/tests-306-green)](./tests/fixtures/)
3
+ [![SPEC v1.4](https://img.shields.io/badge/spec-v1.4-lightgrey)](./SPEC.md)
4
+ [![Tests](https://img.shields.io/badge/tests-323-green)](./tests/fixtures/)
5
5
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
6
6
 
7
7
  This repository contains the official specification for **Token-Oriented Object Notation (TOON)**, a compact, human-readable serialization format designed for passing structured data to Large Language Models with significantly reduced token usage.
@@ -10,7 +10,7 @@ This repository contains the official specification for **Token-Oriented Object
10
10
 
11
11
  [→ Read the full specification (SPEC.md)](./SPEC.md)
12
12
 
13
- - **Version:** 1.3 (2025-10-31)
13
+ - **Version:** 1.4 (2025-11-05)
14
14
  - **Status:** Working Draft
15
15
  - **License:** MIT
16
16
 
package/SPEC.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  ## Token-Oriented Object Notation
4
4
 
5
- **Version:** 1.3
5
+ **Version:** 1.4
6
6
 
7
- **Date:** 2025-10-31
7
+ **Date:** 2025-11-05
8
8
 
9
9
  **Status:** Working Draft
10
10
 
@@ -16,11 +16,11 @@
16
16
 
17
17
  ## Abstract
18
18
 
19
- Token-Oriented Object Notation (TOON) is a compact, human-readable serialization format optimized for Large Language Model (LLM) contexts, achieving 30-60% token reduction versus JSON for uniform tabular data. This specification defines TOON's data model, syntax, encoding/decoding semantics, and conformance requirements.
19
+ Token-Oriented Object Notation (TOON) is a line-oriented, indentation-based text format that encodes the JSON data model with explicit structure and minimal quoting. Arrays declare their length and an optional field list once; rows use a single active delimiter (comma, tab, or pipe). Objects use indentation instead of braces; strings are quoted only when required. This specification defines TOONs concrete syntax, canonical number formatting, delimiter scoping, and strict‑mode validation, and sets conformance requirements for encoders, decoders, and validators. TOON provides a compact, deterministic representation of structured data and is particularly efficient for arrays of uniform objects.
20
20
 
21
21
  ## Status of This Document
22
22
 
23
- This document is a Working Draft v1.3 and may be updated, replaced, or obsoleted. Implementers should monitor the canonical repository at https://github.com/toon-format/spec for changes.
23
+ This document is a Working Draft v1.4 and may be updated, replaced, or obsoleted. Implementers should monitor the canonical repository at https://github.com/toon-format/spec for changes.
24
24
 
25
25
  This specification is stable for implementation but not yet finalized. Breaking changes are unlikely but possible before v2.0.
26
26
 
@@ -55,16 +55,6 @@ https://www.unicode.org/versions/Unicode15.1.0/
55
55
  **[ISO8601]** ISO 8601:2019, "Date and time — Representations for information interchange".
56
56
  https://www.iso.org/standard/70907.html
57
57
 
58
- ## Conventions and Terminology
59
-
60
- The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119] and [RFC8174] when, and only when, they appear in all capitals, as shown here.
61
-
62
- Audience: implementers of encoders/decoders/validators; tool authors; practitioners embedding TOON in LLM prompts.
63
-
64
- All normative text in this specification is contained in Sections 1-16 and Section 19. All appendices are informative except where explicitly marked normative. Examples throughout this document are informative unless explicitly stated otherwise.
65
-
66
- Implementations that fail to conform to any MUST or REQUIRED level requirement are non-conformant. Implementations that conform to all MUST and REQUIRED level requirements but fail to conform to SHOULD or RECOMMENDED level requirements are said to be "not fully conformant" but are still considered conformant.
67
-
68
58
  ## Table of Contents
69
59
 
70
60
  - [Introduction](#introduction)
@@ -97,59 +87,104 @@ Implementations that fail to conform to any MUST or REQUIRED level requirement a
97
87
  - [Appendix D: Document Changelog (Informative)](#appendix-d-document-changelog-informative)
98
88
  - [Appendix E: Acknowledgments and License](#appendix-e-acknowledgments-and-license)
99
89
  - [Appendix F: Cross-check With Reference Behavior (Informative)](#appendix-f-cross-check-with-reference-behavior-informative)
90
+ - [Appendix G: Host Type Normalization Examples (Informative)](#appendix-g-host-type-normalization-examples-informative)
100
91
 
101
- ## Introduction
92
+ ## Introduction (Informative)
102
93
 
103
- TOON (Token-Oriented Object Notation) is a serialization format optimized for Large Language Model contexts where token count directly impacts costs, context capacity, and latency. While JSON and similar formats serve general purposes, TOON achieves 30-60% token reduction for tabular data through compact syntax, particularly for arrays of uniform objects. The format maintains human readability, deterministic encoding, and strict validation while modeling JSON-compatible data types.
94
+ ### Purpose and scope
104
95
 
105
- ### Specification Scope
96
+ TOON (Token-Oriented Object Notation) is a line-oriented, indentation-based text format that encodes the JSON data model with explicit structure and minimal quoting. It is designed as a compact, deterministic representation of structured data, particularly well-suited to arrays of uniform objects. TOON is often used as a translation layer: produce data as JSON in code, encode to TOON for downstream consumption (e.g., LLM prompts), and decode back to JSON if needed.
106
97
 
107
- This specification defines:
98
+ ### Applicability and non‑goals
108
99
 
109
- - The abstract data model (Section 2)
110
- - Type normalization rules for encoders (Section 3)
111
- - Concrete syntax and formatting rules (Sections 5-12)
112
- - Parsing and decoding semantics (Section 4)
113
- - Conformance requirements for encoders, decoders, and validators (Section 13)
114
- - Security and internationalization considerations (Sections 15-16)
100
+ Use TOON when:
101
+ - arrays of objects share the same fields (uniform tabular data),
102
+ - deterministic, minimally quoted text is desirable,
103
+ - explicit lengths and fixed row widths help detect truncation or malformed data,
104
+ - you want unambiguous, human-readable structure without repeating keys.
105
+
106
+ TOON is not intended to replace:
107
+ - JSON for non-uniform or deeply nested structures where repeated keys are not dominant,
108
+ - CSV for flat, strictly tabular data where maximum compactness is required and nesting is not needed,
109
+ - general-purpose storage or public APIs. TOON carries the JSON data model; it is a transport/authoring format with explicit structure, not an extended type system or schema language.
110
+
111
+ Out of scope:
112
+ - comments and annotations,
113
+ - alternative number systems or locale-specific formatting,
114
+ - user-defined escape sequences or control directives.
115
+
116
+ ### Relationship to JSON, CSV, and YAML (Informative)
117
+
118
+ - **JSON**: TOON preserves the JSON data model. It is more compact for uniform arrays of objects by declaring length and fields once. For non-uniform or deeply nested data, JSON may be more efficient.
119
+ - **CSV/TSV**: CSV is typically more compact for flat tables but lacks nesting and type awareness. TOON adds explicit lengths, per-array delimiter scoping, field lists, and deterministic quoting, while remaining lightweight.
120
+ - **YAML**: TOON uses indentation and hyphen markers but is more constrained and deterministic: no comments, explicit array headers with lengths, fixed quoting rules, and a narrow escape set.
121
+
122
+ ### Example (Informative)
123
+
124
+ ```
125
+ users[2]{id,name,role}:
126
+ 1,Alice,admin
127
+ 2,Bob,user
128
+ ```
129
+
130
+ ### Document roadmap
131
+
132
+ Normative rules are organized as follows:
133
+ - Data model and canonical number form (§2); normalization on encode (§3); decoding interpretation (§4).
134
+ - Concrete syntax, including root-form determination (§5) and header syntax (§6).
135
+ - Strings and keys (§7); objects (§8); arrays and their sub-forms (§9); objects as list items (§10); delimiter rules (§11).
136
+ - Indentation and whitespace (§12); conformance and options (§13).
137
+ - Strict-mode errors (authoritative checklist) (§14).
138
+
139
+ Appendices are informative unless stated otherwise and provide examples, parsing helpers, and implementation guidance.
115
140
 
116
141
  ## 1. Terminology and Conventions
117
142
 
118
- ### Core Concepts
143
+ ### 1.1 Use of RFC2119 Keywords and Normativity
144
+
145
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119] and [RFC8174] when, and only when, they appear in all capitals, as shown here.
146
+
147
+ Audience: implementers of encoders/decoders/validators; tool authors; practitioners embedding TOON in LLM prompts.
148
+
149
+ All normative text in this specification is contained in Sections 1-16 and Section 19. All appendices are informative except where explicitly marked normative. Examples throughout this document are informative unless explicitly stated otherwise.
150
+
151
+ Implementations that fail to conform to any MUST or REQUIRED level requirement are non-conformant. Implementations that conform to all MUST and REQUIRED level requirements but fail to conform to SHOULD or RECOMMENDED level requirements are said to be "not fully conformant" but are still considered conformant.
152
+
153
+ ### 1.2 Core Concepts
119
154
 
120
155
  - TOON document: A sequence of UTF-8 text lines formatted according to this spec.
121
156
  - Line: A sequence of non-newline characters terminated by LF (U+000A) in serialized form. Encoders MUST use LF.
122
157
 
123
- ### Structural Terms
158
+ ### 1.3 Structural Terms
124
159
 
125
160
  - Indentation level (depth): Leading indentation measured in fixed-size space units (indentSize). Depth 0 has no indentation.
126
161
  - Indentation unit (indentSize): A fixed number of spaces per level (default 2). Tabs MUST NOT be used for indentation.
127
162
 
128
- ### Array Terms
163
+ ### 1.4 Array Terms
129
164
 
130
165
  - Header: The bracketed declaration for arrays, optionally followed by a field list, and terminating with a colon; e.g., key[3]: or items[2]{a,b}:.
131
166
  - Field list: Brace-enclosed, delimiter-separated list of field names for tabular arrays: {f1<delim>f2}.
132
167
  - List item: A line beginning with "- " at a given depth representing an element in an expanded array.
133
168
  - Length marker: Optional "#" prefix for array lengths in headers, e.g., [#3]. Decoders MUST accept and ignore it semantically.
134
169
 
135
- ### Delimiter Terms
170
+ ### 1.5 Delimiter Terms
136
171
 
137
172
  - Delimiter: The character used to separate array/tabular values: comma (default), tab (HTAB, U+0009), or pipe ("|").
138
173
  - Document delimiter: The encoder-selected delimiter used for quoting decisions outside any array scope (default comma).
139
174
  - Active delimiter: The delimiter declared by the closest array header in scope, used to split inline primitive arrays and tabular rows under that header; it also governs quoting decisions for values within that array's scope.
140
175
 
141
- ### Type Terms
176
+ ### 1.6 Type Terms
142
177
 
143
178
  - Primitive: string, number, boolean, or null.
144
179
  - Object: Mapping from string keys to `JsonValue`.
145
180
  - Array: Ordered sequence of `JsonValue`.
146
181
  - `JsonValue`: Primitive | Object | Array.
147
182
 
148
- ### Conformance Terms
183
+ ### 1.7 Conformance Terms
149
184
 
150
185
  - Strict mode: Decoder mode that enforces counts, indentation, and delimiter consistency; also rejects invalid escapes and missing colons (default: true).
151
186
 
152
- ### Notation
187
+ ### 1.8 Notation
153
188
 
154
189
  - Regular expressions appear in slash-delimited form.
155
190
  - ABNF snippets follow RFC 5234; HTAB means the U+0009 character.
@@ -163,34 +198,43 @@ This specification defines:
163
198
  - Ordering:
164
199
  - Array order MUST be preserved.
165
200
  - Object key order MUST be preserved as encountered by the encoder.
166
- - Numbers (encoding):
167
- - -0 MUST be normalized to 0.
168
- - Finite numbers MUST be rendered without scientific notation (e.g., 1e6 1000000; 1e-6 0.000001).
169
- - Implementations MUST ensure decimal rendering does not use exponent notation.
170
- - Numbers (precision):
171
- - JavaScript implementations SHOULD use the language's default Number.toString() conversion, which provides sufficient precision (typically 15-17 significant digits) for round-trip fidelity with IEEE 754 double-precision values.
172
- - Implementations MUST preserve sufficient precision to ensure round-trip fidelity: decoding an encoded number MUST yield a value equal to the original.
173
- - Trailing zeros MAY be omitted for whole numbers (e.g., 1000000 is preferred over 1000000.0).
174
- - Very large numbers (e.g., greater than 10^20) that may lose precision in floating-point representation SHOULD be converted to quoted decimal strings if exact precision is required.
201
+ - Numbers (canonical form for encoding):
202
+ - Encoders MUST emit numbers in canonical decimal form:
203
+ - No exponent notation (e.g., 1e6 MUST be rendered as 1000000; 1e-6 as 0.000001).
204
+ - No leading zeros except for the single digit "0" (e.g., "05" is not canonical).
205
+ - No trailing zeros in the fractional part (e.g., 1.5000 MUST be rendered as 1.5).
206
+ - If the fractional part is zero after normalization, emit as an integer (e.g., 1.0 1).
207
+ - -0 MUST be normalized to 0.
208
+ - Encoders MUST emit sufficient precision to ensure round-trip fidelity within the encoder's host environment: decode(encode(x)) MUST equal x.
209
+ - If the encoder's host environment cannot represent a numeric value without loss (e.g., arbitrary-precision decimals or integers exceeding the host's numeric range), the encoder MAY:
210
+ - Emit a quoted string containing the exact decimal representation to preserve value fidelity, OR
211
+ - Emit a canonical number that round-trips to the host's numeric approximation (losing precision), provided it conforms to the canonical formatting rules above.
212
+ - Encoders SHOULD provide an option to choose lossless stringification for out-of-range numbers.
213
+ - Numbers (decoding):
214
+ - Decoders MUST accept decimal and exponent forms on input (e.g., 42, -3.14, 1e-6, -1E+9).
215
+ - Decoders MUST treat tokens with forbidden leading zeros (e.g., "05", "0001") as strings, not numbers.
216
+ - If a decoded numeric token is not representable in the host's default numeric type without loss, implementations MAY:
217
+ - Return a higher-precision numeric type (e.g., arbitrary-precision integer or decimal), OR
218
+ - Return a string, OR
219
+ - Return an approximate numeric value if that is the documented policy.
220
+ - Implementations MUST document their policy for handling out-of-range or non-representable numbers. A lossless-first policy is RECOMMENDED for libraries intended for data interchange or validation.
175
221
  - Null: Represented as the literal null.
176
222
 
177
223
  ## 3. Encoding Normalization (Reference Encoder)
178
224
 
179
- The reference encoder normalizes non-JSON values to the data model:
225
+ Encoders MUST normalize non-JSON values to the JSON data model before encoding:
180
226
 
181
227
  - Number:
182
- - Finite → number (non-exponential). -0 → 0.
228
+ - Finite → number (canonical decimal form per Section 2). -0 → 0.
183
229
  - NaN, +Infinity, -Infinity → null.
184
- - BigInt (JavaScript):
185
- - If within Number.MIN_SAFE_INTEGER..Number.MAX_SAFE_INTEGER converted to number.
186
- - Otherwiseconverted to a decimal string (e.g., "9007199254740993") and encoded as a string (quoted because it is numeric-like).
187
- - DateISO string (e.g., "2025-01-01T00:00:00.000Z").
188
- - Setarray by iterating entries and normalizing each element.
189
- - Map object using String(key) for keys and normalizing values.
190
- - Plain object → own enumerable string keys in encounter order; values normalized recursively.
191
- - Function, symbol, undefined, or unrecognized types → null.
230
+ - Non-JSON types MUST be normalized to the JSON data model (object, array, string, number, boolean, or null) before encoding. The mapping from host-specific types to JSON model is implementation-defined and MUST be documented.
231
+ - Examples of host-type normalization (non-normative):
232
+ - Date/time objects ISO 8601 string representation.
233
+ - Set-like collections array.
234
+ - Map-like collections object (with string keys).
235
+ - Undefined, function, symbol, or unrecognized types null.
192
236
 
193
- Note: Other language ports SHOULD apply analogous normalization consistent with this spec’s data model and encoding rules.
237
+ See Appendix G for non-normative language-specific examples (Go, JavaScript, Python, Rust).
194
238
 
195
239
  ## 4. Decoding Interpretation (Reference Decoder)
196
240
 
@@ -205,6 +249,10 @@ Decoders map text tokens to host values:
205
249
  - MUST accept standard decimal and exponent forms (e.g., 42, -3.14, 1e-6, -1E+9).
206
250
  - MUST treat tokens with forbidden leading zeros (e.g., "05", "0001") as strings (not numbers).
207
251
  - Only finite numbers are expected from conforming encoders.
252
+ - Decoding examples:
253
+ - `"1.5000"` → numeric value `1.5` (trailing zeros in fractional part are accepted)
254
+ - `"-1E+03"` → numeric value `-1000` (exponent forms are accepted)
255
+ - `"-0"` → numeric value `0` (negative zero decodes to zero; most host environments do not distinguish -0 from 0)
208
256
  - Otherwise → string.
209
257
  - Keys:
210
258
  - Decoded as strings (quoted keys MUST be unescaped per Section 7.1).
@@ -225,9 +273,15 @@ TOON is a deterministic, line-oriented, indentation-based notation.
225
273
  - Otherwise: expanded list items: key[N<delim?>]: with "- …" items (see Sections 9.4 and 10).
226
274
  - Root form discovery:
227
275
  - If the first non-empty depth-0 line is a valid root array header per Section 6 (must include a colon), decode a root array.
228
- - Else if the document has exactly one non-empty line and it is neither a valid array header nor a key-value line (quoted or unquoted key), decode a single primitive.
276
+ - Else if the document has exactly one non-empty line and it is neither a valid array header nor a key-value line (quoted or unquoted key), decode a single primitive (examples: `hello`, `42`, `true`).
229
277
  - Otherwise, decode an object.
230
- - In strict mode, multiple non-key/value non-header lines at depth 0 is invalid.
278
+ - An empty document (no non-empty lines after ignoring trailing newline(s) and ignorable blank lines) decodes to an empty object `{}`.
279
+ - In strict mode, if there are two or more non-empty depth-0 lines that are neither headers nor key-value lines, the document is invalid. Example of invalid input (strict mode):
280
+ ```
281
+ hello
282
+ world
283
+ ```
284
+ This would be two primitives at root depth, which is not a valid TOON document structure.
231
285
 
232
286
  ## 6. Header Syntax (Normative)
233
287
 
@@ -253,7 +307,7 @@ Spacing and delimiters:
253
307
  - The active delimiter declared by the bracket segment applies to:
254
308
  - splitting inline primitive arrays on that header line,
255
309
  - splitting tabular field names in "{…}",
256
- - splitting all rows/items within the headers scope,
310
+ - splitting all rows/items within the header's scope,
257
311
  - unless a nested header changes it.
258
312
  - The same delimiter symbol declared in the bracket MUST be used in the fields segment and in all row/value splits in that scope.
259
313
  - Absence of a delimiter symbol in a bracket segment ALWAYS means comma, regardless of any parent header.
@@ -287,6 +341,8 @@ unquoted-key = ( ALPHA / "_" ) *( ALPHA / DIGIT / "_" / "." )
287
341
  ; quoted-key = DQUOTE *(escaped-char / safe-char) DQUOTE
288
342
  ```
289
343
 
344
+ Note: The ABNF grammar above cannot enforce that the delimiter used in the fields segment (braces) matches the delimiter declared in the bracket segment. This equality requirement is normative per the prose in lines 311-312 above and MUST be enforced by implementations. Mismatched delimiters between bracket and brace segments MUST error in strict mode.
345
+
290
346
  Note: The grammar above specifies header syntax. TOON's grammar is deliberately designed to prioritize human readability and token efficiency over strict LR(1) parseability. This requires some context-sensitive parsing (particularly for tabular row disambiguation in Section 9.3), which is a deliberate design tradeoff. Reference implementations demonstrate that deterministic parsing is achievable with modest lookahead.
291
347
 
292
348
  Decoding requirements:
@@ -332,7 +388,7 @@ Otherwise, the string MAY be emitted without quotes. Unicode, emoji, and strings
332
388
  ### 7.3 Key Encoding (Encoding)
333
389
 
334
390
  Object keys and tabular field names:
335
- - MAY be unquoted only if they match: ^[A-Za-z_][\w.]*$.
391
+ - MAY be unquoted only if they match: ^[A-Za-z_][A-Za-z0-9_.]*$.
336
392
  - Otherwise, they MUST be quoted and escaped per Section 7.1.
337
393
 
338
394
  Keys requiring quoting per the above rules MUST be quoted in all contexts, including array headers (e.g., "my-key"[N]:).
@@ -368,6 +424,7 @@ Keys requiring quoting per the above rules MUST be quoted in all contexts, inclu
368
424
  - Root arrays: [N<delim?>]: v1<delim>…
369
425
  - Decoding:
370
426
  - Split using the active delimiter declared by the header; non-active delimiters MUST NOT split values.
427
+ - When splitting inline arrays, empty tokens (including those surrounded by whitespace) decode to the empty string.
371
428
  - In strict mode, the number of decoded values MUST equal N; otherwise MUST error.
372
429
 
373
430
  ### 9.2 Arrays of Arrays (Primitives Only) — Expanded List
@@ -390,7 +447,7 @@ Tabular detection (encoding; MUST hold for all elements):
390
447
  - All values across these keys are primitives (no nested arrays/objects).
391
448
 
392
449
  When satisfied (encoding):
393
- - Header: key[N<delim?>]{f1<delim>f2<delim>…}: where field order is the first objects key encounter order.
450
+ - Header: key[N<delim?>]{f1<delim>f2<delim>…}: where field order is the first object's key encounter order.
394
451
  - Field names encoded per Section 7.3.
395
452
  - Rows: one line per object at depth +1 under the header; values are encoded primitives (Section 7) and joined by the active delimiter.
396
453
  - Root tabular arrays omit the key: [N<delim?>]{…}: followed by rows.
@@ -399,7 +456,7 @@ Decoding:
399
456
  - A tabular header declares the active delimiter and ordered field list.
400
457
  - Rows appear at depth +1 as delimiter-separated value lines.
401
458
  - Strict mode MUST enforce:
402
- - Each rows value count equals the field count.
459
+ - Each row's value count equals the field count.
403
460
  - The number of rows equals N.
404
461
  - Disambiguation at row depth (unquoted tokens):
405
462
  - Compute the first unquoted occurrence of the active delimiter and the first unquoted colon.
@@ -455,15 +512,15 @@ Decoding:
455
512
  - Tab: header includes HTAB inside brackets and braces (e.g., [N<TAB>], {a<TAB>b}); rows/inline arrays use tabs.
456
513
  - Pipe: header includes "|" inside brackets and braces; rows/inline arrays use "|".
457
514
  - Document vs Active delimiter:
458
- - Encoders select a document delimiter (option) that influences quoting in contexts not governed by an array header (e.g., object values).
459
- - Inside an array headers scope, the active delimiter governs splitting and quoting of inline arrays and tabular rows for that array.
460
- - Absence of a delimiter symbol in a header ALWAYS means comma for that array’s scope; it does not inherit from any parent.
515
+ - Encoders select a document delimiter (option) that influences quoting for all object values (key: value) throughout the document.
516
+ - Inside an array header's scope, the active delimiter governs splitting and quoting only for inline arrays and tabular rows that the header introduces. Object values (key: value) follow document-delimiter quoting rules regardless of array scope.
461
517
  - Delimiter-aware quoting (encoding):
462
- - Within an array’s scope, strings containing the active delimiter MUST be quoted to avoid splitting.
463
- - Outside any array scope, encoders SHOULD use the document delimiter to decide delimiter-aware quoting for values.
518
+ - Inline array values and tabular row cells: strings containing the active delimiter MUST be quoted to avoid splitting.
519
+ - Object values (key: value): encoders use the document delimiter to decide delimiter-aware quoting, regardless of whether the object appears within an array's scope.
464
520
  - Strings containing non-active delimiters do not require quoting unless another quoting condition applies (Section 7.2).
465
521
  - Delimiter-aware parsing (decoding):
466
522
  - Inline arrays and tabular rows MUST be split only on the active delimiter declared by the nearest array header.
523
+ - Splitting MUST preserve empty tokens; surrounding spaces are trimmed, and empty tokens decode to the empty string.
467
524
  - Strings containing the active delimiter MUST be quoted to avoid splitting; non-active delimiters MUST NOT cause splits.
468
525
  - Nested headers may change the active delimiter; decoding MUST use the delimiter declared by the nearest header.
469
526
  - If the bracket declares tab or pipe, the same symbol MUST be used in the fields segment and for splitting all rows/values in that scope.
@@ -483,7 +540,7 @@ Decoding:
483
540
  - Tabs used as indentation MUST error. Tabs are allowed in quoted strings and as the HTAB delimiter.
484
541
  - Non-strict mode:
485
542
  - Depth MAY be computed as floor(indentSpaces / indentSize).
486
- - Tabs in indentation are non-conforming and MAY be accepted or rejected.
543
+ - Implementations MAY accept tab characters in indentation. Depth computation for tabs is implementation-defined. Implementations MUST document their tab policy.
487
544
  - Surrounding whitespace around tokens SHOULD be tolerated; internal semantics follow quoting rules.
488
545
  - Blank lines:
489
546
  - Outside arrays/tabular rows: decoders SHOULD ignore completely blank lines (do not create/close structures).
@@ -529,7 +586,7 @@ Options:
529
586
  - indent (default: 2 spaces)
530
587
  - strict (default: true)
531
588
 
532
- Note: Section 14 is authoritative for strict-mode errors; validators MAY add informative diagnostics for style and encoding invariants.
589
+ Strict-mode errors are enumerated in §14; validators MAY add informative diagnostics for style and encoding invariants.
533
590
 
534
591
  ### 13.1 Encoder Conformance Checklist
535
592
 
@@ -590,7 +647,8 @@ When strict mode is enabled (default), decoders MUST error on the following cond
590
647
  ### 14.4 Structural Errors
591
648
 
592
649
  - Blank lines inside arrays/tabular rows.
593
- - Empty input (document with no non-empty lines after ignoring trailing newline(s) and ignorable blank lines outside arrays/tabular rows).
650
+
651
+ For root-form rules, including handling of empty documents, see §5.
594
652
 
595
653
  ### 14.5 Recommended Error Messages and Validator Diagnostics (Informative)
596
654
 
@@ -949,6 +1007,7 @@ These sketches illustrate structure and common decoding helpers. They are inform
949
1007
  - If token starts with a quote, it MUST be a properly quoted string (no trailing characters after the closing quote). Unescape using only the five escapes; otherwise MUST error.
950
1008
  - Else if token is true/false/null → boolean/null.
951
1009
  - Else if token is numeric without forbidden leading zeros and finite → number.
1010
+ - Examples: `"1.5000"` → `1.5`, `"-1E+03"` → `-1000`, `"-0"` → `0` (host normalization applies)
952
1011
  - Else → string.
953
1012
 
954
1013
  ### B.5 Object and List Item Parsing
@@ -994,15 +1053,20 @@ The reference test suite covers:
994
1053
  - Tabular detection and formatting, including delimiter variations.
995
1054
  - Mixed arrays and objects-as-list-items behavior, including nested arrays and objects.
996
1055
  - Whitespace invariants (no trailing spaces/newline).
997
- - Normalization (BigInt, Date, undefined, NaN/Infinity, functions, symbols).
1056
+ - Canonical number formatting (no exponent, no trailing zeros, no leading zeros).
998
1057
  - Decoder strict-mode errors: count mismatches, invalid escapes, missing colon, delimiter mismatches, indentation errors, blank-line handling.
999
1058
 
1059
+ Note: Host-type normalization tests (e.g., BigInt, Date, Set, Map) are language-specific and maintained in implementation repositories. See Appendix G for normalization guidance.
1060
+
1000
1061
  ## Appendix D: Document Changelog (Informative)
1001
1062
 
1002
- ### v1.4 (2025-11-02)
1063
+ ### v1.4 (2025-11-05)
1003
1064
 
1004
- - Clarified that keys requiring quoting (Section 7.3) MUST be quoted in all contexts, including array headers (e.g., "my-key"[N]:, "x-custom"[2]{fields}:).
1005
- - No semantic changes to the specification; this is purely clarifying documentation that was already implied by the grammar.
1065
+ - Removed JavaScript-specific normalization details; replaced with language-agnostic requirements (Section 3).
1066
+ - Defined canonical number format for encoders and decoder acceptance rules (Section 2).
1067
+ - Added Appendix G with host-type normalization examples for Go, JavaScript, Python, and Rust.
1068
+ - Clarified non-strict mode tab handling as implementation-defined (Section 12).
1069
+ - Expanded regex notation for cross-language clarity (Section 7.3).
1006
1070
 
1007
1071
  ### v1.3 (2025-10-31)
1008
1072
 
@@ -1053,39 +1117,127 @@ This specification and reference implementation are released under the MIT Licen
1053
1117
  - Whitespace invariants for encoding and strict-mode indentation enforcement for decoding.
1054
1118
  - Blank-line handling and trailing-newline acceptance.
1055
1119
 
1056
- ## 19. TOON Core Profile (Normative Subset)
1120
+ ## Appendix G: Host Type Normalization Examples (Informative)
1057
1121
 
1058
- This profile captures the most common, memory-friendly rules.
1122
+ This appendix provides non-normative guidance on how implementations in different programming languages MAY normalize host-specific types to the JSON data model before encoding. The normative requirement is in Section 3: implementations MUST normalize non-JSON types to the JSON data model and MUST document their normalization policy.
1059
1123
 
1060
- - Character set: UTF-8; LF line endings.
1061
- - Indentation: 2 spaces per level (configurable indentSize).
1062
- - Strict mode: leading spaces MUST be a multiple of indentSize; tabs in indentation MUST error.
1063
- - Keys:
1064
- - Unquoted if they match ^[A-Za-z_][\w.]*$; otherwise quoted.
1065
- - A colon MUST follow a key.
1066
- - Strings:
1067
- - Only these escapes allowed in quotes: \\, \", \n, \r, \t.
1068
- - Quote if empty; leading/trailing whitespace; equals true/false/null; numeric-like; contains colon/backslash/quote/brackets/braces/control char; contains the relevant delimiter (active inside arrays, document otherwise); equals "-" or starts with "-".
1069
- - Numbers:
1070
- - Encoder emits non-exponential decimal; -0 → 0.
1071
- - Decoder accepts decimal and exponent forms; tokens with forbidden leading zeros decode as strings.
1072
- - Arrays and headers:
1073
- - Header: [#?N[delim?]] where delim is absent (comma), HTAB (tab), or "|" (pipe).
1074
- - Keyed header: key[#?N[delim?]]:. Optional fields: {f1<delim>f2}.
1075
- - Primitive arrays inline: key[N]: v1<delim>v2. Empty arrays: key[0]: (no values).
1076
- - Tabular arrays: key[N]{fields}: then N rows at depth +1.
1077
- - Otherwise list form: key[N]: then N items, each starting with "- ".
1078
- - Delimiters:
1079
- - Only split on the active delimiter from the nearest header. Non-active delimiters never split.
1080
- - Objects as list items:
1081
- - "- value" (primitive), "- [M]: …" (inline array), or "- key: …" (object).
1082
- - If first field is "- key:" with nested object: nested fields at +2; subsequent sibling fields at +1.
1083
- - Root form:
1084
- - Root array if the first depth-0 line is a header (per Section 6).
1085
- - Root primitive if exactly one non-empty line and it is not a header or key-value.
1086
- - Otherwise object.
1087
- - Strict mode checks:
1088
- - All count/width checks; missing colon; invalid escapes; indentation multiple-of-indentSize; delimiter mismatches via count checks; blank lines inside arrays/tabular rows; empty input.
1124
+ ### G.1 Go
1125
+
1126
+ Go implementations commonly normalize the following host types:
1127
+
1128
+ Numeric Types:
1129
+ - `big.Int`: If within `int64` range, convert to number. Otherwise, convert to quoted decimal string per lossless policy.
1130
+ - `math.Inf()`, `math.NaN()`: Convert to `null`.
1131
+
1132
+ Temporal Types:
1133
+ - `time.Time`: Convert to ISO 8601 string via `.Format(time.RFC3339)` or `.Format(time.RFC3339Nano)`.
1134
+
1135
+ Collection Types:
1136
+ - `map[K]V`: Convert to object. Keys MUST be strings or convertible to strings via `fmt.Sprint`.
1137
+ - `[]T` (slices): Preserve as array.
1138
+
1139
+ Struct Types:
1140
+ - Structs with exported fields: Convert to object using JSON struct tags if present.
1141
+
1142
+ Non-Serializable Types:
1143
+ - `nil`: Maps to `null`.
1144
+ - Functions, channels, `unsafe.Pointer`: Not serializable; implementations MUST error or skip these fields.
1145
+
1146
+ ### G.2 JavaScript
1147
+
1148
+ JavaScript implementations commonly normalize the following host types:
1149
+
1150
+ Numeric Types:
1151
+ - `BigInt`: If the value is within `Number.MIN_SAFE_INTEGER` to `Number.MAX_SAFE_INTEGER`, convert to `number`. Otherwise, convert to a quoted decimal string (e.g., `BigInt(9007199254740993)` → `"9007199254740993"`).
1152
+ - `NaN`, `Infinity`, `-Infinity`: Convert to `null`.
1153
+ - `-0`: Normalize to `0`.
1154
+
1155
+ Temporal Types:
1156
+ - `Date`: Convert to ISO 8601 string via `.toISOString()` (e.g., `"2025-01-01T00:00:00.000Z"`).
1157
+
1158
+ Collection Types:
1159
+ - `Set`: Convert to array by iterating entries and normalizing each element.
1160
+ - `Map`: Convert to object using `String(key)` for keys and normalizing values recursively. Non-string keys are coerced to strings.
1161
+
1162
+ Object Types:
1163
+ - Plain objects: Enumerate own enumerable string keys in encounter order; normalize values recursively.
1164
+
1165
+ Non-Serializable Types:
1166
+ - `undefined`, `function`, `Symbol`: Convert to `null`.
1167
+
1168
+ ### G.3 Python
1169
+
1170
+ Python implementations commonly normalize the following host types:
1171
+
1172
+ Numeric Types:
1173
+ - `decimal.Decimal`: Convert to `float` if representable without loss, OR convert to quoted decimal string for exact preservation (implementation policy).
1174
+ - `float('inf')`, `float('-inf')`, `float('nan')`: Convert to `null`.
1175
+ - Arbitrary-precision integers (large `int`): Emit as number if within host numeric range, OR as quoted decimal string per lossless policy.
1176
+
1177
+ Temporal Types:
1178
+ - `datetime.datetime`, `datetime.date`, `datetime.time`: Convert to ISO 8601 string representation via `.isoformat()`.
1179
+
1180
+ Collection Types:
1181
+ - `set`, `frozenset`: Convert to list (array).
1182
+ - `dict`: Preserve as object with string keys. Non-string keys MUST be coerced to strings.
1183
+
1184
+ Object Types:
1185
+ - Custom objects: Extract attributes via `__dict__` or implement custom serialization; convert to object (dict) with string keys.
1186
+
1187
+ Non-Serializable Types:
1188
+ - `None`: Maps to `null`.
1189
+ - Functions, lambdas, modules: Convert to `null`.
1190
+
1191
+ ### G.4 Rust
1192
+
1193
+ Rust implementations commonly normalize the following host types (typically using serialization frameworks like `serde`):
1194
+
1195
+ Numeric Types:
1196
+ - `i128`, `u128`: If within `i64`/`u64` range, emit as number. Otherwise, convert to quoted decimal string per lossless policy.
1197
+ - `f64::INFINITY`, `f64::NEG_INFINITY`, `f64::NAN`: Convert to `null`.
1198
+
1199
+ Temporal Types:
1200
+ - `chrono::DateTime<T>`: Convert to ISO 8601 string via `.to_rfc3339()`.
1201
+ - `chrono::NaiveDate`, `chrono::NaiveTime`: Convert to ISO 8601 partial representations.
1202
+
1203
+ Collection Types:
1204
+ - `HashSet<T>`, `BTreeSet<T>`: Convert to `Vec<T>` (array).
1205
+ - `HashMap<K, V>`, `BTreeMap<K, V>`: Convert to object. Keys MUST be strings or convertible to strings via `Display` or `ToString`.
1206
+
1207
+ Enum Types:
1208
+ - Unit variants: Convert to string of variant name (e.g., `Color::Red` → `"Red"`).
1209
+ - Tuple/struct variants: Typically convert to object with `"type"` field and data fields per `serde` conventions.
1210
+
1211
+ Non-Serializable Types:
1212
+ - `Option::None`: Convert to `null`.
1213
+ - `Option::Some(T)`: Unwrap and normalize `T`.
1214
+ - Function pointers, raw pointers: Not serializable; implementations MUST error or skip these fields.
1215
+
1216
+ ### G.5 General Guidance
1217
+
1218
+ Implementations in any language SHOULD:
1219
+ 1. Document their normalization policy clearly, especially for:
1220
+ - Large or arbitrary-precision numbers (lossless string vs. approximate number)
1221
+ - Date/time representations (ISO 8601 format details)
1222
+ - Collection type mappings (order preservation for sets)
1223
+ 2. Provide configuration options where multiple strategies are reasonable (e.g., lossless vs. approximate numeric encoding).
1224
+ 3. Ensure that normalization is deterministic: encoding the same host value twice MUST produce identical TOON output.
1225
+
1226
+ ## 19. TOON Core Profile (Normative Subset)
1227
+
1228
+ This profile captures the most common, memory-friendly rules by reference to normative sections.
1229
+
1230
+ - Character set and line endings: As defined in §1 (Core Concepts) and §12.
1231
+ - Indentation: MUST conform to §12 (2 spaces per level by default; strict mode enforces indentSize multiples).
1232
+ - Keys and colon syntax: MUST conform to §7.2 (unquoted keys match ^[A-Za-z_][A-Za-z0-9_.]*$; quoted otherwise; colon required after keys).
1233
+ - Strings and quoting: MUST be quoted as defined in §7.2 (deterministic quoting rules for empty strings, whitespace, reserved literals, control characters, delimiters, leading hyphens, and structural tokens).
1234
+ - Escape sequences: MUST conform to §7.1 (only \\, \", \n, \r, \t are valid).
1235
+ - Numbers: Encoders MUST emit canonical form per §2; decoders MUST accept input per §4.
1236
+ - Arrays and headers: Header syntax MUST conform to §6; array encoding as defined in §9.
1237
+ - Delimiters: Delimiter scoping and quoting rules as defined in §11.
1238
+ - Objects as list items: Indentation rules as defined in §10.
1239
+ - Root form determination: As defined in §5.
1240
+ - Strict mode validation: All checks enumerated in §14.
1089
1241
 
1090
1242
  ## 20. Versioning and Extensibility
1091
1243
 
package/VERSIONING.md CHANGED
@@ -122,19 +122,6 @@ Your implementation can support multiple spec versions. If you do:
122
122
  - Previous version implementations remain valid
123
123
  - We'll provide a migration guide
124
124
 
125
- ## Version Numbering Examples
126
-
127
- ### Current: v1.3
128
-
129
- **Next minor update (clarifications, additions):**
130
- - v1.4
131
-
132
- **Next major update (breaking changes):**
133
- - v2.0
134
-
135
- **After v2.0, next minor update:**
136
- - v2.1
137
-
138
125
  ## Version History
139
126
 
140
127
  See [CHANGELOG.md](./CHANGELOG.md) for detailed version history.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@toon-format/spec",
3
3
  "type": "module",
4
- "version": "1.3.3",
4
+ "version": "1.4.0",
5
5
  "packageManager": "pnpm@10.19.0",
6
6
  "description": "Official specification for Token-Oriented Object Notation (TOON)",
7
7
  "author": "Johann Schopplich <hello@johannschopplich.com>",