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 - Smart alignment in all printouts
- Full-reduce option - Full-reduce option
- Set reduction limit command - Set reduction limit command
- Runtime limit
## Mention in Docs ## Mention in Docs
- lambda functions only work with single-letter arguments - 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 from pyparsing import exceptions as ppx
import lamb.parser
import lamb.runner as runner import lamb.runner as runner
import lamb.tokens as tokens import lamb.tokens as tokens
import lamb.utils as utils import lamb.utils as utils
@ -32,6 +33,7 @@ def _(event):
event.current_buffer.insert_text("λ") event.current_buffer.insert_text("λ")
"""
r = runner.Runner( r = runner.Runner(
prompt_session = PromptSession( prompt_session = PromptSession(
style = utils.style, style = utils.style,
@ -43,10 +45,178 @@ r = runner.Runner(
("class:prompt", "~~> ") ("class:prompt", "~~> ")
]), ]),
) )
"""
r.run_lines([ class Node:
"T = λab.a", def __init__(self):
"F = λab.b", # 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)", "NOT = λa.(a F T)",
"AND = λab.(a F b)", "AND = λab.(a F b)",
"OR = λab.(a T b)", "OR = λab.(a T b)",
@ -59,44 +229,4 @@ r.run_lines([
"Z = λn.n (λa.F) T", "Z = λn.n (λa.F) T",
"MULT = λnmf.n (m f)", "MULT = λnmf.n (m f)",
"H = λp.((PAIR (p F)) (S (p 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_expr <<= (
self.pp_church ^ #self.pp_church ^
self.pp_lambda_fun ^ self.pp_lambda_fun ^
self.pp_name ^ self.pp_name ^
(self.lp + self.pp_expr + self.rp) ^ (self.lp + self.pp_expr + self.rp) ^
@ -60,31 +60,31 @@ class LambdaParser:
self.pp_all = ( self.pp_all = (
self.pp_expr ^ self.pp_expr ^
self.pp_macro_def ^ self.pp_macro_def ^
self.pp_command ^ #self.pp_command ^
self.pp_call self.pp_call
) )
def __init__( def __init__(
self, self,
*, *,
action_command, #action_command,
action_macro_def, #action_macro_def,
action_church, #action_church,
action_func, action_func,
action_bound, action_bound,
action_macro, action_macro,
action_apply action_call
): ):
self.make_parser() self.make_parser()
self.pp_command.set_parse_action(action_command) #self.pp_command.set_parse_action(action_command)
self.pp_macro_def.set_parse_action(action_macro_def) #self.pp_macro_def.set_parse_action(action_macro_def)
self.pp_church.set_parse_action(action_church) #self.pp_church.set_parse_action(action_church)
self.pp_lambda_fun.set_parse_action(action_func) self.pp_lambda_fun.set_parse_action(action_func)
self.pp_macro.set_parse_action(action_macro) self.pp_macro.set_parse_action(action_macro)
self.pp_bound.set_parse_action(action_bound) 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): def parse_line(self, line: str):
return self.pp_all.parse_string( return self.pp_all.parse_string(