kopia lustrzana https://gitlab.com/gerbolyze/gerbonara
Working version of Gerber RS-274X parser.
rodzic
e53c6bba9b
commit
c1518b5dfe
|
@ -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.
|
||||
|
||||
|
|
|
@ -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}
|
|
@ -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)
|
Ładowanie…
Reference in New Issue