propositional
Version:
Propositional logic symbolic computation library
130 lines (129 loc) • 6.29 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.toCNF = exports.CNFFormula = void 0;
const __1 = require("..");
const generate_1 = require("../syntax/generate");
const AST = __importStar(require("../syntax/ast"));
const token_1 = require("../syntax/token");
const simplify_1 = require("../transform/simplify");
const dpll_1 = require("./dpll");
class CNFFormula extends __1.Formula {
cnf() {
return this;
}
simplify() {
return this;
}
/**
* Run the DPLL algorithm on the formula to check for a set of conditions that will satisfy the formula.
* @returns A set of conditions for which the formula will evaluate to true - In the same format as is required for `evaluate`.
*/
dpll() {
return (0, dpll_1.dpll)(this);
}
}
exports.CNFFormula = CNFFormula;
;
function toCNF(expression) {
let transformedAst = (0, simplify_1.simplify)(distributeOrOverAnd(moveNegationInwards(expandNonCNFSymbols(expression))));
return new CNFFormula(transformedAst);
}
exports.toCNF = toCNF;
function expandNonCNFSymbols(expression) {
switch (true) {
case expression instanceof AST.BinaryExpression:
let left = expandNonCNFSymbols(expression.left);
let right = expandNonCNFSymbols(expression.right);
switch (expression.operator.type) {
case token_1.TokenType.AND:
case token_1.TokenType.OR:
return new AST.BinaryExpression(left, expression.operator, right);
case token_1.TokenType.XOR:
return (0, generate_1.or)((0, generate_1.and)(left, (0, generate_1.not)(right)), (0, generate_1.and)((0, generate_1.not)(left), right));
case token_1.TokenType.IF:
return (0, generate_1.or)((0, generate_1.not)(left), right);
case token_1.TokenType.IFF:
return (0, generate_1.or)((0, generate_1.and)(left, right), (0, generate_1.and)((0, generate_1.not)(left), (0, generate_1.not)(right)));
}
throw new Error("What");
case expression instanceof AST.UnaryExpression:
return new AST.UnaryExpression(expression.operator, expandNonCNFSymbols(expression.inner));
case expression instanceof AST.Literal:
return expression;
}
}
// assumes an expression returned by expandNonCNFSymbols
function moveNegationInwards(expression) {
switch (true) {
case expression instanceof AST.UnaryExpression:
let { inner } = expression;
switch (true) {
case inner instanceof AST.UnaryExpression:
// double negation, remove
return moveNegationInwards(inner.inner);
case inner instanceof AST.BinaryExpression:
// Apply demorgan's law
let notLeft = moveNegationInwards((0, generate_1.not)(inner.left));
let notRight = moveNegationInwards((0, generate_1.not)(inner.right));
switch (inner.operator.type) {
case token_1.TokenType.AND:
return (0, generate_1.or)(notLeft, notRight);
case token_1.TokenType.OR:
return (0, generate_1.and)(notLeft, notRight);
}
case inner instanceof AST.Literal:
// Nothing to simplify
return expression;
}
throw new Error("what");
case expression instanceof AST.BinaryExpression:
return new AST.BinaryExpression(moveNegationInwards(expression.left), expression.operator, moveNegationInwards(expression.right));
case expression instanceof AST.Literal:
return expression;
}
}
// assumes an expression returned by moveNegationInwards
function distributeOrOverAnd(expression) {
switch (true) {
case expression instanceof AST.BinaryExpression:
if (expression.operator.type == token_1.TokenType.AND)
return (0, generate_1.and)(distributeOrOverAnd(expression.left), distributeOrOverAnd(expression.right));
let { left, right } = expression;
if (left instanceof AST.BinaryExpression && left.operator.type == token_1.TokenType.AND) {
// (a and b) or (c) -> (a or c) and (b or c)
return (0, generate_1.and)(distributeOrOverAnd((0, generate_1.or)(left.left, right)), distributeOrOverAnd((0, generate_1.or)(left.right, right)));
}
if (right instanceof AST.BinaryExpression && right.operator.type == token_1.TokenType.AND) {
// (a) or (b and c) -> (a or b) and (a and c)
return (0, generate_1.and)(distributeOrOverAnd((0, generate_1.or)(left, right.left)), distributeOrOverAnd((0, generate_1.or)(left, right.right)));
}
// Neither left or right are binary expressions, so already simplified
case expression instanceof AST.UnaryExpression:
// Negations have been moved inwards and are therefore already simplified
case expression instanceof AST.Literal:
return expression;
}
}