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

99 lines
2.4 KiB
Python
Raw Normal View History

2022-10-21 17:55:31 -07:00
from prompt_toolkit.formatted_text import FormattedText
2022-10-21 14:40:49 -07:00
import tokens
from parser import Parser
2022-10-21 17:55:31 -07:00
import commands
from runstatus import RunStatus
from runstatus import MacroStatus
from runstatus import StopReason
from runstatus import ReduceStatus
from runstatus import CommandStatus
2022-10-21 17:05:25 -07:00
2022-10-21 14:40:49 -07:00
class Runner:
def __init__(self):
self.macro_table = {}
2022-10-21 17:05:25 -07:00
# Maximum amount of reductions.
# If None, no maximum is enforced.
self.reduction_limit: int | None = 300
2022-10-21 14:40:49 -07:00
2022-10-21 17:55:31 -07:00
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}\"")
])
)
2022-10-21 14:44:52 -07:00
2022-10-21 17:05:25 -07:00
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
2022-10-21 19:39:45 -07:00
while (self.reduction_limit is None) or (i < self.reduction_limit):
2022-10-21 17:05:25 -07:00
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,
2022-10-21 19:39:45 -07:00
result = r.output # type: ignore
2022-10-21 17:05:25 -07:00
)
2022-10-21 14:40:49 -07:00
# Apply a list of definitions
2022-10-21 17:05:25 -07:00
def run(self, line: str) -> RunStatus:
2022-10-21 14:40:49 -07:00
e = Parser.parse_line(line)
2022-10-21 17:05:25 -07:00
# If this line is a macro definition, save the macro.
2022-10-21 14:40:49 -07:00
if isinstance(e, tokens.macro_expression):
2022-10-21 17:05:25 -07:00
was_rewritten = e.label in self.macro_table
2022-10-21 14:40:49 -07:00
e.exp.bind_variables()
self.macro_table[e.label] = e.exp
2022-10-21 17:05:25 -07:00
return MacroStatus(
was_rewritten = was_rewritten,
2022-10-21 19:55:15 -07:00
macro_label = e.label,
macro_expr = e.exp
2022-10-21 17:05:25 -07:00
)
# If this line is a command, do the command.
2022-10-21 14:40:49 -07:00
elif isinstance(e, tokens.command):
2022-10-21 17:05:25 -07:00
return self.exec_command(e.name)
# If this line is a plain expression, reduce it.
2022-10-21 19:39:45 -07:00
elif isinstance(e, tokens.LambdaToken):
2022-10-21 14:40:49 -07:00
e.bind_variables()
2022-10-21 17:05:25 -07:00
return self.reduce_expression(e)
2022-10-21 19:39:45 -07:00
else:
raise TypeError(f"I don't know what to do with a {type(e)}")
2022-10-21 14:40:49 -07:00
2022-10-21 17:05:25 -07:00
def run_lines(self, lines: list[str]):
2022-10-21 14:40:49 -07:00
for l in lines:
self.run(l)