function truthTableFunction(bits = [1, 0, 1, 1]) {
function truthTable(exp) {
const data = [];
const sub = [];
const f = logicParser(exp);
const text = textFormula(f);
const subText = {};
const subPush = (f) => {
if (subText.hasOwnProperty(f.text)) return;
sub.push(f);
subText[f.text] = true;
};
function trav(f) {
if (f.node === "Literal") {
subPush(f);
return;
}
if (f.node === "Not") {
trav(f.child[0]);
subPush(f);
return;
}
trav(f.child[0]);
trav(f.child[1]);
subPush(f);
}
trav(f);
const alignment = {};
sub.forEach((f) => (alignment[f.text] = "center"));
let vars = variables(f);
for (let v of values(vars)) {
const row = {};
for (let i = 0; i < sub.length; ++i) {
row[sub[i].text] = evalFormula(sub[i], v) ? "T" : "F";
}
data.push(row);
}
return Inputs.table(data, { layout: "auto", align: alignment });
}
function textFormula(f) {
const precedence = {
Or: 2,
And: 2,
Implies: 3,
WeakImply: 3,
Not: 1,
Literal: 0
};
const paren = (f, op) =>
precedence[f.node] >= precedence[op]
? "(" + textFormula(f) + ")"
: textFormula(f);
if (f.text === undefined) {
switch (f.node) {
case "Literal":
f.text = f.value;
break;
case "Or":
f.text = paren(f.child[0], "Or") + "∨" + paren(f.child[1], "Or");
break;
case "Xor":
f.text = paren(f.child[0], "Xor") + " ⊕" + paren(f.child[1], "Xor");
break;
case "And":
f.text = paren(f.child[0], "And") + "∧" + paren(f.child[1], "And");
break;
case "Implies":
f.text =
paren(f.child[0], "Implies") + "⇒" + paren(f.child[1], "Implies");
break;
case "Equiv":
f.text =
paren(f.child[0], "Equiv") + "⇔" + paren(f.child[1], "Equiv");
break;
case "Not":
f.text = "¬" + paren(f.child[0], "Not");
break;
case "WeakImply":
f.text =
paren(f.child[0], "WeakImply") +
"→" +
paren(f.child[1], "WeakImply");
break;
}
}
return f.text;
}
function* values(vars) {
const n = vars.length;
for (let i = 0; i < 2 ** n; ++i) {
let vl = {};
let b = 1;
for (let j = n - 1; j >= 0; --j, b *= 2) {
vl[vars[j]] = (b & i) == 0;
}
yield vl;
}
}
function variables(f) {
const v = {};
function vr(f) {
if (f.node === "Literal") {
v[f.value] = true;
return;
}
f.child.forEach((c) => vr(c));
}
vr(f, v);
return Object.keys(v).sort();
}
function evalFormula(f, v) {
if (typeof f === "string") f = logicParser(f);
function ef(f) {
if (f.node === "Literal") return v[f.value];
if (f.node === "And") return ef(f.child[0]) && ef(f.child[1]);
if (f.node === "Or") return ef(f.child[0]) || ef(f.child[1]);
if (f.node === "Implies") return !ef(f.child[0]) || ef(f.child[1]);
if (f.node === "Equiv") return ef(f.child[0]) === ef(f.child[1]);
if (f.node === "Xor") {
const a = ef(f.child[0]);
const b = ef(f.child[1]);
return (a || b) && !(a && b);
}
if (f.node === "Not") return !ef(f.child[0]);
if (f.node === "WeakImply") {
return explicit_formula(...bits)(ef(f.child[0]), ef(f.child[1]));
}
}
return ef(f);
}
const expParserData = {
tokens: {
Space: /\s+/,
Literal: /\w+/,
Or: [/∨/, /\|/],
And: [/∧/, /&/],
Implies: [/⇒/, /=>/],
WeakImply: [/→/, /->/],
Equiv: [/⇔/, /<=>/],
Xor: [/⊕/, /\^/],
Not: [/¬/, /\!/],
LeftParen: /\(/,
RightParen: /\)/
},
binaryOp: ["Or", "And", "Implies", "WeakImply", "Equiv", "Xor"],
precedence: {
Or: 3,
And: 3,
Xor: 3,
Implies: 2,
WeakImply: 2,
Not: 4,
Equiv: 1
},
associativity: { Implies: "Right", Equiv: "Right", WeakImply: "Right" },
unaryOp: ["Not"],
terminal: ["Literal"]
};
let logicParser = expressionParser(expParserData);
function tokenizer(lex) {
const lexa = Object.entries(lex);
lex = [];
lexa.forEach(([a, b]) => {
if (b instanceof RegExp)
lex.push([new RegExp("^(" + b.source + ")(.*)"), a]);
else
b.forEach((t) => lex.push([new RegExp("^(" + t.source + ")(.*)"), a]));
});
return (str) => {
let s = str;
const r = [];
while (s) {
let flag = true;
for (let i = 0; i < lex.length; ++i) {
let m = s.match(lex[i][0]);
if (m) {
const l = lex[i][1];
r.push([l, m[1]]);
s = m[2];
flag = false;
break;
}
}
if (flag) throw "SyntaxError: invalid token";
}
r.push(["End", ""]);
return r;
};
}
function expressionParser(data) {
const to = tokenizer(data.tokens);
const binaryOp = {};
data.binaryOp.forEach((op) => (binaryOp[op] = true));
const isBinary = (tok) => binaryOp.hasOwnProperty(tok);
const unaryOp = {};
data.unaryOp.forEach((op) => (unaryOp[op] = true));
const isUnary = (tok) => unaryOp.hasOwnProperty(tok);
const terminal = {};
data.terminal.forEach((op) => (terminal[op] = true));
const isTerminal = (tok) => terminal.hasOwnProperty(tok);
const prec = (tok) => data.precedence[tok];
const associativity = (tok) =>
!data.associativity.hasOwnProperty(tok)
? "Left"
: data.associativity[tok];
return function (str) {
const l = to(str).filter(([tok, _]) => tok !== "Space");
let it = 0;
const next = () => l[it][0];
const value = () => l[it][1];
const consume = () => (it = it + 1);
const expect = (tok) => {
if (next() === tok) consume();
else throw "SyntaxError: unexpected token";
};
let Eparser, Exp, P;
const binary = (tok) => {
return tok;
};
const unary = (tok) => {
if (isBinary(tok)) return "Unary" + tok;
else return tok;
};
Eparser = () => {
const t = Exp(0);
expect("End");
return t;
};
Exp = (p) => {
let t = P();
while (isBinary(next()) && prec(binary(next())) >= p) {
const op = binary(next());
consume();
const q = prec(op) + (associativity(op) === "Right" ? 0 : 1);
const t1 = Exp(q);
t = { node: op, child: [t, t1] };
}
return t;
};
P = () => {
if (isUnary(next())) {
const op = unary(next());
consume();
const q = prec(op);
const t = Exp(q);
return { node: op, child: [t] };
} else if (next() === "LeftParen") {
consume();
const t = Exp(0);
expect("RightParen");
return t;
} else if (isTerminal(next())) {
const t = { node: next(), value: value() };
consume();
return t;
} else throw "SyntaxError: ";
};
return Eparser();
};
}
function explicit_formula(z1, z2, z3, z4) {
let f = (p, q) => {
if (p && q) {
return Boolean(z1);
} else if (p & !q) {
return Boolean(z2);
} else if (!p & q) {
return Boolean(z3);
} else if (!p & !q) {
return Boolean(z4);
}
};
return f;
}
return truthTable;
}