diff --git a/README.md b/README.md index e020b90..9cbd5bb 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ - Smart alignment in all printouts - Full-reduce option - Set reduction limit command + - Runtime limit ## Mention in Docs - lambda functions only work with single-letter arguments diff --git a/lamb/__main__.py b/lamb/__main__.py index 6a1f6d9..cb8ac28 100755 --- a/lamb/__main__.py +++ b/lamb/__main__.py @@ -8,6 +8,7 @@ from prompt_toolkit.lexers import Lexer from pyparsing import exceptions as ppx +import lamb.parser import lamb.runner as runner import lamb.tokens as tokens import lamb.utils as utils @@ -32,6 +33,7 @@ def _(event): event.current_buffer.insert_text("λ") +""" r = runner.Runner( prompt_session = PromptSession( style = utils.style, @@ -43,10 +45,178 @@ r = runner.Runner( ("class:prompt", "~~> ") ]), ) +""" -r.run_lines([ - "T = λab.a", - "F = λab.b", +class Node: + def __init__(self): + # The node this one is connected to. + # None if this is the top node. + self.parent: Node | None = None + + # True if we're connected to the left side + # of the parent, False otherwise. + self.parent_left: bool | None = None + + # Left and right nodes, None if empty + self.left: Node | None = None + self.right: Node | None = None + + def set_parent(self, parent, is_left): + self.parent = parent + self.parent_left = is_left + + def go_left(self): + if self.left is None: + raise Exception("Can't go left when left is None") + return None, self.left + + def go_right(self): + if self.right is None: + raise Exception("Can't go right when right is None") + return None, self.right + + def go_up(self): + if self.parent is None: + raise Exception("Can't go up when parent is None") + return self.parent_left, self.parent + +def to_node(result_pair) -> Node: + return result_pair[0].from_parse(result_pair[1]) + + +class Macro(Node): + @staticmethod + def from_parse(results): + return Macro(results[0]) + + def __init__(self, name: str) -> None: + super().__init__() + self.name = name + self.left = None + self.right = None + + def __repr__(self): + return f"" + +class Func(Node): + @staticmethod + def from_parse(result): + if len(result[0]) == 1: + i = to_node(result[0][0]) + below = to_node(result[1]) + this = Func(i, below) # type: ignore + + below.set_parent(this, True) + return this + else: + i = to_node(result[0].pop(0)) + below = Func.from_parse(result) + this = Func(i, below) # type: ignore + + below.set_parent(this, True) + return this + + def __init__(self, input: Macro, output: Node) -> None: + super().__init__() + self.input = input + self.left = output + self.right = None + + def __repr__(self): + return f"" + +class Call(Node): + @staticmethod + def from_parse(results): + if len(results) == 2: + left = results[0] + if not isinstance(left, Node): + left = to_node(left) + + right = to_node(results[1]) + this = Call(left, right) + + left.set_parent(this, True) + right.set_parent(this, False) + return this + else: + left = results[0] + if not isinstance(left, Node): + left = to_node(left) + + right = to_node(results[1]) + this = Call(left, right) + + left.set_parent(this, True) + right.set_parent(this, False) + return Call.from_parse( + [this] + results[2:] + ) + + def __init__(self, fn: Node, arg: Node) -> None: + super().__init__() + self.left = fn + self.right = arg + + def __repr__(self): + return f"" + +p = lamb.parser.LambdaParser( + action_func = lambda x: (Func, x), + action_bound = lambda x: (Macro, x), + action_macro = lambda x: (Macro, x), + action_call = lambda x: (Call, x) +) + + +def traverse(node: Node): + ptr = node + back_from_left = None + + out = "" + + while True: + if isinstance(ptr, Macro): + out += ptr.name + back_from_left, ptr = ptr.go_up() + if isinstance(ptr, Func): + if back_from_left is None: + if isinstance(ptr.parent, Func): + out += ptr.input.name + else: + out += "λ" + ptr.input.name + if not isinstance(ptr.left, Func): + out += "." + back_from_left, ptr = ptr.go_left() + elif back_from_left is True: + back_from_left, ptr = ptr.go_up() + if isinstance(ptr, Call): + if back_from_left is None: + out += "(" + back_from_left, ptr = ptr.go_left() + elif back_from_left is True: + out += " " + back_from_left, ptr = ptr.go_right() + elif back_from_left is False: + out += ")" + back_from_left, ptr = ptr.go_up() + + if ptr.parent is None: + break + return out + +for l in [ + "λab.(a (NOT a b) b)", + "λnmf.n (m f)", + "λf.(λx. f(x x))(λx.f(x x))", +]: + i = p.parse_line(l) + #print(i) + n = to_node(i) + #print(n) + print(traverse(n)) + +""" "NOT = λa.(a F T)", "AND = λab.(a F b)", "OR = λab.(a T b)", @@ -59,44 +229,4 @@ r.run_lines([ "Z = λn.n (λa.F) T", "MULT = λnmf.n (m f)", "H = λp.((PAIR (p F)) (S (p F)))", - "D = λn.n H (PAIR 0 0) T", - "FAC = λyn.(Z n)(1)(MULT n (y (D n)))" -]) - - -while True: - try: - i = r.prompt() - - # Catch Ctrl-C and Ctrl-D - except KeyboardInterrupt: - printf("\n\nGoodbye.\n") - break - except EOFError: - printf("\n\nGoodbye.\n") - break - - # Skip empty lines - if i.strip() == "": - continue - - - # Try to run an input line. - # Catch parse errors and point them out. - try: - x = r.run(i) - except ppx.ParseException as e: - l = len(to_plain_text(r.prompt_session.message)) - printf(FormattedText([ - ("class:err", " "*(e.loc + l) + "^\n"), - ("class:err", f"Syntax error at char {e.loc}."), - ("class:text", "\n") - ]), style = utils.style) - continue - except tokens.ReductionError as e: - printf(FormattedText([ - ("class:err", f"{e.msg}\n") - ]), style = utils.style) - continue - - printf("") +])""" diff --git a/lamb/parser.py b/lamb/parser.py index 1442a1c..8cf409d 100755 --- a/lamb/parser.py +++ b/lamb/parser.py @@ -47,7 +47,7 @@ class LambdaParser: ) self.pp_expr <<= ( - self.pp_church ^ + #self.pp_church ^ self.pp_lambda_fun ^ self.pp_name ^ (self.lp + self.pp_expr + self.rp) ^ @@ -60,31 +60,31 @@ class LambdaParser: self.pp_all = ( self.pp_expr ^ self.pp_macro_def ^ - self.pp_command ^ + #self.pp_command ^ self.pp_call ) def __init__( self, *, - action_command, - action_macro_def, - action_church, + #action_command, + #action_macro_def, + #action_church, action_func, action_bound, action_macro, - action_apply + action_call ): 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_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) + self.pp_call.set_parse_action(action_call) def parse_line(self, line: str): return self.pp_all.parse_string(