forked from cdibbs/folproof
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfolproof-web.js
202 lines (181 loc) · 5.85 KB
/
folproof-web.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// Used for rendering fol.js proofs to HTML. Requires JQuery.
var folproofWeb = (function() {
var debugMode = false;
var obj = {};
var defaultOpts = {
parentheses : "user"
};
// Top-level AST will be an array of rules and boxes. Render them to HTML. :-)
obj.render = function(ast, opts) {
var options = $.extend({}, defaultOpts, opts);
var dom = $("<div></div>");
if (!ast) return dom;
renderRules(dom, ast, 1, options);
return dom;
}
function renderRules(dom, ast, line, options) {
for (var i=0; i<ast.length; i++) {
debug(ast[i]);
if (ast[i][0] === 'rule') {
line = renderRule(dom, ast[i], line, options);
} else if (ast[i][0] === 'box') {
line = renderSimpleBox(dom, ast[i], line, options);
} else if (ast[i][0] === 'folbox') {
line = renderFOLBox(dom, ast[i], line, options);
} else if (ast[i][0] === 'error') {
line = renderSyntaxError(dom, ast[i], line, options);
}
}
return line;
};
function renderRule(dom, ast, line, options) {
var nest = $("<div class='rule'></div>");
nest.append("<span class='lineno'>" + line + "</span>");
nest.append(renderClause(ast[1], options));
nest.append(renderJustification(ast[2], options));
dom.append(nest);
return line + 1;
}
function renderSyntaxError(dom, ast, line, options) {
var nest = $("<div class='folproof-error'></div>");
nest.append("<span class='lineno'>" + line + "</span>");
nest.append("Syntax error near: ", ast[1]);
dom.append(nest);
return line + 1;
}
function renderClause(ast, options) {
var c, l, r, op, reqParens;
switch(ast[0]) {
case "forall": op = "∀"; break;
case "exists": op = "∃";
}
if (op) {
var wrapper = $("<span class='folproof-quantifier'></span>");
wrapper.append(op);
t = renderTerm(ast[1], options);
wrapper.append(t," ");
c = renderClause(ast[2], options);
wrapper.append(c);
if (requireParens(ast[0], ast[2], true, options))
wrapper.append("(", c, ")");
else wrapper.append(c);
return wrapper;
}
switch(ast[0]) {
case "iff": op = "↔"; break;
case "->": op = "→"; break;
case "and": op = "∧"; break;
case "or": op = "∨"; break;
case "=": op = "=";
}
if (op) {
debug(ast[1], ast[2]);
l = renderClause(ast[1], options);
if (requireParens(ast[0], ast[1], true, options))
l.prepend("(").append(")");
r = renderClause(ast[2], options);
if (requireParens(ast[0], ast[2], false, options))
r.prepend("(").append(")");
l.append(" ", op, " ").append(r);
return l;
}
if (ast[0] === "id") {
return renderTerm(ast, options);
} else if (ast[0] === "not") {
l = renderClause(ast[1], options);
if (requireParens(ast[0], ast[1], true, options))
l.prepend("(").append(")");
l.prepend("¬");
return l;
}
return renderTerm(ast);
}
var opOrder = { "not": 1, "=": 1, "forall": 2, "exists": 2, "and":3, "or":4, "->":5, "iff":6 };
function requireParens(parentOp, ast, leftTerm, options) {
if (ast[0] === 'id') return false;
if (options.parentheses === "user") {
return ast.userParens;
} else if (options.parentheses === "minimal") {
if (opOrder[parentOp] == opOrder[ast[0]] && leftTerm) return false;
else if (opOrder[parentOp] < opOrder[ast[0]]) return true;
else if (opOrder[parentOp] > opOrder[ast[0]] && !leftTerm) return false;
return true;
}
return true;
}
function unaryOp(op) {
return op === "not" || op === "forall" || op === "exists";
}
function binaryOp(op) {
return op === "iff" || op === "->" || op === "and" || op === "or" || op === "=";
}
var infixTerms = ['='];
function renderTerm(ast, options) {
if (ast instanceof Array) {
if (ast.length === 2) {
return $("<span></span>").append(renderSimpleTerm(ast[1], options));
} else if (ast.length >= 3) {
var term = $("<span class='term parameterized'></span>");
if ($.inArray(ast[1], infixTerms) == -1) {
term.append(renderSimpleTerm(ast[1], options), "(");
for (var i=0; i<ast[2].length; i++) {
term.append(renderSimpleTerm(ast[2][i][1], options));
if (i < ast[2].length-1) term.append(", ");
}
term.append(")");
} else { // infix
term.append(ast[2][0][1]," ", ast[1], " ", ast[2][1][1]);
}
return term;
}
} else {
return renderSimpleTerm(ast, options);
}
}
function renderSimpleTerm(t, options) {
var symbols = "alpha beta gamma delta epsilon zeta eta theta iota kappa lambda mu nu xi omicron pi rho sigma tau upsilon phi chi psi omega".split(" ");
var others = {
"_|_" : "⊥", "contradiction" : "⊥"
};
var parts = t.match(/(.*?)(\d+)?$/);
var sym = parts[1];
// Ω and ω are different. &OmEGa; does not exist, hence the quirkiness
// to allow users to distinguish between lower and uppercase greek letters.
if ($.inArray(sym[0].toLowerCase() + sym.substr(1), symbols) !== -1) {
sym = "&" + sym + ";";
} else if (others[sym]) {
sym = others[sym];
}
if (parts[2]) {
return $("<span class='special-symbol'>" + sym + "<sub>" + parts[2] + "</sub></span>");
} else {
return $("<span class='symbol'>" + sym + "</span>");
}
}
function renderJustification(ast, options) {
var nest = $("<div class='justification'></div>");
nest.append(ast[0], " ", ast[1]);
if (ast[2]) nest.append(ast[2]);
if (ast[3])
nest.append(" ", ast[3].join(", "));
return nest;
}
function renderSimpleBox(dom, ast, line, options) {
var nest = $("<div class='simple-box'></div>");
var lines = renderRules(nest, ast[1], line, options);
dom.append(nest);
return lines;
}
function renderFOLBox(dom, ast, line) {
var nest = $("<div class='FOL-box'></div>");
nest.append(renderSimpleTerm(ast[2][1]));
var line = renderRules(nest, ast[1], line, options);
dom.append(nest);
return line;
}
return obj;
function debug() {
if (debugMode)
console.log.apply(console, Array.prototype.slice.call(arguments, 0));
}
})();