git @ Cat's Eye Technologies Maxixe / f902e48
Improve error subsystem. Chris Pressey 8 years ago
1 changed file(s) with 39 addition(s) and 19 deletion(s). Raw diff Collapse all Expand all
00 from maxixe.ast import Block
1
2
3 class ProofStructureError(ValueError):
4 pass
5
6
7 class ReasoningError(ValueError):
8 pass
19
210
311 class Checker(object):
614 self.current_block = None
715 self.local_atoms = set()
816 self.used_atoms = set()
9 self.last_step = None
17 self.current_step = None
1018
1119 def check(self):
12 assert self.proof.goal.is_ground(), "goal is not ground"
20 if not self.proof.goal.is_ground():
21 raise ProofStructureError("goal is not ground")
22
1323 for step in self.proof.steps:
1424 if isinstance(step, Block):
1525 self.check_block(step)
1626 else:
1727 self.check_step(step)
1828
19 if self.last_step.term != self.proof.goal:
20 raise ValueError("proof does not reach goal")
29 if self.current_step.term != self.proof.goal:
30 raise ProofStructureError("proof does not reach goal")
2131
2232 def check_block(self, block):
2333 prev_block, prev_local_atoms = self.current_block, self.local_atoms
2535
2636 block_rule = self.proof.get_block_rule(self.current_block.name.name)
2737 if len(block_rule.cases) != len(block.cases):
28 raise ValueError("block must have same number of cases as block rule")
38 raise ProofStructureError("block must have same number of cases as block rule")
2939 last_term = None
3040 for (case_num, (block_rule_case, case)) in enumerate(zip(block_rule.cases, block.cases)):
3141 self.check_case(case_num + 1, block_rule_case, case)
3242 if last_term is None:
3343 last_term = case.steps[-1].term
3444 elif last_term != case.steps[-1].term:
35 raise ValueError("cases do not finish with same term")
45 raise ProofStructureError("cases do not finish with same term")
3646
3747 self.current_block, self.local_atoms = prev_block, prev_local_atoms
3848
3949 def check_case(self, case_num, block_rule_case, case):
4050 if case.steps[0].by.name != block_rule_case.initial.var.name:
41 raise ValueError("initial step of case %s of %s must use rule %s" %
51 raise ProofStructureError("initial step of case %s of %s must use rule %s" %
4252 (case_num, self.current_block.name, block_rule_case.initial.var.name)
4353 )
4454 if block_rule_case.final is not None and case.steps[-1].by.name != block_rule_case.final.var.name:
45 raise ValueError("final step of case %s of %s must use rule %s" %
55 raise ProofStructureError("final step of case %s of %s must use rule %s" %
4656 (case_num, self.current_block.name, block_rule_case.final.var.name)
4757 )
4858
5666 case.steps[-1].term.collect_atoms(final_atoms)
5767 for atom in self.local_atoms:
5868 if atom in final_atoms:
59 raise ValueError("Local atom '%s' cannot be used in final step of case" % atom)
69 raise ProofStructureError("Local atom '%s' cannot be used in final step of case" % atom)
6070
6171 def check_step(self, step):
6272 block = self.current_block
63 self.last_step = step
73 self.current_step = step
6474 rule = self.proof.get_rule(step.by.name)
6575
6676 if len(rule.hypotheses) != len(step.with_):
67 raise ValueError("In %s, Number of arguments provided (%s) does not match number of hypotheses (%s)" %
68 (step.var.name, len(step.with_), len(rule.hypotheses))
77 self.step_error("Number of arguments provided (%s) does not match number of hypotheses (%s)" %
78 (len(step.with_), len(rule.hypotheses))
6979 )
7080 unifier = {}
7181 with_terms = []
7282 for (hypothesis, with_) in zip(rule.hypotheses, step.with_):
7383 if hypothesis.has_attribute('atom'):
74 assert with_.is_atom(), "argument '%s' to hypothesis '%s' is not an atom" % (with_, hypothesis)
84 if not with_.is_atom():
85 self.step_error("argument '%s' to hypothesis '%s' is not an atom" % (with_, hypothesis))
7586 hypothesis.term.match(with_, unifier)
7687 with_term = with_
7788 if hypothesis.has_attribute('local'):
8596 from_level = 0 if from_block is None else from_block.level
8697 if from_level > level:
8798 if not from_block.has_as_last_step(with_step):
88 raise ValueError('%s is a non-final step in an inner block' % with_.name)
99 self.step_error('%s is a non-final step in an inner block' % with_.name)
89100 try:
90101 hypothesis.term.match(with_step.term, unifier)
91102 except ValueError as e:
92103 unifier_str = ", ".join(["%s -> %s" % (k, v) for k, v in unifier.iteritems()])
93 raise ValueError("In %s, could not match '%s' with '%s' with unifier '%s': %s" % (step.var.name, hypothesis.term, with_step.term, unifier_str, e))
104 self.step_error(
105 "could not match '%s' with '%s' with unifier '%s': %s" % (
106 hypothesis.term, with_step.term, unifier_str, e
107 ), class_=ReasoningError
108 )
94109 with_term = with_step.term
95110 with_terms.append(with_term)
96111 if hypothesis.has_attribute('unique'):
97112 if with_term in self.used_atoms:
98 raise ValueError("In %s, '%s' has already been used as an atom in this proof" % (step.by.name, with_term))
113 self.step_error("'%s' has already been used as an atom in this proof" % (step.by.name, with_term))
99114
100115 instance = rule.conclusion.subst(unifier)
101 assert instance.is_ground(), "Not all variables replaced during rule instantiation"
116 if not instance.is_ground():
117 self.step_error("Not all variables replaced during rule instantiation")
102118
103119 instance = instance.resolve_substs(unifier)
104120
105121 if instance != step.term:
106 raise ValueError("%s does not follow from %s with %s - it would be %s." % (
122 self.step_error("%s does not follow from %s with %s - it would be %s." % (
107123 step.term, step.by.name, ' ; '.join([str(t) for t in with_terms]), instance
108 ))
124 ), class_=ReasoningError)
109125
110126 step.term.collect_atoms(self.used_atoms)
127
128 def step_error(self, message, class_=ProofStructureError):
129 message = "In %s, %s" % (self.current_step.var.name, message)
130 raise class_(message)