initial (hacky af)
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
__pycache__/
|
0
examples/__init__.py
Normal file
0
examples/__init__.py
Normal file
18
examples/bf.py
Normal file
18
examples/bf.py
Normal 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
9
examples/calc.py
Normal 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
13
examples/golike.py
Normal 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
2
gll/__init__.py
Normal 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
152
gll/parser.py
Normal 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
72
gll/result.py
Normal 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)
|
Reference in New Issue
Block a user