61a44f0a06fb293b8f141a295b6c9d5e787090a0
Stefan Schuermans PCB element Python types

Stefan Schuermans authored 3 years ago

1) """
2) Parser for GNU PCB files.
3) """
4) 
5) import re
6) 
7) import pcb_types
8) 
9) 
10) class ParseError(Exception):
11)     def __init__(self, msg: str, pos: str):
12)         super().__init__(self, msg, pos)
13)         self.msg = msg
14)         self.pos = pos
15) 
16)     def __repr__(self) -> str:
17)         return f'ParseError({repr(self.msg):s}, {repr(self.pos):s})'
18) 
19)     def __str__(self) -> str:
20)         return f'ParseError: {self.msg:s} at {self.pos:s}'
21) 
22) 
23) class StringBuffer:
24)     """
25)     Store a string, an index and provide some operations.
26)     """
27) 
28)     def __init__(self, string: str):
29)         self._string = string
30)         self._index = 0
31)         self._stack = []
32) 
33)     def __enter__(self):
34)         self.enter()
35)         return self
36) 
37)     def __exit__(self, exc_type, _exc_value, _traceback):
38)         if exc_type is None:
39)             self.complete()
40)         else:
41)             self.backtrack()
42) 
43)     def advance(self, length: int):
44)         """
45)         Advance the index by length.
46)         """
47)         self._index = min(len(self._string), self._index + length)
48) 
49)     def backtrack(self):
50)         """
51)         Track back from parsing something.
52)         Pop index from stack and use it as the new current index.
53)         """
54)         self._index = self._stack[-1]
55)         del self._stack[-1]
56) 
57)     def check(self, s: str) -> bool:
58)         """
59)         Check if string s follows index.
60)         If so, advance by length of s and return True
61)         Otherwise, keep index and return False.
62)         """
63)         if self.peek(len(s)) == s:
64)             self.advance(len(s))
65)             return True
66)         return False
67) 
68)     def complete(self):
69)         """
70)         Complete parsing something.
71)         Pop index from stack and throw it away.
72)         """
73)         del self._stack[-1]
74) 
75)     def enter(self):
76)         """
77)         Enter parsing something.
78)         Push current index onto stack.
79)         """
80)         self._stack.append(self._index)
81) 
82)     def expect(self, s: str):
83)         """
84)         Check that string s follows index.
85)         If so, advance.
86)         If not, throw ParseError.
87)         """
88)         if not self.check(s):
89)             raise ParseError(f'expected {repr(s):s}', self.pos())
90) 
91)     def get(self, length: int) -> str:
92)         """
93)         Return the next length characters and advance the index.
94)         """
95)         s = self.peek(length)
96)         self.advance(length)
97)         return s
98) 
99)     def getRe(self, ptrn: re.Pattern):
100)         """
101)         Return the matching the regular expression pattern and advance
102)         index or throw ParseError.
103)         """
104)         m = ptrn.match(self._string[self._index:])
105)         if not m:
106)             raise ParseError(f'expected pattern {repr(ptrn.pattern):s}',
107)                              self.pos())
108)         self._index += len(m.group(0))
109)         return m
110) 
111)     def peek(self, length: int) -> str:
112)         """
113)         Return the next length characters without advancing the index.
114)         """
115)         return self._string[self._index:self._index + length]
116) 
117)     def pos(self) -> str:
118)         """
119)         Get string describing current position.
120)         """
121)         before = self._string[:self._index].split('\n')
122)         after = self._string[self._index:].split('\n')
123)         line_no = len(before)
124)         column_no = len(before[-1]) + 1
125)         rest = after[0]
126)         return f'line {line_no:d} column {column_no:d} {repr(rest):s}'
127) 
128) 
129) class PcbBaseParser(StringBuffer):
130)     """
131)     Parser for the basic element of GNU PCB files.
132)     """
133) 
134)     re_float = re.compile(r'^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)'
135)                           r'(?:[]eE[+-]?[0-9]\+)?')
136)     re_int = re.compile(r'^[+-]?[0-9]+')
137)     re_quoted = re.compile(r'^"([^"]*)"')
138)     re_space = re.compile(r'^\s+', re.MULTILINE)
139)     re_space_opt = re.compile(r'^\s*', re.MULTILINE)
140) 
141)     known_string_flags = set([
142)         'edge2', 'hole', 'nopaste', 'octagon', 'onsolder', 'pin', 'showname',
143)         'square', 'via'
144)     ])
145) 
Stefan Schuermans PCB coordinate type

Stefan Schuermans authored 3 years ago

146)     def parseCoord(self) -> pcb_types.Coordinate:
Stefan Schuermans PCB element Python types

Stefan Schuermans authored 3 years ago

147)         """
148)         Parse a coordinate.
149)         """
150)         with self:
151)             f = self.parseFloat()
Stefan Schuermans PCB coordinate type

Stefan Schuermans authored 3 years ago

152)             c = pcb_types.Coordinate(f)
Stefan Schuermans PCB element Python types

Stefan Schuermans authored 3 years ago

153)             if self.check('mil'):
Stefan Schuermans PCB coordinate type

Stefan Schuermans authored 3 years ago

154)                 c.mil = f
Stefan Schuermans PCB element Python types

Stefan Schuermans authored 3 years ago

155)             elif self.check('mm'):
Stefan Schuermans PCB coordinate type

Stefan Schuermans authored 3 years ago

156)                 c.mm = f
157)             return c