def parse_point(cli_input: str): """ Parse either a relative point from input, e.g. '+10,-12' OR an absolute point from input, e.g. '10,20'. :param cli_input: string :return Success(AbsoluteParserPoint or RelativeParserPoint, remainder) if input contains either valid absolute point or relative point, Failure(expected format, the given cli_input) otherwise """ first_nat_parser = NatParser(',') second_nat_parser = NatParser() first_int_parser = IntParser(',') second_int_parser = IntParser() # Parse absolute point from given input abs_point_parse = PointParser().parse_input(first_nat_parser, second_nat_parser, cli_input) if abs_point_parse.is_successful(): abs_point = AbsoluteParserPoint(abs_point_parse.get_match()[0], abs_point_parse.get_match()[1]) return Success(abs_point, abs_point_parse.get_remainder()) # Parse relative point from given input rel_point_parse = PointParser().parse_input(first_int_parser, second_int_parser, cli_input) if rel_point_parse.is_successful(): rel_point = RelativeParserPoint(rel_point_parse.get_match()[0], rel_point_parse.get_match()[1]) return Success(rel_point, rel_point_parse.get_remainder()) # Both parsers has failed to parse a point, return the last result (a Failure object) return rel_point_parse
def parse_params(self, cli_input: str) -> ParseResult: points_result = self.parse_two_points(cli_input) if points_result.is_successful(): abs_points = self.convert_points(points_result.get_match()) # Parse color color_result = self.parse_color(points_result.get_remainder(), self.color_parser) if color_result.is_successful(): start_x = abs_points[0].x start_y = abs_points[0].y end_x = abs_points[1].x end_y = abs_points[1].y return Success( PrintRectCommand( self._controller, start_x, start_y, color_result.get_match(), end_x=end_x, end_y=end_y, ), '') else: return Failure(color_result.get_expected(), points_result.get_remainder()) point_and_nats_result = self.parse_point_and_nats(cli_input) if point_and_nats_result.is_successful(): width = point_and_nats_result.get_match()[1] height = point_and_nats_result.get_match()[2] abs_points = self.convert_points( [point_and_nats_result.get_match()[0]]) # Parse color color_result = self.parse_color( point_and_nats_result.get_remainder(), self.color_parser) if color_result.is_successful(): start_x = abs_points[0].x start_y = abs_points[0].y return Success( PrintRectCommand(self._controller, start_x, start_y, color_result.get_match(), DimensionsRectFactory, width=width, height=height), '') else: return Failure(color_result.get_expected(), point_and_nats_result.get_remainder()) return Failure( points_result.get_expected() + ' | ' + point_and_nats_result.get_expected(), cli_input)
def parse_params(self, cli_input: str) -> ParseResult: points_result = self.parse_two_points(cli_input) if points_result.is_successful(): abs_points = self.convert_points(points_result.get_match()) start_x = abs_points[0].x start_y = abs_points[0].y end_x = abs_points[1].x end_y = abs_points[1].y # Parse color color_result = self.parse_color(points_result.get_remainder(), self.color_parser) if color_result.is_successful(): color = color_result.get_match() return Success( PrintCircleCommand(self._controller, start_x, start_y, color, end_x=end_x, end_y=end_y), '') else: return Failure(color_result.get_expected(), points_result.get_remainder()) point_and_nat_result = self.parse_point_and_nat(cli_input) if point_and_nat_result.is_successful(): radius = point_and_nat_result.get_match()[1] abs_point = self.convert_points( [point_and_nat_result.get_match()[0]]) start_x = abs_point[0].x start_y = abs_point[0].y # Parse color color_result = self.parse_color( point_and_nat_result.get_remainder(), self.color_parser) if color_result.is_successful(): color = color_result.get_match() return Success( PrintCircleCommand(self._controller, start_x, start_y, color, DimensionsCircleFactory, radius=radius), '') else: return Failure(color_result.get_expected(), point_and_nat_result.get_remainder()) return Failure( points_result.get_expected() + ' | ' + point_and_nat_result.get_expected(), cli_input)
def parse_color(self, cli_input: str, color_parser: ColorParser) -> ParseResult: """ Parse a Color from given input. """ default_color = Color(0, 0, 0) if cli_input == '': return Success(default_color, '') color_result = color_parser.parse_color(cli_input) if color_result.is_successful(): return Success(color_result.get_match(), '') return Failure(color_result.get_expected(), cli_input)
def parse_params(self, cli_input: str): if cli_input == '': # point parameter is absent, return ListShapeCommand with default values of parameters x and y return Success(ListShapeCommand(self._controller), '') result = PointParser.parse_point(cli_input) if result.is_successful(): abs_point = self.convert_points([result.get_match()]) x = abs_point[0].x y = abs_point[0].y return Success(ListShapeCommand(self._controller, x, y), result.get_remainder()) else: return result
def test_point_parser_relative_points(): """ Test PointParser's parsing of relative points. """ parser = PointParser() # Test invalid inputs invalid_inputs = [ '-10 -20', '-10.-20', '+10', '+ 10,-20', '+10,- 20', '-10,', '+x,-20', '-10,+y', '-x,-y' ] for cli_input in invalid_inputs: result = parser.parse_point(cli_input) assert result == Failure("x,y or (+-)x,(+-)y", cli_input) # Test valid inputs valid_inputs = [ ('-10,+20', RelativeParserPoint(-10, 20), ''), ('+10,+20 ', RelativeParserPoint(10, 20), ''), ('-10 ,-20', RelativeParserPoint(-10, -20), ''), ('+10, -20', RelativeParserPoint(10, -20), ''), ('-10 , +20', RelativeParserPoint(-10, 20), ''), (' +10 , -20', RelativeParserPoint(10, -20), ''), (' +10 , -20 something', RelativeParserPoint(10, -20), 'something'), ('+100,-250 something', RelativeParserPoint(100, -250), 'something'), ('-0,+0', RelativeParserPoint(0, 0), ''), ('+10,-20 30,40', RelativeParserPoint(10, -20), '30,40') ] for cli_input, expected, remainder in valid_inputs: result = parser.parse_point(cli_input) assert result == Success(expected, remainder)
def test_rgb_color_parser(): """ Test RgbColorParser's parsing of color in 'rgb([0,255],[0,255],[0,255])' format. """ parser = RgbColorParser() # Test invalid inputs invalid_inputs = [ "r gb(0,1,2)", "rgb 0,1,2)", "rgb(0,1,2", "rgb(-5,1,2)", "rgb(-5,-1-2)", "rgb(256,1,2)", "rgb(0,256,2)", "rgb(0,1,256)", "rgb(260,300,600)" ] for cli_input in invalid_inputs: result = parser.parse_color(cli_input) assert result == Failure("rgb([0,255],[0,255],[0,255])", cli_input) valid_inputs = [ ("rgb(0,1,2)", (0, 1, 2), ""), ("rgb(255,255,255)", (255, 255, 255), ""), (" rgb( 0,1,2)", (0, 1, 2), ""), ("rgb (0, 1,2 )", (0, 1, 2), ""), ("rgb (0,1,2)", (0, 1, 2), ""), ("rgb (20,30,40) something else", (20, 30, 40), "something else"), ("rgb ( 0 ,1 , 2 ) something", (0, 1, 2), "something") ] # Test valid inputs for cli_input, expected, remainder in valid_inputs: result = parser.parse_color(cli_input) assert result == Success(expected, remainder)
def test_point_parser_absolute_points(): """ Test PointParser's parsing of absolute points. """ parser = PointParser() # Test invalid inputs invalid_inputs = [ '1020', '10 20', '10.20', '10', '10,', 'x,20', '10,y', 'x,y' ] for cli_input in invalid_inputs: result = parser.parse_point(cli_input) assert result == Failure("x,y or (+-)x,(+-)y", cli_input) # Test valid inputs valid_inputs = [ ('10,20', AbsoluteParserPoint(10, 20), ''), ('10,20 ', AbsoluteParserPoint(10, 20), ''), ('10 ,20', AbsoluteParserPoint(10, 20), ''), ('10, 20', AbsoluteParserPoint(10, 20), ''), ('10 , 20', AbsoluteParserPoint(10, 20), ''), (' 10 , 20', AbsoluteParserPoint(10, 20), ''), (' 10 , 20 something', AbsoluteParserPoint(10, 20), 'something'), ('100,250 something', AbsoluteParserPoint(100, 250), 'something'), ('0,0', AbsoluteParserPoint(0, 0), ''), ('10,20 30,40', AbsoluteParserPoint(10, 20), '30,40') ] for cli_input, expected, remainder in valid_inputs: result = parser.parse_point(cli_input) assert result == Success(expected, remainder)
def parse_input(self, cli_input: str): result = super().parse_input(cli_input) if result.is_successful(): integer = int(result.get_match()) return Success(integer, result.get_remainder()) return result
def parse_params(self, cli_input: str) -> ParseResult: points_result = self.parse_two_points(cli_input) if points_result.is_successful(): points = points_result.get_match() # two points parsed successfully, try to parse a color remainder = points_result.get_remainder() color_result = self.parse_color(remainder, self.color_parser) if color_result.is_successful(): # color parsed successfully, no more points will be parsed color = color_result.get_match() abs_points = self.convert_points(points_result.get_match()) start_x = abs_points[0].x start_y = abs_points[0].y end_x = abs_points[1].x end_y = abs_points[1].y return Success( PrintLineCommand(self._controller, start_x, start_y, end_x, end_y, color), '') else: # try to parse a point or a color while True: point_result = PointParser().parse_point(remainder) if point_result.is_successful(): points.append(point_result.get_match()) remainder = point_result.get_remainder() else: color_result = self.parse_color( remainder, self.color_parser) if color_result.is_successful(): # color parsed successfully, no more points will be parsed color = color_result.get_match() abs_points = self.convert_points(points) return Success( PrintPolylineCommand(self._controller, [(p.x, p.y) for p in abs_points], color), color_result.get_remainder()) else: break return Failure("line <POINT> <POINTS>", cli_input)
def test_string_parser(): """ Test StringParser used for parsing exact word from the beginning of a given string. """ # Test invalid inputs without a delimiter invalid_inputs = [('rect', 'recta'), ('rect', 'list rect'), ('rect', 'rect, '), ('rect', 'rect('), ('rect', 'RECT'), ('rect', 'circle'), ('rect', ' circle'), ('rect', 'rect10,20 30,40')] for expected, actual in invalid_inputs: parser = StringParser(expected, '') result = parser.parse_input(actual) assert isinstance(result, Failure) # Test valid inputs without a delimiter valid_inputs = [('rect', 'rect +10 -10', '+10 -10'), ('rect', 'rect anything ', 'anything '), ('rect', 'rect +1002', '+1002'), ('rect', ' rect + 1 0 1 2', '+ 1 0 1 2'), ('rect', ' rect', ''), ('rect', ' rect +10', '+10')] for expected, actual, remainder in valid_inputs: parser = StringParser(expected, '') result = parser.parse_input(actual) assert result == Success(expected, remainder) # Test invalid inputs with a delimiter invalid_inputs = [('rgb', '(', 'rg b(20,20,20)'), ('rgb', '(', ' rgb2(0...'), ('rgb', '(', 'rgb 20...')] for expected, delimiter, actual in invalid_inputs: parser = StringParser(expected, delimiter) result = parser.parse_input(actual) assert isinstance(result, Failure) # Test valid inputs with a delimiter valid_inputs = [('rgb', '(', 'rgb(20,20,20)', '20,20,20)'), ('rgb', '(', ' rgb(20...', '20...'), ('rgb', '(', 'rgb (20...', '20...'), ('rgb', '(', 'rgb ( 20...', '20...')] for expected, delimiter, actual, remainder in valid_inputs: parser = StringParser(expected, delimiter) result = parser.parse_input(actual) assert result == Success(expected, remainder)
def parse_params(self, cli_input: str) -> ParseResult: result = PointParser().parse_point(cli_input) if result.is_successful(): abs_point = self.convert_points([result.get_match()]) x = abs_point[0].x y = abs_point[0].y return Success(RemoveShapeCommand(self._controller, x, y), result.get_remainder()) else: return result
def parse_input(self, cli_input: str) -> ParseResult: if self._delimiter == '': match = re.match(r'^(\s*)(' + self._expected + ')(\s+|$)', cli_input) else: match = re.match( r'^(\s*)(' + self._expected + ')' + r'(\s*)' + re.escape(self._delimiter) + r'(\s*|$)', cli_input) if match: remainder = cli_input[match.end():] return Success(match.group(2), remainder) else: return Failure(self._expected, cli_input)
def parse_point_and_nat(self, cli_input: str) -> ParseResult: """ Parse a Point and a Natural number from given input. """ point_result = PointParser().parse_point(cli_input) if point_result.is_successful(): radius_result = self.radius_parser.parse_input( point_result.get_remainder()) if radius_result.is_successful(): return Success( [point_result.get_match(), radius_result.get_match()], radius_result.get_remainder()) return Failure("circle <POINT> <NAT>", cli_input)
def parse_two_points(cli_input: str) -> ParseResult: """ Parse two Points from given input. """ point_result1 = PointParser().parse_point(cli_input) if point_result1.is_successful(): point_result2 = PointParser().parse_point( point_result1.get_remainder()) if point_result2.is_successful(): return Success( [point_result1.get_match(), point_result2.get_match()], point_result2.get_remainder()) return Failure("<POINT> <POINT>", cli_input)
def parse_params(self, cli_input: str) -> ParseResult: result = self.parse_two_points(cli_input) if result.is_successful(): abs_points = self.convert_points(result.get_match()) start_x = abs_points[0].x start_y = abs_points[0].y end_x = abs_points[1].x end_y = abs_points[1].y return Success( MoveShapeCommand(self._controller, start_x, start_y, end_x, end_y), result.get_remainder()) else: return result
def parse_point_and_nats(self, cli_input: str) -> ParseResult: """ Parse a Point and two Natural numbers from given input. """ point_result = PointParser().parse_point(cli_input) if point_result.is_successful(): width_result = self.width_parser.parse_input( point_result.get_remainder()) if width_result.is_successful(): height_result = self.height_parser.parse_input( width_result.get_remainder()) if height_result.is_successful(): return Success([ point_result.get_match(), width_result.get_match(), height_result.get_match() ], height_result.get_remainder()) return Failure("rect <POINT> <NAT> <NAT>", cli_input)
def parse_params(self, cli_input: str) -> ParseResult: point_result = PointParser().parse_point(cli_input) if point_result.is_successful(): abs_point = self.convert_points([point_result.get_match()]) point_x = abs_point[0].x point_y = abs_point[0].y # Parse color color_result = self.parse_color(point_result.get_remainder(), self.color_parser) if color_result.is_successful(): color = color_result.get_match() return Success( PrintDotCommand(self._controller, point_x, point_y, color), '') else: return Failure(color_result.get_expected(), point_result.get_remainder()) return Failure("dot <POINT>", cli_input)
def parse_input(first_parser: NumberParser, second_parser: NumberParser, cli_input: str) -> ParseResult: """ Parse point from input using given NumberParsers. Both NumberParser must be of the same type! :param first_parser: NumberParser :param second_parser: NumberParser :param cli_input: string :return: Success([X coordinate, Y coordinate], string) if input contains a number within range, Failure(string, string) otherwise """ # Parse a number using comma and only comma as a delimiter" result1 = first_parser.parse_input(cli_input) if result1.is_successful(): # Parse a number using space or end of string as a delimiter" result2 = second_parser.parse_input(result1.get_remainder()) if result2.is_successful(): x = result1.get_match() y = result2.get_match() return Success([x, y], result2.get_remainder()) return Failure('x,y or (+-)x,(+-)y', cli_input)
def parse_color(self, cli_input: str) -> ParseResult: failure = Failure("rgb([0,255],[0,255],[0,255])", cli_input) # Parse prefix \"rgb(\" prefix_parse_result = self.string_parser.parse_input(cli_input) if not prefix_parse_result.is_successful(): return failure # Parse red (first number) red_parse_result = self.first_nat_parser.parse_input( prefix_parse_result.get_remainder()) if not red_parse_result.is_successful(): return failure red = red_parse_result.get_match() # Parse green (second number) green_parse_result = self.first_nat_parser.parse_input( red_parse_result.get_remainder()) if not green_parse_result.is_successful(): return failure green = green_parse_result.get_match() # Parse blue (third number) blue_parse_result = self.second_nat_parser.parse_input( green_parse_result.get_remainder()) if not blue_parse_result.is_successful(): return failure blue = blue_parse_result.get_match() # Check if provided values form valid RGB color if 0 <= red <= 255 and 0 <= green <= 255 and 0 <= blue <= 255: return Success((red, green, blue), blue_parse_result.get_remainder()) else: return failure
def parse_params(self, cli_input: str) -> ParseResult: if cli_input == '': return Success(LoadCommand(self._controller), '') else: return Success(LoadCommand(self._controller, cli_input), '')
def test_nat_parser(): """ Test NatParser's method "parse_input" used for parsing natural numbers (^\d+(\s+|$)) from a string. """ parser = NatParser() # Test invalid inputs without a delimiter invalid_inputs = [ '+10', '-10', '+1 ', '-1', '+10.5', '10.5', '+ 10', '- 10', '+-10', '+- 10', '1-0', '10+', '+10+10', 'k10', 'k 10', 'k+10', 'k +10', 'k + 10', 'k1k0', '1k0', 'k 10+', 'k1', ' +10', '10k', '10-k', '1k', '+10lalala', "", "lala", " ", " ", "+", '10#', '10$', '10^^', '10^4', '10>', # special characters '10.', '10:', '10-', '1/', '1\\', '10\"', "10\'", '10?', '1!', # word delimiters '10(', '10)', '10{', '10}', '10[', '10]' ] # brackets for cli_input in invalid_inputs: result = parser.parse_input(cli_input) assert isinstance(result, Failure) # Test invalid inputs with a delimiter invalid_inputs = [('+10_', '.'), ('10_', ','), ('10.$', '$'), ('10, 20', ' ')] for cli_input, delimiter in invalid_inputs: parser = NatParser(delimiter) result = parser.parse_input(cli_input) assert isinstance(result, Failure) # Test valid inputs valid_inputs = [ ('10,+20', ',', 10, '+20'), ('10 some string +10 yes', ' ', 10, 'some string +10 yes'), ('10A delimiterA remainder', 'A delimiter', 10, 'A remainder'), ('10', '', 10, ''), (' 10 ', '', 10, ''), (' 10', '', 10, ''), ('10 ', '', 10, ''), ('10, 12, ', ',', 10, '12, '), (' 10 , 20', ',', 10, '20'), (' 10,20', ',', 10, '20'), (' 10 ,20 ', ',', 10, '20 '), (' 10, 20', ',', 10, '20'), ('10, ', ',', 10, '') ] for cli_input, delimiter, expected, remainder in valid_inputs: parser = NatParser(delimiter) result = parser.parse_input(cli_input) assert result == Success(expected, remainder)