def or_else(*parsers, **kwargs): """ Applies parsers one by one. At least one parsers must be successful to return Success """ label = kwargs.get('label', None) if label is None: label = '[' + ''.join([p.label for p in parsers]) + ']' if len(parsers) == 1: return bp.LParser(parsers[0], label) if len(parsers) == 2: def internal(txt): res1 = parsers[0](txt) if res1.isSuccess: return res1 res2 = parsers[1](txt) if res2.isSuccess: return res2 return res.Failure('error') return bp.LParser(internal, label) parser_new = or_else(parsers[0], parsers[1]) new_parsers = [parser_new] + list(parsers[2:]) return or_else(*new_parsers, label=label)
def and_then(*parsers, **kwargs): """ Applies parsers one by one. All parsers must be successful to return Success """ label = kwargs.get('label', None) if label is None: label = ''.join([p.label for p in parsers]) if len(parsers) == 1: return bp.LParser(parsers[0], label) if len(parsers) == 2: def internal(txt): res1 = parsers[0](txt) if res1.isFailure: return res1 char1, remaining1 = res1.value res2 = parsers[1](remaining1) if isinstance(res2, res.Failure): return res2 char2, remaining2 = res2.value return res.Success((char1 + char2, remaining2)) return bp.LParser(internal, label) parser_new = and_then(parsers[0], parsers[1]) new_parsers = [parser_new] + list(parsers[2:]) return and_then(*new_parsers, label=label)
def test_CorrectlyHandlesDifferentNumberOfArguments(self): # Arrange txt1 = 'lorem ipsum dolor sit amet' txt2 = 'ipsum dolor sit amet' txt3 = 'dolor sit amet' txt4 = 'sit amet' parser_l = bpr.LParser('l') parser_o = bpr.LParser('i') parser_r = bpr.LParser('d') parser_e = bpr.LParser('s') parser = cmb.or_else(parser_l, parser_o, parser_r, parser_e) # Act result1 = parser(txt1) result2 = parser(txt2) result3 = parser(txt3) result4 = parser(txt4) expected1 = res.Success(('l', 'orem ipsum dolor sit amet')) expected2 = res.Success(('i', 'psum dolor sit amet')) expected3 = res.Success(('d', 'olor sit amet')) expected4 = res.Success(('s', 'it amet')) # Assert self.assertEqual(result1, expected1) self.assertEqual(result2, expected2) self.assertEqual(result3, expected3) self.assertEqual(result4, expected4)
def test_CorrectlyHandlesDifferentNumberOfArguments(self): # Arrange txt = 'aabbacccadd1234' parser_a = bpr.LParser('a') parser_b = bpr.LParser('b') parser_c = bpr.LParser('c') parser_d = bpr.LParser('d') # Act parser1 = cmb.many1(parser_a) parser2 = cmb.many1(parser_a, parser_b) parser3 = cmb.many1(parser_a, parser_b, parser_c) parser4 = cmb.many1(parser_a, parser_b, parser_c, parser_d) result1 = parser1(txt) result2 = parser2(txt) result3 = parser3(txt) result4 = parser4(txt) expected1 = res.Success(('aa', 'bbacccadd1234')) expected2 = res.Success(('aabba', 'cccadd1234')) expected3 = res.Success(('aabbaccca', 'dd1234')) expected4 = res.Success(('aabbacccadd', '1234')) # Assert self.assertEqual(result1, expected1) self.assertEqual(result2, expected2) self.assertEqual(result3, expected3) self.assertEqual(result4, expected4)
def test_CorrectlyHandlesDifferentNumberOfArguments(self): # Arrange txt = 'lorem ipsum' parser_l = bpr.LParser('l') parser_o = bpr.LParser('o') parser_r = bpr.LParser('r') parser_e = bpr.LParser('e') parser1 = cmb.and_then(parser_l) parser2 = cmb.and_then(parser_l, parser_o) parser3 = cmb.and_then(parser_l, parser_o, parser_r) parser4 = cmb.and_then(parser_l, parser_o, parser_r, parser_e) # Act result1 = parser1(txt) result2 = parser2(txt) result3 = parser3(txt) result4 = parser4(txt) expected1 = res.Success(('l', 'orem ipsum')) expected2 = res.Success(('lo', 'rem ipsum')) expected3 = res.Success(('lor', 'em ipsum')) expected4 = res.Success(('lore', 'm ipsum')) # Assert self.assertEqual(result1, expected1) self.assertEqual(result2, expected2) self.assertEqual(result3, expected3) self.assertEqual(result4, expected4)
def parse_character(*chars, **kwargs): """ returns parser that match one of the strings given """ label = kwargs.get('label', None) if len(chars) == 1 and len(chars[0]) > 1: return cmb.or_else(*[bp.LParser(char) for char in chars[0]], label=label) return cmb.or_else(*[bp.LParser(char) for char in chars], label=label)
def test_AddingLabel(self): # Arrange parser1 = bpr.LParser('l') parser2 = bpr.LParser('o') parser = cmb.rightparser(parser1, parser2, label='Parser: lor') parser_default_label = cmb.rightparser(parser1, parser2) # Assert self.assertEqual(parser.label, 'Parser: lor') self.assertEqual(parser_default_label.label, 'lo')
def test_AddingLabel(self): # Arrange parser1 = bpr.LParser('x') parser2 = bpr.LParser(',') parser = cmb.sep_by1(parser1, parser2, label='Parser: lor') parser_default_label = cmb.sep_by1(parser1, parser2) # Assert self.assertEqual(parser.label, 'Parser: lor') self.assertEqual(parser_default_label.label, 'x[,x]+')
def test_AddingLabel(self): # Arrange parser1 = bpr.LParser('l') parser2 = bpr.LParser('o') parser3 = bpr.LParser('r') parser = cmb.and_then(parser1, parser2, parser3, label='Parser: lor') parser_default_label = cmb.and_then(parser1, parser2, parser3) # Assert self.assertEqual(parser.label, 'Parser: lor') self.assertEqual(parser_default_label.label, 'lor')
def parse_float(label='FLOAT'): """ returns parser for floating point number """ sign = cmb.opt(bp.LParser('-')) coma = bp.LParser('.') e = cmb.or_else(bp.LParser('e'), bp.LParser('E')) exponent = cmb.opt(cmb.and_then(e, parse_integer())) integer = parse_integer() return cmb.and_then(sign, cmb.opt(integer), coma, parse_digits(), exponent, label=label)
def test_NoMatch(self): # Arrange txt = 'aabbaccca1234' parser1 = bpr.LParser('x') parser2 = bpr.LParser('y') # Act parser = cmb.many(parser1, parser2) result = parser(txt) expected = res.Success(('', 'aabbaccca1234')) # Assert self.assertEqual(result, expected)
def test_Failure(self): # Arrange txt = 'aabbaccca1234' parser1 = bpr.LParser('x') parser2 = bpr.LParser('y') # Act parser = cmb.many1(parser1, parser2) result = parser(txt) expected = res.Failure('error') # Assert self.assertEqual(result, expected)
def test_CorrectlyHandlesDifferentNumberOfArguments_Failure(self): # Arrange txt = 'Lorem' parser_1 = bpr.LParser('1') parser_2 = bpr.LParser('2') parser_3 = bpr.LParser('3') parser = cmb.until(parser_1, parser_2, parser_3) # Act result = parser(txt) expected = res.Failure.unit('No more input') # Arrange self.assertEqual(result, expected)
def test_SuccessCase(self): # Arrange txt = 'aabbaccca1234' parser1 = bpr.LParser('a') parser2 = bpr.LParser('b') parser3 = bpr.LParser('c') # Act parser = cmb.many1(parser1, parser2, parser3) result = parser(txt) expected = res.Success(('aabbaccca', '1234')) # Assert self.assertEqual(result, expected)
def test_WhenFirstLettersAreIncorrect_ReturnFailure(self): # Arrange txt = 'lorem ipsum' parser1 = bpr.LParser('l') parser2 = bpr.LParser('x') parser3 = bpr.LParser('r') parser = cmb.and_then(parser1, parser2, parser3) # Act result = parser(txt) expected = res.Failure('error') # Assert self.assertEqual(result, expected)
def test_WhenFirstLettersAreCorrect_ReturnSuccess(self): # Arrange txt = 'lorem ipsum' parser1 = bpr.LParser('l') parser2 = bpr.LParser('o') parser3 = bpr.LParser('r') parser = cmb.and_then(parser1, parser2, parser3) # Act result = parser(txt) expected = res.Success(('lor', 'em ipsum')) # Assert self.assertEqual(result, expected)
def test_FailureCase(self): # Arrange txt = 'Lorem' parser1 = bpr.LParser('l') parser2 = bpr.LParser('x') parser3 = bpr.LParser('i') # Act parser = cmb.or_else(parser1, parser2, parser3) result = parser(txt) expected = res.Failure('error') # Assert self.assertEqual(result, expected)
def test_ParserParseEverything_ReturnsSuccess(self): # Arrange txt = 'aabbaccca' parser_a = bpr.LParser('a') parser_b = bpr.LParser('b') parser_c = bpr.LParser('c') parser_d = bpr.LParser('d') # Act parser = cmb.many1(parser_a, parser_b, parser_c, parser_d) result = parser(txt) expected = res.Success(('aabbaccca', '')) # Assert self.assertEqual(result, expected)
def sep_by1(item, sep, **kwargs): """ parses one or more occurrences of a parser with a separator """ label = kwargs.get('label', None) if label is None: label = item.label + '[' + sep.label + item.label + ']+' sep_items = sep_by(item, sep) parser = and_then(item, sep, sep_items) def internal(txt): if txt == "": return res.Failure.unit('error') fst_res = item(txt) if fst_res.isFailure: return res.Failure('error') parsed, remaining = fst_res.value snd_res = sep(remaining) if snd_res.isFailure: return res.Success((parsed, remaining)) result = parser(txt) if result.isSuccess: parsed, remaining = result.value return res.Success.unit((parsed, remaining)) return res.Failure('error') return bp.LParser(internal, label)
def sep_by(item, sep, **kwargs): """ parses zero or more occurrences of a parser with a separator """ label = kwargs.get('label', None) if label is None: label = item.label + '[' + sep.label + item.label + ']+' sep_item = and_then(sep, item) sep_items = many(sep_item) def internal(txt): if txt == "": return res.Success.unit(('', '')) ret = '' fst_element = item(txt) if fst_element.isSuccess: parsed, remaining = fst_element.value rest_of_elements = sep_items(remaining) if rest_of_elements.isSuccess: parsed_rest, remaining = rest_of_elements.value return res.Success.unit((parsed + parsed_rest, remaining)) return res.Failure('error') return bp.LParser(internal, label)
def betweenparsers(lparser, mparser, rparser, **kwargs): """ Matches left parsers then mid one and finally right one, return result of middle parser """ label = kwargs.get('label', None) if label is None: label = lparser.label + mparser.label + rparser.label def internal(txt): fst_result = lparser(txt) if fst_result.isFailure: return res.Failure.unit("error") _, remaining = fst_result.value snd_result = mparser(remaining) if snd_result.isFailure: return res.Failure.unit("error") parsed, remaining = snd_result.value trd_result = rparser(remaining) _, remaining = trd_result.value if trd_result.isFailure: return res.Failure.unit("error") return res.Success.unit((parsed, remaining)) return bp.LParser(internal, label)
def until(*parsers, **kwargs): """ matches zero or one occurrence of the specified parsers. """ parser = or_else(*parsers) label = kwargs.get('label', None) if label is None: label = '(.*?)[' + ''.join([p.label for p in parsers]) + ']' def internal(input_txt): ret = "" txt = input_txt anyp = parse_any() checker = parser(txt) while checker.isFailure: parsed, txt = anyp(txt).value ret = ret + parsed checker = parser(txt) if txt == "": break if checker.isSuccess: return res.Success.unit((ret, txt)) return res.Failure("No more input") return bp.LParser(internal, label)
def test_SuccessCase(self): # Arrange txt1 = 'Lorem' txt2 = 'ipsum' parser1 = bpr.LParser('L') parser2 = bpr.LParser('x') parser3 = bpr.LParser('i') # Act parser = cmb.or_else(parser1, parser2, parser3) result1 = parser(txt1) result2 = parser(txt2) expected1 = res.Success(('L', 'orem')) expected2 = res.Success(('i', 'psum')) # Assert self.assertEqual(result1, expected1) self.assertEqual(result2, expected2)
def test_SuccessCase(self): # Arrange txt1 = 'Lorem1' txt2 = 'Lorem#ipsum' parser_1 = bpr.LParser('1') parser_sharp = bpr.LParser('#') # Act parser = cmb.until(parser_1, parser_sharp) result1 = parser(txt1) result2 = parser(txt2) expected1 = res.Success(('Lorem', '1')) expected2 = res.Success(('Lorem', '#ipsum')) # Assert self.assertEqual(result1, expected1) self.assertEqual(result2, expected2)
def parse_string(string, label=None): """ returns parser of a given string """ if not isinstance(string, str): raise Exception('Expected string') return cmb.and_then(*[bp.LParser(char) for char in string], label=label)
def test_WrongArgumentPassedToConstructor_RaisesException(self): # Act with self.assertRaises(Exception) as context: bp.LParser(123) # Assert self.assertEqual('Incorrect initialization of parsers', str(context.exception))
def many(*parsers, **kwargs): """ matches zero or more occurrences of the specified parsers. """ label = kwargs.get('label', None) if label is None: label = '(' + ''.join([p.label for p in parsers]) + ')*' if len(parsers) == 0: raise Exception("Expected parser") if len(parsers) == 1: parser = parsers[0] elif len(parsers) > 1: parser = or_else(*parsers) def internal(txt): fst_res = parser(txt) if fst_res.isFailure: return res.Success.unit(("", txt)) parsed1, remaining1 = fst_res.value parsed2, remaining2 = internal(remaining1).value ret = (parsed1 + parsed2, remaining2) return res.Success.unit(ret) return bp.LParser(internal, label=label)
def test_CorrectlyHandlesDifferentNumberOfArguments_Success(self): # Arrange txt1 = '1789' txt2 = '789' parser_1 = bpr.LParser('1') parser_2 = bpr.LParser('2') parser_3 = bpr.LParser('3') parser_4 = bpr.LParser('4') # Act parser = cmb.opt(parser_1, parser_2, parser_3, parser_4) result1 = parser(txt1) result2 = parser(txt2) expected1 = res.Success(('1', '789')) expected2 = res.Success(('', '789')) # Assert self.assertEqual(result1, expected1) self.assertEqual(result2, expected2)
def test_CorrectlyHandlesDifferentNumberOfArguments_Failure(self): # Arrange txt1 = '11789' txt2 = '123789' parser_1 = bpr.LParser('1') parser_2 = bpr.LParser('2') parser_3 = bpr.LParser('3') parser_4 = bpr.LParser('4') # Act parser = cmb.opt(parser_1, parser_2, parser_3, parser_4) result1 = parser(txt1) result2 = parser(txt2) expected1 = res.Failure('error') expected2 = res.Failure('error') # Assert self.assertEqual(result1, expected1) self.assertEqual(result2, expected2)
def test_WhenFirstLetterIsCorrect_ReturnsSuccess(self): # Arrange txt = 'Lorem ipsum' parser = bp.LParser('L') # Act result = parser(txt) expected = res.Success(('L', 'orem ipsum')) # Assert self.assertEqual(result, expected)