diff options
Diffstat (limited to 'gerbonara/gerber/aperture_macros/am_expression.py')
-rw-r--r-- | gerbonara/gerber/aperture_macros/am_expression.py | 244 |
1 files changed, 0 insertions, 244 deletions
diff --git a/gerbonara/gerber/aperture_macros/am_expression.py b/gerbonara/gerber/aperture_macros/am_expression.py deleted file mode 100644 index 809f60e..0000000 --- a/gerbonara/gerber/aperture_macros/am_expression.py +++ /dev/null @@ -1,244 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright 2021 Jan Götte <gerbonara@jaseg.de> - -import operator -import re - -class Expression(object): - @property - def value(self): - return self - - def optimized(self): - return self - - -class UnitExpression(Expression): - def __init__(self, expr, unit): - self._expr = expr - self.unit = unit - - def to_gerber(self, unit=None): - return self.converted(unit).optimized().to_gerber() - - def __eq__(self, other): - return type(other) == type(self) and \ - self.unit == other.unit and\ - self._expr == other._expr - - def __str__(self): - return f'<{str(self.expr)[1:-1]} {self.unit}>' - - def converted(self, unit): - if unit is None or self.unit == unit: - return self._expr - - elif unit == 'mm': - return OperatorExpression.mul(self._expr, MILLIMETERS_PER_INCH) - - elif unit == 'inch': - return OperatorExpression.div(self._expr, MILLIMETERS_PER_INCH) - - else: - raise ValueError('invalid unit, must be "inch" or "mm".') - - def calculate(self, variable_binding={}, unit=None): - expr = self.converted(unit).optimized(variable_binding) - if not isinstance(expr, ConstantExpression): - raise IndexError(f'Cannot fully resolve expression due to unresolved variables: {expr} with variables {variable_binding}') - - -class ConstantExpression(Expression): - def __init__(self, value): - self._value = value - - @property - def value(self): - return self._value - - def __float__(self): - return float(self._value) - - def __eq__(self, other): - return type(self) == type(other) and self._value == other._value - - def to_gerber(self, _unit=None): - if isinstance(self._value, str): - return self._value - return f'{self.value:.6f}'.rstrip('0').rstrip('.') - - def __str__(self): - return f'<{self._value}>' - - -class VariableExpression(Expression): - def __init__(self, number): - self.number = number - - def optimized(variable_binding={}): - if self.number in variable_binding: - return ConstantExpression(variable_binding[self.number]) - return self - - def __eq__(self, other): - return type(self) == type(other) and \ - self.number == other.number - - def to_gerber(self, _unit=None): - return f'${self.number}' - - def __str__(self): - return f'<@{self.number}>' - - -class OperatorExpression(Expression): - def __init__(self, op, l, r): - super(OperatorExpression, self).__init__(Expression.OPERATOR) - self.op = op - self.l = ConstantExpression(l) if isinstance(l, (int, float)) else l - self.r = ConstantExpression(r) if isinstance(r, (int, float)) else r - - def __eq__(self, other): - return type(self) == type(other) and \ - self.op == other.op and \ - self.lvalue == other.lvalue and \ - self.rvalue == other.rvalue - - def optimized(self, variable_binding={}): - l = self.lvalue.optimized(variable_binding) - r = self.rvalue.optimized(variable_binding) - - if self.op in (operator.add, operator.mul): - if hash(r) < hash(l): - l, r = r, l - - if isinstance(l, ConstantExpression) and isinstance(r, ConstantExpression): - return ConstantExpression(self.op(float(r), float(l))) - - return OperatorExpression(self.op, l, r) - - def to_gerber(self, unit=None): - lval = self.lvalue.to_gerber(unit) - rval = self.rvalue.to_gerber(unit) - op = {OperatorExpression.ADD: '+', - OperatorExpression.SUB: '-', - OperatorExpression.MUL: 'x', - OperatorExpression.DIV: '/'} [self.op] - return f'({lval}{op}{rval})' - - def __str__(self): - op = {operator.add: '+', operator.sub: '-', operator.mul: '*', operator.truediv: '/'}[self.op] - return f'<{str(self.lvalue)[1:-1]} {op} {str(self.rvalue)[1:-1]}>' - -operator_map = { - '+': operator.add, - '-': operator.sub, - 'x': operator.mul, - 'X': operator.mul, - '/': operator.truediv, - } - -precedence_map = { - operator.add : 0, - operator.sub : 0, - operator.mul : 1, - operator.truediv : 1, - } - -def _parse_expression(expr_str): - output_stack = [] - operator_stack = [] - - drop_unary = lambda s: (s[0] == '-', s[1:] if s[0] in '-+' else s) - negate = lambda expr: OperatorExpression(operator.sub, ConstantExpression(0), expr) - - # See http://faculty.cs.niu.edu/~hutchins/csci241/eval.htm - # We handle the unary +/- operators by including them into variable/number/parenthesis tokens. - for variable, number, operator, parenthesis in re.findall(r'([-+]?\$[0-9]+)|([-+]?[0-9]+)|([-+]?\(|\))|([-+xX/])', expr_str): - - if variable: - is_negative, variable = drop_unary(variable) - var_ex = VariableExpression(int(variable[1:])) - output_stack.append(negate(var_ex) if is_negative else var_ex) - - -def _parse_expression(expr_str): - output_stack = [] - operator_stack = [] - - drop_unary = lambda s: (s[0] == '-', s[1:] if s[0] in '-+' else s) - negate = lambda expr: OperatorExpression(operator.sub, ConstantExpression(0), expr) - - # See http://faculty.cs.niu.edu/~hutchins/csci241/eval.htm - # We handle the unary +/- operators by including them into variable/number/parenthesis tokens. - for variable, number, operator, parenthesis in re.findall(r'([-+xX/])|([-+]?\$[0-9]+)|([-+]?[0-9]+\.?[0-9]*)|([()])', expr_str): - - if variable: - is_negative, variable = drop_unary(variable) - var_ex = VariableExpression(int(variable[1:])) - output_stack.append(negate(var_ex) if is_negative else var_ex) - - elif number: - output_stack.append(ConstantExpression(float(number))) - - elif parenthesis[-1] == '(': # be careful, we might have a leading unary +/- here! - is_negative, parenthesis = drop_unary(parenthesis) - if is_negative: - operator_stack.push('-') - operator_stack.push('(') - - elif parenthesis == ')': # here we cannot have a leading unary +/- - if not operator_stack: - raise SyntaxError('Unbalanced parenthesis in aperture macro expression') - - while operator_stack and not operator_stack[-1] == '(': - op = operator_stack.pop() - l, r = output_stack.pop(), output_stack.pop() - output_stack.append(OperatorExpression(op, l, r)) - - assert output_stack.pop() == '(' - if output_stack[-1] == '-': - output_stack.append(negate(output_stack.pop())) - - elif operator: - operator = operator_map[operator] - - if not operator_stack or operator_stack[-1] == '(': - operator_stack.push(operator) - - else: - while operator_stack and operator_stack[-1] != '(' and\ - precedence_map[operator] <= precedence_map[operator_stack[-1]]: - output_stack.append(OperatorExpression(operator_stack.pop(), output_stack.pop(), output_stack.pop())) - operator_stack.push(operator) - - for operator in reversed(operator_stack): - if operator == '(': - raise SyntaxError('Unbalanced parenthesis in aperture macro expression') - - output_stack.append(OperatorExpression(operator_stack.pop(), output_stack.pop(), output_stack.pop())) - print(output_stack, operator_stack) - - if len(output_stack) != 1: - raise SyntaxError('Invalid aperture macro expression') - - return output_stack[0] - -def parse_macro(macro, unit): - blocks = re.sub(r'\s', '', macro).split('*') - variables = {} - for block in blocks: - block = block.strip() - if block[0] == '$': # variable definition - name, expr = block.partition('=') - variables[int(name[1:])] = _parse_expression(expr) - else: # primitive - primitive, args = block.split(',') - yield PRIMITIVE_CLASSES[int(primitive)](unit=unit, args=list(map(_parse_expression, args))) - -if __name__ == '__main__': - import sys - for line in sys.stdin: - print(_parse_expression(line.strip())) |