UNPKG

propositional

Version:

Propositional logic symbolic computation library

130 lines (129 loc) 6.29 kB
"use strict"; 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; } }