Working version of Gerber RS-274X parser.

refactor
Paulo Henrique Silva 2013-12-17 18:05:30 -02:00
rodzic e53c6bba9b
commit c1518b5dfe
3 zmienionych plików z 209 dodań i 0 usunięć

Wyświetl plik

@ -1,2 +1,9 @@
gerber-tools
============
This hopefully will be a useful set of tools to handle Gerber files in Python.
Right now we have a working parser and I am working on simple Gerber to SVG converter.
See gerber.md for some random information regardind Gerber format.

73
gerber.md 100644
Wyświetl plik

@ -0,0 +1,73 @@
# Gerber (RS-274X or Extended Gerber) is a bilevel, resolution independent image format.
# // graphic objects
# // draw: line segment, thickness, round or square line endings. (solid circle and rectangule apertures only)
# // arc: circular arc, thickness, round endings. (solid circle standard aperture only)
# // flash: replication of a given apertura (shape)
# // region: are defined by a countour (linear/arc segments.)
#
# // draw/arc: can have zero length (just flash the aperture)
# // flash: any aperture can be flashed
#
# // operation codes operates on coordinate data blocks. each operation code is for one coordinate data block pair and vice-versa.
# // D01: stroke an aperture from current point to coordinate pair. region mode off. lights-on move.
# // D02: move current point to this coordinate pair
# // D03: flash current aperture at this coordinate pair.
#
# // graphics state
# // all state controlled by codes and parameters, except current point
# //
# // state fixed? initial value
# // coordinate format fixed undefined
# // unit fixed undefined
# // image polarity fixed positive
# // steps/repeat variable 1,1,-,-
# // level polarity variable dark
# // region mode variable off
# // current aperture variable undefined
# // quadrant mode variable undefined
# // interpolation mode variable undefined
# // current point variable (0,0)
#
# // attributes: metadata, both standard and custom. No change on image.
#
# // G01: linear
# // G04: comment
# // M02: end of file
# // D: select aperture
# // G75: multi quadrant mode (circles)
# // G36: region begin
# // G37: region end
#
# // [G01] [Xnnfffff] [Ynnffff] D01*
#
# // ASCII 32-126, CR LF.
# // * end-of-block
# // % parameer delimiter
# // , field separator
# // <space> only in comments
# // case sensitive
#
# // int: +/- 32 bit signed
# // decimal: +/- digits
# // names: [a-zA-Z_$]{[a-zA-Z_$0-9]+} (255)
# // strings: [a-zA-Z0-9_+-/!?<>”’(){}.\|&@# ]+ (65535)
#
# // data block: end in *
# // statement: one or more data block, if contain parameters starts and end in % (parameter statement)
# // statement: [%]<Data Block>{<Data Block>}[%]
# // statements: function code, coordinate data, parameters
#
# // function code: operation codes (D01..) or code that set state.
# // function codes applies before operation codes act on coordinates
#
# // coordinate data: <Coordinate data>: [X<Number>][Y<Number>][I<Number>][J<Number>](D01|D02|D03)
# // offsets are not modal
#
# // parameter: %Parameter code<required modifiers>[optional modifiers]*%
# // code: 2 characters
#
# // parameters can have line separators: %<Parameter>{{<Line separator>}<Parameter>}%
#
# // function code: (GDM){1}[number], parameters: [AZ]{2}

129
gerber.py 100644
Wyświetl plik

@ -0,0 +1,129 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re
def red(s):
return '\033[1;31m{0}\033[0;m'.format(s)
class Statement:
pass
class ParamStmt(Statement):
pass
class CoordStmt(Statement):
pass
class ApertureStmt(Statement):
pass
class CommentStmt(Statement):
def __init__(self, comment):
self.comment = comment
class EofStmt(Statement):
pass
class UnexpectedStmt(Statement):
def __init__(self, line):
self.line = line
class GerberContext:
x = 0
y = 0
class Gerber:
NUMBER = r"[\+-]?\d+"
FUNCTION = r"G\d{2}"
STRING = r"[a-zA-Z0-9_+-/!?<>”’(){}.\|&@# :]+"
COORD_OP = r"D[0]?[123]"
PARAM_STMT = re.compile(r"%.*%")
COORD_STMT = re.compile((
r"(?P<f>{f})?"
r"(X(?P<x>{number}))?(Y(?P<y>{number}))?"
r"(I(?P<i>{number}))?(J(?P<j>{number}))?"
r"(?P<op>{op})?\*".format(number=NUMBER, f=FUNCTION, op=COORD_OP)))
APERTURE_STMT = re.compile(r"(G54)?D\d+\*")
COMMENT_STMT = re.compile(r"G04(?P<comment>{string})\*".format(string=STRING))
EOF_STMT = re.compile(r"M02\*")
def __init__(self):
self.apertures = {}
self.ctx = GerberContext()
def parse(self, filename):
fp = open(filename, "r")
data = fp.readlines()
self.tokens = list(self.tokenize(data))
for token in self.tokens:
if isinstance(token, UnexpectedStmt):
print filename
print red("[UNEXPECTED TOKEN]")
print self.COORD_STMT.pattern
print token.line
def tokenize(self, data):
multiline = None
for i, line in enumerate(data):
# remove EOL
if multiline:
line = multiline + line.strip()
else:
line = line.strip()
# deal with multi-line parameters
if line.startswith("%") and not line.endswith("%"):
multiline = line
continue
else:
multiline = None
# parameter
match = self.PARAM_STMT.match(line)
if match:
yield ParamStmt()
continue
# coord
matches = self.COORD_STMT.finditer(line)
if matches:
for match in matches:
yield CoordStmt()
continue
# aperture selection
match = self.APERTURE_STMT.match(line)
if match:
yield ApertureStmt()
continue
# comment
match = self.COMMENT_STMT.match(line)
if match:
yield CommentStmt(match.groupdict("comment"))
continue
# eof
match = self.EOF_STMT.match(line)
if match:
yield EofStmt()
continue
yield UnexpectedStmt(line)
if __name__ == "__main__":
import sys
for f in sys.argv[1:]:
g = Gerber()
g.parse(f)