Author diagrams

See every problem at once

diagnoseSchematic reports every problem in a document in one call, each with its line, its column range, and a stable code.

sheet
9 / 18
rev
v1.0
sections
3
compiled
3

01 / ReportEvery problem, in source order

compileSchematic stops at the first problem, because a compiler that has found one mistake cannot draw the document. An editor wants something else: every mistake, at once, each pointing at the exact characters to underline. That is diagnoseSchematic.

import { diagnoseSchematic, parseSchematicFence } from '@schemd/core';

const fence = parseSchematicFence('schemd bounds="760x420" title="Filter"')!;
const diagnostics = diagnoseSchematic(source, fence);
// [{ code, message, line, column, endColumn, severity }, …]

It throws only for arguments that are not a document. For anything wrong inside the document it returns a list, in source order, and an empty list means the document compiles. Give it this:

source:VIN "V_{in}" at (90, 170) #blue [type=voltage-dc]
resistor:R1 "4.7 k\Omega" at (300, 170) #amber [tolerance=1%]
capacitor:C1 "100 nF" below R2 by 80 #cyan [orientation=down]

and both problems come back together, each with the columns of the token at fault:

2:41–62  option     Option tolerance is not supported.
3:29–31  placement  C1 is placed relative to R2, which the document never declares.
                    Declare R2 anywhere in the document, or place C1 with at (x, y).

Columns are one-based and endColumn is exclusive, so line.slice(column - 1, endColumn - 1) is exactly the offending text — [tolerance=1%] and R2 here. Every message says how to fix what it reports, not only what is wrong.

source:VIN "V_{in}" at (90, 170) #blue [type=voltage-dc]
resistor:R1 "4.7 k\Omega" at (300, 170) #amber
capacitor:C1 "100 nF" below R1 by 80 #cyan [orientation=down]
ground:G "0 V" below C1 by 20 aligned-x with C1 #slate

VIN.positive -> R1.in #blue [ortho]
R1.out -> C1.in #amber [ortho]
C1.out -> G.in #slate [ortho]
VIN.negative -> G.in #slate [ortho]
compiled by @schemd/core → shown in the rail

02 / CodesA stable code for every kind of problem

Each diagnostic carries a code a host can switch on without parsing prose. SCHEMATIC_DIAGNOSTIC_CODES lists them, in the order the compiler meets them:

Code Raised by
syntax a line that is not a declaration, a connection, or block syntax
option an unknown, duplicated, or ill-typed option for the kind we named
limit a document past one of its resource budgets
reference a connection to a part or terminal that does not exist
topology conflicting nets at one terminal, or a bus width that does not match
placement a relation to nothing, a cycle, a negative distance, or too long a chain
geometry a part outside the fence, or bodies that overlap
routing a wire the router could not place, with what to move

Two rules keep the list honest. A declaration that fails does not cascade. Its id is set aside, so a mistyped kind on one line does not reappear as "unknown part" on every line that wires to it. Geometry and routing run only on a document whose declarations are sound, because a drawing of a document with a broken declaration would be a drawing of something we did not write. References are a different matter. A terminal's name depends on its part's kind, never on where the part sits, so since 1.0.2 a broken relative placement no longer hides a mistyped terminal elsewhere: every connection is still checked.

port:EN "EN" at (100, 120) #blue
nand:G1 "NAND" right-of EN by 160 #purple [inputs=2]
port:RDY "RDY" right-of G1 by 160 #emerald
clock:CK "CLK" below G1 by 110 aligned-x with G1 #amber

EN.out -> G1.in1 #blue [ortho digital]
CK.out -> G1.in2 #amber [ortho digital]
G1.out1 -> RDY.in #emerald [ortho digital]
compiled by @schemd/core → shown in the rail

03 / EditorsUnderline it where it was typed

A thrown error carries the same fields. SchematicSyntaxError has line, column, endColumn and code, so a host that compiles directly can underline the one problem it was given without calling the diagnostics API at all:

try {
	compileSchematic(source, fence);
} catch (error) {
	if (error instanceof SchematicSyntaxError) {
		underline(error.line, error.column, error.endColumn, error.message);
	}
}

A line rewritten by a sub-circuit parameter carries no column, since its columns would point at text the author never wrote; its line still points at the declaration.

The playground does exactly this as we type: it underlines every reported range, lists the problems beside the source, and keeps the last drawing that compiled on screen, dimmed, until the document is sound again.

port:SRC "S" at (100, 150) #violet
resistor:RS "220 \Omega" right-of SRC by 150 #amber
diode:DL "on" right-of RS by 150 #emerald [type=led]

SRC.out -> RS.in #violet [ortho]
RS.out -> DL.anode #amber [ortho]
compiled by @schemd/core → shown in the rail