folproof
Version:
A first-order logic proof verifier
784 lines (702 loc) • 27 kB
JavaScript
!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){
var u = _dereq_("./util");
var Justifier = function Justifier(format, fn) {
// format = { hasPart : (true/false), stepRefs : ("num" | "range")*, subst : (true/false) };
var self = this;
this.exec = function(proof, step, part, steps, subst) {
u.debug(step, part, steps, subst);
var checked = self.checkParams(step, part, steps, subst);
if (typeof checked === "string") return checked;
return fn(proof, step, checked[0], checked[1], checked[2]);
};
this.checkParams = function checkParams(curStep, part, steps, subst) {
if (format === null) {
if (part != null)
return "Step part (e.g., 2 in 'and e2') not applicable, in this context.";
if (steps != null)
return "Step references not applicable.";
if (subst != null)
return "Substitutions not applicable.";
return [];
}
var partNum = null, refNums = [], w = null;
if (format.hasPart) {
partNum = parseInt(part);
if (!(partNum == 1 || partNum == 2))
return "Part number must be 1 or 2";
} else
if (part != null)
return "Step part (e.g., 2 in 'and e2') not applicable, in this context.";
if (format.stepRefs) {
if (steps.length != format.stepRefs.length) {
var f = format.stepRefs.map(function(e) { return e == "num" ? "n" : "n-m" });
return "Step reference mismatch; required format: " + f.join(", ") + ".";
}
for (var i=0; i<steps.length; i++) {
if (format.stepRefs[i] == "num") {
var n = parseInt(steps[i]) - 1;
if (!(n >= 0 && n < curStep))
return "Step reference #" + (i + 1) + " must be 1 <= step < current.";
refNums.push(n);
} else {
var ab = steps[i].split("-");
if (ab.length != 2)
return "Step reference # " + (i + 1) + " must be range, a-b, with a <= b.";
ab = [parseInt(ab[0]) - 1, parseInt(ab[1]) - 1];
if (ab[0] > ab[1] || Math.max(ab[0], ab[1]) >= curStep)
return "Step reference # " + (i + 1) + " must be range, a-b, with a <= b.";
refNums.push(ab);
}
}
} else {
if (steps != null)
return "Step references not applicable, here.";
}
if (format.subst) {
if (!subst)
return "Substitution specification required (e.g., A.x/x0 intro n-m)";
w = subst.map(function(e) { return e.match("^[A-Za-z_][A-Za-z_0-9]*$"); });
var allValidIds = w.reduce(function(a, e) { return a && e && e.length == 1 && e[0] });
if (w.length != 2 || !allValidIds)
return "Substitution format must match (e.g., A.x/x0 intro n-m.)";
w = w.map(function(e) { return e[0] });
} else {
if (subst)
return "Substitution unexpected.";
}
return [partNum, refNums, w];
};
};
module.exports = Justifier;
},{"./util":4}],2:[function(_dereq_,module,exports){
var Rule = function Rule(options) {
// { name : name,
// type : ["simple", "derived", "normal"],
// verifier : new Verifier(parseFormat, function(proof, step) {}),
// introduction : new Verifier(parseFormat, function(proof, step, part, steps, subst) {}),
// elimination : new Verifier(parseFormat, function(proof, step, part, steps, subst) {})
// }
this.getName = function getName() { return options.name; };
this.getType = function getType() { return options.type; };
this.getSimpleVerifier = function getSimpleVerifier() { return options.verifier || null; };
this.getIntroVerifier = function getIntroVerifier() { return options.introduction || null; };
this.getElimVerifier = function getElimVerifier() { return options.elimination || null; };
};
module.exports = Rule;
},{}],3:[function(_dereq_,module,exports){
var u = _dereq_("./util");
var Rule = _dereq_("./rule.js");
var Justifier = _dereq_("./justifier.js");
var rules = {
"premise" : new Rule({
name : "Premise",
type : "simple",
verifier : new Justifier(null, function(proof, step) { return true; })
}),
"assumption" : new Rule({
name : "Assumption",
type : "simple",
verifier : new Justifier(null, function(proof, step) {
if (proof.steps[step].isFirstStmt())
return true;
return "Assumptions can only be made at the start of an assumption box.";
})
}),
"lem" : new Rule({
name : "LEM",
type : "derived",
verifier : new Justifier(null, function(proof, step) {
var s = proof.steps[step].getSentence();
if (s[0] !== "or")
return "LEM: must be phi or not phi.";
var left = s[1], right = s[2];
if (right[0] !== "not" || !semanticEq(left, right[1]))
return "LEM: right side must be negation of left.";
return true;
})
}),
"copy" : new Rule({
name : "COPY",
type : "derived",
verifier : new Justifier({stepRefs:["num"]},
function(proof, step, part, steps) {
var curStep = proof.steps[step].getSentence();
var refStep = proof.steps[steps[0]].getSentence();
if (!semanticEq(curStep, refStep))
return "Copy: Current step is not semantically equal to the referenced step.";
return true;
}
)
}),
"mt" : new Rule({
name : "MT",
type : "derived",
verifier : new Justifier({stepRefs:["num","num"]},
function(proof, step, part, steps) {
var impStep = proof.steps[steps[0]].getSentence();
if (impStep[0] !== "->")
return "MT: 1st referenced step must be implication.";
var left = impStep[1], right = impStep[2];
var negStep = proof.steps[steps[1]].getSentence();
if (negStep[0] !== "not" || !semanticEq(negStep[1], right))
return "MT: 2nd ref step must be negation of right side of 1st ref step.";
var s = proof.steps[step].getSentence();
if (s[0] !== 'not' || !semanticEq(left, s[1]))
return "MT: current step must be negation of left side of ref step.";
return true;
})
}),
"pbc" : new Rule({
name : "PBC",
type : "derived",
verifier : new Justifier(
{ hasPart : false, stepRefs : ["range"], subst : false },
function(proof, step, part, steps) {
var assumptionExpr = proof.steps[steps[0][0]].getSentence();
var contraExpr = proof.steps[steps[0][1]].getSentence();
if (! isContradiction(contraExpr)) {
return "PBC: Final step in range must be a contradiction.";
}
if (assumptionExpr[0] !== 'not')
return "PBC: Assumption is not a negation. Might you be thinking of not-introduction?";
var semEq = semanticEq(assumptionExpr[1], proof.steps[step].getSentence());
if (semEq)
return true;
return "PBC: Negation of assumption doesn't match current step.";
})
}),
"contra" : new Rule({
name : "Contradiction",
type : "normal",
elimination : new Justifier(
{ hasPart : false, stepRefs : ["num"], subst : false },
function(proof, step, part, steps) {
var refStep = proof.steps[steps[0]].getSentence();
if (refStep[0] != 'id' || (refStep[1] != 'contradiction' && refStep[1] != '_|_'))
return "Contra-elim: Referenced step is not a contradiction.";
return true;
})
}),
"notnot" : new Rule({
name : "Double-negation",
type : "normal",
elimination : new Justifier(
{ hasPart : false, stepRefs : ["num"], subst : false },
function(proof, step, part, steps) {
var curStep = proof.steps[step].getSentence();
var refStep = proof.steps[steps[0]].getSentence();
if (refStep[0] !== 'not' || refStep[1][0] !== 'not')
return "Notnot-elim: Referenced step is not a double-negation.";
if (!semanticEq(refStep[1][1], curStep))
return "Notnot-elim: Does not result in current step.";
return true;
})
}),
"->" : new Rule({
name : "Implication",
type : "normal",
introduction : new Justifier(
{ hasPart : false, stepRefs : ["range"], subst : false },
function(proof, step, part, steps) {
var truth = proof.steps[steps[0][0]].getSentence();
var result = proof.steps[steps[0][1]].getSentence();
var implies = proof.steps[step].getSentence();
if (implies[0] != '->')
return "Implies-Intro: Current step is not an implication";
var truthSemEq = semanticEq(implies[1], truth);
if (! truthSemEq)
return "Implies-Intro: The left side does not match the assumption.";
var resultSemEq = semanticEq(implies[2], result);
if (! resultSemEq)
return "Implies-Intro: The result does not match the right side.";
return true;
}
),
elimination : new Justifier(
{ hasPart : false, stepRefs : ["num", "num"], subst : false },
function(proof, step, part, steps) {
var truthStep = steps[1], impliesStep = steps[0];
if (truthStep >= step || impliesStep >= step)
return "Implies-Elim: Referenced proof steps must precede current step.";
var truth = proof.steps[truthStep].getSentence();
var implies = proof.steps[impliesStep].getSentence();
if (implies[0] != '->')
return "Implies-Elim: Step " + steps[0] + " is not an implication";
var truthSemEq = semanticEq(implies[1], truth);
var resultSemEq = semanticEq(implies[2], proof.steps[step].getSentence());
if (truthSemEq) {
if (resultSemEq) {
return true;
} else {
return "Implies-Elim: The left side does not imply this result.";
}
}
return "Implies-Elim: The implication's left side does not match the referenced step.";
}
)
}),
"and" : new Rule({
name : "And",
type : "normal",
introduction : new Justifier(
{ stepRefs : ["num", "num"] },
function(proof, step, part, steps) {
var s = proof.steps[step].getSentence();
if (s[0] !== 'and')
return "And-Intro: Current step is not an 'and'-expression." + proof.steps[step].getSentence();
if (semanticEq(s[1], proof.steps[steps[0]].getSentence())) {
if (semanticEq(s[2], proof.steps[steps[1]].getSentence())) {
return true;
} else {
return "And-Intro: Right side doesn't match referenced step.";
}
}
return "And-Intro: Left side doesn't match referenced step.";
}),
elimination : new Justifier(
{ hasPart: true, stepRefs: ["num"] },
function(proof, step, part, steps) {
var andExp = proof.steps[steps[0]].getSentence();
if (andExp[0] != 'and')
return "And-Elim: Referenced step is not an 'and' expression.";
var semEq = semanticEq(andExp[part], proof.steps[step].getSentence());
if (semEq)
return true;
return "And-Elim: In referenced line, side " + part + " does not match current step.";
})
}),
"or" : new Rule({
name : "Or",
type : "normal",
introduction : new Justifier(
{ hasPart: true, stepRefs: ["num"] },
function(proof, step, part, steps) {
var s = proof.steps[step].getSentence();
if (s[0] !== 'or')
return "Or-Intro: Current step is not an 'or'-expression.";
if (semanticEq(s[part], proof.steps[steps[0]].getSentence()))
return true;
return "Or-Intro: Side " + part + " doesn't match referenced step.";
}),
elimination : new Justifier(
{ stepRefs : ["num", "range", "range"] },
function(proof, step, part, steps) {
var currStepExpr = proof.steps[step].getSentence();
var orStepExpr = proof.steps[steps[0]].getSentence();
var a1p1Expr = proof.steps[steps[1][0]].getSentence();
var a1p2Expr = proof.steps[steps[1][1]].getSentence();
var a2p1Expr = proof.steps[steps[2][0]].getSentence();
var a2p2Expr = proof.steps[steps[2][1]].getSentence();
// and through the gauntlet...
if (orStepExpr[0] !== 'or')
return "Or-Elim: First referenced step is not an 'or'-expression.";
if (!semanticEq(orStepExpr[1], a1p1Expr))
return "Or-Elim: First range intro doesn't match left side of 'or'.";
if (!semanticEq(orStepExpr[2], a2p1Expr))
return "Or-Elim: Second range range intro doesn't match right side of 'or'.";
if (!semanticEq(a1p2Expr, a2p2Expr))
return "Or-Elim: Step range conclusions don't match.";
if (!semanticEq(a1p2Expr, currStepExpr))
return "Or-Elim: Current step doesn't match step range conclusions.";
return true;
})
}),
"not" : new Rule({
name : "Not",
type : "normal",
introduction : new Justifier(
{ stepRefs: ["range"] },
function(proof, step, part, steps) {
var assumptionExpr = proof.steps[steps[0][0]].getSentence();
var contraExpr = proof.steps[steps[0][1]].getSentence();
if (! isContradiction(contraExpr)) {
return "Not-Intro: Final step in range must be a contradiction.";
}
var curStep = proof.steps[step].getSentence();
if (curStep[0] !== 'not') {
return "Not-Intro: Current step is not a negation. Might you be thinking of PBC?";
} else {
var semEq = semanticEq(assumptionExpr, curStep[1]);
if (semEq)
return true;
return "Not-Intro: Negation of assumption doesn't match current step.";
}
}),
elimination : new Justifier(
{ stepRefs: ["num", "num"] },
function(proof, step, part, steps) {
var s = proof.steps[step].getSentence();
if (! isContradiction(s))
return "Not-Elim: Current step is not a contradiction." + proof.steps[step].getSentence();
var step1expr = proof.steps[steps[0]].getSentence();
var step2expr = proof.steps[steps[1]].getSentence();
var semEq;
if (step1expr[0] === 'not') {
semEq = semanticEq(step1expr[1], step2expr);
} else if (step2expr[0] === 'not') {
semEq = semanticEq(step2expr[1], step1expr);
} else {
return "Not-Elim: Neither referenced proof step is a 'not' expression.";
}
if (semEq) return true;
return "Not-Elim: Subexpression in not-expr does not match other expr.";
})
}),
"a." : new Rule({
name : "ForAll",
type : "normal",
introduction : new Justifier(
{ stepRefs : ["range"], subst : true },
function(proof, step, part, steps, subst) {
var currStep = proof.steps[step];
var currExpr = currStep.getSentence();
var startStep = proof.steps[steps[0][0]];
var startExpr = startStep.getSentence();
var scope = startStep.getScope(); // ex: [['x0','x'], ['y0', 'y'], ...], LIFO
var endExpr = proof.steps[steps[0][1]].getSentence();
if (currExpr[0] !== 'forall')
return "All-x-Intro: Current step is not a 'for-all' expression.";
if (scope.length == 0 || scope[0] == null)
return "All-x-Intro: Not valid without a scoping assumption (e.g., an x0 box).";
// check if any substitutions from our scope match refExpr
var scopeVar = scope[scope.length-1];
var found = scope.slice().reverse().reduce(function(a,e) { return a && (e == null || e == subst[1]); }, true);
if (! found)
return "All-x-intro: Substitution " + subst[1] + " doesn't match scope: " + scope.filter(function(e) { if (e != null) return e; }).join(", ");
var endExprSub = substitute(endExpr, subst[1], subst[0]);
if (semanticEq(endExprSub, currExpr[2]))
return true;
return "All-x-Intro: Last step in range doesn't match current step after " + subst[0] + "/" + subst[1] + ".";
}),
elimination : new Justifier(
{ stepRefs : ["num"], subst: true },
function(proof, step, part, steps, subst) {
var currStep = proof.steps[step];
var currExpr = currStep.getSentence();
var refExpr = proof.steps[steps[0]].getSentence();
if (refExpr[0] !== 'forall')
return "All-x-Elim: Referenced step is not a for-all expression.";
var refExprSub = substitute(refExpr[2], subst[0], subst[1]);
if (semanticEq(refExprSub, currExpr))
return true;
return "All-x-Elim: Referenced step did not match current step after " + subst[1] + "/" + subst[0] + ".";
})
}),
"e." : new Rule({
name : "Exists",
type : "normal",
introduction : new Justifier(
{ stepRefs: ["num"], subst: true },
function(proof, step, part, steps, subst) {
var currStep = proof.steps[step];
var currExpr = currStep.getSentence();
var refExpr = proof.steps[steps[0]].getSentence();
if (currExpr[0] !== 'exists')
return "Exists-x-Intro: Current step is not an 'exists' expression.";
var refExprSub = substitute(refExpr, subst[1], subst[0]);
if (semanticEq(refExprSub, currExpr[2]))
return true;
return "Exists-x-Intro: Referenced step did not match current step after " + subst[1] + "/" + subst[0] + " substitution.";
}),
elimination : new Justifier(
{ stepRefs: ["num", "range"], subst: true },
function(proof, step, part, steps, subst) {
var currStep = proof.steps[step];
var currExpr = currStep.getSentence();
var refExpr = proof.steps[steps[0]].getSentence();
var startStep = proof.steps[steps[1][0]];
var startExpr = startStep.getSentence();
var scope = startStep.getScope(); // ex: [['x0','x'], ['y0', 'y'], ...], LIFO
var endExpr = proof.steps[steps[1][1]].getSentence();
if (refExpr[0] !== 'exists')
return "Exists-x-Elim: Referenced step is not an 'exists' expression.";
if (scope.length == 0 || scope[scope.length - 1] == null)
return "Exists-x-Elim: Range must be within an assumption scope (e.g., an x0 box).";
// check whether substition matches ref line with current line
var scopeVars = scope[scope.length-1];
var refExprSub = substitute(refExpr[2], subst[0], subst[1]);
if (semanticEq(refExprSub, startExpr)) {
if (semanticEq(endExpr, currExpr))
return true;
return "Exists-x-Elim: assumption ending step does not match current step.";
}
return "Exists-x-Elim: assumption beginning step doesn't match ref step for " + scopeVars[0] + ".";
})
}),
"=" : new Rule({
name : "Equality",
type : "normal",
introduction : new Justifier(
{ /* no params required */ },
function(proof, step, part, steps) {
var s = proof.steps[step].getSentence();
if (s[0] !== '=')
return "Equality-Intro: Current step is not an equality." + proof.steps[step].getSentence();
if (semanticEq(s[1], s[2]))
return true;
return "Equality-Intro: Left and right sides do not match.";
}),
elimination : new Justifier(
{ stepRefs: ["num", "num"] },
function(proof, step, part, steps) {
var equalityExpr = proof.steps[steps[0]].getSentence();
var elimExpr = proof.steps[steps[1]].getSentence();
var proposedResult = proof.steps[step].getSentence();
if (equalityExpr[0] !== '=')
return "Equality-Elim: First referenced step is not an equality.";
if (!semanticEq(elimExpr, proposedResult, equalityExpr[1], equalityExpr[2]))
return "Equality-Elim: Does not result in current step.";
return true;
})
}),
};
function substitute(startExpr, a, b, bound) {
u.debug("substitute", startExpr, a, b);
bound = bound ? bound : [];
var binOps = ["->", "and", "or", "<->", "="];
var unOps = ["not", "forall", "exists"];
// remove parens, which are basically stylistic no-ops
while (startExpr[0] === 'paren') startExpr = startExpr[1];
if (arrayContains(binOps, startExpr[0])) {
var leftSide = substitute(startExpr[1], a, b);
var rightSide = substitute(startExpr[2], a, b);
return [startExpr[0], leftSide, rightSide];
} else if (arrayContains(unOps, startExpr[0])) {
if (startExpr[0] === "forall" || startExpr[0] === "exists") {
bound = bound.slice(0);
bound.push(startExpr[1]);
return [startExpr[0], startExpr[1],
substitute(startExpr[2], a, b, bound)];
}
return [startExpr[0], substitute(startExpr[1], a, b, bound)];
} else if (startExpr[0] === 'id') {
if (startExpr.length === 2) { // our loverly base case
if (! arrayContains(bound, startExpr[1])) {
if (startExpr[1] === a)
return [startExpr[0], b];
}
return startExpr;
}
if (startExpr.length === 3) {
var newTerms = [];
for (var i=0; i<startExpr[2].length; i++) {
newTerms.push(substitute(startExpr[2][i], a, b, bound));
}
return [startExpr[0], startExpr[1], newTerms];
}
throw Error("Unexpected AST format.");
}
}
/**
* Determines whether two expressions are semantically equivalent
* under the given (and optional) substitution.
* a, b - abstract syntax trees of the expressions to be compared.
* suba, subb (optional) - does comparison after substituting suba in a with subb.
*/
function semanticEq(A, B, suba, subb) {
u.debug("semanticEq", A, B);
var bound = {}, sub;
if (suba) {
sub = true;
return _rec(A, B, {});
} else {
sub = false;
return _rec(A, B);
}
function _rec(a, b, bound) {
var binOps = ["->", "and", "or", "<->", "="];
var unOps = ["not"];
// if eq w/substitution, return true, otherwise continue
if (sub && semanticEq(a, suba)) {
if ((a[0] !== 'id' || !bound[a[1]]) && _rec(subb, b, bound)) return true;
}
if (arrayContains(binOps, a[0]) && a[0] === b[0]) {
if (_rec(a[1], b[1], bound) && _rec(a[2], b[2], bound)) {
return true;
}
return false;
} else if (arrayContains(unOps, a[0]) && a[0] === b[0]) {
if (_rec(a[1], b[1], bound)) {
return true;
}
return false;
} else if (a[0] === 'exists' || a[0] === 'forall' && a[0] === b[0]) {
var newb;
if (sub) {
newb = clone(bound);
newb[a[1]] = true;
}
if (_rec(a[2], b[2], newb)) {
return true;
}
return false;
} else if (a[0] === "id") {
if (b && a[1] !== b[1]) return false;
if (a.length == 2 && b.length == 2) {
return true;
}
if (a.length == 3 && b.length == 3) {
if (a[2].length != b[2].length) {
return false;
}
for (var i=0; i<a[2].length; i++) {
if (!_rec(a[2][i], b[2][i], bound)) {
return false;
}
}
return true;
}
}
return false;
}
}
function isContradiction(s) {
return (s[0] === 'id' && (s[1] === '_|_' || s[1] === 'contradiction'));
}
function arrayContains(arr, el) {
for (var i=0; i<arr.length; i++) {
if (arr[i] === el) return true;
}
return false;
}
function clone(obj) {
var newo = {};
for(var k in Object.keys(obj)) {
newo[k] = obj[k];
}
return newo;
}
if (typeof _dereq_ !== 'undefined' && typeof exports !== 'undefined') {
module.exports = rules;
}
},{"./justifier.js":1,"./rule.js":2,"./util":4}],4:[function(_dereq_,module,exports){
var util = {};
util.debug = function debug() {
if (typeof debugMode !== "undefined" && debugMode)
console.log.apply(console, Array.prototype.slice.call(arguments));
};
if (typeof _dereq_ !== 'undefined' && typeof exports !== 'undefined') {
module.exports = util;
}
},{}],5:[function(_dereq_,module,exports){
var rules = _dereq_("./rules");
var u = _dereq_("./util");
var Verifier = (function() {
var debugMode = false;
var obj = this;
obj.verifyFromAST = function(ast) {
var proof = preprocess(ast);
return obj.verify(proof);
};
// proof = { 1 : Statement(), 2 : Statement() ... };
obj.verify = function(proof) {
var result = { message : "Proof is valid.", valid : true };
for (var i=0; i<proof.steps.length; i++) {
obj.validateStatement(result, proof, i);
if (! result.valid) {
break;
}
}
return result;
};
obj.validateStatement = function validateStatement(result, proof, step) {
var stmt = proof.steps[step];
if (stmt[0] === 'error') {
result.valid = false;
result.message = "Proof invalid due to syntax errors.";
result.errorStep = step + 1;
return;
}
var why = stmt.getJustification();
var newv = null;
if (why[0].split('.').length == 2)
newv = why[0].split('.')[1];
var validator = obj.lookupValidator(why);
if (typeof validator === 'function') {
var part = why[2], lines = why[3];
var subst = null;
if (newv && why[4]) subst = [newv, why[4]];
var isValid = validator(proof, step, part, lines, subst);
if (isValid === true) {
result.valid = true;
} else {
result.valid = false;
result.message = isValid;
result.errorStep = step + 1;
result.errorSrcLoc = stmt.getMeta();
}
return;
} else if (typeof validator === "string") {
result.valid = false;
result.message = validator;
result.errorStep = step + 1;
result.errorSrcLoc = stmt.getMeta();
}
result.valid = false;
};
obj.lookupValidator = function lookupValidator(why) {
var name = why[0].toLowerCase();
if (name.split('.').length == 2)
name = name.split('.')[0] + ".";
var rule = rules[name];
if (!rule) return "Cannot find rule: " + name;
if (rule.getType() === "simple" || rule.getType() === "derived") {
var fn = rule.getSimpleVerifier();
if (!fn) throw new Error("Not implemented for " + name);
return fn.exec;
}
if (why[1]) {
var elimOrIntro = why[1].toLowerCase();
if ("introduction".indexOf(elimOrIntro) === 0) {
var fn = rule.getIntroVerifier();
if (!fn) throw new Error("Not implemented for " + name);
return fn.exec;
} else if ("elimination".indexOf(elimOrIntro) === 0) {
var fn = rule.getElimVerifier();
if (!fn) throw new Error("Not implemented for " + name);
return fn.exec;
}
return "Cannot determine elim/intro rule type from " + elimOrIntro;
}
return "Unrecognized rule: " + why[0] + " " + (why[1] ? why[1] : "") + (why[2] ? why[2] : "") + " " + (why[3] ? why[3] : "");
}
obj.preprocess = function preprocess(ast) {
var proof = { steps : [] };
obj.preprocessBox(proof, ast, 0, []);
return proof;
}
obj.preprocessBox = function preprocessBox(proof, ast, step, scope) {
for(var i=0; i<ast.length; i++) {
if (ast[i][0] === 'rule') {
proof.steps[step] = new Statement(ast[i][1], ast[i][2], scope, ast[i][3], i == 0, i == ast.length - 1);
step = step + 1;
} else if (ast[i][0] === 'folbox') {
var newScope = scope.slice(0)
newScope.push(ast[i][2][1]);
step = obj.preprocessBox(proof, ast[i][1], step, newScope);
} else if (ast[i][0] === 'box') {
var newScope = scope.slice(0)
newScope.push(null);
step = obj.preprocessBox(proof, ast[i][1], step, newScope);
} else if (ast[i][0] === 'error') {
proof.steps[step] = ast[i];
}
}
return step;
}
var Statement = function(sentenceAST, justificationAST, scope, loc, isFirst, isLast) {
this.isFirstStmt = function() { return isFirst; };
this.isLastStmt = function() { return isLast; };
this.getSentence = function getSentence() { return sentenceAST; };
this.getScope = function getScope() { return scope; }
this.getJustification = function getJustification() { return justificationAST; };
this.getMeta = function() { return loc; }
};
return obj;
})();
if (typeof _dereq_ !== 'undefined' && typeof exports !== 'undefined') {
exports.Verifier = Verifier;
}
},{"./rules":3,"./util":4}]},{},[5])
(5)
});