Cleaned up parser
parent
3cd0762d16
commit
218af2cd49
|
@ -7,6 +7,7 @@
|
||||||
"Packrat",
|
"Packrat",
|
||||||
"pyparsing",
|
"pyparsing",
|
||||||
"runstatus",
|
"runstatus",
|
||||||
|
"srange",
|
||||||
"subvar"
|
"subvar"
|
||||||
],
|
],
|
||||||
"python.analysis.typeCheckingMode": "basic"
|
"python.analysis.typeCheckingMode": "basic"
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
- Command and macro autocomplete
|
- Command and macro autocomplete
|
||||||
- step-by-step reduction
|
- step-by-step reduction
|
||||||
- Maybe a better icon?
|
- Maybe a better icon?
|
||||||
- Warn when overwriting macro
|
|
||||||
- Syntax highlighting: parenthesis, bound variables, macros, etc
|
- Syntax highlighting: parenthesis, bound variables, macros, etc
|
||||||
- Pin header to top of screen
|
- Pin header to top of screen
|
||||||
- PyPi package
|
- PyPi package
|
||||||
|
|
|
@ -52,13 +52,11 @@ r.run_lines([
|
||||||
"AND = λab.(a F b)",
|
"AND = λab.(a F b)",
|
||||||
"OR = λab.(a T b)",
|
"OR = λab.(a T b)",
|
||||||
"XOR = λab.(a (NOT a b) b)",
|
"XOR = λab.(a (NOT a b) b)",
|
||||||
"w = λx.(x x)",
|
"M = λx.(x x)",
|
||||||
"W = w w",
|
"W = M M",
|
||||||
"Y = λf.( (λx.(f (x x))) (λx.(f (x x))) )",
|
"Y = λf.( (λx.(f (x x))) (λx.(f (x x))) )",
|
||||||
"PAIR = λabi.( i a b )",
|
"PAIR = λabi.( i a b )",
|
||||||
"inc = λnfa.(f (n f a))",
|
"S = λnfa.(f (n f a))",
|
||||||
"zero = λax.x",
|
|
||||||
"one = λfx.(f x)"
|
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,7 +87,7 @@ while True:
|
||||||
("class:err", " "*(e.loc + l) + "^\n"),
|
("class:err", " "*(e.loc + l) + "^\n"),
|
||||||
("class:err", f"Syntax error at char {e.loc}."),
|
("class:err", f"Syntax error at char {e.loc}."),
|
||||||
("class:text", "\n")
|
("class:text", "\n")
|
||||||
]))
|
]), style = utils.style)
|
||||||
continue
|
continue
|
||||||
except tokens.ReductionError as e:
|
except tokens.ReductionError as e:
|
||||||
printf(FormattedText([
|
printf(FormattedText([
|
||||||
|
|
144
lamb/parser.py
144
lamb/parser.py
|
@ -1,82 +1,96 @@
|
||||||
import pyparsing as pp
|
import pyparsing as pp
|
||||||
|
|
||||||
# Packrat gives a MAD speed boost.
|
# Packrat gives a significant speed boost.
|
||||||
pp.ParserElement.enablePackrat()
|
pp.ParserElement.enablePackrat()
|
||||||
|
|
||||||
import lamb.tokens as tokens
|
class LambdaParser:
|
||||||
import lamb.utils as utils
|
def make_parser(self):
|
||||||
|
self.lp = pp.Suppress("(")
|
||||||
|
self.rp = pp.Suppress(")")
|
||||||
|
self.pp_expr = pp.Forward()
|
||||||
|
|
||||||
class Parser:
|
# Bound variables are ALWAYS lowercase and single-character.
|
||||||
lp = pp.Suppress("(")
|
# We still create macro objects from them, they are turned into
|
||||||
rp = pp.Suppress(")")
|
# bound variables after the expression is built.
|
||||||
|
self.pp_macro = pp.Word(pp.alphas + "_")
|
||||||
|
self.pp_bound = pp.Char(pp.srange("[a-z]"))
|
||||||
|
self.pp_name = self.pp_bound ^ self.pp_macro
|
||||||
|
self.pp_church = pp.Word(pp.nums)
|
||||||
|
|
||||||
# Simple tokens
|
# Function calls.
|
||||||
pp_expr = pp.Forward()
|
#
|
||||||
pp_macro = pp.Word(pp.alphas + "_")
|
# <exp> <exp>
|
||||||
pp_macro.set_parse_action(tokens.macro.from_parse)
|
# <exp> <exp> <exp>
|
||||||
|
self.pp_call = pp.Forward()
|
||||||
|
self.pp_call <<= (self.pp_expr | self.pp_bound)[2, ...]
|
||||||
|
|
||||||
pp_church = pp.Word(pp.nums)
|
# Function definitions, right associative.
|
||||||
pp_church.set_parse_action(utils.autochurch)
|
# Function args MUST be lowercase.
|
||||||
|
#
|
||||||
|
# <var> => <exp>
|
||||||
|
self.pp_lambda_fun = (
|
||||||
|
(pp.Suppress("λ") | pp.Suppress("\\")) +
|
||||||
|
pp.Group(self.pp_bound[1, ...]) +
|
||||||
|
pp.Suppress(".") +
|
||||||
|
(self.pp_expr ^ self.pp_call)
|
||||||
|
)
|
||||||
|
|
||||||
# Function calls.
|
# Assignment.
|
||||||
# `tokens.lambda_apply.from_parse` handles chained calls.
|
# Can only be found at the start of a line.
|
||||||
#
|
#
|
||||||
# <exp> <exp>
|
# <name> = <exp>
|
||||||
# <exp> <exp> <exp>
|
self.pp_macro_def = (
|
||||||
pp_call = pp.Forward()
|
pp.line_start() +
|
||||||
pp_call <<= pp_expr[2, ...]
|
self.pp_macro +
|
||||||
pp_call.set_parse_action(tokens.lambda_apply.from_parse)
|
pp.Suppress("=") +
|
||||||
|
(self.pp_expr ^ self.pp_call)
|
||||||
|
)
|
||||||
|
|
||||||
# Function definitions.
|
self.pp_expr <<= (
|
||||||
# Right associative.
|
self.pp_church ^
|
||||||
#
|
self.pp_lambda_fun ^
|
||||||
# <var> => <exp>
|
self.pp_name ^
|
||||||
pp_lambda_fun = (
|
(self.lp + self.pp_expr + self.rp) ^
|
||||||
(pp.Suppress("λ") | pp.Suppress("\\")) +
|
(self.lp + self.pp_call + self.rp)
|
||||||
pp.Group(pp.Char(pp.alphas)[1, ...]) +
|
)
|
||||||
pp.Suppress(".") +
|
|
||||||
(pp_expr ^ pp_call)
|
|
||||||
)
|
|
||||||
pp_lambda_fun.set_parse_action(tokens.lambda_func.from_parse)
|
|
||||||
|
|
||||||
# Assignment.
|
self.pp_command = pp.Suppress(":") + pp.Word(pp.alphas + "_") + pp.Word(pp.alphas + "_")[0, ...]
|
||||||
# Can only be found at the start of a line.
|
|
||||||
#
|
|
||||||
# <name> = <exp>
|
|
||||||
pp_macro_def = (
|
|
||||||
pp.line_start() +
|
|
||||||
pp_macro +
|
|
||||||
pp.Suppress("=") +
|
|
||||||
(pp_expr ^ pp_call)
|
|
||||||
)
|
|
||||||
pp_macro_def.set_parse_action(tokens.macro_expression.from_parse)
|
|
||||||
|
|
||||||
pp_expr <<= (
|
|
||||||
pp_church ^
|
|
||||||
pp_lambda_fun ^
|
|
||||||
pp_macro ^
|
|
||||||
(lp + pp_expr + rp) ^
|
|
||||||
(lp + pp_call + rp)
|
|
||||||
)
|
|
||||||
|
|
||||||
pp_command = pp.Suppress(":") + pp.Word(pp.alphas + "_") + pp.Word(pp.alphas + "_")[0, ...]
|
|
||||||
pp_command.set_parse_action(tokens.command.from_parse)
|
|
||||||
|
|
||||||
|
|
||||||
pp_all = (
|
self.pp_all = (
|
||||||
pp_expr ^
|
self.pp_expr ^
|
||||||
pp_macro_def ^
|
self.pp_macro_def ^
|
||||||
pp_command ^
|
self.pp_command ^
|
||||||
pp_call
|
self.pp_call
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
def __init__(
|
||||||
def parse_line(line):
|
self,
|
||||||
return Parser.pp_all.parse_string(
|
*,
|
||||||
|
action_command,
|
||||||
|
action_macro_def,
|
||||||
|
action_church,
|
||||||
|
action_func,
|
||||||
|
action_bound,
|
||||||
|
action_macro,
|
||||||
|
action_apply
|
||||||
|
):
|
||||||
|
|
||||||
|
self.make_parser()
|
||||||
|
|
||||||
|
self.pp_command.set_parse_action(action_command)
|
||||||
|
self.pp_macro_def.set_parse_action(action_macro_def)
|
||||||
|
self.pp_church.set_parse_action(action_church)
|
||||||
|
self.pp_lambda_fun.set_parse_action(action_func)
|
||||||
|
self.pp_macro.set_parse_action(action_macro)
|
||||||
|
self.pp_bound.set_parse_action(action_bound)
|
||||||
|
self.pp_call.set_parse_action(action_apply)
|
||||||
|
|
||||||
|
def parse_line(self, line: str):
|
||||||
|
return self.pp_all.parse_string(
|
||||||
line,
|
line,
|
||||||
parse_all = True
|
parse_all = True
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
@staticmethod
|
def run_tests(self, lines: list[str]):
|
||||||
def run_tests(lines):
|
return self.pp_all.run_tests(lines)
|
||||||
return Parser.pp_all.run_tests(lines)
|
|
|
@ -1,8 +1,9 @@
|
||||||
from prompt_toolkit import PromptSession
|
from prompt_toolkit import PromptSession
|
||||||
|
|
||||||
import lamb.commands as commands
|
import lamb.commands as commands
|
||||||
from lamb.parser import Parser
|
from lamb.parser import LambdaParser
|
||||||
import lamb.tokens as tokens
|
import lamb.tokens as tokens
|
||||||
|
import lamb.utils as utils
|
||||||
import lamb.runstatus as rs
|
import lamb.runstatus as rs
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,11 +12,25 @@ class Runner:
|
||||||
self.macro_table = {}
|
self.macro_table = {}
|
||||||
self.prompt_session = prompt_session
|
self.prompt_session = prompt_session
|
||||||
self.prompt_message = prompt_message
|
self.prompt_message = prompt_message
|
||||||
|
self.parser = LambdaParser(
|
||||||
|
action_command = tokens.command.from_parse,
|
||||||
|
action_macro_def = tokens.macro_expression.from_parse,
|
||||||
|
action_church = utils.autochurch(self),
|
||||||
|
action_func = tokens.lambda_func.from_parse,
|
||||||
|
action_bound = tokens.macro.from_parse,
|
||||||
|
action_macro = tokens.macro.from_parse,
|
||||||
|
action_apply = tokens.lambda_apply.from_parse
|
||||||
|
)
|
||||||
|
|
||||||
# Maximum amount of reductions.
|
# Maximum amount of reductions.
|
||||||
# If None, no maximum is enforced.
|
# If None, no maximum is enforced.
|
||||||
self.reduction_limit: int | None = 300
|
self.reduction_limit: int | None = 300
|
||||||
|
|
||||||
|
# Ensure bound variables are unique.
|
||||||
|
# This is automatically incremented whenever we make
|
||||||
|
# a bound variable.
|
||||||
|
self.bound_variable_counter = 0
|
||||||
|
|
||||||
def prompt(self):
|
def prompt(self):
|
||||||
return self.prompt_session.prompt(message = self.prompt_message)
|
return self.prompt_session.prompt(message = self.prompt_message)
|
||||||
|
|
||||||
|
@ -29,7 +44,8 @@ class Runner:
|
||||||
macro_expansions = 0
|
macro_expansions = 0
|
||||||
|
|
||||||
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(self.macro_table)
|
print(repr(expr))
|
||||||
|
r = expr.reduce()
|
||||||
expr = r.output
|
expr = r.output
|
||||||
|
|
||||||
# If we can't reduce this expression anymore,
|
# If we can't reduce this expression anymore,
|
||||||
|
@ -55,13 +71,15 @@ class Runner:
|
||||||
|
|
||||||
# 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, *, macro_only = False) -> rs.RunStatus:
|
||||||
e = Parser.parse_line(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()
|
||||||
|
|
||||||
# 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
|
was_rewritten = e.label in self.macro_table
|
||||||
|
|
||||||
e.exp.bind_variables()
|
|
||||||
self.macro_table[e.label] = e.exp
|
self.macro_table[e.label] = e.exp
|
||||||
|
|
||||||
return rs.MacroStatus(
|
return rs.MacroStatus(
|
||||||
|
@ -80,7 +98,6 @@ class Runner:
|
||||||
|
|
||||||
# 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):
|
||||||
e.bind_variables()
|
|
||||||
return self.reduce_expression(e)
|
return self.reduce_expression(e)
|
||||||
|
|
||||||
# We shouldn't ever get here.
|
# We shouldn't ever get here.
|
||||||
|
|
|
@ -40,16 +40,18 @@ class ReductionStatus:
|
||||||
# this will be false.
|
# this will be false.
|
||||||
self.was_reduced = was_reduced
|
self.was_reduced = was_reduced
|
||||||
|
|
||||||
|
|
||||||
class LambdaToken:
|
class LambdaToken:
|
||||||
"""
|
"""
|
||||||
Base class for all lambda tokens.
|
Base class for all lambda tokens.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def set_runner(self, runner):
|
||||||
|
self.runner = runner
|
||||||
|
|
||||||
def bind_variables(self) -> None:
|
def bind_variables(self) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def reduce(self, macro_table) -> ReductionStatus:
|
def reduce(self) -> ReductionStatus:
|
||||||
return ReductionStatus(
|
return ReductionStatus(
|
||||||
was_reduced = False,
|
was_reduced = False,
|
||||||
output = self
|
output = self
|
||||||
|
@ -121,7 +123,6 @@ class macro(LambdaToken):
|
||||||
|
|
||||||
def reduce(
|
def reduce(
|
||||||
self,
|
self,
|
||||||
macro_table = {},
|
|
||||||
*,
|
*,
|
||||||
# To keep output readable, we avoid expanding macros as often as possible.
|
# To keep output readable, we avoid expanding macros as often as possible.
|
||||||
# Macros are irreducible if force_substitute is false.
|
# Macros are irreducible if force_substitute is false.
|
||||||
|
@ -132,10 +133,10 @@ class macro(LambdaToken):
|
||||||
auto_free_vars = True
|
auto_free_vars = True
|
||||||
) -> ReductionStatus:
|
) -> ReductionStatus:
|
||||||
|
|
||||||
if (self.name in macro_table) and force_substitute:
|
if (self.name in self.runner.macro_table) and force_substitute:
|
||||||
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 = macro_table[self.name],
|
output = self.runner.macro_table[self.name],
|
||||||
reduction_type = ReductionType.MACRO_EXPAND,
|
reduction_type = ReductionType.MACRO_EXPAND,
|
||||||
was_reduced = True
|
was_reduced = True
|
||||||
)
|
)
|
||||||
|
@ -173,6 +174,11 @@ class macro_expression:
|
||||||
result[1]
|
result[1]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def set_runner(self, runner):
|
||||||
|
self.exp.set_runner(runner)
|
||||||
|
def bind_variables(self):
|
||||||
|
self.exp.bind_variables()
|
||||||
|
|
||||||
def __init__(self, label: str, exp: LambdaToken):
|
def __init__(self, label: str, exp: LambdaToken):
|
||||||
self.label = label
|
self.label = label
|
||||||
self.exp = exp
|
self.exp = exp
|
||||||
|
@ -184,14 +190,14 @@ class macro_expression:
|
||||||
return f"{self.label} := {self.exp}"
|
return f"{self.label} := {self.exp}"
|
||||||
|
|
||||||
|
|
||||||
bound_variable_counter = 0
|
|
||||||
class bound_variable(LambdaToken):
|
class bound_variable(LambdaToken):
|
||||||
def __init__(self, forced_id = None):
|
def __init__(self, name: str, *, runner, forced_id = None):
|
||||||
global bound_variable_counter
|
self.original_name = name
|
||||||
|
self.runner = runner
|
||||||
|
|
||||||
if forced_id is None:
|
if forced_id is None:
|
||||||
self.identifier = bound_variable_counter
|
self.identifier = self.runner.bound_variable_counter
|
||||||
bound_variable_counter += 1
|
self.runner.bound_variable_counter += 1
|
||||||
else:
|
else:
|
||||||
self.identifier = forced_id
|
self.identifier = forced_id
|
||||||
|
|
||||||
|
@ -201,7 +207,7 @@ class bound_variable(LambdaToken):
|
||||||
return self.identifier == other.identifier
|
return self.identifier == other.identifier
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<in {self.identifier}>"
|
return f"<{self.original_name} {self.identifier}>"
|
||||||
|
|
||||||
class lambda_func(LambdaToken):
|
class lambda_func(LambdaToken):
|
||||||
"""
|
"""
|
||||||
|
@ -219,15 +225,20 @@ class lambda_func(LambdaToken):
|
||||||
def from_parse(result):
|
def from_parse(result):
|
||||||
if len(result[0]) == 1:
|
if len(result[0]) == 1:
|
||||||
return lambda_func(
|
return lambda_func(
|
||||||
macro(result[0][0]),
|
result[0][0],
|
||||||
result[1]
|
result[1]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return lambda_func(
|
return lambda_func(
|
||||||
macro(result[0].pop(0)),
|
result[0].pop(0),
|
||||||
lambda_func.from_parse(result)
|
lambda_func.from_parse(result)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def set_runner(self, runner):
|
||||||
|
self.runner = runner
|
||||||
|
self.input.set_runner(runner)
|
||||||
|
self.output.set_runner(runner)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
input_var: macro | bound_variable,
|
input_var: macro | bound_variable,
|
||||||
|
@ -280,10 +291,14 @@ class lambda_func(LambdaToken):
|
||||||
# We only need to check for collisions if we're
|
# We only need to check for collisions if we're
|
||||||
# binding another function's variable. If this
|
# binding another function's variable. If this
|
||||||
# function starts the bind chain, skip that step.
|
# function starts the bind chain, skip that step.
|
||||||
if not ((placeholder is None) and (val is None)):
|
if placeholder is not None:
|
||||||
if not binding_self and isinstance(self.input, macro):
|
if not binding_self and isinstance(self.input, macro):
|
||||||
if self.input == placeholder:
|
if self.input == placeholder:
|
||||||
raise ReductionError(f"Variable name conflict: \"{self.input.name}\"")
|
raise ReductionError(f"Bound variable name conflict: \"{self.input.name}\"")
|
||||||
|
|
||||||
|
if self.input.name in self.runner.macro_table:
|
||||||
|
raise ReductionError(f"Bound variable name conflict: \"{self.input.name}\" is a macro")
|
||||||
|
|
||||||
|
|
||||||
# If this function's variables haven't been bound yet,
|
# If this function's variables haven't been bound yet,
|
||||||
# bind them BEFORE binding the outer function's.
|
# bind them BEFORE binding the outer function's.
|
||||||
|
@ -292,7 +307,10 @@ class lambda_func(LambdaToken):
|
||||||
# functions' variables, we won't be able to detect
|
# functions' variables, we won't be able to detect
|
||||||
# name conflicts.
|
# name conflicts.
|
||||||
if isinstance(self.input, macro) and not binding_self:
|
if isinstance(self.input, macro) and not binding_self:
|
||||||
new_bound_var = bound_variable()
|
new_bound_var = bound_variable(
|
||||||
|
self.input.name,
|
||||||
|
runner = self.runner
|
||||||
|
)
|
||||||
self.bind_variables(
|
self.bind_variables(
|
||||||
self.input,
|
self.input,
|
||||||
new_bound_var,
|
new_bound_var,
|
||||||
|
@ -310,15 +328,15 @@ class lambda_func(LambdaToken):
|
||||||
elif isinstance(self.output, lambda_apply):
|
elif isinstance(self.output, lambda_apply):
|
||||||
self.output.bind_variables(placeholder, val)
|
self.output.bind_variables(placeholder, val)
|
||||||
|
|
||||||
def reduce(self, macro_table = {}) -> ReductionStatus:
|
def reduce(self) -> ReductionStatus:
|
||||||
|
|
||||||
r = self.output.reduce(macro_table)
|
r = self.output.reduce()
|
||||||
|
|
||||||
# If a macro becomes a free variable,
|
# If a macro becomes a free variable,
|
||||||
# reduce twice.
|
# reduce twice.
|
||||||
if r.reduction_type == ReductionType.MACRO_TO_FREE:
|
if r.reduction_type == ReductionType.MACRO_TO_FREE:
|
||||||
self.output = r.output
|
self.output = r.output
|
||||||
return self.reduce(macro_table)
|
return self.reduce()
|
||||||
|
|
||||||
return ReductionStatus(
|
return ReductionStatus(
|
||||||
was_reduced = r.was_reduced,
|
was_reduced = r.was_reduced,
|
||||||
|
@ -394,6 +412,11 @@ class lambda_apply(LambdaToken):
|
||||||
)] + result[2:]
|
)] + result[2:]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def set_runner(self, runner):
|
||||||
|
self.runner = runner
|
||||||
|
self.fn.set_runner(runner)
|
||||||
|
self.arg.set_runner(runner)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
fn: LambdaToken,
|
fn: LambdaToken,
|
||||||
|
@ -416,9 +439,6 @@ class lambda_apply(LambdaToken):
|
||||||
"""
|
"""
|
||||||
Does exactly what lambda_func.bind_variables does,
|
Does exactly what lambda_func.bind_variables does,
|
||||||
but acts on applications instead.
|
but acts on applications instead.
|
||||||
|
|
||||||
There will be little documentation in this method,
|
|
||||||
see lambda_func.bind_variables.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if (placeholder is None) and (val != placeholder):
|
if (placeholder is None) and (val != placeholder):
|
||||||
|
@ -474,7 +494,7 @@ class lambda_apply(LambdaToken):
|
||||||
new_arg
|
new_arg
|
||||||
)
|
)
|
||||||
|
|
||||||
def reduce(self, macro_table = {}) -> ReductionStatus:
|
def reduce(self) -> ReductionStatus:
|
||||||
|
|
||||||
# If we can directly apply self.fn, do so.
|
# If we can directly apply self.fn, do so.
|
||||||
if isinstance(self.fn, lambda_func):
|
if isinstance(self.fn, lambda_func):
|
||||||
|
@ -491,17 +511,16 @@ class lambda_apply(LambdaToken):
|
||||||
# Macros must be reduced before we apply them as functions.
|
# Macros must be reduced before we apply them as functions.
|
||||||
# This is the only place we force substitution.
|
# This is the only place we force substitution.
|
||||||
r = self.fn.reduce(
|
r = self.fn.reduce(
|
||||||
macro_table,
|
|
||||||
force_substitute = True
|
force_substitute = True
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
r = self.fn.reduce(macro_table)
|
r = self.fn.reduce()
|
||||||
|
|
||||||
# If a macro becomes a free variable,
|
# If a macro becomes a free variable,
|
||||||
# reduce twice.
|
# reduce twice.
|
||||||
if r.reduction_type == ReductionType.MACRO_TO_FREE:
|
if r.reduction_type == ReductionType.MACRO_TO_FREE:
|
||||||
self.fn = r.output
|
self.fn = r.output
|
||||||
return self.reduce(macro_table)
|
return self.reduce()
|
||||||
|
|
||||||
if r.was_reduced:
|
if r.was_reduced:
|
||||||
return ReductionStatus(
|
return ReductionStatus(
|
||||||
|
@ -514,11 +533,11 @@ class lambda_apply(LambdaToken):
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
r = self.arg.reduce(macro_table)
|
r = self.arg.reduce()
|
||||||
|
|
||||||
if r.reduction_type == ReductionType.MACRO_TO_FREE:
|
if r.reduction_type == ReductionType.MACRO_TO_FREE:
|
||||||
self.arg = r.output
|
self.arg = r.output
|
||||||
return self.reduce(macro_table)
|
return self.reduce()
|
||||||
|
|
||||||
return ReductionStatus(
|
return ReductionStatus(
|
||||||
was_reduced = r.was_reduced,
|
was_reduced = r.was_reduced,
|
||||||
|
|
|
@ -6,31 +6,33 @@ from importlib.metadata import version
|
||||||
import lamb.tokens as tokens
|
import lamb.tokens as tokens
|
||||||
|
|
||||||
|
|
||||||
def autochurch(results):
|
def autochurch(runner):
|
||||||
"""
|
"""
|
||||||
Makes a church numeral from an integer.
|
Makes a church numeral from an integer.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
num = int(results[0])
|
def inner(results):
|
||||||
|
num = int(results[0])
|
||||||
|
|
||||||
f = tokens.bound_variable()
|
f = tokens.bound_variable("f", runner = runner)
|
||||||
a = tokens.bound_variable()
|
a = tokens.bound_variable("a", runner = runner)
|
||||||
|
|
||||||
chain = a
|
chain = a
|
||||||
|
|
||||||
for i in range(num):
|
for i in range(num):
|
||||||
chain = tokens.lambda_apply(f, chain)
|
chain = tokens.lambda_apply(f, chain)
|
||||||
|
|
||||||
return tokens.lambda_func(
|
return tokens.lambda_func(
|
||||||
f,
|
f,
|
||||||
tokens.lambda_func(
|
tokens.lambda_func(
|
||||||
a,
|
a,
|
||||||
chain
|
chain
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
return inner
|
||||||
|
|
||||||
|
|
||||||
style = Style.from_dict({
|
style = Style.from_dict({ # type: ignore
|
||||||
# Basic formatting
|
# Basic formatting
|
||||||
"text": "#FFFFFF",
|
"text": "#FFFFFF",
|
||||||
"warn": "#FFFF00",
|
"warn": "#FFFF00",
|
||||||
|
|
Reference in New Issue