1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.folproof=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
|
2 | var u = _dereq_("./util");
|
3 |
|
4 | var Justifier = function Justifier(format, fn) {
|
5 |
|
6 | var self = this;
|
7 |
|
8 | this.exec = function(proof, step, part, steps, subst) {
|
9 | u.debug(step, part, steps, subst);
|
10 | var checked = self.checkParams(step, part, steps, subst);
|
11 | if (typeof checked === "string") return checked;
|
12 | return fn(proof, step, checked[0], checked[1], checked[2]);
|
13 | };
|
14 |
|
15 | this.checkParams = function checkParams(curStep, part, steps, subst) {
|
16 | if (format === null) {
|
17 | if (part != null)
|
18 | return "Step part (e.g., 2 in 'and e2') not applicable, in this context.";
|
19 | if (steps != null)
|
20 | return "Step references not applicable.";
|
21 | if (subst != null)
|
22 | return "Substitutions not applicable.";
|
23 | return [];
|
24 | }
|
25 |
|
26 | var partNum = null, refNums = [], w = null;
|
27 | if (format.hasPart) {
|
28 | partNum = parseInt(part);
|
29 | if (!(partNum == 1 || partNum == 2))
|
30 | return "Part number must be 1 or 2";
|
31 | } else
|
32 | if (part != null)
|
33 | return "Step part (e.g., 2 in 'and e2') not applicable, in this context.";
|
34 |
|
35 | if (format.stepRefs) {
|
36 | if (steps.length != format.stepRefs.length) {
|
37 | var f = format.stepRefs.map(function(e) { return e == "num" ? "n" : "n-m" });
|
38 | return "Step reference mismatch; required format: " + f.join(", ") + ".";
|
39 | }
|
40 | for (var i=0; i<steps.length; i++) {
|
41 | if (format.stepRefs[i] == "num") {
|
42 | var n = parseInt(steps[i]) - 1;
|
43 | if (!(n >= 0 && n < curStep))
|
44 | return "Step reference #" + (i + 1) + " must be 1 <= step < current.";
|
45 | refNums.push(n);
|
46 | } else {
|
47 | var ab = steps[i].split("-");
|
48 | if (ab.length != 2)
|
49 | return "Step reference # " + (i + 1) + " must be range, a-b, with a <= b.";
|
50 |
|
51 | ab = [parseInt(ab[0]) - 1, parseInt(ab[1]) - 1];
|
52 | if (ab[0] > ab[1] || Math.max(ab[0], ab[1]) >= curStep)
|
53 | return "Step reference # " + (i + 1) + " must be range, a-b, with a <= b.";
|
54 | refNums.push(ab);
|
55 | }
|
56 | }
|
57 | } else {
|
58 | if (steps != null)
|
59 | return "Step references not applicable, here.";
|
60 | }
|
61 |
|
62 | if (format.subst) {
|
63 | if (!subst)
|
64 | return "Substitution specification required (e.g., A.x/x0 intro n-m)";
|
65 | w = subst.map(function(e) { return e.match("^[A-Za-z_][A-Za-z_0-9]*$"); });
|
66 | var allValidIds = w.reduce(function(a, e) { return a && e && e.length == 1 && e[0] });
|
67 | if (w.length != 2 || !allValidIds)
|
68 | return "Substitution format must match (e.g., A.x/x0 intro n-m.)";
|
69 |
|
70 | w = w.map(function(e) { return e[0] });
|
71 | } else {
|
72 | if (subst)
|
73 | return "Substitution unexpected.";
|
74 | }
|
75 |
|
76 | return [partNum, refNums, w];
|
77 | };
|
78 | };
|
79 |
|
80 | module.exports = Justifier;
|
81 |
|
82 | },{"./util":4}],2:[function(_dereq_,module,exports){
|
83 | var Rule = function Rule(options) {
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 | this.getName = function getName() { return options.name; };
|
91 | this.getType = function getType() { return options.type; };
|
92 | this.getSimpleVerifier = function getSimpleVerifier() { return options.verifier || null; };
|
93 | this.getIntroVerifier = function getIntroVerifier() { return options.introduction || null; };
|
94 | this.getElimVerifier = function getElimVerifier() { return options.elimination || null; };
|
95 | };
|
96 |
|
97 | module.exports = Rule;
|
98 |
|
99 | },{}],3:[function(_dereq_,module,exports){
|
100 | var u = _dereq_("./util");
|
101 | var Rule = _dereq_("./rule.js");
|
102 | var Justifier = _dereq_("./justifier.js");
|
103 |
|
104 | var rules = {
|
105 | "premise" : new Rule({
|
106 | name : "Premise",
|
107 | type : "simple",
|
108 | verifier : new Justifier(null, function(proof, step) { return true; })
|
109 | }),
|
110 | "assumption" : new Rule({
|
111 | name : "Assumption",
|
112 | type : "simple",
|
113 | verifier : new Justifier(null, function(proof, step) {
|
114 | if (proof.steps[step].isFirstStmt())
|
115 | return true;
|
116 | return "Assumptions can only be made at the start of an assumption box.";
|
117 | })
|
118 | }),
|
119 | "lem" : new Rule({
|
120 | name : "LEM",
|
121 | type : "derived",
|
122 | verifier : new Justifier(null, function(proof, step) {
|
123 | var s = proof.steps[step].getSentence();
|
124 | if (s[0] !== "or")
|
125 | return "LEM: must be phi or not phi.";
|
126 | var left = s[1], right = s[2];
|
127 | if (right[0] !== "not" || !semanticEq(left, right[1]))
|
128 | return "LEM: right side must be negation of left.";
|
129 |
|
130 | return true;
|
131 | })
|
132 | }),
|
133 | "copy" : new Rule({
|
134 | name : "COPY",
|
135 | type : "derived",
|
136 | verifier : new Justifier({stepRefs:["num"]},
|
137 | function(proof, step, part, steps) {
|
138 | var curStep = proof.steps[step].getSentence();
|
139 | var refStep = proof.steps[steps[0]].getSentence();
|
140 | if (!semanticEq(curStep, refStep))
|
141 | return "Copy: Current step is not semantically equal to the referenced step.";
|
142 | return true;
|
143 | }
|
144 | )
|
145 | }),
|
146 | "mt" : new Rule({
|
147 | name : "MT",
|
148 | type : "derived",
|
149 | verifier : new Justifier({stepRefs:["num","num"]},
|
150 | function(proof, step, part, steps) {
|
151 | var impStep = proof.steps[steps[0]].getSentence();
|
152 | if (impStep[0] !== "->")
|
153 | return "MT: 1st referenced step must be implication.";
|
154 | var left = impStep[1], right = impStep[2];
|
155 | var negStep = proof.steps[steps[1]].getSentence();
|
156 | if (negStep[0] !== "not" || !semanticEq(negStep[1], right))
|
157 | return "MT: 2nd ref step must be negation of right side of 1st ref step.";
|
158 |
|
159 | var s = proof.steps[step].getSentence();
|
160 | if (s[0] !== 'not' || !semanticEq(left, s[1]))
|
161 | return "MT: current step must be negation of left side of ref step.";
|
162 |
|
163 | return true;
|
164 | })
|
165 | }),
|
166 | "pbc" : new Rule({
|
167 | name : "PBC",
|
168 | type : "derived",
|
169 | verifier : new Justifier(
|
170 | { hasPart : false, stepRefs : ["range"], subst : false },
|
171 | function(proof, step, part, steps) {
|
172 | var assumptionExpr = proof.steps[steps[0][0]].getSentence();
|
173 | var contraExpr = proof.steps[steps[0][1]].getSentence();
|
174 | if (! isContradiction(contraExpr)) {
|
175 | return "PBC: Final step in range must be a contradiction.";
|
176 | }
|
177 |
|
178 | if (assumptionExpr[0] !== 'not')
|
179 | return "PBC: Assumption is not a negation. Might you be thinking of not-introduction?";
|
180 |
|
181 | var semEq = semanticEq(assumptionExpr[1], proof.steps[step].getSentence());
|
182 | if (semEq)
|
183 | return true;
|
184 |
|
185 | return "PBC: Negation of assumption doesn't match current step.";
|
186 | })
|
187 | }),
|
188 | "contra" : new Rule({
|
189 | name : "Contradiction",
|
190 | type : "normal",
|
191 | elimination : new Justifier(
|
192 | { hasPart : false, stepRefs : ["num"], subst : false },
|
193 | function(proof, step, part, steps) {
|
194 | var refStep = proof.steps[steps[0]].getSentence();
|
195 | if (refStep[0] != 'id' || (refStep[1] != 'contradiction' && refStep[1] != '_|_'))
|
196 | return "Contra-elim: Referenced step is not a contradiction.";
|
197 |
|
198 | return true;
|
199 | })
|
200 | }),
|
201 | "notnot" : new Rule({
|
202 | name : "Double-negation",
|
203 | type : "normal",
|
204 | elimination : new Justifier(
|
205 | { hasPart : false, stepRefs : ["num"], subst : false },
|
206 | function(proof, step, part, steps) {
|
207 | var curStep = proof.steps[step].getSentence();
|
208 | var refStep = proof.steps[steps[0]].getSentence();
|
209 | if (refStep[0] !== 'not' || refStep[1][0] !== 'not')
|
210 | return "Notnot-elim: Referenced step is not a double-negation.";
|
211 |
|
212 | if (!semanticEq(refStep[1][1], curStep))
|
213 | return "Notnot-elim: Does not result in current step.";
|
214 |
|
215 | return true;
|
216 | })
|
217 | }),
|
218 | "->" : new Rule({
|
219 | name : "Implication",
|
220 | type : "normal",
|
221 | introduction : new Justifier(
|
222 | { hasPart : false, stepRefs : ["range"], subst : false },
|
223 | function(proof, step, part, steps) {
|
224 | var truth = proof.steps[steps[0][0]].getSentence();
|
225 | var result = proof.steps[steps[0][1]].getSentence();
|
226 | var implies = proof.steps[step].getSentence();
|
227 | if (implies[0] != '->')
|
228 | return "Implies-Intro: Current step is not an implication";
|
229 |
|
230 | var truthSemEq = semanticEq(implies[1], truth);
|
231 | if (! truthSemEq)
|
232 | return "Implies-Intro: The left side does not match the assumption.";
|
233 |
|
234 | var resultSemEq = semanticEq(implies[2], result);
|
235 | if (! resultSemEq)
|
236 | return "Implies-Intro: The result does not match the right side.";
|
237 |
|
238 | return true;
|
239 | }
|
240 | ),
|
241 | elimination : new Justifier(
|
242 | { hasPart : false, stepRefs : ["num", "num"], subst : false },
|
243 | function(proof, step, part, steps) {
|
244 | var truthStep = steps[1], impliesStep = steps[0];
|
245 | if (truthStep >= step || impliesStep >= step)
|
246 | return "Implies-Elim: Referenced proof steps must precede current step.";
|
247 |
|
248 | var truth = proof.steps[truthStep].getSentence();
|
249 | var implies = proof.steps[impliesStep].getSentence();
|
250 | if (implies[0] != '->')
|
251 | return "Implies-Elim: Step " + steps[0] + " is not an implication";
|
252 | var truthSemEq = semanticEq(implies[1], truth);
|
253 | var resultSemEq = semanticEq(implies[2], proof.steps[step].getSentence());
|
254 | if (truthSemEq) {
|
255 | if (resultSemEq) {
|
256 | return true;
|
257 | } else {
|
258 | return "Implies-Elim: The left side does not imply this result.";
|
259 | }
|
260 | }
|
261 |
|
262 | return "Implies-Elim: The implication's left side does not match the referenced step.";
|
263 | }
|
264 | )
|
265 | }),
|
266 | "and" : new Rule({
|
267 | name : "And",
|
268 | type : "normal",
|
269 | introduction : new Justifier(
|
270 | { stepRefs : ["num", "num"] },
|
271 | function(proof, step, part, steps) {
|
272 | var s = proof.steps[step].getSentence();
|
273 | if (s[0] !== 'and')
|
274 | return "And-Intro: Current step is not an 'and'-expression." + proof.steps[step].getSentence();
|
275 |
|
276 | if (semanticEq(s[1], proof.steps[steps[0]].getSentence())) {
|
277 | if (semanticEq(s[2], proof.steps[steps[1]].getSentence())) {
|
278 | return true;
|
279 | } else {
|
280 | return "And-Intro: Right side doesn't match referenced step.";
|
281 | }
|
282 | }
|
283 |
|
284 | return "And-Intro: Left side doesn't match referenced step.";
|
285 | }),
|
286 | elimination : new Justifier(
|
287 | { hasPart: true, stepRefs: ["num"] },
|
288 | function(proof, step, part, steps) {
|
289 | var andExp = proof.steps[steps[0]].getSentence();
|
290 | if (andExp[0] != 'and')
|
291 | return "And-Elim: Referenced step is not an 'and' expression.";
|
292 |
|
293 | var semEq = semanticEq(andExp[part], proof.steps[step].getSentence());
|
294 |
|
295 | if (semEq)
|
296 | return true;
|
297 |
|
298 | return "And-Elim: In referenced line, side " + part + " does not match current step.";
|
299 | })
|
300 | }),
|
301 | "or" : new Rule({
|
302 | name : "Or",
|
303 | type : "normal",
|
304 | introduction : new Justifier(
|
305 | { hasPart: true, stepRefs: ["num"] },
|
306 | function(proof, step, part, steps) {
|
307 | var s = proof.steps[step].getSentence();
|
308 | if (s[0] !== 'or')
|
309 | return "Or-Intro: Current step is not an 'or'-expression.";
|
310 |
|
311 | if (semanticEq(s[part], proof.steps[steps[0]].getSentence()))
|
312 | return true;
|
313 |
|
314 | return "Or-Intro: Side " + part + " doesn't match referenced step.";
|
315 | }),
|
316 | elimination : new Justifier(
|
317 | { stepRefs : ["num", "range", "range"] },
|
318 | function(proof, step, part, steps) {
|
319 | var currStepExpr = proof.steps[step].getSentence();
|
320 | var orStepExpr = proof.steps[steps[0]].getSentence();
|
321 | var a1p1Expr = proof.steps[steps[1][0]].getSentence();
|
322 | var a1p2Expr = proof.steps[steps[1][1]].getSentence();
|
323 | var a2p1Expr = proof.steps[steps[2][0]].getSentence();
|
324 | var a2p2Expr = proof.steps[steps[2][1]].getSentence();
|
325 |
|
326 |
|
327 | if (orStepExpr[0] !== 'or')
|
328 | return "Or-Elim: First referenced step is not an 'or'-expression.";
|
329 | if (!semanticEq(orStepExpr[1], a1p1Expr))
|
330 | return "Or-Elim: First range intro doesn't match left side of 'or'.";
|
331 | if (!semanticEq(orStepExpr[2], a2p1Expr))
|
332 | return "Or-Elim: Second range range intro doesn't match right side of 'or'.";
|
333 | if (!semanticEq(a1p2Expr, a2p2Expr))
|
334 | return "Or-Elim: Step range conclusions don't match.";
|
335 | if (!semanticEq(a1p2Expr, currStepExpr))
|
336 | return "Or-Elim: Current step doesn't match step range conclusions.";
|
337 |
|
338 | return true;
|
339 | })
|
340 | }),
|
341 | "not" : new Rule({
|
342 | name : "Not",
|
343 | type : "normal",
|
344 | introduction : new Justifier(
|
345 | { stepRefs: ["range"] },
|
346 | function(proof, step, part, steps) {
|
347 | var assumptionExpr = proof.steps[steps[0][0]].getSentence();
|
348 | var contraExpr = proof.steps[steps[0][1]].getSentence();
|
349 | if (! isContradiction(contraExpr)) {
|
350 | return "Not-Intro: Final step in range must be a contradiction.";
|
351 | }
|
352 | var curStep = proof.steps[step].getSentence();
|
353 | if (curStep[0] !== 'not') {
|
354 | return "Not-Intro: Current step is not a negation. Might you be thinking of PBC?";
|
355 | } else {
|
356 | var semEq = semanticEq(assumptionExpr, curStep[1]);
|
357 | if (semEq)
|
358 | return true;
|
359 |
|
360 | return "Not-Intro: Negation of assumption doesn't match current step.";
|
361 | }
|
362 | }),
|
363 | elimination : new Justifier(
|
364 | { stepRefs: ["num", "num"] },
|
365 | function(proof, step, part, steps) {
|
366 | var s = proof.steps[step].getSentence();
|
367 | if (! isContradiction(s))
|
368 | return "Not-Elim: Current step is not a contradiction." + proof.steps[step].getSentence();
|
369 |
|
370 | var step1expr = proof.steps[steps[0]].getSentence();
|
371 | var step2expr = proof.steps[steps[1]].getSentence();
|
372 | var semEq;
|
373 | if (step1expr[0] === 'not') {
|
374 | semEq = semanticEq(step1expr[1], step2expr);
|
375 | } else if (step2expr[0] === 'not') {
|
376 | semEq = semanticEq(step2expr[1], step1expr);
|
377 | } else {
|
378 | return "Not-Elim: Neither referenced proof step is a 'not' expression.";
|
379 | }
|
380 |
|
381 | if (semEq) return true;
|
382 |
|
383 | return "Not-Elim: Subexpression in not-expr does not match other expr.";
|
384 | })
|
385 | }),
|
386 | "a." : new Rule({
|
387 | name : "ForAll",
|
388 | type : "normal",
|
389 | introduction : new Justifier(
|
390 | { stepRefs : ["range"], subst : true },
|
391 | function(proof, step, part, steps, subst) {
|
392 | var currStep = proof.steps[step];
|
393 | var currExpr = currStep.getSentence();
|
394 | var startStep = proof.steps[steps[0][0]];
|
395 | var startExpr = startStep.getSentence();
|
396 | var scope = startStep.getScope();
|
397 | var endExpr = proof.steps[steps[0][1]].getSentence();
|
398 | if (currExpr[0] !== 'forall')
|
399 | return "All-x-Intro: Current step is not a 'for-all' expression.";
|
400 | if (scope.length == 0 || scope[0] == null)
|
401 | return "All-x-Intro: Not valid without a scoping assumption (e.g., an x0 box).";
|
402 |
|
403 |
|
404 | var scopeVar = scope[scope.length-1];
|
405 | var found = scope.slice().reverse().reduce(function(a,e) { return a && (e == null || e == subst[1]); }, true);
|
406 | if (! found)
|
407 | return "All-x-intro: Substitution " + subst[1] + " doesn't match scope: " + scope.filter(function(e) { if (e != null) return e; }).join(", ");
|
408 |
|
409 | var endExprSub = substitute(endExpr, subst[1], subst[0]);
|
410 | if (semanticEq(endExprSub, currExpr[2]))
|
411 | return true;
|
412 | return "All-x-Intro: Last step in range doesn't match current step after " + subst[0] + "/" + subst[1] + ".";
|
413 | }),
|
414 | elimination : new Justifier(
|
415 | { stepRefs : ["num"], subst: true },
|
416 | function(proof, step, part, steps, subst) {
|
417 | var currStep = proof.steps[step];
|
418 | var currExpr = currStep.getSentence();
|
419 | var refExpr = proof.steps[steps[0]].getSentence();
|
420 | if (refExpr[0] !== 'forall')
|
421 | return "All-x-Elim: Referenced step is not a for-all expression.";
|
422 |
|
423 | var refExprSub = substitute(refExpr[2], subst[0], subst[1]);
|
424 | if (semanticEq(refExprSub, currExpr))
|
425 | return true;
|
426 |
|
427 | return "All-x-Elim: Referenced step did not match current step after " + subst[1] + "/" + subst[0] + ".";
|
428 | })
|
429 | }),
|
430 | "e." : new Rule({
|
431 | name : "Exists",
|
432 | type : "normal",
|
433 | introduction : new Justifier(
|
434 | { stepRefs: ["num"], subst: true },
|
435 | function(proof, step, part, steps, subst) {
|
436 | var currStep = proof.steps[step];
|
437 | var currExpr = currStep.getSentence();
|
438 | var refExpr = proof.steps[steps[0]].getSentence();
|
439 | if (currExpr[0] !== 'exists')
|
440 | return "Exists-x-Intro: Current step is not an 'exists' expression.";
|
441 |
|
442 | var refExprSub = substitute(refExpr, subst[1], subst[0]);
|
443 | if (semanticEq(refExprSub, currExpr[2]))
|
444 | return true;
|
445 |
|
446 | return "Exists-x-Intro: Referenced step did not match current step after " + subst[1] + "/" + subst[0] + " substitution.";
|
447 | }),
|
448 | elimination : new Justifier(
|
449 | { stepRefs: ["num", "range"], subst: true },
|
450 | function(proof, step, part, steps, subst) {
|
451 | var currStep = proof.steps[step];
|
452 | var currExpr = currStep.getSentence();
|
453 | var refExpr = proof.steps[steps[0]].getSentence();
|
454 | var startStep = proof.steps[steps[1][0]];
|
455 | var startExpr = startStep.getSentence();
|
456 | var scope = startStep.getScope();
|
457 | var endExpr = proof.steps[steps[1][1]].getSentence();
|
458 | if (refExpr[0] !== 'exists')
|
459 | return "Exists-x-Elim: Referenced step is not an 'exists' expression.";
|
460 | if (scope.length == 0 || scope[scope.length - 1] == null)
|
461 | return "Exists-x-Elim: Range must be within an assumption scope (e.g., an x0 box).";
|
462 |
|
463 |
|
464 | var scopeVars = scope[scope.length-1];
|
465 | var refExprSub = substitute(refExpr[2], subst[0], subst[1]);
|
466 | if (semanticEq(refExprSub, startExpr)) {
|
467 | if (semanticEq(endExpr, currExpr))
|
468 | return true;
|
469 | return "Exists-x-Elim: assumption ending step does not match current step.";
|
470 | }
|
471 | return "Exists-x-Elim: assumption beginning step doesn't match ref step for " + scopeVars[0] + ".";
|
472 | })
|
473 | }),
|
474 | "=" : new Rule({
|
475 | name : "Equality",
|
476 | type : "normal",
|
477 | introduction : new Justifier(
|
478 | { },
|
479 | function(proof, step, part, steps) {
|
480 | var s = proof.steps[step].getSentence();
|
481 | if (s[0] !== '=')
|
482 | return "Equality-Intro: Current step is not an equality." + proof.steps[step].getSentence();
|
483 |
|
484 | if (semanticEq(s[1], s[2]))
|
485 | return true;
|
486 |
|
487 | return "Equality-Intro: Left and right sides do not match.";
|
488 | }),
|
489 | elimination : new Justifier(
|
490 | { stepRefs: ["num", "num"] },
|
491 | function(proof, step, part, steps) {
|
492 | var equalityExpr = proof.steps[steps[0]].getSentence();
|
493 | var elimExpr = proof.steps[steps[1]].getSentence();
|
494 | var proposedResult = proof.steps[step].getSentence();
|
495 | if (equalityExpr[0] !== '=')
|
496 | return "Equality-Elim: First referenced step is not an equality.";
|
497 |
|
498 | if (!semanticEq(elimExpr, proposedResult, equalityExpr[1], equalityExpr[2]))
|
499 | return "Equality-Elim: Does not result in current step.";
|
500 |
|
501 | return true;
|
502 | })
|
503 | }),
|
504 | };
|
505 |
|
506 | function substitute(startExpr, a, b, bound) {
|
507 | u.debug("substitute", startExpr, a, b);
|
508 | bound = bound ? bound : [];
|
509 | var binOps = ["->", "and", "or", "<->", "="];
|
510 | var unOps = ["not", "forall", "exists"];
|
511 |
|
512 |
|
513 | while (startExpr[0] === 'paren') startExpr = startExpr[1];
|
514 |
|
515 | if (arrayContains(binOps, startExpr[0])) {
|
516 | var leftSide = substitute(startExpr[1], a, b);
|
517 | var rightSide = substitute(startExpr[2], a, b);
|
518 | return [startExpr[0], leftSide, rightSide];
|
519 | } else if (arrayContains(unOps, startExpr[0])) {
|
520 | if (startExpr[0] === "forall" || startExpr[0] === "exists") {
|
521 | bound = bound.slice(0);
|
522 | bound.push(startExpr[1]);
|
523 | return [startExpr[0], startExpr[1],
|
524 | substitute(startExpr[2], a, b, bound)];
|
525 | }
|
526 |
|
527 | return [startExpr[0], substitute(startExpr[1], a, b, bound)];
|
528 | } else if (startExpr[0] === 'id') {
|
529 | if (startExpr.length === 2) {
|
530 | if (! arrayContains(bound, startExpr[1])) {
|
531 | if (startExpr[1] === a)
|
532 | return [startExpr[0], b];
|
533 | }
|
534 | return startExpr;
|
535 | }
|
536 | if (startExpr.length === 3) {
|
537 | var newTerms = [];
|
538 | for (var i=0; i<startExpr[2].length; i++) {
|
539 | newTerms.push(substitute(startExpr[2][i], a, b, bound));
|
540 | }
|
541 | return [startExpr[0], startExpr[1], newTerms];
|
542 | }
|
543 | throw Error("Unexpected AST format.");
|
544 | }
|
545 | }
|
546 |
|
547 |
|
548 |
|
549 |
|
550 |
|
551 |
|
552 |
|
553 | function semanticEq(A, B, suba, subb) {
|
554 | u.debug("semanticEq", A, B);
|
555 | var bound = {}, sub;
|
556 | if (suba) {
|
557 | sub = true;
|
558 | return _rec(A, B, {});
|
559 | } else {
|
560 | sub = false;
|
561 | return _rec(A, B);
|
562 | }
|
563 |
|
564 | function _rec(a, b, bound) {
|
565 | var binOps = ["->", "and", "or", "<->", "="];
|
566 | var unOps = ["not"];
|
567 |
|
568 |
|
569 | if (sub && semanticEq(a, suba)) {
|
570 | if ((a[0] !== 'id' || !bound[a[1]]) && _rec(subb, b, bound)) return true;
|
571 | }
|
572 |
|
573 | if (arrayContains(binOps, a[0]) && a[0] === b[0]) {
|
574 | if (_rec(a[1], b[1], bound) && _rec(a[2], b[2], bound)) {
|
575 | return true;
|
576 | }
|
577 | return false;
|
578 | } else if (arrayContains(unOps, a[0]) && a[0] === b[0]) {
|
579 | if (_rec(a[1], b[1], bound)) {
|
580 | return true;
|
581 | }
|
582 | return false;
|
583 | } else if (a[0] === 'exists' || a[0] === 'forall' && a[0] === b[0]) {
|
584 | var newb;
|
585 | if (sub) {
|
586 | newb = clone(bound);
|
587 | newb[a[1]] = true;
|
588 | }
|
589 | if (_rec(a[2], b[2], newb)) {
|
590 | return true;
|
591 | }
|
592 | return false;
|
593 | } else if (a[0] === "id") {
|
594 | if (b && a[1] !== b[1]) return false;
|
595 | if (a.length == 2 && b.length == 2) {
|
596 | return true;
|
597 | }
|
598 |
|
599 | if (a.length == 3 && b.length == 3) {
|
600 | if (a[2].length != b[2].length) {
|
601 | return false;
|
602 | }
|
603 | for (var i=0; i<a[2].length; i++) {
|
604 | if (!_rec(a[2][i], b[2][i], bound)) {
|
605 | return false;
|
606 | }
|
607 | }
|
608 | return true;
|
609 | }
|
610 | }
|
611 | return false;
|
612 | }
|
613 | }
|
614 |
|
615 | function isContradiction(s) {
|
616 | return (s[0] === 'id' && (s[1] === '_|_' || s[1] === 'contradiction'));
|
617 | }
|
618 |
|
619 | function arrayContains(arr, el) {
|
620 | for (var i=0; i<arr.length; i++) {
|
621 | if (arr[i] === el) return true;
|
622 | }
|
623 | return false;
|
624 | }
|
625 |
|
626 | function clone(obj) {
|
627 | var newo = {};
|
628 | for(var k in Object.keys(obj)) {
|
629 | newo[k] = obj[k];
|
630 | }
|
631 | return newo;
|
632 | }
|
633 |
|
634 | if (typeof _dereq_ !== 'undefined' && typeof exports !== 'undefined') {
|
635 | module.exports = rules;
|
636 | }
|
637 |
|
638 | },{"./justifier.js":1,"./rule.js":2,"./util":4}],4:[function(_dereq_,module,exports){
|
639 | var util = {};
|
640 | util.debug = function debug() {
|
641 | if (typeof debugMode !== "undefined" && debugMode)
|
642 | console.log.apply(console, Array.prototype.slice.call(arguments));
|
643 | };
|
644 |
|
645 | if (typeof _dereq_ !== 'undefined' && typeof exports !== 'undefined') {
|
646 | module.exports = util;
|
647 | }
|
648 |
|
649 | },{}],5:[function(_dereq_,module,exports){
|
650 | var rules = _dereq_("./rules");
|
651 | var u = _dereq_("./util");
|
652 |
|
653 | var Verifier = (function() {
|
654 | var debugMode = false;
|
655 | var obj = this;
|
656 |
|
657 | obj.verifyFromAST = function(ast) {
|
658 | var proof = preprocess(ast);
|
659 | return obj.verify(proof);
|
660 | };
|
661 |
|
662 |
|
663 | obj.verify = function(proof) {
|
664 | var result = { message : "Proof is valid.", valid : true };
|
665 | for (var i=0; i<proof.steps.length; i++) {
|
666 | obj.validateStatement(result, proof, i);
|
667 | if (! result.valid) {
|
668 | break;
|
669 | }
|
670 | }
|
671 | return result;
|
672 | };
|
673 |
|
674 | obj.validateStatement = function validateStatement(result, proof, step) {
|
675 | var stmt = proof.steps[step];
|
676 | if (stmt[0] === 'error') {
|
677 | result.valid = false;
|
678 | result.message = "Proof invalid due to syntax errors.";
|
679 | result.errorStep = step + 1;
|
680 | return;
|
681 | }
|
682 |
|
683 | var why = stmt.getJustification();
|
684 | var newv = null;
|
685 | if (why[0].split('.').length == 2)
|
686 | newv = why[0].split('.')[1];
|
687 | var validator = obj.lookupValidator(why);
|
688 | if (typeof validator === 'function') {
|
689 | var part = why[2], lines = why[3];
|
690 | var subst = null;
|
691 | if (newv && why[4]) subst = [newv, why[4]];
|
692 | var isValid = validator(proof, step, part, lines, subst);
|
693 | if (isValid === true) {
|
694 | result.valid = true;
|
695 | } else {
|
696 | result.valid = false;
|
697 | result.message = isValid;
|
698 | result.errorStep = step + 1;
|
699 | result.errorSrcLoc = stmt.getMeta();
|
700 | }
|
701 | return;
|
702 | } else if (typeof validator === "string") {
|
703 | result.valid = false;
|
704 | result.message = validator;
|
705 | result.errorStep = step + 1;
|
706 | result.errorSrcLoc = stmt.getMeta();
|
707 | }
|
708 | result.valid = false;
|
709 | };
|
710 |
|
711 | obj.lookupValidator = function lookupValidator(why) {
|
712 | var name = why[0].toLowerCase();
|
713 | if (name.split('.').length == 2)
|
714 | name = name.split('.')[0] + ".";
|
715 | var rule = rules[name];
|
716 | if (!rule) return "Cannot find rule: " + name;
|
717 | if (rule.getType() === "simple" || rule.getType() === "derived") {
|
718 | var fn = rule.getSimpleVerifier();
|
719 | if (!fn) throw new Error("Not implemented for " + name);
|
720 | return fn.exec;
|
721 | }
|
722 |
|
723 | if (why[1]) {
|
724 | var elimOrIntro = why[1].toLowerCase();
|
725 | if ("introduction".indexOf(elimOrIntro) === 0) {
|
726 | var fn = rule.getIntroVerifier();
|
727 | if (!fn) throw new Error("Not implemented for " + name);
|
728 | return fn.exec;
|
729 | } else if ("elimination".indexOf(elimOrIntro) === 0) {
|
730 | var fn = rule.getElimVerifier();
|
731 | if (!fn) throw new Error("Not implemented for " + name);
|
732 | return fn.exec;
|
733 | }
|
734 | return "Cannot determine elim/intro rule type from " + elimOrIntro;
|
735 | }
|
736 |
|
737 | return "Unrecognized rule: " + why[0] + " " + (why[1] ? why[1] : "") + (why[2] ? why[2] : "") + " " + (why[3] ? why[3] : "");
|
738 | }
|
739 |
|
740 | obj.preprocess = function preprocess(ast) {
|
741 | var proof = { steps : [] };
|
742 | obj.preprocessBox(proof, ast, 0, []);
|
743 | return proof;
|
744 | }
|
745 |
|
746 | obj.preprocessBox = function preprocessBox(proof, ast, step, scope) {
|
747 | for(var i=0; i<ast.length; i++) {
|
748 | if (ast[i][0] === 'rule') {
|
749 | proof.steps[step] = new Statement(ast[i][1], ast[i][2], scope, ast[i][3], i == 0, i == ast.length - 1);
|
750 | step = step + 1;
|
751 | } else if (ast[i][0] === 'folbox') {
|
752 | var newScope = scope.slice(0)
|
753 | newScope.push(ast[i][2][1]);
|
754 | step = obj.preprocessBox(proof, ast[i][1], step, newScope);
|
755 | } else if (ast[i][0] === 'box') {
|
756 | var newScope = scope.slice(0)
|
757 | newScope.push(null);
|
758 | step = obj.preprocessBox(proof, ast[i][1], step, newScope);
|
759 | } else if (ast[i][0] === 'error') {
|
760 | proof.steps[step] = ast[i];
|
761 | }
|
762 | }
|
763 | return step;
|
764 | }
|
765 |
|
766 | var Statement = function(sentenceAST, justificationAST, scope, loc, isFirst, isLast) {
|
767 | this.isFirstStmt = function() { return isFirst; };
|
768 | this.isLastStmt = function() { return isLast; };
|
769 | this.getSentence = function getSentence() { return sentenceAST; };
|
770 | this.getScope = function getScope() { return scope; }
|
771 | this.getJustification = function getJustification() { return justificationAST; };
|
772 | this.getMeta = function() { return loc; }
|
773 | };
|
774 |
|
775 | return obj;
|
776 | })();
|
777 |
|
778 | if (typeof _dereq_ !== 'undefined' && typeof exports !== 'undefined') {
|
779 | exports.Verifier = Verifier;
|
780 | }
|
781 |
|
782 | },{"./rules":3,"./util":4}]},{},[5])
|
783 | (5)
|
784 | }); |
\ | No newline at end of file |