diff --git a/.gitignore b/.gitignore index 670b156..55d22e5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,3 @@ # Output /dist /venv - -pantograph/pantograph -pantograph/lean-toolchain diff --git a/build.py b/build.py index 959c1ad..b116e42 100644 --- a/build.py +++ b/build.py @@ -6,10 +6,10 @@ from pathlib import Path PATH_PANTOGRAPH = Path("./src") PATH_PY = Path("./pantograph") -with subprocess.Popen(["make"], cwd=PATH_PANTOGRAPH) as p: +with subprocess.Popen(["lake", "build", "repl"], cwd=PATH_PANTOGRAPH) as p: p.wait() -path_executable = PATH_PY / "pantograph" -shutil.copyfile(PATH_PANTOGRAPH / ".lake/build/bin/pantograph", path_executable) +path_executable = PATH_PY / "pantograph-repl" +shutil.copyfile(PATH_PANTOGRAPH / ".lake/build/bin/repl", path_executable) os.chmod(path_executable, os.stat(path_executable).st_mode | stat.S_IEXEC) shutil.copyfile(PATH_PANTOGRAPH / "lean-toolchain", PATH_PY / "lean-toolchain") diff --git a/pantograph/.gitignore b/pantograph/.gitignore new file mode 100644 index 0000000..06b89c3 --- /dev/null +++ b/pantograph/.gitignore @@ -0,0 +1,2 @@ +pantograph-repl +lean-toolchain diff --git a/pantograph/server.py b/pantograph/server.py index a12d1eb..28268bc 100644 --- a/pantograph/server.py +++ b/pantograph/server.py @@ -10,7 +10,7 @@ from pantograph.compiler import TacticInvocation def _get_proc_cwd(): return pathlib.Path(__file__).parent def _get_proc_path(): - return _get_proc_cwd() / "pantograph" + return _get_proc_cwd() / "pantograph-repl" class ServerError(Exception): pass @@ -21,7 +21,10 @@ class Server: imports=["Init"], project_path=None, lean_path=None, - options=[], + # Options for executing the REPL. + # Set `{ "automaticMode" : True }` to get a gym-like experience + options={}, + core_options=[], timeout=20, maxread=1000000): """ @@ -36,7 +39,8 @@ class Server: self.proc_path = _get_proc_path() self.options = options - self.args = " ".join(imports + [f'--{opt}' for opt in options]) + self.core_options = core_options + self.args = " ".join(imports + [f'--{opt}' for opt in core_options]) self.proc = None self.restart() @@ -54,10 +58,16 @@ class Server: f"{self.proc_path} {self.args}", encoding="utf-8", maxread=self.maxread, + timeout=self.timeout, cwd=self.project_path, env=env, ) - self.proc.setecho(False) + self.proc.setecho(False) # Do not send any command before this. + ready = self.proc.readline() # Reads the "ready." + assert ready == "ready.\r\n" + + if self.options: + self.run("options.set", self.options) def run(self, cmd, payload): """ @@ -66,9 +76,8 @@ class Server: s = json.dumps(payload) self.proc.sendline(f"{cmd} {s}") try: - self.proc.expect("{.*}\r\n", timeout=self.timeout) - output = self.proc.match.group() - return json.loads(output) + line = self.proc.readline() + return json.loads(line) except pexpect.exceptions.TIMEOUT as exc: raise exc @@ -167,7 +176,7 @@ def get_version(): class TestServer(unittest.TestCase): def test_version(self): - self.assertEqual(get_version(), "0.2.16") + self.assertEqual(get_version(), "0.2.17") def test_expr_type(self): server = Server() @@ -199,6 +208,58 @@ class TestServer(unittest.TestCase): server.gc() self.assertEqual(len(server.to_remove_goal_states), 0) + def test_automatic_mode(self): + server = Server(options={"automaticMode": True}) + state0 = server.goal_start("forall (p q: Prop), Or p q -> Or q p") + self.assertEqual(len(server.to_remove_goal_states), 0) + self.assertEqual(state0.state_id, 0) + state1 = server.goal_tactic(state0, goal_id=0, tactic="intro a b h") + self.assertEqual(state1.state_id, 1) + self.assertEqual(state1.goals, [Goal( + variables=[ + Variable(name="a", t="Prop"), + Variable(name="b", t="Prop"), + Variable(name="h", t="a ∨ b"), + ], + target="b ∨ a", + name=None, + )]) + state2 = server.goal_tactic(state1, goal_id=0, tactic="cases h") + self.assertEqual(state2.goals, [ + Goal( + variables=[ + Variable(name="a", t="Prop"), + Variable(name="b", t="Prop"), + Variable(name="h✝", t="a"), + ], + target="b ∨ a", + name="inl", + ), + Goal( + variables=[ + Variable(name="a", t="Prop"), + Variable(name="b", t="Prop"), + Variable(name="h✝", t="b"), + ], + target="b ∨ a", + name="inr", + ), + ]) + state3 = server.goal_tactic(state2, goal_id=1, tactic="apply Or.inl") + state4 = server.goal_tactic(state3, goal_id=0, tactic="assumption") + self.assertEqual(state4.goals, [ + Goal( + variables=[ + Variable(name="a", t="Prop"), + Variable(name="b", t="Prop"), + Variable(name="h✝", t="a"), + ], + target="b ∨ a", + name="inl", + ) + ]) + + def test_conv_calc(self): server = Server() state0 = server.goal_start("∀ (a b: Nat), (b = 2) -> 1 + a + 1 = a + b") diff --git a/src b/src index 67e7f22..9b3eef3 160000 --- a/src +++ b/src @@ -1 +1 @@ -Subproject commit 67e7f22b0a1eb280af5eca078bcb383731b76f37 +Subproject commit 9b3eef35ec40a09bba7140ecfc04dafddbd92c27