fregejs
Version:
A propositional logic library written in Typescript
126 lines (108 loc) • 3.62 kB
text/typescript
import { InferenceException } from 'exceptions';
import {
Proof,
Formula,
inferenceRulesMap,
MappedProof,
ProofItemInferred,
} from 'types';
import {
isProofItemInferred,
parseToFormulaString,
isHypothesis,
isEndOfHypothesis,
} from 'utils';
/**
* Class responsible for checking the syntactic validity of proofs.
*/
export class ProofChecker {
/**
* Checks the given proof for validity.
*
* @param {Proof} proof - The proof to be checked.
* @returns {boolean} - `true` if the proof is valid, `InferenceError` or `Error` otherwise.
*/
static check(proof: Proof) {
const mappedProof = ProofChecker.createMappedProof(proof);
let premises: Array<string | Formula> = [];
let conclusion: string;
Object.keys(mappedProof).forEach((_, idx) => {
const item = mappedProof[idx + 1];
if (isProofItemInferred(item)) {
const [requiredItens, inferenceRule] = item.from;
ProofChecker.validateScope(requiredItens, item, mappedProof);
inferenceRulesMap[inferenceRule](item, proof);
console.log(
'\x1b[32m',
`Applied ${inferenceRule} with success at line ${item.id} ✔️`
);
}
if (item.type === 'Premise') {
premises.push(item.expression);
}
if (item.type === 'Conclusion') {
conclusion = parseToFormulaString(item.expression as Formula);
}
premises = premises.map((formula) => {
return parseToFormulaString(formula as Formula);
});
});
console.log('\x1b[0m', `\n{ ${premises.join(', ')} } ⊢ ${conclusion}`);
return true;
}
/**
* Creates a mapped version of the proof, with the representation of the scopes of each item.
* @param {Proof} proof - The proof to be mapped.
* @returns {MappedProof} - The mapped proof.
*/
private static createMappedProof(proof: Proof) {
let layerIdx = 0;
let blockIdx = 0;
Object.keys(proof).forEach((_, idx) => {
idx++;
const item = proof[idx];
if (isHypothesis(item)) {
blockIdx++;
layerIdx++;
proof[idx]['scopeIdx'] = [layerIdx, blockIdx];
} else if (isEndOfHypothesis(item)) {
const itemBlockIdx = proof[item.hypothesisId]['scopeIdx'][1];
proof[idx]['scopeIdx'] = [layerIdx, itemBlockIdx];
layerIdx--;
} else {
proof[idx]['scopeIdx'] = layerIdx === 0 ? [0, 0] : [layerIdx, blockIdx];
if (layerIdx !== 0 && proof[idx].type === 'Conclusion')
throw new Error(
`(Line ${idx}): You cannot put your conclusion inside of a hypothesis.`
);
}
});
return proof as MappedProof;
}
/**
* Validates the scope of inferred items.
* @param {number[]} requiredItens - An array of required item IDs.
* @param {ProofItemInferred} item - The inferred proof item to be validated.
* @param {MappedProof} mappedProof - The mapped proof.
*/
private static validateScope(
requiredItens: number[],
item: ProofItemInferred,
mappedProof: MappedProof
) {
requiredItens.forEach((requiredItemId) => {
const [actualLayer, actualBlock] = mappedProof[item.id].scopeIdx;
const [requiredLayer, requiredBlock] =
mappedProof[requiredItemId].scopeIdx;
const [, inferenceRule] = item.from;
if (
actualLayer < requiredLayer &&
actualBlock != requiredBlock &&
inferenceRule != 'Conditional Proof'
)
throw new InferenceException(
`Scope Error: cannot access line ${requiredItemId} by the ${item.id} line.`
);
});
}
}