#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright 2019 Hiroshi Murayama from gerber.utils import * from gerber.am_eval import OpCode from gerber.am_statements import * class AMExpression(object): CONSTANT = 1 VARIABLE = 2 OPERATOR = 3 def __init__(self, kind): self.kind = kind @property def value(self): return self def optimize(self): pass def to_inch(self): return AMOperatorExpression(AMOperatorExpression.DIV, self, AMConstantExpression(MILLIMETERS_PER_INCH)) def to_metric(self): return AMOperatorExpression(AMOperatorExpression.MUL, self, AMConstantExpression(MILLIMETERS_PER_INCH)) def to_gerber(self, settings=None): pass def to_instructions(self): pass class AMConstantExpression(AMExpression): def __init__(self, value): super(AMConstantExpression, self).__init__(AMExpression.CONSTANT) self._value = value @property def value(self): return self._value def optimize(self): return self def to_gerber(self, settings=None): if isinstance(self._value, str): return self._value gerber = '%.6g' % self._value return '%.6f' % self._value if 'e' in gerber else gerber def to_instructions(self): return [(OpCode.PUSH, self._value)] class AMVariableExpression(AMExpression): def __init__(self, number): super(AMVariableExpression, self).__init__(AMExpression.VARIABLE) self.number = number def optimize(self): return self def to_gerber(self, settings=None): return '$%d' % self.number def to_instructions(self): return (OpCode.LOAD, self.number) class AMOperatorExpression(AMExpression): ADD = '+' SUB = '-' MUL = 'X' DIV = '/' def __init__(self, op, lvalue, rvalue): super(AMOperatorExpression, self).__init__(AMExpression.OPERATOR) self.op = op self.lvalue = lvalue self.rvalue = rvalue def optimize(self): self.lvalue = self.lvalue.optimize() self.rvalue = self.rvalue.optimize() if isinstance(self.lvalue, AMConstantExpression) and isinstance(self.rvalue, AMConstantExpression): lvalue = float(self.lvalue.value) rvalue = float(self.rvalue.value) value = lvalue + rvalue if self.op == self.ADD else \ lvalue - rvalue if self.op == self.SUB else \ lvalue * rvalue if self.op == self.MUL else \ lvalue / rvalue if self.op == self.DIV else None return AMConstantExpression(value) elif self.op == self.ADD: if self.rvalue.value == 0: return self.lvalue elif self.lvalue.value == 0: return self.rvalue elif self.op == self.SUB: if self.rvalue.value == 0: return self.lvalue elif self.lvalue.value == 0 and isinstance(self.rvalue, AMConstantExpression): return AMConstantExpression(-self.rvalue.value) elif self.op == self.MUL: if self.rvalue.value == 1: return self.lvalue elif self.lvalue.value == 1: return self.rvalue elif self.lvalue == 0 or self.rvalue == 0: return AMConstantExpression(0) elif self.op == self.DIV: if self.rvalue.value == 1: return self.lvalue elif self.lvalue.value == 0: return AMConstantExpression(0) return self def to_gerber(self, settings=None): return '(%s)%s(%s)' % (self.lvalue.to_gerber(settings), self.op, self.rvalue.to_gerber(settings)) def to_instructions(self): for i in self.lvalue.to_instructions(): yield i for i in self.rvalue.to_instructions(): yield i op = OpCode.ADD if self.op == self.ADD else\ OpCode.SUB if self.op == self.SUB else\ OpCode.MUL if self.op == self.MUL else\ OpCode.DIV yield (op, None) def eval_macro(instructions): 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(AMConstantExpression(argument)) elif opcode == OpCode.LOAD: push(AMVariableExpression(argument)) elif opcode == OpCode.STORE: yield (-argument, [pop()]) elif opcode == OpCode.ADD: op1 = pop() op2 = pop() push(AMOperatorExpression(AMOperatorExpression.ADD, op2, op1)) elif opcode == OpCode.SUB: op1 = pop() op2 = pop() push(AMOperatorExpression(AMOperatorExpression.SUB, op2, op1)) elif opcode == OpCode.MUL: op1 = pop() op2 = pop() push(AMOperatorExpression(AMOperatorExpression.MUL, op2, op1)) elif opcode == OpCode.DIV: op1 = pop() op2 = pop() push(AMOperatorExpression(AMOperatorExpression.DIV, op2, op1)) elif opcode == OpCode.PRIM: yield (argument, stack) stack = []