Skip to content

Instantly share code, notes, and snippets.

@nerodono
Last active April 24, 2024 16:34
Show Gist options
  • Save nerodono/d87aee7fb6f51efc88638b3b2ecfa576 to your computer and use it in GitHub Desktop.
Save nerodono/d87aee7fb6f51efc88638b3b2ecfa576 to your computer and use it in GitHub Desktop.
Another thing that I did on my phone (omg this was fucking suffering)
from functools import reduce as foldl
import string
import pprint
import builtins
class ParseFail(Exception):
...
def group_fails(message, *fails):
return ParseFail(f"{message}: {fails!r}")
def concat(x, y):
return x + y
def take_n(n):
def take_n_impl(src):
if len(src) < n:
raise ParseFail(f"{src!r} not enough data (needed {n})")
return src[:n], src[n:]
return take_n_impl
take1 = take_n(1)
def many(f, acc=concat):
def many_impl(src):
initial, left = f(src)
while True:
try:
cur, left = f(left)
except ParseFail:
break
initial = acc(initial, cur)
return initial, left
return many_impl
def skip(f):
return first(f, lambda _: None)
def ok_if(f, pred, fail_reason):
def ok_if_impl(src):
val, left = f(src)
if pred(val):
return val, left
raise ParseFail(f"{src!r} failed: {fail_reason}")
return ok_if_impl
def first(f, map):
def first_impl(src):
res = f(src)
return (map(res[0]), res[1])
return first_impl
def compose(*funcs):
return foldl(lambda acc, curr: lambda x: acc(curr(x)), funcs)
def one_of(d):
return lambda x: x in d
def name(name_val):
return lambda x: (name_val, x)
def tag(t):
return ok_if(take_n(len(t)), lambda x: x == t, f"expected {t}")
def alt(group_name, *parsers):
if not isinstance(group_name, str):
parsers = (group_name, *parsers)
def alt_impl(src):
errs = []
for f in parsers:
try:
return f(src)
except ParseFail as e:
errs.append(e)
raise group_fails(
f"{group_name} failed to parse {src!r}",
errs
)
return alt_impl
def opt(parser):
def opt_impl(src):
try:
return parser(src)
except ParseFail:
return (None, src)
return opt_impl
def pick_c(n):
return lambda parser: pick(n, parser)
def pick(n, parser):
return first(parser, lambda x: x[n])
def trim(parser, label="no label"):
return pick(0, seq(
ws,
parser,
ws,
))
def seq(*parsers):
def seq_impl(src):
buffer = ()
for f in parsers:
item, src = f(src)
if item is None:
continue
buffer = (*buffer, item)
return buffer, src
return seq_impl
OPS = {
"+": lambda x, y: x + y,
"-": lambda x, y: x - y,
"*": lambda x, y: x * y,
"/": lambda x, y: x // y,
"^": lambda x, y: x**y,
}
def perform(op, *args):
return OPS[op](*args)
singleton = lambda x: (x,)
singleton_of = lambda f: first(f, singleton)
ident_begin_char = ok_if(
take1,
one_of(string.ascii_letters),
"Expected begin ident char"
)
ident_rest_char = ok_if(
take1,
one_of(string.ascii_letters + string.digits + "_?!"),
"Expected rest ident char"
)
ident = first(
seq(ident_begin_char, opt(many(ident_rest_char))),
lambda x: ('binding', x[0] + (x[1:] or ('',))[0])
)
skip_opt = compose(opt, skip)
whitespace = ok_if(take1, one_of(" \n\t"), "whitespace expected")
whitespaces = many(whitespace)
ws = skip_opt(whitespaces)
digit = ok_if(take1, one_of("0123456789"), "must be valid digit")
number = first(many(digit), compose(name("int"), int))
semicolon = tag(";")
add = tag("+")
sub = tag("-")
mul = tag("*")
div = tag("/")
lt = tag("<")
gt = tag(">")
lte = tag(">=")
gte = tag("<=")
def define_func(expr):
def define_func_impl(left):
(arg, _, body), left = seq(
ident,
trim(tag(":")),
expr
)(left)
return ('defunc', arg, body), left
return define_func_impl
def let_in_chain(expr):
def let_in_chain_impl(left):
assignment = seq(
ident,
trim(tag("=")),
expr,
semicolon,
)
let, left = seq(
tag("let"),
skip(whitespaces),
many(singleton_of(trim(assignment))),
trim(tag("in")),
expr
)(left)
assignments = tuple(
(bind, value)
for bind, _, value, _ in let[1]
)
bind_in = let[3]
return ('let', assignments, bind_in), left
return let_in_chain_impl
def make_item(expr):
return alt(
let_in_chain(expr),
define_func(expr),
pick(1, seq(tag("("), expr, tag(")"))),
number,
ident,
)
def right_associative(f, down):
def ra_impl(left):
x, left = down(left)
try:
op, left = f(left)
except ParseFail:
return x, left
y, left = ra_impl(left)
return (op, x, y), left
return ra_impl
def left_associative(f, down):
def la_impl(left):
x, left = down(left)
while True:
try:
(action, operand), left = f(down, left)
except ParseFail:
break
x = (action, x, operand)
return x, left
return la_impl
def binary(op):
def binary_impl(down, left):
operator, left = op(left)
rhs, left = down(left)
return (operator, rhs), left
return binary_impl
def application(down, left):
operand, left = down(left)
return ('apply', operand), left
# defer name lookup
item = make_item(lambda x: expr(x))
expr = left_associative(
binary(trim(alt(add, sub))),
left_associative(
binary(trim(alt(div, mul))),
right_associative(
trim(tag("^")),
left_associative(
application,
trim(item),
)
),
)
)
def evaluate(tup, ctx):
tag, *args = tup
if tag in OPS:
return OPS[tag](*map(lambda x: evaluate(x, ctx), args))
if tag == 'defunc':
(_, arg), body = args
return lambda x: evaluate(body, {**ctx, arg: x})
elif tag == 'int':
return args[0]
elif tag == 'binding':
return ctx[args[0]]
elif tag == 'apply':
f, arg = args
x = evaluate(arg, ctx)
return evaluate(f, ctx)(x)
elif tag == 'let':
assignments, bind_in = args
for (_, name), value in assignments:
ctx = {**ctx, name: evaluate(value, ctx)}
return evaluate(bind_in, ctx)
else:
raise NotImplementedError(f"{tup} (ctx = {ctx})")
print("-- Хуйня --")
print("Может исполнять:")
for i, op in enumerate("+-*/"):
print(f"{i+1}. a {op} b")
print("Еще умеет")
print("1. Функции: (x: x + 10) 20")
print(" (Результат исполнения: 30)")
print("ВАЖНО!! функции могут быть только с одним аргументом, если надо больше — возвращайте функцию из функции, вот так:")
print(" (x: y: x + y) 2 2")
print("2. let-биндинги: let x = 10; y = 20; in x + y")
print("На выходе напечатается результат парсинга и исполнения")
while True:
inp = input("Enter expression: ")
parsed, left = expr(inp)
pprint.pprint(parsed)
if left:
print("Tail =", repr(left))
else:
print("Результат исполнения:", evaluate(parsed, {}))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment