@proposit/proposit-core 0.12.3 → 1.0.1
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 +104 -93
- package/dist/cli/commands/premises.d.ts.map +1 -1
- package/dist/cli/commands/premises.js +28 -24
- package/dist/cli/commands/premises.js.map +1 -1
- package/dist/cli/commands/repair.d.ts.map +1 -1
- package/dist/cli/commands/repair.js +4 -2
- package/dist/cli/commands/repair.js.map +1 -1
- package/dist/cli/commands/validate.d.ts.map +1 -1
- package/dist/cli/commands/validate.js +7 -1
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/cli/engine.d.ts.map +1 -1
- package/dist/cli/engine.js +7 -25
- package/dist/cli/engine.js.map +1 -1
- package/dist/cli/import.d.ts.map +1 -1
- package/dist/cli/import.js +66 -28
- package/dist/cli/import.js.map +1 -1
- package/dist/lib/core/argument-engine.d.ts +275 -10
- package/dist/lib/core/argument-engine.d.ts.map +1 -1
- package/dist/lib/core/argument-engine.js +442 -82
- package/dist/lib/core/argument-engine.js.map +1 -1
- package/dist/lib/core/argument-library.d.ts +5 -1
- package/dist/lib/core/argument-library.d.ts.map +1 -1
- package/dist/lib/core/argument-library.js +7 -3
- package/dist/lib/core/argument-library.js.map +1 -1
- package/dist/lib/core/expression-manager.d.ts +68 -73
- package/dist/lib/core/expression-manager.d.ts.map +1 -1
- package/dist/lib/core/expression-manager.js +272 -771
- package/dist/lib/core/expression-manager.js.map +1 -1
- package/dist/lib/core/fork.d.ts +5 -1
- package/dist/lib/core/fork.d.ts.map +1 -1
- package/dist/lib/core/fork.js +16 -6
- package/dist/lib/core/fork.js.map +1 -1
- package/dist/lib/core/interfaces/argument-engine.interfaces.d.ts +68 -7
- package/dist/lib/core/interfaces/argument-engine.interfaces.d.ts.map +1 -1
- package/dist/lib/core/interfaces/library.interfaces.d.ts +8 -3
- package/dist/lib/core/interfaces/library.interfaces.d.ts.map +1 -1
- package/dist/lib/core/interfaces/premise-engine.interfaces.d.ts +50 -47
- package/dist/lib/core/interfaces/premise-engine.interfaces.d.ts.map +1 -1
- package/dist/lib/core/premise-engine.d.ts +80 -11
- package/dist/lib/core/premise-engine.d.ts.map +1 -1
- package/dist/lib/core/premise-engine.js +245 -83
- package/dist/lib/core/premise-engine.js.map +1 -1
- package/dist/lib/core/proposit-core.d.ts.map +1 -1
- package/dist/lib/core/proposit-core.js +13 -3
- package/dist/lib/core/proposit-core.js.map +1 -1
- package/dist/lib/grammar/an-rules.d.ts +158 -0
- package/dist/lib/grammar/an-rules.d.ts.map +1 -0
- package/dist/lib/grammar/an-rules.js +778 -0
- package/dist/lib/grammar/an-rules.js.map +1 -0
- package/dist/lib/grammar/auto-normalize.d.ts +14 -0
- package/dist/lib/grammar/auto-normalize.d.ts.map +1 -0
- package/dist/lib/grammar/auto-normalize.js +35 -0
- package/dist/lib/grammar/auto-normalize.js.map +1 -0
- package/dist/lib/grammar/bounded-subtree.d.ts +30 -0
- package/dist/lib/grammar/bounded-subtree.d.ts.map +1 -0
- package/dist/lib/grammar/bounded-subtree.js +74 -0
- package/dist/lib/grammar/bounded-subtree.js.map +1 -0
- package/dist/lib/grammar/naked-q.d.ts +20 -0
- package/dist/lib/grammar/naked-q.d.ts.map +1 -0
- package/dist/lib/grammar/naked-q.js +58 -0
- package/dist/lib/grammar/naked-q.js.map +1 -0
- package/dist/lib/grammar/normalize.d.ts +12 -0
- package/dist/lib/grammar/normalize.d.ts.map +1 -0
- package/dist/lib/grammar/normalize.js +45 -0
- package/dist/lib/grammar/normalize.js.map +1 -0
- package/dist/lib/grammar/populate-from.d.ts +25 -0
- package/dist/lib/grammar/populate-from.d.ts.map +1 -0
- package/dist/lib/grammar/populate-from.js +252 -0
- package/dist/lib/grammar/populate-from.js.map +1 -0
- package/dist/lib/grammar/repair.d.ts +65 -0
- package/dist/lib/grammar/repair.d.ts.map +1 -0
- package/dist/lib/grammar/repair.js +251 -0
- package/dist/lib/grammar/repair.js.map +1 -0
- package/dist/lib/grammar/types.d.ts +17 -0
- package/dist/lib/grammar/types.d.ts.map +1 -0
- package/dist/lib/grammar/types.js +82 -0
- package/dist/lib/grammar/types.js.map +1 -0
- package/dist/lib/grammar/validate.d.ts +4 -0
- package/dist/lib/grammar/validate.d.ts.map +1 -0
- package/dist/lib/grammar/validate.js +28 -0
- package/dist/lib/grammar/validate.js.map +1 -0
- package/dist/lib/grammar/validators/context.d.ts +11 -0
- package/dist/lib/grammar/validators/context.d.ts.map +1 -0
- package/dist/lib/grammar/validators/context.js +5 -0
- package/dist/lib/grammar/validators/context.js.map +1 -0
- package/dist/lib/grammar/validators/derivable.d.ts +63 -0
- package/dist/lib/grammar/validators/derivable.d.ts.map +1 -0
- package/dist/lib/grammar/validators/derivable.js +502 -0
- package/dist/lib/grammar/validators/derivable.js.map +1 -0
- package/dist/lib/grammar/validators/evaluable.d.ts +48 -0
- package/dist/lib/grammar/validators/evaluable.d.ts.map +1 -0
- package/dist/lib/grammar/validators/evaluable.js +226 -0
- package/dist/lib/grammar/validators/evaluable.js.map +1 -0
- package/dist/lib/grammar/validators/presentable.d.ts +45 -0
- package/dist/lib/grammar/validators/presentable.d.ts.map +1 -0
- package/dist/lib/grammar/validators/presentable.js +231 -0
- package/dist/lib/grammar/validators/presentable.js.map +1 -0
- package/dist/lib/grammar/validators/structural.d.ts +103 -0
- package/dist/lib/grammar/validators/structural.d.ts.map +1 -0
- package/dist/lib/grammar/validators/structural.js +602 -0
- package/dist/lib/grammar/validators/structural.js.map +1 -0
- package/dist/lib/index.d.ts +4 -3
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +2 -2
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/parsing/argument-parser.d.ts.map +1 -1
- package/dist/lib/parsing/argument-parser.js +52 -10
- package/dist/lib/parsing/argument-parser.js.map +1 -1
- package/dist/lib/types/evaluation.d.ts +1 -1
- package/dist/lib/types/evaluation.d.ts.map +1 -1
- package/dist/lib/types/fork.d.ts +12 -3
- package/dist/lib/types/fork.d.ts.map +1 -1
- package/dist/lib/types/validation.d.ts +0 -6
- package/dist/lib/types/validation.d.ts.map +1 -1
- package/dist/lib/types/validation.js +23 -6
- package/dist/lib/types/validation.js.map +1 -1
- package/package.json +1 -1
- package/dist/lib/core/managed-derivation-premise-engine.d.ts +0 -172
- package/dist/lib/core/managed-derivation-premise-engine.d.ts.map +0 -1
- package/dist/lib/core/managed-derivation-premise-engine.js +0 -550
- package/dist/lib/core/managed-derivation-premise-engine.js.map +0 -1
- package/dist/lib/types/grammar.d.ts +0 -83
- package/dist/lib/types/grammar.d.ts.map +0 -1
- package/dist/lib/types/grammar.js +0 -24
- package/dist/lib/types/grammar.js.map +0 -1
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { CorePremiseSchema, isExternallyBound, isPremiseBound, } from "../schemata/index.js";
|
|
2
2
|
import { DefaultMap } from "../utils/default-map.js";
|
|
3
|
-
import { midpoint, POSITION_INITIAL, POSITION_MAX } from "../utils/position.js";
|
|
3
|
+
import { midpoint, POSITION_INITIAL, POSITION_MAX, } from "../utils/position.js";
|
|
4
4
|
import { sortedCopyById, sortedUnique } from "../utils/collections.js";
|
|
5
5
|
import { kleeneAnd, kleeneIff, kleeneImplies, kleeneNot, kleeneOr, } from "./evaluation/kleene.js";
|
|
6
6
|
import { buildDirectionalVacuity, makeErrorIssue, makeValidationResult, } from "./evaluation/validation.js";
|
|
7
7
|
import { Value } from "typebox/value";
|
|
8
8
|
import { PREMISE_SCHEMA_INVALID, PREMISE_ROOT_EXPRESSION_INVALID, PREMISE_VARIABLE_REF_NOT_FOUND, } from "../types/validation.js";
|
|
9
9
|
import { defaultGenerateId, } from "./argument-engine.js";
|
|
10
|
-
import { DEFAULT_GRAMMAR_CONFIG, resolveAutoNormalize, } from "../types/grammar.js";
|
|
11
10
|
import { DEFAULT_CHECKSUM_CONFIG, normalizeChecksumConfig, serializeChecksumConfig, } from "../consts.js";
|
|
12
11
|
import { ChangeCollector } from "./change-collector.js";
|
|
13
12
|
import { computeHash, entityChecksum } from "./checksum.js";
|
|
@@ -22,7 +21,6 @@ export class PremiseEngine {
|
|
|
22
21
|
expressionsByVariableId;
|
|
23
22
|
argument;
|
|
24
23
|
checksumConfig;
|
|
25
|
-
grammarConfig;
|
|
26
24
|
checksumDirty = true;
|
|
27
25
|
cachedMetaChecksum;
|
|
28
26
|
cachedDescendantChecksum;
|
|
@@ -39,7 +37,6 @@ export class PremiseEngine {
|
|
|
39
37
|
this.premise = { ...premise };
|
|
40
38
|
this.argument = deps.argument;
|
|
41
39
|
this.checksumConfig = config?.checksumConfig;
|
|
42
|
-
this.grammarConfig = config?.grammarConfig ?? DEFAULT_GRAMMAR_CONFIG;
|
|
43
40
|
this.rootExpressionId = undefined;
|
|
44
41
|
this.variables = deps.variables;
|
|
45
42
|
this.expressions = new ExpressionManager(config);
|
|
@@ -48,13 +45,16 @@ export class PremiseEngine {
|
|
|
48
45
|
this.generateId = config?.generateId ?? defaultGenerateId;
|
|
49
46
|
}
|
|
50
47
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
48
|
+
* Returns the position config in effect for this premise engine.
|
|
49
|
+
* Used by in-package helpers (notably the native AN-4 absorption
|
|
50
|
+
* pass in `src/lib/grammar/an-rules.ts`) that need the position
|
|
51
|
+
* range boundaries when computing target positions for absorbed
|
|
52
|
+
* children.
|
|
53
|
+
*
|
|
54
|
+
* @internal
|
|
54
55
|
*/
|
|
55
|
-
|
|
56
|
-
this.
|
|
57
|
-
this.expressions.setGrammarConfig(grammarConfig);
|
|
56
|
+
getPositionConfig() {
|
|
57
|
+
return this.expressions.getPositionConfig();
|
|
58
58
|
}
|
|
59
59
|
setOnMutate(callback) {
|
|
60
60
|
this.onMutate = callback;
|
|
@@ -184,6 +184,16 @@ export class PremiseEngine {
|
|
|
184
184
|
if (this.rootExpressionId !== undefined) {
|
|
185
185
|
throw new Error(`Premise "${this.premise.id}" already has a root expression.`);
|
|
186
186
|
}
|
|
187
|
+
// S-14: derivation premise root must be one of variable,
|
|
188
|
+
// implies, or iff. Enforced at mutation time regardless
|
|
189
|
+
// of engine `behavior` — Structural rules throw in both
|
|
190
|
+
// modes (spec §4).
|
|
191
|
+
if (this.premise.type === "derivation" &&
|
|
192
|
+
expression.type === "operator" &&
|
|
193
|
+
expression.operator !== "implies" &&
|
|
194
|
+
expression.operator !== "iff") {
|
|
195
|
+
throw new Error(`S-14: derivation premise "${this.premise.id}" root must be variable, implies, or iff (got operator "${expression.operator}").`);
|
|
196
|
+
}
|
|
187
197
|
}
|
|
188
198
|
else {
|
|
189
199
|
if (!this.expressions.getExpression(expression.parentId)) {
|
|
@@ -418,24 +428,199 @@ export class PremiseEngine {
|
|
|
418
428
|
});
|
|
419
429
|
}
|
|
420
430
|
/**
|
|
421
|
-
*
|
|
422
|
-
*
|
|
423
|
-
*
|
|
431
|
+
* Reparent an existing expression onto a new parent at the given
|
|
432
|
+
* position. Bundled-composite mutation per spec §8 — the parent
|
|
433
|
+
* reference, position field, and checksum-dirty propagation update
|
|
434
|
+
* atomically in a single call. No transient orphan state is
|
|
435
|
+
* externally observable.
|
|
436
|
+
*
|
|
437
|
+
* Enforces Structural rules only (S-1 FK soundness, S-4 no-cycles,
|
|
438
|
+
* entity-not-found, and S-9 logical sibling-position uniqueness at
|
|
439
|
+
* the bundled-composite level — same-position collisions with the
|
|
440
|
+
* moved expression's own prior slot are tolerated as transient,
|
|
441
|
+
* since the move atomically frees that slot). Higher-tier violations
|
|
442
|
+
* never throw here — Evaluable/Derivable/Presentable issues surface
|
|
443
|
+
* through `validate(tier)` per spec §7.1.
|
|
444
|
+
*
|
|
445
|
+
* Used by the native AN-1 (formula-buffer insertion) and AN-4
|
|
446
|
+
* (same-operator absorption) passes in `src/lib/grammar/an-rules.ts`,
|
|
447
|
+
* and available to repair primitives and future composite ops that
|
|
448
|
+
* need a public reparent surface (e.g. `removeOrphanOperators`).
|
|
449
|
+
*
|
|
450
|
+
* @throws If `expressionId` or `newParentId` does not exist in this
|
|
451
|
+
* premise.
|
|
452
|
+
* @throws S-1: if `newParent` is not an `operator` or `formula` (a
|
|
453
|
+
* variable cannot be a parent — parity with `addExpression`
|
|
454
|
+
* at em.ts:418-422).
|
|
455
|
+
* @throws S-1: arity — if reparenting would push `newParent`'s
|
|
456
|
+
* child count past its operator-specific limit (unary `not`
|
|
457
|
+
* max 1; binary `implies`/`iff` max 2). Same-parent moves
|
|
458
|
+
* leave count unchanged and bypass this check.
|
|
459
|
+
* @throws S-4: if `newParentId === expressionId` or `newParentId` is
|
|
460
|
+
* a descendant of `expressionId`.
|
|
461
|
+
* @throws S-9: if another sibling (NOT the expression being moved)
|
|
462
|
+
* already occupies `newPosition` under `newParentId`.
|
|
463
|
+
*
|
|
464
|
+
* @since 1.0.0
|
|
424
465
|
*/
|
|
425
|
-
|
|
466
|
+
reparentExpression(expressionId, newParentId, newPosition) {
|
|
426
467
|
return this.withValidation(() => {
|
|
468
|
+
const expression = this.expressions.getExpression(expressionId);
|
|
469
|
+
if (!expression) {
|
|
470
|
+
throw new Error(`Expression "${expressionId}" not found in premise "${this.premise.id}".`);
|
|
471
|
+
}
|
|
472
|
+
const newParent = this.expressions.getExpression(newParentId);
|
|
473
|
+
if (!newParent) {
|
|
474
|
+
throw new Error(`Parent expression "${newParentId}" not found in premise "${this.premise.id}".`);
|
|
475
|
+
}
|
|
476
|
+
// S-1 parent-type: only operators and formulas accept
|
|
477
|
+
// children. Without this guard a caller could reparent under
|
|
478
|
+
// a variable (or any other non-container) and produce a
|
|
479
|
+
// malformed AST that no validator catches. Parity with
|
|
480
|
+
// `addExpression` at em.ts:418-422.
|
|
481
|
+
if (newParent.type !== "operator" && newParent.type !== "formula") {
|
|
482
|
+
throw new Error(`S-1: cannot reparent under non-operator/formula parent "${newParentId}" (type=${newParent.type}).`);
|
|
483
|
+
}
|
|
484
|
+
// S-4 no-cycles: newParent cannot be expressionId itself nor
|
|
485
|
+
// a descendant of expressionId.
|
|
486
|
+
if (newParentId === expressionId) {
|
|
487
|
+
throw new Error(`S-4: cannot reparent expression "${expressionId}" under itself.`);
|
|
488
|
+
}
|
|
489
|
+
if (this.isDescendantOf(newParentId, expressionId)) {
|
|
490
|
+
throw new Error(`S-4: cannot reparent expression "${expressionId}" under its descendant "${newParentId}" (would create a cycle).`);
|
|
491
|
+
}
|
|
492
|
+
// S-9 sibling-position uniqueness — only fires if a sibling
|
|
493
|
+
// OTHER than the moved expression occupies the target slot.
|
|
494
|
+
// Same-parent move with newPosition === expression.position
|
|
495
|
+
// is a no-op-position case and tolerated.
|
|
496
|
+
const isSameParentSamePosition = expression.parentId === newParentId &&
|
|
497
|
+
expression.position === newPosition;
|
|
498
|
+
if (!isSameParentSamePosition) {
|
|
499
|
+
const siblings = this.expressions.getChildExpressions(newParentId);
|
|
500
|
+
const collision = siblings.find((s) => s.id !== expressionId && s.position === newPosition);
|
|
501
|
+
if (collision) {
|
|
502
|
+
throw new Error(`S-9: position ${newPosition} is already occupied by sibling "${collision.id}" under parent "${newParentId}".`);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
// S-1 arity: when source is moving OUT of one parent and
|
|
506
|
+
// INTO `newParent`, the new parent's child count increases
|
|
507
|
+
// by 1. Reuse `addExpression`'s `assertChildLimit` parity
|
|
508
|
+
// (which only enforces caps for `not`/`implies`/`iff`).
|
|
509
|
+
// Same-parent moves bypass: count is unchanged. Formula
|
|
510
|
+
// parents enforce their 1-child cap separately.
|
|
511
|
+
const isSameParent = expression.parentId === newParentId;
|
|
512
|
+
if (!isSameParent) {
|
|
513
|
+
if (newParent.type === "operator") {
|
|
514
|
+
// assertChildLimit lives in EM — gate via the public
|
|
515
|
+
// reparent call's pre-check shape: replicate its
|
|
516
|
+
// logic here so we don't widen EM's surface.
|
|
517
|
+
const childCount = this.expressions.getChildExpressions(newParentId).length;
|
|
518
|
+
if (newParent.operator === "not" && childCount >= 1) {
|
|
519
|
+
throw new Error(`Operator expression "${newParentId}" with "not" can only have one child.`);
|
|
520
|
+
}
|
|
521
|
+
if ((newParent.operator === "implies" ||
|
|
522
|
+
newParent.operator === "iff") &&
|
|
523
|
+
childCount >= 2) {
|
|
524
|
+
throw new Error(`Operator expression "${newParentId}" with "${newParent.operator}" can only have two children.`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
else if (newParent.type === "formula") {
|
|
528
|
+
const childCount = this.expressions.getChildExpressions(newParentId).length;
|
|
529
|
+
if (childCount >= 1) {
|
|
530
|
+
throw new Error(`Formula expression "${newParentId}" can only have one child.`);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
427
534
|
const collector = new ChangeCollector();
|
|
428
535
|
this.expressions.setCollector(collector);
|
|
429
536
|
try {
|
|
430
|
-
this.expressions.
|
|
537
|
+
this.expressions.reparentExpression(expressionId, newParentId, newPosition);
|
|
431
538
|
const changes = this.finalizeExpressionMutation(collector);
|
|
432
|
-
return {
|
|
539
|
+
return {
|
|
540
|
+
result: this.expressions.getExpression(expressionId),
|
|
541
|
+
changes,
|
|
542
|
+
};
|
|
433
543
|
}
|
|
434
544
|
finally {
|
|
435
545
|
this.expressions.setCollector(null);
|
|
436
546
|
}
|
|
437
547
|
});
|
|
438
548
|
}
|
|
549
|
+
/**
|
|
550
|
+
* Wrap an existing expression in a freshly-minted `formula` node
|
|
551
|
+
* atomically. The formula takes the child's original parent slot
|
|
552
|
+
* (parentId + position); the child becomes the formula's sole
|
|
553
|
+
* child at position 0. Bundled-composite mutation per spec §8.
|
|
554
|
+
*
|
|
555
|
+
* Used by the native AN-1 (formula-buffer insertion) pass in
|
|
556
|
+
* `src/lib/grammar/an-rules.ts`. Composing this from
|
|
557
|
+
* `addExpression` + `reparentExpression` is not possible: the
|
|
558
|
+
* intermediate state would either (a) violate S-9 with the child
|
|
559
|
+
* still occupying the formula's target slot, or (b) trip the
|
|
560
|
+
* parent's `assertChildLimit` (unary `not`, binary
|
|
561
|
+
* `implies`/`iff`) even though the *net* child count of the parent
|
|
562
|
+
* is unchanged after the wrap.
|
|
563
|
+
*
|
|
564
|
+
* The new formula's id is minted via the engine's `idGenerator`
|
|
565
|
+
* accessor and returned in the mutation result. Argument fields
|
|
566
|
+
* (`argumentId`, `argumentVersion`, `premiseId`) are inherited
|
|
567
|
+
* from the source child.
|
|
568
|
+
*
|
|
569
|
+
* @throws If `childId` does not exist in this premise.
|
|
570
|
+
* @throws If `childId` is at the root (no parent to insert a
|
|
571
|
+
* buffer beneath).
|
|
572
|
+
* @throws S-10: if `formulaId` is already used by an existing
|
|
573
|
+
* expression in this premise (entity-ID uniqueness).
|
|
574
|
+
*
|
|
575
|
+
* @since 1.0.0
|
|
576
|
+
*/
|
|
577
|
+
wrapInFormula(childId, formulaId) {
|
|
578
|
+
return this.withValidation(() => {
|
|
579
|
+
const child = this.expressions.getExpression(childId);
|
|
580
|
+
if (!child) {
|
|
581
|
+
throw new Error(`Expression "${childId}" not found in premise "${this.premise.id}".`);
|
|
582
|
+
}
|
|
583
|
+
if (child.parentId === null) {
|
|
584
|
+
throw new Error(`Cannot wrap root expression "${childId}" in a formula — no parent operator above it.`);
|
|
585
|
+
}
|
|
586
|
+
const collector = new ChangeCollector();
|
|
587
|
+
this.expressions.setCollector(collector);
|
|
588
|
+
try {
|
|
589
|
+
this.expressions.wrapInFormula(childId, formulaId);
|
|
590
|
+
const changes = this.finalizeExpressionMutation(collector);
|
|
591
|
+
return {
|
|
592
|
+
result: this.expressions.getExpression(formulaId),
|
|
593
|
+
changes,
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
finally {
|
|
597
|
+
this.expressions.setCollector(null);
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Returns true iff `candidateId` is a descendant of `ancestorId` in
|
|
603
|
+
* this premise's expression tree. Used by `reparentExpression` for
|
|
604
|
+
* the S-4 no-cycles check.
|
|
605
|
+
*/
|
|
606
|
+
isDescendantOf(candidateId, ancestorId) {
|
|
607
|
+
const stack = [ancestorId];
|
|
608
|
+
while (stack.length > 0) {
|
|
609
|
+
const cursor = stack.pop();
|
|
610
|
+
for (const child of this.expressions.getChildExpressions(cursor)) {
|
|
611
|
+
if (child.id === candidateId)
|
|
612
|
+
return true;
|
|
613
|
+
stack.push(child.id);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
// D2 — `pe.normalizeExpressions()` was the per-premise wrapper for
|
|
619
|
+
// the legacy `ExpressionManager.normalize()` 5-pass sweep. Both
|
|
620
|
+
// methods are deleted in D2. The replacement is
|
|
621
|
+
// `engine.normalize(tier?)` (Phase C3) which routes through the
|
|
622
|
+
// native AN-1..AN-4 passes in `src/lib/grammar/an-rules.ts`. The
|
|
623
|
+
// post-mutation assistive hook covers the per-mutation use case.
|
|
439
624
|
toggleNegation(expressionId, extraFields) {
|
|
440
625
|
return this.withValidation(() => {
|
|
441
626
|
const target = this.expressions.getExpression(expressionId);
|
|
@@ -473,71 +658,42 @@ export class PremiseEngine {
|
|
|
473
658
|
return { result: null, changes };
|
|
474
659
|
}
|
|
475
660
|
else if (target.type === "operator" &&
|
|
476
|
-
target.operator === "not"
|
|
477
|
-
|
|
478
|
-
//
|
|
479
|
-
//
|
|
661
|
+
target.operator === "not") {
|
|
662
|
+
// Target is already NOT — toggling adds a second NOT and
|
|
663
|
+
// immediately collapses to the inner child. We express
|
|
664
|
+
// this directly by removing the existing NOT (promotes
|
|
665
|
+
// its child into its slot). D2: the pre-v1.0 gate on
|
|
666
|
+
// `collapseDoubleNegation` was deleted — `toggleNegation`
|
|
667
|
+
// unconditionally toggles.
|
|
480
668
|
this.expressions.removeExpression(expressionId, false);
|
|
481
669
|
const changes = this.finalizeExpressionMutation(collector);
|
|
482
670
|
return { result: null, changes };
|
|
483
671
|
}
|
|
484
672
|
else {
|
|
485
|
-
//
|
|
486
|
-
//
|
|
487
|
-
//
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
};
|
|
507
|
-
this.expressions.insertExpression(formulaExpr, expressionId);
|
|
508
|
-
const notExpr = {
|
|
509
|
-
...extraFields,
|
|
510
|
-
id: this.generateId(),
|
|
511
|
-
argumentId: target.argumentId,
|
|
512
|
-
argumentVersion: target.argumentVersion,
|
|
513
|
-
premiseId: target.premiseId,
|
|
514
|
-
type: "operator",
|
|
515
|
-
operator: "not",
|
|
516
|
-
parentId: target.parentId,
|
|
517
|
-
position: target.position,
|
|
518
|
-
};
|
|
519
|
-
this.expressions.insertExpression(notExpr, formulaExpr.id);
|
|
520
|
-
notExprId = notExpr.id;
|
|
521
|
-
}
|
|
522
|
-
else {
|
|
523
|
-
// Wrap target with a new NOT operator
|
|
524
|
-
const notExpr = {
|
|
525
|
-
...extraFields,
|
|
526
|
-
id: this.generateId(),
|
|
527
|
-
argumentId: target.argumentId,
|
|
528
|
-
argumentVersion: target.argumentVersion,
|
|
529
|
-
premiseId: target.premiseId,
|
|
530
|
-
type: "operator",
|
|
531
|
-
operator: "not",
|
|
532
|
-
parentId: target.parentId,
|
|
533
|
-
position: target.position,
|
|
534
|
-
};
|
|
535
|
-
this.expressions.insertExpression(notExpr, expressionId);
|
|
536
|
-
notExprId = notExpr.id;
|
|
537
|
-
}
|
|
673
|
+
// D2: the pre-v1.0 P-1 inline buffer-insertion branch
|
|
674
|
+
// (gated on `grammarConfig.enforceFormulaBetweenOperators`
|
|
675
|
+
// + `resolveAutoNormalize(_, 'negationInsertFormula')`,
|
|
676
|
+
// which built `NOT(formula(target))` inline) is gone.
|
|
677
|
+
// Always wrap with just NOT. AN-1 (post-mutation hook in
|
|
678
|
+
// assistive mode) inserts the formula buffer if the
|
|
679
|
+
// target is a non-not operator; permissive mode leaves
|
|
680
|
+
// the un-buffered state and `validate('presentable')`
|
|
681
|
+
// flags it.
|
|
682
|
+
const notExpr = {
|
|
683
|
+
...extraFields,
|
|
684
|
+
id: this.generateId(),
|
|
685
|
+
argumentId: target.argumentId,
|
|
686
|
+
argumentVersion: target.argumentVersion,
|
|
687
|
+
premiseId: target.premiseId,
|
|
688
|
+
type: "operator",
|
|
689
|
+
operator: "not",
|
|
690
|
+
parentId: target.parentId,
|
|
691
|
+
position: target.position,
|
|
692
|
+
};
|
|
693
|
+
this.expressions.insertExpression(notExpr, expressionId);
|
|
538
694
|
const changes = this.finalizeExpressionMutation(collector);
|
|
539
695
|
return {
|
|
540
|
-
result: this.expressions.getExpression(
|
|
696
|
+
result: this.expressions.getExpression(notExpr.id),
|
|
541
697
|
changes,
|
|
542
698
|
};
|
|
543
699
|
}
|
|
@@ -699,9 +855,19 @@ export class PremiseEngine {
|
|
|
699
855
|
position: POSITION_INITIAL,
|
|
700
856
|
};
|
|
701
857
|
this.expressions.addExpression(newOpExpr);
|
|
702
|
-
// Now reparent the children under the new sub-operator
|
|
703
|
-
|
|
704
|
-
|
|
858
|
+
// Now reparent the children under the new sub-operator.
|
|
859
|
+
// For `implies`/`iff` S-8 pins positions to exact [0, 1];
|
|
860
|
+
// for `and`/`or` keep the midpoint-spaced pattern so
|
|
861
|
+
// future inserts can bisect. (S-5 throws on non-root
|
|
862
|
+
// implies/iff at addExpression above, so this branch is
|
|
863
|
+
// unreachable for the binary case today — the explicit
|
|
864
|
+
// [0, 1] is defense-in-depth against future loosening
|
|
865
|
+
// of S-5 enforcement at the mutation surface.)
|
|
866
|
+
const isBinaryOp = newOperator === "implies" || newOperator === "iff";
|
|
867
|
+
this.expressions.reparentExpression(firstChild.id, newOpId, isBinaryOp ? 0 : POSITION_INITIAL);
|
|
868
|
+
this.expressions.reparentExpression(secondChild.id, newOpId, isBinaryOp
|
|
869
|
+
? 1
|
|
870
|
+
: midpoint(POSITION_INITIAL, POSITION_MAX));
|
|
705
871
|
const changes = this.finalizeExpressionMutation(collector);
|
|
706
872
|
return {
|
|
707
873
|
result: this.expressions.getExpression(newOpId),
|
|
@@ -1361,7 +1527,7 @@ export class PremiseEngine {
|
|
|
1361
1527
|
};
|
|
1362
1528
|
}
|
|
1363
1529
|
/** Creates a new PremiseEngine from a previously captured snapshot. */
|
|
1364
|
-
static fromSnapshot(snapshot, argument, variables, expressionIndex,
|
|
1530
|
+
static fromSnapshot(snapshot, argument, variables, expressionIndex, generateId) {
|
|
1365
1531
|
// Normalize checksumConfig in case the snapshot went through a JSON
|
|
1366
1532
|
// round-trip that converted Sets to arrays or empty objects.
|
|
1367
1533
|
const normalizedConfig = snapshot.config
|
|
@@ -1374,12 +1540,8 @@ export class PremiseEngine {
|
|
|
1374
1540
|
? { generateId }
|
|
1375
1541
|
: snapshot.config;
|
|
1376
1542
|
const pe = new PremiseEngine(snapshot.premise, { argument, variables, expressionIndex }, normalizedConfig);
|
|
1377
|
-
// Override grammar config if the caller specified one.
|
|
1378
|
-
if (grammarConfig) {
|
|
1379
|
-
pe.grammarConfig = grammarConfig;
|
|
1380
|
-
}
|
|
1381
1543
|
// Restore expressions from the snapshot
|
|
1382
|
-
pe.expressions = ExpressionManager.fromSnapshot(snapshot.expressions,
|
|
1544
|
+
pe.expressions = ExpressionManager.fromSnapshot(snapshot.expressions, generateId);
|
|
1383
1545
|
// Restore rootExpressionId from snapshot
|
|
1384
1546
|
pe.rootExpressionId = snapshot.rootExpressionId;
|
|
1385
1547
|
// Rebuild the expressionsByVariableId index
|