Skip to main content

SYN — syntax and composition

The SYN entries in the diagnostic ledger are listed below.

CodeSeverityWhat it says
SYN01errorA syntax or composition error

SYN01 is not an individual code. It is a copy, gathered into one, of the exception the parser threw. Since the file never became a model, not one semantic check ran — with even a single syntax error, the result of check is exactly "one SYN01".

It appears only under --json​

grid X 0 3600
grid Y 0 4000
level L1 0
space /L1/a room X1..X9 Y1..Y2
koyu check bad.muro --json
[
 {
  "code": "SYN01",
  "severity": "error",
  "message": "Undefined grid line name: X9",
  "line": 4,
  "file": "<absolute path>/bad.muro"
 }
]

A check without --json, and every other subcommand, print the exception as-is and exit 1.

koyu check bad.muro
✖ <absolute path>/bad.muro:line 4: Undefined grid line name: X9

The single SYN01 exists only so that valid JSON can still be returned. A machine running check --json should not have syntax errors come back in a different shape from everything else.

Common bodies and their fixes​

Grids and levels​

BodyCauseFix
Undefined grid line name: X1grid X has not been written yetWrite grid X / grid Y before any line that uses a grid line. grid and level cannot be forward-referenced (a boundary can)
Undefined grid line name: X9That grid line exceeds the number in the gridWith grid X 0 3600 7200 only X1–X3 exist
grid coordinates are written in ascending orderThe coordinates descendReorder them ascending
grid X is declared once (in the base layer when composing)Several layers carry grid XConsolidate into the base layer (the entry)
Undeclared level: level:L9What level: points at does not existDeclare level L9 …, or fix the spelling
Duplicate level: L2The same level name declared twice (including a clash with a range declaration)Delete one
The level height (z) is not a number:No z was written, as in level L1level L1 0
A level range requires pitch: (the storey height in mm): L1..L3A range declaration with no storey heightlevel L1..L3 0 pitch:3000
level carries spec:, which is not in the ledger (level reads h / slab / pitch / underground)An unknown key on levellevel reads only those four. Put other information on a space or a zone
The range includes an undeclared level (declare level first): L1..L2A stack or column naming an undeclared levelWrite the level first

Spaces, boundaries, zones​

BodyCauseFix
A region is given as two ranges, X?..X? and Y?..Y?A region needs two ranges, one on X and one on Y, and only one is writtenWrite the other axis: space /L1/a room X1..X2 Y1..Y2. A forgotten type is not the cause — the type is optional
space /L1/a requires a type (a word from the vocabulary)Neither type nor regionAdd the type
Duplicate space path: /L1/a (first seen …)Two spaces at the same pathThe path is the identity. Change one of them
boundary takes the form boundary /pathA /pathB [attributes...]The second path is missingA boundary is a relation joining two spaces
zone takes the form zone /path [attributes...]No path
Unknown keyword: wallThat keyword does not existA wall is a relation, not a thing. Use boundary
The attribute h is written as a number: 24OOA numeric attribute on level does not read as a numberWrite a number with no unit

Indentation​

Openings, seg and line are written indented under a boundary; area and band members under a space.

BodyCauseFix
Unknown keyword: doorThe door line is not indentedPut whitespace at the head of the line so it is subordinate to its boundary
door is written indented directly under boundaryIt is indented, but the preceding unindented line is not a boundaryMake the parent a boundary (the case above had it under a space)
area is written indented directly under spaceThe indented area's parent is not a space
Only door / window / seg / line / area / space (a band member) may sit on an indented line: noteSome other word on an indented lineOnly those six may be indented
Only + (add) / - (remove) / = (replace) may sit directly under over: doorSomething other than a set edit under overWrite it as - door D1
One boundary carries one line: /L1/a | /L1/bA second line under one boundarySplit it into two boundaries

Openings and attributes​

BodyCauseFix
door requires a width w:(mm) (the asset may supply it)The opening has no w:Write door w:800, or reference an asset that carries a width (door SD1)
Duplicate attribute key: nameThe same key twice on one lineConsolidate into one. The later one is never silently taken
Unclosed quoteAn odd number of "Close it
An attribute is written key:value: 居室A token with no : is in an attribute positionMake it key:value. To include whitespace in a value, wrap it in "…"

Versions and composition​

BodyCauseFix
Unsupported koyu version: 0.9 (this tool supports 0.1, 0.2, 0.3, 0.4, 0.5, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6)A version that does not existUse one the parser accepts
The koyu version is declared only in the base layer (the entry)A version in an imported layerMove it to the base layer
The koyu version is declared once (already 1.0)The version written twiceDelete one
Cannot read file: ./assets.muroThe import's relative path is wrongA path is resolved relative to the file it is written in

How far misspellings are caught​

A misspelled attribute key is caught. nmae:居室A is not carried through as a free attribute; it is an ATT03 error. A key not in the ledger cannot be written without a dot-separated namespace (acme.nmae).

A misspelled type (the second positional) is not caught. The type is an open vocabulary, so writing bedroom as bedrom is not an error.

grid X 0 3600
grid Y 0 4000
level L1 0 h:2400 slab:150
space /L1/a bedrom X1..X2 Y1..Y2
space /out outside:1
boundary /L1/a /out

That file is green. A type is used for little more than an aggregation axis and the paleness of a drawing, and core never reads that position at all, so there is nothing to close.

Facts of composition are not kept in the type position. Outside, void and daylight all live in a declaration — outside:1, void:1, daylight:1 are keys in the ledger, so one character off stops at ATT03. outsid:1 is an error; nothing quietly stops being outside. It used to be otherwise: exterior sat in the type position, and exteriorr doubled the gross floor area while passing green.

The validation face does read a few type words when it judges. That face does not freeze, and its spellings are not guarded (scope).

The real work starts once syntax passes​

SYN01 disappearing means only that the file became a model. From there the remaining semantic checks run.

koyu check house.muro --strict

--strict makes warnings exit 1 as well. That is the one to put in a CI gate.