From 7d11ee1bb4f7074060154373d6910f26fe9c9ee3 Mon Sep 17 00:00:00 2001 From: Chris Gregory Date: Fri, 24 May 2024 15:57:30 -0700 Subject: [PATCH 1/2] Pretty printing for named closures --- scrapscript.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/scrapscript.py b/scrapscript.py index cb0466d5..75b54e08 100755 --- a/scrapscript.py +++ b/scrapscript.py @@ -938,6 +938,7 @@ def __str__(self) -> str: class Closure(Object): env: Env func: Union[Function, MatchFunction] + name: Optional[str] = None def serialize(self) -> Dict[str, object]: return self._serialize(env=serialize_env(self.env), func=self.func.serialize()) @@ -955,8 +956,11 @@ def deserialize(msg: Dict[str, object]) -> "Closure": return Closure(env, func) def __str__(self) -> str: - # TODO: Better pretty printing for Closure - return self.__repr__() + object_id = id(self) + object_address = hex(object_id) + if self.name is not None: + return f"" + return f"" @dataclass(eq=True, frozen=True, unsafe_hash=True) @@ -1208,7 +1212,7 @@ def free_in(exp: Object) -> Set[str]: def improve_closure(closure: Closure) -> Closure: freevars = free_in(closure.func) env = {boundvar: value for boundvar, value in closure.env.items() if boundvar in freevars} - return Closure(env, closure.func) + return Closure(env, closure.func, name=closure.name) def eval_exp(env: Env, exp: Object) -> Object: @@ -1243,6 +1247,10 @@ def eval_exp(env: Env, exp: Object) -> Object: # captured environment with a binding to themselves. assert isinstance(value.env, dict) value.env[exp.name.name] = value + # If creating a Closure with an Assign we can specify a name for the + # Closure, which may be useful for debugging. + print("Setting closure with name ", exp.name.name) + value = Closure(value.env, value.func, name=exp.name.name) # We still improve_closure here even though we also did it on # Closure creation because the Closure might not need a binding for # itself (it might not be recursive). From e957ff7f8566f34ca74776370be6a11e44f154a8 Mon Sep 17 00:00:00 2001 From: Chris Gregory Date: Fri, 24 May 2024 16:23:10 -0700 Subject: [PATCH 2/2] Fix tests --- scrapscript.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/scrapscript.py b/scrapscript.py index 75b54e08..b55792ee 100755 --- a/scrapscript.py +++ b/scrapscript.py @@ -956,11 +956,9 @@ def deserialize(msg: Dict[str, object]) -> "Closure": return Closure(env, func) def __str__(self) -> str: - object_id = id(self) - object_address = hex(object_id) if self.name is not None: - return f"" - return f"" + return f"" + return f"" @dataclass(eq=True, frozen=True, unsafe_hash=True) @@ -1241,16 +1239,15 @@ def eval_exp(env: Env, exp: Object) -> Object: assert isinstance(exp.name, Var) value = eval_exp(env, exp.value) if isinstance(value, Closure): + # If creating a Closure with an Assign we can specify a name for the + # Closure, which may be useful for debugging. + value = Closure(value.env, value.func, name=exp.name.name) # We want functions to be able to call themselves without using the # Y combinator or similar, so we bind functions (and only # functions) using a letrec-like strategy. We augment their # captured environment with a binding to themselves. assert isinstance(value.env, dict) value.env[exp.name.name] = value - # If creating a Closure with an Assign we can specify a name for the - # Closure, which may be useful for debugging. - print("Setting closure with name ", exp.name.name) - value = Closure(value.env, value.func, name=exp.name.name) # We still improve_closure here even though we also did it on # Closure creation because the Closure might not need a binding for # itself (it might not be recursive). @@ -2735,7 +2732,7 @@ def test_eval_assign_function_returns_closure_without_function_in_env(self) -> N assert isinstance(result, EnvObject) closure = result.env["a"] self.assertIsInstance(closure, Closure) - self.assertEqual(closure, Closure({}, Function(Var("x"), Var("x")))) + self.assertEqual(closure, Closure({}, Function(Var("x"), Var("x")), "a")) def test_eval_assign_function_returns_closure_with_function_in_env(self) -> None: exp = Assign(Var("a"), Function(Var("x"), Var("a"))) @@ -2743,7 +2740,7 @@ def test_eval_assign_function_returns_closure_with_function_in_env(self) -> None assert isinstance(result, EnvObject) closure = result.env["a"] self.assertIsInstance(closure, Closure) - self.assertEqual(closure, Closure({"a": closure}, Function(Var("x"), Var("a")))) + self.assertEqual(closure, Closure({"a": closure}, Function(Var("x"), Var("a")), "a")) def test_eval_assign_does_not_modify_env(self) -> None: exp = Assign(Var("a"), Int(1)) @@ -3392,7 +3389,7 @@ def test_function_can_reference_itself(self) -> None: """, {}, ) - self.assertEqual(result, Closure({"f": result}, Function(Var("n"), Var("f")))) + self.assertEqual(result, Closure({"f": result}, Function(Var("n"), Var("f")), "f")) def test_function_can_call_itself(self) -> None: with self.assertRaises(RecursionError): @@ -4352,9 +4349,7 @@ def test_pretty_print_nativefunction(self) -> None: def test_pretty_print_closure(self) -> None: obj = Closure({"a": Int(123)}, Function(Var("x"), Var("x"))) - self.assertEqual( - str(obj), "Closure(env={'a': Int(value=123)}, func=Function(arg=Var(name='x'), body=Var(name='x')))" - ) + self.assertRegex(str(obj), r"") def test_pretty_print_record(self) -> None: obj = Record({"a": Int(1), "b": Int(2)})