#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright 2019 Hiroshi Murayama import operator from ..utils import * from ..am_eval import OpCode from ..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): return self def to_inch(self): return AMOperatorExpression.div(self, MILLIMETERS_PER_INCH) def to_metric(self): return AMOperatorExpression.mul(self, 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 __float__(self): return float(self._value) @staticmethod def _amex_val(other): return float(other) if isinstance(other, AMConstantExpression) else other def __eq__(self, val): return self._value == AMConstantExpression._amex_val(val) def __ne__(self, val): return self._value != AMConstantExpression._amex_val(val) def __lt__(self, val): return self._value < AMConstantExpression._amex_val(val) def __gt__(self, val): return self._value > AMConstantExpression._amex_val(val) def __le__(self, val): return self._value <= AMConstantExpression._amex_val(val) def __ge__(self, val): return self._value >= AMConstantExpression._amex_val(val) def to_gerber(self, settings=None): if isinstance(self._value, str): return self._value return f'{self.value:.6f}'.rstrip('0').rstrip('.') 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 to_gerber(self, settings=None): return f'${self.number}' def to_instructions(self): return (OpCode.LOAD, self.number) class AMOperatorExpression(AMExpression): def __init__(self, op, lvalue, rvalue): super(AMOperatorExpression, self).__init__(AMExpression.OPERATOR) self.op = op self.lvalue = AMConstantExpression(lvalue) if isinstance(lvalue, (int, float)) else lvalue self.rvalue = AMConstantExpression(rvalue) if isinstance(rvalue, (int, float)) else rvalue @classmethod def add(kls, lvalue, rvalue): return kls(operator.add, lvalue, rvalue) @classmethod def sub(kls, lvalue, rvalue): return kls(operator.sub, lvalue, rvalue) @classmethod def mul(kls, lvalue, rvalue): return kls(operator.mul, lvalue, rvalue) @classmethod def div(kls, lvalue, rvalue): return kls(operator.truediv, lvalue, rvalue) def optimize(self): l = self.lvalue = self.lvalue.optimize() r = self.rvalue = self.rvalue.optimize() if isinstance(l, AMConstantExpression) and isinstance(r, AMConstantExpression): return AMConstantExpression(self.op(float(r), float(l))) elif self.op == operator.ADD: if r == 0: return l elif l == 0: return r elif self.op == operator.SUB: if r == 0: return l elif l == 0 and isinstance(r, AMConstantExpression): return AMConstantExpression(-float(r)) elif self.op == operator.MUL: if r == 1: return l elif l == 1: return r elif l == 0 or r == 0: return AMConstantExpression(0) elif self.op == operator.TRUEDIV: if r == 1: return self.lvalue elif l == 0: return AMConstantExpression(0) return self def to_gerber(self, settings=None): lval = self.lvalue.to_gerber(settings) rval = self.rvalue.to_gerber(settings)) op = {AMOperatorExpression.ADD: '+', AMOperatorExpression.SUB: '-', AMOperatorExpression.MUL: 'x', AMOperatorExpression.DIV: '/'} [self.op] return '(' + lval + op + rval + ')' def to_instructions(self): for i in self.lvalue.to_instructions(): yield i for i in self.rvalue.to_instructions(): yield i op = {AMOperatorExpression.ADD: OpCode.ADD, AMOperatorExpression.SUB: OpCode.SUB, AMOperatorExpression.MUL: OpCode.MUL, AMOperatorExpression.DIV: OpCode.DIV} [self.op] 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 = []