class ParserTestCase(unittest.TestCase): def setUp(self): self.parser = Parser(TurtleDrawer()) self.source = ["p 3"] def test_parse_exists(self): """Test to confirm that the parser has a method called parse""" self.assertTrue("parse" in dir(Parser)) def test_parse_executes(self): """Test to confirm that the parser's method parse runs without error""" raised = False # noinspection PyBroadException try: self.parser.parse(self.source) except Exception: raised = True self.assertFalse(raised, "Error Raised") def test_source_valid(self): """Test to confirm source stored in parser is valid""" self.parser.parse(self.source) self.assertEqual(self.parser.source, self.source)
def setUp(self): self.parser = Parser(TurtleDrawer()) self.source = ["p 3"]
def setUp(self): self.source_reader = SourceReader(Parser(TurtleDrawer()))
>>> source_reader.source = ['p 3', 'x 5', 'y 5', 'd' ,'e 5' ,'n 5' ,'w 5' ,'s 5' ,'u'] >>> source_reader.go() Selected pen 3.0 Gone to X=5.0 Gone to Y=5.0 Pen down drawing line of length 5.0 at 0 degrees drawing line of length 5.0 at 90 degrees drawing line of length 5.0 at 180 degrees drawing line of length 5.0 at 270 degrees Pen lifted End doctest """ def __init__(self, parser): super().__init__(parser) self.script_extension = '.tigr' def go(self): self.parser.parse(self.source) if __name__ == '__main__': import doctest from TIGrExParser import Parser from TIGrExTurtleDrawer import TurtleDrawer source_reader = SourceReader(Parser(TurtleDrawer())) doctest.testmod(verbose=3)
class TIGrEx(cmd.Cmd): """Main application controller for TIGrEx Begin doctest - Written with Jonathan Holdaway and Sean Ryan 24/08/2019 >>> app.do_drawer('turtle') Now using Turtle Drawer >>> app.do_pen(2) Selected pen 2 >>> app.do_x(5) Gone to X=5 >>> app.do_y(5) Gone to Y=5 >>> app.do_down() Pen down >>> app.do_east(50) drawing line of length 50 at 0 degrees >>> app.do_north(50) drawing line of length 50 at 90 degrees >>> app.do_west(50) drawing line of length 50 at 180 degrees >>> app.do_south(50) drawing line of length 50 at 270 degrees >>> app.do_up() Pen lifted Test script running >>> app.do_clear() Cleared drawing >>> app.do_run('script') Running script: script.tigr Selected pen 2.0 Gone to X=5.0 Gone to Y=15.0 Pen down drawing line of length 2.0 at 180 degrees drawing line of length 1.0 at 90 degrees drawing line of length 2.0 at 0 degrees drawing line of length 12.7 at 270 degrees Pen lifted End doctest """ intro = 'Welcome to Extended Tiny Interpreted Graphics.\n' \ 'Use "drawer" command to choose graphics library.\n' \ 'Enter ? for help.' prompt = 'TIGrEx >: ' def __init__(self): super().__init__() self.drawer = None self.parser = None self.source_reader = None def setup(self, drawer): # Generic setup for classes self.drawer = drawer self.parser = Parser(self.drawer) self.source_reader = SourceReader(self.parser) def do_drawer(self, arg): """Select graphics library to draw with. Available drawers: text, turtle -----""" if self.drawer: self.drawer.shutdown() if arg.lower() == 'text': self.setup(TextDrawer()) elif arg.lower() == 'turtle': self.setup(TurtleDrawer()) else: print('Please select a valid drawer.') def do_clear(self, arg=None): del arg try: self.parser.draw_clear() except AttributeError as exception: if self.drawer is None: # If no drawer is set then don't allow a command to run print( 'Please select a drawer before trying to run drawer commands.' ) else: print(exception) def do_up(self, arg=None): """Stop drawing. -----""" del arg try: self.parser.draw_pen_up() except AttributeError as exception: # If no drawer is set then don't allow a command to run if self.drawer is None: print( 'Please select a drawer before trying to run drawer commands.' ) else: print(exception) def do_down(self, arg=None): """Start drawing. -----""" del arg try: self.parser.draw_pen_down() except AttributeError as exception: # If no drawer is set then don't allow a command to run if self.drawer is None: print( 'Please select a drawer before trying to run drawer commands.' ) else: print(exception) def do_north(self, arg): """Draw north by a specified amount. -----""" try: self.parser.data = int(arg) self.parser.command = 'north' self.parser.draw_line_data(self.parser.data) except ValueError: print('This command only takes numbers') except AttributeError as exception: # If no drawer is set then don't allow a command to run if self.drawer is None: print( 'Please select a drawer before trying to run drawer commands.' ) else: print(exception) def do_south(self, arg): """Draw south by a specified amount. -----""" try: self.parser.data = int(arg) self.parser.command = 'south' self.parser.draw_line_data(self.parser.data) except AttributeError as exception: # If no drawer is set then don't allow a command to run if self.drawer is None: print( 'Please select a drawer before trying to run drawer commands.' ) else: print(exception) def do_east(self, arg): """Draw east by a specified amount. -----""" try: self.parser.data = int(arg) self.parser.command = 'east' self.parser.draw_line_data(self.parser.data) except ValueError: print('This command only takes numbers') except AttributeError as exception: # If no drawer is set then don't allow a command to run if self.drawer is None: print( 'Please select a drawer before trying to run drawer commands.' ) else: print(exception) def do_west(self, arg): """Draw west by a specified amount. -----""" try: self.parser.data = int(arg) self.parser.command = 'west' self.parser.draw_line_data(self.parser.data) except ValueError: print('This command only takes numbers') except AttributeError as exception: # If no drawer is set then don't allow a command to run if self.drawer is None: print( 'Please select a drawer before trying to run drawer commands.' ) else: print(exception) def do_x(self, arg): """Set X position of the pen. -----""" try: self.parser.data = int(arg) self.parser.draw_goto_x(self.parser.data) except ValueError: print('This command only takes numbers') except AttributeError as exception: # If no drawer is set then don't allow a command to run if self.drawer is None: print( 'Please select a drawer before trying to run drawer commands.' ) else: print(exception) def do_y(self, arg): """Set Y position of the pen. -----""" try: self.parser.data = int(arg) self.parser.draw_goto_y(self.parser.data) except ValueError: print('This command only takes numbers') except AttributeError as exception: # If no drawer is set then don't allow a command to run if self.drawer is None: print( 'Please select a drawer before trying to run drawer commands.' ) else: print(exception) def do_pen(self, arg): """Select preset pen. Preset pens: 1 - colour black, size 10 2 - colour red, size 10 3 - colour blue, size 10 -----""" try: self.parser.data = int(arg) self.parser.draw_select_pen(self.parser.data) except ValueError: print('This command only takes numbers') except AttributeError as exception: # If no drawer is set then don't allow a command to run if self.drawer is None: print( 'Please select a drawer before trying to run drawer commands.' ) else: print(exception) def do_run(self, arg): """Load a script and run it. -----""" try: # Search for file without extension, if the file isn't found try with extension # Using multi-line and case insensitive for regex search if re.search( r'^.*\.(' + self.source_reader.script_extension + r')$', arg, re.M | re.I): file = arg else: file = arg + self.source_reader.script_extension print('Running script:', file) self.source_reader.source = [ line.rstrip('\n') for line in open(file) ] except FileNotFoundError: # If file is found in search without the script extension alert that TIGr only reads .tigr scripts if not re.search( r'^.*\.(' + self.source_reader.script_extension + r')$', arg, re.M | re.I): print('Script not found. Enter a valid file name.') else: print( f'Script not found. TIGrEx only reads {self.source_reader.script_extension} files as scripts.' ) except AttributeError as exception: # If no drawer is set then don't allow a script to run if self.drawer is None: print('Please select a drawer before trying to run scripts.') else: print(exception) else: # Start parsing the script self.source_reader.file_name = arg self.source_reader.go() @staticmethod def do_quit(arg): """Closes the program. -----""" del arg exit() @staticmethod def do_exit(arg): """Closes the program. -----""" del arg exit() @staticmethod def parse(arg): # Convert arg to a tuple return tuple(map(int, arg.split()))
def setup(self, drawer): # Generic setup for classes self.drawer = drawer self.parser = Parser(self.drawer) self.source_reader = SourceReader(self.parser)
class TIGrEx(cmd.Cmd): """Main application controller for TIGrEx Begin doctest - Written with Jonathan Holdaway and Sean Ryan 24/08/2019 >>> app.do_drawer('turtle') Now using Turtle Drawer >>> app.do_pen(2) Selected pen 2.0 >>> app.do_x(5) Gone to X=5.0 >>> app.do_y(5) Gone to Y=5.0 >>> app.do_down() Pen down >>> app.do_east(50) drawing line of length 50.0 at 0 degrees >>> app.do_north(50) drawing line of length 50.0 at 90 degrees >>> app.do_west(50) drawing line of length 50.0 at 180 degrees >>> app.do_south(50) drawing line of length 50.0 at 270 degrees >>> app.do_up() Pen lifted Test script running >>> app.do_clear() Cleared drawing >>> app.do_run('script') Running script: script.tigr Selected pen 2.0 Gone to X=5.0 Gone to Y=15.0 Pen down drawing line of length 2.0 at 180 degrees drawing line of length 1.0 at 90 degrees drawing line of length 2.0 at 0 degrees drawing line of length 12.7 at 270 degrees Pen lifted End doctest # """ def __init__(self): super().__init__() self.drawer = None self.parser = None self.source_reader = None self.intro = 'Welcome to Extended Tiny Interpreted Graphics.\n' \ 'Use "drawer" command to choose graphics library.\n' \ 'Enter ? for help.' self.prompt = 'TIGrEx >: ' self.script_extension = '.tigr' def setup(self, drawer): # Generic setup for classes self.drawer = drawer self.parser = Parser(self.drawer) def do_drawer(self, arg): """Select graphics library to draw with. Available drawers: text, turtle -----""" if self.drawer: self.drawer.shutdown() self.drawer = None if arg.lower() == 'text': self.setup(TextDrawer()) elif arg.lower() == 'turtle': self.setup(TurtleDrawer()) else: print('Please select a valid drawer.') # changed all the do_ methods path to call parser.parse def do_clear(self, arg=None): del arg self.parser.parse({'clear'}) def do_up(self, arg=None): """Stop drawing. -----""" del arg self.parser.parse({'up'}) def do_down(self, arg=None): """Start drawing. -----""" del arg self.parser.parse({'down'}) def do_north(self, arg): """Draw north by a specified amount. -----""" self.parser.parse({'north ' + str(arg)}) def do_south(self, arg): """Draw south by a specified amount. -----""" self.parser.parse({'south ' + str(arg)}) def do_east(self, arg): """Draw east by a specified amount. -----""" self.parser.parse({'east ' + str(arg)}) def do_west(self, arg): """Draw west by a specified amount. -----""" self.parser.parse({'west ' + str(arg)}) def do_x(self, arg): """Set X position of the pen. -----""" self.parser.parse({'x ' + str(arg)}) def do_y(self, arg): """Set Y position of the pen. -----""" self.parser.parse({'y ' + str(arg)}) def do_pen(self, arg): """Select preset pen. Preset pens: 1 - colour black, size 10 2 - colour red, size 10 3 - colour blue, size 10 -----""" self.parser.parse({'pen ' + str(arg)}) def do_run(self, arg): """Load a script and run it. -----""" # Search for file without extension, if the file isn't found try with extension # Using multi-line and case insensitive for regex search # modified by Jerry Wang, this regx can not match a file name end with .tigr extension_check = re.search(r'^.*(' + self.script_extension + r')$', arg, re.M | re.I) if extension_check: file = arg else: file = arg + self.script_extension print('Running script:', file) try: raw_source = [line.rstrip('\n') for line in open(file)] except FileNotFoundError: # If file is found in search without the script extension alert that TIGr only reads .tigr scripts if not extension_check: print('Script not found. Enter a valid file name.') else: print( f'Script not found.TIGrEx only reads {self.script_extension} files as scripts.' ) else: # Start parsing the script self.parser.parse(raw_source) # extract error check into two new methods ---- Duplicated Code """I realized that the way I refactored code caused another duplication bad smell So I put Drawer check in precmd and all data check in parser.parse where it meant to be """ def precmd(self, line): if line is not None and line.split(' ')[0].lower() not in ('drawer', '?', 'exit', 'quit'): if self.drawer is None: print( 'Please select a drawer before trying to run drawer commands' ) return '' else: return line else: return line @staticmethod def do_quit(arg): """Closes the program. -----""" del arg exit() @staticmethod def do_exit(arg): """Closes the program. -----""" del arg exit() # It runs previous command when get '' from precmd method as return. # Override emptyline method to make sure it runs nothing def emptyline(self): pass
def setup(self, drawer): # Generic setup for classes self.drawer = drawer self.parser = Parser(self.drawer)