From efe5d835eaa26d8696c3352c4e1ed42da16fe27b Mon Sep 17 00:00:00 2001
From: Trygve Laugstøl <trygvis@inamo.no>
Date: Thu, 7 Dec 2017 23:48:02 +0100
Subject: wip

---
 src/ee/kicad/__init__.py         |   2 -
 src/ee/kicad/_parse_kicad_pcb.py | 146 ---------------------------------------
 src/ee/kicad/pcb/__init__.py     | 128 ++++++++++++++++++++++++++++++++++
 src/ee/kicad/sexpr/__init__.py   | 123 +++++++++++++++++++++++++++++++++
 4 files changed, 251 insertions(+), 148 deletions(-)
 delete mode 100644 src/ee/kicad/_parse_kicad_pcb.py
 create mode 100644 src/ee/kicad/pcb/__init__.py
 create mode 100644 src/ee/kicad/sexpr/__init__.py

(limited to 'src/ee/kicad')

diff --git a/src/ee/kicad/__init__.py b/src/ee/kicad/__init__.py
index efc20a2..8d5acd1 100644
--- a/src/ee/kicad/__init__.py
+++ b/src/ee/kicad/__init__.py
@@ -4,12 +4,10 @@ from ee import EeException
 from ee.kicad.read_schematic import read_schematic, read_schematics
 from ee.kicad.to_bom import to_bom, to_bom_xml
 from .model import *
-from ._parse_kicad_pcb import parse_kicad_pcb
 
 __all__ = [
     "Component",
     "ComponentField",
-    "parse_kicad_pcb",
     "Position",
     "read_schematic",
     "read_schematics",
diff --git a/src/ee/kicad/_parse_kicad_pcb.py b/src/ee/kicad/_parse_kicad_pcb.py
deleted file mode 100644
index 5ea4d1b..0000000
--- a/src/ee/kicad/_parse_kicad_pcb.py
+++ /dev/null
@@ -1,146 +0,0 @@
-EVENT_START = 1
-EVENT_END = 2
-EVENT_LPAREN = 3
-EVENT_RPAREN = 4
-EVENT_TEXT = 5
-
-def state_to_str(s):
-    if s == EVENT_START:
-        return "EVENT_START"
-    if s == EVENT_END:
-        return "EVENT_END"
-    if s == EVENT_LPAREN:
-        return "EVENT_LPAREN"
-    if s == EVENT_RPAREN:
-        return "EVENT_RPAREN"
-    if s == EVENT_TEXT:
-        return "EVENT_TEXT"
-    return "UNKNOWN STATE"
-
-def parse_kicad_pcb(path):
-    class state(object):
-        def __init__(self):
-            self.f = open(path, "r")#, 128 * 1024)
-            self.state = EVENT_START
-            self.buffer = []
-
-        def text(self):
-            return self._text
-
-        def close(self):
-            if self.f:
-                f = self.f
-                self.f = None
-                f.close()
-
-        def _read_c(self):
-            # TODO: count lines and characters
-            if self.buffer:
-                c = self.buffer[0]
-                while c == '\n':
-                    del self.buffer[0]
-                    c = self.buffer[0]
-                return c
-
-            c = self.f.read(1024)
-            if not c:
-                return c
-
-            self.buffer.extend(c)
-            return self._read_c()
-
-#            if self.buffer:
-#                c = self.buffer[0]
-#                del self.buffer[0]
-#                return c
-#
-#            c = self.f.read(1)
-#            while c and c == '\n':
-#                c = self.f.read(1)
-#            return c
-
-        def _unread(self, s):
-            for c in s:
-                self.buffer.append(c)
-#            print("unreading: '{}', buffer={}".format(s, str(self.buffer)))
-
-        def _read_token(self):
-            s = ""
-            while True:
-                c = self._read_c()
-
-                if not c:
-                    return s if s else None
-
-                if c == " ":
-                    # Ignore leading blanks
-                    if not s:
-                        continue
-
-                if c == "(" or c == ")":
-                    if not s:
-                        return c
-                    self._unread(c)
-                    return s.rstrip()
-
-                if c == " ":
-                    return s
-
-                if c == "\"":
-                    while c:
-                        c = self._read_c()
-
-                        if c == "\"":
-                          break
-
-                        s += c
-                    return s
-                s += c
-
-            return s.rstrip()
-
-        def next(self):
-            s = self.state
-#            print("next: current state={}, buffer={}".format(self.state_to_str(s), self.buffer))
-            self._text = None
-            if s == EVENT_END:
-                raise Exception("Invalid state, state=END")
-
-            token = self._read_token()
-#            print("token={}".format(token))
-
-            if token is None:
-                self.state = EVENT_END
-                self.close()
-                return (EVENT_END, None)
-
-            if token == "(":
-                self.state = EVENT_LPAREN
-                return (EVENT_LPAREN, None)
-            if token == ")":
-                self.state = EVENT_RPAREN
-                return (EVENT_RPAREN, None)
-
-            self._text = token
-            self.state = EVENT_TEXT
-            return (EVENT_TEXT, token)
-
-    s = state()
-    (event, token) = s.next()
-    while event != EVENT_END:
-        yield (event, token)
-        (event, token) = s.next()
-
-#indent = 0
-#for (event_type, text) in parse_kicad_pcb("alpha.kicad_pcb"):
-#    if event_type == EVENT_LPAREN:
-#        prefix = " " * indent
-#        indent = indent + 1
-#        print(prefix + "(")
-#    elif event_type == EVENT_RPAREN:
-#        indent = indent - 1
-#        prefix = " " * indent
-#        print(prefix + ")")
-#    elif event_type == EVENT_TEXT:
-#        prefix = " " * indent
-#        print("{}{}".format(prefix, text))
diff --git a/src/ee/kicad/pcb/__init__.py b/src/ee/kicad/pcb/__init__.py
new file mode 100644
index 0000000..bfd352f
--- /dev/null
+++ b/src/ee/kicad/pcb/__init__.py
@@ -0,0 +1,128 @@
+from .. import sexpr
+
+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__
+    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)
+
+def parse(path):
+    count = 0
+    p = sexpr.parse(path)
+    p = sexpr.logging_parser(p)
+    (event, token) = next(p)
+    assert event == sexpr.EVENT_LPAREN
+
+    (event, token) = next(p)
+    assert event == sexpr.EVENT_TEXT and token == "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):
+        (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 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(rparen = True) 
+            elif token == "area":
+                args[token] = (_parse_text(), _parse_text(), _parse_text(), _parse_text(rparen = True))
+            elif token == "thickness":
+                args[token] = _parse_text(rparen = True)
+            else:
+                _consume()
+            (event, token) = next(p)
+        return General(**args)
+
+    def _parse_module():
+        args = {}
+
+        args["footprint"] = _parse_text()
+
+        (event, token) = next(p)
+        while event == sexpr.EVENT_LPAREN:
+            (event, token) = next(p)
+            if token == "layer":
+                args[token] = _parse_text(rparen = True) 
+            elif token == "at":
+                x = _parse_text()
+                y = _parse_text()
+                rot = _parse_text(optional = True, rparen = True)
+                args[token] = (x, y, rot or 0)
+            else:
+                _consume()
+            (event, token) = next(p)
+        return Module(**args)
+
+    kicad_pcb = _parse_kicad_pcb()
+#    (event, token) = next(p)
+#    assert event == sexpr.EVENT_END
+    return kicad_pcb
diff --git a/src/ee/kicad/sexpr/__init__.py b/src/ee/kicad/sexpr/__init__.py
new file mode 100644
index 0000000..a8604dc
--- /dev/null
+++ b/src/ee/kicad/sexpr/__init__.py
@@ -0,0 +1,123 @@
+EVENT_START = 1
+EVENT_END = 2
+EVENT_LPAREN = 3
+EVENT_RPAREN = 4
+EVENT_TEXT = 5
+
+def state_to_str(s):
+    if s == EVENT_START:
+        return "EVENT_START"
+    if s == EVENT_END:
+        return "EVENT_END"
+    if s == EVENT_LPAREN:
+        return "EVENT_LPAREN"
+    if s == EVENT_RPAREN:
+        return "EVENT_RPAREN"
+    if s == EVENT_TEXT:
+        return "EVENT_TEXT"
+    return "UNKNOWN STATE"
+
+def logging_parser(parser):
+    for (event, token) in parser:
+        if token:
+            print("SEXPR: event={}, token={}".format(state_to_str(event), token or ""))
+        else:
+            print("SEXPR: event={}".format(state_to_str(event)))
+        yield (event, token)
+
+def parse(path):
+    class state(object):
+        def __init__(self):
+            self.f = open(path, "r")#, 128 * 1024)
+            self.state = EVENT_START
+            self.buffer = []
+
+        def text(self):
+            return self._text
+
+        def close(self):
+            if self.f:
+                f = self.f
+                self.f = None
+                f.close()
+
+        def _read_c(self):
+            # TODO: count lines and characters
+            if self.buffer:
+                c = self.buffer[0]
+                del self.buffer[0]
+                return c
+
+            c = self.f.read(1)
+            while c and c == '\n':
+                c = self.f.read(1)
+            return c
+
+        def _unread(self, s):
+            for c in s:
+                self.buffer.append(c)
+
+        def _read_token(self):
+            s = ""
+            while True:
+                c = self._read_c()
+
+                if not c:
+                    return s if s else None
+
+                if c == " ":
+                    # Ignore leading blanks
+                    if not s:
+                        continue
+
+                if c == "(" or c == ")":
+                    if not s:
+                        return c
+                    self._unread(c)
+                    return s.rstrip()
+
+                if c == " ":
+                    return s
+
+                if c == "\"":
+                    while c:
+                        c = self._read_c()
+
+                        if c == "\"":
+                          break
+
+                        s += c
+                    return s
+                s += c
+
+            return s.rstrip()
+
+        def next(self):
+            s = self.state
+            self._text = None
+            if s == EVENT_END:
+                raise Exception("Invalid state, state=END")
+
+            token = self._read_token()
+
+            if token is None:
+                self.state = EVENT_END
+                self.close()
+                return (EVENT_END, None)
+
+            if token == "(":
+                self.state = EVENT_LPAREN
+                return (EVENT_LPAREN, None)
+            if token == ")":
+                self.state = EVENT_RPAREN
+                return (EVENT_RPAREN, None)
+
+            self._text = token
+            self.state = EVENT_TEXT
+            return (EVENT_TEXT, token)
+
+    s = state()
+    (event, token) = s.next()
+    while event != EVENT_END:
+        yield (event, token)
+        (event, token) = s.next()
-- 
cgit v1.2.3