Adds IS NOT to the Part II parser and a resolution pass (negation, then
identity lock) that turns parsed rules into the rules in force; harness
gains activeRules() and hasProperty(). A leading NOT now breaks the
sentence instead of being ignored (carried from M6 review). Nouns say
which objects they name ("selects"), so the ALL Expansion Drop is one
line of data. Corrects lesson 06's NOT wording; harness.md M7 details.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
60 lines
5.1 KiB
Markdown
60 lines
5.1 KiB
Markdown
# Module 7 scenarios: Resolving rules (NOT, identity, meta-nouns)
|
|
|
|
Each heading is a test name. The Rust test lives in `tests/rust/test_07.rs` and the TypeScript test in `tests/ts/test_07.test.ts`; both state exactly what is written here.
|
|
|
|
The level format, `place`, `rules()` and the way a test writes a sentence (`BABA (0,0) IS (1,0) YOU (2,0)` is three text objects) are those of Module 6. This module adds the operator `NOT` to the grammar, and two harness functions:
|
|
|
|
- `rules()` is still everything the text spells, before resolution, and now includes negated rules, written `"BABA IS NOT YOU"`.
|
|
- `activeRules()` is the rule set after the resolution pass: sorted, normalised strings, each distinct rule once. A rule `X IS NOT Y` cancels every `X IS Y`, wherever each is written; the negated rule itself stays in the set, because it keeps overriding. A rule `X IS X` that survives cancels every `X IS Y` whose `Y` is a noun other than `X` (Baba can't convert). The base rule `TEXT IS PUSH` is in it unless `TEXT IS NOT PUSH` cancels it.
|
|
- `hasProperty(id, prop)` asks whether one object has a property under the active rules. A thing is named by its noun (a `baba` object by `BABA`); a text object is named by `TEXT`. The object has the property if an active rule gives it to one of its names and no active negated rule takes it away from one of its names.
|
|
|
|
`X IS NOT X` is decided here and acted on later. It cancels `X IS X` (it is the negation of that rule, like any other), so other `X IS Y` conversions work again, and it stays in `activeRules()` as the record that every `X` is to vanish. Removing the objects is part of the turn, in Module 9. The tests check the cancellation and the unlocked conversion, which the sources state; they do not check that `"BABA IS NOT BABA"` is listed in `activeRules()`, which is this course's way of recording the deletion, not something the sources describe.
|
|
|
|
`NOT` in front of a complement negates that complement only: `BABA IS NOT YOU AND WIN` is `BABA IS NOT YOU` and `BABA IS WIN`. `NOT` in front of a subject is not part of the course's grammar: the sentence it stands in spells nothing.
|
|
|
|
Words used: the nouns `BABA`, `ROCK`; the properties `YOU`, `PUSH`; the operators `IS`, `NOT`; the meta-noun `TEXT`.
|
|
|
|
### test_07_not_overrides
|
|
|
|
- Setup: `newBaba` with an empty level 4 wide and 5 high. Place `BABA (0,0) IS (1,0) YOU (2,0)` and an object `baba` at (0,4).
|
|
- Assertions 1: `hasProperty(baba, "YOU")` is true.
|
|
- Action: place `BABA (0,2) IS (1,2) NOT (2,2) YOU (3,2)`.
|
|
- Assertions 2:
|
|
- `rules()` is exactly `["BABA IS NOT YOU", "BABA IS YOU", "TEXT IS PUSH"]`: both sentences are still on the board.
|
|
- `activeRules()` is exactly `["BABA IS NOT YOU", "TEXT IS PUSH"]`: the negation overrides `BABA IS YOU`, although that rule is written above it.
|
|
- `hasProperty(baba, "YOU")` is false.
|
|
|
|
### test_07_x_is_x_blocks_conversion
|
|
|
|
- Setup: `newBaba` with an empty level 3 wide and 5 high. Place `BABA (0,0) IS (1,0) BABA (2,0)`, `BABA (0,2) IS (1,2) ROCK (2,2)` and `ROCK (0,4) IS (1,4) BABA (2,4)`.
|
|
- Assertions:
|
|
- `rules()` is exactly `["BABA IS BABA", "BABA IS ROCK", "ROCK IS BABA", "TEXT IS PUSH"]`.
|
|
- `activeRules()` is exactly `["BABA IS BABA", "ROCK IS BABA", "TEXT IS PUSH"]`. `BABA IS BABA` blocks Baba from converting into anything, so `BABA IS ROCK` is not active. It doesn't block other things from converting into Baba, so `ROCK IS BABA` is.
|
|
|
|
### test_07_x_is_not_x_deletes_and_unlocks
|
|
|
|
- Setup: `newBaba` with an empty level 4 wide and 5 high. Place `BABA (0,0) IS (1,0) NOT (2,0) BABA (3,0)`, `BABA (0,2) IS (1,2) BABA (2,2)` and `BABA (0,4) IS (1,4) ROCK (2,4)`.
|
|
- Assertions:
|
|
- `rules()` is exactly `["BABA IS BABA", "BABA IS NOT BABA", "BABA IS ROCK", "TEXT IS PUSH"]`.
|
|
- `activeRules()` contains `"BABA IS ROCK"` and does not contain `"BABA IS BABA"`. `BABA IS NOT BABA` cancels the lock, so the conversion is active again. (Baba is also flagged to vanish; see the note above on what the test leaves open.)
|
|
|
|
### test_07_text_is_not_push
|
|
|
|
- Setup: `newBaba` with an empty level 4 wide and 3 high. Place a text `text:ROCK` at (0,2) and an object `rock` at (3,2).
|
|
- Assertions 1:
|
|
- `activeRules()` is exactly `["TEXT IS PUSH"]`.
|
|
- `hasProperty(rockText, "PUSH")` is true: the base rule applies to every text object.
|
|
- `hasProperty(rock, "PUSH")` is false: `TEXT` names text, not the rock the text names.
|
|
- Action: place `TEXT (0,0) IS (1,0) NOT (2,0) PUSH (3,0)`.
|
|
- Assertions 2:
|
|
- `rules()` is exactly `["TEXT IS NOT PUSH", "TEXT IS PUSH"]`.
|
|
- `activeRules()` is exactly `["TEXT IS NOT PUSH"]`: the base rule is a rule like any other and NOT cancels it.
|
|
- `hasProperty(rockText, "PUSH")` is false, and so is `hasProperty` of the `TEXT` text at (0,0).
|
|
|
|
### test_07_leading_not_breaks_sentence
|
|
|
|
- Setup: `newBaba` with an empty level 4 wide and 2 high. Place `NOT (0,0) BABA (1,0) IS (2,0) YOU (3,0)` and an object `baba` at (0,1).
|
|
- Assertions:
|
|
- `rules()` and `activeRules()` are both exactly `["TEXT IS PUSH"]`. A `NOT` in front of the subject is not in the course's grammar, and the parser must not read the rest of the sentence as `BABA IS YOU`: the `NOT` is there, and dropping it would change the meaning.
|
|
- `hasProperty(baba, "YOU")` is false.
|