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,352 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import { printSExpr, type PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
+ import { quoteSExprString } from "../utils/quoteSExprString"
4
+ import { toNumberValue } from "../utils/toNumberValue"
5
+ import { toStringValue } from "../utils/toStringValue"
6
+ import { At } from "./At"
7
+ import { TextEffects } from "./TextEffects"
8
+ import { Uuid } from "./Uuid"
9
+
10
+ function primitiveToString(value: PrimitiveSExpr | undefined): string {
11
+ if (value === undefined) return ""
12
+ const str = toStringValue(value)
13
+ if (str !== undefined) return str
14
+ return printSExpr(value)
15
+ }
16
+
17
+ export class TitleBlock extends SxClass {
18
+ static override token = "title_block"
19
+ token = "title_block"
20
+
21
+ private _sxTitle?: TitleBlockTitle
22
+ private _sxDate?: TitleBlockDate
23
+ private _sxRev?: TitleBlockRevision
24
+ private _sxCompany?: TitleBlockCompany
25
+ comments: TitleBlockComment[] = []
26
+
27
+ static override fromSexprPrimitives(
28
+ primitiveSexprs: PrimitiveSExpr[],
29
+ ): TitleBlock {
30
+ const titleBlock = new TitleBlock()
31
+
32
+ for (const primitive of primitiveSexprs) {
33
+ if (!Array.isArray(primitive) || primitive.length === 0) {
34
+ throw new Error(
35
+ `Unexpected primitive inside title_block: ${printSExpr(primitive)}`,
36
+ )
37
+ }
38
+
39
+ const parsed = SxClass.parsePrimitiveSexpr(primitive, {
40
+ parentToken: this.token,
41
+ })
42
+
43
+ if (!(parsed instanceof SxClass)) {
44
+ throw new Error(
45
+ `Unable to parse child node inside title_block: ${printSExpr(primitive)}`,
46
+ )
47
+ }
48
+
49
+ if (parsed instanceof TitleBlockTitle) {
50
+ titleBlock._sxTitle = parsed
51
+ continue
52
+ }
53
+ if (parsed instanceof TitleBlockDate) {
54
+ titleBlock._sxDate = parsed
55
+ continue
56
+ }
57
+ if (parsed instanceof TitleBlockRevision) {
58
+ titleBlock._sxRev = parsed
59
+ continue
60
+ }
61
+ if (parsed instanceof TitleBlockCompany) {
62
+ titleBlock._sxCompany = parsed
63
+ continue
64
+ }
65
+ if (parsed instanceof TitleBlockComment) {
66
+ titleBlock.comments.push(parsed)
67
+ continue
68
+ }
69
+
70
+ throw new Error(
71
+ `Unsupported child "${parsed.token}" inside title_block expression`,
72
+ )
73
+ }
74
+
75
+ return titleBlock
76
+ }
77
+
78
+ get title(): string | undefined {
79
+ return this._sxTitle?.value
80
+ }
81
+
82
+ set title(value: string | undefined) {
83
+ this._sxTitle = value === undefined ? undefined : new TitleBlockTitle(value)
84
+ }
85
+
86
+ get date(): string | undefined {
87
+ return this._sxDate?.value
88
+ }
89
+
90
+ set date(value: string | undefined) {
91
+ this._sxDate = value === undefined ? undefined : new TitleBlockDate(value)
92
+ }
93
+
94
+ get rev(): string | undefined {
95
+ return this._sxRev?.value
96
+ }
97
+
98
+ set rev(value: string | undefined) {
99
+ this._sxRev = value === undefined ? undefined : new TitleBlockRevision(value)
100
+ }
101
+
102
+ get company(): string | undefined {
103
+ return this._sxCompany?.value
104
+ }
105
+
106
+ set company(value: string | undefined) {
107
+ this._sxCompany =
108
+ value === undefined ? undefined : new TitleBlockCompany(value)
109
+ }
110
+
111
+ getComment(index: number): string | undefined {
112
+ return this.comments.find((comment) => comment.index === index)?.value
113
+ }
114
+
115
+ setComment(index: number, value: string) {
116
+ const existing = this.comments.find((comment) => comment.index === index)
117
+ if (existing) {
118
+ existing.value = value
119
+ return
120
+ }
121
+ this.comments.push(new TitleBlockComment(index, value))
122
+ }
123
+
124
+ removeComment(index: number) {
125
+ this.comments = this.comments.filter((comment) => comment.index !== index)
126
+ }
127
+
128
+ override getChildren(): SxClass[] {
129
+ const children: SxClass[] = []
130
+ if (this._sxTitle) children.push(this._sxTitle)
131
+ if (this._sxDate) children.push(this._sxDate)
132
+ if (this._sxRev) children.push(this._sxRev)
133
+ if (this._sxCompany) children.push(this._sxCompany)
134
+
135
+ const sortedComments = [...this.comments].sort(
136
+ (a, b) => a.index - b.index,
137
+ )
138
+ children.push(...sortedComments)
139
+ return children
140
+ }
141
+
142
+ override getString(): string {
143
+ const lines: string[] = ["(title_block"]
144
+
145
+ for (const child of this.getChildren()) {
146
+ lines.push(child.getStringIndented())
147
+ }
148
+
149
+ lines.push(")")
150
+ return lines.join("\n")
151
+ }
152
+ }
153
+ SxClass.register(TitleBlock)
154
+
155
+ abstract class TitleBlockStringValue extends SxClass {
156
+ value: string
157
+
158
+ constructor(value: string) {
159
+ super()
160
+ this.value = value
161
+ }
162
+
163
+ override getChildren(): SxClass[] {
164
+ return []
165
+ }
166
+ }
167
+
168
+ export class TitleBlockTitle extends TitleBlockStringValue {
169
+ static override token = "title"
170
+ static override parentToken = "title_block"
171
+ token = "title"
172
+
173
+ static override fromSexprPrimitives(
174
+ primitiveSexprs: PrimitiveSExpr[],
175
+ ): TitleBlockTitle {
176
+ const [rawValue] = primitiveSexprs
177
+ return new TitleBlockTitle(primitiveToString(rawValue))
178
+ }
179
+
180
+ override getString(): string {
181
+ return `(title ${quoteSExprString(this.value)})`
182
+ }
183
+ }
184
+ SxClass.register(TitleBlockTitle)
185
+
186
+ export class TitleBlockDate extends TitleBlockStringValue {
187
+ static override token = "date"
188
+ static override parentToken = "title_block"
189
+ token = "date"
190
+
191
+ static override fromSexprPrimitives(
192
+ primitiveSexprs: PrimitiveSExpr[],
193
+ ): TitleBlockDate {
194
+ const [rawValue] = primitiveSexprs
195
+ return new TitleBlockDate(primitiveToString(rawValue))
196
+ }
197
+
198
+ override getString(): string {
199
+ return `(date ${quoteSExprString(this.value)})`
200
+ }
201
+ }
202
+ SxClass.register(TitleBlockDate)
203
+
204
+ export class TitleBlockRevision extends TitleBlockStringValue {
205
+ static override token = "rev"
206
+ static override parentToken = "title_block"
207
+ token = "rev"
208
+
209
+ static override fromSexprPrimitives(
210
+ primitiveSexprs: PrimitiveSExpr[],
211
+ ): TitleBlockRevision {
212
+ const [rawValue] = primitiveSexprs
213
+ return new TitleBlockRevision(primitiveToString(rawValue))
214
+ }
215
+
216
+ override getString(): string {
217
+ return `(rev ${quoteSExprString(this.value)})`
218
+ }
219
+ }
220
+ SxClass.register(TitleBlockRevision)
221
+
222
+ export class TitleBlockCompany extends TitleBlockStringValue {
223
+ static override token = "company"
224
+ static override parentToken = "title_block"
225
+ token = "company"
226
+
227
+ static override fromSexprPrimitives(
228
+ primitiveSexprs: PrimitiveSExpr[],
229
+ ): TitleBlockCompany {
230
+ const [rawValue] = primitiveSexprs
231
+ return new TitleBlockCompany(primitiveToString(rawValue))
232
+ }
233
+
234
+ override getString(): string {
235
+ return `(company ${quoteSExprString(this.value)})`
236
+ }
237
+ }
238
+ SxClass.register(TitleBlockCompany)
239
+
240
+ export class TitleBlockComment extends SxClass {
241
+ static override token = "comment"
242
+ static override parentToken = "title_block"
243
+ token = "comment"
244
+
245
+ index: number
246
+ value: string
247
+ private _sxAt?: At
248
+ private _sxEffects?: TextEffects
249
+ private _sxUuid?: Uuid
250
+
251
+ constructor(index: number, value: string) {
252
+ super()
253
+ this.index = index
254
+ this.value = value
255
+ }
256
+
257
+ static override fromSexprPrimitives(
258
+ primitiveSexprs: PrimitiveSExpr[],
259
+ ): TitleBlockComment {
260
+ const [rawIndex, rawValue, ...rest] = primitiveSexprs
261
+ const index = toNumberValue(rawIndex)
262
+ if (index === undefined) {
263
+ throw new Error("title_block comment requires a numeric index")
264
+ }
265
+ const value = primitiveToString(rawValue)
266
+ const comment = new TitleBlockComment(index, value)
267
+
268
+ for (const primitive of rest) {
269
+ if (!Array.isArray(primitive) || primitive.length === 0) {
270
+ throw new Error(
271
+ `Unexpected primitive inside title_block comment: ${printSExpr(primitive)}`,
272
+ )
273
+ }
274
+
275
+ const parsed = SxClass.parsePrimitiveSexpr(primitive, {
276
+ parentToken: this.token,
277
+ })
278
+
279
+ if (!(parsed instanceof SxClass)) {
280
+ throw new Error(
281
+ `Unable to parse child node inside title_block comment: ${printSExpr(primitive)}`,
282
+ )
283
+ }
284
+
285
+ if (parsed instanceof At) {
286
+ comment._sxAt = parsed
287
+ continue
288
+ }
289
+ if (parsed instanceof TextEffects) {
290
+ comment._sxEffects = parsed
291
+ continue
292
+ }
293
+ if (parsed instanceof Uuid) {
294
+ comment._sxUuid = parsed
295
+ continue
296
+ }
297
+
298
+ throw new Error(
299
+ `Unsupported child "${parsed.token}" inside title_block comment`,
300
+ )
301
+ }
302
+
303
+ return comment
304
+ }
305
+
306
+ get at(): At | undefined {
307
+ return this._sxAt
308
+ }
309
+
310
+ set at(value: At | undefined) {
311
+ this._sxAt = value
312
+ }
313
+
314
+ get effects(): TextEffects | undefined {
315
+ return this._sxEffects
316
+ }
317
+
318
+ set effects(value: TextEffects | undefined) {
319
+ this._sxEffects = value
320
+ }
321
+
322
+ get uuid(): Uuid | undefined {
323
+ return this._sxUuid
324
+ }
325
+
326
+ set uuid(value: Uuid | undefined) {
327
+ this._sxUuid = value
328
+ }
329
+
330
+ override getChildren(): SxClass[] {
331
+ const children: SxClass[] = []
332
+ if (this._sxAt) children.push(this._sxAt)
333
+ if (this._sxEffects) children.push(this._sxEffects)
334
+ if (this._sxUuid) children.push(this._sxUuid)
335
+ return children
336
+ }
337
+
338
+ override getString(): string {
339
+ const children = this.getChildren()
340
+ if (children.length === 0) {
341
+ return `(comment ${this.index} ${quoteSExprString(this.value)})`
342
+ }
343
+
344
+ const lines = [`(comment ${this.index} ${quoteSExprString(this.value)}`]
345
+ for (const child of children) {
346
+ lines.push(child.getStringIndented())
347
+ }
348
+ lines.push(")")
349
+ return lines.join("\n")
350
+ }
351
+ }
352
+ SxClass.register(TitleBlockComment)
@@ -0,0 +1,28 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
+
4
+ export type UnitString = "inches" | "mils" | "millimeters" | "automatic"
5
+
6
+ const unitValues = ["inches", "mils", "millimeters", "automatic"] as const
7
+
8
+ export class Unit extends SxClass {
9
+ static override token = "unit"
10
+ token = "unit"
11
+
12
+ value: UnitString
13
+
14
+ constructor(value: UnitString | number) {
15
+ super()
16
+ if (typeof value === "number") {
17
+ this.value = unitValues[value]!
18
+ } else {
19
+ this.value = value
20
+ }
21
+ }
22
+
23
+ static override fromSexprPrimitives(primitiveSexprs: PrimitiveSExpr[]): Unit {
24
+ const [numericValue] = primitiveSexprs
25
+ return new Unit(numericValue as number)
26
+ }
27
+ }
28
+ SxClass.register(Unit)
@@ -0,0 +1,8 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import { SxPrimitiveString } from "../base-classes/SxPrimitiveString"
3
+
4
+ export class Uuid extends SxPrimitiveString {
5
+ static override token = "uuid"
6
+ token = "uuid"
7
+ }
8
+ SxClass.register(Uuid)
@@ -0,0 +1,328 @@
1
+ import { SxClass } from "../base-classes/SxClass"
2
+ import type { PrimitiveSExpr } from "../parseToPrimitiveSExpr"
3
+ import { quoteSExprString } from "../utils/quoteSExprString"
4
+ import { At } from "./At"
5
+ import { Layers } from "./Layers"
6
+ import { Uuid } from "./Uuid"
7
+ import { ViaNet } from "./ViaNet"
8
+ import { toNumberValue } from "../utils/toNumberValue"
9
+ import { toStringValue } from "../utils/toStringValue"
10
+ import { PadTeardrops } from "./PadTeardrops"
11
+
12
+ const BARE_FLAGS = new Set([
13
+ "locked",
14
+ "free",
15
+ "remove_unused_layers",
16
+ "keep_end_layers",
17
+ ])
18
+
19
+ export class Via extends SxClass {
20
+ static override token = "via"
21
+ token = "via"
22
+
23
+ private _type?: string
24
+ private _locked = false
25
+ private _free = false
26
+ private _removeUnusedLayers = false
27
+ private _keepEndLayers = false
28
+ private _sxAt?: At
29
+ private _size?: number
30
+ private _drill?: number
31
+ private _sxLayers?: Layers
32
+ private _sxNet?: ViaNet
33
+ private _sxUuid?: Uuid
34
+ private _tstamp?: string
35
+ private _sxTeardrops?: PadTeardrops
36
+
37
+ static override fromSexprPrimitives(primitiveSexprs: PrimitiveSExpr[]): Via {
38
+ const via = new Via()
39
+
40
+ for (const primitive of primitiveSexprs) {
41
+ if (typeof primitive === "string") {
42
+ via.consumeBareToken(primitive)
43
+ continue
44
+ }
45
+
46
+ if (!Array.isArray(primitive) || primitive.length === 0) {
47
+ throw new Error(
48
+ `via encountered unsupported primitive child: ${JSON.stringify(primitive)}`,
49
+ )
50
+ }
51
+
52
+ const [token, ...rest] = primitive
53
+ if (typeof token !== "string") {
54
+ throw new Error(
55
+ `via encountered child with non-string token: ${JSON.stringify(primitive)}`,
56
+ )
57
+ }
58
+
59
+ via.consumeNode(token, rest as PrimitiveSExpr[])
60
+ }
61
+
62
+ return via
63
+ }
64
+
65
+ private consumeBareToken(token: string) {
66
+ if (token === "blind" || token === "micro") {
67
+ this._type = token
68
+ return
69
+ }
70
+ if (!BARE_FLAGS.has(token)) {
71
+ throw new Error(`via encountered unsupported flag "${token}"`)
72
+ }
73
+ switch (token) {
74
+ case "locked":
75
+ this._locked = true
76
+ break
77
+ case "free":
78
+ this._free = true
79
+ break
80
+ case "remove_unused_layers":
81
+ this._removeUnusedLayers = true
82
+ break
83
+ case "keep_end_layers":
84
+ this._keepEndLayers = true
85
+ break
86
+ }
87
+ }
88
+
89
+ private consumeNode(token: string, args: PrimitiveSExpr[]) {
90
+ switch (token) {
91
+ case "type": {
92
+ const value = toStringValue(args[0])
93
+ if (value === undefined) {
94
+ throw new Error("via type expects a string value")
95
+ }
96
+ this._type = value
97
+ return
98
+ }
99
+ case "locked":
100
+ this._locked = this.parseYesNo(args)
101
+ return
102
+ case "free":
103
+ this._free = this.parseYesNo(args)
104
+ return
105
+ case "remove_unused_layers":
106
+ this._removeUnusedLayers = this.parseYesNo(args)
107
+ return
108
+ case "keep_end_layers":
109
+ this._keepEndLayers = this.parseYesNo(args)
110
+ return
111
+ case "at": {
112
+ const parsed = SxClass.parsePrimitiveSexpr(["at", ...args] as any, {
113
+ parentToken: this.token,
114
+ })
115
+ if (!(parsed instanceof At)) {
116
+ throw new Error("via failed to parse at child")
117
+ }
118
+ this._sxAt = parsed
119
+ return
120
+ }
121
+ case "size": {
122
+ const value = toNumberValue(args[0])
123
+ if (value === undefined) {
124
+ throw new Error("via size expects a numeric value")
125
+ }
126
+ this._size = value
127
+ return
128
+ }
129
+ case "drill": {
130
+ const value = toNumberValue(args[0])
131
+ if (value === undefined) {
132
+ throw new Error("via drill expects a numeric value")
133
+ }
134
+ this._drill = value
135
+ return
136
+ }
137
+ case "layers": {
138
+ this._sxLayers = Layers.fromSexprPrimitives(args)
139
+ return
140
+ }
141
+ case "net": {
142
+ this._sxNet = ViaNet.fromSexprPrimitives(args)
143
+ return
144
+ }
145
+ case "teardrops": {
146
+ this._sxTeardrops = PadTeardrops.fromSexprPrimitives(args)
147
+ return
148
+ }
149
+ case "uuid": {
150
+ const value = toStringValue(args[0])
151
+ if (value === undefined) {
152
+ throw new Error("via uuid expects a string value")
153
+ }
154
+ this._sxUuid = new Uuid(value)
155
+ return
156
+ }
157
+ case "tstamp": {
158
+ const value = toStringValue(args[0])
159
+ if (value === undefined) {
160
+ throw new Error("via tstamp expects a string value")
161
+ }
162
+ this._tstamp = value
163
+ return
164
+ }
165
+ default:
166
+ throw new Error(`via encountered unsupported child token "${token}"`)
167
+ }
168
+ }
169
+
170
+ private parseYesNo(args: PrimitiveSExpr[]): boolean {
171
+ if (args.length === 0) return true
172
+ const value = toStringValue(args[0])
173
+ if (value === undefined) {
174
+ throw new Error("Expected string when parsing via boolean child")
175
+ }
176
+ return /^(yes|true)$/iu.test(value)
177
+ }
178
+
179
+ get type(): string | undefined {
180
+ return this._type
181
+ }
182
+
183
+ set type(value: string | undefined) {
184
+ this._type = value
185
+ }
186
+
187
+ get locked(): boolean {
188
+ return this._locked
189
+ }
190
+
191
+ set locked(value: boolean) {
192
+ this._locked = value
193
+ }
194
+
195
+ get free(): boolean {
196
+ return this._free
197
+ }
198
+
199
+ set free(value: boolean) {
200
+ this._free = value
201
+ }
202
+
203
+ get removeUnusedLayers(): boolean {
204
+ return this._removeUnusedLayers
205
+ }
206
+
207
+ set removeUnusedLayers(value: boolean) {
208
+ this._removeUnusedLayers = value
209
+ }
210
+
211
+ get keepEndLayers(): boolean {
212
+ return this._keepEndLayers
213
+ }
214
+
215
+ set keepEndLayers(value: boolean) {
216
+ this._keepEndLayers = value
217
+ }
218
+
219
+ get at(): At | undefined {
220
+ return this._sxAt
221
+ }
222
+
223
+ set at(value: At | undefined) {
224
+ this._sxAt = value
225
+ }
226
+
227
+ get size(): number | undefined {
228
+ return this._size
229
+ }
230
+
231
+ set size(value: number | undefined) {
232
+ this._size = value
233
+ }
234
+
235
+ get drill(): number | undefined {
236
+ return this._drill
237
+ }
238
+
239
+ set drill(value: number | undefined) {
240
+ this._drill = value
241
+ }
242
+
243
+ get layers(): Layers | undefined {
244
+ return this._sxLayers
245
+ }
246
+
247
+ set layers(value: Layers | string[] | undefined) {
248
+ if (value === undefined) {
249
+ this._sxLayers = undefined
250
+ return
251
+ }
252
+ this._sxLayers = value instanceof Layers ? value : new Layers(value)
253
+ }
254
+
255
+ get net(): ViaNet | undefined {
256
+ return this._sxNet
257
+ }
258
+
259
+ set net(value: ViaNet | undefined) {
260
+ this._sxNet = value
261
+ }
262
+
263
+ get uuid(): Uuid | undefined {
264
+ return this._sxUuid
265
+ }
266
+
267
+ set uuid(value: Uuid | string | undefined) {
268
+ if (value === undefined) {
269
+ this._sxUuid = undefined
270
+ return
271
+ }
272
+ this._sxUuid = value instanceof Uuid ? value : new Uuid(value)
273
+ }
274
+
275
+ get teardrops(): PadTeardrops | undefined {
276
+ return this._sxTeardrops
277
+ }
278
+
279
+ set teardrops(value: PadTeardrops | undefined) {
280
+ this._sxTeardrops = value
281
+ }
282
+
283
+ get tstamp(): string | undefined {
284
+ return this._tstamp
285
+ }
286
+
287
+ set tstamp(value: string | undefined) {
288
+ this._tstamp = value
289
+ }
290
+
291
+ override getChildren(): SxClass[] {
292
+ const children: SxClass[] = []
293
+ if (this._sxAt) children.push(this._sxAt)
294
+ if (this._sxLayers) children.push(this._sxLayers)
295
+ if (this._sxNet) children.push(this._sxNet)
296
+ if (this._sxUuid) children.push(this._sxUuid)
297
+ if (this._sxTeardrops) children.push(this._sxTeardrops)
298
+ return children
299
+ }
300
+
301
+ override getString(): string {
302
+ const lines = ["(via"]
303
+
304
+ if (this._type !== undefined) {
305
+ lines.push(` (type ${this._type})`)
306
+ }
307
+ if (this._locked) lines.push(" locked")
308
+ if (this._free) lines.push(" free")
309
+ if (this._removeUnusedLayers) lines.push(" remove_unused_layers")
310
+ if (this._keepEndLayers) lines.push(" keep_end_layers")
311
+ if (this._sxAt) lines.push(this._sxAt.getStringIndented())
312
+ if (this._size !== undefined) lines.push(` (size ${this._size})`)
313
+ if (this._drill !== undefined) lines.push(` (drill ${this._drill})`)
314
+ if (this._sxLayers) lines.push(this._sxLayers.getStringIndented())
315
+ if (this._sxNet) lines.push(this._sxNet.getStringIndented())
316
+ if (this._sxUuid) lines.push(this._sxUuid.getStringIndented())
317
+ if (this._tstamp !== undefined) {
318
+ lines.push(` (tstamp ${quoteSExprString(this._tstamp)})`)
319
+ }
320
+ if (this._sxTeardrops) {
321
+ lines.push(this._sxTeardrops.getStringIndented())
322
+ }
323
+
324
+ lines.push(")")
325
+ return lines.join("\n")
326
+ }
327
+ }
328
+ SxClass.register(Via)