Skip to content
Lexer.py 6.18 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/>.
#
####################################################################################################

Fabrice Salvaire's avatar
Fabrice Salvaire committed
"""Module to implement a RS-274 G-code lexer."""

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

__all__ = [
    'GcodeLexerError',
    'GcodeLexer',
    'GcodeLexerMixin',
    'GcodeTokenMixin',
]
Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################

import re

try:
    import ply.lex as lexer
except ModuleNotFoundError:
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    import PythonicGcodeMachine.PythonLexYacc.lex as lexer
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class GcodeLexerError(ValueError):
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    pass

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

Fabrice Salvaire's avatar
Fabrice Salvaire committed
class GcodeTokenMixin:
Fabrice Salvaire's avatar
Fabrice Salvaire committed

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    """Mixin to define RS-274 G-code tokens. """
Fabrice Salvaire's avatar
Fabrice Salvaire committed

    # List of token names.
    tokens = (
        'ABSOLUTE_VALUE',
        'AND',
        'ARC_COSINE',
        'ARC_SINE',
        'ARC_TANGENT',
        'COSINE',
        'DIVIDED_BY',
        'EQUAL_SIGN',
        'EXCLUSIVE_OR',
        'E_RAISED_TO',
        'FIX_DOWN',
        'FIX_UP',
        'LEFT_BRACKET',
        # 'LEFT_PARENTHESIS',
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        'MINUS',
        'MODULO',
        'NATURAL_LOG_OF',
        'NON_EXCLUSIVE_OR',
        'PARAMETER_SIGN',
        'PLUS',
        'POWER',
        'RIGHT_BRACKET',
        # 'RIGHT_PARENTHESIS',
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        'ROUND',
        'SINE',
        'SQUARE_ROOT',
        'TANGENT',
        'TIMES',

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

        'POSITIVE_INTEGER',
        'POSITIVE_REAL',
        'REAL',
        'INLINE_COMMENT',
        'EOF_COMMENT',
    )

    # Regular expression rules for simple tokens
    t_ABSOLUTE_VALUE = r'abs'
    t_AND = r'and'
    t_ARC_COSINE = r'acos'
    t_ARC_SINE = r'asin'
    t_ARC_TANGENT = r'atan'
    # t_BLOCK_DELETE = r'\/' # slash
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    t_COSINE = r'cos'
    t_DIVIDED_BY = r'\/' # slash
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    t_EQUAL_SIGN = r'='
    t_EXCLUSIVE_OR = r'xor'
    t_E_RAISED_TO = r'exp'
    t_FIX_DOWN = r'fix'
    t_FIX_UP = r'fup'
    t_LEFT_BRACKET = r'\['
    # t_LEFT_PARENTHESIS = r'\('
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    t_MINUS = r'-'
    t_MODULO = r'mod'
    t_NATURAL_LOG_OF = r'ln'
    t_NON_EXCLUSIVE_OR = r'or'
    t_PARAMETER_SIGN = r'\#'
    t_PLUS = r'\+'
    t_POWER = r'\*\*'
    t_RIGHT_BRACKET = r'\]'
    # t_RIGHT_PARENTHESIS = r'\)'
Fabrice Salvaire's avatar
Fabrice Salvaire committed
    t_ROUND = r'round'
    t_SINE = r'sin'
    t_SQUARE_ROOT = r'sqrt'
    t_TANGENT = r'tan'
    t_TIMES = r'\*'

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    # clash with acos ... ???
    # t_LETTER = r'(' + '|'.join('abcd' + 'fghijklm' + 'pqrst' + 'xyz') + ')'

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    t_A = r'a'
    T_B = r'b'
    t_C = r'c'
    t_D = r'd'

    t_F = r'f'
    t_G = r'g'
    t_H = r'h'
    t_I = r'i'
    t_J = r'j'
    t_K = r'k'
    t_L = r'l'
    t_M = r'm'

    t_P = r'p'
    t_Q = r'q'
    t_R = r'r'
    t_S = r's'
    t_T = r't'

    t_X = r'x'
    t_Y = r'y'
    t_Z = r'z'

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    t_N = r'n'

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    # def t_POSITIVE_INTEGER(self, t):
    #     r'\d+'
    #     t.value = int(t.value)
    #     return t

    # def t_POSITIVE_REAL(self, t):
    #     r'\d*\.\d+'
    #     t.value = float(t.value)
    #     return t

    def t_REAL(self, t):
        # r'((-)?((\d*\.\d+)(E[\+-]?\d+)?|([1-9]\d*E[\+-]?\d+)))'
        r'((\+|-)?(\d+\.\d*|(\.)?\d+))'
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        value = t.value
        if '.' in value:
            value = float(value)
            if value > 0:
                t.type = 'POSITIVE_REAL'
            else:
                t.type = 'REAL'
        else:
            value = int(value)
            t.type = 'POSITIVE_INTEGER'
        t.value = value
        return t

    def t_INLINE_COMMENT(self, t):
        r'\([^\)]*\)'
        value = t.value[1:-1]
        position = value.find('(')
        if position != -1:
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            raise GcodeLexerError(t.lexpos + position +1)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        t.value = value
        return t

    def t_EOF_COMMENT(self, t):
        r';.*'
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        t.value = t.value[1:].strip()
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        return t

    # Ignored characters (spaces and tabs)
    t_ignore  = ' \t'

    def t_error(self, t):
        'Error handling rule'
        # t.lexer.skip(1)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        # raise GcodeLexerError("Illegal character @{} '{}'".format(t.lexpos, t.value))
        raise GcodeLexerError(t.lexpos)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
####################################################################################################

class GcodeLexerMixin:

    """Class to implement a RS-274 G-code lexer."""

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

    def __init__(self):
        self._build()

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

    def _build(self, **kwargs):
        """Build the lexer"""
        self._lexer = lexer.lex(
            module=self,
            reflags=int(re.VERBOSE + re.IGNORECASE),
            optimize=1,
            lextab='_lextab',
Fabrice Salvaire's avatar
Fabrice Salvaire committed
            **kwargs,
        )

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

    def input(self, data):
        return self._lexer.input(data)

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

Fabrice Salvaire's avatar
Fabrice Salvaire committed
    def tokenize(self, data):
        self.input(data)
Fabrice Salvaire's avatar
Fabrice Salvaire committed
        while True:
            token = self._lexer.token()
            if not token:
                break
            yield token
Fabrice Salvaire's avatar
Fabrice Salvaire committed

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

class GcodeLexer(GcodeLexerMixin, GcodeTokenMixin):
    pass