tfl-js
Version:
A TypeScript library for parsing and evaluating propositional logic formulas
113 lines • 3.72 kB
JavaScript
"use strict";
/**
* Core types for formula representation
* Inspired by Carnap's PurePropositional implementation
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Compound = exports.Operator = exports.Atom = void 0;
/**
* Atomic proposition (e.g., p, q, r)
*/
class Atom {
constructor(name) {
this.name = name;
}
evaluate(valuation) {
if (!(this.name in valuation)) {
throw new Error(`No valuation provided for atomic proposition ${this.name}`);
}
return valuation[this.name];
}
subformulas() {
return [this];
}
atoms() {
return [this.name];
}
toString() {
return this.name;
}
}
exports.Atom = Atom;
/**
* Logical operators (¬, ∧, ∨, →, ↔)
*/
var Operator;
(function (Operator) {
Operator["NOT"] = "\u00AC";
Operator["AND"] = "\u2227";
Operator["OR"] = "\u2228";
Operator["IF"] = "\u2192";
Operator["IFF"] = "\u2194";
})(Operator || (exports.Operator = Operator = {}));
/**
* Compound formula with a logical operator
*/
class Compound {
constructor(operator, left, right) {
this.operator = operator;
this.left = left;
this.right = right;
// Validate that binary operators have two operands
if (operator !== Operator.NOT && !right) {
throw new Error(`Binary operator ${operator} requires two operands`);
}
// Validate that NOT has only one operand
if (operator === Operator.NOT && right) {
throw new Error(`Unary operator ${operator} cannot have two operands`);
}
}
evaluate(valuation) {
switch (this.operator) {
case Operator.AND:
// For AND, both sides must be true
return this.left.evaluate(valuation) && (this.right?.evaluate(valuation) ?? false);
case Operator.OR:
// For OR, either side must be true
return this.left.evaluate(valuation) || (this.right?.evaluate(valuation) ?? false);
case Operator.IF:
// For implication p → q, if p is true then q must be true
const antecedent = this.left.evaluate(valuation);
return antecedent ? (this.right?.evaluate(valuation) ?? false) : true;
case Operator.IFF:
// For biconditional, both sides must have the same value
return this.left.evaluate(valuation) === (this.right?.evaluate(valuation) ?? false);
case Operator.NOT:
// For negation, the operand must be false
return !this.left.evaluate(valuation);
default:
throw new Error(`Unknown operator: ${this.operator}`);
}
}
subformulas() {
const subs = new Set();
// Add left subformulas
for (const sub of this.left.subformulas()) {
subs.add(sub);
}
// Add right subformulas if present
if (this.right) {
for (const sub of this.right.subformulas()) {
subs.add(sub);
}
}
// Add the formula itself last
subs.add(this);
return Array.from(subs);
}
atoms() {
const atoms = [...this.left.atoms()];
if (this.right) {
atoms.push(...this.right.atoms());
}
return [...new Set(atoms)];
}
toString() {
if (this.operator === Operator.NOT) {
return `${this.operator}${this.left.toString()}`;
}
return `(${this.left.toString()} ${this.operator} ${this.right.toString()})`;
}
}
exports.Compound = Compound;
//# sourceMappingURL=formula.js.map