@aleph-ai/tinyaleph 1.0.2 → 1.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/README.md +219 -0
- package/core/index.js +83 -1
- package/core/lambda.js +845 -0
- package/core/reduction.js +741 -0
- package/core/types.js +913 -0
- package/docs/README.md +84 -0
- package/docs/design/ALEPH_CHAT_ARCHITECTURE.md +1 -1
- package/docs/design/AUTONOMOUS_LEARNING_DESIGN.md +1492 -0
- package/docs/design/WHITEPAPER_GAP_ANALYSIS.md +171 -4
- package/docs/reference/README.md +277 -1
- package/docs/theory/03-phase-synchronization.md +196 -0
- package/docs/theory/README.md +47 -0
- package/package.json +2 -2
- package/physics/index.js +30 -10
- package/physics/sync-models.js +770 -0
package/core/types.js
ADDED
|
@@ -0,0 +1,913 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formal Type System for Prime-Based Compositional Languages
|
|
3
|
+
*
|
|
4
|
+
* Implements a typed term calculus for prime-indexed semantics:
|
|
5
|
+
* - N(p): noun/subject term indexed by prime p
|
|
6
|
+
* - A(p): adjective/operator term indexed by prime p
|
|
7
|
+
* - S: sentence-level proposition
|
|
8
|
+
*
|
|
9
|
+
* Key features:
|
|
10
|
+
* - Ordered operator application with p < q constraint
|
|
11
|
+
* - Triadic fusion FUSE(p, q, r) where p+q+r is prime
|
|
12
|
+
* - Sentence composition (◦) and implication (⇒)
|
|
13
|
+
* - Full typing judgments Γ ⊢ e : T
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const { isPrime, firstNPrimes } = require('./prime');
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// TYPE DEFINITIONS
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Type constants for the type system
|
|
24
|
+
*/
|
|
25
|
+
const Types = {
|
|
26
|
+
NOUN: 'N',
|
|
27
|
+
ADJECTIVE: 'A',
|
|
28
|
+
SENTENCE: 'S'
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Base class for all typed terms
|
|
33
|
+
*/
|
|
34
|
+
class Term {
|
|
35
|
+
constructor(type) {
|
|
36
|
+
this.type = type;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Check if term is well-formed
|
|
41
|
+
*/
|
|
42
|
+
isWellFormed() {
|
|
43
|
+
throw new Error('Must be implemented by subclass');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get the semantic signature
|
|
48
|
+
*/
|
|
49
|
+
signature() {
|
|
50
|
+
throw new Error('Must be implemented by subclass');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Clone the term
|
|
55
|
+
*/
|
|
56
|
+
clone() {
|
|
57
|
+
throw new Error('Must be implemented by subclass');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Convert to string representation
|
|
62
|
+
*/
|
|
63
|
+
toString() {
|
|
64
|
+
throw new Error('Must be implemented by subclass');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ============================================================================
|
|
69
|
+
// NOUN TERMS N(p)
|
|
70
|
+
// ============================================================================
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* NounTerm - N(p)
|
|
74
|
+
* Represents a noun/subject indexed by prime p
|
|
75
|
+
* Semantically denotes the prime itself: ⟦N(p)⟧ = p
|
|
76
|
+
*/
|
|
77
|
+
class NounTerm extends Term {
|
|
78
|
+
/**
|
|
79
|
+
* @param {number} prime - The prime number indexing this noun
|
|
80
|
+
*/
|
|
81
|
+
constructor(prime) {
|
|
82
|
+
super(Types.NOUN);
|
|
83
|
+
|
|
84
|
+
if (!isPrime(prime)) {
|
|
85
|
+
throw new TypeError(`NounTerm requires prime number, got ${prime}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
this.prime = prime;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
isWellFormed() {
|
|
92
|
+
return isPrime(this.prime);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
signature() {
|
|
96
|
+
return `N(${this.prime})`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
clone() {
|
|
100
|
+
return new NounTerm(this.prime);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
toString() {
|
|
104
|
+
return `N(${this.prime})`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Semantic interpretation: ⟦N(p)⟧ = p
|
|
109
|
+
*/
|
|
110
|
+
interpret() {
|
|
111
|
+
return this.prime;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Check equality with another noun term
|
|
116
|
+
*/
|
|
117
|
+
equals(other) {
|
|
118
|
+
return other instanceof NounTerm && this.prime === other.prime;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
toJSON() {
|
|
122
|
+
return { type: 'N', prime: this.prime };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
static fromJSON(json) {
|
|
126
|
+
return new NounTerm(json.prime);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ============================================================================
|
|
131
|
+
// ADJECTIVE TERMS A(p)
|
|
132
|
+
// ============================================================================
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* AdjTerm - A(p)
|
|
136
|
+
* Represents an adjective/operator indexed by prime p
|
|
137
|
+
* Semantically denotes a partial function: ⟦A(p)⟧ = f_p : D ⇀ D
|
|
138
|
+
* where dom(f_p) ⊆ {q ∈ D : p < q}
|
|
139
|
+
*/
|
|
140
|
+
class AdjTerm extends Term {
|
|
141
|
+
/**
|
|
142
|
+
* @param {number} prime - The prime number indexing this adjective
|
|
143
|
+
*/
|
|
144
|
+
constructor(prime) {
|
|
145
|
+
super(Types.ADJECTIVE);
|
|
146
|
+
|
|
147
|
+
if (!isPrime(prime)) {
|
|
148
|
+
throw new TypeError(`AdjTerm requires prime number, got ${prime}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
this.prime = prime;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
isWellFormed() {
|
|
155
|
+
return isPrime(this.prime);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
signature() {
|
|
159
|
+
return `A(${this.prime})`;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
clone() {
|
|
163
|
+
return new AdjTerm(this.prime);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
toString() {
|
|
167
|
+
return `A(${this.prime})`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Check if this adjective can apply to a noun (p < q constraint)
|
|
172
|
+
* @param {NounTerm} noun - The noun to apply to
|
|
173
|
+
*/
|
|
174
|
+
canApplyTo(noun) {
|
|
175
|
+
if (!(noun instanceof NounTerm)) {
|
|
176
|
+
throw new TypeError('Can only apply to NounTerm');
|
|
177
|
+
}
|
|
178
|
+
return this.prime < noun.prime;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Apply this adjective to a noun term
|
|
183
|
+
* Returns a ChainTerm for type safety
|
|
184
|
+
* @param {NounTerm} noun - The noun to apply to
|
|
185
|
+
*/
|
|
186
|
+
apply(noun) {
|
|
187
|
+
if (!this.canApplyTo(noun)) {
|
|
188
|
+
throw new TypeError(`Application constraint violated: ${this.prime} must be < ${noun.prime}`);
|
|
189
|
+
}
|
|
190
|
+
return new ChainTerm([this], noun);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Check equality with another adjective term
|
|
195
|
+
*/
|
|
196
|
+
equals(other) {
|
|
197
|
+
return other instanceof AdjTerm && this.prime === other.prime;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
toJSON() {
|
|
201
|
+
return { type: 'A', prime: this.prime };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
static fromJSON(json) {
|
|
205
|
+
return new AdjTerm(json.prime);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ============================================================================
|
|
210
|
+
// CHAIN TERMS A(p₁)...A(pₖ)N(q)
|
|
211
|
+
// ============================================================================
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* ChainTerm - A(p₁), A(p₂), ..., A(pₖ), N(q)
|
|
215
|
+
* Represents an operator chain applied to a noun
|
|
216
|
+
* Semantically: ⟦chain⟧ = f_p₁(f_p₂(...f_pₖ(q)...))
|
|
217
|
+
*/
|
|
218
|
+
class ChainTerm extends Term {
|
|
219
|
+
/**
|
|
220
|
+
* @param {Array<AdjTerm>} operators - Sequence of adjective operators
|
|
221
|
+
* @param {NounTerm} noun - The noun being modified
|
|
222
|
+
*/
|
|
223
|
+
constructor(operators, noun) {
|
|
224
|
+
super(Types.NOUN);
|
|
225
|
+
|
|
226
|
+
if (!Array.isArray(operators)) {
|
|
227
|
+
throw new TypeError('Operators must be an array');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (!(noun instanceof NounTerm)) {
|
|
231
|
+
throw new TypeError('Noun must be a NounTerm');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
this.operators = operators;
|
|
235
|
+
this.noun = noun;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Check well-formedness: all operators must satisfy p < q constraint
|
|
240
|
+
*/
|
|
241
|
+
isWellFormed() {
|
|
242
|
+
if (this.operators.length === 0) {
|
|
243
|
+
return this.noun.isWellFormed();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Check innermost constraint: last operator's prime < noun's prime
|
|
247
|
+
const last = this.operators[this.operators.length - 1];
|
|
248
|
+
if (last.prime >= this.noun.prime) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Check chain constraints: each p_i < p_{i+1} for proper ordering
|
|
253
|
+
// This follows from the composition semantics
|
|
254
|
+
for (let i = 0; i < this.operators.length - 1; i++) {
|
|
255
|
+
// In a well-formed chain, each operator must be able to apply
|
|
256
|
+
// to the result of the inner operators
|
|
257
|
+
// This is validated during reduction
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
signature() {
|
|
264
|
+
const ops = this.operators.map(o => o.signature()).join(', ');
|
|
265
|
+
return ops ? `${ops}, ${this.noun.signature()}` : this.noun.signature();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
clone() {
|
|
269
|
+
return new ChainTerm(
|
|
270
|
+
this.operators.map(o => o.clone()),
|
|
271
|
+
this.noun.clone()
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
toString() {
|
|
276
|
+
const ops = this.operators.map(o => o.toString()).join(' ');
|
|
277
|
+
return ops ? `${ops} ${this.noun}` : this.noun.toString();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Prepend an operator to this chain
|
|
282
|
+
* @param {AdjTerm} operator - The operator to prepend
|
|
283
|
+
*/
|
|
284
|
+
prepend(operator) {
|
|
285
|
+
return new ChainTerm([operator, ...this.operators], this.noun);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Get the chain length (number of operators)
|
|
290
|
+
*/
|
|
291
|
+
get length() {
|
|
292
|
+
return this.operators.length;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Get all primes in the chain (operators + noun)
|
|
297
|
+
*/
|
|
298
|
+
getAllPrimes() {
|
|
299
|
+
return [...this.operators.map(o => o.prime), this.noun.prime];
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
toJSON() {
|
|
303
|
+
return {
|
|
304
|
+
type: 'chain',
|
|
305
|
+
operators: this.operators.map(o => o.toJSON()),
|
|
306
|
+
noun: this.noun.toJSON()
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
static fromJSON(json) {
|
|
311
|
+
return new ChainTerm(
|
|
312
|
+
json.operators.map(o => AdjTerm.fromJSON(o)),
|
|
313
|
+
NounTerm.fromJSON(json.noun)
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ============================================================================
|
|
319
|
+
// FUSION TERMS FUSE(p, q, r)
|
|
320
|
+
// ============================================================================
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* FusionTerm - FUSE(p, q, r)
|
|
324
|
+
* Represents triadic prime fusion
|
|
325
|
+
* Well-formed when: p, q, r are distinct odd primes and p+q+r is prime
|
|
326
|
+
* Semantically: ⟦FUSE(p,q,r)⟧ = p + q + r
|
|
327
|
+
*/
|
|
328
|
+
class FusionTerm extends Term {
|
|
329
|
+
/**
|
|
330
|
+
* @param {number} p - First prime
|
|
331
|
+
* @param {number} q - Second prime
|
|
332
|
+
* @param {number} r - Third prime
|
|
333
|
+
*/
|
|
334
|
+
constructor(p, q, r) {
|
|
335
|
+
super(Types.NOUN);
|
|
336
|
+
|
|
337
|
+
this.p = p;
|
|
338
|
+
this.q = q;
|
|
339
|
+
this.r = r;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Check well-formedness:
|
|
344
|
+
* 1. p, q, r are distinct
|
|
345
|
+
* 2. p, q, r are odd primes (> 2)
|
|
346
|
+
* 3. p + q + r is prime
|
|
347
|
+
*/
|
|
348
|
+
isWellFormed() {
|
|
349
|
+
// Check distinctness
|
|
350
|
+
if (this.p === this.q || this.q === this.r || this.p === this.r) {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Check all are odd primes (> 2)
|
|
355
|
+
if (this.p === 2 || this.q === 2 || this.r === 2) {
|
|
356
|
+
return false;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (!isPrime(this.p) || !isPrime(this.q) || !isPrime(this.r)) {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Check sum is prime
|
|
364
|
+
return isPrime(this.p + this.q + this.r);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Get the fused prime value
|
|
369
|
+
*/
|
|
370
|
+
getFusedPrime() {
|
|
371
|
+
if (!this.isWellFormed()) {
|
|
372
|
+
throw new Error('Cannot get fused prime from ill-formed fusion');
|
|
373
|
+
}
|
|
374
|
+
return this.p + this.q + this.r;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
signature() {
|
|
378
|
+
return `FUSE(${this.p}, ${this.q}, ${this.r})`;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
clone() {
|
|
382
|
+
return new FusionTerm(this.p, this.q, this.r);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
toString() {
|
|
386
|
+
return `FUSE(${this.p}, ${this.q}, ${this.r})`;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Convert to equivalent NounTerm (after reduction)
|
|
391
|
+
*/
|
|
392
|
+
toNounTerm() {
|
|
393
|
+
return new NounTerm(this.getFusedPrime());
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Get canonical form (sorted primes)
|
|
398
|
+
*/
|
|
399
|
+
canonical() {
|
|
400
|
+
const sorted = [this.p, this.q, this.r].sort((a, b) => a - b);
|
|
401
|
+
return new FusionTerm(sorted[0], sorted[1], sorted[2]);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
toJSON() {
|
|
405
|
+
return { type: 'FUSE', p: this.p, q: this.q, r: this.r };
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
static fromJSON(json) {
|
|
409
|
+
return new FusionTerm(json.p, json.q, json.r);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Find valid fusion triads for a target prime
|
|
414
|
+
* @param {number} target - Target prime (sum of three primes)
|
|
415
|
+
* @param {number} limit - Maximum prime to consider
|
|
416
|
+
*/
|
|
417
|
+
static findTriads(target, limit = 100) {
|
|
418
|
+
if (!isPrime(target)) return [];
|
|
419
|
+
|
|
420
|
+
const triads = [];
|
|
421
|
+
const primes = firstNPrimes(Math.min(limit, 100)).filter(p => p > 2 && p < target);
|
|
422
|
+
|
|
423
|
+
for (let i = 0; i < primes.length; i++) {
|
|
424
|
+
for (let j = i + 1; j < primes.length; j++) {
|
|
425
|
+
const p = primes[i];
|
|
426
|
+
const q = primes[j];
|
|
427
|
+
const r = target - p - q;
|
|
428
|
+
|
|
429
|
+
if (r > q && isPrime(r) && r !== 2) {
|
|
430
|
+
triads.push(new FusionTerm(p, q, r));
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return triads;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// ============================================================================
|
|
440
|
+
// SENTENCE TERMS
|
|
441
|
+
// ============================================================================
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* SentenceTerm - S
|
|
445
|
+
* Base class for sentence-level expressions
|
|
446
|
+
*/
|
|
447
|
+
class SentenceTerm extends Term {
|
|
448
|
+
constructor() {
|
|
449
|
+
super(Types.SENTENCE);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* NounSentence - Sentence from noun term
|
|
455
|
+
* A noun-denoting expression treated as a one-token discourse state
|
|
456
|
+
* ⟦e : N⟧_S = [⟦e⟧] ∈ D*
|
|
457
|
+
*/
|
|
458
|
+
class NounSentence extends SentenceTerm {
|
|
459
|
+
/**
|
|
460
|
+
* @param {NounTerm|ChainTerm|FusionTerm} nounExpr - Noun expression
|
|
461
|
+
*/
|
|
462
|
+
constructor(nounExpr) {
|
|
463
|
+
super();
|
|
464
|
+
|
|
465
|
+
if (!(nounExpr instanceof NounTerm ||
|
|
466
|
+
nounExpr instanceof ChainTerm ||
|
|
467
|
+
nounExpr instanceof FusionTerm)) {
|
|
468
|
+
throw new TypeError('NounSentence requires noun-typed expression');
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
this.expr = nounExpr;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
isWellFormed() {
|
|
475
|
+
return this.expr.isWellFormed();
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
signature() {
|
|
479
|
+
return `S(${this.expr.signature()})`;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
clone() {
|
|
483
|
+
return new NounSentence(this.expr.clone());
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
toString() {
|
|
487
|
+
return `[${this.expr}]`;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Get discourse state (sequence of primes)
|
|
492
|
+
*/
|
|
493
|
+
getDiscourseState() {
|
|
494
|
+
if (this.expr instanceof NounTerm) {
|
|
495
|
+
return [this.expr.prime];
|
|
496
|
+
} else if (this.expr instanceof ChainTerm) {
|
|
497
|
+
// Return the primes involved in the chain
|
|
498
|
+
return this.expr.getAllPrimes();
|
|
499
|
+
} else if (this.expr instanceof FusionTerm) {
|
|
500
|
+
return [this.expr.getFusedPrime()];
|
|
501
|
+
}
|
|
502
|
+
return [];
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
toJSON() {
|
|
506
|
+
return { type: 'NounSentence', expr: this.expr.toJSON() };
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* SeqSentence - s₁ ◦ s₂
|
|
512
|
+
* Sequential composition of sentences
|
|
513
|
+
* Semantically: ⟦s₁ ◦ s₂⟧ = ⟦s₁⟧ · ⟦s₂⟧ (concatenation in D*)
|
|
514
|
+
*/
|
|
515
|
+
class SeqSentence extends SentenceTerm {
|
|
516
|
+
/**
|
|
517
|
+
* @param {SentenceTerm} left - Left sentence
|
|
518
|
+
* @param {SentenceTerm} right - Right sentence
|
|
519
|
+
*/
|
|
520
|
+
constructor(left, right) {
|
|
521
|
+
super();
|
|
522
|
+
|
|
523
|
+
if (!(left instanceof SentenceTerm) || !(right instanceof SentenceTerm)) {
|
|
524
|
+
throw new TypeError('SeqSentence requires two SentenceTerms');
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
this.left = left;
|
|
528
|
+
this.right = right;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
isWellFormed() {
|
|
532
|
+
return this.left.isWellFormed() && this.right.isWellFormed();
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
signature() {
|
|
536
|
+
return `(${this.left.signature()} ◦ ${this.right.signature()})`;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
clone() {
|
|
540
|
+
return new SeqSentence(this.left.clone(), this.right.clone());
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
toString() {
|
|
544
|
+
return `(${this.left} ◦ ${this.right})`;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Get combined discourse state (concatenation)
|
|
549
|
+
*/
|
|
550
|
+
getDiscourseState() {
|
|
551
|
+
return [...this.left.getDiscourseState(), ...this.right.getDiscourseState()];
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
toJSON() {
|
|
555
|
+
return { type: 'SeqSentence', left: this.left.toJSON(), right: this.right.toJSON() };
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* ImplSentence - s₁ ⇒ s₂
|
|
561
|
+
* Implication/entailment between sentences
|
|
562
|
+
* Semantically: M ⊨ (s₁ ⇒ s₂) iff ⟦s₁⟧ ⪯ ⟦s₂⟧ (prefix entailment)
|
|
563
|
+
*/
|
|
564
|
+
class ImplSentence extends SentenceTerm {
|
|
565
|
+
/**
|
|
566
|
+
* @param {SentenceTerm} antecedent - Antecedent sentence
|
|
567
|
+
* @param {SentenceTerm} consequent - Consequent sentence
|
|
568
|
+
*/
|
|
569
|
+
constructor(antecedent, consequent) {
|
|
570
|
+
super();
|
|
571
|
+
|
|
572
|
+
if (!(antecedent instanceof SentenceTerm) || !(consequent instanceof SentenceTerm)) {
|
|
573
|
+
throw new TypeError('ImplSentence requires two SentenceTerms');
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
this.antecedent = antecedent;
|
|
577
|
+
this.consequent = consequent;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
isWellFormed() {
|
|
581
|
+
return this.antecedent.isWellFormed() && this.consequent.isWellFormed();
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
signature() {
|
|
585
|
+
return `(${this.antecedent.signature()} ⇒ ${this.consequent.signature()})`;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
clone() {
|
|
589
|
+
return new ImplSentence(this.antecedent.clone(), this.consequent.clone());
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
toString() {
|
|
593
|
+
return `(${this.antecedent} ⇒ ${this.consequent})`;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Check if implication holds (prefix entailment)
|
|
598
|
+
*/
|
|
599
|
+
holds() {
|
|
600
|
+
const ante = this.antecedent.getDiscourseState();
|
|
601
|
+
const cons = this.consequent.getDiscourseState();
|
|
602
|
+
|
|
603
|
+
// Prefix entailment: antecedent is prefix of consequent
|
|
604
|
+
if (ante.length > cons.length) return false;
|
|
605
|
+
|
|
606
|
+
for (let i = 0; i < ante.length; i++) {
|
|
607
|
+
if (ante[i] !== cons[i]) return false;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
return true;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
toJSON() {
|
|
614
|
+
return {
|
|
615
|
+
type: 'ImplSentence',
|
|
616
|
+
antecedent: this.antecedent.toJSON(),
|
|
617
|
+
consequent: this.consequent.toJSON()
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// ============================================================================
|
|
623
|
+
// TYPING CONTEXT AND JUDGMENTS
|
|
624
|
+
// ============================================================================
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* TypingContext - Γ
|
|
628
|
+
* A context for typing judgments
|
|
629
|
+
*/
|
|
630
|
+
class TypingContext {
|
|
631
|
+
constructor() {
|
|
632
|
+
this.bindings = new Map();
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Bind a variable name to a type
|
|
637
|
+
*/
|
|
638
|
+
bind(name, type, term = null) {
|
|
639
|
+
this.bindings.set(name, { type, term });
|
|
640
|
+
return this;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Get type of a variable
|
|
645
|
+
*/
|
|
646
|
+
getType(name) {
|
|
647
|
+
const binding = this.bindings.get(name);
|
|
648
|
+
return binding ? binding.type : null;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Get term for a variable
|
|
653
|
+
*/
|
|
654
|
+
getTerm(name) {
|
|
655
|
+
const binding = this.bindings.get(name);
|
|
656
|
+
return binding ? binding.term : null;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Check if variable is bound
|
|
661
|
+
*/
|
|
662
|
+
has(name) {
|
|
663
|
+
return this.bindings.has(name);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Clone the context
|
|
668
|
+
*/
|
|
669
|
+
clone() {
|
|
670
|
+
const ctx = new TypingContext();
|
|
671
|
+
for (const [name, binding] of this.bindings) {
|
|
672
|
+
ctx.bindings.set(name, { ...binding });
|
|
673
|
+
}
|
|
674
|
+
return ctx;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
toString() {
|
|
678
|
+
const entries = [];
|
|
679
|
+
for (const [name, { type }] of this.bindings) {
|
|
680
|
+
entries.push(`${name}: ${type}`);
|
|
681
|
+
}
|
|
682
|
+
return `Γ = {${entries.join(', ')}}`;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* TypingJudgment - Γ ⊢ e : T
|
|
688
|
+
* Represents a typing judgment
|
|
689
|
+
*/
|
|
690
|
+
class TypingJudgment {
|
|
691
|
+
/**
|
|
692
|
+
* @param {TypingContext} context - The typing context Γ
|
|
693
|
+
* @param {Term} term - The term e
|
|
694
|
+
* @param {string} type - The type T
|
|
695
|
+
*/
|
|
696
|
+
constructor(context, term, type) {
|
|
697
|
+
this.context = context;
|
|
698
|
+
this.term = term;
|
|
699
|
+
this.type = type;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* Check if this judgment is valid
|
|
704
|
+
*/
|
|
705
|
+
isValid() {
|
|
706
|
+
if (!this.term.isWellFormed()) {
|
|
707
|
+
return false;
|
|
708
|
+
}
|
|
709
|
+
return this.term.type === this.type;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
toString() {
|
|
713
|
+
return `${this.context} ⊢ ${this.term} : ${this.type}`;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// ============================================================================
|
|
718
|
+
// TYPE CHECKER
|
|
719
|
+
// ============================================================================
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* TypeChecker - Implements typing rules from the paper
|
|
723
|
+
*/
|
|
724
|
+
class TypeChecker {
|
|
725
|
+
constructor() {
|
|
726
|
+
this.context = new TypingContext();
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Infer type of a term
|
|
731
|
+
* @param {Term} term - The term to type
|
|
732
|
+
* @returns {string|null} The inferred type, or null if ill-typed
|
|
733
|
+
*/
|
|
734
|
+
inferType(term) {
|
|
735
|
+
if (term instanceof NounTerm) {
|
|
736
|
+
return term.isWellFormed() ? Types.NOUN : null;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
if (term instanceof AdjTerm) {
|
|
740
|
+
return term.isWellFormed() ? Types.ADJECTIVE : null;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
if (term instanceof ChainTerm) {
|
|
744
|
+
return term.isWellFormed() ? Types.NOUN : null;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
if (term instanceof FusionTerm) {
|
|
748
|
+
return term.isWellFormed() ? Types.NOUN : null;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if (term instanceof SentenceTerm) {
|
|
752
|
+
return term.isWellFormed() ? Types.SENTENCE : null;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
return null;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Check if a term has a specific type
|
|
760
|
+
* @param {Term} term - The term to check
|
|
761
|
+
* @param {string} expectedType - The expected type
|
|
762
|
+
*/
|
|
763
|
+
checkType(term, expectedType) {
|
|
764
|
+
const inferred = this.inferType(term);
|
|
765
|
+
return inferred === expectedType;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Derive typing judgment
|
|
770
|
+
* @param {Term} term - The term to type
|
|
771
|
+
* @returns {TypingJudgment|null} The judgment, or null if ill-typed
|
|
772
|
+
*/
|
|
773
|
+
derive(term) {
|
|
774
|
+
const type = this.inferType(term);
|
|
775
|
+
if (type === null) return null;
|
|
776
|
+
return new TypingJudgment(this.context.clone(), term, type);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* Check application well-formedness
|
|
781
|
+
* @param {AdjTerm} adj - Adjective term
|
|
782
|
+
* @param {NounTerm} noun - Noun term
|
|
783
|
+
*/
|
|
784
|
+
checkApplication(adj, noun) {
|
|
785
|
+
if (!this.checkType(adj, Types.ADJECTIVE)) {
|
|
786
|
+
return { valid: false, reason: 'Adjective ill-typed' };
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
if (!this.checkType(noun, Types.NOUN)) {
|
|
790
|
+
return { valid: false, reason: 'Noun ill-typed' };
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
if (!adj.canApplyTo(noun)) {
|
|
794
|
+
return { valid: false, reason: `Ordering constraint violated: ${adj.prime} ≮ ${noun.prime}` };
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
return { valid: true };
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* Check fusion well-formedness
|
|
802
|
+
* @param {FusionTerm} fusion - Fusion term
|
|
803
|
+
*/
|
|
804
|
+
checkFusion(fusion) {
|
|
805
|
+
if (!fusion.isWellFormed()) {
|
|
806
|
+
return {
|
|
807
|
+
valid: false,
|
|
808
|
+
reason: `Fusion ill-formed: primes ${fusion.p}, ${fusion.q}, ${fusion.r} don't satisfy constraints`
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
return { valid: true, result: fusion.getFusedPrime() };
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// ============================================================================
|
|
816
|
+
// TERM BUILDERS
|
|
817
|
+
// ============================================================================
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* Build a noun term from prime
|
|
821
|
+
*/
|
|
822
|
+
function N(prime) {
|
|
823
|
+
return new NounTerm(prime);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Build an adjective term from prime
|
|
828
|
+
*/
|
|
829
|
+
function A(prime) {
|
|
830
|
+
return new AdjTerm(prime);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Build a fusion term
|
|
835
|
+
*/
|
|
836
|
+
function FUSE(p, q, r) {
|
|
837
|
+
return new FusionTerm(p, q, r);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Build a chain term
|
|
842
|
+
*/
|
|
843
|
+
function CHAIN(operators, noun) {
|
|
844
|
+
const ops = operators.map(p => typeof p === 'number' ? A(p) : p);
|
|
845
|
+
const n = typeof noun === 'number' ? N(noun) : noun;
|
|
846
|
+
return new ChainTerm(ops, n);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Build a sentence from noun expression
|
|
851
|
+
*/
|
|
852
|
+
function SENTENCE(expr) {
|
|
853
|
+
if (typeof expr === 'number') {
|
|
854
|
+
expr = N(expr);
|
|
855
|
+
}
|
|
856
|
+
return new NounSentence(expr);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
/**
|
|
860
|
+
* Sequential composition of sentences
|
|
861
|
+
*/
|
|
862
|
+
function SEQ(s1, s2) {
|
|
863
|
+
const left = s1 instanceof SentenceTerm ? s1 : SENTENCE(s1);
|
|
864
|
+
const right = s2 instanceof SentenceTerm ? s2 : SENTENCE(s2);
|
|
865
|
+
return new SeqSentence(left, right);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
/**
|
|
869
|
+
* Implication of sentences
|
|
870
|
+
*/
|
|
871
|
+
function IMPL(s1, s2) {
|
|
872
|
+
const ante = s1 instanceof SentenceTerm ? s1 : SENTENCE(s1);
|
|
873
|
+
const cons = s2 instanceof SentenceTerm ? s2 : SENTENCE(s2);
|
|
874
|
+
return new ImplSentence(ante, cons);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
// ============================================================================
|
|
878
|
+
// EXPORTS
|
|
879
|
+
// ============================================================================
|
|
880
|
+
|
|
881
|
+
module.exports = {
|
|
882
|
+
// Type constants
|
|
883
|
+
Types,
|
|
884
|
+
|
|
885
|
+
// Base class
|
|
886
|
+
Term,
|
|
887
|
+
|
|
888
|
+
// Term classes
|
|
889
|
+
NounTerm,
|
|
890
|
+
AdjTerm,
|
|
891
|
+
ChainTerm,
|
|
892
|
+
FusionTerm,
|
|
893
|
+
|
|
894
|
+
// Sentence classes
|
|
895
|
+
SentenceTerm,
|
|
896
|
+
NounSentence,
|
|
897
|
+
SeqSentence,
|
|
898
|
+
ImplSentence,
|
|
899
|
+
|
|
900
|
+
// Typing system
|
|
901
|
+
TypingContext,
|
|
902
|
+
TypingJudgment,
|
|
903
|
+
TypeChecker,
|
|
904
|
+
|
|
905
|
+
// Builders
|
|
906
|
+
N,
|
|
907
|
+
A,
|
|
908
|
+
FUSE,
|
|
909
|
+
CHAIN,
|
|
910
|
+
SENTENCE,
|
|
911
|
+
SEQ,
|
|
912
|
+
IMPL
|
|
913
|
+
};
|