This repository has been archived on 2024-11-05. You can view files and clone it, but cannot push or open issues/pull-requests.
lamb/runner.py

98 lines
2.4 KiB
Python

from prompt_toolkit.formatted_text import FormattedText
import tokens
from parser import Parser
import commands
from runstatus import RunStatus
from runstatus import MacroStatus
from runstatus import StopReason
from runstatus import ReduceStatus
from runstatus import CommandStatus
class Runner:
def __init__(self):
self.macro_table = {}
# Maximum amount of reductions.
# If None, no maximum is enforced.
self.reduction_limit: int | None = 300
def exec_command(self, command: str) -> CommandStatus:
if command in commands.commands:
return commands.run(command, self)
# Handle unknown commands
else:
return CommandStatus(
formatted_text = FormattedText([
("#FFFF00", f"Unknown command \"{command}\"")
])
)
def reduce_expression(self, expr: tokens.LambdaToken) -> ReduceStatus:
# Reduction Counter.
# We also count macro expansions,
# and subtract those from the final count.
i = 0
macro_expansions = 0
while (self.reduction_limit is None) or (i < self.reduction_limit):
r = expr.reduce(self.macro_table)
expr = r.output
# If we can't reduce this expression anymore,
# it's in beta-normal form.
if not r.was_reduced:
return ReduceStatus(
reduction_count = i - macro_expansions,
stop_reason = StopReason.BETA_NORMAL,
result = r.output
)
# Count reductions
i += 1
if r.reduction_type == tokens.ReductionType.MACRO_EXPAND:
macro_expansions += 1
return ReduceStatus(
reduction_count = i - macro_expansions,
stop_reason = StopReason.MAX_EXCEEDED,
result = r.output # type: ignore
)
# Apply a list of definitions
def run(self, line: str) -> RunStatus:
e = Parser.parse_line(line)
# If this line is a macro definition, save the macro.
if isinstance(e, tokens.macro_expression):
was_rewritten = e.label in self.macro_table
e.exp.bind_variables()
self.macro_table[e.label] = e.exp
return MacroStatus(
was_rewritten = was_rewritten,
macro_label = e.label
)
# If this line is a command, do the command.
elif isinstance(e, tokens.command):
return self.exec_command(e.name)
# If this line is a plain expression, reduce it.
elif isinstance(e, tokens.LambdaToken):
e.bind_variables()
return self.reduce_expression(e)
else:
raise TypeError(f"I don't know what to do with a {type(e)}")
def run_lines(self, lines: list[str]):
for l in lines:
self.run(l)