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.
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2025 Lagomorph Labs
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,401 @@
1
+ # Fitch-JS
2
+
3
+ **We are not affiliated with the [Open Logic Project](https://openlogicproject.org/) or [Carnap](https://carnap.io/) in any way. (Currently)**
4
+
5
+ This is a JavaScript library for validating Fitch-style natural deduction proofs. It provides functionality to check the validity of logical proofs in both propositional and first-order logic entirely in the browser, with no server-side dependencies.
6
+
7
+ If you're familiar with [Carnap](https://carnap.io/) or [Logic Penguin](https://github.com/frabjous/logicpenguin), you understand what we're broadly working towards.
8
+
9
+ However, unlike these platforms (which pass the proof to a server for validation), Fitch-JS is designed to be a standalone library that can be integrated into any JavaScript application. This means you can:
10
+
11
+ - Build your own proof interface without being tied to a specific UI implementation
12
+ - Run proof validation entirely client-side with no network latency
13
+ - Use it in offline-capable web apps
14
+ - Integrate it into desktop applications via frameworks like Electron
15
+ - Include it in educational software and interactive textbooks (scorm, xapi, etc.)
16
+
17
+ Want to see a demo in action? Check out [Axiom ILE (Integrated Logic Environment)](https://lagomor.ph/axiom/)!
18
+
19
+ The idea is by seperating the validator into a mini language-server, we can build a rich ecosystem of tools that can be used to build proof interfaces, homework checkers, etc.
20
+
21
+ Our goal for project "Completeness" is to support the full set of logical rules and validation requirements as presented in [forall x Calgary](https://forallx.openlogicproject.org/) as solved by [fitch-checker](https://github.com/frabjous/fitch-checker).
22
+
23
+ **This project is in very early Alpha. Many things may not be working the way they're expected to.**
24
+
25
+ Redbull Consumed in the name of Glorious Progress: 4 Redbull.
26
+
27
+ # What won't be worked on
28
+
29
+ I'm currently not interested in adding support for any proof formats than Fitch-style natural deduction as presented in [forall x Calgary](https://forallx.openlogicproject.org/).
30
+
31
+ # Implementation Comparison
32
+
33
+ This JavaScript implementation is a modern reimplementation of the original PHP version from [fitch-checker](https://github.com/frabjous/fitch-checker). Both implementations support the same set of logical rules and validation requirements, ensuring a degree of compatibility and consistent behavior.
34
+
35
+ I would like to personally thank [Professor Kevin C. Klement (frabjous)](https://people.umass.edu/klement/) for the original implementation, which was the inspiration for this project.
36
+
37
+ ### Supported Rules
38
+
39
+ Both implementations support the full set of propositional and first-order logic rules:
40
+
41
+ #### Propositional Logic Rules
42
+ - Conjunction (∧I, ∧E)
43
+ - Disjunction (∨I, ∨E)
44
+ - Implication (→I, →E)
45
+ - Biconditional (↔I, ↔E)
46
+ - Negation (¬I, ¬E)
47
+ - Contradiction (⊥I, ⊥E)
48
+ - Derived Rules (DS, MT, DNE, DeM)
49
+ - Structural Rules (Pr, Hyp, R)
50
+ - Meta Rules (TND, LEM, IP)
51
+
52
+ #### First-Order Logic Rules
53
+ - Universal Quantification (∀I, ∀E)
54
+ - Existential Quantification (∃I, ∃E)
55
+ - Identity (=I, =E)
56
+ - Classical Quantifier (CQ)
57
+
58
+ ### Key Differences
59
+
60
+ 1. **Code Structure**
61
+ - PHP: Single file implementation with procedural style
62
+ - JS: Modular architecture with separate modules for parsing, types, and validation
63
+
64
+ 2. **Type System**
65
+ - PHP: Runtime type checking with basic object types
66
+ - JS: TypeScript-style type annotations with comprehensive interfaces
67
+
68
+ 3. **Validation Approach**
69
+ - PHP: Direct formula comparison with recursive checks
70
+ - JS: Structured validation pipeline with clear separation of concerns
71
+
72
+ 4. **Error Handling**
73
+ - PHP: Basic error messages with line numbers
74
+ - JS: Detailed error reporting with specific rule violation explanations
75
+
76
+ 5. **Testing**
77
+ - PHP: Implicit testing through usage (everything does work)
78
+ - JS: Comprehensive test suite with unit tests and edge cases
79
+
80
+ ## Project Status
81
+
82
+ ### In Progress
83
+ - [ ] Rule validation implementations:
84
+ - [ ] Identity Rules
85
+ - [ ] Identity Introduction (=I)
86
+ - [ ] Identity Elimination (=E)
87
+ - [ ] Term substitution for identity logic
88
+ - [ ] Classical Quantifier (CQ) rule
89
+ - [ ] Quantifier conversion validation
90
+ - [ ] Support for CQ rule application
91
+ - [ ] Derived Rules
92
+ - [ ] Double Negation Elimination (DNE)
93
+ - [ ] Modus Tollens (MT)
94
+ - [ ] De Morgan's Laws (DeM)
95
+ - [ ] Disjunctive Syllogism (DS)
96
+
97
+ ### Completed
98
+ - [x] Project structure setup
99
+ - [x] Basic types and interfaces
100
+ - [x] Formula parsing
101
+ - [x] Core validation logic for formulas
102
+ - [x] Support for First-Order Logic operations
103
+ - [x] Universal Elimination (∀E)
104
+ - [x] Existential Introduction (∃I)
105
+ - [x] Existential Elimination (∃E)
106
+ - [x] Formula substitution
107
+ - [x] Free variable checking
108
+ - [x] Witness restrictions for ∃E
109
+ - [x] Propositional Logic Rules
110
+ - [x] Conjunction Introduction (∧I)
111
+ - [x] Conjunction Elimination (∧E)
112
+ - [x] Disjunction Introduction (∨I)
113
+ - [x] Disjunction Elimination (∨E)
114
+ - [x] Conditional Introduction (→I)
115
+ - [x] Conditional Elimination (→E)
116
+ - [x] Biconditional Introduction (↔I)
117
+ - [x] Biconditional Elimination (↔E)
118
+ - [x] Negation Introduction (¬I)
119
+ - [x] Negation Elimination (¬E)
120
+ - [x] Contradiction Introduction (⊥I)
121
+ - [x] Contradiction Elimination (⊥E)
122
+ - [x] Law of Excluded Middle (LEM)
123
+
124
+ ### Needed Improvements
125
+
126
+ - [ ] Additional test cases:
127
+ - [ ] Complex nested quantifiers
128
+ - [ ] Multiple existential eliminations
129
+ - [ ] Invalid witness usage in conclusion
130
+ - [ ] Subproof scope violations
131
+ - [ ] Edge cases for variable binding
132
+ - [ ] Validation enhancements:
133
+ - [ ] Better error messages
134
+ - [ ] Detailed explanation of rule violations
135
+ - [ ] Suggestions for fixing invalid proofs?
136
+ - [ ] Documentation:
137
+ - [ ] Rule descriptions and examples
138
+ - [ ] Common pitfalls
139
+ - [ ] Best practices for proof construction
140
+
141
+ ## Project Structure
142
+
143
+ ```
144
+ fitch-js/
145
+ ├── src/
146
+ │ ├── parser.js # Formula parsing
147
+ │ ├── types.js # Type definitions
148
+ │ ├── rules/ # Rule implementations
149
+ │ └── proofChecker.js # Main proof checking logic
150
+ ├── test/
151
+ │ ├── parser.test.js
152
+ │ ├── rules.test.js
153
+ │ └── proofChecker.test.js
154
+ └── package.json
155
+ ```
156
+
157
+ ## Development Setup
158
+
159
+ 1. Clone the repository
160
+ 2. Install dependencies:
161
+ ```bash
162
+ cd fitch-js
163
+ npm install
164
+ ```
165
+ 3. Run tests:
166
+ ```bash
167
+ npm test
168
+ ```
169
+
170
+ ## Usage
171
+
172
+ ```javascript
173
+ const { parseFormula } = require('./src/parser');
174
+ const { checkProof } = require('./src/proofChecker');
175
+
176
+ // Example: Existential Elimination
177
+ const proof = {
178
+ lines: [
179
+ {
180
+ lineNum: 1,
181
+ formula: parseFormula('∃x(P(x))'),
182
+ rule: 'Pr',
183
+ citations: [],
184
+ subproofs: [],
185
+ issues: []
186
+ },
187
+ {
188
+ lineNum: 2,
189
+ formula: parseFormula('Q'),
190
+ rule: 'Pr',
191
+ citations: [],
192
+ subproofs: [],
193
+ issues: []
194
+ },
195
+ {
196
+ lineNum: 3,
197
+ formula: parseFormula('P(c)'),
198
+ rule: 'Hyp',
199
+ citations: [],
200
+ subproofs: [],
201
+ issues: [],
202
+ location: [0, 0]
203
+ },
204
+ {
205
+ lineNum: 4,
206
+ formula: parseFormula('Q'),
207
+ rule: 'R',
208
+ citations: [2],
209
+ subproofs: [],
210
+ issues: [],
211
+ location: [0, 0]
212
+ },
213
+ {
214
+ lineNum: 5,
215
+ formula: parseFormula('Q'),
216
+ rule: '∃E',
217
+ citations: [1],
218
+ subproofs: [{start: 3, end: 4}],
219
+ issues: [],
220
+ location: [0]
221
+ }
222
+ ],
223
+ numPremises: 2,
224
+ conclusion: parseFormula('Q')
225
+ };
226
+
227
+ const result = checkProof(proof);
228
+ console.log(result);
229
+ // {
230
+ // isValid: true,
231
+ // issues: [],
232
+ // conclusionReached: true
233
+ // }
234
+ ```
235
+
236
+ ## Rule Implementations
237
+
238
+ ### First-Order Logic Rules
239
+
240
+ #### Existential Elimination (∃E)
241
+ From ∃x(P(x)), to prove Q:
242
+ 1. Assume P(c) for a fresh constant c
243
+ 2. Derive Q without using c in premises/conclusion
244
+ 3. Conclude Q by ∃E
245
+
246
+ Restrictions:
247
+ - Witness term c must not appear in:
248
+ - Available premises
249
+ - The conclusion Q
250
+ - The original existential formula
251
+ - Subproof must start with assumption P(c)
252
+ - Q must be derivable from the subproof
253
+
254
+ ## Contributing
255
+
256
+ Contributions are welcome! Please feel free to submit a Pull Request. Before contributing:
257
+ 1. Ensure all tests pass
258
+ 2. Add tests for new features
259
+ 3. Update documentation
260
+ 4. Follow existing code style
261
+
262
+ # Implemented Rules and Usage
263
+
264
+ The following rules have been fully implemented and tested. Each rule follows specific patterns and requirements:
265
+
266
+ ## Propositional Logic Rules
267
+
268
+ ### Conjunction (∧)
269
+ - **Conjunction Introduction (∧I)**
270
+ - Requires two premises P and Q
271
+ - Concludes P ∧ Q
272
+ - Example: From P, Q derive P ∧ Q
273
+
274
+ - **Conjunction Elimination (∧E)**
275
+ - Requires one premise P ∧ Q
276
+ - Can conclude either P or Q
277
+ - Example: From P ∧ Q derive P (or Q)
278
+
279
+ ### Disjunction (∨)
280
+ - **Disjunction Introduction (∨I)**
281
+ - Requires one premise P
282
+ - Can conclude P ∨ Q (or Q ∨ P) for any Q
283
+ - Example: From P derive P ∨ Q
284
+
285
+ - **Disjunction Elimination (∨E)**
286
+ - Requires a disjunction P ∨ Q and two subproofs
287
+ - First subproof assumes P and derives R
288
+ - Second subproof assumes Q and derives R
289
+ - Concludes R
290
+ - Example: From P ∨ Q, [P ⊢ R], [Q ⊢ R] derive R
291
+
292
+ ### Conditional (→)
293
+ - **Conditional Introduction (→I)**
294
+ - Requires one subproof
295
+ - Subproof assumes P and derives Q
296
+ - Concludes P → Q
297
+ - Example: From [P ⊢ Q] derive P → Q
298
+
299
+ - **Conditional Elimination (→E)**
300
+ - Requires two premises: P → Q and P
301
+ - Concludes Q
302
+ - Example: From P → Q, P derive Q
303
+
304
+ ### Biconditional (↔)
305
+ - **Biconditional Introduction (↔I)**
306
+ - Requires two subproofs
307
+ - First subproof: P ⊢ Q
308
+ - Second subproof: Q ⊢ P
309
+ - Concludes P ↔ Q
310
+ - Example: From [P ⊢ Q], [Q ⊢ P] derive P ↔ Q
311
+
312
+ - **Biconditional Elimination (↔E)**
313
+ - Requires two premises: P ↔ Q and either P or Q
314
+ - Concludes the other side of the biconditional
315
+ - Example: From P ↔ Q, P derive Q (or from P ↔ Q, Q derive P)
316
+
317
+ ### Negation (¬)
318
+ - **Negation Introduction (¬I)**
319
+ - Requires one subproof
320
+ - Subproof assumes P and derives a contradiction (⊥)
321
+ - Concludes ¬P
322
+ - Example: From [P ⊢ ⊥] derive ¬P
323
+
324
+ - **Negation Elimination (¬E)**
325
+ - Requires two premises: P and ¬P
326
+ - Concludes a contradiction (⊥)
327
+ - Example: From P, ¬P derive ⊥
328
+
329
+ ### Contradiction (⊥)
330
+ - **Contradiction Introduction (⊥I)**
331
+ - Requires two premises: P and ¬P
332
+ - Concludes ⊥
333
+ - Example: From P, ¬P derive ⊥
334
+
335
+ - **Contradiction Elimination (⊥E)**
336
+ - Requires one premise: ⊥
337
+ - Can conclude any formula P
338
+ - Example: From ⊥ derive P
339
+
340
+ ### Meta Rules
341
+ - **Law of Excluded Middle (LEM)**
342
+ - Requires two subproofs
343
+ - First subproof assumes P and derives R
344
+ - Second subproof assumes ¬P and derives R
345
+ - Concludes R
346
+ - Example: From [P ⊢ R], [¬P ⊢ R] derive R
347
+
348
+ ### Structural Rules
349
+ - **Reiteration (R)**
350
+ - Requires one premise P
351
+ - Concludes P
352
+ - Can only reiterate from available scope
353
+ - Example: From P derive P
354
+
355
+ ## First-Order Logic Rules
356
+
357
+ ### Universal Quantification (∀)
358
+ - **Universal Elimination (∀E)**
359
+ - Requires one premise ∀x(P(x))
360
+ - Concludes P(t) for any term t
361
+ - Example: From ∀x(P(x)) derive P(a)
362
+
363
+ ### Existential Quantification (∃)
364
+ - **Existential Introduction (∃I)**
365
+ - Requires one premise P(t)
366
+ - Concludes ∃x(P(x))
367
+ - Example: From P(a) derive ∃x(P(x))
368
+
369
+ - **Existential Elimination (∃E)**
370
+ - Requires one premise ∃x(P(x)) and one subproof
371
+ - Subproof assumes P(c) for a fresh constant c and derives Q
372
+ - Constant c must not appear in:
373
+ - Available premises
374
+ - The conclusion Q
375
+ - The original existential formula
376
+ - Concludes Q
377
+ - Example: From ∃x(P(x)), [P(c) ⊢ Q] derive Q
378
+
379
+ ## Rule Requirements
380
+
381
+ Each rule has specific citation and subproof requirements:
382
+
383
+ 1. Citations must reference available lines (within scope)
384
+ 2. Subproofs must begin with hypotheses
385
+ 3. Subproof assumptions and conclusions must match rule requirements
386
+ 4. Fresh constants in ∃E must satisfy witness restrictions
387
+ 5. Formulas must exactly match rule patterns
388
+
389
+ ## Example Valid Proofs
390
+
391
+ Here's a simple example of a valid proof using conjunction and conditional rules:
392
+
393
+ ```
394
+ 1. P Pr
395
+ 2. Q Pr
396
+ 3. P ∧ Q ∧I 1,2
397
+ 4. Q ∧ P ∧I 2,1
398
+ 5. (P ∧ Q) → (Q ∧ P) →I [3,4]
399
+ ```
400
+
401
+ For more examples, refer to the test file which contains numerous test cases demonstrating correct usage of each rule.
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "OPERATORS", {
7
+ enumerable: true,
8
+ get: function get() {
9
+ return _parser.OPERATORS;
10
+ }
11
+ });
12
+ Object.defineProperty(exports, "RULES", {
13
+ enumerable: true,
14
+ get: function get() {
15
+ return _types.RULES;
16
+ }
17
+ });
18
+ Object.defineProperty(exports, "RULE_MAP", {
19
+ enumerable: true,
20
+ get: function get() {
21
+ return _index.RULE_MAP;
22
+ }
23
+ });
24
+ Object.defineProperty(exports, "RULE_REQUIREMENTS", {
25
+ enumerable: true,
26
+ get: function get() {
27
+ return _types.RULE_REQUIREMENTS;
28
+ }
29
+ });
30
+ Object.defineProperty(exports, "checkProof", {
31
+ enumerable: true,
32
+ get: function get() {
33
+ return _proofChecker.checkProof;
34
+ }
35
+ });
36
+ Object.defineProperty(exports, "convertAsciiToSymbols", {
37
+ enumerable: true,
38
+ get: function get() {
39
+ return _symbols.convertAsciiToSymbols;
40
+ }
41
+ });
42
+ Object.defineProperty(exports, "convertSymbolsToAscii", {
43
+ enumerable: true,
44
+ get: function get() {
45
+ return _symbols.convertSymbolsToAscii;
46
+ }
47
+ });
48
+ Object.defineProperty(exports, "formulasEqual", {
49
+ enumerable: true,
50
+ get: function get() {
51
+ return _proofChecker.formulasEqual;
52
+ }
53
+ });
54
+ Object.defineProperty(exports, "getAllTerms", {
55
+ enumerable: true,
56
+ get: function get() {
57
+ return _types.getAllTerms;
58
+ }
59
+ });
60
+ Object.defineProperty(exports, "getAvailableTerms", {
61
+ enumerable: true,
62
+ get: function get() {
63
+ return _proofChecker.getAvailableTerms;
64
+ }
65
+ });
66
+ Object.defineProperty(exports, "getDocumentLineForProofLine", {
67
+ enumerable: true,
68
+ get: function get() {
69
+ return _index.getDocumentLineForProofLine;
70
+ }
71
+ });
72
+ Object.defineProperty(exports, "getLineForPosition", {
73
+ enumerable: true,
74
+ get: function get() {
75
+ return _index.getLineForPosition;
76
+ }
77
+ });
78
+ Object.defineProperty(exports, "isLineAvailable", {
79
+ enumerable: true,
80
+ get: function get() {
81
+ return _ruleValidators.isLineAvailable;
82
+ }
83
+ });
84
+ Object.defineProperty(exports, "isValidCitation", {
85
+ enumerable: true,
86
+ get: function get() {
87
+ return _ruleValidators.isValidCitation;
88
+ }
89
+ });
90
+ Object.defineProperty(exports, "isVariable", {
91
+ enumerable: true,
92
+ get: function get() {
93
+ return _ruleValidators.isVariable;
94
+ }
95
+ });
96
+ Object.defineProperty(exports, "occursFree", {
97
+ enumerable: true,
98
+ get: function get() {
99
+ return _proofChecker.occursFree;
100
+ }
101
+ });
102
+ Object.defineProperty(exports, "parseFormula", {
103
+ enumerable: true,
104
+ get: function get() {
105
+ return _parser.parseFormula;
106
+ }
107
+ });
108
+ Object.defineProperty(exports, "parseProofText", {
109
+ enumerable: true,
110
+ get: function get() {
111
+ return _index.parseProofText;
112
+ }
113
+ });
114
+ Object.defineProperty(exports, "substitute", {
115
+ enumerable: true,
116
+ get: function get() {
117
+ return _proofChecker.substitute;
118
+ }
119
+ });
120
+ Object.defineProperty(exports, "symbolMappings", {
121
+ enumerable: true,
122
+ get: function get() {
123
+ return _symbols.symbolMappings;
124
+ }
125
+ });
126
+ var _parser = require("./parser.js");
127
+ var _proofChecker = require("./proofChecker.js");
128
+ var _types = require("./types.js");
129
+ var _ruleValidators = require("./ruleValidators.js");
130
+ var _symbols = require("./symbols.js");
131
+ var _index = require("./proof/index.js");