Improved printing
parent
81c78d7921
commit
09f78a7642
|
@ -30,7 +30,7 @@ To define macros, use `=`. For example,
|
|||
|
||||
Note that there are spaces in `λa.a F T`. With no spaces, `aFT` will be parsed as one variable. \
|
||||
Lambda functions can only take single-letter, lowercase arguments. `λA.A` is not valid syntax. \
|
||||
Unbound variables (upper and lower case) that aren't macros will become free variables.
|
||||
Unbound variables (upper and lower case) that aren't macros will become free variables. Free variables will be shown with a `'`, like `a'`.
|
||||
|
||||
Be careful, macros are case-sensitive. If you define a macro `MAC` and accidentally write `mac` in the prompt, `mac` will become a free variable.
|
||||
|
||||
|
@ -76,7 +76,6 @@ Lamb treats each λ expression as a binary tree. Variable binding and reduction
|
|||
|
||||
## Todo (pre-release):
|
||||
- Make command output accessible in prompt
|
||||
- Prettyprint functions and rename bound variables
|
||||
- Prettier colors
|
||||
- Prevent macro-chaining recursion
|
||||
- step-by-step reduction
|
||||
|
@ -86,11 +85,9 @@ Lamb treats each λ expression as a binary tree. Variable binding and reduction
|
|||
|
||||
|
||||
## Todo:
|
||||
- Optimization: clone only if absolutely necessary
|
||||
- Better class mutation: when is a node no longer valid?
|
||||
- Loop detection
|
||||
- Command-line options (load a file, run a set of commands)
|
||||
- $\alpha$-equivalence check
|
||||
- Unchurch macro: make church numerals human-readable
|
||||
- Smart alignment in all printouts
|
||||
- Syntax highlighting: parenthesis, bound variables, macros, etc
|
|
@ -62,7 +62,7 @@ def cmd_save(command, runner) -> None:
|
|||
|
||||
with open(target, "w") as f:
|
||||
f.write("\n".join(
|
||||
[f"{n} = {e}" for n, e in runner.macro_table.items()]
|
||||
[f"{n} = {e.export()}" for n, e in runner.macro_table.items()]
|
||||
))
|
||||
|
||||
printf(
|
||||
|
|
44
lamb/node.py
44
lamb/node.py
|
@ -1,4 +1,5 @@
|
|||
import enum
|
||||
import lamb
|
||||
|
||||
class Direction(enum.Enum):
|
||||
UP = enum.auto()
|
||||
|
@ -207,6 +208,12 @@ class Node:
|
|||
def __str__(self) -> str:
|
||||
return print_node(self)
|
||||
|
||||
def export(self) -> str:
|
||||
"""
|
||||
Convert this tree to a parsable string.
|
||||
"""
|
||||
return print_node(self, export = True)
|
||||
|
||||
def bind_variables(self, *, ban_macro_name = None):
|
||||
return bind_variables(
|
||||
self,
|
||||
|
@ -214,7 +221,7 @@ class Node:
|
|||
)
|
||||
|
||||
class EndNode(Node):
|
||||
def print_value(self):
|
||||
def print_value(self, *, export: bool = False) -> str:
|
||||
raise NotImplementedError("EndNodes MUST provide a `print_value` method!")
|
||||
|
||||
class ExpandableEndNode(EndNode):
|
||||
|
@ -228,8 +235,11 @@ class FreeVar(EndNode):
|
|||
def __repr__(self):
|
||||
return f"<freevar {self.name}>"
|
||||
|
||||
def print_value(self):
|
||||
return f"{self.name}"
|
||||
def print_value(self, *, export: bool = False) -> str:
|
||||
if export:
|
||||
return f"{self.name}'"
|
||||
else:
|
||||
return f"{self.name}'"
|
||||
|
||||
def copy(self):
|
||||
return FreeVar(self.name)
|
||||
|
@ -248,7 +258,7 @@ class Macro(ExpandableEndNode):
|
|||
def __repr__(self):
|
||||
return f"<macro {self.name}>"
|
||||
|
||||
def print_value(self):
|
||||
def print_value(self, *, export: bool = False) -> str:
|
||||
return self.name
|
||||
|
||||
def expand(self, *, macro_table = {}) -> tuple[ReductionType, Node]:
|
||||
|
@ -274,7 +284,7 @@ class Church(ExpandableEndNode):
|
|||
def __repr__(self):
|
||||
return f"<church {self.value}>"
|
||||
|
||||
def print_value(self):
|
||||
def print_value(self, *, export: bool = False) -> str:
|
||||
return str(self.value)
|
||||
|
||||
def expand(self, *, macro_table = {}) -> tuple[ReductionType, Node]:
|
||||
|
@ -316,7 +326,7 @@ class Bound(EndNode):
|
|||
def __repr__(self):
|
||||
return f"<{self.name} {self.identifier}>"
|
||||
|
||||
def print_value(self):
|
||||
def print_value(self, *, export: bool = False) -> str:
|
||||
return self.name
|
||||
|
||||
class Func(Node):
|
||||
|
@ -378,15 +388,31 @@ class Call(Node):
|
|||
return Call(None, None) # type: ignore
|
||||
|
||||
|
||||
def print_node(node: Node) -> str:
|
||||
def print_node(node: Node, *, export: bool = False) -> str:
|
||||
if not isinstance(node, Node):
|
||||
raise TypeError(f"I don't know how to print a {type(node)}")
|
||||
|
||||
out = ""
|
||||
|
||||
bound_subs = {}
|
||||
|
||||
for s, n in node:
|
||||
if isinstance(n, EndNode):
|
||||
out += n.print_value()
|
||||
if isinstance(n, Bound):
|
||||
if n.identifier not in bound_subs.keys():
|
||||
o = n.print_value(export = export)
|
||||
if o in bound_subs.items():
|
||||
i = 1
|
||||
while o in bound_subs.items():
|
||||
o = lamb.utils.subscript(i := i + 1)
|
||||
bound_subs[n.identifier] = o
|
||||
else:
|
||||
bound_subs[n.identifier] = n.print_value()
|
||||
|
||||
|
||||
out += bound_subs[n.identifier]
|
||||
else:
|
||||
out += n.print_value(export = export)
|
||||
|
||||
elif isinstance(n, Func):
|
||||
if s == Direction.UP:
|
||||
|
@ -502,7 +528,7 @@ def call_func(fn: Func, arg: Node):
|
|||
raise Exception("Tried to substitute a None bound variable.")
|
||||
|
||||
n.parent.set_side(n.parent_side, clone(arg)) # type: ignore
|
||||
return clone(fn.left)
|
||||
return fn.left
|
||||
|
||||
|
||||
# Do a single reduction step
|
||||
|
|
|
@ -116,7 +116,7 @@ class Runner:
|
|||
|
||||
# Show reduction count
|
||||
if (i >= self.iter_update) and (i % self.iter_update == 0):
|
||||
print(f" Reducing... {i}", end = "\r")
|
||||
print(f" Reducing... {i:,}", end = "\r")
|
||||
|
||||
try:
|
||||
red_type, node = lamb.node.reduce(
|
||||
|
|
|
@ -70,4 +70,35 @@ def show_greeting():
|
|||
"<_s> A λ calculus engine</_s>",
|
||||
"<_p> Type :help for help</_p>",
|
||||
""
|
||||
])), style = style)
|
||||
])), style = style)
|
||||
|
||||
def subscript(num: int):
|
||||
sub = {
|
||||
"0": "₀",
|
||||
"1": "₁",
|
||||
"2": "₂",
|
||||
"3": "₃",
|
||||
"4": "₄",
|
||||
"5": "₅",
|
||||
"6": "₆",
|
||||
"7": "₇",
|
||||
"8": "₈",
|
||||
"9": "₉"
|
||||
}
|
||||
|
||||
sup = {
|
||||
"0": "⁰",
|
||||
"1": "¹",
|
||||
"2": "²",
|
||||
"3": "³",
|
||||
"4": "⁴",
|
||||
"5": "⁵",
|
||||
"6": "⁶",
|
||||
"7": "⁷",
|
||||
"8": "⁸",
|
||||
"9": "⁹"
|
||||
}
|
||||
|
||||
return "".join(
|
||||
[sup[x] for x in str(num)]
|
||||
)
|
|
@ -14,7 +14,7 @@ description = "A lambda calculus engine"
|
|||
#
|
||||
# Patch release:
|
||||
# Small, compatible fixes.
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
|
||||
dependencies = [
|
||||
"prompt-toolkit==3.0.31",
|
||||
|
|
Reference in New Issue