Cleanup
parent
a1d8714f2f
commit
affcbc33ee
|
@ -4,7 +4,6 @@ from prompt_toolkit import print_formatted_text as printf
|
||||||
from prompt_toolkit.shortcuts import clear as clear_screen
|
from prompt_toolkit.shortcuts import clear as clear_screen
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
from pyparsing import exceptions as ppx
|
from pyparsing import exceptions as ppx
|
||||||
|
|
||||||
import lamb
|
import lamb
|
||||||
|
@ -12,30 +11,32 @@ import lamb
|
||||||
commands = {}
|
commands = {}
|
||||||
help_texts = {}
|
help_texts = {}
|
||||||
|
|
||||||
def lamb_command(*, help_text: str):
|
def lamb_command(
|
||||||
|
*,
|
||||||
|
command_name: str | None = None,
|
||||||
|
help_text: str
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
A decorator that allows us to easily make commands
|
||||||
|
"""
|
||||||
|
|
||||||
def inner(func):
|
def inner(func):
|
||||||
commands[func.__name__] = func
|
name = func.__name__ if command_name is None else command_name
|
||||||
help_texts[func.__name__] = help_text
|
|
||||||
|
commands[name] = func
|
||||||
|
help_texts[name] = help_text
|
||||||
return inner
|
return inner
|
||||||
|
|
||||||
def run(command, runner) -> None:
|
|
||||||
if command.name not in commands:
|
@lamb_command(
|
||||||
printf(
|
command_name = "save",
|
||||||
FormattedText([
|
help_text = "Save macros to a file"
|
||||||
("class:warn", f"Unknown command \"{command.name}\"")
|
|
||||||
]),
|
|
||||||
style = lamb.utils.style
|
|
||||||
)
|
)
|
||||||
else:
|
def cmd_save(command, runner) -> None:
|
||||||
commands[command.name](command, runner)
|
|
||||||
|
|
||||||
|
|
||||||
@lamb_command(help_text = "Save macros to a file")
|
|
||||||
def save(command, runner) -> None:
|
|
||||||
if len(command.args) != 1:
|
if len(command.args) != 1:
|
||||||
printf(
|
printf(
|
||||||
HTML(
|
HTML(
|
||||||
"<err>Command <cmd_code>:save</cmd_code> takes exactly one argument.</err>"
|
"<err>Command <cmd_code>:{command.name}</cmd_code> takes exactly one argument.</err>"
|
||||||
),
|
),
|
||||||
style = lamb.utils.style
|
style = lamb.utils.style
|
||||||
)
|
)
|
||||||
|
@ -72,12 +73,15 @@ def save(command, runner) -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@lamb_command(help_text = "Load macros from a file")
|
@lamb_command(
|
||||||
def load(command, runner):
|
command_name = "load",
|
||||||
|
help_text = "Load macros from a file"
|
||||||
|
)
|
||||||
|
def cmd_load(command, runner):
|
||||||
if len(command.args) != 1:
|
if len(command.args) != 1:
|
||||||
printf(
|
printf(
|
||||||
HTML(
|
HTML(
|
||||||
"<err>Command <cmd_code>:load</cmd_code> takes exactly one argument.</err>"
|
"<err>Command <cmd_code>:{command.name}</cmd_code> takes exactly one argument.</err>"
|
||||||
),
|
),
|
||||||
style = lamb.utils.style
|
style = lamb.utils.style
|
||||||
)
|
)
|
||||||
|
@ -134,13 +138,14 @@ def load(command, runner):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@lamb_command(
|
||||||
@lamb_command(help_text = "Delete a macro")
|
help_text = "Delete a macro"
|
||||||
|
)
|
||||||
def mdel(command, runner) -> None:
|
def mdel(command, runner) -> None:
|
||||||
if len(command.args) != 1:
|
if len(command.args) != 1:
|
||||||
printf(
|
printf(
|
||||||
HTML(
|
HTML(
|
||||||
"<err>Command <cmd_code>:mdel</cmd_code> takes exactly one argument.</err>"
|
"<err>Command <cmd_code>:{command.name}</cmd_code> takes exactly one argument.</err>"
|
||||||
),
|
),
|
||||||
style = lamb.utils.style
|
style = lamb.utils.style
|
||||||
)
|
)
|
||||||
|
@ -159,8 +164,9 @@ def mdel(command, runner) -> None:
|
||||||
del runner.macro_table[target]
|
del runner.macro_table[target]
|
||||||
|
|
||||||
|
|
||||||
|
@lamb_command(
|
||||||
@lamb_command(help_text = "Show macros")
|
help_text = "Show macros"
|
||||||
|
)
|
||||||
def macros(command, runner) -> None:
|
def macros(command, runner) -> None:
|
||||||
printf(FormattedText([
|
printf(FormattedText([
|
||||||
("class:cmd_h", "\nDefined Macros:\n"),
|
("class:cmd_h", "\nDefined Macros:\n"),
|
||||||
|
@ -172,13 +178,17 @@ def macros(command, runner) -> None:
|
||||||
style = lamb.utils.style
|
style = lamb.utils.style
|
||||||
)
|
)
|
||||||
|
|
||||||
@lamb_command(help_text = "Clear the screen")
|
@lamb_command(
|
||||||
|
help_text = "Clear the screen"
|
||||||
|
)
|
||||||
def clear(command, runner) -> None:
|
def clear(command, runner) -> None:
|
||||||
clear_screen()
|
clear_screen()
|
||||||
lamb.utils.show_greeting()
|
lamb.utils.show_greeting()
|
||||||
|
|
||||||
|
|
||||||
@lamb_command(help_text = "Print this help")
|
@lamb_command(
|
||||||
|
help_text = "Print this help"
|
||||||
|
)
|
||||||
def help(command, runner) -> None:
|
def help(command, runner) -> None:
|
||||||
printf(
|
printf(
|
||||||
HTML(
|
HTML(
|
||||||
|
|
144
lamb/node.py
144
lamb/node.py
|
@ -5,6 +5,24 @@ class Direction(enum.Enum):
|
||||||
LEFT = enum.auto()
|
LEFT = enum.auto()
|
||||||
RIGHT = enum.auto()
|
RIGHT = enum.auto()
|
||||||
|
|
||||||
|
class ReductionType(enum.Enum):
|
||||||
|
# Nothing happened. This implies that
|
||||||
|
# an expression cannot be reduced further.
|
||||||
|
NOTHING = enum.auto()
|
||||||
|
|
||||||
|
# We replaced a macro with an expression.
|
||||||
|
MACRO_EXPAND = enum.auto()
|
||||||
|
|
||||||
|
# We turned a church numeral into an expression
|
||||||
|
AUTOCHURCH = enum.auto()
|
||||||
|
|
||||||
|
# We replaced a macro with a free variable.
|
||||||
|
MACRO_TO_FREE = enum.auto()
|
||||||
|
|
||||||
|
# We applied a function.
|
||||||
|
# This is the only type of "formal" reduction step.
|
||||||
|
FUNCTION_APPLY = enum.auto()
|
||||||
|
|
||||||
class ReductionError(Exception):
|
class ReductionError(Exception):
|
||||||
"""
|
"""
|
||||||
Raised when we encounter an error while reducing.
|
Raised when we encounter an error while reducing.
|
||||||
|
@ -16,12 +34,26 @@ class ReductionError(Exception):
|
||||||
|
|
||||||
|
|
||||||
class TreeWalker:
|
class TreeWalker:
|
||||||
|
"""
|
||||||
|
An iterator that walks the "outline" of a tree
|
||||||
|
defined by a chain of nodes.
|
||||||
|
|
||||||
|
It returns a tuple: (out_side, out)
|
||||||
|
|
||||||
|
out is the node we moved to,
|
||||||
|
out_side is the direction we came to the node from.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, expr):
|
def __init__(self, expr):
|
||||||
self.expr = expr
|
self.expr = expr
|
||||||
self.ptr = expr
|
self.ptr = expr
|
||||||
self.from_side = Direction.UP
|
self.from_side = Direction.UP
|
||||||
|
|
||||||
def __next__(self):
|
def __next__(self):
|
||||||
|
# This could be implemented without checking the node type,
|
||||||
|
# but there's no reason to do that.
|
||||||
|
# Maybe later?
|
||||||
|
|
||||||
if self.ptr is self.expr.parent:
|
if self.ptr is self.expr.parent:
|
||||||
raise StopIteration
|
raise StopIteration
|
||||||
|
|
||||||
|
@ -50,6 +82,10 @@ class TreeWalker:
|
||||||
return out_side, out
|
return out_side, out
|
||||||
|
|
||||||
class Node:
|
class Node:
|
||||||
|
"""
|
||||||
|
Generic class for an element of an expression tree.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# The node this one is connected to.
|
# The node this one is connected to.
|
||||||
# None if this is the top objects.
|
# None if this is the top objects.
|
||||||
|
@ -67,6 +103,12 @@ class Node:
|
||||||
return TreeWalker(self)
|
return TreeWalker(self)
|
||||||
|
|
||||||
def _set_parent(self, parent, side):
|
def _set_parent(self, parent, side):
|
||||||
|
"""
|
||||||
|
Set this node's parent and parent side.
|
||||||
|
This method shouldn't be called explicitly unless
|
||||||
|
there's no other option. Use self.left and self.right instead.
|
||||||
|
"""
|
||||||
|
|
||||||
if (parent is not None) and (side is None):
|
if (parent is not None) and (side is None):
|
||||||
raise Exception("If a node has a parent, it must have a direction.")
|
raise Exception("If a node has a parent, it must have a direction.")
|
||||||
if (parent is None) and (side is not None):
|
if (parent is None) and (side is not None):
|
||||||
|
@ -97,6 +139,11 @@ class Node:
|
||||||
|
|
||||||
|
|
||||||
def set_side(self, side: Direction, node):
|
def set_side(self, side: Direction, node):
|
||||||
|
"""
|
||||||
|
A wrapper around Node.left and Node.right that
|
||||||
|
automatically selects a side.
|
||||||
|
"""
|
||||||
|
|
||||||
if side == Direction.LEFT:
|
if side == Direction.LEFT:
|
||||||
self.left = node
|
self.left = node
|
||||||
elif side == Direction.RIGHT:
|
elif side == Direction.RIGHT:
|
||||||
|
@ -106,20 +153,47 @@ class Node:
|
||||||
|
|
||||||
|
|
||||||
def go_left(self):
|
def go_left(self):
|
||||||
|
"""
|
||||||
|
Go down the left branch of this node.
|
||||||
|
Returns a tuple (from_dir, node)
|
||||||
|
|
||||||
|
from_dir is the direction from which we came INTO the next node.
|
||||||
|
node is the node on the left of this one.
|
||||||
|
"""
|
||||||
|
|
||||||
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):
|
||||||
|
"""
|
||||||
|
Go down the right branch of this node.
|
||||||
|
Returns a tuple (from_dir, node)
|
||||||
|
|
||||||
|
from_dir is the direction from which we came INTO the next node.
|
||||||
|
node is the node on the right of this one.
|
||||||
|
"""
|
||||||
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):
|
||||||
|
"""
|
||||||
|
Go up th the parent of this node.
|
||||||
|
Returns a tuple (from_dir, node)
|
||||||
|
|
||||||
|
from_dir is the direction from which we came INTO the parent.
|
||||||
|
node is the node above of this one.
|
||||||
|
"""
|
||||||
return self.parent_side, self.parent
|
return self.parent_side, self.parent
|
||||||
|
|
||||||
def clone(self):
|
def copy(self):
|
||||||
raise NotImplementedError("Nodes MUST provide a `clone` method!")
|
"""
|
||||||
|
Return a copy of this node.
|
||||||
|
parent, parent_side, left, and right should be left
|
||||||
|
as None, and will be filled later.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError("Nodes MUST provide a `copy` method!")
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return print_node(self)
|
return print_node(self)
|
||||||
|
@ -135,7 +209,7 @@ class EndNode(Node):
|
||||||
raise NotImplementedError("EndNodes MUST provide a `print_value` method!")
|
raise NotImplementedError("EndNodes MUST provide a `print_value` method!")
|
||||||
|
|
||||||
class ExpandableEndNode(EndNode):
|
class ExpandableEndNode(EndNode):
|
||||||
def expand(self):
|
def expand(self) -> tuple[ReductionType, Node]:
|
||||||
raise NotImplementedError("ExpandableEndNodes MUST provide an `expand` method!")
|
raise NotImplementedError("ExpandableEndNodes MUST provide an `expand` method!")
|
||||||
|
|
||||||
class FreeVar(EndNode):
|
class FreeVar(EndNode):
|
||||||
|
@ -148,7 +222,7 @@ class FreeVar(EndNode):
|
||||||
def print_value(self):
|
def print_value(self):
|
||||||
return f"{self.name}"
|
return f"{self.name}"
|
||||||
|
|
||||||
def clone(self):
|
def copy(self):
|
||||||
return FreeVar(self.name)
|
return FreeVar(self.name)
|
||||||
|
|
||||||
class Macro(ExpandableEndNode):
|
class Macro(ExpandableEndNode):
|
||||||
|
@ -168,14 +242,13 @@ class Macro(ExpandableEndNode):
|
||||||
def print_value(self):
|
def print_value(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def expand(self, *, macro_table = {}):
|
def expand(self, *, macro_table = {}) -> tuple[ReductionType, Node]:
|
||||||
if self.name in macro_table:
|
if self.name in macro_table:
|
||||||
return clone(macro_table[self.name])
|
return ReductionType.MACRO_EXPAND, clone(macro_table[self.name])
|
||||||
else:
|
else:
|
||||||
f = FreeVar(self.name)
|
return ReductionType.MACRO_TO_FREE, FreeVar(self.name)
|
||||||
return f
|
|
||||||
|
|
||||||
def clone(self):
|
def copy(self):
|
||||||
return Macro(self.name)
|
return Macro(self.name)
|
||||||
|
|
||||||
class Church(ExpandableEndNode):
|
class Church(ExpandableEndNode):
|
||||||
|
@ -195,7 +268,7 @@ class Church(ExpandableEndNode):
|
||||||
def print_value(self):
|
def print_value(self):
|
||||||
return str(self.value)
|
return str(self.value)
|
||||||
|
|
||||||
def expand(self):
|
def expand(self) -> tuple[ReductionType, Node]:
|
||||||
f = Bound("f")
|
f = Bound("f")
|
||||||
a = Bound("a")
|
a = Bound("a")
|
||||||
chain = a
|
chain = a
|
||||||
|
@ -203,12 +276,12 @@ class Church(ExpandableEndNode):
|
||||||
for i in range(self.value):
|
for i in range(self.value):
|
||||||
chain = Call(clone(f), clone(chain))
|
chain = Call(clone(f), clone(chain))
|
||||||
|
|
||||||
return Func(
|
return (
|
||||||
f,
|
ReductionType.AUTOCHURCH,
|
||||||
Func(a, chain)
|
Func(f, Func(a, chain))
|
||||||
)
|
)
|
||||||
|
|
||||||
def clone(self):
|
def copy(self):
|
||||||
return Church(self.value)
|
return Church(self.value)
|
||||||
|
|
||||||
bound_counter = 0
|
bound_counter = 0
|
||||||
|
@ -223,7 +296,7 @@ class Bound(EndNode):
|
||||||
else:
|
else:
|
||||||
self.identifier = forced_id
|
self.identifier = forced_id
|
||||||
|
|
||||||
def clone(self):
|
def copy(self):
|
||||||
return Bound(self.name, forced_id = self.identifier)
|
return Bound(self.name, forced_id = self.identifier)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
|
@ -260,7 +333,7 @@ class Func(Node):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<func {self.input!r} {self.left!r}>"
|
return f"<func {self.input!r} {self.left!r}>"
|
||||||
|
|
||||||
def clone(self):
|
def copy(self):
|
||||||
return Func(self.input, None) # type: ignore
|
return Func(self.input, None) # type: ignore
|
||||||
|
|
||||||
class Call(Node):
|
class Call(Node):
|
||||||
|
@ -292,14 +365,13 @@ class Call(Node):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<call {self.left!r} {self.right!r}>"
|
return f"<call {self.left!r} {self.right!r}>"
|
||||||
|
|
||||||
def clone(self):
|
def copy(self):
|
||||||
return Call(None, None) # type: ignore
|
return Call(None, None) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def print_node(node: Node) -> str:
|
def print_node(node: Node) -> str:
|
||||||
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 don't know how to print a {type(node)}")
|
||||||
else:
|
else:
|
||||||
out = ""
|
out = ""
|
||||||
|
|
||||||
|
@ -330,7 +402,7 @@ def clone(node: 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 don't know what to do with a {type(node)}")
|
||||||
|
|
||||||
out = node.clone()
|
out = node.copy()
|
||||||
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 = node
|
ptr = node
|
||||||
from_side = Direction.UP
|
from_side = Direction.UP
|
||||||
|
@ -347,7 +419,7 @@ def clone(node: Node):
|
||||||
elif isinstance(ptr, Func):
|
elif isinstance(ptr, Func):
|
||||||
if from_side == Direction.UP:
|
if from_side == Direction.UP:
|
||||||
from_side, ptr = ptr.go_left()
|
from_side, ptr = ptr.go_left()
|
||||||
out_ptr.set_side(ptr.parent_side, ptr.clone())
|
out_ptr.set_side(ptr.parent_side, ptr.copy())
|
||||||
_, out_ptr = out_ptr.go_left()
|
_, out_ptr = out_ptr.go_left()
|
||||||
elif from_side == Direction.LEFT:
|
elif from_side == Direction.LEFT:
|
||||||
from_side, ptr = ptr.go_up()
|
from_side, ptr = ptr.go_up()
|
||||||
|
@ -355,12 +427,11 @@ def clone(node: Node):
|
||||||
elif isinstance(ptr, Call):
|
elif isinstance(ptr, Call):
|
||||||
if from_side == Direction.UP:
|
if from_side == Direction.UP:
|
||||||
from_side, ptr = ptr.go_left()
|
from_side, ptr = ptr.go_left()
|
||||||
out_ptr.set_side(ptr.parent_side, ptr.clone()
|
out_ptr.set_side(ptr.parent_side, ptr.copy())
|
||||||
)
|
|
||||||
_, out_ptr = out_ptr.go_left()
|
_, out_ptr = out_ptr.go_left()
|
||||||
elif from_side == Direction.LEFT:
|
elif from_side == Direction.LEFT:
|
||||||
from_side, ptr = ptr.go_right()
|
from_side, ptr = ptr.go_right()
|
||||||
out_ptr.set_side(ptr.parent_side, ptr.clone())
|
out_ptr.set_side(ptr.parent_side, ptr.copy())
|
||||||
_, out_ptr = out_ptr.go_right()
|
_, out_ptr = out_ptr.go_right()
|
||||||
elif from_side == Direction.RIGHT:
|
elif from_side == Direction.RIGHT:
|
||||||
from_side, ptr = ptr.go_up()
|
from_side, ptr = ptr.go_up()
|
||||||
|
@ -371,7 +442,6 @@ def clone(node: Node):
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def bind_variables(node: Node, *, ban_macro_name = None) -> None:
|
def bind_variables(node: Node, *, ban_macro_name = None) -> None:
|
||||||
|
|
||||||
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 don't know what to do with a {type(node)}")
|
||||||
|
|
||||||
|
@ -432,31 +502,29 @@ def call_func(fn: Func, arg: Node):
|
||||||
|
|
||||||
|
|
||||||
# Do a single reduction step
|
# Do a single reduction step
|
||||||
def reduce(node: Node, *, macro_table = {}) -> tuple[bool, Node]:
|
def reduce(node: Node, *, macro_table = {}) -> tuple[ReductionType, Node]:
|
||||||
|
|
||||||
if not isinstance(node, Node):
|
if not isinstance(node, Node):
|
||||||
raise TypeError(f"I can't reduce a {type(node)}")
|
raise TypeError(f"I can't reduce a {type(node)}")
|
||||||
|
|
||||||
reduced = False
|
|
||||||
|
|
||||||
out = node
|
out = node
|
||||||
for s, n in out:
|
for s, n in out:
|
||||||
if isinstance(n, Call):
|
if isinstance(n, Call) and (s == Direction.UP):
|
||||||
if s == Direction.UP:
|
|
||||||
if isinstance(n.left, Func):
|
if isinstance(n.left, Func):
|
||||||
if n.parent is None:
|
if n.parent is None:
|
||||||
out = call_func(n.left, n.right)
|
out = call_func(n.left, n.right)
|
||||||
out._set_parent(None, None)
|
out._set_parent(None, None)
|
||||||
else:
|
else:
|
||||||
n.parent.left = call_func(n.left, n.right)
|
n.parent.left = call_func(n.left, n.right)
|
||||||
reduced = True
|
|
||||||
break
|
return ReductionType.FUNCTION_APPLY, out
|
||||||
|
|
||||||
elif isinstance(n.left, ExpandableEndNode):
|
elif isinstance(n.left, ExpandableEndNode):
|
||||||
if isinstance(n.left, Macro):
|
if isinstance(n.left, Macro):
|
||||||
n.left = n.left.expand(macro_table = macro_table)
|
r, n.left = n.left.expand(
|
||||||
|
macro_table = macro_table
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
n.left = n.left.expand()
|
r, n.left = n.left.expand()
|
||||||
reduced = True
|
return r, out
|
||||||
break
|
|
||||||
|
|
||||||
return reduced, out
|
return ReductionType.NOTHING, out
|
||||||
|
|
|
@ -103,39 +103,32 @@ class Runner:
|
||||||
while (self.reduction_limit is None) or (i < self.reduction_limit):
|
while (self.reduction_limit is None) or (i < self.reduction_limit):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
w, r = lamb.node.reduce(
|
red_type, new_node = lamb.node.reduce(
|
||||||
node,
|
node,
|
||||||
macro_table = self.macro_table
|
macro_table = self.macro_table
|
||||||
)
|
)
|
||||||
except RecursionError:
|
except RecursionError:
|
||||||
stop_reason = StopReason.RECURSION
|
stop_reason = StopReason.RECURSION
|
||||||
break
|
break
|
||||||
node = r
|
node = new_node
|
||||||
|
|
||||||
#print(expr)
|
|
||||||
#self.prompt()
|
|
||||||
|
|
||||||
# If we can't reduce this expression anymore,
|
# If we can't reduce this expression anymore,
|
||||||
# it's in beta-normal form.
|
# it's in beta-normal form.
|
||||||
if not w:
|
if red_type == lamb.node.ReductionType.NOTHING:
|
||||||
stop_reason = StopReason.BETA_NORMAL
|
stop_reason = StopReason.BETA_NORMAL
|
||||||
break
|
break
|
||||||
|
|
||||||
# Count reductions
|
# Count reductions
|
||||||
#i += 1
|
|
||||||
#if (
|
|
||||||
# r.reduction_type == tokens.ReductionType.MACRO_EXPAND or
|
|
||||||
# r.reduction_type == tokens.ReductionType.AUTOCHURCH
|
|
||||||
# ):
|
|
||||||
# macro_expansions += 1
|
|
||||||
#else:
|
|
||||||
i += 1
|
i += 1
|
||||||
|
if red_type == lamb.node.ReductionType.FUNCTION_APPLY:
|
||||||
|
macro_expansions += 1
|
||||||
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
stop_reason == StopReason.BETA_NORMAL or
|
stop_reason == StopReason.BETA_NORMAL or
|
||||||
stop_reason == StopReason.LOOP_DETECTED
|
stop_reason == StopReason.LOOP_DETECTED
|
||||||
):
|
):
|
||||||
out_str = str(r) # type: ignore
|
out_str = str(new_node) # type: ignore
|
||||||
|
|
||||||
printf(FormattedText([
|
printf(FormattedText([
|
||||||
("class:result_header", f"\nExit reason: "),
|
("class:result_header", f"\nExit reason: "),
|
||||||
|
@ -195,7 +188,15 @@ class Runner:
|
||||||
|
|
||||||
# If this line is a command, do the command.
|
# If this line is a command, do the command.
|
||||||
elif isinstance(e, Command):
|
elif isinstance(e, Command):
|
||||||
lamb.commands.run(e, self)
|
if e.name not in lamb.commands.commands:
|
||||||
|
printf(
|
||||||
|
FormattedText([
|
||||||
|
("class:warn", f"Unknown command \"{e.name}\"")
|
||||||
|
]),
|
||||||
|
style = lamb.utils.style
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
lamb.commands.commands[e.name](e, self)
|
||||||
|
|
||||||
# If this line is a plain expression, reduce it.
|
# If this line is a plain expression, reduce it.
|
||||||
elif isinstance(e, lamb.node.Node):
|
elif isinstance(e, lamb.node.Node):
|
||||||
|
|
Reference in New Issue