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.
- package/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc +111 -0
- package/.vscode/settings.json +16 -0
- package/AGENTS.md +30 -0
- package/README.md +206 -0
- package/biome.json +93 -0
- package/bun.lock +48 -0
- package/bunfig.toml +5 -0
- package/lib/index.ts +1 -0
- package/lib/sexpr/base-classes/SxClass.ts +164 -0
- package/lib/sexpr/base-classes/SxPrimitiveBoolean.ts +35 -0
- package/lib/sexpr/base-classes/SxPrimitiveNumber.ts +26 -0
- package/lib/sexpr/base-classes/SxPrimitiveString.ts +26 -0
- package/lib/sexpr/classes/At.ts +38 -0
- package/lib/sexpr/classes/Bus.ts +83 -0
- package/lib/sexpr/classes/BusEntry.ts +142 -0
- package/lib/sexpr/classes/Color.ts +29 -0
- package/lib/sexpr/classes/Dnp.ts +8 -0
- package/lib/sexpr/classes/EmbeddedFonts.ts +70 -0
- package/lib/sexpr/classes/ExcludeFromSim.ts +8 -0
- package/lib/sexpr/classes/FieldsAutoplaced.ts +8 -0
- package/lib/sexpr/classes/Footprint.ts +719 -0
- package/lib/sexpr/classes/FootprintAttr.ts +102 -0
- package/lib/sexpr/classes/FootprintAutoplaceCost180.ts +9 -0
- package/lib/sexpr/classes/FootprintAutoplaceCost90.ts +9 -0
- package/lib/sexpr/classes/FootprintClearance.ts +9 -0
- package/lib/sexpr/classes/FootprintDescr.ts +44 -0
- package/lib/sexpr/classes/FootprintLocked.ts +32 -0
- package/lib/sexpr/classes/FootprintModel.ts +145 -0
- package/lib/sexpr/classes/FootprintNetTiePadGroups.ts +50 -0
- package/lib/sexpr/classes/FootprintPad.ts +705 -0
- package/lib/sexpr/classes/FootprintPath.ts +44 -0
- package/lib/sexpr/classes/FootprintPlaced.ts +32 -0
- package/lib/sexpr/classes/FootprintPrivateLayers.ts +56 -0
- package/lib/sexpr/classes/FootprintSheetfile.ts +44 -0
- package/lib/sexpr/classes/FootprintSheetname.ts +44 -0
- package/lib/sexpr/classes/FootprintSolderMaskMargin.ts +9 -0
- package/lib/sexpr/classes/FootprintSolderPasteMargin.ts +9 -0
- package/lib/sexpr/classes/FootprintSolderPasteRatio.ts +9 -0
- package/lib/sexpr/classes/FootprintTags.ts +44 -0
- package/lib/sexpr/classes/FootprintTedit.ts +21 -0
- package/lib/sexpr/classes/FootprintThermalGap.ts +9 -0
- package/lib/sexpr/classes/FootprintThermalWidth.ts +9 -0
- package/lib/sexpr/classes/FootprintZoneConnect.ts +9 -0
- package/lib/sexpr/classes/FpArc.ts +289 -0
- package/lib/sexpr/classes/FpCircle.ts +293 -0
- package/lib/sexpr/classes/FpLine.ts +288 -0
- package/lib/sexpr/classes/FpPoly.ts +266 -0
- package/lib/sexpr/classes/FpPolyFill.ts +48 -0
- package/lib/sexpr/classes/FpPolyLocked.ts +40 -0
- package/lib/sexpr/classes/FpRect.ts +293 -0
- package/lib/sexpr/classes/FpText.ts +341 -0
- package/lib/sexpr/classes/FpTextBox.ts +412 -0
- package/lib/sexpr/classes/GrLine.ts +245 -0
- package/lib/sexpr/classes/GrLineAngle.ts +32 -0
- package/lib/sexpr/classes/GrLineEnd.ts +61 -0
- package/lib/sexpr/classes/GrLineLocked.ts +40 -0
- package/lib/sexpr/classes/GrLineStart.ts +61 -0
- package/lib/sexpr/classes/GrText.ts +202 -0
- package/lib/sexpr/classes/Image.ts +256 -0
- package/lib/sexpr/classes/InBom.ts +8 -0
- package/lib/sexpr/classes/Junction.ts +134 -0
- package/lib/sexpr/classes/KicadPcb.ts +313 -0
- package/lib/sexpr/classes/KicadSch.ts +303 -0
- package/lib/sexpr/classes/KicadSchGenerator.ts +32 -0
- package/lib/sexpr/classes/KicadSchGeneratorVersion.ts +30 -0
- package/lib/sexpr/classes/KicadSchVersion.ts +22 -0
- package/lib/sexpr/classes/Label.ts +136 -0
- package/lib/sexpr/classes/Layer.ts +51 -0
- package/lib/sexpr/classes/Layers.ts +47 -0
- package/lib/sexpr/classes/LibSymbols.ts +61 -0
- package/lib/sexpr/classes/NoConnect.ts +73 -0
- package/lib/sexpr/classes/OnBoard.ts +8 -0
- package/lib/sexpr/classes/PadChamfer.ts +50 -0
- package/lib/sexpr/classes/PadChamferRatio.ts +9 -0
- package/lib/sexpr/classes/PadClearance.ts +9 -0
- package/lib/sexpr/classes/PadDieLength.ts +9 -0
- package/lib/sexpr/classes/PadDrill.ts +145 -0
- package/lib/sexpr/classes/PadDrillOffset.ts +54 -0
- package/lib/sexpr/classes/PadLayers.ts +59 -0
- package/lib/sexpr/classes/PadNet.ts +56 -0
- package/lib/sexpr/classes/PadOptions.ts +182 -0
- package/lib/sexpr/classes/PadPinFunction.ts +9 -0
- package/lib/sexpr/classes/PadPinType.ts +9 -0
- package/lib/sexpr/classes/PadPrimitiveGrArc.ts +254 -0
- package/lib/sexpr/classes/PadPrimitiveGrCircle.ts +279 -0
- package/lib/sexpr/classes/PadPrimitiveGrLine.ts +126 -0
- package/lib/sexpr/classes/PadPrimitives.ts +289 -0
- package/lib/sexpr/classes/PadRectDelta.ts +57 -0
- package/lib/sexpr/classes/PadRoundrectRratio.ts +9 -0
- package/lib/sexpr/classes/PadSize.ts +54 -0
- package/lib/sexpr/classes/PadSolderMaskMargin.ts +9 -0
- package/lib/sexpr/classes/PadSolderPasteMargin.ts +9 -0
- package/lib/sexpr/classes/PadSolderPasteMarginRatio.ts +9 -0
- package/lib/sexpr/classes/PadTeardrops.ts +208 -0
- package/lib/sexpr/classes/PadThermalBridgeAngle.ts +9 -0
- package/lib/sexpr/classes/PadThermalGap.ts +9 -0
- package/lib/sexpr/classes/PadThermalWidth.ts +9 -0
- package/lib/sexpr/classes/PadZoneConnect.ts +9 -0
- package/lib/sexpr/classes/Paper.ts +119 -0
- package/lib/sexpr/classes/PcbGeneral.ts +75 -0
- package/lib/sexpr/classes/PcbGeneralLegacyTeardrops.ts +44 -0
- package/lib/sexpr/classes/PcbGeneralThickness.ts +9 -0
- package/lib/sexpr/classes/PcbGenerator.ts +16 -0
- package/lib/sexpr/classes/PcbGeneratorVersion.ts +16 -0
- package/lib/sexpr/classes/PcbLayerDefinition.ts +102 -0
- package/lib/sexpr/classes/PcbLayers.ts +34 -0
- package/lib/sexpr/classes/PcbNet.ts +56 -0
- package/lib/sexpr/classes/PcbVersion.ts +9 -0
- package/lib/sexpr/classes/Property.ts +246 -0
- package/lib/sexpr/classes/PropertyHide.ts +9 -0
- package/lib/sexpr/classes/PropertyUnlocked.ts +9 -0
- package/lib/sexpr/classes/Pts.ts +65 -0
- package/lib/sexpr/classes/RenderCache.ts +221 -0
- package/lib/sexpr/classes/SchematicText.ts +141 -0
- package/lib/sexpr/classes/Segment.ts +222 -0
- package/lib/sexpr/classes/SegmentEnd.ts +59 -0
- package/lib/sexpr/classes/SegmentLocked.ts +33 -0
- package/lib/sexpr/classes/SegmentNet.ts +62 -0
- package/lib/sexpr/classes/SegmentStart.ts +59 -0
- package/lib/sexpr/classes/Setup/PcbPlotParams.ts +729 -0
- package/lib/sexpr/classes/Setup/PcbPlotParamsBase.ts +9 -0
- package/lib/sexpr/classes/Setup/PcbPlotParamsNumericProperties.ts +105 -0
- package/lib/sexpr/classes/Setup/PcbPlotParamsStringPropertiesA.ts +104 -0
- package/lib/sexpr/classes/Setup/PcbPlotParamsStringPropertiesB.ts +105 -0
- package/lib/sexpr/classes/Setup/Setup.ts +573 -0
- package/lib/sexpr/classes/Setup/SetupPropertyTypes.ts +119 -0
- package/lib/sexpr/classes/Setup/Stackup.ts +140 -0
- package/lib/sexpr/classes/Setup/StackupLayer.ts +233 -0
- package/lib/sexpr/classes/Setup/StackupLayerProperties.ts +78 -0
- package/lib/sexpr/classes/Setup/StackupProperties.ts +41 -0
- package/lib/sexpr/classes/Setup/base.ts +167 -0
- package/lib/sexpr/classes/Setup/index.ts +14 -0
- package/lib/sexpr/classes/Setup/setupMultiValueProperties.ts +54 -0
- package/lib/sexpr/classes/Setup/setupNumericProperties.ts +151 -0
- package/lib/sexpr/classes/Setup/setupPropertyHandlers.ts +90 -0
- package/lib/sexpr/classes/Setup/setupStringProperties.ts +75 -0
- package/lib/sexpr/classes/Sheet.ts +205 -0
- package/lib/sexpr/classes/SheetFill.ts +44 -0
- package/lib/sexpr/classes/SheetInstances.ts +168 -0
- package/lib/sexpr/classes/SheetInstancesRoot.ts +165 -0
- package/lib/sexpr/classes/SheetPin.ts +122 -0
- package/lib/sexpr/classes/SheetProperty.ts +115 -0
- package/lib/sexpr/classes/SheetSize.ts +44 -0
- package/lib/sexpr/classes/Stroke.ts +58 -0
- package/lib/sexpr/classes/StrokeType.ts +34 -0
- package/lib/sexpr/classes/Symbol.ts +1541 -0
- package/lib/sexpr/classes/TextEffects.ts +444 -0
- package/lib/sexpr/classes/TitleBlock.ts +352 -0
- package/lib/sexpr/classes/Unit.ts +28 -0
- package/lib/sexpr/classes/Uuid.ts +8 -0
- package/lib/sexpr/classes/Via.ts +328 -0
- package/lib/sexpr/classes/ViaNet.ts +59 -0
- package/lib/sexpr/classes/Width.ts +8 -0
- package/lib/sexpr/classes/Wire.ts +91 -0
- package/lib/sexpr/classes/Xy.ts +35 -0
- package/lib/sexpr/classes/Zone.ts +41 -0
- package/lib/sexpr/index.ts +130 -0
- package/lib/sexpr/parseKicadSexpr.ts +5 -0
- package/lib/sexpr/parseToPrimitiveSExpr.ts +240 -0
- package/lib/sexpr/utils/indentLines.ts +3 -0
- package/lib/sexpr/utils/parseYesNo.ts +12 -0
- package/lib/sexpr/utils/quoteSExprString.ts +8 -0
- package/lib/sexpr/utils/strokeFromArgs.ts +19 -0
- package/lib/sexpr/utils/toNumberValue.ts +13 -0
- package/lib/sexpr/utils/toStringValue.ts +10 -0
- package/package.json +26 -0
- package/scripts/download-references.ts +66 -0
- package/tests/fixtures/expectEqualPrimitiveSExpr.ts +200 -0
- package/tests/sexpr/KicadPcbDemos.test.ts +48 -0
- package/tests/sexpr/KicadSchDemos.test.ts +49 -0
- package/tests/sexpr/classes/Footprint.test.ts +277 -0
- package/tests/sexpr/classes/FootprintPad.test.ts +71 -0
- package/tests/sexpr/classes/FpArc.test.ts +45 -0
- package/tests/sexpr/classes/FpCircle.test.ts +39 -0
- package/tests/sexpr/classes/FpPoly.test.ts +43 -0
- package/tests/sexpr/classes/FpRect.test.ts +40 -0
- package/tests/sexpr/classes/FpTextBox.test.ts +84 -0
- package/tests/sexpr/classes/Image.test.ts +50 -0
- package/tests/sexpr/classes/KicadSch.test.ts +97 -0
- package/tests/sexpr/classes/Paper.test.ts +30 -0
- package/tests/sexpr/classes/Property.test.ts +48 -0
- package/tests/sexpr/classes/Setup.test.ts +189 -0
- package/tests/sexpr/classes/Sheet.test.ts +107 -0
- package/tests/sexpr/classes/Stroke.test.ts +15 -0
- package/tests/sexpr/classes/Symbol.test.ts +96 -0
- package/tests/sexpr/classes/TextEffects.test.ts +56 -0
- package/tests/sexpr/classes/TitleBlock.test.ts +40 -0
- 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,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)
|