Compare commits

..

No commits in common. "ecbb8661ced3a7ae5d9ded7cb75e580649a46370" and "33c8d5bb661ccaf064987080657efc1b43e1688b" have entirely different histories.

4 changed files with 43 additions and 49 deletions

View File

@ -1,7 +1,6 @@
{ {
"cSpell.words": [ "cSpell.words": [
"autochurch", "autochurch",
"Endnodes",
"freevar", "freevar",
"mdel", "mdel",
"onefile", "onefile",

View File

@ -75,15 +75,15 @@ Lamb treats each λ expression as a binary tree. Variable binding and reduction
## Todo (pre-release): ## Todo (pre-release):
- Make command output accessible in prompt
- Prettier colors - Prettier colors
- Prevent macro-chaining recursion - Prevent macro-chaining recursion
- step-by-step reduction - step-by-step reduction
- Full-reduce option (expand all macros) - Full-reduce option (expand all macros)
- PyPi package - PyPi package
- Cleanup warnings - Cleanup warnings
- Preprocess method: bind, macros to free, etc
- History queue - History queue
- Truncate long expressions in warnings - `W` isn't fully expanded. Why??
## Todo: ## Todo:

View File

@ -36,6 +36,7 @@ class ReductionError(Exception):
def __init__(self, msg: str): def __init__(self, msg: str):
self.msg = msg self.msg = msg
class TreeWalker: class TreeWalker:
""" """
An iterator that walks the "outline" of a tree An iterator that walks the "outline" of a tree
@ -243,7 +244,6 @@ class ExpandableEndNode(EndNode):
class FreeVar(EndNode): class FreeVar(EndNode):
def __init__(self, name: str, *, runner = None): def __init__(self, name: str, *, runner = None):
super().__init__()
self.name = name self.name = name
self.runner = runner # type: ignore self.runner = runner # type: ignore
@ -347,6 +347,7 @@ class History(ExpandableEndNode):
def copy(self): def copy(self):
return History(runner = self.runner) return History(runner = self.runner)
bound_counter = 0 bound_counter = 0
class Bound(EndNode): class Bound(EndNode):
def __init__(self, name: str, *, forced_id = None, runner = None): def __init__(self, name: str, *, forced_id = None, runner = None):
@ -588,6 +589,7 @@ def call_func(fn: Func, arg: Node):
n.parent.set_side(n.parent_side, clone(arg)) # type: ignore n.parent.set_side(n.parent_side, clone(arg)) # type: ignore
return fn.left return fn.left
# Do a single reduction step # Do a single reduction step
def reduce(node: Node) -> tuple[ReductionType, Node]: def reduce(node: Node) -> tuple[ReductionType, Node]:
if not isinstance(node, Node): if not isinstance(node, Node):
@ -614,19 +616,12 @@ def reduce(node: Node) -> tuple[ReductionType, Node]:
return ReductionType.NOTHING, out return ReductionType.NOTHING, out
def expand(node: Node, *, force_all = False) -> tuple[int, Node]:
"""
Expands expandable nodes in the given tree.
If force_all is false, this only expands
ExpandableEndnodes that have "always_expand" set to True.
If force_all is True, this expands ALL
ExpandableEndnodes.
"""
# Expand all expandable end nodes.
def finalize_macros(node: Node, *, force = False) -> tuple[int, Node]:
if not isinstance(node, Node): if not isinstance(node, Node):
raise TypeError(f"I don't know what to do with a {type(node)}") raise TypeError(f"I can't reduce a {type(node)}")
out = clone(node) out = clone(node)
ptr = out ptr = out
@ -636,7 +631,7 @@ def expand(node: Node, *, force_all = False) -> tuple[int, Node]:
while True: while True:
if ( if (
isinstance(ptr, ExpandableEndNode) and isinstance(ptr, ExpandableEndNode) and
(force_all or ptr.always_expand) (force or ptr.always_expand)
): ):
if ptr.parent is None: if ptr.parent is None:
ptr = ptr.expand()[1] ptr = ptr.expand()[1]
@ -653,7 +648,6 @@ def expand(node: Node, *, force_all = False) -> tuple[int, Node]:
macro_expansions += 1 macro_expansions += 1
# Tree walk logic
if isinstance(ptr, EndNode): if isinstance(ptr, EndNode):
from_side, ptr = ptr.go_up() from_side, ptr = ptr.go_up()
elif isinstance(ptr, Func): elif isinstance(ptr, Func):
@ -671,4 +665,4 @@ def expand(node: Node, *, force_all = False) -> tuple[int, Node]:
if ptr is node.parent: if ptr is node.parent:
break break
return macro_expansions, out return macro_expansions, out # type: ignore

View File

@ -110,18 +110,9 @@ class Runner:
def reduce(self, node: lamb.node.Node, *, status = {}) -> None: def reduce(self, node: lamb.node.Node, *, status = {}) -> None:
# Show warnings
warning_text = [] warning_text = []
# Reduction Counter.
# We also count macro (and church) expansions,
# and subtract those from the final count.
k = 0
macro_expansions = 0
stop_reason = StopReason.MAX_EXCEEDED
start_time = time.time()
out_text = []
if status["has_history"] and len(self.history) != 0: if status["has_history"] and len(self.history) != 0:
warning_text += [ warning_text += [
("class:code", "$"), ("class:code", "$"),
@ -130,31 +121,32 @@ class Runner:
("class:warn", "\n") ("class:warn", "\n")
] ]
only_macro = isinstance(node, lamb.node.ExpandableEndNode)
if only_macro:
warning_text += [
("class:warn", "All macros will be expanded"),
("class:warn", "\n")
]
m, node = lamb.node.expand(node, force_all = only_macro)
macro_expansions += m
for i in status["free_variables"]: for i in status["free_variables"]:
warning_text += [ warning_text += [
("class:warn", "Name "), ("class:warn", "Macro "),
("class:code", i), ("class:code", i),
("class:warn", " is a free variable\n"), ("class:warn", " will become a free variable.\n"),
] ]
printf(FormattedText(warning_text), style = lamb.utils.style) printf(FormattedText(warning_text), style = lamb.utils.style)
# Reduction Counter.
# We also count macro (and church) expansions,
# and subtract those from the final count.
i = 0
macro_expansions = 0
while (self.reduction_limit is None) or (k < self.reduction_limit): stop_reason = StopReason.MAX_EXCEEDED
start_time = time.time()
full_reduce = isinstance(node, lamb.node.ExpandableEndNode)
out_text = []
while (self.reduction_limit is None) or (i < self.reduction_limit):
# Show reduction count # Show reduction count
if (k >= self.iter_update) and (k % self.iter_update == 0): if (i >= self.iter_update) and (i % self.iter_update == 0):
print(f" Reducing... {k:,}", end = "\r") print(f" Reducing... {i:,}", end = "\r")
try: try:
red_type, node = lamb.node.reduce(node) red_type, node = lamb.node.reduce(node)
@ -169,13 +161,17 @@ class Runner:
break break
# Count reductions # Count reductions
k += 1 i += 1
if red_type == lamb.node.ReductionType.FUNCTION_APPLY: if red_type == lamb.node.ReductionType.FUNCTION_APPLY:
macro_expansions += 1 macro_expansions += 1
if k >= self.iter_update: # Expand all remaining macros
# Clear reduction counter if it was printed m, node = lamb.node.finalize_macros(node, force = full_reduce)
print(" " * round(14 + math.log10(k)), end = "\r") macro_expansions += m
if i >= self.iter_update:
# Clear reduction counter
print(" " * round(14 + math.log10(i)), end = "\r")
out_text += [ out_text += [
("class:result_header", f"Runtime: "), ("class:result_header", f"Runtime: "),
@ -188,17 +184,22 @@ class Runner:
("class:text", f"{macro_expansions:,}"), ("class:text", f"{macro_expansions:,}"),
("class:result_header", f"\nReductions: "), ("class:result_header", f"\nReductions: "),
("class:text", f"{k:,}\t"), ("class:text", f"{i:,}\t"),
("class:muted", f"(Limit: {self.reduction_limit:,})") ("class:muted", f"(Limit: {self.reduction_limit:,})")
] ]
if full_reduce:
out_text += [
("class:warn", "\nAll macros have been expanded")
]
if (stop_reason == StopReason.BETA_NORMAL or stop_reason == StopReason.LOOP_DETECTED): if (stop_reason == StopReason.BETA_NORMAL or stop_reason == StopReason.LOOP_DETECTED):
out_text += [ out_text += [
("class:result_header", "\n\n => "), ("class:result_header", "\n\n => "),
("class:text", str(node)), # type: ignore ("class:text", str(node)), # type: ignore
] ]
self.history.append(lamb.node.expand(node, force_all = True)[1]) self.history.append(lamb.node.finalize_macros(node, force = True)[1])
printf( printf(