Skip to content

Instantly share code, notes, and snippets.

@markrwilliams
Created September 13, 2013 17:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save markrwilliams/6553973 to your computer and use it in GitHub Desktop.
Save markrwilliams/6553973 to your computer and use it in GitHub Desktop.
import re
from collections import namedtuple
ParseResult = namedtuple('ParseResult', 'captured remaining matched')
class Rule(object):
def __init__(self, value, bind=None):
self.value = value
self.bind = bind
def __repr__(self):
name = self.__class__.__name__
return '%s(value=%r, bind=%r)' % (name, self.value, self.bind)
class Group(object):
def __init__(self, value, bind=None):
for i, v in enumerate(value):
if isinstance(v, basestring):
value[i] = Literal(v)
super(Group, self).__init__(value, bind)
class Literal(Rule):
def __init__(self, *args, **kwargs):
super(Literal, self).__init__(*args, **kwargs)
self.length = len(self.value)
def __call__(self, sliceable):
head, tail = sliceable[:self.length], sliceable[self.length:]
m = False
if self.value == head:
captured, remaining = self.value, tail
m = True
else:
captured, remaining = [], sliceable
return ParseResult(captured=captured, remaining=remaining,
matched=m)
class And(Group, Rule):
def __call__(self, sliceable):
res, prematch_spliceable = [[]], sliceable
for member in self.value:
parsed = member(sliceable)
if not parsed.matched:
res, spliceable = [[]], prematch_spliceable
break
res.append(parsed.captured)
sliceable = parsed.remaining
return ParseResult(captured=res[-1], remaining=sliceable,
matched=parsed.matched)
class Or(Group, Rule):
def __call__(self, sliceable):
for member in self.value:
parsed = member(sliceable)
if parsed.match:
return parsed
return ParseResult(captured=[], remaining=sliceable,
matched=False)
class OneOrZero(Rule):
def __call__(self, sliceable):
# TODO: always results in a bind/action invocation
res = self.value(sliceable)
return res._replace(matched=True)
class OneOrMore(Rule):
def __call__(self, sliceable):
res = [[]]
matched_any = False
while True:
parsed = self.value(sliceable)
if not parsed.matched:
break
matched_any = parsed.matched
res.append(parsed.captured)
sliceable = parsed.remaining
return ParseResult(captured=res[-1], remaining=sliceable,
matched=matched_any)
class ZeroOrMore(OneOrMore):
def __call__(self, sliceable):
res = super(ZeroOrMore, self).__call__(sliceable)
return res._replace(matched=True)
class Pattern(Rule):
def __init__(self, *args, **kwargs):
super(Pattern, self).__init__(*args, **kwargs)
self.value = re.compile(self.value)
def __call__(self, sliceable):
m = self.value.match(sliceable)
if not m:
return ParseResult(captured=[], remaining=sliceable, matched=False)
return ParseResult(captured=m.group(), remaining=sliceable[m.end():],
matched=True)
array = And(['"', Pattern('\w+'),
ZeroOrMore(And([Pattern('\s*'),
',',
Pattern('\s*'),
Pattern('\w+')])),
'"'])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment