diff options
Diffstat (limited to 'kicad/lib/tools/derive_symbol.py')
| -rwxr-xr-x | kicad/lib/tools/derive_symbol.py | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/kicad/lib/tools/derive_symbol.py b/kicad/lib/tools/derive_symbol.py new file mode 100755 index 0000000..1278128 --- /dev/null +++ b/kicad/lib/tools/derive_symbol.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +"""Add a pin-compatible variant of a symbol to a KiCad symbol library. + +The variant keeps the geometry of the base symbol (every pin number at the +same position), so "Change symbol" in the schematic swaps the two without +breaking wires. Pin names and the description come from the variant's kipart +CSV (as written by lattice2kipart.py). + + derive_symbol.py LIB.kicad_sym BASE_NAME VARIANT.csv > OUT.kicad_sym +""" +import csv +import re +import sys + + +def match_paren(t, i): + depth = 0 + j = i + while True: + c = t[j] + if c == '"': + j += 1 + while t[j] != '"': + j += 2 if t[j] == "\\" else 1 + elif c == "(": + depth += 1 + elif c == ")": + depth -= 1 + if depth == 0: + return j + 1 + j += 1 + + +lib_path, base, var_csv = sys.argv[1:4] +lib = open(lib_path).read() + +rows = list(csv.reader(open(var_csv, newline=""))) +variant = rows[0][0] +props = {r[0][:-1].replace("*", ""): r[1] for r in rows[1:] if r and r[0].endswith(":")} +hdr = next(i for i, r in enumerate(rows) if r and r[0] == "pin") +cols = rows[hdr] +pins = {r[cols.index("pin")]: (r[cols.index("name")], r[cols.index("type")]) + for r in rows[hdr + 1:] if r and r[0]} + +m = re.search(r'\(symbol "' + re.escape(base) + r'"', lib) +start = m.start() +block = lib[start:match_paren(lib, start)] +base_types = {n: t for t, n in re.findall(r'\(pin (\w+) \w+ .*?\(number "([^"]+)"', block, re.S)} + +new = block.replace(f'(symbol "{base}"', f'(symbol "{variant}"', 1) +new = new.replace(f'(symbol "{base}_', f'(symbol "{variant}_') +new = re.sub(r'(\(property "Value" )"[^"]*"', rf'\1"{variant}"', new, count=1) +for key in ("Description", "Source"): + if key in props: + new = re.sub(r'(\(property "' + key + r'" )"[^"]*"', lambda mm: f'{mm.group(1)}"{props[key]}"', + new, count=1) + + +def rename(mm): + num = mm.group(3) + name, typ = pins[num] + if typ != base_types[num] and not (base_types[num] == "passive"): # stacked pins are passive + sys.exit(f"pin {num}: type {typ} differs from {base_types[num]}") + return f'{mm.group(1)}(name "{name}"{mm.group(2)}(number "{num}"' + + +new, n = re.subn(r'(\(pin [^\n]*\n(?:(?!\(pin ).)*?)\(name "[^"]*"(.*?)\(number "([^"]+)"', + rename, new, flags=re.S) +if n != len(pins): + sys.exit(f"renamed {n} pins, variant has {len(pins)}") + +end = match_paren(lib, start) +sys.stdout.write(lib[:end] + "\n " + new + lib[end:]) |
