fitch-js 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.convertAsciiToSymbols = convertAsciiToSymbols;
7
+ exports.convertSymbolsToAscii = convertSymbolsToAscii;
8
+ exports.symbolMappings = void 0;
9
+ function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
10
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
11
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
12
+ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
13
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
14
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
15
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
16
+ /**
17
+ * ASCII to Unicode symbol conversion utilities for logical notation
18
+ * @module symbols
19
+ */
20
+
21
+ // ASCII to Unicode symbol mappings (sorted by length, longest first for correct replacement order)
22
+ var SYMBOL_MAPPINGS = [['<->', '↔'], ['/\\', '∧'], ['\\/', '∨'], ['_|_', '⊥'], ['->', '→'], ['~', '¬'], ['!', '¬'], ['&', '∧'], ['#', '⊥'], ['@', '∀'], ['$', '∃']];
23
+
24
+ /**
25
+ * Converts ASCII logical symbols to their Unicode equivalents
26
+ * @param {string} text - Text containing ASCII symbols
27
+ * @returns {string} Text with Unicode symbols
28
+ * @example
29
+ * convertAsciiToSymbols('P -> Q') // Returns 'P → Q'
30
+ * convertAsciiToSymbols('~P /\\ Q') // Returns '¬P ∧ Q'
31
+ * convertAsciiToSymbols('@x P(x)') // Returns '∀x P(x)'
32
+ */
33
+ function convertAsciiToSymbols(text) {
34
+ var result = text;
35
+ var _iterator = _createForOfIteratorHelper(SYMBOL_MAPPINGS),
36
+ _step;
37
+ try {
38
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
39
+ var _step$value = _slicedToArray(_step.value, 2),
40
+ ascii = _step$value[0],
41
+ symbol = _step$value[1];
42
+ var regex = new RegExp(ascii.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
43
+ result = result.replace(regex, symbol);
44
+ }
45
+ } catch (err) {
46
+ _iterator.e(err);
47
+ } finally {
48
+ _iterator.f();
49
+ }
50
+ return result;
51
+ }
52
+
53
+ /**
54
+ * Converts Unicode logical symbols back to ASCII equivalents
55
+ * @param {string} text - Text containing Unicode symbols
56
+ * @returns {string} Text with ASCII symbols
57
+ * @example
58
+ * convertSymbolsToAscii('P → Q') // Returns 'P -> Q'
59
+ * convertSymbolsToAscii('¬P ∧ Q') // Returns '~P /\\ Q'
60
+ */
61
+ function convertSymbolsToAscii(text) {
62
+ var result = text;
63
+ var _iterator2 = _createForOfIteratorHelper(SYMBOL_MAPPINGS),
64
+ _step2;
65
+ try {
66
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
67
+ var _step2$value = _slicedToArray(_step2.value, 2),
68
+ ascii = _step2$value[0],
69
+ symbol = _step2$value[1];
70
+ var regex = new RegExp(symbol.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
71
+ result = result.replace(regex, ascii);
72
+ }
73
+ } catch (err) {
74
+ _iterator2.e(err);
75
+ } finally {
76
+ _iterator2.f();
77
+ }
78
+ return result;
79
+ }
80
+
81
+ /**
82
+ * The symbol mappings array for external use
83
+ * @type {Array<[string, string]>}
84
+ */
85
+ var symbolMappings = exports.symbolMappings = SYMBOL_MAPPINGS;
@@ -0,0 +1,184 @@
1
+ "use strict";
2
+
3
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.RULE_REQUIREMENTS = exports.RULES = void 0;
8
+ exports.getAllTerms = getAllTerms;
9
+ var _parser = require("./parser.js");
10
+ var _RULE_REQUIREMENTS;
11
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
12
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
13
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
14
+ function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
15
+ function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
16
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
17
+ function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
18
+ function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
19
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
20
+ /**
21
+ * @typedef {Object} Formula
22
+ * @property {string} type - The type of formula ('atomic', 'compound', 'quantified', 'identity', 'splat')
23
+ * @property {string} [operator] - The main logical operator (for compound/quantified formulas)
24
+ * @property {string} [predicate] - The predicate symbol (for atomic formulas)
25
+ * @property {string[]} [terms] - The terms in the formula
26
+ * @property {Formula} [left] - The left subformula (for binary operators)
27
+ * @property {Formula} [right] - The right subformula (for binary operators)
28
+ * @property {string} [variable] - The bound variable (for quantified formulas)
29
+ * @property {Formula} [scope] - The scope of the quantifier (for quantified formulas)
30
+ */
31
+
32
+ /**
33
+ * @typedef {Object} ProofLine
34
+ * @property {number} lineNum - The line number in the proof
35
+ * @property {Formula} formula - The formula at this line
36
+ * @property {string} rule - The rule applied at this line
37
+ * @property {number[]} citations - Line numbers cited in the justification
38
+ * @property {Array<{start: number, end: number}>} subproofs - Subproof ranges cited
39
+ * @property {string[]} issues - Any issues with this line
40
+ * @property {number[]} location - Array indicating the scope depth
41
+ */
42
+
43
+ /**
44
+ * @typedef {Object} Proof
45
+ * @property {ProofLine[]} lines - The lines in the proof
46
+ * @property {number} numPremises - The number of premises
47
+ * @property {Formula} conclusion - The desired conclusion
48
+ */
49
+
50
+ /**
51
+ * @typedef {Object} ValidationResult
52
+ * @property {boolean} isValid - Whether the proof is valid
53
+ * @property {string[]} issues - Any issues with the proof
54
+ * @property {boolean} conclusionReached - Whether the conclusion was reached
55
+ */
56
+
57
+ /**
58
+ * Extracts all terms from a formula recursively
59
+ * @param {Formula} formula
60
+ * @returns {string[]}
61
+ */
62
+ function getAllTerms(formula) {
63
+ if (!formula) return [];
64
+ switch (formula.type) {
65
+ case 'atomic':
66
+ return formula.terms || [];
67
+ case 'compound':
68
+ if (formula.operator === _parser.OPERATORS.NOT) {
69
+ return formula.right ? getAllTerms(formula.right) : [];
70
+ }
71
+ return [].concat(_toConsumableArray(formula.left ? getAllTerms(formula.left) : []), _toConsumableArray(formula.right ? getAllTerms(formula.right) : []));
72
+ case 'quantified':
73
+ if (!formula.scope) return [];
74
+ return getAllTerms(formula.scope).filter(function (t) {
75
+ return t !== formula.variable;
76
+ });
77
+ case 'identity':
78
+ return formula.terms || [];
79
+ default:
80
+ return [];
81
+ }
82
+ }
83
+
84
+ // Rule definitions
85
+ var RULES = exports.RULES = {
86
+ // Propositional Logic Rules
87
+ CONJ_INTRO: '∧I',
88
+ CONJ_ELIM: '∧E',
89
+ DISJ_INTRO: '∨I',
90
+ DISJ_ELIM: '∨E',
91
+ COND_INTRO: '→I',
92
+ COND_ELIM: '→E',
93
+ BICOND_INTRO: '↔I',
94
+ BICOND_ELIM: '↔E',
95
+ NEG_INTRO: '¬I',
96
+ NEG_ELIM: '¬E',
97
+ CONTRA_INTRO: '⊥I',
98
+ CONTRA_ELIM: '⊥E',
99
+ LEM: 'LEM',
100
+ // First-Order Logic Rules
101
+ UNIV_INTRO: '∀I',
102
+ UNIV_ELIM: '∀E',
103
+ EXIST_INTRO: '∃I',
104
+ EXIST_ELIM: '∃E',
105
+ ID_INTRO: '=I',
106
+ ID_ELIM: '=E',
107
+ CQ: 'CQ',
108
+ // Structural Rules
109
+ PREMISE: 'Pr',
110
+ HYPOTHESIS: 'Hyp',
111
+ REIT: 'R'
112
+ };
113
+
114
+ // Rule citation requirements
115
+ var RULE_REQUIREMENTS = exports.RULE_REQUIREMENTS = (_RULE_REQUIREMENTS = {}, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_RULE_REQUIREMENTS, RULES.CONJ_INTRO, {
116
+ lines: 2,
117
+ subproofs: 0
118
+ }), RULES.CONJ_ELIM, {
119
+ lines: 1,
120
+ subproofs: 0
121
+ }), RULES.DISJ_INTRO, {
122
+ lines: 1,
123
+ subproofs: 0
124
+ }), RULES.DISJ_ELIM, {
125
+ lines: 1,
126
+ subproofs: 2
127
+ }), RULES.COND_INTRO, {
128
+ lines: 0,
129
+ subproofs: 1
130
+ }), RULES.COND_ELIM, {
131
+ lines: 2,
132
+ subproofs: 0
133
+ }), RULES.BICOND_INTRO, {
134
+ lines: 0,
135
+ subproofs: 2
136
+ }), RULES.BICOND_ELIM, {
137
+ lines: 2,
138
+ subproofs: 0
139
+ }), RULES.NEG_INTRO, {
140
+ lines: 0,
141
+ subproofs: 1
142
+ }), RULES.NEG_ELIM, {
143
+ lines: 2,
144
+ subproofs: 0
145
+ }), _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_RULE_REQUIREMENTS, RULES.CONTRA_INTRO, {
146
+ lines: 2,
147
+ subproofs: 0
148
+ }), RULES.CONTRA_ELIM, {
149
+ lines: 1,
150
+ subproofs: 0
151
+ }), RULES.LEM, {
152
+ lines: 0,
153
+ subproofs: 2
154
+ }), RULES.UNIV_INTRO, {
155
+ lines: 1,
156
+ subproofs: 0
157
+ }), RULES.UNIV_ELIM, {
158
+ lines: 1,
159
+ subproofs: 0
160
+ }), RULES.EXIST_INTRO, {
161
+ lines: 1,
162
+ subproofs: 0
163
+ }), RULES.EXIST_ELIM, {
164
+ lines: 1,
165
+ subproofs: 1
166
+ }), RULES.ID_INTRO, {
167
+ lines: 0,
168
+ subproofs: 0
169
+ }), RULES.ID_ELIM, {
170
+ lines: 2,
171
+ subproofs: 0
172
+ }), RULES.CQ, {
173
+ lines: 1,
174
+ subproofs: 0
175
+ }), _defineProperty(_defineProperty(_defineProperty(_RULE_REQUIREMENTS, RULES.PREMISE, {
176
+ lines: 0,
177
+ subproofs: 0
178
+ }), RULES.HYPOTHESIS, {
179
+ lines: 0,
180
+ subproofs: 0
181
+ }), RULES.REIT, {
182
+ lines: 1,
183
+ subproofs: 0
184
+ }));
@@ -0,0 +1,17 @@
1
+ // Public API exports
2
+
3
+ // Formula parsing
4
+ export { parseFormula, OPERATORS } from './parser.js';
5
+
6
+ // Proof validation
7
+ export { checkProof, formulasEqual, occursFree, substitute, getAvailableTerms } from './proofChecker.js';
8
+
9
+ // Rule definitions and utilities
10
+ export { RULES, RULE_REQUIREMENTS, getAllTerms } from './types.js';
11
+ export { isVariable, isLineAvailable, isValidCitation } from './ruleValidators.js';
12
+
13
+ // Symbol conversion utilities
14
+ export { convertAsciiToSymbols, convertSymbolsToAscii, symbolMappings } from './symbols.js';
15
+
16
+ // Text-format proof parsing
17
+ export { parseProofText, getLineForPosition, getDocumentLineForProofLine, RULE_MAP } from './proof/index.js';
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Formula parser for Fitch-style proofs
3
+ */
4
+
5
+ export var OPERATORS = {
6
+ AND: '∧',
7
+ OR: '∨',
8
+ NOT: '¬',
9
+ IMPLIES: '→',
10
+ IFF: '↔',
11
+ FORALL: '∀',
12
+ EXISTS: '∃',
13
+ EQUALS: '=',
14
+ CONTRA: '⊥'
15
+ };
16
+
17
+ // Pre-compute operator set for O(1) lookup
18
+ var OPERATOR_SET = new Set(Object.values(OPERATORS));
19
+
20
+ /**
21
+ * Tokenizes a formula string into an array of tokens
22
+ * @param {string} formula - The formula string to tokenize
23
+ * @returns {string[]} Array of tokens
24
+ */
25
+ function tokenize(formula) {
26
+ var tokens = [];
27
+ var chars = [];
28
+ for (var i = 0; i < formula.length; i++) {
29
+ var _char = formula[i];
30
+ if (_char === ' ') {
31
+ if (chars.length > 0) {
32
+ tokens.push(chars.join(''));
33
+ chars.length = 0;
34
+ }
35
+ continue;
36
+ }
37
+ if ('(),'.includes(_char)) {
38
+ if (chars.length > 0) {
39
+ tokens.push(chars.join(''));
40
+ chars.length = 0;
41
+ }
42
+ tokens.push(_char);
43
+ continue;
44
+ }
45
+ if (OPERATOR_SET.has(_char)) {
46
+ if (chars.length > 0) {
47
+ tokens.push(chars.join(''));
48
+ chars.length = 0;
49
+ }
50
+ tokens.push(_char);
51
+ continue;
52
+ }
53
+ chars.push(_char);
54
+ }
55
+ if (chars.length > 0) {
56
+ tokens.push(chars.join(''));
57
+ }
58
+ return tokens;
59
+ }
60
+
61
+ /**
62
+ * Parses a formula string into a Formula object
63
+ * @param {string} formulaStr - The formula string to parse
64
+ * @returns {import('./types').Formula} The parsed formula
65
+ */
66
+ export function parseFormula(formulaStr) {
67
+ var tokens = tokenize(formulaStr);
68
+ var pos = 0;
69
+ function parseAtom() {
70
+ var token = tokens[pos];
71
+ pos++;
72
+
73
+ // Check for contradiction
74
+ if (token === OPERATORS.CONTRA) {
75
+ return {
76
+ type: 'splat'
77
+ };
78
+ }
79
+
80
+ // Check if it's followed by terms in parentheses
81
+ if (pos < tokens.length && tokens[pos] === '(') {
82
+ pos++; // Skip opening parenthesis
83
+ var terms = [];
84
+ while (pos < tokens.length && tokens[pos] !== ')') {
85
+ if (tokens[pos] !== ',') {
86
+ terms.push(tokens[pos]);
87
+ }
88
+ pos++;
89
+ }
90
+ if (pos >= tokens.length || tokens[pos] !== ')') {
91
+ throw new Error('Missing closing parenthesis');
92
+ }
93
+ pos++; // Skip closing parenthesis
94
+
95
+ return {
96
+ type: 'atomic',
97
+ predicate: token,
98
+ terms: terms
99
+ };
100
+ }
101
+
102
+ // Single term atomic formula (propositional variable)
103
+ return {
104
+ type: 'atomic',
105
+ predicate: token,
106
+ terms: []
107
+ };
108
+ }
109
+ function parseQuantified() {
110
+ var operator = tokens[pos];
111
+ pos++;
112
+ if (pos >= tokens.length) {
113
+ throw new Error('Incomplete quantified formula');
114
+ }
115
+ var variable = tokens[pos];
116
+ pos++;
117
+ var scope = parseFormula(tokens.slice(pos).join(' '));
118
+ pos = tokens.length;
119
+ return {
120
+ type: 'quantified',
121
+ operator: operator,
122
+ variable: variable,
123
+ scope: scope
124
+ };
125
+ }
126
+ function parseCompound() {
127
+ if (tokens[pos] === '(') {
128
+ pos++; // Skip opening parenthesis
129
+ var formula = parseFormula(tokens.slice(pos).join(' '));
130
+
131
+ // Find matching closing parenthesis
132
+ var parenCount = 1;
133
+ var endPos = pos;
134
+ while (parenCount > 0 && endPos < tokens.length) {
135
+ endPos++;
136
+ if (tokens[endPos] === '(') parenCount++;
137
+ if (tokens[endPos] === ')') parenCount--;
138
+ }
139
+ if (parenCount > 0) {
140
+ throw new Error('Unmatched parentheses');
141
+ }
142
+ pos = endPos + 1;
143
+
144
+ // Check if there's an operator after the parentheses
145
+ if (pos < tokens.length && OPERATOR_SET.has(tokens[pos])) {
146
+ var _operator = tokens[pos];
147
+ pos++;
148
+ var _right = parseFormula(tokens.slice(pos).join(' '));
149
+ pos = tokens.length;
150
+ return {
151
+ type: 'compound',
152
+ operator: _operator,
153
+ left: formula,
154
+ right: _right
155
+ };
156
+ }
157
+ return formula;
158
+ }
159
+ if (tokens[pos] === OPERATORS.NOT) {
160
+ pos++;
161
+ return {
162
+ type: 'compound',
163
+ operator: OPERATORS.NOT,
164
+ right: parseFormula(tokens.slice(pos).join(' '))
165
+ };
166
+ }
167
+ var left = parseAtom();
168
+ if (pos >= tokens.length) {
169
+ return left;
170
+ }
171
+ var operator = tokens[pos];
172
+ if (!OPERATOR_SET.has(operator)) {
173
+ return left;
174
+ }
175
+ pos++;
176
+ var right = parseFormula(tokens.slice(pos).join(' '));
177
+ pos = tokens.length;
178
+ return {
179
+ type: 'compound',
180
+ operator: operator,
181
+ left: left,
182
+ right: right
183
+ };
184
+ }
185
+
186
+ // Start parsing
187
+ if (pos >= tokens.length) {
188
+ throw new Error('Empty formula');
189
+ }
190
+
191
+ // Check for quantifiers
192
+ if (tokens[pos] === OPERATORS.FORALL || tokens[pos] === OPERATORS.EXISTS) {
193
+ return parseQuantified();
194
+ }
195
+
196
+ // Check for identity formulas
197
+ if (tokens.length >= 3 && tokens[1] === OPERATORS.EQUALS) {
198
+ return {
199
+ type: 'identity',
200
+ terms: [tokens[0], tokens[2]]
201
+ };
202
+ }
203
+ return parseCompound();
204
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Proof text parsing module
3
+ * @module proof
4
+ */
5
+
6
+ export { parseProofText, getLineForPosition, getDocumentLineForProofLine } from './textParser.js';
7
+ export { RULE_MAP } from './types.js';