bs
-
Python,
pasted
on Aug 8:
|
# Pythonic Perversities: the Guido taunts - part I
# Parser Combinators in Python - for shits and giggles, but mostly for shits.
#
# ``We deployed this lib on our prod server. Picture of cat.'' - codinghorror.com
# ``I am literally on it this time. This stuff is hot'' - joelonsoftware.com
# ``We use it to open the pod bay doors.'' - anonymous blogger
# The author of this grand work accepts donations and is also available for
# Python related consulting and coding jobs. He's expensive though and currently running
# from the government for a crime he didn't commit (allegedly he used an catamorphism in public --
# I know this is something he would never condone), but he will lambda your shit up if you can find him.
# Features:
# Uses nested lambdas to simulate currying and laziness (OOOH YEAH!)
# Uses nested ternary operators and list comprehensions to be able to write everything as a lambda (IT'S SHORTER)
# Can ostensibly be used to write context free and context dependent grammars
# Gives strange non-descriptive errors that you won't see using conventional Python, making it hard to write context free and context dependent grammars
# Only suitable for real programmers
import operator
satisfy = lambda charToBool: lambda text: ([(text[0], text[1:])]
if charToBool(text[0]) else []) if text != "" else []
isChar = lambda c: satisfy(lambda x: c == x)
letter = satisfy( str.isalpha)
digit = satisfy(str.isdigit)
alphanumeric = satisfy(str.isalnum)
token = lambda token: lambda text: [(token, text[len(token):])] if token == text[:len(token)] else []
comb = lambda parser1, parser2: lambda text: [ (v1(v2), t2)
for v1, t1 in parser1(text)
for v2, t2 in parser2(t1)]
choice = lambda parser1, parser2: lambda text: parser1(text) + parser2(text)
# Extra combinators
just = lambda parser: lambda text: [ (v, "") for (v, t) in parser(text) if t == "" ]
succeed = lambda value: lambda text: [ (value, text) ]
option = lambda parser, value: choice(parser, succeed(value))
fail = lambda parser: lambda text: []
mapp = lambda f, parser: lambda text: [ ( f(v), t ) for v, t in parser(text) ]
anyp = lambda plist: lambda text: [ v for p in plist for v in p(text) if v != [] ]
parse = lambda parser, text: just(parser)(text)
# holy shit -- lazy evaluation!
many = lambda parser: option(comb(mapp(lambda a: lambda b: [a] + b, parser), lambda x: many(parser)(x)), [])
many1 = lambda parser: comb(mapp(lambda a: lambda b: [a] + b, parser), many(parser))
# extras for characters; automatically flatten list (ie. concatenate, dumbledoremorphism)
concat = lambda xs: reduce(operator.add, xs, "")
manyc = lambda parser: mapp(concat, many(parser))
many1c = lambda parser: mapp(concat, many1(parser))
# Some helper functions for my new language that will soon take over the world by storm.
integer = many1(digit)
whitespace = many1c(satisfy(str.isspace))
space = many1c(isChar(" "))
# Example: counting highest matching parentheses -
# IT'S CRAZY INTUITIVE!
parens = lambda text: choice( comb(comb(comb(mapp(lambda x: lambda h1: lambda y: lambda h2: max(1+h1, h2), isChar("(")), parens), isChar(")")), parens) , succeed(0))(text)
print parse(parens, "()((()))")
# Here's a bigger tree generating parser. I know you want it.
class ParseTree:
def __init__(self, value):
self.value = value
def __str__(self):
return "%s {%s}" % (self.__class__.__name__, self.value)
def __repr__(self):
return str(self)
class Identifier(ParseTree): pass
class IdentifierList(ParseTree): pass
class Operator(ParseTree): pass
class Lambda(ParseTree): pass
class Expression(ParseTree): pass
class ApplyOperator(ParseTree): pass
class ApplyFunction(ParseTree): pass
class Program(ParseTree): pass
spacepad = lambda parser: choice( comb(mapp( lambda a: lambda b: b, whitespace), parser), parser)
spacepad1 = lambda parser: comb(mapp( lambda a: lambda b: b, whitespace), parser)
ident = comb(mapp(lambda a: lambda b: Identifier(a + b), spacepad(letter)), manyc(alphanumeric))
identlist = comb(mapp(lambda a: lambda b: IdentifierList([a] + b), ident),
many(comb(mapp(lambda a: lambda b: b, whitespace),ident)))
funcapply = comb(mapp(lambda a: lambda b: ApplyFunction([a] + b), ident),
many1(spacepad1(ident)))
simple_expr = anyp([ident, funcapply])
compound_expr = anyp([simple_expr, lambda text: operapply(text)])
expr = anyp([lambda text: _lambda(text), compound_expr])
program = comb(comb(mapp(lambda a: lambda b: lambda c: Program([a] + b), expr),
many(comb(mapp(lambda a: lambda b: b, isChar(";")), expr))), many(satisfy(str.isspace)))
oper = mapp(Operator, spacepad(many1c(anyp(map(isChar, ["=", "-", "|", "!", "<", ">", "$", "+", "/"])))))
operapply = comb(comb(mapp(lambda a: lambda o: lambda b: ApplyOperator([a, o, b]),
simple_expr), oper), compound_expr)
_lambda = comb(comb(comb(mapp(lambda a: lambda idents: lambda c: lambda exp:
Lambda([idents,exp]), spacepad(isChar('\\'))), identlist),
spacepad(token("->"))), expr)
print parse(program, """
\\ a b -> a + b + c;
b
""")
|
Output:
|
[(3, '')]
[(Program {[Lambda {[IdentifierList {[Identifier {a}, Identifier {b}]}, ApplyOperator {[Identifier {a}, Operator {+}, ApplyOperator {[Identifier {b}, Operator {+}, Identifier {c}]}]}]}, Identifier {b}]}, '')]
|
|