mikeljl's picture
use pypantograph for checking submitted statements
0115dcf
Raw
History Blame Contribute Delete
5.4 kB
"""
Data structuers for expressions and goals
"""
from pantograph.message import Message
from dataclasses import dataclass, field
from enum import Enum
from typing import Optional, TypeAlias
Expr: TypeAlias = str
def parse_expr(payload: dict) -> Expr:
"""
:meta private:
"""
return payload["pp"]
class TacticMode(Enum):
"""
Current execution mode
"""
TACTIC = 1
CONV = 2
CALC = 3
def serial(self):
match self:
case TacticMode.TACTIC: return "tactic"
case TacticMode.CONV: return "conv"
case TacticMode.CALC: return "calc"
@dataclass(frozen=True)
class Variable:
t: Expr
v: Optional[Expr] = None
name: Optional[str] = None
@staticmethod
def parse(payload: dict):
name = payload.get("userName")
t = parse_expr(payload["type"])
v = payload.get("value")
if v:
v = parse_expr(v)
return Variable(t, v, name)
def __str__(self):
"""
:meta public:
"""
result = self.name if self.name else "_"
result += f" : {self.t}"
if self.v:
result += f" := {self.v}"
return result
@dataclass(frozen=True)
class Goal:
id: str
variables: list[Variable]
target: Expr
sibling_dep: Optional[set[int]] = field(default_factory=lambda: None)
name: Optional[str] = None
mode: TacticMode = TacticMode.TACTIC
@staticmethod
def sentence(id: str, target: Expr):
"""
:meta public:
"""
return Goal(id=id, variables=[], target=target)
@staticmethod
def parse(payload: dict, sibling_map: dict[str, int]):
id = payload["name"]
name = payload.get("userName")
variables = [Variable.parse(v) for v in payload["vars"]]
target = parse_expr(payload["target"])
mode = TacticMode[payload["fragment"].upper()]
sibling_dep = None
for e in [payload["target"]] \
+ [v["type"] for v in payload["vars"]] \
+ [v["value"] for v in payload["vars"] if "value" in v]:
dependents = e.get("dependentMVars")
if dependents is None:
continue
deps = [sibling_map[d] for d in dependents if d in sibling_map]
if sibling_dep:
sibling_dep = {*sibling_dep, *deps}
else:
sibling_dep = {*deps}
return Goal(id, variables, target, sibling_dep, name, mode)
def __str__(self):
head = f"case {self.name}\n" if self.name else ""
front = "|" if self.mode == TacticMode.CONV else "⊢"
return head +\
"\n".join(str(v) for v in self.variables) +\
f"\n{front} {self.target}"
@dataclass(frozen=True)
class GoalState:
state_id: int
goals: list[Goal]
messages: list[Message]
# For tracking memory usage
_sentinel: list[int]
def __del__(self):
self._sentinel.append(self.state_id)
def __repr__(self):
cls = self.__class__.__name__
messages = f"messages={repr(self.messages)}, " if self.messages else ""
return f"{cls}(#{self.state_id}, goals={repr(self.goals)}{messages}, _sentinel=#{len(self._sentinel)}"
@property
def is_solved(self) -> bool:
"""
WARNING: Does not handle dormant goals.
:meta public:
"""
return not self.goals
@staticmethod
def parse_inner(state_id: int, goals: list, messages: list[dict], _sentinel: list[int]):
assert _sentinel is not None
goal_names = { g["name"]: i for i, g in enumerate(goals) }
goals = [Goal.parse(g, goal_names) for g in goals]
messages = [Message.parse(m) for m in messages]
return GoalState(state_id, goals, messages, _sentinel)
@staticmethod
def parse(payload: dict, messages: list[dict], _sentinel: list[int]):
return GoalState.parse_inner(payload["nextStateId"], payload["goals"], messages, _sentinel)
def __str__(self):
"""
:meta public:
"""
return "\n".join([str(g) for g in self.goals])
@dataclass(frozen=True)
class Site:
"""
Acting area of a tactic
"""
goal_id: Optional[int] = None
auto_resume: Optional[bool] = None
def serial(self) -> dict:
result = {}
if self.goal_id is not None:
result["goalId"] = self.goal_id
if self.auto_resume is not None:
result["autoResume"] = self.auto_resume
return result
class Subsumption(Enum):
"""
Subsumption result
"""
NONE = 1
SUBSUMED = 2
CYCLE = 3
@dataclass(frozen=True)
class TacticHave:
"""
The `have` tactic, equivalent to
```lean
have {binder_name} : {branch} := ...
```
"""
branch: str
binder_name: Optional[str] = None
@dataclass(frozen=True)
class TacticLet:
"""
The `let` tactic, equivalent to
```lean
let {binder_name} : {branch} := ...
```
"""
branch: str
binder_name: Optional[str] = None
@dataclass(frozen=True)
class TacticExpr:
"""
Assigns an expression to the current goal
"""
expr: str
@dataclass(frozen=True)
class TacticDraft:
"""
Assigns an expression to the current goal
"""
expr: str
Tactic: TypeAlias = str | TacticHave | TacticLet | TacticExpr | TacticDraft | TacticMode