diff --git a/PythonicGCodeMachine/GCode/Lexer.py b/PythonicGCodeMachine/GCode/Lexer.py new file mode 100644 index 0000000000000000000000000000000000000000..b6202a5ba35b43d5838fbc1397ea7fb4aa69fae4 --- /dev/null +++ b/PythonicGCodeMachine/GCode/Lexer.py @@ -0,0 +1,219 @@ +#################################################################################################### +# +# PythonicGCodeMachine - @licence_header_description@ +# 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 . +# +#################################################################################################### + +#################################################################################################### + +import re + +import PythonicGCodeMachine.Lexer as lexer + +#################################################################################################### + +class GCodeLexerError(ValueError): + pass + +#################################################################################################### + +class GCodeLexer: + + """Class to implement a CGode lexer. + + For references, see + + * The NIST RS274NGC Interpreter — Version 3 — Appendix E. Production Rules for the RS274/NGC Language + * http://linuxcnc.org/docs/2.7/html/gcode/overview.html + + """ + + # List of token names. + tokens = ( + 'ABSOLUTE_VALUE', + 'AND', + 'ARC_COSINE', + 'ARC_SINE', + 'ARC_TANGENT', + 'COSINE', + 'DECIMAL_POINT', + 'DIVIDED_BY', + 'EQUAL_SIGN', + 'EXCLUSIVE_OR', + 'E_RAISED_TO', + 'FIX_DOWN', + 'FIX_UP', + 'LEFT_BRACKET', + 'LEFT_PARENTHESIS', + 'MINUS', + 'MODULO', + 'NATURAL_LOG_OF', + 'NON_EXCLUSIVE_OR', + 'PARAMETER_SIGN', + 'PLUS', + 'POWER', + 'RIGHT_BRACKET', + 'RIGHT_PARENTHESIS', + 'ROUND', + 'SINE', + 'SQUARE_ROOT', + 'TANGENT', + 'TIMES', + + 'A', 'B', 'C', 'D', + 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'P', 'Q', 'R', 'S', 'T', + 'X', 'Y', 'Z', + + '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'\/' + t_COSINE = r'cos' + t_DECIMAL_POINT = r'\.' + t_DIVIDED_BY = r'\/' + 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'\(' + 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'\)' + t_ROUND = r'round' + t_SINE = r'sin' + t_SQUARE_ROOT = r'sqrt' + t_TANGENT = r'tan' + t_TIMES = r'\*' + + 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_N = r'n' + + 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' + + # 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+)' + 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: + raise GCodeLexerError(t.lexpos + position +1) + t.value = value + return t + + def t_EOF_COMMENT(self, t): + r';.*' + t.value = t.value[1:] + return t + + # Ignored characters (spaces and tabs) + t_ignore = ' \t' + + def t_error(self, t): + 'Error handling rule' + # t.lexer.skip(1) + # raise GCodeLexerError("Illegal character @{} '{}'".format(t.lexpos, t.value)) + raise GCodeLexerError(t.lexpos) + + ############################################## + + def __init__(self): + self._build() + + ############################################## + + def _build(self, **kwargs): + """Build the lexer""" + self._lexer = lexer.lex( + module=self, + reflags=int(re.VERBOSE + re.IGNORECASE), + **kwargs, + ) + + ############################################## + + def tokenize(self, data): + + self._lexer.input(data) + while True: + token = self._lexer.token() + if not token: + break + yield token diff --git a/PythonicGCodeMachine/__init__.py b/PythonicGCodeMachine/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391