Rearranged code
parent
239aa210c5
commit
123d885adf
|
@ -9,7 +9,6 @@ from prompt_toolkit.lexers import Lexer
|
||||||
from pyparsing import exceptions as ppx
|
from pyparsing import exceptions as ppx
|
||||||
|
|
||||||
import lamb.runner as runner
|
import lamb.runner as runner
|
||||||
import lamb.runstatus as rs
|
|
||||||
import lamb.tokens as tokens
|
import lamb.tokens as tokens
|
||||||
import lamb.utils as utils
|
import lamb.utils as utils
|
||||||
|
|
||||||
|
@ -100,31 +99,4 @@ while True:
|
||||||
]), style = utils.style)
|
]), style = utils.style)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# If this line defined a macro, print nothing.
|
|
||||||
if isinstance(x, rs.MacroStatus):
|
|
||||||
printf(FormattedText([
|
|
||||||
("class:text", "Set "),
|
|
||||||
("class:syn_macro", x.macro_label),
|
|
||||||
("class:text", " to "),
|
|
||||||
("class:text", str(x.macro_expr))
|
|
||||||
]), style = utils.style)
|
|
||||||
|
|
||||||
|
|
||||||
if isinstance(x, rs.CommandStatus):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# If this line was an expression, print reduction status
|
|
||||||
elif isinstance(x, rs.ReduceStatus):
|
|
||||||
printf(FormattedText([
|
|
||||||
("class:result_header", f"\nExit reason: "),
|
|
||||||
x.stop_reason.value,
|
|
||||||
|
|
||||||
("class:result_header", f"\nReduction count: "),
|
|
||||||
("class:text", str(x.reduction_count)),
|
|
||||||
|
|
||||||
|
|
||||||
("class:result_header", "\n\n => "),
|
|
||||||
("class:text", str(x.result)),
|
|
||||||
]), style = utils.style)
|
|
||||||
|
|
||||||
printf("")
|
printf("")
|
||||||
|
|
|
@ -6,9 +6,9 @@ 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.runstatus as rs
|
|
||||||
import lamb.utils as utils
|
|
||||||
|
|
||||||
|
import lamb.tokens
|
||||||
|
import lamb.utils
|
||||||
|
|
||||||
|
|
||||||
commands = {}
|
commands = {}
|
||||||
|
@ -26,7 +26,7 @@ def run(command, runner) -> None:
|
||||||
FormattedText([
|
FormattedText([
|
||||||
("class:warn", f"Unknown command \"{command.name}\"")
|
("class:warn", f"Unknown command \"{command.name}\"")
|
||||||
]),
|
]),
|
||||||
style = utils.style
|
style = lamb.utils.style
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
commands[command.name](command, runner)
|
commands[command.name](command, runner)
|
||||||
|
@ -39,7 +39,7 @@ def save(command, runner) -> None:
|
||||||
HTML(
|
HTML(
|
||||||
"<err>Command <cmd_code>:save</cmd_code> takes exactly one argument.</err>"
|
"<err>Command <cmd_code>:save</cmd_code> takes exactly one argument.</err>"
|
||||||
),
|
),
|
||||||
style = utils.style
|
style = lamb.utils.style
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ def save(command, runner) -> None:
|
||||||
HTML(
|
HTML(
|
||||||
"<err>Cancelled.</err>"
|
"<err>Cancelled.</err>"
|
||||||
),
|
),
|
||||||
style = utils.style
|
style = lamb.utils.style
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ def save(command, runner) -> None:
|
||||||
HTML(
|
HTML(
|
||||||
f"Wrote {len(runner.macro_table)} macros to <cmd_code>{target}</cmd_code>"
|
f"Wrote {len(runner.macro_table)} macros to <cmd_code>{target}</cmd_code>"
|
||||||
),
|
),
|
||||||
style = utils.style
|
style = lamb.utils.style
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ def load(command, runner):
|
||||||
HTML(
|
HTML(
|
||||||
"<err>Command <cmd_code>:load</cmd_code> takes exactly one argument.</err>"
|
"<err>Command <cmd_code>:load</cmd_code> takes exactly one argument.</err>"
|
||||||
),
|
),
|
||||||
style = utils.style
|
style = lamb.utils.style
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ def load(command, runner):
|
||||||
HTML(
|
HTML(
|
||||||
f"<err>File {target} doesn't exist.</err>"
|
f"<err>File {target} doesn't exist.</err>"
|
||||||
),
|
),
|
||||||
style = utils.style
|
style = lamb.utils.style
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ def load(command, runner):
|
||||||
for i in range(len(lines)):
|
for i in range(len(lines)):
|
||||||
l = lines[i]
|
l = lines[i]
|
||||||
try:
|
try:
|
||||||
x = runner.run(l, macro_only = True)
|
x = runner.parse(l)
|
||||||
except ppx.ParseException as e:
|
except ppx.ParseException as e:
|
||||||
printf(
|
printf(
|
||||||
FormattedText([
|
FormattedText([
|
||||||
|
@ -110,24 +110,29 @@ def load(command, runner):
|
||||||
("class:err", l[e.loc]),
|
("class:err", l[e.loc]),
|
||||||
("class:cmd_code", l[e.loc+1:])
|
("class:cmd_code", l[e.loc+1:])
|
||||||
]),
|
]),
|
||||||
style = utils.style
|
style = lamb.utils.style
|
||||||
)
|
)
|
||||||
except rs.NotAMacro:
|
return
|
||||||
|
|
||||||
|
if not isinstance(x, lamb.tokens.macro_expression):
|
||||||
printf(
|
printf(
|
||||||
FormattedText([
|
FormattedText([
|
||||||
("class:warn", f"Skipping line {i+1:02}: "),
|
("class:warn", f"Skipping line {i+1:02}: "),
|
||||||
("class:cmd_code", l),
|
("class:cmd_code", l),
|
||||||
("class:warn", f" is not a macro definition.")
|
("class:warn", f" is not a macro definition.")
|
||||||
]),
|
]),
|
||||||
style = utils.style
|
style = lamb.utils.style
|
||||||
)
|
)
|
||||||
else:
|
return
|
||||||
|
|
||||||
|
runner.save_macro(x, silent = True)
|
||||||
|
|
||||||
printf(
|
printf(
|
||||||
FormattedText([
|
FormattedText([
|
||||||
("class:ok", f"Loaded {x.macro_label}: "),
|
("class:ok", f"Loaded {x.label}: "),
|
||||||
("class:cmd_code", str(x.macro_expr))
|
("class:cmd_code", str(x.expr))
|
||||||
]),
|
]),
|
||||||
style = utils.style
|
style = lamb.utils.style
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -139,7 +144,7 @@ def mdel(command, runner) -> None:
|
||||||
HTML(
|
HTML(
|
||||||
"<err>Command <cmd_code>:mdel</cmd_code> takes exactly one argument.</err>"
|
"<err>Command <cmd_code>:mdel</cmd_code> takes exactly one argument.</err>"
|
||||||
),
|
),
|
||||||
style = utils.style
|
style = lamb.utils.style
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -149,7 +154,7 @@ def mdel(command, runner) -> None:
|
||||||
HTML(
|
HTML(
|
||||||
f"<warn>Macro \"{target}\" is not defined</warn>"
|
f"<warn>Macro \"{target}\" is not defined</warn>"
|
||||||
),
|
),
|
||||||
style = utils.style
|
style = lamb.utils.style
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -166,13 +171,13 @@ def macros(command, runner) -> None:
|
||||||
("class:cmd_text", f"\t{name} \t {exp}\n")
|
("class:cmd_text", f"\t{name} \t {exp}\n")
|
||||||
for name, exp in runner.macro_table.items()
|
for name, exp in runner.macro_table.items()
|
||||||
]),
|
]),
|
||||||
style = 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()
|
||||||
utils.show_greeting()
|
lamb.utils.show_greeting()
|
||||||
|
|
||||||
|
|
||||||
@lamb_command(help_text = "Print this help")
|
@lamb_command(help_text = "Print this help")
|
||||||
|
@ -196,5 +201,5 @@ def help(command, runner) -> None:
|
||||||
]) +
|
]) +
|
||||||
"</cmd_text>"
|
"</cmd_text>"
|
||||||
),
|
),
|
||||||
style = utils.style
|
style = lamb.utils.style
|
||||||
)
|
)
|
|
@ -1,10 +1,20 @@
|
||||||
from prompt_toolkit import PromptSession
|
from prompt_toolkit import PromptSession
|
||||||
|
from prompt_toolkit.formatted_text import FormattedText
|
||||||
|
from prompt_toolkit import print_formatted_text as printf
|
||||||
|
|
||||||
|
import enum
|
||||||
|
|
||||||
import lamb.commands as commands
|
import lamb.commands as commands
|
||||||
from lamb.parser import LambdaParser
|
from lamb.parser import LambdaParser
|
||||||
import lamb.tokens as tokens
|
import lamb.tokens as tokens
|
||||||
import lamb.utils as utils
|
import lamb.utils as utils
|
||||||
import lamb.runstatus as rs
|
|
||||||
|
|
||||||
|
class StopReason(enum.Enum):
|
||||||
|
BETA_NORMAL = ("class:text", "β-normal form")
|
||||||
|
LOOP_DETECTED = ("class:warn", "loop detected")
|
||||||
|
MAX_EXCEEDED = ("class:err", "too many reductions")
|
||||||
|
INTERRUPT = ("class:warn", "user interrupt")
|
||||||
|
|
||||||
|
|
||||||
class Runner:
|
class Runner:
|
||||||
|
@ -24,6 +34,7 @@ class Runner:
|
||||||
|
|
||||||
# Maximum amount of reductions.
|
# Maximum amount of reductions.
|
||||||
# If None, no maximum is enforced.
|
# If None, no maximum is enforced.
|
||||||
|
# Must be at least 1.
|
||||||
self.reduction_limit: int | None = 1_000_000
|
self.reduction_limit: int | None = 1_000_000
|
||||||
|
|
||||||
# Ensure bound variables are unique.
|
# Ensure bound variables are unique.
|
||||||
|
@ -34,8 +45,16 @@ class Runner:
|
||||||
def prompt(self):
|
def prompt(self):
|
||||||
return self.prompt_session.prompt(message = self.prompt_message)
|
return self.prompt_session.prompt(message = self.prompt_message)
|
||||||
|
|
||||||
|
def parse(self, line):
|
||||||
|
e = self.parser.parse_line(line)
|
||||||
|
# Give the elements of this expression access to the runner.
|
||||||
|
# Runner must be set BEFORE variables are bound.
|
||||||
|
e.set_runner(self)
|
||||||
|
e.bind_variables()
|
||||||
|
return e
|
||||||
|
|
||||||
def reduce_expression(self, expr: tokens.LambdaToken) -> rs.ReduceStatus:
|
|
||||||
|
def reduce_expression(self, expr: tokens.LambdaToken) -> None:
|
||||||
|
|
||||||
# Reduction Counter.
|
# Reduction Counter.
|
||||||
# We also count macro (and church) expansions,
|
# We also count macro (and church) expansions,
|
||||||
|
@ -43,7 +62,7 @@ class Runner:
|
||||||
i = 0
|
i = 0
|
||||||
macro_expansions = 0
|
macro_expansions = 0
|
||||||
|
|
||||||
|
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):
|
||||||
r = expr.reduce()
|
r = expr.reduce()
|
||||||
expr = r.output
|
expr = r.output
|
||||||
|
@ -54,11 +73,8 @@ class Runner:
|
||||||
# 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 r.was_reduced:
|
if not r.was_reduced:
|
||||||
return rs.ReduceStatus(
|
stop_reason = StopReason.BETA_NORMAL
|
||||||
reduction_count = i - macro_expansions,
|
break
|
||||||
stop_reason = rs.StopReason.BETA_NORMAL,
|
|
||||||
result = r.output
|
|
||||||
)
|
|
||||||
|
|
||||||
# Count reductions
|
# Count reductions
|
||||||
#i += 1
|
#i += 1
|
||||||
|
@ -70,43 +86,47 @@ class Runner:
|
||||||
else:
|
else:
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
return rs.ReduceStatus(
|
out_str = str(r.output) # type: ignore
|
||||||
reduction_count = i, # - macro_expansions,
|
|
||||||
stop_reason = rs.StopReason.MAX_EXCEEDED,
|
|
||||||
result = r.output # type: ignore
|
|
||||||
)
|
|
||||||
|
|
||||||
|
printf(FormattedText([
|
||||||
|
("class:result_header", f"\nExit reason: "),
|
||||||
|
stop_reason.value,
|
||||||
|
|
||||||
|
("class:result_header", f"\nReduction count: "),
|
||||||
|
("class:text", str(i)),
|
||||||
|
|
||||||
|
|
||||||
|
("class:result_header", "\n\n => "),
|
||||||
|
("class:text", out_str),
|
||||||
|
]), style = utils.style)
|
||||||
|
|
||||||
|
def save_macro(self, macro: tokens.macro_expression, *, silent = False) -> None:
|
||||||
|
was_rewritten = macro.label in self.macro_table
|
||||||
|
self.macro_table[macro.label] = macro.expr
|
||||||
|
|
||||||
|
if not silent:
|
||||||
|
printf(FormattedText([
|
||||||
|
("class:text", "Set "),
|
||||||
|
("class:syn_macro", macro.label),
|
||||||
|
("class:text", " to "),
|
||||||
|
("class:text", str(macro.expr))
|
||||||
|
]), style = utils.style)
|
||||||
|
|
||||||
# Apply a list of definitions
|
# Apply a list of definitions
|
||||||
def run(self, line: str, *, macro_only = False) -> rs.RunStatus:
|
def run(self, line: str, *, silent = False) -> None:
|
||||||
e = self.parser.parse_line(line)
|
e = self.parse(line)
|
||||||
# Give the elements of this expression access to the runner.
|
|
||||||
# Runner must be set BEFORE variables are bound.
|
|
||||||
e.set_runner(self)
|
|
||||||
e.bind_variables()
|
|
||||||
|
|
||||||
# If this line is a macro definition, save the macro.
|
# If this line is a macro definition, save the macro.
|
||||||
if isinstance(e, tokens.macro_expression):
|
if isinstance(e, tokens.macro_expression):
|
||||||
was_rewritten = e.label in self.macro_table
|
self.save_macro(e, silent = silent)
|
||||||
self.macro_table[e.label] = e.exp
|
|
||||||
|
|
||||||
return rs.MacroStatus(
|
|
||||||
was_rewritten = was_rewritten,
|
|
||||||
macro_label = e.label,
|
|
||||||
macro_expr = e.exp
|
|
||||||
)
|
|
||||||
|
|
||||||
elif macro_only:
|
|
||||||
raise rs.NotAMacro()
|
|
||||||
|
|
||||||
# If this line is a command, do the command.
|
# If this line is a command, do the command.
|
||||||
elif isinstance(e, tokens.command):
|
elif isinstance(e, tokens.command):
|
||||||
commands.run(e, self)
|
commands.run(e, self)
|
||||||
return rs.CommandStatus(cmd = e.name)
|
|
||||||
|
|
||||||
# If this line is a plain expression, reduce it.
|
# If this line is a plain expression, reduce it.
|
||||||
elif isinstance(e, tokens.LambdaToken):
|
elif isinstance(e, tokens.LambdaToken):
|
||||||
return self.reduce_expression(e)
|
self.reduce_expression(e)
|
||||||
|
|
||||||
# We shouldn't ever get here.
|
# We shouldn't ever get here.
|
||||||
else:
|
else:
|
||||||
|
@ -115,4 +135,4 @@ class Runner:
|
||||||
|
|
||||||
def run_lines(self, lines: list[str]):
|
def run_lines(self, lines: list[str]):
|
||||||
for l in lines:
|
for l in lines:
|
||||||
self.run(l)
|
self.run(l, silent = True)
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
from prompt_toolkit.formatted_text import FormattedText
|
|
||||||
from prompt_toolkit.formatted_text import HTML
|
|
||||||
import enum
|
|
||||||
|
|
||||||
import lamb.tokens as tokens
|
|
||||||
|
|
||||||
|
|
||||||
class NotAMacro(Exception):
|
|
||||||
"""
|
|
||||||
Raised when we try to run a non-macro line
|
|
||||||
while enforcing macro_only in Runner.run().
|
|
||||||
|
|
||||||
This should be caught and elegantly presented to the user.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
class RunStatus:
|
|
||||||
"""
|
|
||||||
Base class for run status.
|
|
||||||
These are returned whenever the runner does something.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
class MacroStatus(RunStatus):
|
|
||||||
"""
|
|
||||||
Returned when a macro is defined.
|
|
||||||
|
|
||||||
Values:
|
|
||||||
`was_rewritten`: If true, an old macro was replaced.
|
|
||||||
`macro_label`: The name of the macro we just made.
|
|
||||||
`macro_expr`: The expr of the macro we just made.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
was_rewritten: bool,
|
|
||||||
macro_label: str,
|
|
||||||
macro_expr
|
|
||||||
):
|
|
||||||
self.was_rewritten = was_rewritten
|
|
||||||
self.macro_label = macro_label
|
|
||||||
self.macro_expr = macro_expr
|
|
||||||
|
|
||||||
|
|
||||||
class StopReason(enum.Enum):
|
|
||||||
BETA_NORMAL = ("class:text", "β-normal form")
|
|
||||||
LOOP_DETECTED = ("class:warn", "loop detected")
|
|
||||||
MAX_EXCEEDED = ("class:err", "too many reductions")
|
|
||||||
INTERRUPT = ("class:warn", "user interrupt")
|
|
||||||
|
|
||||||
|
|
||||||
class ReduceStatus(RunStatus):
|
|
||||||
"""
|
|
||||||
Returned when an expression is reduced.
|
|
||||||
|
|
||||||
Values:
|
|
||||||
`reduction_count`: How many reductions were made.
|
|
||||||
`stop_reason`: Why we stopped. See `StopReason`.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
reduction_count: int,
|
|
||||||
stop_reason: StopReason,
|
|
||||||
result: tokens.LambdaToken
|
|
||||||
):
|
|
||||||
self.reduction_count = reduction_count
|
|
||||||
self.stop_reason = stop_reason
|
|
||||||
self.result = result
|
|
||||||
|
|
||||||
|
|
||||||
class CommandStatus(RunStatus):
|
|
||||||
"""
|
|
||||||
Returned when a command is executed.
|
|
||||||
Doesn't do anything interesting.
|
|
||||||
|
|
||||||
Values:
|
|
||||||
`cmd`: The command that was run, without a colon.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, *, cmd: str):
|
|
||||||
self.cmd = cmd
|
|
|
@ -75,13 +75,27 @@ class church_num(LambdaToken):
|
||||||
return f"<{self.val}>"
|
return f"<{self.val}>"
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.val}"
|
return f"{self.val}"
|
||||||
|
|
||||||
|
def to_church(self):
|
||||||
|
"""
|
||||||
|
Return this number as an expanded church numeral.
|
||||||
|
"""
|
||||||
|
f = bound_variable("f", runner = self.runner)
|
||||||
|
a = bound_variable("a", runner = self.runner)
|
||||||
|
chain = a
|
||||||
|
|
||||||
|
for i in range(self.val):
|
||||||
|
chain = lambda_apply(f, chain)
|
||||||
|
|
||||||
|
return lambda_func(
|
||||||
|
f,
|
||||||
|
lambda_func(a, chain)
|
||||||
|
)
|
||||||
|
|
||||||
def reduce(self, *, force_substitute = False) -> ReductionStatus:
|
def reduce(self, *, force_substitute = False) -> ReductionStatus:
|
||||||
if force_substitute: # Only expand macros if we NEED to
|
if force_substitute: # Only expand macros if we NEED to
|
||||||
return ReductionStatus(
|
return ReductionStatus(
|
||||||
output = utils.autochurch(
|
output = self.to_church(),
|
||||||
self.runner,
|
|
||||||
self.val
|
|
||||||
),
|
|
||||||
was_reduced = True,
|
was_reduced = True,
|
||||||
reduction_type = ReductionType.AUTOCHURCH
|
reduction_type = ReductionType.AUTOCHURCH
|
||||||
)
|
)
|
||||||
|
@ -214,19 +228,19 @@ class macro_expression:
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_runner(self, runner):
|
def set_runner(self, runner):
|
||||||
self.exp.set_runner(runner)
|
self.expr.set_runner(runner)
|
||||||
def bind_variables(self):
|
def bind_variables(self):
|
||||||
self.exp.bind_variables()
|
self.expr.bind_variables()
|
||||||
|
|
||||||
def __init__(self, label: str, exp: LambdaToken):
|
def __init__(self, label: str, expr: LambdaToken):
|
||||||
self.label = label
|
self.label = label
|
||||||
self.exp = exp
|
self.expr = expr
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<{self.label} := {self.exp!r}>"
|
return f"<{self.label} := {self.expr!r}>"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.label} := {self.exp}"
|
return f"{self.label} := {self.expr}"
|
||||||
|
|
||||||
|
|
||||||
class bound_variable(LambdaToken):
|
class bound_variable(LambdaToken):
|
||||||
|
|
|
@ -3,30 +3,6 @@ from prompt_toolkit.formatted_text import HTML
|
||||||
from prompt_toolkit import print_formatted_text as printf
|
from prompt_toolkit import print_formatted_text as printf
|
||||||
from importlib.metadata import version
|
from importlib.metadata import version
|
||||||
|
|
||||||
import lamb.tokens as tokens
|
|
||||||
|
|
||||||
|
|
||||||
def autochurch(runner, num):
|
|
||||||
"""
|
|
||||||
Makes a church numeral from an integer.
|
|
||||||
"""
|
|
||||||
|
|
||||||
f = tokens.bound_variable("f", runner = runner)
|
|
||||||
a = tokens.bound_variable("a", runner = runner)
|
|
||||||
|
|
||||||
chain = a
|
|
||||||
|
|
||||||
for i in range(num):
|
|
||||||
chain = tokens.lambda_apply(f, chain)
|
|
||||||
|
|
||||||
return tokens.lambda_func(
|
|
||||||
f,
|
|
||||||
tokens.lambda_func(
|
|
||||||
a,
|
|
||||||
chain
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
style = Style.from_dict({ # type: ignore
|
style = Style.from_dict({ # type: ignore
|
||||||
# Basic formatting
|
# Basic formatting
|
||||||
|
|
Reference in New Issue