from .. import sexpr from ..._utils import run_filters def auto_str(cls): def __str__(self): return str({k: [str(x) for x in v] if isinstance(v, list) else str(v) for k, v in vars(self).items()}) cls.__str__ = __str__ cls.__name__ = cls.__name__ return cls @auto_str class KicadPcb(object): def __init__(self, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) @auto_str class General(object): def __init__(self, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) @auto_str class Module(object): def __init__(self, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) self.fp_texts = self.fp_texts if hasattr(self, "fp_texts") else [] self.attr = self.attr if hasattr(self, "attr") else [] def filter_fp_text(self, kind=None): filters = [] if kind: filters.append(lambda fp_text: fp_text.kind == kind) return (fp_text for fp_text in self.fp_texts if run_filters(filters, fp_text)) def has_attr(self, name): return next((True for a in self.attr if a == name), False) @auto_str class Pad(object): def __init__(self, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) @auto_str class FpText(object): def __init__(self, **kwargs): self.kind = None # type: str self.value = None # type: str for k, v in kwargs.items(): setattr(self, k, v) def parse(path) -> KicadPcb: p = sexpr.parse(path) (e, t) = next(p) assert e == sexpr.EVENT_LPAREN (e, t) = next(p) assert e == sexpr.EVENT_TEXT and t == "kicad_pcb" idx = 0 def _consume(): nonlocal idx idx = idx + 1 # print("consume: idx={}".format(idx)) (event, token) = next(p) # print("consume: event={}".format(event)) while event != sexpr.EVENT_RPAREN: if event == sexpr.EVENT_LPAREN: _consume() elif event == sexpr.EVENT_TEXT: pass (event, token) = next(p) # print("consume: event={}".format(event)) # print("consume: done".format()) idx = idx - 1 def _parse_kicad_pcb(): modules = [] args = {"modules": modules} (event, token) = next(p) while event == sexpr.EVENT_LPAREN: (event, token) = next(p) if token == "version": args[token] = _parse_text(rparen=True) elif token == "general": args[token] = _parse_general() elif token == "module": modules.append(_parse_module()) else: _consume() (event, token) = next(p) return KicadPcb(**args) def _parse_text(optional=False, rparen=False, to=None): (event, token) = next(p) if not optional: assert event == sexpr.EVENT_TEXT else: assert event == sexpr.EVENT_TEXT or event == sexpr.EVENT_RPAREN if event == sexpr.EVENT_RPAREN: return None text = token if rparen: (event, token) = next(p) assert event == sexpr.EVENT_RPAREN return to(text) if to else text def _parse_general(): args = {} (event, token) = next(p) while event == sexpr.EVENT_LPAREN: (event, token) = next(p) if token == "no_connects": args[token] = _parse_text(to=int, rparen=True) elif token == "area": args[token] = (_parse_text(to=float), _parse_text(to=float), _parse_text(to=float), _parse_text(to=float, rparen=True)) elif token == "thickness": args[token] = _parse_text(rparen=True) else: _consume() (event, token) = next(p) return General(**args) def _parse_module(): pads = [] fp_texts = [] args = { "footprint": _parse_text(), "pads": pads, "fp_texts": fp_texts, "layer": None, "tedit": None, "tstamp": None, } (event, token) = next(p) while event == sexpr.EVENT_TEXT: (event, token) = next(p) while event == sexpr.EVENT_LPAREN: (event, token) = next(p) if token in ["layer", "tedit", "tstamp"]: args[token] = _parse_text(rparen=True) elif token == "at": args[token] = _parse_at() elif token == "attr": args[token] = [_parse_text(rparen=True)] elif token == "pad": pads.append(_parse_pad()) elif token == "fp_text": fp_texts.append(_parse_fp_text()) else: _consume() (event, token) = next(p) return Module(**args) def _parse_pad(): texts = [] args = { "footprint": _parse_text() } (event, token) = next(p) while event == sexpr.EVENT_TEXT: texts.append(token) (event, token) = next(p) while event == sexpr.EVENT_LPAREN: (event, token) = next(p) if token == "at": args[token] = _parse_at() if token == "size": args[token] = (_parse_text(to=float), _parse_text(to=float)) else: _consume() return Pad(**args) def _parse_fp_text() -> FpText: args = { "kind": _parse_text(), "value": _parse_text(), } (event, token) = next(p) while event == sexpr.EVENT_LPAREN: (event, token) = next(p) if token == "at": args[token] = _parse_at() if token == "layer": args[token] = _parse_text() else: _consume() return FpText(**args) def _parse_at(): x = _parse_text(to=float) y = _parse_text(to=float) rot = _parse_text(to=float, optional=True, rparen=True) return x, y, rot or 0 kicad_pcb = _parse_kicad_pcb() assert next(p, None) is None return kicad_pcb