From 7ee1866b607f5a7227c0fb949fd909f49c1b6334 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Silva Date: Wed, 5 Feb 2014 00:34:57 +0100 Subject: N w modular organization. * parser and render separeted on its own modules. * svn made optional. * PEP-8 compiant. --- gerber/parser.py | 355 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 355 insertions(+) create mode 100644 gerber/parser.py (limited to 'gerber/parser.py') diff --git a/gerber/parser.py b/gerber/parser.py new file mode 100644 index 0000000..cf755d4 --- /dev/null +++ b/gerber/parser.py @@ -0,0 +1,355 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright 2013-2014 Paulo Henrique Silva + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import re +import json + + +class Statement(object): + def __init__(self, type): + self.type = type + + def __str__(self): + s = "<{0} ".format(self.__class__.__name__) + + for key, value in self.__dict__.items(): + s += "{0}={1} ".format(key, value) + + s = s.rstrip() + ">" + return s + + +class ParamStmt(Statement): + def __init__(self, param): + Statement.__init__(self, "PARAM") + self.param = param + + +class FSParamStmt(ParamStmt): + def __init__(self, param, zero="L", notation="A", x="24", y="24"): + ParamStmt.__init__(self, param) + self.zero = zero + self.notation = notation + self.x = x + self.y = y + + +class MOParamStmt(ParamStmt): + def __init__(self, param, mo): + ParamStmt.__init__(self, param) + self.mo = mo + + +class IPParamStmt(ParamStmt): + def __init__(self, param, ip): + ParamStmt.__init__(self, param) + self.ip = ip + + +class OFParamStmt(ParamStmt): + def __init__(self, param, a, b): + ParamStmt.__init__(self, param) + self.a = a + self.b = b + + +class LPParamStmt(ParamStmt): + def __init__(self, param, lp): + ParamStmt.__init__(self, param) + self.lp = lp + + +class ADParamStmt(ParamStmt): + def __init__(self, param, d, shape, modifiers): + ParamStmt.__init__(self, param) + self.d = d + self.shape = shape + self.modifiers = [[x for x in m.split("X")] for m in modifiers.split(",")] + + +class AMParamStmt(ParamStmt): + def __init__(self, param, name, macro): + ParamStmt.__init__(self, param) + self.name = name + self.macro = macro + + +class INParamStmt(ParamStmt): + def __init__(self, param, name): + ParamStmt.__init__(self, param) + self.name = name + + +class LNParamStmt(ParamStmt): + def __init__(self, param, name): + ParamStmt.__init__(self, param) + self.name = name + + +class CoordStmt(Statement): + def __init__(self, function, x, y, i, j, op): + Statement.__init__(self, "COORD") + self.function = function + self.x = x + self.y = y + self.i = i + self.j = j + self.op = op + + +class ApertureStmt(Statement): + def __init__(self, d): + Statement.__init__(self, "APERTURE") + self.d = int(d) + + +class CommentStmt(Statement): + def __init__(self, comment): + Statement.__init__(self, "COMMENT") + self.comment = comment + + +class EofStmt(Statement): + def __init__(self): + Statement.__init__(self, "EOF") + + +class UnknownStmt(Statement): + def __init__(self, line): + Statement.__init__(self, "UNKNOWN") + self.line = line + + +class GerberParser(object): + NUMBER = r"[\+-]?\d+" + DECIMAL = r"[\+-]?\d+([.]?\d+)?" + STRING = r"[a-zA-Z0-9_+\-/!?<>”’(){}.\|&@# :]+" + NAME = "[a-zA-Z_$][a-zA-Z_$0-9]+" + FUNCTION = r"G\d{2}" + + COORD_OP = r"D[0]?[123]" + + FS = r"(?PFS)(?P(L|T))?(?P(A|I))X(?P[0-7][0-7])Y(?P[0-7][0-7])" + MO = r"(?PMO)(?P(MM|IN))" + IP = r"(?PIP)(?P(POS|NEG))" + LP = r"(?PLP)(?P(D|C))" + AD_CIRCLE = r"(?PAD)D(?P\d+)(?PC)[,](?P[^,]*)" + AD_RECT = r"(?PAD)D(?P\d+)(?PR)[,](?P[^,]*)" + AD_OBROUND = r"(?PAD)D(?P\d+)(?PO)[,](?P[^,]*)" + AD_POLY = r"(?PAD)D(?P\d+)(?PP)[,](?P[^,]*)" + AD_MACRO = r"(?PAD)D(?P\d+)+(?P{name})[,](?P[^,]*)".format(name=NAME) + AM = r"(?PAM)(?P{name})\*(?P.*)".format(name=NAME) + + # begin deprecated + OF = r"(?POF)(A(?P{decimal}))?(B(?P{decimal}))?".format(decimal=DECIMAL) + IN = r"(?PIN)(?P.*)" + LN = r"(?PLN)(?P.*)" + # end deprecated + + PARAMS = (FS, MO, IP, LP, AD_CIRCLE, AD_RECT, AD_OBROUND, AD_MACRO, AD_POLY, AM, OF, IN, LN) + PARAM_STMT = [re.compile(r"%{0}\*%".format(p)) for p in PARAMS] + + COORD_STMT = re.compile(( + r"(?P{function})?" + r"(X(?P{number}))?(Y(?P{number}))?" + r"(I(?P{number}))?(J(?P{number}))?" + r"(?P{op})?\*".format(number=NUMBER, function=FUNCTION, op=COORD_OP))) + + APERTURE_STMT = re.compile(r"(G54)?D(?P\d+)\*") + + COMMENT_STMT = re.compile(r"G04(?P{string})(\*)?".format(string=STRING)) + + EOF_STMT = re.compile(r"(?PM02)\*") + + def __init__(self, ctx): + self.statements = [] + self.ctx = ctx + + def parse(self, filename): + fp = open(filename, "r") + data = fp.readlines() + + for stmt in self._parse(data): + self.statements.append(stmt) + self._evaluate(stmt) + + def dump_json(self): + stmts = {"statements": [stmt.__dict__ for stmt in self.statements]} + return json.dumps(stmts) + + def dump_str(self): + s = "" + for stmt in self.statements: + s += str(stmt) + "\n" + return s + + def dump(self): + self.ctx.dump() + + def _parse(self, data): + multiline = None + + for i, line in enumerate(data): + # remove EOL + if multiline: + line = multiline + line.strip() + else: + line = line.strip() + + # skip empty lines + if not len(line): + continue + + # deal with multi-line parameters + if line.startswith("%") and not line.endswith("%"): + multiline = line + continue + else: + multiline = None + + # coord + coords = self._match_many(self.COORD_STMT, line) + if coords: + for coord in coords: + yield CoordStmt(**coord) + continue + + # aperture selection + aperture = self._match_one(self.APERTURE_STMT, line) + if aperture: + yield ApertureStmt(**aperture) + continue + + # comment + comment = self._match_one(self.COMMENT_STMT, line) + if comment: + yield CommentStmt(comment["comment"]) + continue + + # parameter + param = self._match_one_from_many(self.PARAM_STMT, line) + if param: + if param["param"] == "FS": + yield FSParamStmt(**param) + elif param["param"] == "MO": + yield MOParamStmt(**param) + elif param["param"] == "IP": + yield IPParamStmt(**param) + elif param["param"] == "LP": + yield LPParamStmt(**param) + elif param["param"] == "AD": + yield ADParamStmt(**param) + elif param["param"] == "AM": + yield AMParamStmt(**param) + elif param["param"] == "OF": + yield OFParamStmt(**param) + elif param["param"] == "IN": + yield INParamStmt(**param) + elif param["param"] == "LN": + yield LNParamStmt(**param) + else: + yield UnknownStmt(line) + + continue + + # eof + eof = self._match_one(self.EOF_STMT, line) + if eof: + yield EofStmt() + continue + + if False: + print self.COORD_STMT.pattern + print self.APERTURE_STMT.pattern + print self.COMMENT_STMT.pattern + print self.EOF_STMT.pattern + for i in self.PARAM_STMT: + print i.pattern + + yield UnknownStmt(line) + + def _match_one(self, expr, data): + match = expr.match(data) + if match is None: + return {} + else: + return match.groupdict() + + def _match_one_from_many(self, exprs, data): + for expr in exprs: + match = expr.match(data) + if match: + return match.groupdict() + + return {} + + def _match_many(self, expr, data): + result = [] + pos = 0 + while True: + match = expr.match(data, pos) + if match: + result.append(match.groupdict()) + pos = match.endpos + else: + break + + return result + + def _evaluate(self, stmt): + if isinstance(stmt, (CommentStmt, UnknownStmt, EofStmt)): + return + + elif isinstance(stmt, ParamStmt): + self._evaluate_param(stmt) + + elif isinstance(stmt, CoordStmt): + self._evaluate_coord(stmt) + + elif isinstance(stmt, ApertureStmt): + self._evaluate_aperture(stmt) + + else: + raise Exception("Invalid statement to evaluate") + + def _evaluate_param(self, stmt): + if stmt.param == "FS": + self.ctx.set_coord_format(stmt.zero, stmt.x, stmt.y) + self.ctx.set_coord_notation(stmt.notation) + elif stmt.param == "MO:": + self.ctx.set_coord_unit(stmt.mo) + elif stmt.param == "IP:": + self.ctx.set_image_polarity(stmt.ip) + elif stmt.param == "LP:": + self.ctx.set_level_polarity(stmt.lp) + elif stmt.param == "AD": + self.ctx.define_aperture(stmt.d, stmt.shape, stmt.modifiers) + + def _evaluate_coord(self, stmt): + + if stmt.function in ("G01", "G1", "G02", "G2", "G03", "G3"): + self.ctx.set_interpolation(stmt.function) + + if stmt.op == "D01": + self.ctx.stroke(stmt.x, stmt.y) + elif stmt.op == "D02": + self.ctx.move(stmt.x, stmt.y) + elif stmt.op == "D03": + self.ctx.flash(stmt.x, stmt.y) + + def _evaluate_aperture(self, stmt): + self.ctx.set_aperture(stmt.d) -- cgit