diff --git a/PythonicGcodeMachine/Gcode/Rs274/Ast.py b/PythonicGcodeMachine/Gcode/Rs274/Ast.py index 40b77ac2fd55530312ac124b869827ed415873ac..9a64839375040f27aabafed3464e60fbdef7d33a 100644 --- a/PythonicGcodeMachine/Gcode/Rs274/Ast.py +++ b/PythonicGcodeMachine/Gcode/Rs274/Ast.py @@ -239,7 +239,7 @@ class Line: line = '' if not self: - line += r'\ ' + line += '/ ' if self._line_number: line += 'N{} '.format(self._line_number) line += ' '.join(map(str, self)) diff --git a/PythonicGcodeMachine/Gcode/Rs274/Lexer.py b/PythonicGcodeMachine/Gcode/Rs274/Lexer.py index c444d7f3dda66ca9d422daeb2456ec8efbc5dbe6..ab888cd5aa6378ebfe74fecdc17bec4c626547ab 100644 --- a/PythonicGcodeMachine/Gcode/Rs274/Lexer.py +++ b/PythonicGcodeMachine/Gcode/Rs274/Lexer.py @@ -183,7 +183,7 @@ class GcodeLexer: def t_EOF_COMMENT(self, t): r';.*' - t.value = t.value[1:] + t.value = t.value[1:].strip() return t # Ignored characters (spaces and tabs) diff --git a/PythonicGcodeMachine/Gcode/Rs274/Parser.py b/PythonicGcodeMachine/Gcode/Rs274/Parser.py index 011b2354aa20404bcbac80ad254d72a4044effc0..2b4378e18bc41b3d9d93eedaf937fefda30a90b7 100644 --- a/PythonicGcodeMachine/Gcode/Rs274/Parser.py +++ b/PythonicGcodeMachine/Gcode/Rs274/Parser.py @@ -25,6 +25,7 @@ __all__ = ['GcodeParserError', 'GcodeParser'] # https://rply.readthedocs.io/en/latest/ from ply import yacc +from . import Ast from .Lexer import GcodeLexer #################################################################################################### @@ -93,6 +94,13 @@ class GcodeParser: __lexer_cls__ = GcodeLexer + # Build the operation map + __operation_map__ = {} + for cls_name in Ast.__all__: + cls = getattr(Ast, cls_name) + if hasattr(cls, '__gcode__'): + __operation_map__[cls.__gcode__] = cls + ############################################## # Start symbol @@ -100,43 +108,59 @@ class GcodeParser: '''line : DIVIDED_BY line_right | line_right ''' + if len(p) == 3: + self._line.deleted = True + # p[0] = self._line def p_line_right(self, p): '''line_right : line_content | line_content EOF_COMMENT ''' + if len(p) == 3: + self._line.comment = p[2] + # p[0] = self._line def p_line_content(self, p): 'line_content : segments' - # p[0] = + # p[0] = self._line def p_numbered_line(self, p): 'line_content : line_number segments' + self._line.line_number = p[1] + # p[0] = self._line def p_line_number(self, p): '''line_number : N POSITIVE_INTEGER | N POSITIVE_REAL ''' + p[0] = p[2] def p_segments(self, p): '''segments : segment | segments segment ''' + if len(p) == 2: + self._line += p[1] + else: + self._line += p[2] def p_segment(self, p): '''segment : mid_line_word | comment | parameter_setting ''' + p[0] = p[1] ############################################## def p_comment(self, p): 'comment : ordinary_comment' - # 'comment : message | ordinary_comment': + # 'comment : message | ordinary_comment': + p[0] = p[1] def p_ordinary_comment(self, p): 'ordinary_comment : INLINE_COMMENT' + p[0] = Ast.Comment(p[1]) # def p_message(self, p): # 'message : left_parenthesis + {white_space} + letter_m + {white_space} + letter_s + @@ -147,6 +171,7 @@ class GcodeParser: def p_mid_line_word(self, p): 'mid_line_word : mid_line_letter real_value' + p[0] = Ast.Word(p[1], p[2]) def p_mid_line_letter(self, p): '''mid_line_letter : A @@ -170,15 +195,19 @@ class GcodeParser: | Y | Z ''' + p[0] = str(p[1]) def p_parameter_setting(self, p): 'parameter_setting : PARAMETER_SIGN parameter_index EQUAL_SIGN real_value' + p[0] = Ast.ParameterSetting(p[2], p[4]) def p_parameter_value(self, p): 'parameter_value : PARAMETER_SIGN parameter_index' + p[0] = Ast.Parameter(p[2]) def p_parameter_index(self, p): 'parameter_index : real_value' + p[0] = p[1] def p_real_value(self, p): '''real_value : POSITIVE_INTEGER @@ -188,6 +217,7 @@ class GcodeParser: | parameter_value | unary_combo ''' + p[0] = p[1] ############################################## @@ -195,21 +225,29 @@ class GcodeParser: '''unary_combo : ordinary_unary_combo | arc_tangent_combo ''' + p[0] = p[1] def p_ordinary_unary_combo(self, p): 'ordinary_unary_combo : ordinary_unary_operation expression' + p[0] = self.__operation_map__[p[1]](p[2]) def p_expression(self, p): 'expression : LEFT_BRACKET inner_expression RIGHT_BRACKET' + p[0] = p[2] def p_inner_expression(self, p): '''inner_expression : real_value | inner_expression binary_operation real_value ''' + if len(p) == 2: + p[0] = p[1] + else: + p[0] = self.__operation_map__[p[2]](p[1], p[3]) def p_arc_tangent_combo(self, p): # atan[1.5]/[1.0] 'arc_tangent_combo : ARC_TANGENT expression DIVIDED_BY expression' + p[0] = DividedBy(ArcTangent(p[2]), p[4]) def p_ordinary_unary_operation(self, p): '''ordinary_unary_operation : ABSOLUTE_VALUE @@ -225,21 +263,25 @@ class GcodeParser: | SQUARE_ROOT | TANGENT ''' + p[0] = p[1] def p_binary_operation(self, p): '''binary_operation : binary_operation1 | binary_operation2 | binary_operation3 ''' + p[0] = p[1] def p_binary_operation1(self, p): 'binary_operation1 : POWER' + p[0] = p[1] def p_binary_operation2(self, p): '''binary_operation2 : DIVIDED_BY | MODULO | TIMES ''' + p[0] = p[1] def p_binary_operation3(self, p): '''binary_operation3 : AND @@ -248,6 +290,7 @@ class GcodeParser: | NON_EXCLUSIVE_OR | PLUS ''' + p[0] = p[1] ############################################## @@ -264,11 +307,19 @@ class GcodeParser: def __init__(self): self._build() + self._reset() + + ############################################## + + def _reset(self): + self._line = None ############################################## def _build(self, **kwargs): + """Build the parser""" + self._lexer = self.__lexer_cls__() self.tokens = self._lexer.tokens self._parser = yacc.yacc( @@ -280,9 +331,31 @@ class GcodeParser: ############################################## def parse(self, line): + + """Parse a G-code line""" + line = line.strip() + + self._line = Ast.Line() ast = self._parser.parse( line, lexer=self._lexer._lexer, # debug=True, ) + + line = self._line + self._reset() + + return line + + ############################################## + + def parse_lines(self, lines): + + """Parse a G-code lines""" + + program = Ast.Program() + for line in lines.split('\n'): + program += self.parse(line) + + return program diff --git a/unit-test/Gcode/test_Parser.py b/unit-test/Gcode/test_Parser.py index ee4905e8d0d24887197b9eb6ef86283eda992fcb..ee5245cee41cb22cbe56ddfeb442c250baa566ec 100644 --- a/unit-test/Gcode/test_Parser.py +++ b/unit-test/Gcode/test_Parser.py @@ -106,15 +106,16 @@ class TestGcodeParser(unittest.TestCase): 'N3.1 G0 X1.0 Y0 Z0 ; a eof comment', 'N3.1 (comment 1) G0 (comment 2) X1.0 (comment 3) Y0 (comment 4) Z0 ; a eof comment', - '#3=1. G0 X#3 Y0' - 'G0 #3=1. X#3 Y0' + '#3=1. G0 X#3 Y0', + 'G0 #3=1. X#3 Y0', - '#3=1. G0 X [ 1 + acos[0] - [#3 ** [4.0/2]]]' + '#3=1. G0 X [ 1 + acos[0] - [#3 ** [4.0/2]]]', ): print() print(gcode) try: - parser.parse(gcode) + line = parser.parse(gcode) + print('>', line) except GcodeParserError as exception: position, = exception.args print(' ' * position + '^')