Started conversion to Tree reduction

master
Mark 2022-10-23 08:53:26 -07:00
parent 044ec60a49
commit c5df3fcbed
Signed by: Mark
GPG Key ID: AD62BB059C2AAEE4
3 changed files with 185 additions and 54 deletions

View File

@ -20,6 +20,7 @@
- Smart alignment in all printouts
- Full-reduce option
- Set reduction limit command
- Runtime limit
## Mention in Docs
- lambda functions only work with single-letter arguments

View File

@ -8,6 +8,7 @@ from prompt_toolkit.lexers import Lexer
from pyparsing import exceptions as ppx
import lamb.parser
import lamb.runner as runner
import lamb.tokens as tokens
import lamb.utils as utils
@ -32,6 +33,7 @@ def _(event):
event.current_buffer.insert_text("λ")
"""
r = runner.Runner(
prompt_session = PromptSession(
style = utils.style,
@ -43,10 +45,178 @@ r = runner.Runner(
("class:prompt", "~~> ")
]),
)
"""
r.run_lines([
"T = λab.a",
"F = λab.b",
class Node:
def __init__(self):
# The node this one is connected to.
# None if this is the top node.
self.parent: Node | None = None
# True if we're connected to the left side
# of the parent, False otherwise.
self.parent_left: bool | None = None
# Left and right nodes, None if empty
self.left: Node | None = None
self.right: Node | None = None
def set_parent(self, parent, is_left):
self.parent = parent
self.parent_left = is_left
def go_left(self):
if self.left is None:
raise Exception("Can't go left when left is None")
return None, self.left
def go_right(self):
if self.right is None:
raise Exception("Can't go right when right is None")
return None, self.right
def go_up(self):
if self.parent is None:
raise Exception("Can't go up when parent is None")
return self.parent_left, self.parent
def to_node(result_pair) -> Node:
return result_pair[0].from_parse(result_pair[1])
class Macro(Node):
@staticmethod
def from_parse(results):
return Macro(results[0])
def __init__(self, name: str) -> None:
super().__init__()
self.name = name
self.left = None
self.right = None
def __repr__(self):
return f"<macro {self.name}>"
class Func(Node):
@staticmethod
def from_parse(result):
if len(result[0]) == 1:
i = to_node(result[0][0])
below = to_node(result[1])
this = Func(i, below) # type: ignore
below.set_parent(this, True)
return this
else:
i = to_node(result[0].pop(0))
below = Func.from_parse(result)
this = Func(i, below) # type: ignore
below.set_parent(this, True)
return this
def __init__(self, input: Macro, output: Node) -> None:
super().__init__()
self.input = input
self.left = output
self.right = None
def __repr__(self):
return f"<func {self.input!r} {self.left!r}>"
class Call(Node):
@staticmethod
def from_parse(results):
if len(results) == 2:
left = results[0]
if not isinstance(left, Node):
left = to_node(left)
right = to_node(results[1])
this = Call(left, right)
left.set_parent(this, True)
right.set_parent(this, False)
return this
else:
left = results[0]
if not isinstance(left, Node):
left = to_node(left)
right = to_node(results[1])
this = Call(left, right)
left.set_parent(this, True)
right.set_parent(this, False)
return Call.from_parse(
[this] + results[2:]
)
def __init__(self, fn: Node, arg: Node) -> None:
super().__init__()
self.left = fn
self.right = arg
def __repr__(self):
return f"<call {self.left!r} {self.right!r}>"
p = lamb.parser.LambdaParser(
action_func = lambda x: (Func, x),
action_bound = lambda x: (Macro, x),
action_macro = lambda x: (Macro, x),
action_call = lambda x: (Call, x)
)
def traverse(node: Node):
ptr = node
back_from_left = None
out = ""
while True:
if isinstance(ptr, Macro):
out += ptr.name
back_from_left, ptr = ptr.go_up()
if isinstance(ptr, Func):
if back_from_left is None:
if isinstance(ptr.parent, Func):
out += ptr.input.name
else:
out += "λ" + ptr.input.name
if not isinstance(ptr.left, Func):
out += "."
back_from_left, ptr = ptr.go_left()
elif back_from_left is True:
back_from_left, ptr = ptr.go_up()
if isinstance(ptr, Call):
if back_from_left is None:
out += "("
back_from_left, ptr = ptr.go_left()
elif back_from_left is True:
out += " "
back_from_left, ptr = ptr.go_right()
elif back_from_left is False:
out += ")"
back_from_left, ptr = ptr.go_up()
if ptr.parent is None:
break
return out
for l in [
"λab.(a (NOT a b) b)",
"λnmf.n (m f)",
"λf.(λx. f(x x))(λx.f(x x))",
]:
i = p.parse_line(l)
#print(i)
n = to_node(i)
#print(n)
print(traverse(n))
"""
"NOT = λa.(a F T)",
"AND = λab.(a F b)",
"OR = λab.(a T b)",
@ -59,44 +229,4 @@ r.run_lines([
"Z = λn.n (λa.F) T",
"MULT = λnmf.n (m f)",
"H = λp.((PAIR (p F)) (S (p F)))",
"D = λn.n H (PAIR 0 0) T",
"FAC = λyn.(Z n)(1)(MULT n (y (D n)))"
])
while True:
try:
i = r.prompt()
# Catch Ctrl-C and Ctrl-D
except KeyboardInterrupt:
printf("\n\nGoodbye.\n")
break
except EOFError:
printf("\n\nGoodbye.\n")
break
# Skip empty lines
if i.strip() == "":
continue
# Try to run an input line.
# Catch parse errors and point them out.
try:
x = r.run(i)
except ppx.ParseException as e:
l = len(to_plain_text(r.prompt_session.message))
printf(FormattedText([
("class:err", " "*(e.loc + l) + "^\n"),
("class:err", f"Syntax error at char {e.loc}."),
("class:text", "\n")
]), style = utils.style)
continue
except tokens.ReductionError as e:
printf(FormattedText([
("class:err", f"{e.msg}\n")
]), style = utils.style)
continue
printf("")
])"""

View File

@ -47,7 +47,7 @@ class LambdaParser:
)
self.pp_expr <<= (
self.pp_church ^
#self.pp_church ^
self.pp_lambda_fun ^
self.pp_name ^
(self.lp + self.pp_expr + self.rp) ^
@ -60,31 +60,31 @@ class LambdaParser:
self.pp_all = (
self.pp_expr ^
self.pp_macro_def ^
self.pp_command ^
#self.pp_command ^
self.pp_call
)
def __init__(
self,
*,
action_command,
action_macro_def,
action_church,
#action_command,
#action_macro_def,
#action_church,
action_func,
action_bound,
action_macro,
action_apply
action_call
):
self.make_parser()
self.pp_command.set_parse_action(action_command)
self.pp_macro_def.set_parse_action(action_macro_def)
self.pp_church.set_parse_action(action_church)
#self.pp_command.set_parse_action(action_command)
#self.pp_macro_def.set_parse_action(action_macro_def)
#self.pp_church.set_parse_action(action_church)
self.pp_lambda_fun.set_parse_action(action_func)
self.pp_macro.set_parse_action(action_macro)
self.pp_bound.set_parse_action(action_bound)
self.pp_call.set_parse_action(action_apply)
self.pp_call.set_parse_action(action_call)
def parse_line(self, line: str):
return self.pp_all.parse_string(