Практика 6.2 — Расширенные комбинаторы regex
Дополнительные задания по комбинаторному и функциональному подходу.
Edit on GitHubИсточник:
pr_Python/pr_Python/pract6_2.ipynb
4. Комбинаторы регулярных выражений
Реализуйте простой встроенный язык подмножества регулярных выражений с помощью ФВП. Комбинаторы имеют следующий общий вид:
def comb(param, ...):
def parse(text, ...):
...
return new_text, ...
return parsedef char(c):
def parse(text, pos):
if pos >= len(text) or text[pos] != c:
return text, pos
return text, pos + 1
return parse
def seq(*parsers):
def parse(text, pos):
for p in parsers:
text, pos = p(text, pos)
if pos == -1:
return text, pos
return text, pos
return parse
def alt(*parsers):
def parse(text, pos):
for p in parsers:
new_text, new_pos = p(text, pos)
if new_pos != -1:
return new_text, new_pos
return text, -1
return parse
def rep(p):
def parse(text, pos):
while pos < len(text):
text, pos = p(text, pos)
return text, pos
return parse
def opt(p):
def parse(text, pos):
new_text, new_pos = p(text, pos)
return new_text, new_pos if new_pos != -1 else pos
return parse
parse_a = char('a')
parse_b = char('b')
parser = seq(rep(parse_a), opt(parse_b))
result = parser("aaabbaa", 0)
if result[1] == -1:
print("Failed to parse")
else:
print("Parsed successfully")4.1. (уровень сложности: высокий)
Реализуйте комбинаторы sym (разобрать символ) и seq (разобрать последовательность).
Пример использования:
text = 'abc'
re1 = seq(sym('a'), sym('b'), sym('c'))
re2 = seq(sym('a'), sym('z'), sym('c'))
print(re1(text) is not None)
print(re2(text) is not None)
True
Falsedef sym(c):
def parse(text, pos):
if pos < len(text) and text[pos] == c:
return pos + 1
return None
return parse
def seq(*parsers):
def parse(text, pos):
for p in parsers:
pos = p(text, pos)
if pos is None:
return None
return pos
return parse
text = 'abc'
re1 = seq(sym('a'), sym('b'), sym('c'))
re2 = seq(sym('a'), sym('z'), sym('c'))
print(re1(text) is not None)
print(re2(text) is not None)4.2. (уровень сложности: средний)
Реализуйте комбинатор range_of (разобрать диапазон символов).
Пример использования:
digit = range_of('0', '9')
number = seq(digit, digit)
print(number('42') is not None)
print(number('ab') is not None)True
Falsedef range_of(start, end):
def parse(text, pos):
if pos < len(text) and start <= text[pos] <= end:
return pos + 1
return None
return parse
def seq(*parsers):
def parse(text, pos):
for p in parsers:
pos = p(text, pos)
if pos is None:
return None
return pos
return parse
digit = range_of('0', '9')
number = seq(digit, digit)
print(number('42') is not None)
print(number('ab') is not None)4.3. (уровень сложности: высокий)
Реализуйте комбинатор alt (альтернатива).
Пример использования:
digit = range_of('0', '9')
hex_digit = alt(digit, range_of('a', 'f'), range_of('A', 'F'))
space = alt(sym(' '), sym('\n'), sym('\t'))
hex_color = seq(sym('#'), hex_digit, hex_digit, hex_digit, hex_digit, hex_digit, hex_digit)
print(hex_color('#ffaa43') is not None)
print(hex_color('#xxxxxx') is not None)True
Falsedef alt(*parsers):
def parse(text, pos):
for p in parsers:
new_pos = p(text, pos)
if new_pos is not None:
return new_pos
return None
return parse
def range_of(start, end):
def parse(text, pos):
if pos < len(text) and start <= text[pos] <= end:
return pos + 1
return None
return parse
def sym(char):
def parse(text, pos):
if pos < len(text) and text[pos] == char:
return pos + 1
return None
return parse
def seq(*parsers):
def parse(text, pos):
for p in parsers:
pos = p(text, pos)
if pos is None:
return None
return pos
return parse
digit = range_of('0', '9')
hex_digit = alt(digit, range_of('a', 'f'), range_of('A', 'F'))
space = alt(sym(' '), sym('\n'), sym('\t'))
hex_color = seq(sym('#'), hex_digit, hex_digit, hex_digit, hex_digit, hex_digit, hex_digit)
print(hex_color('#ffaa43') is not None)
print(hex_color('#xxxxxx') is not None)4.4. (уровень сложности: высокий)
Реализуйте комбинатор group (группировка) для извлечения разобранных фрагментов.
Пример использования:
digit = range_of('0', '9')
hex_digit = alt(digit, range_of('a', 'f'), range_of('A', 'F'))
space = alt(sym(' '), sym('\n'), sym('\t'))
hex_color = seq(sym('#'), group(seq(hex_digit, hex_digit, hex_digit, hex_digit, hex_digit, hex_digit)))
hex_colors = seq(hex_color, sym(' '), hex_color)
print(hex_colors('#ffaa43 #123456', []))('', ['ffaa43', '123456'])def group(rule):
def inner(text, start):
result, end = rule(text, start)
if result:
return ([result], end)
return (result, end)
return inner
def alt(*args):
pass
def range_of(start, end):
pass
def sym(char):
pass
def seq(*args):
pass
digit = range_of('0', '9')
hex_digit = alt(digit, range_of('a', 'f'), range_of('A', 'F'))
space = alt(sym(' '), sym('\n'), sym('\t'))
hex_color = seq(sym('#'), group(seq(hex_digit, hex_digit, hex_digit, hex_digit, hex_digit, hex_digit)))
hex_colors = seq(hex_color, sym(' '), hex_color)
def parse_hex_colors(text, start):
result, end = hex_colors(text, start)
return ('', result)
print(parse_hex_colors('#ffaa43 #123456', 0))