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/lamb_engine/runner/commands.py

420 lines
8.4 KiB
Python
Raw Normal View History

2022-10-21 17:55:31 -07:00
from prompt_toolkit.formatted_text import FormattedText
2022-10-22 07:48:17 -07:00
from prompt_toolkit.formatted_text import HTML
from prompt_toolkit import print_formatted_text as printf
2022-10-21 17:55:31 -07:00
from prompt_toolkit.shortcuts import clear as clear_screen
2022-11-11 15:30:21 -08:00
from prompt_toolkit import prompt
2022-10-21 17:55:31 -07:00
import os.path
from pyparsing import exceptions as ppx
2022-10-21 17:55:31 -07:00
2022-11-11 17:20:59 -08:00
import lamb_engine
2022-10-21 17:55:31 -07:00
commands = {}
help_texts = {}
2022-10-28 16:01:58 -07:00
def lamb_command(
*,
command_name: str | None = None,
help_text: str
):
"""
A decorator that allows us to easily make commands
"""
2022-10-21 17:55:31 -07:00
def inner(func):
2022-10-28 16:01:58 -07:00
name = func.__name__ if command_name is None else command_name
2022-10-21 17:55:31 -07:00
2022-10-28 16:01:58 -07:00
commands[name] = func
help_texts[name] = help_text
return inner
2022-10-22 07:48:17 -07:00
2022-11-11 15:30:21 -08:00
@lamb_command(
command_name = "step",
help_text = "Toggle step-by-step reduction"
)
def cmd_step(command, runner) -> None:
if len(command.args) > 1:
printf(
HTML(
f"<err>Command <code>:{command.name}</code> takes no more than one argument.</err>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-11-11 15:30:21 -08:00
)
return
target = not runner.step_reduction
if len(command.args) == 1:
if command.args[0].lower() in ("y", "yes"):
target = True
elif command.args[0].lower() in ("n", "no"):
target = False
else:
printf(
HTML(
f"<err>Usage: <code>:step [yes|no]</code></err>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-11-11 15:30:21 -08:00
)
return
if target:
printf(
HTML(
2022-11-11 15:45:22 -08:00
f"<warn>Enabled step-by-step reduction.</warn>"
2022-11-11 15:30:21 -08:00
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-11-11 15:30:21 -08:00
)
runner.step_reduction = True
else:
printf(
HTML(
2022-11-11 15:45:22 -08:00
f"<warn>Disabled step-by-step reduction.</warn>"
2022-11-11 15:30:21 -08:00
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-11-11 15:30:21 -08:00
)
runner.step_reduction = False
2022-11-11 15:45:22 -08:00
@lamb_command(
command_name = "expand",
help_text = "Toggle full expansion"
)
def cmd_expand(command, runner) -> None:
if len(command.args) > 1:
printf(
HTML(
f"<err>Command <code>:{command.name}</code> takes no more than one argument.</err>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-11-11 15:45:22 -08:00
)
return
target = not runner.full_expansion
if len(command.args) == 1:
if command.args[0].lower() in ("y", "yes"):
target = True
elif command.args[0].lower() in ("n", "no"):
target = False
else:
printf(
HTML(
f"<err>Usage: <code>:expand [yes|no]</code></err>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-11-11 15:45:22 -08:00
)
return
if target:
printf(
HTML(
f"<warn>Enabled complete expansion.</warn>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-11-11 15:45:22 -08:00
)
runner.full_expansion = True
else:
printf(
HTML(
f"<warn>Disabled complete expansion.</warn>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-11-11 15:45:22 -08:00
)
runner.full_expansion = False
2022-10-28 16:01:58 -07:00
@lamb_command(
command_name = "save",
help_text = "Save macros to a file"
)
def cmd_save(command, runner) -> None:
2022-10-22 07:48:17 -07:00
if len(command.args) != 1:
printf(
HTML(
2022-10-29 15:43:59 -07:00
f"<err>Command <code>:{command.name}</code> takes exactly one argument.</err>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
)
return
target = command.args[0]
if os.path.exists(target):
2022-11-11 15:30:21 -08:00
confirm = prompt(
message = FormattedText([
("class:warn", "File exists. Overwrite? "),
("class:text", "[yes/no]: ")
2022-11-11 15:45:22 -08:00
]),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
).lower()
if confirm != "yes":
printf(
HTML(
"<err>Cancelled.</err>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-10-22 07:48:17 -07:00
)
return
with open(target, "w") as f:
f.write("\n".join(
2022-10-29 13:25:37 -07:00
[f"{n} = {e.export()}" for n, e in runner.macro_table.items()]
))
printf(
HTML(
2022-10-29 15:43:59 -07:00
f"Wrote {len(runner.macro_table)} macros to <code>{target}</code>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
)
2022-10-28 16:01:58 -07:00
@lamb_command(
command_name = "load",
help_text = "Load macros from a file"
)
def cmd_load(command, runner):
if len(command.args) != 1:
printf(
HTML(
2022-10-29 15:43:59 -07:00
f"<err>Command <code>:{command.name}</code> takes exactly one argument.</err>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-10-22 07:48:17 -07:00
)
return
target = command.args[0]
if not os.path.exists(target):
printf(
HTML(
f"<err>File {target} doesn't exist.</err>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
)
return
with open(target, "r") as f:
lines = [x.strip() for x in f.readlines()]
for i in range(len(lines)):
2022-11-07 19:58:11 -08:00
l = lines[i].strip()
# Skip comments and empty lines
if l.startswith("#"):
continue
if l == "":
continue
try:
2022-11-01 21:29:39 -07:00
x = runner.parse(l)[0]
except ppx.ParseException as e:
printf(
FormattedText([
("class:warn", f"Syntax error on line {i+1:02}: "),
2022-10-29 15:43:59 -07:00
("class:code", l[:e.loc]),
("class:err", l[e.loc]),
2022-10-29 15:43:59 -07:00
("class:code", l[e.loc+1:])
]),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
)
2022-10-22 18:53:40 -07:00
return
2022-11-11 17:20:59 -08:00
if not isinstance(x, lamb_engine.runner.runner.MacroDef):
printf(
FormattedText([
("class:warn", f"Skipping line {i+1:02}: "),
2022-10-29 15:43:59 -07:00
("class:code", l),
("class:warn", f" is not a macro definition.")
]),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
)
2022-10-22 18:53:40 -07:00
return
runner.save_macro(x, silent = True)
printf(
FormattedText([
2022-11-11 17:04:05 -08:00
("class:ok", f"Loaded {x.label}: ")
2022-11-11 17:20:59 -08:00
] + lamb_engine.utils.lex_str(str(x.expr))),
style = lamb_engine.utils.style
2022-10-22 18:53:40 -07:00
)
2022-10-28 16:01:58 -07:00
@lamb_command(
help_text = "Delete a macro"
)
def mdel(command, runner) -> None:
if len(command.args) != 1:
printf(
HTML(
2022-10-29 15:43:59 -07:00
f"<err>Command <code>:{command.name}</code> takes exactly one argument.</err>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
)
return
2022-10-22 07:48:17 -07:00
target = command.args[0]
if target not in runner.macro_table:
printf(
HTML(
2022-10-22 08:37:19 -07:00
f"<warn>Macro \"{target}\" is not defined</warn>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-10-22 07:48:17 -07:00
)
return
2022-10-21 17:55:31 -07:00
2022-10-22 07:48:17 -07:00
del runner.macro_table[target]
2022-10-21 17:55:31 -07:00
2022-11-07 19:02:27 -08:00
@lamb_command(
help_text = "Delete all macros"
)
2022-11-11 15:45:22 -08:00
def delmac(command, runner) -> None:
2022-11-11 15:30:21 -08:00
confirm = prompt(
2022-11-07 19:02:27 -08:00
message = FormattedText([
("class:warn", "Are you sure? "),
("class:text", "[yes/no]: ")
2022-11-11 15:45:22 -08:00
]),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-11-07 19:02:27 -08:00
).lower()
if confirm != "yes":
printf(
HTML(
"<err>Cancelled.</err>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-11-07 19:02:27 -08:00
)
return
runner.macro_table = {}
2022-10-22 08:37:19 -07:00
2022-10-28 16:01:58 -07:00
@lamb_command(
help_text = "Show macros"
)
def macros(command, runner) -> None:
2022-11-07 19:02:27 -08:00
if len(runner.macro_table) == 0:
printf(FormattedText([
("class:warn", "No macros are defined."),
]),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-11-07 19:02:27 -08:00
)
else:
printf(FormattedText([
("class:cmd_h", "\nDefined Macros:\n"),
] +
[
("class:text", f"\t{name} \t {exp}\n")
for name, exp in runner.macro_table.items()
]),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-11-07 19:02:27 -08:00
)
2022-10-21 17:55:31 -07:00
2022-10-28 16:01:58 -07:00
@lamb_command(
help_text = "Clear the screen"
)
def clear(command, runner) -> None:
2022-10-21 17:55:31 -07:00
clear_screen()
2022-11-11 17:20:59 -08:00
lamb_engine.utils.show_greeting()
2022-10-21 17:55:31 -07:00
2022-10-28 19:47:50 -07:00
@lamb_command(
help_text = "Get or set reduction limit"
)
def rlimit(command, runner) -> None:
if len(command.args) == 0:
if runner.reduction_limit is None:
printf(
HTML(
"<ok>No reduction limit is set</ok>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-10-28 19:47:50 -07:00
)
else:
printf(
HTML(
f"<ok>Reduction limit is {runner.reduction_limit:,}</ok>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-10-28 19:47:50 -07:00
)
return
elif len(command.args) != 1:
printf(
HTML(
2022-10-29 15:43:59 -07:00
f"<err>Command <code>:{command.name}</code> takes exactly one argument.</err>"
2022-10-28 19:47:50 -07:00
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-10-28 19:47:50 -07:00
)
return
t = command.args[0]
if t.lower() == "none":
runner.reduction_limit = None
printf(
HTML(
f"<ok>Removed reduction limit</ok>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-10-28 19:47:50 -07:00
)
return
try:
t = int(t)
except ValueError:
printf(
HTML(
"<err>Reduction limit must be a positive integer or \"none\".</err>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-10-28 19:47:50 -07:00
)
return
if 50 > t:
printf(
HTML(
"<err>Reduction limit must be at least 50.</err>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-10-28 19:47:50 -07:00
)
return
runner.reduction_limit = t
printf(
HTML(
f"<ok>Set reduction limit to {t:,}</ok>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-10-28 19:47:50 -07:00
)
2022-10-21 17:55:31 -07:00
2022-10-28 16:01:58 -07:00
@lamb_command(
help_text = "Print this help"
)
def help(command, runner) -> None:
printf(
HTML(
2022-10-29 15:43:59 -07:00
"\n<text>" +
2022-10-28 19:48:12 -07:00
2022-10-22 08:37:19 -07:00
"<cmd_h>Usage:</cmd_h>" +
"\n" +
"\tWrite lambda expressions using your <cmd_key>\\</cmd_key> key." +
"\n" +
2022-10-29 15:43:59 -07:00
"\tMacros can be defined using <cmd_key>=</cmd_key>, as in <code>T = λab.a</code>" +
2022-10-22 08:37:19 -07:00
"\n" +
2022-10-29 15:43:59 -07:00
"\tRun commands using <cmd_key>:</cmd_key>, for example <code>:help</code>" +
"\n" +
"\tHistory can be accessed with <cmd_key>$</cmd_key>, which will expand to the result of the last successful reduction." +
2022-10-22 08:37:19 -07:00
"\n\n" +
"<cmd_h>Commands:</cmd_h>"+
"\n" +
"\n".join([
2022-10-29 15:43:59 -07:00
f"\t<code>{name}</code> \t {text}"
2022-10-22 08:37:19 -07:00
for name, text in help_texts.items()
]) +
2022-10-28 19:48:12 -07:00
"\n\n"
"<muted>Detailed documentation can be found on this project's git page.</muted>" +
2022-10-29 15:43:59 -07:00
"</text>"
),
2022-11-11 17:20:59 -08:00
style = lamb_engine.utils.style
2022-10-21 17:55:31 -07:00
)