Added tree iterator, cloning
parent
a991c3bb91
commit
0dae1afb61
309
lamb/__main__.py
309
lamb/__main__.py
|
@ -48,6 +48,40 @@ r = runner.Runner(
|
||||||
|
|
||||||
macro_table = {}
|
macro_table = {}
|
||||||
|
|
||||||
|
class TreeWalker:
|
||||||
|
def __init__(self, expr):
|
||||||
|
self.expr = expr
|
||||||
|
self.ptr = expr
|
||||||
|
self.from_side = Direction.UP
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
if self.ptr is self.expr.parent:
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
|
out = self.ptr
|
||||||
|
out_side = self.from_side
|
||||||
|
if isinstance(self.ptr, EndNode):
|
||||||
|
self.from_side, self.ptr = self.ptr.go_up()
|
||||||
|
|
||||||
|
elif isinstance(self.ptr, Func):
|
||||||
|
if self.from_side == Direction.UP:
|
||||||
|
self.from_side, self.ptr = self.ptr.go_left()
|
||||||
|
elif self.from_side == Direction.LEFT:
|
||||||
|
self.from_side, self.ptr = self.ptr.go_up()
|
||||||
|
|
||||||
|
elif isinstance(self.ptr, Call):
|
||||||
|
if self.from_side == Direction.UP:
|
||||||
|
self.from_side, self.ptr = self.ptr.go_left()
|
||||||
|
elif self.from_side == Direction.LEFT:
|
||||||
|
self.from_side, self.ptr = self.ptr.go_right()
|
||||||
|
elif self.from_side == Direction.RIGHT:
|
||||||
|
self.from_side, self.ptr = self.ptr.go_up()
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise TypeError(f"I don't know how to iterate a {type(self.ptr)}")
|
||||||
|
|
||||||
|
return out_side, out
|
||||||
|
|
||||||
class Direction(enum.Enum):
|
class Direction(enum.Enum):
|
||||||
UP = enum.auto()
|
UP = enum.auto()
|
||||||
LEFT = enum.auto()
|
LEFT = enum.auto()
|
||||||
|
@ -73,8 +107,33 @@ class Node:
|
||||||
self.parent_side: Direction | None = None
|
self.parent_side: Direction | None = None
|
||||||
|
|
||||||
# Left and right nodes, None if empty
|
# Left and right nodes, None if empty
|
||||||
self.left: Node | None = None
|
self._left: Node | None = None
|
||||||
self.right: Node | None = None
|
self._right: Node | None = None
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return TreeWalker(self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def left(self):
|
||||||
|
return self._left
|
||||||
|
|
||||||
|
@left.setter
|
||||||
|
def left(self, node):
|
||||||
|
if node is not None:
|
||||||
|
node.set_parent(self, Direction.LEFT)
|
||||||
|
self._left = node
|
||||||
|
|
||||||
|
@property
|
||||||
|
def right(self):
|
||||||
|
return self._right
|
||||||
|
|
||||||
|
@right.setter
|
||||||
|
def right(self, node):
|
||||||
|
if node is not None:
|
||||||
|
node.set_parent(self, Direction.RIGHT)
|
||||||
|
self._right = node
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def set_parent(self, parent, side):
|
def set_parent(self, parent, side):
|
||||||
if (parent is not None) and (side is None):
|
if (parent is not None) and (side is None):
|
||||||
|
@ -83,16 +142,17 @@ class Node:
|
||||||
raise Exception("If a node has no parent, it cannot have a direction.")
|
raise Exception("If a node has no parent, it cannot have a direction.")
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.parent_side = side
|
self.parent_side = side
|
||||||
|
return self
|
||||||
|
|
||||||
def go_left(self):
|
def go_left(self):
|
||||||
if self.left is None:
|
if self._left is None:
|
||||||
raise Exception("Can't go left when left is None")
|
raise Exception("Can't go left when left is None")
|
||||||
return Direction.UP, self.left
|
return Direction.UP, self._left
|
||||||
|
|
||||||
def go_right(self):
|
def go_right(self):
|
||||||
if self.right is None:
|
if self._right is None:
|
||||||
raise Exception("Can't go right when right is None")
|
raise Exception("Can't go right when right is None")
|
||||||
return Direction.UP, self.right
|
return Direction.UP, self._right
|
||||||
|
|
||||||
def go_up(self):
|
def go_up(self):
|
||||||
return self.parent_side, self.parent
|
return self.parent_side, self.parent
|
||||||
|
@ -172,7 +232,7 @@ class Church(ExpandableEndNode):
|
||||||
chain = a
|
chain = a
|
||||||
|
|
||||||
for i in range(self.value):
|
for i in range(self.value):
|
||||||
chain = Call(f, chain)
|
chain = Call(clone(f), clone(chain))
|
||||||
|
|
||||||
return Func(
|
return Func(
|
||||||
f,
|
f,
|
||||||
|
@ -316,19 +376,13 @@ p = lamb.parser.LambdaParser(
|
||||||
def clone_one(ptr, out):
|
def clone_one(ptr, out):
|
||||||
if ptr.parent_side == Direction.LEFT:
|
if ptr.parent_side == Direction.LEFT:
|
||||||
out.left = ptr.clone()
|
out.left = ptr.clone()
|
||||||
out.left.set_parent(out, Direction.LEFT)
|
|
||||||
else:
|
else:
|
||||||
out.right = ptr.clone()
|
out.right = ptr.clone()
|
||||||
out.right.set_parent(out, Direction.RIGHT)
|
|
||||||
|
|
||||||
def clone(expr: Node):
|
def clone(expr: Node):
|
||||||
if not isinstance(expr, Node):
|
if not isinstance(expr, Node):
|
||||||
raise TypeError(f"I don't know what to do with a {type(expr)}")
|
raise TypeError(f"I don't know what to do with a {type(expr)}")
|
||||||
|
|
||||||
# Disconnect parent while cloning
|
|
||||||
old_parent = expr.parent
|
|
||||||
expr.parent = None
|
|
||||||
|
|
||||||
out = expr.clone()
|
out = expr.clone()
|
||||||
out_ptr = out # Stays one step behind ptr, in the new tree.
|
out_ptr = out # Stays one step behind ptr, in the new tree.
|
||||||
ptr = expr
|
ptr = expr
|
||||||
|
@ -337,7 +391,12 @@ def clone(expr: Node):
|
||||||
if isinstance(expr, EndNode):
|
if isinstance(expr, EndNode):
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
print("cloning", expr)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
print("p", ptr)
|
||||||
|
print("o", out_ptr)
|
||||||
|
print("r", out)
|
||||||
if isinstance(ptr, EndNode):
|
if isinstance(ptr, EndNode):
|
||||||
from_side, ptr = ptr.go_up()
|
from_side, ptr = ptr.go_up()
|
||||||
_, out_ptr = out_ptr.go_up()
|
_, out_ptr = out_ptr.go_up()
|
||||||
|
@ -361,57 +420,53 @@ def clone(expr: Node):
|
||||||
elif from_side == Direction.RIGHT:
|
elif from_side == Direction.RIGHT:
|
||||||
from_side, ptr = ptr.go_up()
|
from_side, ptr = ptr.go_up()
|
||||||
_, out_ptr = out_ptr.go_up()
|
_, out_ptr = out_ptr.go_up()
|
||||||
if ptr is None:
|
|
||||||
|
if ptr is expr.parent:
|
||||||
break
|
break
|
||||||
|
|
||||||
expr.parent = old_parent
|
if out_ptr is None:
|
||||||
|
print("fail")
|
||||||
|
print(out_ptr)
|
||||||
|
print(out)
|
||||||
|
print(ptr, expr.parent)
|
||||||
|
print("<fail")
|
||||||
|
#return False
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def print_expr(expr) -> str:
|
def print_expr(expr) -> str:
|
||||||
|
|
||||||
out = ""
|
|
||||||
|
|
||||||
# Type check
|
# Type check
|
||||||
if isinstance(expr, MacroDef):
|
if isinstance(expr, MacroDef):
|
||||||
out = expr.label + " = "
|
out = expr.label + " = "
|
||||||
expr = expr.expr
|
expr = expr.expr
|
||||||
elif not isinstance(expr, Node):
|
elif not isinstance(expr, Node):
|
||||||
raise TypeError(f"I don't know what to do with a {type(expr)}")
|
raise TypeError(f"I don't know what to do with a {type(expr)}")
|
||||||
|
else:
|
||||||
|
out = ""
|
||||||
|
|
||||||
ptr = expr
|
for s, n in expr:
|
||||||
from_side = Direction.UP
|
|
||||||
|
|
||||||
while True:
|
print(out)
|
||||||
print(ptr)
|
if isinstance(n, EndNode):
|
||||||
if isinstance(ptr, EndNode):
|
out += n.print_value()
|
||||||
out += ptr.print_value()
|
|
||||||
from_side, ptr = ptr.go_up()
|
|
||||||
|
|
||||||
elif isinstance(ptr, Func):
|
elif isinstance(n, Func):
|
||||||
if from_side == Direction.UP:
|
if s == Direction.UP:
|
||||||
if isinstance(ptr.parent, Func):
|
if isinstance(n.parent, Func):
|
||||||
out += ptr.input.name
|
out += n.input.name
|
||||||
else:
|
else:
|
||||||
out += "λ" + ptr.input.name
|
out += "λ" + n.input.name
|
||||||
if not isinstance(ptr.left, Func):
|
if not isinstance(n.left, Func):
|
||||||
out += "."
|
out += "."
|
||||||
from_side, ptr = ptr.go_left()
|
|
||||||
elif from_side == Direction.LEFT:
|
|
||||||
from_side, ptr = ptr.go_up()
|
|
||||||
|
|
||||||
elif isinstance(ptr, Call):
|
elif isinstance(n, Call):
|
||||||
if from_side == Direction.UP:
|
if s == Direction.UP:
|
||||||
out += "("
|
out += "("
|
||||||
from_side, ptr = ptr.go_left()
|
elif s == Direction.LEFT:
|
||||||
elif from_side == Direction.LEFT:
|
|
||||||
out += " "
|
out += " "
|
||||||
from_side, ptr = ptr.go_right()
|
elif s == Direction.RIGHT:
|
||||||
elif from_side == Direction.RIGHT:
|
|
||||||
out += ")"
|
out += ")"
|
||||||
from_side, ptr = ptr.go_up()
|
|
||||||
|
|
||||||
if ptr is None:
|
|
||||||
break
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def bind_variables(expr) -> None:
|
def bind_variables(expr) -> None:
|
||||||
|
@ -422,118 +477,51 @@ def bind_variables(expr) -> None:
|
||||||
elif not isinstance(expr, Node):
|
elif not isinstance(expr, Node):
|
||||||
raise TypeError(f"I don't know what to do with a {type(expr)}")
|
raise TypeError(f"I don't know what to do with a {type(expr)}")
|
||||||
|
|
||||||
ptr = expr
|
|
||||||
from_side = Direction.UP
|
|
||||||
bound_variables = {}
|
bound_variables = {}
|
||||||
|
|
||||||
while True:
|
for s, n in expr:
|
||||||
if isinstance(ptr, Func):
|
if isinstance(n, Func):
|
||||||
if from_side == Direction.UP:
|
if s == Direction.UP:
|
||||||
# Add this function's input to the table of bound variables.
|
# Add this function's input to the table of bound variables.
|
||||||
# If it is already there, raise an error.
|
# If it is already there, raise an error.
|
||||||
if (ptr.input.name in bound_variables):
|
if (n.input.name in bound_variables):
|
||||||
raise ReductionError(f"Bound variable name conflict: \"{ptr.input.name}\"")
|
raise ReductionError(f"Bound variable name conflict: \"{n.input.name}\"")
|
||||||
else:
|
else:
|
||||||
bound_variables[ptr.input.name] = Bound(ptr.input.name)
|
bound_variables[n.input.name] = Bound(n.input.name)
|
||||||
ptr.input = bound_variables[ptr.input.name]
|
n.input = bound_variables[n.input.name]
|
||||||
|
|
||||||
# If output is a macro, swap it with a bound variable.
|
# If output is a macro, swap it with a bound variable.
|
||||||
if isinstance(ptr.left, Macro):
|
if isinstance(n.left, Macro):
|
||||||
if ptr.left.name in bound_variables:
|
if n.left.name in bound_variables:
|
||||||
ptr.left = bound_variables[ptr.left.name].clone()
|
n.left = clone(bound_variables[n.left.name])
|
||||||
ptr.left.set_parent(ptr, Direction.LEFT)
|
|
||||||
|
|
||||||
# If we can't move down the tree, move up.
|
elif s == Direction.LEFT:
|
||||||
if isinstance(ptr.left, EndNode):
|
del bound_variables[n.input.name]
|
||||||
del bound_variables[ptr.input.name]
|
|
||||||
from_side, ptr = ptr.go_up()
|
|
||||||
else:
|
|
||||||
from_side, ptr = ptr.go_left()
|
|
||||||
|
|
||||||
elif from_side == Direction.LEFT:
|
elif isinstance(n, Call):
|
||||||
del bound_variables[ptr.input.name]
|
if s == Direction.UP:
|
||||||
from_side, ptr = ptr.go_up()
|
|
||||||
|
|
||||||
elif isinstance(ptr, Call):
|
|
||||||
if from_side == Direction.UP:
|
|
||||||
# Bind macros
|
# Bind macros
|
||||||
if isinstance(ptr.left, Macro):
|
if isinstance(n.left, Macro):
|
||||||
if ptr.left.name in bound_variables:
|
if n.left.name in bound_variables:
|
||||||
ptr.left = bound_variables[ptr.left.name].clone()
|
n.left = clone(bound_variables[n.left.name])
|
||||||
ptr.left.set_parent(ptr, Direction.LEFT)
|
if isinstance(n.right, Macro):
|
||||||
if isinstance(ptr.right, Macro):
|
if n.right.name in bound_variables:
|
||||||
if ptr.right.name in bound_variables:
|
n.right = clone(bound_variables[n.right.name])
|
||||||
ptr.right = bound_variables[ptr.right.name].clone()
|
|
||||||
ptr.right.set_parent(ptr, Direction.RIGHT)
|
|
||||||
|
|
||||||
if not isinstance(ptr.left, EndNode):
|
|
||||||
from_side, ptr = ptr.go_left()
|
|
||||||
elif not isinstance(ptr.right, EndNode):
|
|
||||||
from_side, ptr = ptr.go_right()
|
|
||||||
else:
|
|
||||||
from_side, ptr = ptr.go_up()
|
|
||||||
|
|
||||||
elif from_side == Direction.LEFT:
|
|
||||||
if not isinstance(ptr.right, EndNode):
|
|
||||||
from_side, ptr = ptr.go_right()
|
|
||||||
else:
|
|
||||||
from_side, ptr = ptr.go_up()
|
|
||||||
|
|
||||||
elif from_side == Direction.RIGHT:
|
|
||||||
from_side, ptr = ptr.go_up()
|
|
||||||
|
|
||||||
if ptr is None:
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
# Apply a function.
|
# Apply a function.
|
||||||
# Returns the function's output.
|
# Returns the function's output.
|
||||||
def call_func(fn: Func, arg: Node):
|
def call_func(fn: Func, arg: Node):
|
||||||
ptr = fn
|
for s, n in fn:
|
||||||
|
if isinstance(n, Bound):
|
||||||
# Temporarily disconnect this function's
|
if n == fn.input:
|
||||||
# parent to keep our pointer inside this
|
if n.parent is None:
|
||||||
# subtree.
|
|
||||||
old_parent = fn.parent
|
|
||||||
fn.parent = None
|
|
||||||
|
|
||||||
from_side = Direction.UP
|
|
||||||
|
|
||||||
while True:
|
|
||||||
if isinstance(ptr, Bound):
|
|
||||||
if ptr == fn.input:
|
|
||||||
if ptr.parent is None:
|
|
||||||
raise Exception("Tried to substitute a None bound variable.")
|
raise Exception("Tried to substitute a None bound variable.")
|
||||||
|
|
||||||
if ptr.parent_side == Direction.LEFT:
|
if n.parent_side == Direction.LEFT:
|
||||||
ptr.parent.left = clone(arg)
|
n.parent.left = clone(arg)
|
||||||
ptr.parent.left.set_parent(ptr, Direction.LEFT)
|
|
||||||
else:
|
else:
|
||||||
ptr.parent.right = clone(arg)
|
n.parent.right = clone(arg)
|
||||||
ptr.parent.right.set_parent(ptr, Direction.RIGHT)
|
|
||||||
|
|
||||||
from_side, ptr = ptr.go_up()
|
|
||||||
|
|
||||||
elif isinstance(ptr, Func):
|
|
||||||
if from_side == Direction.UP:
|
|
||||||
from_side, ptr = ptr.go_left()
|
|
||||||
elif from_side == Direction.LEFT:
|
|
||||||
from_side, ptr = ptr.go_up()
|
|
||||||
|
|
||||||
elif isinstance(ptr, Call):
|
|
||||||
if from_side == Direction.UP:
|
|
||||||
from_side, ptr = ptr.go_left()
|
|
||||||
elif from_side == Direction.LEFT:
|
|
||||||
from_side, ptr = ptr.go_right()
|
|
||||||
elif from_side == Direction.RIGHT:
|
|
||||||
from_side, ptr = ptr.go_up()
|
|
||||||
else:
|
|
||||||
from_side, ptr = ptr.go_up()
|
|
||||||
|
|
||||||
if ptr is None:
|
|
||||||
break
|
|
||||||
|
|
||||||
fn.parent = old_parent
|
|
||||||
return fn.left
|
return fn.left
|
||||||
|
|
||||||
|
|
||||||
|
@ -543,48 +531,26 @@ def reduce(expr) -> tuple[bool, Node]:
|
||||||
if not isinstance(expr, Node):
|
if not isinstance(expr, Node):
|
||||||
raise TypeError(f"I can't reduce a {type(expr)}")
|
raise TypeError(f"I can't reduce a {type(expr)}")
|
||||||
|
|
||||||
ptr = expr
|
|
||||||
from_side = Direction.UP
|
|
||||||
reduced = False
|
reduced = False
|
||||||
|
|
||||||
while True:
|
|
||||||
print("redu", ptr)
|
|
||||||
|
|
||||||
if isinstance(ptr, Call):
|
for s, n in expr:
|
||||||
if from_side == Direction.UP:
|
if isinstance(n, Call):
|
||||||
if isinstance(ptr.left, Func):
|
if s == Direction.UP:
|
||||||
if ptr.parent is None:
|
if isinstance(n.left, Func):
|
||||||
expr = call_func(ptr.left, ptr.right)
|
if n.parent is None:
|
||||||
|
expr = call_func(n.left, n.right)
|
||||||
expr.set_parent(None, None)
|
expr.set_parent(None, None)
|
||||||
else:
|
else:
|
||||||
ptr.parent.left = call_func(ptr.left, ptr.right)
|
n.parent.left = call_func(n.left, n.right)
|
||||||
ptr.parent.left.set_parent(ptr.parent, Direction.LEFT)
|
|
||||||
reduced = True
|
reduced = True
|
||||||
break
|
break
|
||||||
elif isinstance(ptr.left, ExpandableEndNode):
|
elif isinstance(n.left, ExpandableEndNode):
|
||||||
ptr.left = ptr.left.expand()
|
n.left = n.left.expand()
|
||||||
reduced = True
|
reduced = True
|
||||||
break
|
break
|
||||||
elif isinstance(ptr.left, Call):
|
|
||||||
from_side, ptr = ptr.go_left()
|
|
||||||
else:
|
|
||||||
from_side, ptr = ptr.go_right()
|
|
||||||
|
|
||||||
else:
|
|
||||||
from_side, ptr = ptr.go_up()
|
|
||||||
|
|
||||||
elif isinstance(ptr, Func):
|
|
||||||
if from_side == Direction.UP:
|
|
||||||
from_side, ptr = ptr.go_left()
|
|
||||||
else:
|
|
||||||
from_side, ptr = ptr.go_up()
|
|
||||||
|
|
||||||
elif isinstance(ptr, EndNode):
|
|
||||||
from_side, ptr = ptr.go_up()
|
|
||||||
|
|
||||||
if ptr is None:
|
|
||||||
break
|
|
||||||
|
|
||||||
|
print("r")
|
||||||
return reduced, expr
|
return reduced, expr
|
||||||
|
|
||||||
|
|
||||||
|
@ -605,18 +571,19 @@ for l in [
|
||||||
"H = λp.((PAIR (p F)) (S (p F)))",
|
"H = λp.((PAIR (p F)) (S (p F)))",
|
||||||
"D = λn.n H (PAIR 0 0) T",
|
"D = λn.n H (PAIR 0 0) T",
|
||||||
"FAC = λyn.(Z n)(1)(MULT n (y (D n)))",
|
"FAC = λyn.(Z n)(1)(MULT n (y (D n)))",
|
||||||
"S (λfa.f a)"
|
"3 NOT T"
|
||||||
]:
|
]:
|
||||||
n = p.parse_line(l)
|
n = p.parse_line(l)
|
||||||
bind_variables(n)
|
bind_variables(n)
|
||||||
|
|
||||||
|
|
||||||
if isinstance(n, MacroDef):
|
if isinstance(n, MacroDef):
|
||||||
macro_table[n.label] = n.expr
|
macro_table[n.label] = n.expr
|
||||||
print(print_expr(n))
|
print(print_expr(n))
|
||||||
else:
|
else:
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
r, n = reduce(n)
|
r, n = reduce(n)
|
||||||
|
print(print_expr(n))
|
||||||
if not r:
|
if not r:
|
||||||
break
|
break
|
||||||
print(print_expr(n))
|
|
||||||
#print(print_expr(clone(n)))
|
#print(print_expr(clone(n)))
|
Reference in New Issue