Skip to content
Ast.py 23.3 KiB
Newer Older
Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################
#
Fabrice Salvaire's avatar
Fabrice Salvaire committed
# PythonicGcodeMachine - A Python G-code Toolkit
Fabrice Salvaire's avatar
Fabrice Salvaire committed
# Copyright (C) 2018 Fabrice Salvaire
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
####################################################################################################

"""Module to implement an AST for RS-274 G-code.
Fabrice Salvaire's avatar
Fabrice Salvaire committed

All classes are clonable.
Fabrice Salvaire's avatar
Fabrice Salvaire committed
"""

####################################################################################################

__all__ = [
    'Program',
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    # 'LineItem',
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    'Line',
    'Comment',
    'Word',
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    # 'RealValue',
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    'ParameterMixin',
    'ParameterSetting',
    'Parameter',
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    # 'UnaryOperation',
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    'AbsoluteValue',
    'ArcCosine',
    'ArcSine',
    'ArcTangent',
    'Cosine',
    'ERaisedTo',
    'FixDown',
    'FixUp',
    'NaturalLogOf',
    'Round',
    'Sine',
    'SquareRoot',
    'Tangent',
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    # 'BinaryOperation',
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    'Power',
    'DividedBy',
    'Modulo',
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    'Multiply',
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    'And',
    'ExclusiveOr',
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    'Subtraction',
    'Or',
    'Addition',
Fabrice Salvaire's avatar
Fabrice Salvaire committed
]

####################################################################################################

import math
Fabrice Salvaire's avatar
Fabrice Salvaire committed
import re
Fabrice Salvaire's avatar
Fabrice Salvaire committed

Fabrice Salvaire's avatar
Fabrice Salvaire committed
import colors

Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class MachineMixin:

    """Mixin to define the target machine for the AST node"""

    ##############################################

    def __init__(self, machine=None):
        self._machine = machine

    ##############################################

    @property
    def machine(self):
        return self._machine

    @machine.setter
    def machine(self, value):
        self._machine = value

####################################################################################################

class Program(MachineMixin):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    """Class to implement a G-code program

    Usage::

        program = Program()
        program += line
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        # Array interface
        for line in programs:
            print(line)

Fabrice Salvaire's avatar
Fabrice Salvaire committed
        str(program)

Fabrice Salvaire's avatar
Fabrice Salvaire committed
        program2 = program.clone()

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    """

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def __init__(self, machine=None):
        super().__init__(machine)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        self._lines = []

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def clone(self):

Fabrice Salvaire's avatar
Fabrice Salvaire committed
        program = self.__class__(machine=self._machine)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        for line in self:
            program += line.clone()

        return program

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def push(self, line):
        self._lines.append(line)

    def __iadd__(self, item):
        self.push(item)
        return self

    ##############################################

    def __len__(self):
        return len(self._lines)

    def __iter__(self):
        return iter(self._lines)

    def __getitem__(self, _slice):
        return self._lines[_slice]

    def iter_on_not_deleted(self):
        for line in self._lines:
            if line:
                yield line

    ##############################################

    def __repr__(self):

        text = 'Program(\n'
        for line in self:
            text += repr(line) + '\n'
        text += ')\n'

        return text

    ##############################################

    def __str__(self):
        return '\n'.join(map(str, self))

####################################################################################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class CloneMixin:

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    """Mixin to provide a method to clone value"""

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    @staticmethod
    def _clone_value(value):
        if hasattr(value, 'clone'):
            return value.clone()
        else:
            return value

####################################################################################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class LineItem(MachineMixin, CloneMixin):

    """Base class for line item"""
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

    def _check_value(self, value):

        if (isinstance(value, (int, float)) or
            isinstance(value, RealValue)):
            return value
        else:
            try:
                str_value = str(value)
            except:
                raise ValueError("Invalid value {}".format(value))
            # Fixme:
            from .Parser import GcodeParser, GcodeParserError
            parser = GcodeParser()
            try:
                # Fixme: parser hack
                ast = parser.parse('X' + value)
                return ast[0].value
            except GcodeParserError:
                raise ValueError("Invalid G-code value {}".format(value))

    ##############################################
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    def ansi_str(self):
        return str(self)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

####################################################################################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class Line(MachineMixin):
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    """Class to implement a G-code line

    Usage::

Fabrice Salvaire's avatar
Fabrice Salvaire committed
        line = Line(deleted=False, line_number=1, comment='a comment')

        line.deleted = True
        print(line.deleted)
        # same apply for line_number and comment

        # Is line not deleted ?
        bool(line)

        # Push some items
        # Note: order doesn't matter, see RS-274 for details
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        line += Word('G', 0)
        line += Comment('move')
        line += Word('X', 10)
        line += Comment('Y value')
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        line += Word('Y', 20.)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        line += ParameterSetting('1', 1.2)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

Fabrice Salvaire's avatar
Fabrice Salvaire committed
        # using expression, AST way
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        line += Word('Z', Addition(30, Multiply(Parameter(100), Cosine(30))))
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        # string way
        line += Word('Z', '[30 + [#100 * cos[30]]]')
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        # Array interface
        for item in line:
            print(item)

Fabrice Salvaire's avatar
Fabrice Salvaire committed
        str(line)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        print(line.ansi_str()) # use ANSI colors, see Line.ANSI_... attributes

Fabrice Salvaire's avatar
Fabrice Salvaire committed
        a_line = line.clone()

    Values can be passed as:

    * int or float,
    * AST for expression,
    * any object that "str" evaluate to a valid G-code expression.

    As a shortcut, a G/M-code operation can be passed as string::

        line += 'G0'

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    Expression can be evaluated using :code:`float(obj.value)`, excepted when we must access a parameter
    value.
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ANSI_DELETED = colors.red
    ANSI_LINE_NUMBER = colors.blue
    ANSI_COMMENT = colors.green
    ANSI_SETTING = colors.blue
    ANSI_G = colors.red
    ANSI_X = colors.blue
    ANSI_VALUE = colors.black

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def __init__(self, deleted=False, line_number=None, comment=None, machine=None):

        super().__init__(machine)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

        self.deleted = deleted
        self.line_number = line_number
        self.comment = comment

        self._items = []

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def clone(self):

Fabrice Salvaire's avatar
Fabrice Salvaire committed
        line = self.__class__(self._deleted, self._line_number, self._comment, self._machine)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        for item in self:
            line += item.clone()

        return line

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    @property
    def deleted(self):
        return self._deleted

    @deleted.setter
    def deleted(self, value):
        self._deleted = bool(value)

    @property
    def line_number(self):
        return self._line_number

    @line_number.setter
    def line_number(self, value):
        if value is not None:
            value = float(value)
            if value.is_integer():
                value = int(value)
        self._line_number = value

    @property
    def comment(self):
        return self._comment

    @comment.setter
    def comment(self, value):
        if value is not None:
            self._comment = str(value)
        else:
            self._comment = None

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def _push_item(self, item):
        if not isinstance(item, LineItem):
            item = Word.from_str(item)
            # Fixme: try to parse ???
        self._items.append(item)

    def push_items(self, iterable):
        """Method to push an iterable"""
        for item in iterable:
            self.push(item)

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def push(self, item):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        """Method to push a valid item, a 'G/Mxxx' shortcut string, a list or tuple"""
        if isinstance(item, (list, tuple, Line)):
            self.push_items(item)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        else:
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            self._push_item(item)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    def __iadd__(self, item):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        """push shortcut"""
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        self.push(item)
        return self

    ##############################################

    def __bool__(self):
        return not self._deleted

    ##############################################

    def __len__(self):
        return len(self._items)

    def __iter__(self):
        return iter(self._items)

    def __getitem__(self, _slice):
        return self._items[_slice]

    ##############################################

    def iter_on_word(self):
        for item in self:
            if isinstance(item, Word):
                yield item

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def iter_on_gm_word(self):
        for item in self:
            if isinstance(item, Word) and item.is_gm_gcode:
                yield item

    def iter_on_x_word(self):
        for item in self:
            if isinstance(item, Word) and not item.is_gm_gcode:
                yield item

    def iter_on_setting(self):
        for item in self:
            if isinstance(item, ParameterSetting):
                yield item

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def iter_in_order(self):
        words = [word for word in self.iter_on_gm_word()]
        words.sort(key=lambda word: word.execution_order.index)
        return words

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def toggle(self):
        """Toggle deleted flag"""
        self._deleted = not self._deleted

    def remove_line_number(self):
        self._line_number = None

    ##############################################

    def remove_comment(self):
        self._comment = None
        for i, item in enumerate(self):
            if isinstance(item, Comment):
                # self._items.pop(i)
                del self._items[i]

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def __repr__(self):

        items = []
        if not self:
            items.append('Deleted')
        items += list(map(repr, self))
        if self._comment:
            items.append(self._comment)

        return 'Line{}'.format(self, items)

    ##############################################

    def __str__(self):

        line = ''
        if not self:
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            line += '/ '
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        if self._line_number:
            line += 'N{} '.format(self._line_number)
        line += ' '.join(map(str, self))
        if self._comment:
            line += ' ; ' + self._comment

        return line

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ##############################################

    def ansi_str(self):

        line = ''
        if not self:
            # line += self.ANSI_DELETED('/ ')
            return self.ANSI_DELETED(str(self))
        if self._line_number:
            line += self.ANSI_LINE_NUMBER('N{} '.format(self._line_number))
        line += ' '.join([item.ansi_str() for item in self])
        if self._comment:
            line += ' ' + self.ANSI_COMMENT('; ' + self._comment)

        return line

Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################

class Comment(LineItem):

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    """Class to implement comment"""

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def __init__(self, text, machine=None):
        super().__init__(machine)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        self.set(text)

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def clone(self):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        return self.__class__(self._text, self._machine)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def set(self, text):
        if '(' in text:
            raise ValueError('Comment cannot contains a "("')
        self._text = str(text)

    ##############################################

    @property
    def text(self):
        return self._text

    @text.setter
    def text(self, value):
        self.set(value)

    ##############################################

    def __repr__(self):
        return 'Comment({0._text})'.format(self)

    def __str__(self):
        return '({0._text})'.format(self)

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def ansi_str(self):
        return Line.ANSI_COMMENT(str(self))

Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################

class Word(LineItem):

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    """Class to implement word"""

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    LETTERS = (
        'A', 'B', 'C', 'D',
        'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', # 'N',
        'P', 'Q', 'R', 'S', 'T',
        'X', 'Y', 'Z',
    )

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    WORD_RE = re.compile('(G|M)(\d+)')

    ##############################################

    @classmethod
    def from_str(cls, obj):

        str_obj = str(obj)
        match = cls.WORD_RE.match(str_obj)
        if match is not None:
            return cls(*match.groups())
        else:
            raise ValueError(obj)

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def __init__(self, letter, value, machine=None):
        super().__init__(machine)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        self.letter = letter
        self.value = value

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def clone(self):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        return self.__class__(self._letter, self._clone_value(self._value), self._machine)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    @property
    def letter(self):
        return self._letter

    @letter.setter
    def letter(self, value):
        value = str(value).upper()
        if value not in self.LETTERS:
            raise ValueError
        self._letter = value

    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, value):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        self._value = self._check_value(value)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

    def __repr__(self):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        return 'Word({0._letter}, {0._value})'.format(self)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    def __str__(self):
        return '{0._letter}{0._value}'.format(self)

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def ansi_str(self):

Fabrice Salvaire's avatar
Fabrice Salvaire committed
        if self._machine:
            gm = self._machine.GM_LETTERS
        else:
            gm = 'GM'

        if self._letter in gm:
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            return Line.ANSI_G(str(self))
        else:
            return Line.ANSI_X(self._letter) + Line.ANSI_VALUE(str(self._value))

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ##############################################

    def _check_machine(self):
        if self._machine is None:
            raise NameError('Machine is not defined')

    ##############################################

    @property
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def is_gm_gcode(self):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        self._check_machine()
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        return self._machine.is_gm_word(self)

    ##############################################

    @property
    def is_valid_gcode(self):
        if self.is_gm_gcode:
            return str(self) in self._machine.config.gcodes
        else:
            return True
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

    @property
    def meaning(self):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        if self.is_gm_gcode:
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            return self._machine.config.gcodes[str(self)].meaning
        else:
            return self._machine.config.letters[self.letter].meaning

    ##############################################

    @property
    def modal_group(self):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        if self.is_gm_gcode:
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            return self._machine.config.modal_groups[str(self)]
        else:
            return None

    ##############################################

    @property
    def execution_order(self):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        if self.is_gm_gcode:
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            return self._machine.config.execution_order[str(self)]
        else:
            return None

Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class RealValue(MachineMixin, CloneMixin):
    """Base class for real value"""
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    pass

####################################################################################################

class ParameterMixin:

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    """Mixin for parameter"""

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ##############################################

    def __init__(self, parameter):
        self.parameter = parameter

    ##############################################

    @property
    def parameter(self):
        return self._parameter

    @parameter.setter
    def parameter(self, value):
        try:
            value = int(value)
        except ValueError:
            value = str(value)
        self._parameter = value

####################################################################################################

class ParameterSetting(LineItem, ParameterMixin):

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    """Class to implement parameter setting"""

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def __init__(self, parameter, value, machine=None):
        super().__init__(machine)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        ParameterMixin.__init__(self, parameter)
        self.value = value

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ##############################################

    def clone(self):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        return self.__class__(self._parameter, self._clone_value(self._value), self._machine)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ##############################################
    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, value):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        self._value = self._check_value(value)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

    def __repr__(self):
        return 'ParameterSetting({0._parameter} = {0._value})'.format(self)

    def __str__(self):
        return '#{0._parameter}={0._value}'.format(self)

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def ansi_str(self):
        return Line.ANSI_SETTING('#{0._parameter}='.format(self)) + Line.ANSI_VALUE(str(self._value))

Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################

class Parameter(RealValue, ParameterMixin):

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    """Class to implement parameter"""

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def __init__(self, parameter, machine=None):
        super().__init__(machine)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        ParameterMixin.__init__(self, parameter)

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def clone(self):
        return self.__class__(self._parameter, self._machine)

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def __repr__(self):
        return 'Parameter({0._parameter})'.format(self)

    def __str__(self):
        return '#{0._parameter}'.format(self)

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    ##############################################

    def __float__(self):
        raise NotImplementedError

Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################

class UnaryOperation(RealValue):

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    """Base class for unary operation"""

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = None
    __gcode__ = None

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def __init__(self, arg, machine=None):
        super().__init__(machine)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        self.arg = arg

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def clone(self):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        return self.__class__(self._clone_value(self._arg), self._machine)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    @property
    def arg(self):
        return self._arg

    @arg.setter
    def arg(self, value):
        self._arg = value

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def __float__(self):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        return self.__function__(float(self._arg))

    ##############################################

    def __repr__(self):
        return '{}({})'.format(self.__class__.__name__, repr(self._arg))

    ##############################################

    def __str__(self):
        return '{0.__gcode__}[{0._arg}]'.format(self)

####################################################################################################

class AbsoluteValue(UnaryOperation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = staticmethod(abs)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ = 'abs'

class ArcCosine(UnaryOperation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = staticmethod(lambda x: math.acos(math.radians(x)))
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ = 'acos'

class ArcSine(UnaryOperation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = staticmethod(lambda x: math.degrees(math.asin(x)))
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ = 'asin'

class ArcTangent(UnaryOperation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = staticmethod(lambda x: math.degrees(math.atan(x)))
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ = 'atan'

class Cosine(UnaryOperation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = staticmethod(lambda x: math.cos(math.radians(x)))
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ =  'cos'

class ERaisedTo(UnaryOperation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = staticmethod(math.exp)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ = 'exp'

class FixDown(UnaryOperation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = staticmethod(math.ceil)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ = 'fix'

class FixUp(UnaryOperation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = staticmethod(math.floor)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ = 'fup'

class NaturalLogOf(UnaryOperation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = staticmethod(math.log)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ = 'ln'

class Round(UnaryOperation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = staticmethod(round)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ =  'round'

class Sine(UnaryOperation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = staticmethod(lambda x: math.sin(math.radians(x)))
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ = 'sin'

class SquareRoot(UnaryOperation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = staticmethod(math.sqrt)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ = 'sqrt'

class Tangent(UnaryOperation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = staticmethod(lambda x: ath.tan(math.radians(x)))
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ = 'tan'

####################################################################################################

class BinaryOperation(RealValue):

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    """Base class for binary operation"""

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = None
    __gcode__ = None

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def __init__(self, arg1, arg2, machine=None):
        super().__init__(machine)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        self.arg1 = arg1
        self.arg2 = arg2

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def clone(self):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        return self.__class__(self._clone_value(self._arg1), self._clone_value(self.arg2), self._machine)
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    @property
    def arg1(self):
        return self._arg1

    @arg1.setter
    def arg1(self, value):
        self._arg1 = value

    @property
    def arg2(self):
        return self._arg2

    @arg2.setter
    def arg2(self, value):
        self._arg2 = value

    ##############################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def __float__(self):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        return self.__function__(float(self._arg1), float(self._arg2))

    ##############################################

    def __repr__(self):
        return '{}({})'.format(self.__class__.__name__, repr(self._arg))

    ##############################################

    def __str__(self):
        return '[{0._arg1} {0.__gcode__} {0._arg2}]'.format(self)

####################################################################################################

class Power(BinaryOperation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = staticmethod(lambda a, b: a**b)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ = '**'

class DividedBy(BinaryOperation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = staticmethod(lambda a, b: a / b)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ = '/'

class Modulo(BinaryOperation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = staticmethod(lambda a, b: a % b)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ = 'mod'

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class Multiply(BinaryOperation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = staticmethod(lambda a, b: a * b)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ =  '*'

class And(BinaryOperation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = staticmethod(lambda a, b: a & b)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ = 'and'

class ExclusiveOr(BinaryOperation):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __function__ = staticmethod(lambda a, b: a ^ b)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ = 'xor'

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class Subtraction(BinaryOperation):
    __function__ = staticmethod(lambda a, b: a - b)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ = '-'

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class Or(BinaryOperation):
    __function__ = staticmethod(lambda a, b: a | b)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ = 'or'

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class Addition(BinaryOperation):
    __function__ = staticmethod(lambda a, b: a + b)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    __gcode__ = '+'