6 Types and value-reading statements
This chapter defines the execution of declarations, lists, and named-type definitions. Chapter 5 defines their syntax; chapter 4 defines scanning, token conversion, and positions. Results use the entries and canonical JSON of chapter 3.
6.1 Reading types and resulting entries
A reading type determines how input is converted and validated. Its
resulting value type describes the stored scalar leaves. Implementations
MUST use the following correspondence and reject incompatible statement
forms during parsing with E-SPEC-TYPE.
Aliases MUST obey the same restrictions as their underlying reading type.
| Reading type | Stored value type | Permitted value reads |
|---|---|---|
int |
int |
Scalar, fixed-size array, list |
float |
float |
Scalar, fixed-size array, list |
num |
int or float |
Scalar, fixed-size array, list |
| Integer range | int |
Scalar, fixed-size array, list |
word |
string |
Scalar, fixed-size array, list |
rest |
string |
Scalar only |
| Alphabet | int |
Exactly two-dimensional fixed-size array |
| Named type | Its underlying reading type's result | Its underlying type's permitted forms |
Numeric reading types are int, float, num, integer ranges, and their
aliases.
A scalar declaration MUST produce depth 0. A declaration with k dimensions MUST produce depth k; a list MUST produce depth 1. A successful value read MUST store its name, value type, depth, and complete value as an entry, except for a discarded read (§6.3). A type definition MUST NOT produce an entry. Repeat collection adds array structure around these local results; §7.5 defines that collection.
6.2 Scalar reading and numeric promotion
An int read MUST produce a signed 64-bit integer. A float read MUST
produce binary64 even for an integer-looking token. A num read MUST try
integer conversion first, then float conversion. An integer spelling
outside the signed 64-bit domain can therefore produce a float through
num. The accepted spellings, conversion failures, and rounding rules are
in §§4.4–4.5.
A word read MUST produce the next token as a string. A rest read MUST
produce the remaining line text under
§4.2. It requires available
input after initial skipping. Both produce value type string; string
is not a built-in reading type. All reads, including discarded reads,
MUST obey §4.10.
id: values-scalarsoperation: read--- spec no-final-newlinea:int b:float c:num d:num w:word r:rest--- input hex2d 37 20 32 20 33 20 34 2e 35 20 68 69 20 20 74 68 65 20 72 65 73 74 20 20 0a--- expected data{"iris": "0.1", "entries": [ {"name": "a", "type": "int", "depth": 0, "value": -7}, {"name": "b", "type": "float", "depth": 0, "value": 2}, {"name": "c", "type": "int", "depth": 0, "value": 3}, {"name": "d", "type": "float", "depth": 0, "value": 4.5}, {"name": "w", "type": "string", "depth": 0, "value": "hi"}, {"name": "r", "type": "string", "depth": 0, "value": "the rest"}]}In a num array or list, implementations MUST retain integer storage until
a stored float requires promotion. They MUST then convert all earlier
integer leaves and every later integer leaf to binary64. Promotion MUST
apply across the whole array, including different rows. It can round an
earlier large integer. A sentinel is not stored and MUST NOT itself cause
promotion. Empty num arrays and lists MUST have value type int.
id: values-num-promotionoperation: read--- spec no-final-newlinea[2][2]:num list b:num until 0 c:num--- input no-final-newline1 2.5 3 4 9007199254740993 1.0 0 9223372036854775808--- expected data{"iris": "0.1", "entries": [ {"name": "a", "type": "float", "depth": 2, "value": [[1, 2.5], [3, 4]]}, {"name": "b", "type": "float", "depth": 1, "value": [9007199254740992, 1]}, {"name": "c", "type": "float", "depth": 0, "value": 9223372036854776000}]}6.3 Names, discarded reads, and replacement
Names MUST follow §5.4, including its reserved
prefix and contextual-keyword rules. A declaration or list named _ MUST
perform its complete read and validation but MUST NOT store an entry.
Discarding MUST NOT bypass conversion, dimensions, ranges, layouts,
offsets, or their errors.
A later successful value read with the same name MUST replace the earlier value, including its type and depth, while retaining its original entry position under §3.1. General value lookup follows §8.1; record-name scope follows §8.12.
id: values-discard-redefineoperation: read--- spec no-final-newlinex:int y:word _:int x[2]:float list _:int until 0--- input no-final-newline1 hi 99 2 3 8 0--- expected data{"iris": "0.1", "entries": [ {"name": "x", "type": "float", "depth": 1, "value": [2, 3]}, {"name": "y", "type": "string", "depth": 0, "value": "hi"}]}Named types instead obey §6.5: a visible type MUST NOT be redefined. Types and ordinary value definitions MUST NOT share a name anywhere in a reader spec. A type and a record-list name MAY coincide. §7.6 defines record names and record-list names; they are not value entries.
6.4 Integer ranges
A range MUST read integers. Iris 0.1 has no bounded float or bounded
num reading type. A bound that evaluates to a float, even an integral
float, MUST fail with E-RANGE-BOUNDS.
An input token 2.0 cannot satisfy an integer range.
id: values-range-float-valueoperation: read--- spec no-final-newlinex:1..3--- input no-final-newline2.0--- expected error{ "code": "E-EXPECTED-VALUE", "message": "expected an int in 1..3, found '2.0'", "input_location": {"origin": "input.bin", "line": 1, "column": 1}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "x", "record_path": null}The range lo..hi MUST include both bounds; lo..<hi MUST include lo and
exclude hi. An omitted bound MUST leave that end unbounded within the
signed 64-bit input domain. The permitted omissions are specified by
§5.7.
id: values-range-exclusive-openoperation: read--- specx:..<0--- input no-final-newline-1--- expected data{"iris": "0.1", "entries": [{"name": "x", "type": "int", "depth": 0, "value": -1}]}Implementations MUST evaluate lower then upper bounds as integer scalars.
Strings, arrays, and floats MUST fail with E-RANGE-BOUNDS. For an exclusive
upper bound, implementations MUST subtract 1 using checked integer
arithmetic; subtracting from -9223372036854775808 MUST fail with
E-INTEGER-OVERFLOW. An inverted interval MUST NOT itself be rejected;
it accepts no ordinary value, but an immediate sentinel can end an empty
list.
id: values-range-reversedoperation: read--- spec no-final-newlinelist a:2..1 until 0--- input no-final-newline0--- expected data{"iris": "0.1", "entries": [ {"name": "a", "type": "int", "depth": 1, "value": []}]}For an anonymous range, implementations MUST evaluate its bounds once on each execution of the declaration or list, before reading values, including when its result is empty. They MUST NOT reevaluate bounds for each array or list element. Reexecuting a statement reevaluates its anonymous range. A named range instead uses the bounds saved by its executed definition (§6.5). §8.1 and §8.4 define lookup and arithmetic for expressions used as bounds.
id: values-range-empty-evaluatesoperation: read--- spec no-final-newlinea[0]:0..2.5--- input--- expected error{ "code": "E-RANGE-BOUNDS", "message": "the bounds of a range must be integers", "input_location": {"origin": "input.bin", "line": 1, "column": 1}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "a", "record_path": null}id: values-range-exclusive-overflowoperation: read--- spec no-final-newlinex:0..<(-9223372036854775807-1)--- input no-final-newline1--- expected error{ "code": "E-INTEGER-OVERFLOW", "message": "integer overflow in the upper bound of a range", "input_location": {"origin": "input.bin", "line": 1, "column": 1}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "x", "record_path": null}Implementations MUST check each input integer against the range before applying offset. They MUST recognize numeric sentinels before range checking and MUST NOT range-check them. Unread triangular diagonal zeros are not input values and MUST NOT be range-checked.
id: values-rangesoperation: read--- spec no-final-newlinen:int a[n]:0..<n b:..0 c:-2..-1 d:1..2 offset -1 list e:1..2 until -1 offset 10--- input no-final-newline2 0 1 -5 -2 2 1 2 -1--- expected data{"iris": "0.1", "entries": [ {"name": "n", "type": "int", "depth": 0, "value": 2}, {"name": "a", "type": "int", "depth": 1, "value": [0, 1]}, {"name": "b", "type": "int", "depth": 0, "value": -5}, {"name": "c", "type": "int", "depth": 0, "value": -2}, {"name": "d", "type": "int", "depth": 0, "value": 1}, {"name": "e", "type": "int", "depth": 1, "value": [11, 12]}]}A failed conversion or out-of-range value MUST report E-EXPECTED-VALUE
at the offending token's start, with profile R availability from
§11.3.2: the active reader-spec line,
null reader-spec column, active execution context, and null record path.
There is no separate range-rejection code. Named-type diagnostics SHOULD
include the name and saved range, as in §6.5.
id: values-range-rejectoperation: read--- spec no-final-newlinex:1..3--- input4--- expected error{ "code": "E-EXPECTED-VALUE", "message": "expected an int in 1..3, found '4'", "input_location": {"origin": "input.bin", "line": 1, "column": 1}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "x", "record_path": null}6.5 Named types and visibility
Type visibility MUST be lexical, according to this table. These rules apply even in blocks execution would not reach.
| Situation | Rule |
|---|---|
| Definition's right-hand side | The new type is not yet visible. |
| Following statements in the same block | The type is visible. |
| Nested blocks after the definition | The type remains visible. |
| Statements outside its defining block | The type is not visible. |
| Forward use or use after leaving its block | E-SPEC-UNKNOWN-TYPE |
| Redefinition while a same-named type is visible | E-SPEC-NAME-CONFLICT; no shadowing |
| Separate sibling blocks | Each may define its own type with the same name. |
Defining a base-type name, including alphabet |
E-SPEC-NAME-CONFLICT |
| Same spelling as an ordinary value definition anywhere in the reader spec | E-SPEC-NAME-CONFLICT, including disjoint or unreachable blocks |
The final row MUST be checked globally after statement parsing, before name resolution, under §5.1. Type visibility is determined during parsing; bound-expression resolution and record-name checks do not extend that visibility.
id: values-type-forwardoperation: parse--- spec no-final-newlinea:t type t=int--- expected error{ "code": "E-SPEC-UNKNOWN-TYPE", "message": "unknown type 't'", "input_location": null, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": null, "record_path": null}id: values-type-escapeoperation: parse--- spec no-final-newlineif 1 {type t=int} a:t--- expected error{ "code": "E-SPEC-UNKNOWN-TYPE", "message": "unknown type 't'", "input_location": null, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": null, "record_path": null}id: values-type-shadowoperation: parse--- spec no-final-newlinetype t=int if 1 {type t=word}--- expected error{ "code": "E-SPEC-NAME-CONFLICT", "message": "the type 't' is already defined", "input_location": null, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": null, "record_path": null}id: values-type-baseoperation: parse--- spec no-final-newlinetype int=0..--- expected error{ "code": "E-SPEC-NAME-CONFLICT", "message": "'int' is a base type", "input_location": null, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": null, "record_path": null}id: values-type-value-conflictoperation: parse--- spec no-final-newlineif 0 {type t=int} t:int--- expected error{ "code": "E-SPEC-NAME-CONFLICT", "message": "'t' names a type, so it cannot also name a value", "input_location": null, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": null, "record_path": null}id: values-type-siblingsoperation: read--- spec no-final-newlineif 1 {type t=int a:t} if 1 {type t=word b:t}--- input no-final-newline7 hi--- expected data{"iris": "0.1", "entries": [ {"name": "a", "type": "int", "depth": 0, "value": 7}, {"name": "b", "type": "string", "depth": 0, "value": "hi"}]}id: values-type-nestedoperation: read--- spec no-final-newlinetype t=int if 1 {a:t}--- input no-final-newline7--- expected data{"iris": "0.1", "entries": [ {"name": "a", "type": "int", "depth": 0, "value": 7}]}Executing a type definition MUST evaluate and save its range at that point and MUST NOT read input or produce an entry. Subsequent changes to values used by its bounds MUST NOT change the saved range. Executing an alias definition MUST capture the referenced type's evaluated range. The alias also retains its underlying reading type and alphabet options, if any.
id: values-range-timingoperation: read--- spec no-final-newlinen:int type frozen=0..n type alias=frozen let n=3 a:alias b:0..n--- input no-final-newline2 2 3--- expected data{"iris": "0.1", "entries": [ {"name": "n", "type": "int", "depth": 0, "value": 3}, {"name": "a", "type": "int", "depth": 0, "value": 2}, {"name": "b", "type": "int", "depth": 0, "value": 3}]}id: values-range-frozen-rejectoperation: read--- spec no-final-newlinen:int type t=0..n let n=3 a:t--- input no-final-newline2 3--- expected error{ "code": "E-EXPECTED-VALUE", "message": "expected t (an int in 0..2), found '3'", "input_location": {"origin": "input.bin", "line": 1, "column": 3}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "a", "record_path": null}id: values-type-aliasesoperation: read--- spec no-final-newlinetype w=word type v=w type c=alphabet "ab" blank "." a:v g[1][2]:c--- input no-final-newlinehi a.--- expected data{"iris": "0.1", "entries": [ {"name": "a", "type": "string", "depth": 0, "value": "hi"}, {"name": "g", "type": "int", "depth": 2, "value": [[1, 0]]}]}A type definition in a repeat MUST execute again on each iteration. An unexecuted type definition MUST NOT evaluate its bounds.
id: values-range-reexecutionoperation: read--- spec no-final-newlinerepeat 2 {n:int type t=0..n a:t}--- input no-final-newline1 1 3 3--- expected data{"iris": "0.1", "entries": [ {"name": "n", "type": "int", "depth": 1, "value": [1, 3]}, {"name": "a", "type": "int", "depth": 1, "value": [1, 3]}]}id: values-type-unreachedoperation: read--- spec no-final-newlineif 0 {type t=0..1.0 a:t}--- input--- expected data{"iris": "0.1", "entries": []}If runtime lookup of a parsed named type has no executed definition,
implementations MUST report E-TYPE-UNDEFINED. This is a conditional guard
preserved from the reference. No public reader spec exercising it has been
established: lexical visibility and ordinary sequencing appear to place
each legal use after its definition. Forward or escaped uses fail during
parsing instead, as the examples above show.
6.6 Fixed-size arrays and empty rows
Implementations MUST evaluate dimensions from left to right. Every dimension
MUST be an integer scalar in 0..2147483647; violations MUST report
E-COUNT. A zero dimension MUST NOT suppress evaluation of later dimensions.
All dimensions MUST be evaluated before checking their product.
id: values-count-typeoperation: read--- spec no-final-newlinex[1.0]:int--- input--- expected error{ "code": "E-COUNT", "message": "array size must be an integer", "input_location": {"origin": "input.bin", "line": 1, "column": 1}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "x", "record_path": null}id: values-count-negativeoperation: read--- spec no-final-newlinex[-1]:int--- input--- expected error{ "code": "E-COUNT", "message": "array size is negative (-1)", "input_location": {"origin": "input.bin", "line": 1, "column": 1}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "x", "record_path": null}id: values-count-largeoperation: read--- spec no-final-newlinex[2147483648]:int--- input--- expected error{ "code": "E-COUNT", "message": "array size is too large (2147483648)", "input_location": {"origin": "input.bin", "line": 1, "column": 1}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "x", "record_path": null}For ordinary arrays, implementations MUST multiply dimensions in order and
report E-SIZE-LIMIT if any intermediate product exceeds 2147483647.
Thus a later zero dimension does not rescue an excessive earlier product.
Implementations MUST read leaves with the last dimension varying fastest,
independently of physical input line boundaries.
id: values-size-limitoperation: read--- spec no-final-newlinex[46341][46341]:int--- input--- expected error{ "code": "E-SIZE-LIMIT", "message": "array is too large", "input_location": {"origin": "input.bin", "line": 1, "column": 1}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "x", "record_path": null}id: values-arraysoperation: read--- spec no-final-newlinen:int a[n][2][2]:int--- input no-final-newline2 1 2 3 4 5 6 7 8--- expected data{"iris": "0.1", "entries": [ {"name": "n", "type": "int", "depth": 0, "value": 2}, {"name": "a", "type": "int", "depth": 3, "value": [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]}]}Zero dimensions MUST consume no values and MUST preserve the declared
value type and depth, with num stored as int. For example, [2][0]
produces two empty rows, while [0][3] produces no rows at depth 2.
Implementations MUST NOT invent structures below a nonexistent row.
Alphabet arrays likewise retain value type int and depth 2 when empty.
id: values-emptyoperation: read--- spec no-final-newlinea[0]:int b[0]:float c[0]:word d[2][0]:float e[0][3]:num list f:float until 0 list g:word until "END"--- input no-final-newline0 END--- expected data{"iris": "0.1", "entries": [ {"name": "a", "type": "int", "depth": 1, "value": []}, {"name": "b", "type": "float", "depth": 1, "value": []}, {"name": "c", "type": "string", "depth": 1, "value": []}, {"name": "d", "type": "float", "depth": 2, "value": [[], []]}, {"name": "e", "type": "int", "depth": 2, "value": []}, {"name": "f", "type": "float", "depth": 1, "value": []}, {"name": "g", "type": "string", "depth": 1, "value": []}]}Empty rows can also be read using an immediate sentinel. §7.5 defines their collection by repeat.
id: values-empty-rowsoperation: read--- spec no-final-newlinea[2][0]:int repeat 2 {list b:int until 0}--- input00--- expected data{"iris": "0.1", "entries": [ {"name": "a", "type": "int", "depth": 2, "value": [[], []]}, {"name": "b", "type": "int", "depth": 2, "value": [[], []]}]}A blank physical line MUST NOT supply an empty until eol list: initial
skipping seeks data on a later line and fails if none remains (§6.9).
Use a zero dimension or an immediate sentinel for an empty row. Empty
structures remain subject to the
idle charges in §10.2.
id: values-type-rest-arrayoperation: parse--- spec no-final-newlinetype t=rest a[1]:t--- expected error{ "code": "E-SPEC-TYPE", "message": "'rest' values cannot be arrays", "input_location": null, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": null, "record_path": null}6.7 Offsets and evaluation order
An offset MUST be permitted only for numeric reads and MUST evaluate to an
integer scalar. A nonnumeric reading type with an offset MUST fail during
parsing with E-SPEC-TYPE; an offset value of the wrong type or depth MUST
fail during execution with E-OFFSET-TYPE.
id: values-type-word-offsetoperation: parse--- spec no-final-newlinea:word offset 1--- expected error{ "code": "E-SPEC-TYPE", "message": "'offset' needs numbers", "input_location": null, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": null, "record_path": null}Implementations MUST add the offset to each converted, range-checked number,
preserving its int or float value type. Integer addition MUST be checked
and report E-INTEGER-OVERFLOW on overflow. Float addition MUST convert
the offset to binary64. Sentinels MUST NOT be adjusted. For triangular
layouts, implementations MUST adjust each stored triangle value before
mirroring; omitted diagonals MUST stay zero (§6.8).
The evaluation order MUST be:
| Statement | Order |
|---|---|
| Declaration | Evaluate offset; obtain or evaluate range; evaluate dimensions; evaluate layout if present; read and range-check values; adjust values; store. |
| List | Obtain or evaluate range; evaluate sentinel if present; read through termination; evaluate offset; adjust stored values; store. |
| Type definition | Evaluate and save its range. |
A failing step MUST stop execution before later steps. In particular, a list's offset failure occurs after consuming its sentinel or reaching end of line, while a declaration's offset-type failure occurs before reading.
id: values-offset-typeoperation: read--- spec no-final-newlinex:int offset 1.0--- input no-final-newline7--- expected error{ "code": "E-OFFSET-TYPE", "message": "offset must be an integer", "input_location": {"origin": "input.bin", "line": 1, "column": 1}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "x", "record_path": null}id: values-offset-list-timingoperation: read--- spec no-final-newlinelist x:int until 0 offset "x"--- input no-final-newline1 2 0--- expected error{ "code": "E-OFFSET-TYPE", "message": "offset must be an integer", "input_location": {"origin": "input.bin", "line": 1, "column": 6}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "x", "record_path": null}id: values-offset-overflowoperation: read--- spec no-final-newlinex:int offset 1--- input no-final-newline9223372036854775807--- expected error{ "code": "E-INTEGER-OVERFLOW", "message": "integer overflow when applying offset", "input_location": {"origin": "input.bin", "line": 1, "column": 20}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "x", "record_path": null}When no offset is written, the default zero MUST still be added to stored numbers. The effect on floating point signed zero is defined and illustrated in §3.5.
6.8 Matrix layouts
A layout MUST be used only with a two-dimensional numeric declaration;
other uses MUST fail during parsing with E-SPEC-TYPE. Its expression MUST
evaluate to a string scalar. Implementations MUST recognize exactly the
nine case-sensitive names in this table; there are no other aliases or
a diagonal-only layout.
Indices below start at 0. For r rows and c columns, or an n by n triangular layout, implementations MUST consume values in the following cell order. Both loops increase; an empty index interval contributes no cells.
| Layout | Outer loop | Inner loop; cell is (i, j) |
|---|---|---|
FULL_MATRIX |
i = 0 through r−1 | j = 0 through c−1 |
UPPER_ROW |
i = 0 through n−1 | j = i+1 through n−1 |
LOWER_ROW |
i = 0 through n−1 | j = 0 through i−1 |
UPPER_DIAG_ROW |
i = 0 through n−1 | j = i through n−1 |
LOWER_DIAG_ROW |
i = 0 through n−1 | j = 0 through i |
UPPER_COL |
j = 0 through n−1 | i = 0 through j−1 |
LOWER_COL |
j = 0 through n−1 | i = j+1 through n−1 |
UPPER_DIAG_COL |
j = 0 through n−1 | i = 0 through j |
LOWER_DIAG_COL |
j = 0 through n−1 | i = j through n−1 |
FULL_MATRIX MUST read all cells without mirroring. It MUST accept
rectangular and asymmetric matrices and use ordinary array size checks.
id: values-layout-full-matrixoperation: read--- spec no-final-newlinem[4][4]:int layout "FULL_MATRIX" offset 10--- input1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16--- expected data{"iris": "0.1", "entries": [ {"name": "m", "type": "int", "depth": 2, "value": [[11, 12, 13, 14], [15, 16, 17, 18], [19, 20, 21, 22], [23, 24, 25, 26]]}]}id: values-layout-rectangularoperation: read--- spec no-final-newlinem[2][3]:int layout "FULL_MATRIX" offset -1--- input no-final-newline1 2 3 4 5 6--- expected data{"iris": "0.1", "entries": [ {"name": "m", "type": "int", "depth": 2, "value": [[0, 1, 2], [3, 4, 5]]}]}Every triangular layout MUST require equal dimensions and n*n at most
2147483647, reporting E-LAYOUT or E-SIZE-LIMIT, respectively.
Implementations MUST initialize the matrix to numeric zero, read and
range-check the stored triangle, apply offset to its values, and assign
each adjusted value at (i, j) to both (i, j) and (j, i). The result MUST be
symmetric at depth 2. Non-diagonal layouts MUST leave all diagonal cells
zero, regardless of range or offset. Diagonal layouts MUST read and adjust
the diagonals as well.
Each example uses distinct stored input values and offset 10. Mirrored
copies necessarily repeat. The four row/column pairs yield equal results:
UPPER_ROW/LOWER_COL, LOWER_ROW/UPPER_COL,
UPPER_DIAG_ROW/LOWER_DIAG_COL, and
LOWER_DIAG_ROW/UPPER_DIAG_COL.
id: values-layout-upper-rowoperation: read--- spec no-final-newlinem[4][4]:int layout "UPPER_ROW" offset 10--- input1 2 3 4 5 6--- expected data{"iris": "0.1", "entries": [ {"name": "m", "type": "int", "depth": 2, "value": [[0, 11, 12, 13], [11, 0, 14, 15], [12, 14, 0, 16], [13, 15, 16, 0]]}]}id: values-layout-lower-rowoperation: read--- spec no-final-newlinem[4][4]:int layout "LOWER_ROW" offset 10--- input1 2 3 4 5 6--- expected data{"iris": "0.1", "entries": [ {"name": "m", "type": "int", "depth": 2, "value": [[0, 11, 12, 14], [11, 0, 13, 15], [12, 13, 0, 16], [14, 15, 16, 0]]}]}id: values-layout-upper-diag-rowoperation: read--- spec no-final-newlinem[4][4]:int layout "UPPER_DIAG_ROW" offset 10--- input1 2 3 4 5 6 7 8 9 10--- expected data{"iris": "0.1", "entries": [ {"name": "m", "type": "int", "depth": 2, "value": [[11, 12, 13, 14], [12, 15, 16, 17], [13, 16, 18, 19], [14, 17, 19, 20]]}]}id: values-layout-lower-diag-rowoperation: read--- spec no-final-newlinem[4][4]:int layout "LOWER_DIAG_ROW" offset 10--- input1 2 3 4 5 6 7 8 9 10--- expected data{"iris": "0.1", "entries": [ {"name": "m", "type": "int", "depth": 2, "value": [[11, 12, 14, 17], [12, 13, 15, 18], [14, 15, 16, 19], [17, 18, 19, 20]]}]}id: values-layout-upper-coloperation: read--- spec no-final-newlinem[4][4]:int layout "UPPER_COL" offset 10--- input1 2 3 4 5 6--- expected data{"iris": "0.1", "entries": [ {"name": "m", "type": "int", "depth": 2, "value": [[0, 11, 12, 14], [11, 0, 13, 15], [12, 13, 0, 16], [14, 15, 16, 0]]}]}id: values-layout-lower-coloperation: read--- spec no-final-newlinem[4][4]:int layout "LOWER_COL" offset 10--- input1 2 3 4 5 6--- expected data{"iris": "0.1", "entries": [ {"name": "m", "type": "int", "depth": 2, "value": [[0, 11, 12, 13], [11, 0, 14, 15], [12, 14, 0, 16], [13, 15, 16, 0]]}]}id: values-layout-upper-diag-coloperation: read--- spec no-final-newlinem[4][4]:int layout "UPPER_DIAG_COL" offset 10--- input1 2 3 4 5 6 7 8 9 10--- expected data{"iris": "0.1", "entries": [ {"name": "m", "type": "int", "depth": 2, "value": [[11, 12, 14, 17], [12, 13, 15, 18], [14, 15, 16, 19], [17, 18, 19, 20]]}]}id: values-layout-lower-diag-coloperation: read--- spec no-final-newlinem[4][4]:int layout "LOWER_DIAG_COL" offset 10--- input1 2 3 4 5 6 7 8 9 10--- expected data{"iris": "0.1", "entries": [ {"name": "m", "type": "int", "depth": 2, "value": [[11, 12, 13, 14], [12, 15, 16, 17], [13, 16, 18, 19], [14, 17, 19, 20]]}]}Layout validation MUST first require a string scalar, then handle
FULL_MATRIX, then require squareness, then recognize a triangular name,
then check its cell count. An unknown non-FULL_MATRIX name on a
rectangular matrix therefore fails the square-matrix check first.
id: values-type-layout-shapeoperation: parse--- spec no-final-newlinea[1]:int layout "FULL_MATRIX"--- expected error{ "code": "E-SPEC-TYPE", "message": "'layout' needs a two-dimensional array of numbers", "input_location": null, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": null, "record_path": null}id: values-layout-typeoperation: read--- spec no-final-newlinem[2][2]:int layout 1--- input--- expected error{ "code": "E-LAYOUT", "message": "matrix layout must be a string", "input_location": {"origin": "input.bin", "line": 1, "column": 1}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "m", "record_path": null}id: values-layout-squareoperation: read--- spec no-final-newlinem[2][3]:int layout "UPPER_ROW"--- input--- expected error{ "code": "E-LAYOUT", "message": "layout UPPER_ROW needs a square matrix", "input_location": {"origin": "input.bin", "line": 1, "column": 1}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "m", "record_path": null}id: values-layout-unknownoperation: read--- spec no-final-newlinem[2][2]:int layout "DIAGONAL"--- input--- expected error{ "code": "E-LAYOUT", "message": "unknown matrix layout 'DIAGONAL'", "input_location": {"origin": "input.bin", "line": 1, "column": 1}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "m", "record_path": null}id: values-layout-limitoperation: read--- spec no-final-newlinem[46341][46341]:int layout "UPPER_ROW"--- input--- expected error{ "code": "E-SIZE-LIMIT", "message": "matrix is too large", "input_location": {"origin": "input.bin", "line": 1, "column": 1}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "m", "record_path": null}6.9 Sentinel and end-of-line lists
A list MUST produce a depth-1 array. For a sentinel list, implementations MUST evaluate the sentinel expression once as a scalar, then read across physical lines until it is found. They MUST consume but MUST NOT store the sentinel. Its type MUST NOT cause numeric promotion of the result.
Sentinel recognition MUST follow these rules:
- Two integers compare exactly. Other numeric comparisons convert both operands to binary64 before testing equality.
- A string sentinel compares against the complete next token before
conversion, so a nonnumeric string such as
ENDcan end a numeric list. - A word list accepts a string sentinel, or an integer sentinel converted
to its ordinary decimal spelling. The words
+0and00do not match the integer sentinel 0. A float sentinel for a word list MUST reportE-LIST-TERMINATOR. - An input token matching a numeric sentinel MUST still be readable by the list's reading type. Numeric conversion precedes equality; equality precedes range checking.
- Sentinels MUST NOT be range-checked or adjusted by offset.
id: values-listsoperation: read--- spec no-final-newlinelist a:1..3 until -1 offset -1 list b:float until "END" list c:word until 0 list d:num until 2.0 tail:int--- input no-final-newline1 3 -1 1 2.5 END +0 00 0 1 2 9--- expected data{"iris": "0.1", "entries": [ {"name": "a", "type": "int", "depth": 1, "value": [0, 2]}, {"name": "b", "type": "float", "depth": 1, "value": [1, 2.5]}, {"name": "c", "type": "string", "depth": 1, "value": ["+0", "00"]}, {"name": "d", "type": "int", "depth": 1, "value": [1]}, {"name": "tail", "type": "int", "depth": 0, "value": 9}]}Mixed numeric comparison can consume a mathematically different integer because of binary64 rounding. The sentinel below excludes the integer 9007199254740993 without promoting the empty result.
id: values-sentinel-roundingoperation: read--- spec no-final-newlinelist a:num until 9007199254740992.0 tail:int--- input no-final-newline9007199254740993 7--- expected data{"iris": "0.1", "entries": [ {"name": "a", "type": "int", "depth": 1, "value": []}, {"name": "tail", "type": "int", "depth": 0, "value": 7}]}For until eol, implementations MUST use the remaining data on the current
physical line, or seek the next data if that line is exhausted, then read
while data remains on that line. They MUST apply
chapter 4's scanning settings.
Blank physical lines MUST NOT produce empty lists. If no data remains,
the statement MUST report E-EXPECTED-VALUE rather than return an empty
list. End of line is a scanning boundary, not a token sentinel.
id: values-lists-eoloperation: read--- spec no-final-newlinelist a:int until 0 list b:int until eol list c:int until eol--- input1 0 2 3
4 5--- expected data{"iris": "0.1", "entries": [ {"name": "a", "type": "int", "depth": 1, "value": [1]}, {"name": "b", "type": "int", "depth": 1, "value": [2, 3]}, {"name": "c", "type": "int", "depth": 1, "value": [4, 5]}]}id: values-lists-eofoperation: read--- spec no-final-newlinelist a:int until eol--- input
--- expected error{ "code": "E-EXPECTED-VALUE", "message": "expected a line of values, found end of input", "input_location": {"origin": "input.bin", "line": 3, "column": 1}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "a", "record_path": null}At end of input without its sentinel, a string-sentinel list MUST report
E-EXPECTED-TOKEN; a numeric-sentinel list MUST report E-EXPECTED-VALUE
for the missing reading value.
id: values-lists-numeric-eofoperation: read--- spec no-final-newlinelist a:int until 0--- input1--- expected error{ "code": "E-EXPECTED-VALUE", "message": "expected int, found end of input", "input_location": {"origin": "input.bin", "line": 2, "column": 1}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "a", "record_path": null}id: values-lists-string-eofoperation: read--- spec no-final-newlinelist a:int until "END"--- input1--- expected error{ "code": "E-EXPECTED-TOKEN", "message": "expected 'END', found end of input", "input_location": {"origin": "input.bin", "line": 2, "column": 1}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "a", "record_path": null}id: values-lists-terminator-shapeoperation: read--- spec no-final-newlinet[1]:int list a:int until t--- input no-final-newline0--- expected error{ "code": "E-LIST-TERMINATOR", "message": "list terminator must be a single value", "input_location": {"origin": "input.bin", "line": 1, "column": 2}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "a", "record_path": null}id: values-lists-terminator-wordoperation: read--- spec no-final-newlinelist a:word until 1.0--- input--- expected error{ "code": "E-LIST-TERMINATOR", "message": "a list of words needs a string or integer terminator", "input_location": {"origin": "input.bin", "line": 1, "column": 1}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "a", "record_path": null}id: values-type-list-restoperation: parse--- spec no-final-newlinelist a:rest until eol--- expected error{ "code": "E-SPEC-TYPE", "message": "a list cannot be of 'rest' or of an alphabet type", "input_location": null, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": null, "record_path": null}6.10 Alphabets
An alphabet MUST read exactly rows*columns symbols into an int entry
at depth 2, in row-major order. Physical input lines MUST NOT delimit
matrix rows. The dimensions follow §6.6; their product MUST be at most
2147483647, otherwise E-SIZE-LIMIT.
Before each symbol, implementations MUST apply normal skipping under
chapter 4, then discard a byte
in strip and repeat skipping until a non-stripped byte is available.
For that byte they MUST apply this priority:
- A byte in
blankproduces 0, even if it also occurs in the alphabet. - Otherwise, a byte in the alphabet produces its first zero-based byte index plus base.
- Otherwise, report
E-SYMBOLat that byte.
Base defaults to 1; blank and strip default to empty strings. Strip thus takes priority over blank, which takes priority over alphabet mapping. Duplicate alphabet bytes MUST use their first occurrence. Repeated options MUST replace earlier values independently, as specified in §5.7.
id: values-alphabetoperation: read--- spec no-final-newlineg[2][3]:alphabet "ab" base 0 blank "." strip "|-"--- input no-final-newlinea|b.---b|a.--- expected data{"iris": "0.1", "entries": [ {"name": "g", "type": "int", "depth": 2, "value": [[0, 1, 0], [1, 0, 0]]}]}id: values-alphabet-optionsoperation: read--- spec no-final-newlineg[1][4]:alphabet "aba" base 5 blank "a" strip "b" base -2 blank "." strip "|"--- input no-final-newlinea|b.a--- expected data{"iris": "0.1", "entries": [ {"name": "g", "type": "int", "depth": 2, "value": [[-2, -1, 0, -2]]}]}id: values-alphabet-priorityoperation: read--- spec no-final-newlineg[1][2]:alphabet "ab" blank "a" strip "."--- input no-final-newlinea.b--- expected data{"iris": "0.1", "entries": [ {"name": "g", "type": "int", "depth": 2, "value": [[0, 2]]}]}id: values-alphabet-overlap-stripoperation: read--- spec no-final-newlineg[1][1]:alphabet "ab" blank "a" strip "a"--- input no-final-newlineab--- expected data{"iris": "0.1", "entries": [ {"name": "g", "type": "int", "depth": 2, "value": [[2]]}]}Alphabet matching, indexing, and membership in options MUST operate on
bytes, including UTF-8 bytes, rather than Unicode scalar values. A
non-ASCII character in the alphabet occupies one position per UTF-8 byte;
in blank or strip, each of its bytes is an independent member. Each
byte selected after skipping and stripping occupies one matrix cell. Thus
éa below supplies three cells. This integer result does not relax the
input-wide UTF-8 requirements or the
reader-spec UTF-8 requirements.
id: values-alphabet-bytesoperation: read--- spec no-final-newlineg[1][3]:alphabet "éa"--- input no-final-newlineéa--- expected data{"iris": "0.1", "entries": [ {"name": "g", "type": "int", "depth": 2, "value": [[1, 2, 3]]}]}Empty alphabets MUST be permitted, including blank-only reads and empty
matrices. Empty arrays MUST retain value type int and depth 2.
id: values-alphabet-emptyoperation: read--- spec no-final-newlinea[0][2]:alphabet "ab" b[2][0]:alphabet ""--- input--- expected data{"iris": "0.1", "entries": [ {"name": "a", "type": "int", "depth": 2, "value": []}, {"name": "b", "type": "int", "depth": 2, "value": [[], []]}]}Base-plus-index addition MUST be checked and report E-INTEGER-OVERFLOW
on overflow. An unrecognized byte or missing symbol MUST report E-SYMBOL.
Missing symbols locate end of input; the recommended message identifies
the required row and column, both starting at 1.
id: values-alphabet-symboloperation: read--- spec no-final-newlineg[1][2]:alphabet "ab"--- input no-final-newlineax--- expected error{ "code": "E-SYMBOL", "message": "unexpected symbol 'x', expected one of 'ab'", "input_location": {"origin": "input.bin", "line": 1, "column": 2}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "g", "record_path": null}id: values-alphabet-eofoperation: read--- spec no-final-newlineg[2][2]:alphabet "ab"--- input no-final-newlineaba--- expected error{ "code": "E-SYMBOL", "message": "expected a symbol for row 2, column 2, found end of input", "input_location": {"origin": "input.bin", "line": 2, "column": 2}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "g", "record_path": null}id: values-alphabet-overflowoperation: read--- spec no-final-newlineg[1][1]:alphabet "ab" base 9223372036854775807--- input no-final-newlineb--- expected error{ "code": "E-INTEGER-OVERFLOW", "message": "integer overflow in the value of a symbol", "input_location": {"origin": "input.bin", "line": 1, "column": 1}, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": "g", "record_path": null}Alphabet reading types MUST have exactly two dimensions and MUST NOT be
used as scalars, lists, or with layout or offset. These are parse-time
E-SPEC-TYPE restrictions. Captures MUST reject alphabet types with
E-SPEC-PATTERN; §7.2
defines captures.
id: values-type-alphabet-shapeoperation: parse--- spec no-final-newlineg[1]:alphabet "ab"--- expected error{ "code": "E-SPEC-TYPE", "message": "an alphabet type needs a two-dimensional array", "input_location": null, "spec_location": {"origin": "spec.iris", "line": 1, "column": null}, "context": null, "record_path": null}6.11 Errors and related chapters
The parse-time registry assigns codes for unknown types, name conflicts, and incompatible reading forms. The read-time registry assigns codes for value conversion and range rejection, invalid bounds, counts, layouts, offsets, sentinels, symbols, structural size limits, and checked integer adjustment. Implementations MUST use those codes and location profiles, following the local evaluation order in §6.7 and the general error order. Errors after consumption, such as offset overflow, MUST retain their reported cursor rather than be moved to a token's start.
E-VALUE-TYPE-MIX guards attempts to combine strings and numbers. A single
fixed reading type cannot produce that mixture in an ordinary array or
list; the direct value-construction guard is defensive. Repeat columns can
encounter it through different reading statements.
§7.5 defines
that behavior; §7.10
includes the structure-value-type-mix example.
E-TYPE-UNDEFINED remains the conditional runtime guard described in §6.5,
without a verified public reproducer.
Expression evaluation and its failures follow §8.14; parameters and instances belong to chapter 9. Chapter 10 defines work accounting, including empty rows. Appendix B defines dzn output for rectangular arrays and ragged or deeper arrays. The existing data model defines the complete canonical JSON used in this chapter's examples.