UNPKG

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