UNPKG

27 kBJavaScriptView Raw
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){
2var u = _dereq_("./util");
3
4var Justifier = function Justifier(format, fn) {
5 // format = { hasPart : (true/false), stepRefs : ("num" | "range")*, subst : (true/false) };
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
80module.exports = Justifier;
81
82},{"./util":4}],2:[function(_dereq_,module,exports){
83var Rule = function Rule(options) {
84 // { name : name,
85 // type : ["simple", "derived", "normal"],
86 // verifier : new Verifier(parseFormat, function(proof, step) {}),
87 // introduction : new Verifier(parseFormat, function(proof, step, part, steps, subst) {}),
88 // elimination : new Verifier(parseFormat, function(proof, step, part, steps, subst) {})
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
97module.exports = Rule;
98
99},{}],3:[function(_dereq_,module,exports){
100var u = _dereq_("./util");
101var Rule = _dereq_("./rule.js");
102var Justifier = _dereq_("./justifier.js");
103
104var 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 // and through the gauntlet...
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(); // ex: [['x0','x'], ['y0', 'y'], ...], LIFO
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 // check if any substitutions from our scope match refExpr
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(); // ex: [['x0','x'], ['y0', 'y'], ...], LIFO
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 // check whether substition matches ref line with current line
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 { /* no params required */ },
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
506function 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 // remove parens, which are basically stylistic no-ops
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) { // our loverly base case
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 * Determines whether two expressions are semantically equivalent
549 * under the given (and optional) substitution.
550 * a, b - abstract syntax trees of the expressions to be compared.
551 * suba, subb (optional) - does comparison after substituting suba in a with subb.
552 */
553function 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 // if eq w/substitution, return true, otherwise continue
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
615function isContradiction(s) {
616 return (s[0] === 'id' && (s[1] === '_|_' || s[1] === 'contradiction'));
617}
618
619function 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
626function clone(obj) {
627 var newo = {};
628 for(var k in Object.keys(obj)) {
629 newo[k] = obj[k];
630 }
631 return newo;
632}
633
634if (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){
639var util = {};
640util.debug = function debug() {
641 if (typeof debugMode !== "undefined" && debugMode)
642 console.log.apply(console, Array.prototype.slice.call(arguments));
643};
644
645if (typeof _dereq_ !== 'undefined' && typeof exports !== 'undefined') {
646 module.exports = util;
647}
648
649},{}],5:[function(_dereq_,module,exports){
650var rules = _dereq_("./rules");
651var u = _dereq_("./util");
652
653var 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 // proof = { 1 : Statement(), 2 : Statement() ... };
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
778if (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