kicad-sch-api 0.4.0__py3-none-any.whl → 0.4.1__py3-none-any.whl
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.
- kicad_sch_api/__init__.py +2 -2
- kicad_sch_api/cli/__init__.py +45 -0
- kicad_sch_api/cli/base.py +302 -0
- kicad_sch_api/cli/bom.py +164 -0
- kicad_sch_api/cli/erc.py +229 -0
- kicad_sch_api/cli/export_docs.py +289 -0
- kicad_sch_api/cli/netlist.py +94 -0
- kicad_sch_api/cli/types.py +43 -0
- kicad_sch_api/core/collections/__init__.py +5 -0
- kicad_sch_api/core/collections/base.py +248 -0
- kicad_sch_api/core/component_bounds.py +5 -0
- kicad_sch_api/core/components.py +62 -45
- kicad_sch_api/core/config.py +85 -3
- kicad_sch_api/core/factories/__init__.py +5 -0
- kicad_sch_api/core/factories/element_factory.py +276 -0
- kicad_sch_api/core/junctions.py +26 -75
- kicad_sch_api/core/labels.py +28 -52
- kicad_sch_api/core/managers/file_io.py +3 -2
- kicad_sch_api/core/managers/metadata.py +6 -5
- kicad_sch_api/core/managers/validation.py +3 -2
- kicad_sch_api/core/managers/wire.py +7 -1
- kicad_sch_api/core/nets.py +38 -43
- kicad_sch_api/core/no_connects.py +29 -53
- kicad_sch_api/core/parser.py +75 -1765
- kicad_sch_api/core/schematic.py +211 -148
- kicad_sch_api/core/texts.py +28 -55
- kicad_sch_api/core/types.py +59 -18
- kicad_sch_api/core/wires.py +27 -75
- kicad_sch_api/parsers/elements/__init__.py +22 -0
- kicad_sch_api/parsers/elements/graphics_parser.py +564 -0
- kicad_sch_api/parsers/elements/label_parser.py +194 -0
- kicad_sch_api/parsers/elements/library_parser.py +165 -0
- kicad_sch_api/parsers/elements/metadata_parser.py +58 -0
- kicad_sch_api/parsers/elements/sheet_parser.py +352 -0
- kicad_sch_api/parsers/elements/symbol_parser.py +313 -0
- kicad_sch_api/parsers/elements/text_parser.py +250 -0
- kicad_sch_api/parsers/elements/wire_parser.py +242 -0
- kicad_sch_api/parsers/utils.py +80 -0
- kicad_sch_api/validation/__init__.py +25 -0
- kicad_sch_api/validation/erc.py +171 -0
- kicad_sch_api/validation/erc_models.py +203 -0
- kicad_sch_api/validation/pin_matrix.py +243 -0
- kicad_sch_api/validation/validators.py +391 -0
- {kicad_sch_api-0.4.0.dist-info → kicad_sch_api-0.4.1.dist-info}/METADATA +17 -9
- kicad_sch_api-0.4.1.dist-info/RECORD +87 -0
- kicad_sch_api/core/manhattan_routing.py +0 -430
- kicad_sch_api/core/simple_manhattan.py +0 -228
- kicad_sch_api/core/wire_routing.py +0 -380
- kicad_sch_api/parsers/label_parser.py +0 -254
- kicad_sch_api/parsers/symbol_parser.py +0 -222
- kicad_sch_api/parsers/wire_parser.py +0 -99
- kicad_sch_api-0.4.0.dist-info/RECORD +0 -67
- {kicad_sch_api-0.4.0.dist-info → kicad_sch_api-0.4.1.dist-info}/WHEEL +0 -0
- {kicad_sch_api-0.4.0.dist-info → kicad_sch_api-0.4.1.dist-info}/entry_points.txt +0 -0
- {kicad_sch_api-0.4.0.dist-info → kicad_sch_api-0.4.1.dist-info}/licenses/LICENSE +0 -0
- {kicad_sch_api-0.4.0.dist-info → kicad_sch_api-0.4.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Label and hierarchical label elements parser for KiCAD schematics.
|
|
3
|
+
|
|
4
|
+
Handles parsing and serialization of Label and hierarchical label elements.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
|
+
|
|
10
|
+
import sexpdata
|
|
11
|
+
|
|
12
|
+
from ...core.config import config
|
|
13
|
+
from ..base import BaseElementParser
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class LabelParser(BaseElementParser):
|
|
19
|
+
"""Parser for Label and hierarchical label elements."""
|
|
20
|
+
|
|
21
|
+
def __init__(self):
|
|
22
|
+
"""Initialize label parser."""
|
|
23
|
+
super().__init__("label")
|
|
24
|
+
|
|
25
|
+
def _parse_label(self, item: List[Any]) -> Optional[Dict[str, Any]]:
|
|
26
|
+
"""Parse a label definition."""
|
|
27
|
+
# Label format: (label "text" (at x y rotation) (effects ...) (uuid ...))
|
|
28
|
+
if len(item) < 2:
|
|
29
|
+
return None
|
|
30
|
+
|
|
31
|
+
label_data = {
|
|
32
|
+
"text": str(item[1]), # Label text is second element
|
|
33
|
+
"position": {"x": 0, "y": 0},
|
|
34
|
+
"rotation": 0,
|
|
35
|
+
"size": config.defaults.font_size,
|
|
36
|
+
"uuid": None,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for elem in item[2:]: # Skip label keyword and text
|
|
40
|
+
if not isinstance(elem, list):
|
|
41
|
+
continue
|
|
42
|
+
|
|
43
|
+
elem_type = str(elem[0]) if isinstance(elem[0], sexpdata.Symbol) else None
|
|
44
|
+
|
|
45
|
+
if elem_type == "at":
|
|
46
|
+
# Parse position: (at x y rotation)
|
|
47
|
+
if len(elem) >= 3:
|
|
48
|
+
label_data["position"] = {"x": float(elem[1]), "y": float(elem[2])}
|
|
49
|
+
if len(elem) >= 4:
|
|
50
|
+
label_data["rotation"] = float(elem[3])
|
|
51
|
+
|
|
52
|
+
elif elem_type == "effects":
|
|
53
|
+
# Parse effects for font size: (effects (font (size x y)) ...)
|
|
54
|
+
for effect_elem in elem[1:]:
|
|
55
|
+
if isinstance(effect_elem, list) and str(effect_elem[0]) == "font":
|
|
56
|
+
for font_elem in effect_elem[1:]:
|
|
57
|
+
if isinstance(font_elem, list) and str(font_elem[0]) == "size":
|
|
58
|
+
if len(font_elem) >= 2:
|
|
59
|
+
label_data["size"] = float(font_elem[1])
|
|
60
|
+
|
|
61
|
+
elif elem_type == "uuid":
|
|
62
|
+
label_data["uuid"] = str(elem[1]) if len(elem) > 1 else None
|
|
63
|
+
|
|
64
|
+
return label_data
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _parse_hierarchical_label(self, item: List[Any]) -> Optional[Dict[str, Any]]:
|
|
68
|
+
"""Parse a hierarchical label definition."""
|
|
69
|
+
# Format: (hierarchical_label "text" (shape input) (at x y rotation) (effects ...) (uuid ...))
|
|
70
|
+
if len(item) < 2:
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
hlabel_data = {
|
|
74
|
+
"text": str(item[1]), # Hierarchical label text is second element
|
|
75
|
+
"shape": "input", # input/output/bidirectional/tri_state/passive
|
|
76
|
+
"position": {"x": 0, "y": 0},
|
|
77
|
+
"rotation": 0,
|
|
78
|
+
"size": config.defaults.font_size,
|
|
79
|
+
"justify": "left",
|
|
80
|
+
"uuid": None,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
for elem in item[2:]: # Skip hierarchical_label keyword and text
|
|
84
|
+
if not isinstance(elem, list):
|
|
85
|
+
continue
|
|
86
|
+
|
|
87
|
+
elem_type = str(elem[0]) if isinstance(elem[0], sexpdata.Symbol) else None
|
|
88
|
+
|
|
89
|
+
if elem_type == "shape":
|
|
90
|
+
# Parse shape: (shape input)
|
|
91
|
+
if len(elem) >= 2:
|
|
92
|
+
hlabel_data["shape"] = str(elem[1])
|
|
93
|
+
|
|
94
|
+
elif elem_type == "at":
|
|
95
|
+
# Parse position: (at x y rotation)
|
|
96
|
+
if len(elem) >= 3:
|
|
97
|
+
hlabel_data["position"] = {"x": float(elem[1]), "y": float(elem[2])}
|
|
98
|
+
if len(elem) >= 4:
|
|
99
|
+
hlabel_data["rotation"] = float(elem[3])
|
|
100
|
+
|
|
101
|
+
elif elem_type == "effects":
|
|
102
|
+
# Parse effects for font size and justification: (effects (font (size x y)) (justify left))
|
|
103
|
+
for effect_elem in elem[1:]:
|
|
104
|
+
if isinstance(effect_elem, list):
|
|
105
|
+
effect_type = (
|
|
106
|
+
str(effect_elem[0])
|
|
107
|
+
if isinstance(effect_elem[0], sexpdata.Symbol)
|
|
108
|
+
else None
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
if effect_type == "font":
|
|
112
|
+
# Parse font size
|
|
113
|
+
for font_elem in effect_elem[1:]:
|
|
114
|
+
if isinstance(font_elem, list) and str(font_elem[0]) == "size":
|
|
115
|
+
if len(font_elem) >= 2:
|
|
116
|
+
hlabel_data["size"] = float(font_elem[1])
|
|
117
|
+
|
|
118
|
+
elif effect_type == "justify":
|
|
119
|
+
# Parse justification (e.g., "left", "right")
|
|
120
|
+
if len(effect_elem) >= 2:
|
|
121
|
+
hlabel_data["justify"] = str(effect_elem[1])
|
|
122
|
+
|
|
123
|
+
elif elem_type == "uuid":
|
|
124
|
+
hlabel_data["uuid"] = str(elem[1]) if len(elem) > 1 else None
|
|
125
|
+
|
|
126
|
+
return hlabel_data
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _label_to_sexp(self, label_data: Dict[str, Any]) -> List[Any]:
|
|
130
|
+
"""Convert local label to S-expression."""
|
|
131
|
+
sexp = [sexpdata.Symbol("label"), label_data["text"]]
|
|
132
|
+
|
|
133
|
+
# Add position
|
|
134
|
+
pos = label_data["position"]
|
|
135
|
+
x, y = pos["x"], pos["y"]
|
|
136
|
+
rotation = label_data.get("rotation", 0)
|
|
137
|
+
|
|
138
|
+
# Format coordinates properly
|
|
139
|
+
if isinstance(x, float) and x.is_integer():
|
|
140
|
+
x = int(x)
|
|
141
|
+
if isinstance(y, float) and y.is_integer():
|
|
142
|
+
y = int(y)
|
|
143
|
+
|
|
144
|
+
sexp.append([sexpdata.Symbol("at"), x, y, rotation])
|
|
145
|
+
|
|
146
|
+
# Add effects (font properties)
|
|
147
|
+
size = label_data.get("size", config.defaults.font_size)
|
|
148
|
+
effects = [sexpdata.Symbol("effects")]
|
|
149
|
+
font = [sexpdata.Symbol("font"), [sexpdata.Symbol("size"), size, size]]
|
|
150
|
+
effects.append(font)
|
|
151
|
+
effects.append(
|
|
152
|
+
[sexpdata.Symbol("justify"), sexpdata.Symbol("left"), sexpdata.Symbol("bottom")]
|
|
153
|
+
)
|
|
154
|
+
sexp.append(effects)
|
|
155
|
+
|
|
156
|
+
# Add UUID
|
|
157
|
+
if "uuid" in label_data:
|
|
158
|
+
sexp.append([sexpdata.Symbol("uuid"), label_data["uuid"]])
|
|
159
|
+
|
|
160
|
+
return sexp
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _hierarchical_label_to_sexp(self, hlabel_data: Dict[str, Any]) -> List[Any]:
|
|
164
|
+
"""Convert hierarchical label to S-expression."""
|
|
165
|
+
sexp = [sexpdata.Symbol("hierarchical_label"), hlabel_data["text"]]
|
|
166
|
+
|
|
167
|
+
# Add shape
|
|
168
|
+
shape = hlabel_data.get("shape", "input")
|
|
169
|
+
sexp.append([sexpdata.Symbol("shape"), sexpdata.Symbol(shape)])
|
|
170
|
+
|
|
171
|
+
# Add position
|
|
172
|
+
pos = hlabel_data["position"]
|
|
173
|
+
x, y = pos["x"], pos["y"]
|
|
174
|
+
rotation = hlabel_data.get("rotation", 0)
|
|
175
|
+
sexp.append([sexpdata.Symbol("at"), x, y, rotation])
|
|
176
|
+
|
|
177
|
+
# Add effects (font properties)
|
|
178
|
+
size = hlabel_data.get("size", config.defaults.font_size)
|
|
179
|
+
effects = [sexpdata.Symbol("effects")]
|
|
180
|
+
font = [sexpdata.Symbol("font"), [sexpdata.Symbol("size"), size, size]]
|
|
181
|
+
effects.append(font)
|
|
182
|
+
|
|
183
|
+
# Use justification from data if provided, otherwise default to "left"
|
|
184
|
+
justify = hlabel_data.get("justify", "left")
|
|
185
|
+
effects.append([sexpdata.Symbol("justify"), sexpdata.Symbol(justify)])
|
|
186
|
+
sexp.append(effects)
|
|
187
|
+
|
|
188
|
+
# Add UUID
|
|
189
|
+
if "uuid" in hlabel_data:
|
|
190
|
+
sexp.append([sexpdata.Symbol("uuid"), hlabel_data["uuid"]])
|
|
191
|
+
|
|
192
|
+
return sexp
|
|
193
|
+
|
|
194
|
+
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Symbol library definitions parser for KiCAD schematics.
|
|
3
|
+
|
|
4
|
+
Handles parsing and serialization of Symbol library definitions.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
|
+
|
|
10
|
+
import sexpdata
|
|
11
|
+
|
|
12
|
+
from ..base import BaseElementParser
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class LibraryParser(BaseElementParser):
|
|
18
|
+
"""Parser for Symbol library definitions."""
|
|
19
|
+
|
|
20
|
+
def __init__(self):
|
|
21
|
+
"""Initialize library parser."""
|
|
22
|
+
super().__init__("library")
|
|
23
|
+
|
|
24
|
+
def _parse_lib_symbols(self, item: List[Any]) -> Dict[str, Any]:
|
|
25
|
+
"""Parse lib_symbols section."""
|
|
26
|
+
# Implementation for lib_symbols parsing
|
|
27
|
+
return {}
|
|
28
|
+
|
|
29
|
+
# Conversion methods from internal format to S-expression
|
|
30
|
+
|
|
31
|
+
def _lib_symbols_to_sexp(self, lib_symbols: Dict[str, Any]) -> List[Any]:
|
|
32
|
+
"""Convert lib_symbols to S-expression."""
|
|
33
|
+
sexp = [sexpdata.Symbol("lib_symbols")]
|
|
34
|
+
|
|
35
|
+
# Add each symbol definition
|
|
36
|
+
for symbol_name, symbol_def in lib_symbols.items():
|
|
37
|
+
if isinstance(symbol_def, list):
|
|
38
|
+
# Raw S-expression data from parsed library file - use directly
|
|
39
|
+
sexp.append(symbol_def)
|
|
40
|
+
elif isinstance(symbol_def, dict):
|
|
41
|
+
# Dictionary format - convert to S-expression
|
|
42
|
+
symbol_sexp = self._create_basic_symbol_definition(symbol_name)
|
|
43
|
+
sexp.append(symbol_sexp)
|
|
44
|
+
|
|
45
|
+
return sexp
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _create_basic_symbol_definition(self, lib_id: str) -> List[Any]:
|
|
49
|
+
"""Create a basic symbol definition for KiCAD compatibility."""
|
|
50
|
+
symbol_sexp = [sexpdata.Symbol("symbol"), lib_id]
|
|
51
|
+
|
|
52
|
+
# Add basic symbol properties
|
|
53
|
+
symbol_sexp.extend(
|
|
54
|
+
[
|
|
55
|
+
[sexpdata.Symbol("pin_numbers"), [sexpdata.Symbol("hide"), sexpdata.Symbol("yes")]],
|
|
56
|
+
[sexpdata.Symbol("pin_names"), [sexpdata.Symbol("offset"), 0]],
|
|
57
|
+
[sexpdata.Symbol("exclude_from_sim"), sexpdata.Symbol("no")],
|
|
58
|
+
[sexpdata.Symbol("in_bom"), sexpdata.Symbol("yes")],
|
|
59
|
+
[sexpdata.Symbol("on_board"), sexpdata.Symbol("yes")],
|
|
60
|
+
]
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Add basic properties for the symbol
|
|
64
|
+
if "R" in lib_id: # Resistor
|
|
65
|
+
symbol_sexp.extend(
|
|
66
|
+
[
|
|
67
|
+
[
|
|
68
|
+
sexpdata.Symbol("property"),
|
|
69
|
+
"Reference",
|
|
70
|
+
"R",
|
|
71
|
+
[sexpdata.Symbol("at"), 2.032, 0, 90],
|
|
72
|
+
[
|
|
73
|
+
sexpdata.Symbol("effects"),
|
|
74
|
+
[sexpdata.Symbol("font"), [sexpdata.Symbol("size"), 1.27, 1.27]],
|
|
75
|
+
],
|
|
76
|
+
],
|
|
77
|
+
[
|
|
78
|
+
sexpdata.Symbol("property"),
|
|
79
|
+
"Value",
|
|
80
|
+
"R",
|
|
81
|
+
[sexpdata.Symbol("at"), 0, 0, 90],
|
|
82
|
+
[
|
|
83
|
+
sexpdata.Symbol("effects"),
|
|
84
|
+
[sexpdata.Symbol("font"), [sexpdata.Symbol("size"), 1.27, 1.27]],
|
|
85
|
+
],
|
|
86
|
+
],
|
|
87
|
+
[
|
|
88
|
+
sexpdata.Symbol("property"),
|
|
89
|
+
"Footprint",
|
|
90
|
+
"",
|
|
91
|
+
[sexpdata.Symbol("at"), -1.778, 0, 90],
|
|
92
|
+
[
|
|
93
|
+
sexpdata.Symbol("effects"),
|
|
94
|
+
[sexpdata.Symbol("font"), [sexpdata.Symbol("size"), 1.27, 1.27]],
|
|
95
|
+
[sexpdata.Symbol("hide"), sexpdata.Symbol("yes")],
|
|
96
|
+
],
|
|
97
|
+
],
|
|
98
|
+
[
|
|
99
|
+
sexpdata.Symbol("property"),
|
|
100
|
+
"Datasheet",
|
|
101
|
+
"~",
|
|
102
|
+
[sexpdata.Symbol("at"), 0, 0, 0],
|
|
103
|
+
[
|
|
104
|
+
sexpdata.Symbol("effects"),
|
|
105
|
+
[sexpdata.Symbol("font"), [sexpdata.Symbol("size"), 1.27, 1.27]],
|
|
106
|
+
[sexpdata.Symbol("hide"), sexpdata.Symbol("yes")],
|
|
107
|
+
],
|
|
108
|
+
],
|
|
109
|
+
]
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
elif "C" in lib_id: # Capacitor
|
|
113
|
+
symbol_sexp.extend(
|
|
114
|
+
[
|
|
115
|
+
[
|
|
116
|
+
sexpdata.Symbol("property"),
|
|
117
|
+
"Reference",
|
|
118
|
+
"C",
|
|
119
|
+
[sexpdata.Symbol("at"), 0.635, 2.54, 0],
|
|
120
|
+
[
|
|
121
|
+
sexpdata.Symbol("effects"),
|
|
122
|
+
[sexpdata.Symbol("font"), [sexpdata.Symbol("size"), 1.27, 1.27]],
|
|
123
|
+
],
|
|
124
|
+
],
|
|
125
|
+
[
|
|
126
|
+
sexpdata.Symbol("property"),
|
|
127
|
+
"Value",
|
|
128
|
+
"C",
|
|
129
|
+
[sexpdata.Symbol("at"), 0.635, -2.54, 0],
|
|
130
|
+
[
|
|
131
|
+
sexpdata.Symbol("effects"),
|
|
132
|
+
[sexpdata.Symbol("font"), [sexpdata.Symbol("size"), 1.27, 1.27]],
|
|
133
|
+
],
|
|
134
|
+
],
|
|
135
|
+
[
|
|
136
|
+
sexpdata.Symbol("property"),
|
|
137
|
+
"Footprint",
|
|
138
|
+
"",
|
|
139
|
+
[sexpdata.Symbol("at"), 0, -1.27, 0],
|
|
140
|
+
[
|
|
141
|
+
sexpdata.Symbol("effects"),
|
|
142
|
+
[sexpdata.Symbol("font"), [sexpdata.Symbol("size"), 1.27, 1.27]],
|
|
143
|
+
[sexpdata.Symbol("hide"), sexpdata.Symbol("yes")],
|
|
144
|
+
],
|
|
145
|
+
],
|
|
146
|
+
[
|
|
147
|
+
sexpdata.Symbol("property"),
|
|
148
|
+
"Datasheet",
|
|
149
|
+
"~",
|
|
150
|
+
[sexpdata.Symbol("at"), 0, 0, 0],
|
|
151
|
+
[
|
|
152
|
+
sexpdata.Symbol("effects"),
|
|
153
|
+
[sexpdata.Symbol("font"), [sexpdata.Symbol("size"), 1.27, 1.27]],
|
|
154
|
+
[sexpdata.Symbol("hide"), sexpdata.Symbol("yes")],
|
|
155
|
+
],
|
|
156
|
+
],
|
|
157
|
+
]
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Add basic graphics and pins (minimal for now)
|
|
161
|
+
symbol_sexp.append([sexpdata.Symbol("embedded_fonts"), sexpdata.Symbol("no")])
|
|
162
|
+
|
|
163
|
+
return symbol_sexp
|
|
164
|
+
|
|
165
|
+
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Title block and symbol instances parser for KiCAD schematics.
|
|
3
|
+
|
|
4
|
+
Handles parsing and serialization of Title block and symbol instances.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
|
+
|
|
10
|
+
import sexpdata
|
|
11
|
+
|
|
12
|
+
from ..base import BaseElementParser
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MetadataParser(BaseElementParser):
|
|
18
|
+
"""Parser for Title block and symbol instances."""
|
|
19
|
+
|
|
20
|
+
def __init__(self):
|
|
21
|
+
"""Initialize metadata parser."""
|
|
22
|
+
super().__init__("metadata")
|
|
23
|
+
|
|
24
|
+
def _parse_title_block(self, item: List[Any]) -> Dict[str, Any]:
|
|
25
|
+
"""Parse title block information."""
|
|
26
|
+
title_block = {}
|
|
27
|
+
for sub_item in item[1:]:
|
|
28
|
+
if isinstance(sub_item, list) and len(sub_item) >= 2:
|
|
29
|
+
key = str(sub_item[0]) if isinstance(sub_item[0], sexpdata.Symbol) else None
|
|
30
|
+
if key:
|
|
31
|
+
title_block[key] = sub_item[1] if len(sub_item) > 1 else None
|
|
32
|
+
return title_block
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _parse_symbol_instances(self, item: List[Any]) -> List[Any]:
|
|
36
|
+
"""Parse symbol_instances section."""
|
|
37
|
+
# For now, just return the raw structure minus the header
|
|
38
|
+
return item[1:] if len(item) > 1 else []
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _title_block_to_sexp(self, title_block: Dict[str, Any]) -> List[Any]:
|
|
42
|
+
"""Convert title block to S-expression."""
|
|
43
|
+
sexp = [sexpdata.Symbol("title_block")]
|
|
44
|
+
|
|
45
|
+
# Add standard fields
|
|
46
|
+
for key in ["title", "date", "rev", "company"]:
|
|
47
|
+
if key in title_block and title_block[key]:
|
|
48
|
+
sexp.append([sexpdata.Symbol(key), title_block[key]])
|
|
49
|
+
|
|
50
|
+
# Add comments with special formatting
|
|
51
|
+
comments = title_block.get("comments", {})
|
|
52
|
+
if isinstance(comments, dict):
|
|
53
|
+
for comment_num, comment_text in comments.items():
|
|
54
|
+
sexp.append([sexpdata.Symbol("comment"), comment_num, comment_text])
|
|
55
|
+
|
|
56
|
+
return sexp
|
|
57
|
+
|
|
58
|
+
|