initial (hacky af)

This commit is contained in:
2017-02-24 18:38:35 +01:00
commit ffeacdcb6c
8 changed files with 267 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
__pycache__/

0
examples/__init__.py Normal file
View File

18
examples/bf.py Normal file
View File

@@ -0,0 +1,18 @@
import gll
def loop(string):
parser = gll.seq(gll.skip(gll.string("[")),
gll.many(op),
gll.skip(gll.string("]")),
tag="loop")
return parser(string)
op = (gll.string("+", tag="add") |
gll.string("-", tag="sub") |
gll.string(".", tag="out") |
gll.string(",", tag="in") |
gll.string(">", tag="fwd") |
gll.string("<", tag="bck") |
loop)
parser = gll.all(gll.many(op), tag="program")

9
examples/calc.py Normal file
View File

@@ -0,0 +1,9 @@
import gll
ws = gll.skipmany(gll.whitespace(), tag="spaces")
num = gll.many1(gll.digit(), tag="number")
op = gll.string("+", tag="op") | gll.string("-", tag="op")
expr = gll.seq(num, ws, op, ws, num, tag="expr")
parser = gll.many(expr)

13
examples/golike.py Normal file
View File

@@ -0,0 +1,13 @@
import gll
ws = gll.many(gll.whitespace)
function = function
package_name = gll.seq(gll.skip(gll.string("package")), gl.skip(ws),
gll.regex(".*$"), tag="package_name")
package = gll.seq(package_name, gll.many(function, tag="package_body"),
tag="package")
parser = gll.all(package)

2
gll/__init__.py Normal file
View File

@@ -0,0 +1,2 @@
from gll.parser import string, digit, many, many1, whitespace, skip, skipmany,\
opt, regex, seq, all

152
gll/parser.py Normal file
View File

@@ -0,0 +1,152 @@
import re
from gll.result import Success, Failure
class Parser:
def __init__(self, fun, tag=None):
self.fun = fun
self.tag = tag
def __call__(self, string):
return self.fun(string)
def __or__(self, other):
def internal(string):
res = self(string)
if res.valid:
return res
return other(string)
return Parser(internal)
def __rshift__(self, into):
def internal(string):
res = self(string)
return Success(into(res.value), res.rest, self.tag)
return Parser(internal)
def string(match, tag=None):
def internal(string):
ln = len(match)
if len(string) < ln:
return Failure(string)
head = string[0:ln]
tail = string[ln:]
if head == match:
return Success(head, tail, tag)
return Failure(string)
return Parser(internal, tag)
def regex(match, tag=None, reopts=None):
if not reopts:
reopts = []
rx = regex.compile(match, *reopts)
def internal(string):
res = rx.match(string)
if res:
return Success(res.group(0), string[endpos+1:], tag)
return Failure(string)
return Parser(internal, tag)
def digit(tag=None):
def internal(string):
if not string:
return Failure(string)
head = string[0]
tail = string[1:]
if head.isdigit():
return Success(head, tail, tag)
return Failure(string)
return Parser(internal, tag)
def many(parser, tag=None):
def internal(string):
res = parser(string)
if not res.valid:
return Success("", string)
resl = []
while res.valid:
resl.append(res)
rest = res.rest
res = parser(rest)
return Success(resl, rest, tag)
return Parser(internal, tag)
def many1(parser, tag=None):
def internal(string):
res = parser(string)
if not res.valid:
return res
resl = []
while res.valid:
resl.append(res)
rest = res.rest
res = parser(rest)
return Success(resl, rest, tag)
return Parser(internal, tag)
def whitespace(tag=None):
def internal(string):
head = string[0]
tail = string[1:]
if head.strip == "":
return Success(head, tail, tag)
return Failure(string)
return Parser(internal, tag)
def skip(parser, tag=None):
def internal(string):
res = parser(string)
if not res.valid:
return res
return Success("", res.rest, tag)
return Parser(internal, tag)
def skipmany(parser, tag=None):
return many(skip(parser), tag)
def opt(parser, tag=None):
def internal(string):
res = parser(string)
if res.valid:
return res
return Success("", res.rest, tag)
return Parser(internal, tag)
def seq(*parsers, tag=None):
def internal(string):
resl = []
rest = string
for parser in parsers:
res = parser(rest)
if not res.valid:
return Failure(rest)
rest = res.rest
resl.append(res)
return Success(resl, rest, tag)
return Parser(internal, tag)
def all(parser, tag=None):
def internal(string):
res = parser(string)
if res.valid and not res.rest:
return res
return Failure(res.rest)
return Parser(internal, tag)

72
gll/result.py Normal file
View File

@@ -0,0 +1,72 @@
class Token:
def __init__(self, value, tag):
self.value = value
self.tag = tag
def __str__(self):
return "{}({})".format(self.tag or '', self.value)
def __repr__(self):
return str(self)
def __bool__(self):
return bool(self.value)
class Result:
def __init__(self):
raise NotImplemented("This is an abstract base class")
class Success(Result):
def __init__(self, value, rest, tag=None):
self.value = value
self.rest = rest
self.tag = tag
self.valid = True
def __str__(self):
return "Result({}, {})".format(self.tag, self.value)
def __repr__(self):
return str(self)
def __bool__(self):
return bool(self.value)
def tokens(self, purge=False, inner=False):
if type(self.value) == list:
for val in self.value:
if type(val) == Success:
if inner:
for tok in val.tokens(purge):
yield tok
else:
toks = list(val.tokens(purge, inner=True))
if len(toks) == 1:
toks = toks[0]
yield Token(toks, tag=val.tag)
else:
if not purge or val:
if inner:
yield val
else:
yield Token(val, self.tag)
else:
if not purge or self.value:
if inner:
yield self.value
else:
yield Token(self.value, self.tag)
class Failure(Result):
def __init__(self, rest):
self.rest = rest
self.valid = False
def __str__(self):
return "Failure(rest={})".format(self.rest)
def __repr__(self):
return str(self)