Compare commits
No commits in common. "ecbb8661ced3a7ae5d9ded7cb75e580649a46370" and "33c8d5bb661ccaf064987080657efc1b43e1688b" have entirely different histories.
ecbb8661ce
...
33c8d5bb66
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"autochurch",
|
"autochurch",
|
||||||
"Endnodes",
|
|
||||||
"freevar",
|
"freevar",
|
||||||
"mdel",
|
"mdel",
|
||||||
"onefile",
|
"onefile",
|
||||||
|
@ -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:
|
||||||
|
24
lamb/node.py
24
lamb/node.py
@ -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
|
@ -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(
|
||||||
|
Reference in New Issue
Block a user