kicadts 0.0.1

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.
Files changed (188) hide show
  1. package/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc +111 -0
  2. package/.vscode/settings.json +16 -0
  3. package/AGENTS.md +30 -0
  4. package/README.md +206 -0
  5. package/biome.json +93 -0
  6. package/bun.lock +48 -0
  7. package/bunfig.toml +5 -0
  8. package/lib/index.ts +1 -0
  9. package/lib/sexpr/base-classes/SxClass.ts +164 -0
  10. package/lib/sexpr/base-classes/SxPrimitiveBoolean.ts +35 -0
  11. package/lib/sexpr/base-classes/SxPrimitiveNumber.ts +26 -0
  12. package/lib/sexpr/base-classes/SxPrimitiveString.ts +26 -0
  13. package/lib/sexpr/classes/At.ts +38 -0
  14. package/lib/sexpr/classes/Bus.ts +83 -0
  15. package/lib/sexpr/classes/BusEntry.ts +142 -0
  16. package/lib/sexpr/classes/Color.ts +29 -0
  17. package/lib/sexpr/classes/Dnp.ts +8 -0
  18. package/lib/sexpr/classes/EmbeddedFonts.ts +70 -0
  19. package/lib/sexpr/classes/ExcludeFromSim.ts +8 -0
  20. package/lib/sexpr/classes/FieldsAutoplaced.ts +8 -0
  21. package/lib/sexpr/classes/Footprint.ts +719 -0
  22. package/lib/sexpr/classes/FootprintAttr.ts +102 -0
  23. package/lib/sexpr/classes/FootprintAutoplaceCost180.ts +9 -0
  24. package/lib/sexpr/classes/FootprintAutoplaceCost90.ts +9 -0
  25. package/lib/sexpr/classes/FootprintClearance.ts +9 -0
  26. package/lib/sexpr/classes/FootprintDescr.ts +44 -0
  27. package/lib/sexpr/classes/FootprintLocked.ts +32 -0
  28. package/lib/sexpr/classes/FootprintModel.ts +145 -0
  29. package/lib/sexpr/classes/FootprintNetTiePadGroups.ts +50 -0
  30. package/lib/sexpr/classes/FootprintPad.ts +705 -0
  31. package/lib/sexpr/classes/FootprintPath.ts +44 -0
  32. package/lib/sexpr/classes/FootprintPlaced.ts +32 -0
  33. package/lib/sexpr/classes/FootprintPrivateLayers.ts +56 -0
  34. package/lib/sexpr/classes/FootprintSheetfile.ts +44 -0
  35. package/lib/sexpr/classes/FootprintSheetname.ts +44 -0
  36. package/lib/sexpr/classes/FootprintSolderMaskMargin.ts +9 -0
  37. package/lib/sexpr/classes/FootprintSolderPasteMargin.ts +9 -0
  38. package/lib/sexpr/classes/FootprintSolderPasteRatio.ts +9 -0
  39. package/lib/sexpr/classes/FootprintTags.ts +44 -0
  40. package/lib/sexpr/classes/FootprintTedit.ts +21 -0
  41. package/lib/sexpr/classes/FootprintThermalGap.ts +9 -0
  42. package/lib/sexpr/classes/FootprintThermalWidth.ts +9 -0
  43. package/lib/sexpr/classes/FootprintZoneConnect.ts +9 -0
  44. package/lib/sexpr/classes/FpArc.ts +289 -0
  45. package/lib/sexpr/classes/FpCircle.ts +293 -0
  46. package/lib/sexpr/classes/FpLine.ts +288 -0
  47. package/lib/sexpr/classes/FpPoly.ts +266 -0
  48. package/lib/sexpr/classes/FpPolyFill.ts +48 -0
  49. package/lib/sexpr/classes/FpPolyLocked.ts +40 -0
  50. package/lib/sexpr/classes/FpRect.ts +293 -0
  51. package/lib/sexpr/classes/FpText.ts +341 -0
  52. package/lib/sexpr/classes/FpTextBox.ts +412 -0
  53. package/lib/sexpr/classes/GrLine.ts +245 -0
  54. package/lib/sexpr/classes/GrLineAngle.ts +32 -0
  55. package/lib/sexpr/classes/GrLineEnd.ts +61 -0
  56. package/lib/sexpr/classes/GrLineLocked.ts +40 -0
  57. package/lib/sexpr/classes/GrLineStart.ts +61 -0
  58. package/lib/sexpr/classes/GrText.ts +202 -0
  59. package/lib/sexpr/classes/Image.ts +256 -0
  60. package/lib/sexpr/classes/InBom.ts +8 -0
  61. package/lib/sexpr/classes/Junction.ts +134 -0
  62. package/lib/sexpr/classes/KicadPcb.ts +313 -0
  63. package/lib/sexpr/classes/KicadSch.ts +303 -0
  64. package/lib/sexpr/classes/KicadSchGenerator.ts +32 -0
  65. package/lib/sexpr/classes/KicadSchGeneratorVersion.ts +30 -0
  66. package/lib/sexpr/classes/KicadSchVersion.ts +22 -0
  67. package/lib/sexpr/classes/Label.ts +136 -0
  68. package/lib/sexpr/classes/Layer.ts +51 -0
  69. package/lib/sexpr/classes/Layers.ts +47 -0
  70. package/lib/sexpr/classes/LibSymbols.ts +61 -0
  71. package/lib/sexpr/classes/NoConnect.ts +73 -0
  72. package/lib/sexpr/classes/OnBoard.ts +8 -0
  73. package/lib/sexpr/classes/PadChamfer.ts +50 -0
  74. package/lib/sexpr/classes/PadChamferRatio.ts +9 -0
  75. package/lib/sexpr/classes/PadClearance.ts +9 -0
  76. package/lib/sexpr/classes/PadDieLength.ts +9 -0
  77. package/lib/sexpr/classes/PadDrill.ts +145 -0
  78. package/lib/sexpr/classes/PadDrillOffset.ts +54 -0
  79. package/lib/sexpr/classes/PadLayers.ts +59 -0
  80. package/lib/sexpr/classes/PadNet.ts +56 -0
  81. package/lib/sexpr/classes/PadOptions.ts +182 -0
  82. package/lib/sexpr/classes/PadPinFunction.ts +9 -0
  83. package/lib/sexpr/classes/PadPinType.ts +9 -0
  84. package/lib/sexpr/classes/PadPrimitiveGrArc.ts +254 -0
  85. package/lib/sexpr/classes/PadPrimitiveGrCircle.ts +279 -0
  86. package/lib/sexpr/classes/PadPrimitiveGrLine.ts +126 -0
  87. package/lib/sexpr/classes/PadPrimitives.ts +289 -0
  88. package/lib/sexpr/classes/PadRectDelta.ts +57 -0
  89. package/lib/sexpr/classes/PadRoundrectRratio.ts +9 -0
  90. package/lib/sexpr/classes/PadSize.ts +54 -0
  91. package/lib/sexpr/classes/PadSolderMaskMargin.ts +9 -0
  92. package/lib/sexpr/classes/PadSolderPasteMargin.ts +9 -0
  93. package/lib/sexpr/classes/PadSolderPasteMarginRatio.ts +9 -0
  94. package/lib/sexpr/classes/PadTeardrops.ts +208 -0
  95. package/lib/sexpr/classes/PadThermalBridgeAngle.ts +9 -0
  96. package/lib/sexpr/classes/PadThermalGap.ts +9 -0
  97. package/lib/sexpr/classes/PadThermalWidth.ts +9 -0
  98. package/lib/sexpr/classes/PadZoneConnect.ts +9 -0
  99. package/lib/sexpr/classes/Paper.ts +119 -0
  100. package/lib/sexpr/classes/PcbGeneral.ts +75 -0
  101. package/lib/sexpr/classes/PcbGeneralLegacyTeardrops.ts +44 -0
  102. package/lib/sexpr/classes/PcbGeneralThickness.ts +9 -0
  103. package/lib/sexpr/classes/PcbGenerator.ts +16 -0
  104. package/lib/sexpr/classes/PcbGeneratorVersion.ts +16 -0
  105. package/lib/sexpr/classes/PcbLayerDefinition.ts +102 -0
  106. package/lib/sexpr/classes/PcbLayers.ts +34 -0
  107. package/lib/sexpr/classes/PcbNet.ts +56 -0
  108. package/lib/sexpr/classes/PcbVersion.ts +9 -0
  109. package/lib/sexpr/classes/Property.ts +246 -0
  110. package/lib/sexpr/classes/PropertyHide.ts +9 -0
  111. package/lib/sexpr/classes/PropertyUnlocked.ts +9 -0
  112. package/lib/sexpr/classes/Pts.ts +65 -0
  113. package/lib/sexpr/classes/RenderCache.ts +221 -0
  114. package/lib/sexpr/classes/SchematicText.ts +141 -0
  115. package/lib/sexpr/classes/Segment.ts +222 -0
  116. package/lib/sexpr/classes/SegmentEnd.ts +59 -0
  117. package/lib/sexpr/classes/SegmentLocked.ts +33 -0
  118. package/lib/sexpr/classes/SegmentNet.ts +62 -0
  119. package/lib/sexpr/classes/SegmentStart.ts +59 -0
  120. package/lib/sexpr/classes/Setup/PcbPlotParams.ts +729 -0
  121. package/lib/sexpr/classes/Setup/PcbPlotParamsBase.ts +9 -0
  122. package/lib/sexpr/classes/Setup/PcbPlotParamsNumericProperties.ts +105 -0
  123. package/lib/sexpr/classes/Setup/PcbPlotParamsStringPropertiesA.ts +104 -0
  124. package/lib/sexpr/classes/Setup/PcbPlotParamsStringPropertiesB.ts +105 -0
  125. package/lib/sexpr/classes/Setup/Setup.ts +573 -0
  126. package/lib/sexpr/classes/Setup/SetupPropertyTypes.ts +119 -0
  127. package/lib/sexpr/classes/Setup/Stackup.ts +140 -0
  128. package/lib/sexpr/classes/Setup/StackupLayer.ts +233 -0
  129. package/lib/sexpr/classes/Setup/StackupLayerProperties.ts +78 -0
  130. package/lib/sexpr/classes/Setup/StackupProperties.ts +41 -0
  131. package/lib/sexpr/classes/Setup/base.ts +167 -0
  132. package/lib/sexpr/classes/Setup/index.ts +14 -0
  133. package/lib/sexpr/classes/Setup/setupMultiValueProperties.ts +54 -0
  134. package/lib/sexpr/classes/Setup/setupNumericProperties.ts +151 -0
  135. package/lib/sexpr/classes/Setup/setupPropertyHandlers.ts +90 -0
  136. package/lib/sexpr/classes/Setup/setupStringProperties.ts +75 -0
  137. package/lib/sexpr/classes/Sheet.ts +205 -0
  138. package/lib/sexpr/classes/SheetFill.ts +44 -0
  139. package/lib/sexpr/classes/SheetInstances.ts +168 -0
  140. package/lib/sexpr/classes/SheetInstancesRoot.ts +165 -0
  141. package/lib/sexpr/classes/SheetPin.ts +122 -0
  142. package/lib/sexpr/classes/SheetProperty.ts +115 -0
  143. package/lib/sexpr/classes/SheetSize.ts +44 -0
  144. package/lib/sexpr/classes/Stroke.ts +58 -0
  145. package/lib/sexpr/classes/StrokeType.ts +34 -0
  146. package/lib/sexpr/classes/Symbol.ts +1541 -0
  147. package/lib/sexpr/classes/TextEffects.ts +444 -0
  148. package/lib/sexpr/classes/TitleBlock.ts +352 -0
  149. package/lib/sexpr/classes/Unit.ts +28 -0
  150. package/lib/sexpr/classes/Uuid.ts +8 -0
  151. package/lib/sexpr/classes/Via.ts +328 -0
  152. package/lib/sexpr/classes/ViaNet.ts +59 -0
  153. package/lib/sexpr/classes/Width.ts +8 -0
  154. package/lib/sexpr/classes/Wire.ts +91 -0
  155. package/lib/sexpr/classes/Xy.ts +35 -0
  156. package/lib/sexpr/classes/Zone.ts +41 -0
  157. package/lib/sexpr/index.ts +130 -0
  158. package/lib/sexpr/parseKicadSexpr.ts +5 -0
  159. package/lib/sexpr/parseToPrimitiveSExpr.ts +240 -0
  160. package/lib/sexpr/utils/indentLines.ts +3 -0
  161. package/lib/sexpr/utils/parseYesNo.ts +12 -0
  162. package/lib/sexpr/utils/quoteSExprString.ts +8 -0
  163. package/lib/sexpr/utils/strokeFromArgs.ts +19 -0
  164. package/lib/sexpr/utils/toNumberValue.ts +13 -0
  165. package/lib/sexpr/utils/toStringValue.ts +10 -0
  166. package/package.json +26 -0
  167. package/scripts/download-references.ts +66 -0
  168. package/tests/fixtures/expectEqualPrimitiveSExpr.ts +200 -0
  169. package/tests/sexpr/KicadPcbDemos.test.ts +48 -0
  170. package/tests/sexpr/KicadSchDemos.test.ts +49 -0
  171. package/tests/sexpr/classes/Footprint.test.ts +277 -0
  172. package/tests/sexpr/classes/FootprintPad.test.ts +71 -0
  173. package/tests/sexpr/classes/FpArc.test.ts +45 -0
  174. package/tests/sexpr/classes/FpCircle.test.ts +39 -0
  175. package/tests/sexpr/classes/FpPoly.test.ts +43 -0
  176. package/tests/sexpr/classes/FpRect.test.ts +40 -0
  177. package/tests/sexpr/classes/FpTextBox.test.ts +84 -0
  178. package/tests/sexpr/classes/Image.test.ts +50 -0
  179. package/tests/sexpr/classes/KicadSch.test.ts +97 -0
  180. package/tests/sexpr/classes/Paper.test.ts +30 -0
  181. package/tests/sexpr/classes/Property.test.ts +48 -0
  182. package/tests/sexpr/classes/Setup.test.ts +189 -0
  183. package/tests/sexpr/classes/Sheet.test.ts +107 -0
  184. package/tests/sexpr/classes/Stroke.test.ts +15 -0
  185. package/tests/sexpr/classes/Symbol.test.ts +96 -0
  186. package/tests/sexpr/classes/TextEffects.test.ts +56 -0
  187. package/tests/sexpr/classes/TitleBlock.test.ts +40 -0
  188. package/tsconfig.json +35 -0
@@ -0,0 +1,59 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
+ import { quoteSExprString } from "../utils/quoteSExprString"
4
+ import { toNumberValue } from "../utils/toNumberValue"
5
+ import { toStringValue } from "../utils/toStringValue"
6
+
7
+ export class ViaNet extends SxClass {
8
+ static override token = "net"
9
+ static override parentToken = "via"
10
+ token = "net"
11
+
12
+ private _id: number
13
+ private _name?: string
14
+
15
+ constructor(id: number, name?: string) {
16
+ super()
17
+ this._id = id
18
+ this._name = name
19
+ }
20
+
21
+ static override fromSexprPrimitives(
22
+ primitiveSexprs: PrimitiveSExpr[],
23
+ ): ViaNet {
24
+ const id = toNumberValue(primitiveSexprs[0])
25
+ if (id === undefined) {
26
+ throw new Error("via net requires a numeric id")
27
+ }
28
+ const name = primitiveSexprs.length > 1 ? toStringValue(primitiveSexprs[1]) : undefined
29
+ return new ViaNet(id, name)
30
+ }
31
+
32
+ get id(): number {
33
+ return this._id
34
+ }
35
+
36
+ set id(value: number) {
37
+ this._id = value
38
+ }
39
+
40
+ get name(): string | undefined {
41
+ return this._name
42
+ }
43
+
44
+ set name(value: string | undefined) {
45
+ this._name = value
46
+ }
47
+
48
+ override getChildren(): SxClass[] {
49
+ return []
50
+ }
51
+
52
+ override getString(): string {
53
+ if (this._name !== undefined) {
54
+ return `(net ${this._id} ${quoteSExprString(this._name)})`
55
+ }
56
+ return `(net ${this._id})`
57
+ }
58
+ }
59
+ SxClass.register(ViaNet)
@@ -0,0 +1,8 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import { SxPrimitiveNumber } from "../base-classes/SxPrimitiveNumber"
3
+
4
+ export class Width extends SxPrimitiveNumber {
5
+ static override token = "width"
6
+ token = "width"
7
+ }
8
+ SxClass.register(Width)
@@ -0,0 +1,91 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
+ import { Pts } from "./Pts"
4
+ import { Stroke } from "./Stroke"
5
+ import { Uuid } from "./Uuid"
6
+
7
+ const SUPPORTED_TOKENS = new Set(["pts", "stroke", "uuid"])
8
+
9
+ export class Wire extends SxClass {
10
+ static override token = "wire"
11
+ static override parentToken = "kicad_sch"
12
+ override token = "wire"
13
+
14
+ private _sxPts?: Pts
15
+ private _sxStroke?: Stroke
16
+ private _sxUuid?: Uuid
17
+
18
+ static override fromSexprPrimitives(
19
+ primitiveSexprs: PrimitiveSExpr[],
20
+ ): Wire {
21
+ const wire = new Wire()
22
+
23
+ const { propertyMap, arrayPropertyMap } =
24
+ SxClass.parsePrimitivesToClassProperties(primitiveSexprs, this.token)
25
+
26
+ for (const [token, entries] of Object.entries(arrayPropertyMap)) {
27
+ if (!SUPPORTED_TOKENS.has(token)) {
28
+ throw new Error(
29
+ `Unsupported child tokens inside wire expression: ${token}`,
30
+ )
31
+ }
32
+ if (entries.length > 1) {
33
+ throw new Error(`wire does not support repeated child tokens: ${token}`)
34
+ }
35
+ }
36
+
37
+ const unsupportedTokens = Object.keys(propertyMap).filter(
38
+ (token) => !SUPPORTED_TOKENS.has(token),
39
+ )
40
+ if (unsupportedTokens.length > 0) {
41
+ throw new Error(
42
+ `Unsupported child tokens inside wire expression: ${unsupportedTokens.join(", ")}`,
43
+ )
44
+ }
45
+
46
+ wire._sxPts =
47
+ (arrayPropertyMap.pts?.[0] as Pts | undefined) ??
48
+ (propertyMap.pts as Pts | undefined)
49
+ wire._sxStroke = propertyMap.stroke as Stroke | undefined
50
+ wire._sxUuid = propertyMap.uuid as Uuid | undefined
51
+
52
+ return wire
53
+ }
54
+
55
+ get points(): Pts | undefined {
56
+ return this._sxPts
57
+ }
58
+
59
+ set points(value: Pts | undefined) {
60
+ this._sxPts = value
61
+ }
62
+
63
+ get stroke(): Stroke | undefined {
64
+ return this._sxStroke
65
+ }
66
+
67
+ set stroke(value: Stroke | undefined) {
68
+ this._sxStroke = value
69
+ }
70
+
71
+ get uuid(): Uuid | undefined {
72
+ return this._sxUuid
73
+ }
74
+
75
+ set uuid(value: Uuid | string | undefined) {
76
+ if (value === undefined) {
77
+ this._sxUuid = undefined
78
+ return
79
+ }
80
+ this._sxUuid = value instanceof Uuid ? value : new Uuid(value)
81
+ }
82
+
83
+ override getChildren(): SxClass[] {
84
+ const children: SxClass[] = []
85
+ if (this._sxPts) children.push(this._sxPts)
86
+ if (this._sxStroke) children.push(this._sxStroke)
87
+ if (this._sxUuid) children.push(this._sxUuid)
88
+ return children
89
+ }
90
+ }
91
+ SxClass.register(Wire)
@@ -0,0 +1,35 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
+ import { toNumberValue } from "../utils/toNumberValue"
4
+
5
+ export class Xy extends SxClass {
6
+ static override token = "xy"
7
+ token = "xy"
8
+
9
+ x: number
10
+ y: number
11
+
12
+ constructor(x: number, y: number) {
13
+ super()
14
+ this.x = x
15
+ this.y = y
16
+ }
17
+
18
+ static override fromSexprPrimitives(
19
+ primitiveSexprs: PrimitiveSExpr[],
20
+ ): Xy {
21
+ const [rawX, rawY] = primitiveSexprs
22
+ const x = toNumberValue(rawX) ?? 0
23
+ const y = toNumberValue(rawY) ?? 0
24
+ return new Xy(x, y)
25
+ }
26
+
27
+ override getChildren(): SxClass[] {
28
+ return []
29
+ }
30
+
31
+ override getString(): string {
32
+ return `(xy ${this.x} ${this.y})`
33
+ }
34
+ }
35
+ SxClass.register(Xy)
@@ -0,0 +1,41 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import { printSExpr, type PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
+
4
+ export class Zone extends SxClass {
5
+ static override token = "zone"
6
+ token = "zone"
7
+
8
+ private _rawChildren: PrimitiveSExpr[] = []
9
+
10
+ static override fromSexprPrimitives(
11
+ primitiveSexprs: PrimitiveSExpr[],
12
+ ): Zone {
13
+ const zone = new Zone()
14
+ zone._rawChildren = [...primitiveSexprs]
15
+ return zone
16
+ }
17
+
18
+ get rawChildren(): PrimitiveSExpr[] {
19
+ return [...this._rawChildren]
20
+ }
21
+
22
+ set rawChildren(children: PrimitiveSExpr[]) {
23
+ this._rawChildren = [...children]
24
+ }
25
+
26
+ override getChildren(): SxClass[] {
27
+ return []
28
+ }
29
+
30
+ override getString(): string {
31
+ const lines = ["(zone"]
32
+
33
+ for (const arg of this._rawChildren) {
34
+ lines.push(` ${printSExpr(arg)}`)
35
+ }
36
+
37
+ lines.push(")")
38
+ return lines.join("\n")
39
+ }
40
+ }
41
+ SxClass.register(Zone)
@@ -0,0 +1,130 @@
1
+ export * from "./base-classes/SxClass"
2
+ export * from "./classes/Stroke"
3
+ export * from "./classes/Width"
4
+ export * from "./classes/Unit"
5
+ export * from "./classes/InBom"
6
+ export * from "./classes/OnBoard"
7
+ export * from "./classes/StrokeType"
8
+ export * from "./classes/Color"
9
+ export * from "./classes/Pts"
10
+ export * from "./classes/At"
11
+ export * from "./classes/Xy"
12
+ export * from "./classes/TextEffects"
13
+ export * from "./classes/TitleBlock"
14
+ export * from "./classes/Paper"
15
+ export * from "./classes/Image"
16
+ export * from "./classes/RenderCache"
17
+ export * from "./classes/Uuid"
18
+ export * from "./classes/KicadSchGenerator"
19
+ export * from "./classes/KicadSchVersion"
20
+ export * from "./classes/KicadSchGeneratorVersion"
21
+ export * from "./classes/LibSymbols"
22
+ export * from "./classes/Wire"
23
+ export * from "./classes/Bus"
24
+ export * from "./classes/Junction"
25
+ export * from "./classes/NoConnect"
26
+ export * from "./classes/BusEntry"
27
+ export * from "./classes/Label"
28
+ export * from "./classes/SchematicText"
29
+ export * from "./classes/KicadSch"
30
+ export * from "./classes/Property"
31
+ export * from "./classes/PropertyUnlocked"
32
+ export * from "./classes/PropertyHide"
33
+ export * from "./classes/Footprint"
34
+ export * from "./classes/FootprintLocked"
35
+ export * from "./classes/FootprintPlaced"
36
+ export * from "./classes/FootprintTedit"
37
+ export * from "./classes/FootprintDescr"
38
+ export * from "./classes/FootprintTags"
39
+ export * from "./classes/FootprintSheetname"
40
+ export * from "./classes/FootprintSheetfile"
41
+ export * from "./classes/FootprintPath"
42
+ export * from "./classes/FootprintAutoplaceCost90"
43
+ export * from "./classes/FootprintAutoplaceCost180"
44
+ export * from "./classes/FootprintSolderMaskMargin"
45
+ export * from "./classes/FootprintSolderPasteMargin"
46
+ export * from "./classes/FootprintSolderPasteRatio"
47
+ export * from "./classes/FootprintClearance"
48
+ export * from "./classes/FootprintZoneConnect"
49
+ export * from "./classes/FootprintThermalWidth"
50
+ export * from "./classes/FootprintThermalGap"
51
+ export * from "./classes/FootprintAttr"
52
+ export * from "./classes/FootprintPrivateLayers"
53
+ export * from "./classes/FootprintNetTiePadGroups"
54
+ export * from "./classes/GrLine"
55
+ export * from "./classes/GrLineStart"
56
+ export * from "./classes/GrLineEnd"
57
+ export * from "./classes/GrLineAngle"
58
+ export * from "./classes/GrLineLocked"
59
+ export * from "./classes/GrText"
60
+ export * from "./classes/Segment"
61
+ export * from "./classes/SegmentStart"
62
+ export * from "./classes/SegmentEnd"
63
+ export * from "./classes/SegmentNet"
64
+ export * from "./classes/SegmentLocked"
65
+ export * from "./classes/Zone"
66
+ export * from "./classes/EmbeddedFonts"
67
+ export * from "./classes/Layers"
68
+ export * from "./classes/Layer"
69
+ export * from "./classes/FpText"
70
+ export * from "./classes/FpTextBox"
71
+ export * from "./classes/FpLine"
72
+ export * from "./classes/FpRect"
73
+ export * from "./classes/FpCircle"
74
+ export * from "./classes/FpArc"
75
+ export * from "./classes/FpPoly"
76
+ export * from "./classes/FpPolyFill"
77
+ export * from "./classes/FpPolyLocked"
78
+ export * from "./classes/FootprintPad"
79
+ export * from "./classes/FootprintModel"
80
+ export * from "./classes/PadSize"
81
+ export * from "./classes/PadDrill"
82
+ export * from "./classes/PadDrillOffset"
83
+ export * from "./classes/PadLayers"
84
+ export * from "./classes/PadRoundrectRratio"
85
+ export * from "./classes/PadRectDelta"
86
+ export * from "./classes/PadChamferRatio"
87
+ export * from "./classes/PadChamfer"
88
+ export * from "./classes/PadNet"
89
+ export * from "./classes/PadPinFunction"
90
+ export * from "./classes/PadPinType"
91
+ export * from "./classes/PadDieLength"
92
+ export * from "./classes/PadSolderMaskMargin"
93
+ export * from "./classes/PadSolderPasteMargin"
94
+ export * from "./classes/PadSolderPasteMarginRatio"
95
+ export * from "./classes/PadClearance"
96
+ export * from "./classes/PadZoneConnect"
97
+ export * from "./classes/PadThermalWidth"
98
+ export * from "./classes/PadThermalGap"
99
+ export * from "./classes/PadThermalBridgeAngle"
100
+ export * from "./classes/PadOptions"
101
+ export * from "./classes/PadPrimitives"
102
+ export * from "./classes/PadPrimitiveGrLine"
103
+ export * from "./classes/PadPrimitiveGrArc"
104
+ export * from "./classes/PadPrimitiveGrCircle"
105
+ export * from "./classes/PadTeardrops"
106
+ export * from "./classes/Symbol"
107
+ export * from "./classes/Sheet"
108
+ export * from "./classes/SheetPin"
109
+ export * from "./classes/SheetProperty"
110
+ export * from "./classes/SheetInstances"
111
+ export * from "./classes/SheetInstancesRoot"
112
+ export * from "./classes/SheetFill"
113
+ export * from "./classes/SheetSize"
114
+ export * from "./classes/FieldsAutoplaced"
115
+ export * from "./classes/ExcludeFromSim"
116
+ export * from "./classes/Dnp"
117
+ export * from "./classes/Setup"
118
+ export * from "./classes/PcbGeneral"
119
+ export * from "./classes/PcbGeneralThickness"
120
+ export * from "./classes/PcbGeneralLegacyTeardrops"
121
+ export * from "./classes/PcbLayers"
122
+ export * from "./classes/PcbLayerDefinition"
123
+ export * from "./classes/PcbNet"
124
+ export * from "./classes/PcbVersion"
125
+ export * from "./classes/PcbGenerator"
126
+ export * from "./classes/PcbGeneratorVersion"
127
+ export * from "./classes/KicadPcb"
128
+ export * from "./classes/Via"
129
+ export * from "./classes/ViaNet"
130
+ export * from "./parseKicadSexpr"
@@ -0,0 +1,5 @@
1
+ import { SxClass } from "./base-classes/SxClass"
2
+
3
+ export const parseKicadSexpr = (sexpr: string) => {
4
+ return SxClass.parse(sexpr)
5
+ }
@@ -0,0 +1,240 @@
1
+ // s_expr_parser.ts
2
+ export type PrimitiveSExpr =
3
+ | string // symbols and string literals (distinct via `type` guard if you want)
4
+ | number
5
+ | boolean
6
+ | null
7
+ | PrimitiveSExpr[] // lists
8
+
9
+ type Token =
10
+ | { type: "lparen" }
11
+ | { type: "rparen" }
12
+ | { type: "number"; value: number }
13
+ | { type: "string"; value: string }
14
+ | { type: "boolean"; value: boolean }
15
+ | { type: "nil" }
16
+ | { type: "symbol"; value: string }
17
+
18
+ export function tokenize(input: string): Token[] {
19
+ const tokens: Token[] = []
20
+ let i = 0
21
+
22
+ const isWhitespace = (ch: string) => /\s/.test(ch)
23
+ const isSymbolInitial = (ch: string) => /[^\s()"]/u.test(ch) // anything except ws, parens, quote
24
+ const peek = () => (i < input.length ? input[i]! : undefined)
25
+ const advance = () => {
26
+ if (i >= input.length) {
27
+ throw new SyntaxError("Unexpected end of input")
28
+ }
29
+ return input[i++]!
30
+ }
31
+
32
+ while (i < input.length) {
33
+ const ch = peek()
34
+ if (ch === undefined) {
35
+ break
36
+ }
37
+ const current: string = ch
38
+
39
+ // Skip whitespace
40
+ if (isWhitespace(current)) {
41
+ i++
42
+ continue
43
+ }
44
+
45
+ // Comments ;...<EOL>
46
+ if (current === ";") {
47
+ while (i < input.length && input[i] !== "\n") i++
48
+ continue
49
+ }
50
+
51
+ if (current === "(") {
52
+ tokens.push({ type: "lparen" })
53
+ i++
54
+ continue
55
+ }
56
+ if (current === ")") {
57
+ tokens.push({ type: "rparen" })
58
+ i++
59
+ continue
60
+ }
61
+
62
+ // String literal
63
+ if (current === '"') {
64
+ i++ // skip opening quote
65
+ let out = ""
66
+ while (i < input.length) {
67
+ const c = advance()
68
+ if (c === '"') break
69
+ if (c === "\\") {
70
+ if (i >= input.length)
71
+ throw new SyntaxError("Unterminated escape in string")
72
+ const e = advance()
73
+ switch (e) {
74
+ case "n":
75
+ out += "\n"
76
+ break
77
+ case "r":
78
+ out += "\r"
79
+ break
80
+ case "t":
81
+ out += "\t"
82
+ break
83
+ case '"':
84
+ out += '"'
85
+ break
86
+ case "\\":
87
+ out += "\\"
88
+ break
89
+ default:
90
+ out += e
91
+ break // unknown escapes: keep raw
92
+ }
93
+ } else {
94
+ out += c
95
+ }
96
+ }
97
+ if (input[i - 1] !== '"')
98
+ throw new SyntaxError("Unterminated string literal")
99
+ tokens.push({ type: "string", value: out })
100
+ continue
101
+ }
102
+
103
+ // Number or symbol
104
+ if (isSymbolInitial(current) || current === "-" || current === "+" || current === ".") {
105
+ // read a maximal token until delimiter
106
+ let start = i
107
+ while (i < input.length) {
108
+ const nextChar = input[i]!
109
+ if (
110
+ isWhitespace(nextChar) ||
111
+ nextChar === "(" ||
112
+ nextChar === ")" ||
113
+ nextChar === '"'
114
+ ) {
115
+ break
116
+ }
117
+ i++
118
+ }
119
+ const raw = input.slice(start, i)
120
+
121
+ // Booleans
122
+ if (raw === "#t") {
123
+ tokens.push({ type: "boolean", value: true })
124
+ continue
125
+ }
126
+ if (raw === "#f") {
127
+ tokens.push({ type: "boolean", value: false })
128
+ continue
129
+ }
130
+
131
+ // nil
132
+ if (raw === "nil") {
133
+ tokens.push({ type: "nil" })
134
+ continue
135
+ }
136
+
137
+ // Numbers (int/float), allow leading +/-, decimals, exponent
138
+ if (/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/u.test(raw)) {
139
+ tokens.push({ type: "number", value: Number(raw) })
140
+ } else {
141
+ tokens.push({ type: "symbol", value: raw })
142
+ }
143
+ continue
144
+ }
145
+
146
+ throw new SyntaxError(`Unexpected character: ${JSON.stringify(ch)} at ${i}`)
147
+ }
148
+
149
+ return tokens
150
+ }
151
+
152
+ export function parseToPrimitiveSExpr(input: string): PrimitiveSExpr[] {
153
+ const toks = tokenize(input)
154
+ let idx = 0
155
+
156
+ const peekToken = () => (idx < toks.length ? toks[idx]! : undefined)
157
+ const advanceToken = () => {
158
+ if (idx >= toks.length) {
159
+ throw new SyntaxError("Unexpected end of input")
160
+ }
161
+ return toks[idx++]!
162
+ }
163
+
164
+ function readForm(): PrimitiveSExpr {
165
+ const t = advanceToken()
166
+
167
+ switch (t.type) {
168
+ case "lparen": {
169
+ const list: PrimitiveSExpr[] = []
170
+ while (true) {
171
+ const next = peekToken()
172
+ if (!next) {
173
+ throw new SyntaxError("Unmatched '('")
174
+ }
175
+ if (next.type === "rparen") break
176
+ list.push(readForm())
177
+ }
178
+ advanceToken() // consume rparen
179
+ return list
180
+ }
181
+ case "rparen":
182
+ throw new SyntaxError("Unmatched ')'")
183
+ case "number":
184
+ return t.value
185
+ case "string":
186
+ return t.value
187
+ case "boolean":
188
+ return t.value
189
+ case "nil":
190
+ return null
191
+ case "symbol":
192
+ return t.value // represent symbols as strings
193
+ }
194
+ }
195
+
196
+ const forms: PrimitiveSExpr[] = []
197
+ while (peekToken()) forms.push(readForm())
198
+ return forms
199
+ }
200
+
201
+ /* -------------------------
202
+ Tiny helper utilities
203
+ --------------------------*/
204
+
205
+ export function printSExpr(x: PrimitiveSExpr): string {
206
+ if (x === null) return "nil"
207
+ if (typeof x === "boolean") return x ? "#t" : "#f"
208
+ if (typeof x === "number") return Number.isFinite(x) ? String(x) : "nan"
209
+ if (typeof x === "string") {
210
+ // naive: treat as symbol if it looks like a symbol; otherwise quote
211
+ if (/^[^\s()"]+$/u.test(x) && x !== "nil" && x !== "#t" && x !== "#f")
212
+ return x
213
+ return `"${x.replace(/["\\\n\r\t]/g, (m) =>
214
+ m === '"'
215
+ ? '\\"'
216
+ : m === "\\"
217
+ ? "\\\\"
218
+ : m === "\n"
219
+ ? "\\n"
220
+ : m === "\r"
221
+ ? "\\r"
222
+ : "\\t",
223
+ )}"`
224
+ }
225
+ if (Array.isArray(x)) {
226
+ return `(${x.map(printSExpr).join(" ")})`
227
+ }
228
+
229
+ throw new Error(`Unsupported S-expression value: ${JSON.stringify(x)}`)
230
+ }
231
+
232
+ // Quick example:
233
+ // const program = `
234
+ // ; define square
235
+ // (define (square x) (* x x))
236
+ // (list 1 2.5 "hi" #t nil foo-bar)
237
+ // `;
238
+ // const ast = parseSExpr(program);
239
+ // console.log(JSON.stringify(ast, null, 2));
240
+ // console.log(ast.map(printSExpr).join("\n"));
@@ -0,0 +1,3 @@
1
+ export const indentLines = (value: string, indent = " "): string[] => {
2
+ return value.split("\n").map((line) => `${indent}${line}`)
3
+ }
@@ -0,0 +1,12 @@
1
+ import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
2
+ import { toStringValue } from "./toStringValue"
3
+
4
+ export const parseYesNo = (
5
+ value: PrimitiveSExpr | undefined,
6
+ ): boolean | undefined => {
7
+ const str = toStringValue(value)
8
+ if (!str) return undefined
9
+ if (str === "yes" || str === "true") return true
10
+ if (str === "no" || str === "false") return false
11
+ return undefined
12
+ }
@@ -0,0 +1,8 @@
1
+ export const quoteSExprString = (value: string): string => {
2
+ return `"${value
3
+ .replace(/\\/g, "\\\\")
4
+ .replace(/"/g, '\\"')
5
+ .replace(/\n/g, "\\n")
6
+ .replace(/\r/g, "\\r")
7
+ .replace(/\t/g, "\\t")}"`
8
+ }
@@ -0,0 +1,19 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
+ import { Stroke } from "../classes/Stroke"
4
+
5
+ export const strokeFromArgs = (
6
+ args: PrimitiveSExpr[],
7
+ ): Stroke | undefined => {
8
+ try {
9
+ const parsed = SxClass.parsePrimitiveSexpr(
10
+ ["stroke", ...args] as PrimitiveSExpr,
11
+ )
12
+ if (parsed instanceof Stroke) {
13
+ return parsed
14
+ }
15
+ } catch {
16
+ // Ignore parse errors and defer to caller
17
+ }
18
+ return undefined
19
+ }
@@ -0,0 +1,13 @@
1
+ import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
2
+
3
+ export const toNumberValue = (
4
+ value: PrimitiveSExpr | undefined,
5
+ ): number | undefined => {
6
+ if (value === undefined) return undefined
7
+ if (typeof value === "number") return value
8
+ if (typeof value === "string") {
9
+ const parsed = Number(value)
10
+ return Number.isNaN(parsed) ? undefined : parsed
11
+ }
12
+ return undefined
13
+ }
@@ -0,0 +1,10 @@
1
+ import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
2
+
3
+ export const toStringValue = (
4
+ value: PrimitiveSExpr | undefined,
5
+ ): string | undefined => {
6
+ if (value === undefined) return undefined
7
+ if (typeof value === "string") return value
8
+ if (typeof value === "number" || typeof value === "boolean") return String(value)
9
+ return undefined
10
+ }