Skip to content

Instantly share code, notes, and snippets.

@dhilst
Created August 16, 2020 22:27
Show Gist options
  • Save dhilst/858244a7f6b06c6929a0b022c362ff53 to your computer and use it in GitHub Desktop.
Save dhilst/858244a7f6b06c6929a0b022c362ff53 to your computer and use it in GitHub Desktop.
parse_combinator in python
from typing import *
import re
Result = Tuple[str, Any]
Parser = Callable[[str], Result]
class ParseError(Exception):
pass
def id_(i):
return i
def regex(input: str, p: str, f=id_):
m = re.match(f"\s*{p}", input)
if m is None:
raise ParseError("failed to parse expected {p} found {input}")
return input[m.end() :], f(m.group(0).strip())
def regexp(pattern: str, f=id_) -> Parser:
def inner(input: str):
return regex(input, pattern, f)
return inner
def or_(*parsers: Parser) -> Parser:
def inner(input) -> Result:
for p in parsers:
try:
return p(input)
except ParseError:
continue
raise ParseError(f"None of {parsers} suceeded")
return inner
def sequence(*parsers: Parser) -> Parser:
def inner(input) -> Result:
res = []
for p in parsers:
input, result = p(input)
res.append(result)
return input, res
return inner
def parse(input: str, parser: Parser) -> Result:
return parser(input)
def int_(input: str) -> Result:
return regex(input, r"\d+", int)
def plusminus(input: str) -> Result:
return regex(input, r"[+-]")
def expr(input: str) -> Result:
return or_(sequence(int_, plusminus, expr), int_)(input)
def test1():
res = parse("1", regexp(r"\d", int))
assert ("", 1) == res
def test2() -> None:
res = parse("1 + 1", sequence(int_, plusminus, int_))
print(res)
assert ("", [1, "+", 1]) == res
def test3() -> None:
res = parse("1 + 1 + 1", expr)
print(res)
assert ("", [1, "+", 1, [1, "+", 1]]) == res
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment