1 |
|
2 | var folproofWeb = (function() {
|
3 | var debugMode = false;
|
4 | var obj = {};
|
5 | var defaultOpts = {
|
6 | parentheses : "user"
|
7 | };
|
8 |
|
9 |
|
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 = "∀"; break;
|
56 | case "exists": op = "∃";
|
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 = "↔"; break;
|
74 | case "->": op = "→"; break;
|
75 | case "and": op = "∧"; break;
|
76 | case "or": op = "∨"; 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("¬");
|
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 {
|
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 | "_|_" : "⊥", "contradiction" : "⊥"
|
156 | };
|
157 | var parts = t.match(/(.*?)(\d+)?$/);
|
158 | var sym = parts[1];
|
159 |
|
160 |
|
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 | })();
|