From 670d3fbbd7ebfb69bd223ac30b73ec47b195b380 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Silva Date: Tue, 3 Mar 2015 03:41:55 -0300 Subject: Add aperture macro parsing and evaluation. Aperture macros can get complex with arithmetical operations, variables and variables substitution. Current pcb-tools code just read each macro block as an independent unit, this cannot deal with variables that get changed after used. This patch splits the task in two: first we parse all macro content and creates a bytecode representation of all operations. This bytecode representation will be executed when an AD command is issues passing the required parameters. Parsing is heavily based on gerbv using a Shunting Yard approach to math parsing. Integration with rs274x.py code is not finished as I need to figure out how to integrate the final macro primitives with the graphical primitives already in use. --- gerber/am_eval.py | 106 ++++++++++++++++++++ gerber/am_read.py | 229 ++++++++++++++++++++++++++++++++++++++++++++ gerber/am_statements.py | 8 +- gerber/gerber_statements.py | 58 ++++++----- gerber/rs274x.py | 20 +++- 5 files changed, 390 insertions(+), 31 deletions(-) create mode 100644 gerber/am_eval.py create mode 100644 gerber/am_read.py (limited to 'gerber') diff --git a/gerber/am_eval.py b/gerber/am_eval.py new file mode 100644 index 0000000..29b380d --- /dev/null +++ b/gerber/am_eval.py @@ -0,0 +1,106 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# copyright 2014 Hamilton Kibbe +# copyright 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. +""" This module provides RS-274-X AM macro evaluation. +""" + +class OpCode: + PUSH = 1 + LOAD = 2 + STORE = 3 + ADD = 4 + SUB = 5 + MUL = 6 + DIV = 7 + PRIM = 8 + + @staticmethod + def str(opcode): + if opcode == OpCode.PUSH: + return "OPCODE_PUSH" + elif opcode == OpCode.LOAD: + return "OPCODE_LOAD" + elif opcode == OpCode.STORE: + return "OPCODE_STORE" + elif opcode == OpCode.ADD: + return "OPCODE_ADD" + elif opcode == OpCode.SUB: + return "OPCODE_SUB" + elif opcode == OpCode.MUL: + return "OPCODE_MUL" + elif opcode == OpCode.DIV: + return "OPCODE_DIV" + elif opcode == OpCode.PRIM: + return "OPCODE_PRIM" + else: + return "UNKNOWN" + +def eval_macro(instructions, parameters={}): + + if not isinstance(parameters, type({})): + p = {} + for i, val in enumerate(parameters): + p[i+1] = val + + parameters = p + + stack = [] + def pop(): + return stack.pop() + + def push(op): + stack.append(op) + + def top(): + return stack[-1] + + def empty(): + return len(stack) == 0 + + for opcode, argument in instructions: + if opcode == OpCode.PUSH: + push(argument) + + elif opcode == OpCode.LOAD: + push(parameters.get(argument, 0)) + + elif opcode == OpCode.STORE: + parameters[argument] = pop() + + elif opcode == OpCode.ADD: + op1 = pop() + op2 = pop() + push(op2 + op1) + + elif opcode == OpCode.SUB: + op1 = pop() + op2 = pop() + push(op2 - op2) + + elif opcode == OpCode.MUL: + op1 = pop() + op2 = pop() + push(op2 * op1) + + elif opcode == OpCode.DIV: + op1 = pop() + op2 = pop() + push(op2 / op1) + + elif opcode == OpCode.PRIM: + yield "%d,%s" % (argument, ",".join([str(x) for x in stack])) + stack = [] diff --git a/gerber/am_read.py b/gerber/am_read.py new file mode 100644 index 0000000..d1201b2 --- /dev/null +++ b/gerber/am_read.py @@ -0,0 +1,229 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +# copyright 2014 Hamilton Kibbe +# copyright 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. +""" This module provides RS-274-X AM macro modifiers parsing. +""" + +from .am_eval import OpCode, eval_macro + +import string + + +class Token: + ADD = "+" + SUB = "-" + MULT = ("x", "X") # compatibility as many gerber writes do use non compliant X + DIV = "/" + OPERATORS = (ADD, SUB, MULT[0], MULT[1], DIV) + LEFT_PARENS = "(" + RIGHT_PARENS = ")" + EQUALS = "=" + + +def token_to_opcode(token): + if token == Token.ADD: + return OpCode.ADD + elif token == Token.SUB: + return OpCode.SUB + elif token in Token.MULT: + return OpCode.MUL + elif token == Token.DIV: + return OpCode.DIV + else: + return None + + +def precedence(token): + if token == Token.ADD or token == Token.SUB: + return 1 + elif token in Token.MULT or token == Token.DIV: + return 2 + else: + return 0 + + +def is_op(token): + return token in Token.OPERATORS + + +class Scanner: + def __init__(self, s): + self.buff = s + self.n = 0 + + def eof(self): + return self.n == len(self.buff) + + def peek(self): + if not self.eof(): + return self.buff[self.n] + return "" + + def ungetc(self): + if self.n > 0: + self.n -= 1 + + def getc(self): + if self.eof(): + return "" + + c = self.buff[self.n] + self.n += 1 + return c + + def readint(self): + n = "" + while not self.eof() and (self.peek() in string.digits): + n += self.getc() + return int(n) + + def readfloat(self): + n = "" + while not self.eof() and (self.peek() in string.digits or self.peek() == "."): + n += self.getc() + return float(n) + + def readstr(self, end="*"): + s = "" + while not self.eof() and self.peek() != end: + s += self.getc() + return s.strip() + + +def read_macro(macro): + + instructions = [] + + for block in macro.split("*"): + + is_primitive = False + is_equation = False + + found_equation_left_side = False + found_primitive_code = False + + equation_left_side = 0 + primitive_code = 0 + + if Token.EQUALS in block: + is_equation = True + else: + is_primitive = True + + scanner = Scanner(block) + + # inlined here for compactness and convenience + op_stack = [] + + def pop(): + return op_stack.pop() + + def push(op): + op_stack.append(op) + + def top(): + return op_stack[-1] + + def empty(): + return len(op_stack) == 0 + + while not scanner.eof(): + + c = scanner.getc() + + if c == ",": + found_primitive_code = True + + # add all instructions on the stack to finish last modifier + while not empty(): + instructions.append((token_to_opcode(pop()), None)) + + elif c in Token.OPERATORS: + while not empty() and is_op(top()) and precedence(top()) >= precedence(c): + instructions.append((token_to_opcode(pop()), None)) + + push(c) + + elif c == Token.LEFT_PARENS: + push(c) + + elif c == Token.RIGHT_PARENS: + while not empty() and top() != Token.LEFT_PARENS: + instructions.append((token_to_opcode(pop()), None)) + + if empty(): + raise ValueError("unbalanced parentheses") + + # discard "(" + pop() + + elif c.startswith("$"): + n = scanner.readint() + + if is_equation and not found_equation_left_side: + equation_left_side = n + else: + instructions.append((OpCode.LOAD, n)) + + elif c == Token.EQUALS: + found_equation_left_side = True + + elif c == "0": + if is_primitive and not found_primitive_code: + instructions.append((OpCode.PUSH, scanner.readstr("*"))) + else: + # decimal or integer disambiguation + if scanner.peek() not in '.': + instructions.append((OpCode.PUSH, 0)) + + elif c in "123456789.": + scanner.ungetc() + + if is_primitive and not found_primitive_code: + primitive_code = scanner.readint() + else: + instructions.append((OpCode.PUSH, scanner.readfloat())) + + else: + # whitespace or unknown char + pass + + # add all instructions on the stack to finish last modifier (if any) + while not empty(): + instructions.append((token_to_opcode(pop()), None)) + + # at end, we either have a primitive or a equation + if is_primitive: + instructions.append((OpCode.PRIM, primitive_code)) + + if is_equation: + instructions.append((OpCode.STORE, equation_left_side)) + + return instructions + +if __name__ == '__main__': + import sys + + instructions = read_macro(sys.argv[1]) + + print "insructions:" + for opcode, argument in instructions: + print "%s %s" % (OpCode.str(opcode), str(argument) if argument is not None else "") + + print "eval:" + for primitive in eval_macro(instructions): + print primitive diff --git a/gerber/am_statements.py b/gerber/am_statements.py index bdb12dd..c514ad7 100644 --- a/gerber/am_statements.py +++ b/gerber/am_statements.py @@ -406,9 +406,13 @@ class AMPolygonPrimitive(AMPrimitive): modifiers = primitive.strip(' *').split(",") code = int(modifiers[0]) exposure = "on" if modifiers[1].strip() == "1" else "off" - vertices = int(modifiers[2]) + vertices = int(float(modifiers[2])) position = (float(modifiers[3]), float(modifiers[4])) - diameter = float(modifiers[5]) + try: + diameter = float(modifiers[5]) + except: + diameter = 0 + rotation = float(modifiers[6]) return cls(code, exposure, vertices, position, diameter, rotation) diff --git a/gerber/gerber_statements.py b/gerber/gerber_statements.py index 89f4f84..19c7138 100644 --- a/gerber/gerber_statements.py +++ b/gerber/gerber_statements.py @@ -22,7 +22,10 @@ Gerber (RS-274X) Statements """ from .utils import (parse_gerber_value, write_gerber_value, decimal_string, inch, metric) + from .am_statements import * +from .am_read import read_macro +from .am_eval import eval_macro class Statement(object): @@ -340,34 +343,37 @@ class AMParamStmt(ParamStmt): ParamStmt.__init__(self, param) self.name = name self.macro = macro - self.primitives = self._parsePrimitives(macro) - def _parsePrimitives(self, macro): + self.instructions = self.read(macro) + self.primitives = [] + + def read(self, macro): + return read_macro(macro) + + def evaluate(self, modifiers=[]): primitives = [] - for primitive in macro.strip('%\n').split('*'): - # Couldn't find anything explicit about leading whitespace in the spec... - primitive = primitive.strip(' *%\n') - if len(primitive): - if primitive[0] == '0': - primitives.append(AMCommentPrimitive.from_gerber(primitive)) - elif primitive[0] == '1': - primitives.append(AMCirclePrimitive.from_gerber(primitive)) - elif primitive[0:2] in ('2,', '20'): - primitives.append(AMVectorLinePrimitive.from_gerber(primitive)) - elif primitive[0:2] == '21': - primitives.append(AMCenterLinePrimitive.from_gerber(primitive)) - elif primitive[0:2] == '22': - primitives.append(AMLowerLeftLinePrimitive.from_gerber(primitive)) - elif primitive[0] == '4': - primitives.append(AMOutlinePrimitive.from_gerber(primitive)) - elif primitive[0] == '5': - primitives.append(AMPolygonPrimitive.from_gerber(primitive)) - elif primitive[0] =='6': - primitives.append(AMMoirePrimitive.from_gerber(primitive)) - elif primitive[0] == '7': - primitives.append(AMThermalPrimitive.from_gerber(primitive)) - else: - primitives.append(AMUnsupportPrimitive.from_gerber(primitive)) + for primitive in eval_macro(self.instructions, modifiers[0]): + if primitive[0] == '0': + primitives.append(AMCommentPrimitive.from_gerber(primitive)) + elif primitive[0] == '1': + primitives.append(AMCirclePrimitive.from_gerber(primitive)) + elif primitive[0:2] in ('2,', '20'): + primitives.append(AMVectorLinePrimitive.from_gerber(primitive)) + elif primitive[0:2] == '21': + primitives.append(AMCenterLinePrimitive.from_gerber(primitive)) + elif primitive[0:2] == '22': + primitives.append(AMLowerLeftLinePrimitive.from_gerber(primitive)) + elif primitive[0] == '4': + primitives.append(AMOutlinePrimitive.from_gerber(primitive)) + elif primitive[0] == '5': + primitives.append(AMPolygonPrimitive.from_gerber(primitive)) + elif primitive[0] =='6': + primitives.append(AMMoirePrimitive.from_gerber(primitive)) + elif primitive[0] == '7': + primitives.append(AMThermalPrimitive.from_gerber(primitive)) + else: + primitives.append(AMUnsupportPrimitive.from_gerber(primitive)) + return primitives def to_inch(self): diff --git a/gerber/rs274x.py b/gerber/rs274x.py index c5c89fb..69485a8 100644 --- a/gerber/rs274x.py +++ b/gerber/rs274x.py @@ -189,6 +189,7 @@ class GerberParser(object): self.statements = [] self.primitives = [] self.apertures = {} + self.macros = {} self.current_region = None self.x = 0 self.y = 0 @@ -392,6 +393,12 @@ class GerberParser(object): width = modifiers[0][0] height = modifiers[0][1] aperture = Obround(position=None, width=width, height=height) + elif shape == 'P': + # FIXME: not supported yet? + pass + else: + aperture = self.macros[shape].evaluate(modifiers) + self.apertures[d] = aperture def _evaluate_mode(self, stmt): @@ -414,6 +421,8 @@ class GerberParser(object): self.image_polarity = stmt.ip elif stmt.param == "LP": self.level_polarity = stmt.lp + elif stmt.param == "AM": + self.macros[stmt.name] = stmt elif stmt.param == "AD": self._define_aperture(stmt.d, stmt.shape, stmt.modifiers) @@ -449,9 +458,14 @@ class GerberParser(object): primitive = copy.deepcopy(self.apertures[self.aperture]) # XXX: temporary fix because there are no primitives for Macros and Polygon if primitive is not None: - primitive.position = (x, y) - primitive.level_polarity = self.level_polarity - self.primitives.append(primitive) + # XXX: just to make it easy to spot + if isinstance(primitive, type([])): + print primitive[0].to_gerber() + else: + primitive.position = (x, y) + primitive.level_polarity = self.level_polarity + self.primitives.append(primitive) + self.x, self.y = x, y def _evaluate_aperture(self, stmt): -- cgit From a13b981c1c2ea9ede39e9821d9ba818566f044de Mon Sep 17 00:00:00 2001 From: Paulo Henrique Silva Date: Thu, 5 Mar 2015 14:43:30 -0300 Subject: Fix tests for macros with no variables. All AM*Primitive classes now handles float for all but the code modifiers. This simplifies the reading/parsing. --- gerber/am_read.py | 17 ++++++++++++----- gerber/am_statements.py | 16 ++++++++-------- gerber/gerber_statements.py | 27 +++++++++++++-------------- gerber/rs274x.py | 2 +- gerber/tests/test_gerber_statements.py | 16 +++++++++++----- 5 files changed, 45 insertions(+), 33 deletions(-) (limited to 'gerber') diff --git a/gerber/am_read.py b/gerber/am_read.py index d1201b2..ade4389 100644 --- a/gerber/am_read.py +++ b/gerber/am_read.py @@ -32,6 +32,7 @@ class Token: LEFT_PARENS = "(" RIGHT_PARENS = ")" EQUALS = "=" + EOF = "EOF" def token_to_opcode(token): @@ -71,7 +72,8 @@ class Scanner: def peek(self): if not self.eof(): return self.buff[self.n] - return "" + + return Token.EOF def ungetc(self): if self.n > 0: @@ -104,6 +106,11 @@ class Scanner: return s.strip() +def print_instructions(instructions): + for opcode, argument in instructions: + print "%s %s" % (OpCode.str(opcode), str(argument) if argument is not None else "") + + def read_macro(macro): instructions = [] @@ -185,9 +192,10 @@ def read_macro(macro): elif c == "0": if is_primitive and not found_primitive_code: instructions.append((OpCode.PUSH, scanner.readstr("*"))) + found_primitive_code = True else: # decimal or integer disambiguation - if scanner.peek() not in '.': + if scanner.peek() not in '.' or scanner.peek() == Token.EOF: instructions.append((OpCode.PUSH, 0)) elif c in "123456789.": @@ -207,7 +215,7 @@ def read_macro(macro): instructions.append((token_to_opcode(pop()), None)) # at end, we either have a primitive or a equation - if is_primitive: + if is_primitive and found_primitive_code: instructions.append((OpCode.PRIM, primitive_code)) if is_equation: @@ -221,8 +229,7 @@ if __name__ == '__main__': instructions = read_macro(sys.argv[1]) print "insructions:" - for opcode, argument in instructions: - print "%s %s" % (OpCode.str(opcode), str(argument) if argument is not None else "") + print_instructions(instructions) print "eval:" for primitive in eval_macro(instructions): diff --git a/gerber/am_statements.py b/gerber/am_statements.py index c514ad7..38f4d71 100644 --- a/gerber/am_statements.py +++ b/gerber/am_statements.py @@ -160,7 +160,7 @@ class AMCirclePrimitive(AMPrimitive): def from_gerber(cls, primitive): modifiers = primitive.strip(' *').split(',') code = int(modifiers[0]) - exposure = 'on' if modifiers[1].strip() == '1' else 'off' + exposure = 'on' if float(modifiers[1]) == 1 else 'off' diameter = float(modifiers[2]) position = (float(modifiers[3]), float(modifiers[4])) return cls(code, exposure, diameter, position) @@ -233,7 +233,7 @@ class AMVectorLinePrimitive(AMPrimitive): def from_gerber(cls, primitive): modifiers = primitive.strip(' *').split(',') code = int(modifiers[0]) - exposure = 'on' if modifiers[1].strip() == '1' else 'off' + exposure = 'on' if float(modifiers[1]) == 1 else 'off' width = float(modifiers[2]) start = (float(modifiers[3]), float(modifiers[4])) end = (float(modifiers[5]), float(modifiers[6])) @@ -318,8 +318,8 @@ class AMOutlinePrimitive(AMPrimitive): modifiers = primitive.strip(' *').split(",") code = int(modifiers[0]) - exposure = "on" if modifiers[1].strip() == "1" else "off" - n = int(modifiers[2]) + exposure = "on" if float(modifiers[1]) == 1 else "off" + n = int(float(modifiers[2])) start_point = (float(modifiers[3]), float(modifiers[4])) points = [] for i in range(n): @@ -405,7 +405,7 @@ class AMPolygonPrimitive(AMPrimitive): def from_gerber(cls, primitive): modifiers = primitive.strip(' *').split(",") code = int(modifiers[0]) - exposure = "on" if modifiers[1].strip() == "1" else "off" + exposure = "on" if float(modifiers[1]) == 1 else "off" vertices = int(float(modifiers[2])) position = (float(modifiers[3]), float(modifiers[4])) try: @@ -508,7 +508,7 @@ class AMMoirePrimitive(AMPrimitive): diameter = float(modifiers[3]) ring_thickness = float(modifiers[4]) gap = float(modifiers[5]) - max_rings = int(modifiers[6]) + max_rings = int(float(modifiers[6])) crosshair_thickness = float(modifiers[7]) crosshair_length = float(modifiers[8]) rotation = float(modifiers[9]) @@ -690,7 +690,7 @@ class AMCenterLinePrimitive(AMPrimitive): def from_gerber(cls, primitive): modifiers = primitive.strip(' *').split(",") code = int(modifiers[0]) - exposure = 'on' if modifiers[1].strip() == '1' else 'off' + exposure = 'on' if float(modifiers[1]) == 1 else 'off' width = float(modifiers[2]) height = float(modifiers[3]) center= (float(modifiers[4]), float(modifiers[5])) @@ -772,7 +772,7 @@ class AMLowerLeftLinePrimitive(AMPrimitive): def from_gerber(cls, primitive): modifiers = primitive.strip(' *').split(",") code = int(modifiers[0]) - exposure = 'on' if modifiers[1].strip() == '1' else 'off' + exposure = 'on' if float(modifiers[1]) == 1 else 'off' width = float(modifiers[2]) height = float(modifiers[3]) lower_left = (float(modifiers[4]), float(modifiers[5])) diff --git a/gerber/gerber_statements.py b/gerber/gerber_statements.py index 19c7138..99672de 100644 --- a/gerber/gerber_statements.py +++ b/gerber/gerber_statements.py @@ -350,31 +350,30 @@ class AMParamStmt(ParamStmt): def read(self, macro): return read_macro(macro) - def evaluate(self, modifiers=[]): - primitives = [] + def build(self, modifiers=[[]]): + self.primitives = [] + for primitive in eval_macro(self.instructions, modifiers[0]): if primitive[0] == '0': - primitives.append(AMCommentPrimitive.from_gerber(primitive)) + self.primitives.append(AMCommentPrimitive.from_gerber(primitive)) elif primitive[0] == '1': - primitives.append(AMCirclePrimitive.from_gerber(primitive)) + self.primitives.append(AMCirclePrimitive.from_gerber(primitive)) elif primitive[0:2] in ('2,', '20'): - primitives.append(AMVectorLinePrimitive.from_gerber(primitive)) + self.primitives.append(AMVectorLinePrimitive.from_gerber(primitive)) elif primitive[0:2] == '21': - primitives.append(AMCenterLinePrimitive.from_gerber(primitive)) + self.primitives.append(AMCenterLinePrimitive.from_gerber(primitive)) elif primitive[0:2] == '22': - primitives.append(AMLowerLeftLinePrimitive.from_gerber(primitive)) + self.primitives.append(AMLowerLeftLinePrimitive.from_gerber(primitive)) elif primitive[0] == '4': - primitives.append(AMOutlinePrimitive.from_gerber(primitive)) + self.primitives.append(AMOutlinePrimitive.from_gerber(primitive)) elif primitive[0] == '5': - primitives.append(AMPolygonPrimitive.from_gerber(primitive)) + self.primitives.append(AMPolygonPrimitive.from_gerber(primitive)) elif primitive[0] =='6': - primitives.append(AMMoirePrimitive.from_gerber(primitive)) + self.primitives.append(AMMoirePrimitive.from_gerber(primitive)) elif primitive[0] == '7': - primitives.append(AMThermalPrimitive.from_gerber(primitive)) + self.primitives.append(AMThermalPrimitive.from_gerber(primitive)) else: - primitives.append(AMUnsupportPrimitive.from_gerber(primitive)) - - return primitives + self.primitives.append(AMUnsupportPrimitive.from_gerber(primitive)) def to_inch(self): for primitive in self.primitives: diff --git a/gerber/rs274x.py b/gerber/rs274x.py index 69485a8..d2844c9 100644 --- a/gerber/rs274x.py +++ b/gerber/rs274x.py @@ -397,7 +397,7 @@ class GerberParser(object): # FIXME: not supported yet? pass else: - aperture = self.macros[shape].evaluate(modifiers) + aperture = self.macros[shape].build(modifiers) self.apertures[d] = aperture diff --git a/gerber/tests/test_gerber_statements.py b/gerber/tests/test_gerber_statements.py index 04358eb..9032268 100644 --- a/gerber/tests/test_gerber_statements.py +++ b/gerber/tests/test_gerber_statements.py @@ -333,6 +333,7 @@ def test_AMParamStmt_factory(): 8,THIS IS AN UNSUPPORTED PRIMITIVE* ''') s = AMParamStmt.from_dict({'param': 'AM', 'name': name, 'macro': macro }) + s.build() assert_equal(len(s.primitives), 10) assert_true(isinstance(s.primitives[0], AMCommentPrimitive)) assert_true(isinstance(s.primitives[1], AMCirclePrimitive)) @@ -347,29 +348,34 @@ def test_AMParamStmt_factory(): def testAMParamStmt_conversion(): name = 'POLYGON' - macro = '5,1,8,25.4,25.4,25.4,0*%' + macro = '5,1,8,25.4,25.4,25.4,0*' s = AMParamStmt.from_dict({'param': 'AM', 'name': name, 'macro': macro }) + s.build() s.to_inch() assert_equal(s.primitives[0].position, (1., 1.)) assert_equal(s.primitives[0].diameter, 1.) - macro = '5,1,8,1,1,1,0*%' + macro = '5,1,8,1,1,1,0*' s = AMParamStmt.from_dict({'param': 'AM', 'name': name, 'macro': macro }) + s.build() s.to_metric() assert_equal(s.primitives[0].position, (25.4, 25.4)) assert_equal(s.primitives[0].diameter, 25.4) def test_AMParamStmt_dump(): name = 'POLYGON' - macro = '5,1,8,25.4,25.4,25.4,0*%' + macro = '5,1,8,25.4,25.4,25.4,0*' s = AMParamStmt.from_dict({'param': 'AM', 'name': name, 'macro': macro }) + s.build() + assert_equal(s.to_gerber(), '%AMPOLYGON*5,1,8,25.4,25.4,25.4,0.0*%') def test_AMParamStmt_string(): name = 'POLYGON' - macro = '5,1,8,25.4,25.4,25.4,0*%' + macro = '5,1,8,25.4,25.4,25.4,0*' s = AMParamStmt.from_dict({'param': 'AM', 'name': name, 'macro': macro }) - assert_equal(str(s), '') + s.build() + assert_equal(str(s), '') def test_ASParamStmt_factory(): stmt = {'param': 'AS', 'mode': 'AXBY'} -- cgit From adc1ff6d72ac1201517fffcd8e377768be022e91 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Silva Date: Thu, 5 Mar 2015 14:58:36 -0300 Subject: Fix for py3 --- gerber/am_read.py | 2 +- gerber/rs274x.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'gerber') diff --git a/gerber/am_read.py b/gerber/am_read.py index ade4389..9fdd548 100644 --- a/gerber/am_read.py +++ b/gerber/am_read.py @@ -108,7 +108,7 @@ class Scanner: def print_instructions(instructions): for opcode, argument in instructions: - print "%s %s" % (OpCode.str(opcode), str(argument) if argument is not None else "") + print("%s %s" % (OpCode.str(opcode), str(argument) if argument is not None else "")) def read_macro(macro): diff --git a/gerber/rs274x.py b/gerber/rs274x.py index d2844c9..a3a27e9 100644 --- a/gerber/rs274x.py +++ b/gerber/rs274x.py @@ -460,7 +460,7 @@ class GerberParser(object): if primitive is not None: # XXX: just to make it easy to spot if isinstance(primitive, type([])): - print primitive[0].to_gerber() + print(primitive[0].to_gerber()) else: primitive.position = (x, y) primitive.level_polarity = self.level_polarity -- cgit From 21fdb9cb57f5da938084fbf2b8133d903d0b0d77 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Silva Date: Thu, 5 Mar 2015 15:13:59 -0300 Subject: More py3 fixes --- gerber/am_read.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'gerber') diff --git a/gerber/am_read.py b/gerber/am_read.py index 9fdd548..05f3343 100644 --- a/gerber/am_read.py +++ b/gerber/am_read.py @@ -228,9 +228,9 @@ if __name__ == '__main__': instructions = read_macro(sys.argv[1]) - print "insructions:" + print("insructions:") print_instructions(instructions) - print "eval:" + print("eval:") for primitive in eval_macro(instructions): - print primitive + print(primitive) -- cgit