From 1acea4ac0e0edafa0a9fcf2c08bd21c2d4eb53a0 Mon Sep 17 00:00:00 2001 From: xiaopingfeng Date: Wed, 12 Aug 2026 21:59:19 +0800 Subject: [PATCH] fix: parse a class name even when it has no description processClass only set classObj.name when nodes[1] AND nodes[2] were both string atoms, treating nodes[2] as a required "description". But a class with no description -- nodes[2] is the first nested list (e.g. `(clearance_class ...)`), not a string -- is valid DSN, see the `default` class in tests/assets/testkicadproject/freeroutingTraceAdded.dsn. The class's name was silently dropped whenever it had no description, even though name and description are independent fields. Added tests/repros/repro22-class-name-without-description.test.ts. Confirmed it fails without the fix (name undefined instead of "default") and passes with it. Full suite: 56 pass / 1 pre-existing skip / 0 fail. /claim #54 --- .../parse-dsn-to-dsn-json.ts | 9 ++-- ...o22-class-name-without-description.test.ts | 43 +++++++++++++++++++ 2 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 tests/repros/repro22-class-name-without-description.test.ts diff --git a/lib/dsn-pcb/dsn-json-to-circuit-json/parse-dsn-to-dsn-json.ts b/lib/dsn-pcb/dsn-json-to-circuit-json/parse-dsn-to-dsn-json.ts index 6ca0c7b2..aa2a75ab 100644 --- a/lib/dsn-pcb/dsn-json-to-circuit-json/parse-dsn-to-dsn-json.ts +++ b/lib/dsn-pcb/dsn-json-to-circuit-json/parse-dsn-to-dsn-json.ts @@ -860,13 +860,10 @@ function processNet(nodes: ASTNode[]): Net { function processClass(nodes: ASTNode[]): Class { const classObj: Partial = {} - if ( - nodes[1].type === "Atom" && - typeof nodes[1].value === "string" && - nodes[2].type === "Atom" && - typeof nodes[2].value === "string" - ) { + if (nodes[1].type === "Atom" && typeof nodes[1].value === "string") { classObj.name = nodes[1].value + } + if (nodes[2]?.type === "Atom" && typeof nodes[2].value === "string") { classObj.description = nodes[2].value } diff --git a/tests/repros/repro22-class-name-without-description.test.ts b/tests/repros/repro22-class-name-without-description.test.ts new file mode 100644 index 00000000..f99a8e65 --- /dev/null +++ b/tests/repros/repro22-class-name-without-description.test.ts @@ -0,0 +1,43 @@ +import { expect, test } from "bun:test" +import { parseDsnToDsnJson } from "lib" + +/** + * Part of https://github.com/tscircuit/dsn-converter/issues/54 + * + * processClass only set `classObj.name` when nodes[1] AND nodes[2] were both + * string atoms, treating nodes[2] as a required "description". But a class + * with no description (nodes[2] is the first nested list, e.g. + * `(clearance_class ...)`, not a string) is valid DSN -- see the `default` + * class in tests/assets/testkicadproject/freeroutingTraceAdded.dsn. The + * class's name was silently dropped whenever it had no description. + */ +test("a class with no description still gets its name parsed", () => { + const dsn = `(pcb "test.dsn" + (parser + (string_quote ") + (space_in_quoted_tokens on) + (host_cad "test") + (host_version "1") + ) + (resolution um 10) + (unit um) + (structure + (layer Top (type signal) (property (index 0))) + (boundary (path pcb 0 0 0 0 100 0 100 100 0 100 0 0)) + (via "") + (rule (width 200)) + ) + (placement) + (library) + (network + (class default + (clearance_class default) + (rule (width 200)) + ) + ) + (wiring) +)` + + const dsnJson = parseDsnToDsnJson(dsn) as any + expect(dsnJson.network.classes[0].name).toBe("default") +})