Created
May 30, 2018 15:14
-
-
Save PaperclipBadger/43f93f36754ae06854869c7f4f144ba8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
parser_combinators.py | |
-------------------- | |
Functions that make it easy to create string parsers. | |
A parser is a function that takes a string ``remaining`` and returns a pair. | |
If the parser was unsuccessful (the string does not patch the pattern of | |
characters that that parser accepts), it returns ``(None, remaining)``. | |
Otherwise, it returns a pair whose first element is the parsing result | |
(which must not be None) and string remaining after the characters required | |
for the successful parse have been consumed. | |
""" | |
import string | |
def many(parser): | |
"""Runs the given parser until it fails, returning a list of results.""" | |
def parser_(remaining): | |
results = [] | |
result, remaining = parser(remaining) | |
while result is not None: | |
results.append(result) | |
result, remaining = parser(remaining) | |
return results, remaining | |
return parser_ | |
def any_(*parsers): | |
"""Tries parsers in order and returns the first successful result.""" | |
def parser_(remaining): | |
for parser in parsers: | |
result, remaining_ = parser(remaining) | |
if result is not None: | |
return result, remaining_ | |
else: | |
return None, remaining | |
return parser_ | |
def seq(*parsers): | |
"""Runs parsers in order and succeeds only if they all succeed.""" | |
def parser_(remaining): | |
for parser in parsers: | |
result, remaining_ = parser(remaining) | |
if result is not None: | |
return result, remaining_ | |
else: | |
return None, remaining | |
return parser_ | |
def change_result(parser, f): | |
"""Runs ``parser`` and if successful, applies ``f`` to the result.""" | |
def parser_(remaining): | |
result, remaining = parser(remaining) | |
if result is not None: | |
result = f(result) | |
return result, remaining | |
return parser_ | |
def one_or_more(parser): | |
"""Like `many`, but the parser must succeed at least once.""" | |
def flatten(result): | |
[resone, resmany] = result | |
return resmany.insert(0, resone) | |
return change_result(seq(parser, many(parser)), flatten) | |
def char(l): | |
"""Match exactly ``l``""" | |
def parser(remaining): | |
if remaining[0] == l: | |
return l, remaining[1:] | |
else: | |
return None, remaining | |
return parser | |
def digit(remaining): | |
"""Match a character in `string.digits`.""" | |
if remaining[0] in string.digits: | |
return remaining[0], remaining[1:] | |
else: | |
return None, remaining | |
def letter(remaining): | |
"""Match a character in `string.ascii_lowercase`.""" | |
if remaining[0] in string.ascii_lowercase: | |
return remaining[0], remaining[1:] | |
else: | |
return None, remaining | |
def white(remaining): | |
"""Match a character in `string.whitespace`.""" | |
if remaining[0] in string.whitespace: | |
return remaining[0], remaining[1:] | |
else: | |
return None, remaining | |
# matches any non-empty sequence of digits and converts it to an int. | |
integer = change_result(one_or_more(digit), lambda ds: int(ds.join(''))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment