Added RecursionError handling and macro name check
parent
123d885adf
commit
044ec60a49
|
@ -7,9 +7,7 @@
|
||||||
- Write a nice README
|
- Write a nice README
|
||||||
- Handle or avoid recursion errors
|
- Handle or avoid recursion errors
|
||||||
- Fix colors
|
- Fix colors
|
||||||
- Clean up files
|
|
||||||
- Print macro content if only a macro is typed
|
- Print macro content if only a macro is typed
|
||||||
- Don't expand numbers until you have to
|
|
||||||
|
|
||||||
## Todo:
|
## Todo:
|
||||||
- live syntax check
|
- live syntax check
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from tkinter import E
|
||||||
from prompt_toolkit import PromptSession
|
from prompt_toolkit import PromptSession
|
||||||
from prompt_toolkit.formatted_text import FormattedText
|
from prompt_toolkit.formatted_text import FormattedText
|
||||||
from prompt_toolkit import print_formatted_text as printf
|
from prompt_toolkit import print_formatted_text as printf
|
||||||
|
@ -12,9 +13,10 @@ import lamb.utils as utils
|
||||||
|
|
||||||
class StopReason(enum.Enum):
|
class StopReason(enum.Enum):
|
||||||
BETA_NORMAL = ("class:text", "β-normal form")
|
BETA_NORMAL = ("class:text", "β-normal form")
|
||||||
LOOP_DETECTED = ("class:warn", "loop detected")
|
LOOP_DETECTED = ("class:warn", "Loop detected")
|
||||||
MAX_EXCEEDED = ("class:err", "too many reductions")
|
MAX_EXCEEDED = ("class:err", "Too many reductions")
|
||||||
INTERRUPT = ("class:warn", "user interrupt")
|
INTERRUPT = ("class:warn", "User interrupt")
|
||||||
|
RECURSION = ("class:err", "Python Recursion Error")
|
||||||
|
|
||||||
|
|
||||||
class Runner:
|
class Runner:
|
||||||
|
@ -50,6 +52,9 @@ class Runner:
|
||||||
# Give the elements of this expression access to the runner.
|
# Give the elements of this expression access to the runner.
|
||||||
# Runner must be set BEFORE variables are bound.
|
# Runner must be set BEFORE variables are bound.
|
||||||
e.set_runner(self)
|
e.set_runner(self)
|
||||||
|
if isinstance(e, tokens.macro_expression):
|
||||||
|
e.bind_variables(ban_macro_name = e.label)
|
||||||
|
else:
|
||||||
e.bind_variables()
|
e.bind_variables()
|
||||||
return e
|
return e
|
||||||
|
|
||||||
|
@ -63,8 +68,14 @@ class Runner:
|
||||||
macro_expansions = 0
|
macro_expansions = 0
|
||||||
|
|
||||||
stop_reason = StopReason.MAX_EXCEEDED
|
stop_reason = StopReason.MAX_EXCEEDED
|
||||||
|
|
||||||
while (self.reduction_limit is None) or (i < self.reduction_limit):
|
while (self.reduction_limit is None) or (i < self.reduction_limit):
|
||||||
|
|
||||||
|
try:
|
||||||
r = expr.reduce()
|
r = expr.reduce()
|
||||||
|
except RecursionError:
|
||||||
|
stop_reason = StopReason.RECURSION
|
||||||
|
break
|
||||||
expr = r.output
|
expr = r.output
|
||||||
|
|
||||||
#print(expr)
|
#print(expr)
|
||||||
|
@ -86,19 +97,37 @@ class Runner:
|
||||||
else:
|
else:
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
|
if (
|
||||||
|
stop_reason == StopReason.BETA_NORMAL or
|
||||||
|
stop_reason == StopReason.LOOP_DETECTED
|
||||||
|
):
|
||||||
out_str = str(r.output) # type: ignore
|
out_str = str(r.output) # type: ignore
|
||||||
|
|
||||||
printf(FormattedText([
|
printf(FormattedText([
|
||||||
("class:result_header", f"\nExit reason: "),
|
("class:result_header", f"\nExit reason: "),
|
||||||
stop_reason.value,
|
stop_reason.value,
|
||||||
|
|
||||||
("class:result_header", f"\nReduction count: "),
|
("class:result_header", f"\nMacro expansions: "),
|
||||||
|
("class:text", str(macro_expansions)),
|
||||||
|
|
||||||
|
("class:result_header", f"\nReductions: "),
|
||||||
("class:text", str(i)),
|
("class:text", str(i)),
|
||||||
|
|
||||||
|
|
||||||
("class:result_header", "\n\n => "),
|
("class:result_header", "\n\n => "),
|
||||||
("class:text", out_str),
|
("class:text", out_str),
|
||||||
]), style = utils.style)
|
]), style = utils.style)
|
||||||
|
else:
|
||||||
|
printf(FormattedText([
|
||||||
|
("class:result_header", f"\nExit reason: "),
|
||||||
|
stop_reason.value,
|
||||||
|
|
||||||
|
("class:result_header", f"\nMacro expansions: "),
|
||||||
|
("class:text", str(macro_expansions)),
|
||||||
|
|
||||||
|
("class:result_header", f"\nReductions: "),
|
||||||
|
("class:text", str(i)),
|
||||||
|
]), style = utils.style)
|
||||||
|
|
||||||
def save_macro(self, macro: tokens.macro_expression, *, silent = False) -> None:
|
def save_macro(self, macro: tokens.macro_expression, *, silent = False) -> None:
|
||||||
was_rewritten = macro.label in self.macro_table
|
was_rewritten = macro.label in self.macro_table
|
||||||
|
|
|
@ -50,7 +50,7 @@ class LambdaToken:
|
||||||
def set_runner(self, runner):
|
def set_runner(self, runner):
|
||||||
self.runner = runner
|
self.runner = runner
|
||||||
|
|
||||||
def bind_variables(self) -> None:
|
def bind_variables(self, *, ban_macro_name: str | None = None) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def reduce(self) -> ReductionStatus:
|
def reduce(self) -> ReductionStatus:
|
||||||
|
@ -127,12 +127,7 @@ class free_variable(LambdaToken):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.label}"
|
return f"{self.label}"
|
||||||
|
|
||||||
class command:
|
class command(LambdaToken):
|
||||||
def set_runner(self, runner):
|
|
||||||
pass
|
|
||||||
def bind_variables(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_parse(result):
|
def from_parse(result):
|
||||||
return command(
|
return command(
|
||||||
|
@ -175,6 +170,10 @@ class macro(LambdaToken):
|
||||||
raise TypeError("Can only compare macro with macro")
|
raise TypeError("Can only compare macro with macro")
|
||||||
return self.name == other.name
|
return self.name == other.name
|
||||||
|
|
||||||
|
def bind_variables(self, *, ban_macro_name=None) -> None:
|
||||||
|
if self.name == ban_macro_name:
|
||||||
|
raise ReductionError(f"Cannot use macro \"{ban_macro_name}\" here.")
|
||||||
|
|
||||||
def reduce(
|
def reduce(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
|
@ -210,7 +209,7 @@ class macro(LambdaToken):
|
||||||
was_reduced = True
|
was_reduced = True
|
||||||
)
|
)
|
||||||
|
|
||||||
class macro_expression:
|
class macro_expression(LambdaToken):
|
||||||
"""
|
"""
|
||||||
Represents a line that looks like
|
Represents a line that looks like
|
||||||
<name> = <expression>
|
<name> = <expression>
|
||||||
|
@ -229,8 +228,9 @@ class macro_expression:
|
||||||
|
|
||||||
def set_runner(self, runner):
|
def set_runner(self, runner):
|
||||||
self.expr.set_runner(runner)
|
self.expr.set_runner(runner)
|
||||||
def bind_variables(self):
|
|
||||||
self.expr.bind_variables()
|
def bind_variables(self, *, ban_macro_name: str | None = None):
|
||||||
|
self.expr.bind_variables(ban_macro_name = ban_macro_name)
|
||||||
|
|
||||||
def __init__(self, label: str, expr: LambdaToken):
|
def __init__(self, label: str, expr: LambdaToken):
|
||||||
self.label = label
|
self.label = label
|
||||||
|
@ -314,7 +314,8 @@ class lambda_func(LambdaToken):
|
||||||
placeholder: macro | None = None,
|
placeholder: macro | None = None,
|
||||||
val: bound_variable | None = None,
|
val: bound_variable | None = None,
|
||||||
*,
|
*,
|
||||||
binding_self: bool = False
|
binding_self: bool = False,
|
||||||
|
ban_macro_name: str | None = None
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Go through this function and all the functions inside it,
|
Go through this function and all the functions inside it,
|
||||||
|
@ -370,19 +371,22 @@ class lambda_func(LambdaToken):
|
||||||
self.bind_variables(
|
self.bind_variables(
|
||||||
self.input,
|
self.input,
|
||||||
new_bound_var,
|
new_bound_var,
|
||||||
binding_self = True
|
binding_self = True,
|
||||||
|
ban_macro_name = ban_macro_name
|
||||||
)
|
)
|
||||||
self.input = new_bound_var
|
self.input = new_bound_var
|
||||||
|
|
||||||
|
|
||||||
# Bind variables inside this function.
|
# Bind variables inside this function.
|
||||||
if isinstance(self.output, macro) and placeholder is not None:
|
if isinstance(self.output, macro) and placeholder is not None:
|
||||||
|
if self.output.name == ban_macro_name:
|
||||||
|
raise ReductionError(f"Cannot use macro \"{ban_macro_name}\" here.")
|
||||||
if self.output == placeholder:
|
if self.output == placeholder:
|
||||||
self.output = val # type: ignore
|
self.output = val # type: ignore
|
||||||
elif isinstance(self.output, lambda_func):
|
elif isinstance(self.output, lambda_func):
|
||||||
self.output.bind_variables(placeholder, val)
|
self.output.bind_variables(placeholder, val, ban_macro_name = ban_macro_name)
|
||||||
elif isinstance(self.output, lambda_apply):
|
elif isinstance(self.output, lambda_apply):
|
||||||
self.output.bind_variables(placeholder, val)
|
self.output.bind_variables(placeholder, val, ban_macro_name = ban_macro_name)
|
||||||
|
|
||||||
def reduce(self) -> ReductionStatus:
|
def reduce(self) -> ReductionStatus:
|
||||||
|
|
||||||
|
@ -484,7 +488,9 @@ class lambda_apply(LambdaToken):
|
||||||
def bind_variables(
|
def bind_variables(
|
||||||
self,
|
self,
|
||||||
placeholder: macro | None = None,
|
placeholder: macro | None = None,
|
||||||
val: bound_variable | None = None
|
val: bound_variable | None = None,
|
||||||
|
*,
|
||||||
|
ban_macro_name: str | None = None
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Does exactly what lambda_func.bind_variables does,
|
Does exactly what lambda_func.bind_variables does,
|
||||||
|
@ -499,20 +505,24 @@ class lambda_apply(LambdaToken):
|
||||||
# If val and placeholder are None,
|
# If val and placeholder are None,
|
||||||
# everything below should still work as expected.
|
# everything below should still work as expected.
|
||||||
if isinstance(self.fn, macro) and placeholder is not None:
|
if isinstance(self.fn, macro) and placeholder is not None:
|
||||||
|
if self.fn.name == ban_macro_name:
|
||||||
|
raise ReductionError(f"Cannot use macro \"{ban_macro_name}\" here.")
|
||||||
if self.fn == placeholder:
|
if self.fn == placeholder:
|
||||||
self.fn = val # type: ignore
|
self.fn = val # type: ignore
|
||||||
elif isinstance(self.fn, lambda_func):
|
elif isinstance(self.fn, lambda_func):
|
||||||
self.fn.bind_variables(placeholder, val)
|
self.fn.bind_variables(placeholder, val, ban_macro_name = ban_macro_name)
|
||||||
elif isinstance(self.fn, lambda_apply):
|
elif isinstance(self.fn, lambda_apply):
|
||||||
self.fn.bind_variables(placeholder, val)
|
self.fn.bind_variables(placeholder, val, ban_macro_name = ban_macro_name)
|
||||||
|
|
||||||
if isinstance(self.arg, macro) and placeholder is not None:
|
if isinstance(self.arg, macro) and placeholder is not None:
|
||||||
|
if self.arg.name == ban_macro_name:
|
||||||
|
raise ReductionError(f"Cannot use macro \"{ban_macro_name}\" here.")
|
||||||
if self.arg == placeholder:
|
if self.arg == placeholder:
|
||||||
self.arg = val # type: ignore
|
self.arg = val # type: ignore
|
||||||
elif isinstance(self.arg, lambda_func):
|
elif isinstance(self.arg, lambda_func):
|
||||||
self.arg.bind_variables(placeholder, val)
|
self.arg.bind_variables(placeholder, val, ban_macro_name = ban_macro_name)
|
||||||
elif isinstance(self.arg, lambda_apply):
|
elif isinstance(self.arg, lambda_apply):
|
||||||
self.arg.bind_variables(placeholder, val)
|
self.arg.bind_variables(placeholder, val, ban_macro_name = ban_macro_name)
|
||||||
|
|
||||||
def sub_bound_var(
|
def sub_bound_var(
|
||||||
self,
|
self,
|
||||||
|
|
Reference in New Issue