--- /dev/null
+#!/usr/bin/env python3
+"""Regression + oracle test harness for truth.bas (Truth table calculator).
+
+How it works:
+ 1. For every formula in formulas.txt the harness drives the .bas program
+ in crtbasic's headless GameDriver: it types the formula, presses Enter,
+ waits for the computation, takes a *text dump* of the graphics screen
+ (GameDriver --textdump recognizes the 8x8 font cells), satisfies the
+ closing INPUT$(1) with 'x' and quits with ESC.
+ 2. Oracle check: an independent Python implementation of the program's
+ logic (same operator semantics: NOT tightest, AND/OR equal precedence
+ left-associative, then IMPLIES, then EQUIV; 't'/'v' are true/false
+ constants, not variables) recomputes every variable column and the
+ final result column and compares them against the dump.
+ 3. Regression check: the full 80x60 text dump is diffed byte-for-byte
+ against the baseline recorded in expected/ from the original program.
+ Readability improvements must not change a single pixel.
+
+Usage:
+ run_tests.py --record # record baselines from the CURRENT truth.bas
+ run_tests.py # check truth.bas against baselines + oracle
+ run_tests.py --bas PATH # test a different .bas file
+"""
+
+import subprocess
+import sys
+import tempfile
+from pathlib import Path
+
+HERE = Path(__file__).resolve().parent
+DEFAULT_BAS = HERE.parent / "truth.bas"
+FORMULAS = HERE / "formulas.txt"
+EXPECTED = HERE / "expected"
+CRTBASIC = Path("/home/n0/workspace/svjatoslav/crtbasic")
+DRIVER_CP = "target/classes:target/test-classes"
+DRIVER_CLASS = "eu.svjatoslav.crtbasic.drivers.GameDriver"
+
+# Key script template: type formula, Enter, settle, $ = dump screen,
+# 'x' satisfies INPUT$(1), ESC quits the menu (SYSTEM).
+KEYS_TEMPLATE = "{formula}\n~~$~x~\x1b"
+
+OP_NAMES = {"1": "EQUIV", "2": "IMPLIES", "3": "OR", "4": "AND", "5": "NOT"}
+
+
+# ---------------------------------------------------------------- oracle --
+
+class Parser:
+ """Recursive-descent parser mirroring the program's operator semantics."""
+
+ def __init__(self, text: str):
+ self.tokens = list(text)
+ self.pos = 0
+
+ def peek(self) -> str:
+ return self.tokens[self.pos] if self.pos < len(self.tokens) else ""
+
+ def take(self) -> str:
+ tok = self.tokens[self.pos]
+ self.pos += 1
+ return tok
+
+ def parse_equiv(self):
+ node = self.parse_implies()
+ while self.peek() == "1":
+ self.take()
+ node = ("equiv", node, self.parse_implies())
+ return node
+
+ def parse_implies(self):
+ node = self.parse_andor()
+ while self.peek() == "2":
+ self.take()
+ node = ("implies", node, self.parse_andor())
+ return node
+
+ def parse_andor(self):
+ node = self.parse_operand()
+ while self.peek() in ("3", "4"):
+ op = "or" if self.take() == "3" else "and"
+ node = (op, node, self.parse_operand())
+ return node
+
+ def parse_operand(self):
+ tok = self.peek()
+ if tok == "5":
+ self.take()
+ return ("not", self.parse_operand())
+ if tok == "(":
+ self.take()
+ node = self.parse_equiv()
+ assert self.take() == ")", "missing closing paren"
+ return node
+ self.take()
+ return ("var", tok)
+
+
+def evaluate(node, values: dict) -> bool:
+ kind = node[0]
+ if kind == "var":
+ name = node[1]
+ if name == "t":
+ return True
+ if name == "v":
+ return False
+ return values[name]
+ if kind == "not":
+ return not evaluate(node[1], values)
+ left, right = evaluate(node[1], values), evaluate(node[2], values)
+ if kind == "and":
+ return left and right
+ if kind == "or":
+ return left or right
+ if kind == "implies":
+ return (not left) or right
+ if kind == "equiv":
+ return left == right
+ raise ValueError(kind)
+
+
+def variables_of(formula: str) -> list:
+ """Distinct variables in first-appearance order; t/v are constants."""
+ names = []
+ for ch in formula:
+ if ch.isalpha() and ch not in ("t", "v") and ch not in names:
+ names.append(ch)
+ return names
+
+
+def combo_values(names: list, row: int) -> dict:
+ """Variable values for combo row (1-based), mirroring lahend():
+ variable i (1-based) flips every 2**(nm-i) rows, starting at true."""
+ nm = len(names)
+ values = {}
+ for i, name in enumerate(names):
+ block = 2 ** (nm - i - 1)
+ values[name] = ((row - 1) // block) % 2 == 0
+ return values
+
+
+def oracle_check(name: str, formula: str, dump: str) -> list:
+ """Compare variable columns and the result column against the oracle."""
+ errors = []
+ stripped = formula.replace(" ", "")
+ names = variables_of(formula)
+ var_columns = [(i, ch) for i, ch in enumerate(stripped) if ch in names]
+ rows = dump.split("\n")
+ ast = Parser(stripped).parse_equiv()
+ combos = 2 ** len(names)
+ for row in range(1, combos + 1):
+ if row >= len(rows):
+ errors.append(f"{name}: dump has no row {row}")
+ break
+ line = rows[row]
+ values = combo_values(names, row)
+ for col, ch in var_columns:
+ want = "t" if values[ch] else "v"
+ got = line[col] if col < len(line) else "?"
+ if got != want:
+ errors.append(
+ f"{name} row {row} col {col} (variable {ch}): want {want}, got {got!r}")
+ # The result column is the rightmost t/v cell in the row. Its
+ # position shifts right by the number of parentheses teeslg
+ # inserted: truth.bas DECLAREs teeslg's second parameter as x2
+ # while the SUB itself names it x4, and crtbasic registers one
+ # cell under both names, so teeslg's x2 writes land in lahend's
+ # x2. The column is located, not computed.
+ result_col = max(line.rfind("t"), line.rfind("v"))
+ if result_col < 0:
+ errors.append(f"{name} row {row}: no result cell found")
+ continue
+ want = "t" if evaluate(ast, values) else "v"
+ got = line[result_col]
+ if got != want:
+ errors.append(
+ f"{name} row {row} col {result_col} (result): want {want}, got {got!r}")
+ # Below the table the screen must be blank.
+ for row in range(combos + 1, len(rows)):
+ if rows[row].strip():
+ errors.append(f"{name}: unexpected content below table at row {row}: "
+ f"{rows[row].strip()!r}")
+ break
+ return errors
+
+
+# ---------------------------------------------------------------- driver --
+
+def run_formula(bas: Path, name: str, formula: str, workdir: Path) -> str:
+ keys = KEYS_TEMPLATE.format(formula=formula)
+ cmd = [
+ "java", "-cp", DRIVER_CP, DRIVER_CLASS, str(bas),
+ "--steps=200000000", "--interval=60", "--block",
+ f"--screenshot={workdir / name}.png",
+ f"--textdump={workdir / name}.txt",
+ f"--keys={keys}",
+ ]
+ proc = subprocess.run(cmd, cwd=CRTBASIC, capture_output=True, text=True, timeout=300)
+ if proc.returncode != 0:
+ raise RuntimeError(f"{name}: driver failed:\n{proc.stdout}\n{proc.stderr}")
+ dump_path = workdir / f"{name}-1.txt"
+ if not dump_path.exists():
+ raise RuntimeError(f"{name}: no text dump produced:\n{proc.stdout}\n{proc.stderr}")
+ return dump_path.read_text()
+
+
+def load_formulas() -> list:
+ entries = []
+ for line in FORMULAS.read_text().splitlines():
+ line = line.strip()
+ if not line or line.startswith("#"):
+ continue
+ name, formula = line.split("\t")
+ entries.append((name, formula))
+ return entries
+
+
+def main() -> int:
+ record = "--record" in sys.argv
+ bas = DEFAULT_BAS
+ if "--bas" in sys.argv:
+ bas = Path(sys.argv[sys.argv.index("--bas") + 1])
+ formulas = load_formulas()
+ print(f"Program: {bas}")
+ print(f"Formulas: {len(formulas)}")
+ failures = 0
+ with tempfile.TemporaryDirectory(prefix="truthtest-") as tmp:
+ workdir = Path(tmp)
+ for name, formula in formulas:
+ dump = run_formula(bas, name, formula, workdir)
+ errors = oracle_check(name, formula, dump)
+ for err in errors:
+ print(f"ORACLE FAIL: {err}")
+ failures += 1
+ expected_path = EXPECTED / f"{name}.txt"
+ if record:
+ EXPECTED.mkdir(exist_ok=True)
+ expected_path.write_text(dump)
+ elif expected_path.exists():
+ baseline = expected_path.read_text()
+ if dump != baseline:
+ failures += 1
+ print(f"REGRESSION: {name}: dump differs from baseline")
+ old, new = baseline.split("\n"), dump.split("\n")
+ shown = 0
+ for row in range(max(len(old), len(new))):
+ before = old[row] if row < len(old) else "<missing>"
+ after = new[row] if row < len(new) else "<missing>"
+ if before != after and shown < 5:
+ print(f" row {row}: baseline {before.strip()!r} -> got {after.strip()!r}")
+ shown += 1
+ else:
+ failures += 1
+ print(f"MISSING BASELINE: {name} (run with --record)")
+ status = "recorded" if record else ("ok" if not errors else "FAILED")
+ print(f" {name}: {status}")
+ if failures:
+ print(f"\n{failures} failure(s)")
+ return 1
+ print("\nAll checks passed")
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
' Changelog:\r
' 2002, Initial version\r
' 2024 - 2025, Improved program readability\r
+' 2026, Second readability pass: translated the remaining Estonian names\r
+' (sist, lahend, tee, teeslg, lend, mov), documented the operator\r
+' encoding and the addPrecedenceParens/solveEquation parameter\r
+' aliasing. Algorithm unchanged.\r
'\r
'\r
' A truth table is a mathematical table used to determine the output of a\r
' understanding digital circuits, Boolean algebra, and logical\r
' expressions.\r
'\r
-' Implemented operations:\r
+' Implemented operations (typed with digit keys, stored as CHR$(1)..CHR$(5)):\r
' Equivalent ( Keyboard shortcut: 1 )\r
' Implies ( Keyboard shortcut: 2 )\r
' OR ( Keyboard shortcut: 3 )\r
' AND ( Keyboard shortcut: 4 )\r
' NOT ( Keyboard shortcut: 5 )\r
+'\r
+' The letters t and v are NOT variables: they are the truth constants\r
+' (Estonian tosi = true, vaar = false). Every other letter becomes a\r
+' variable of the table.\r
\r
DECLARE SUB removeRedundancies (startIndex!, endIndex!, removalCount!)\r
-DECLARE SUB getOperatorPriority (a!, b!)\r
-DECLARE SUB movM (x1!, n!)\r
-DECLARE SUB clearScreenBuffer ()\r
-DECLARE SUB lendm (x1!, m!)\r
-DECLARE SUB mov (x1!, n!)\r
-DECLARE SUB lendp (x1!, m!)\r
-DECLARE SUB teeslg (x1!, x2!, l!)\r
-DECLARE SUB prepare ()\r
-DECLARE SUB tee (x1!, x2!)\r
-DECLARE SUB lahend (x1, x2)\r
-DECLARE SUB printText (x!, y!, c!, c1!, a$)\r
-DECLARE SUB sist ()\r
-DECLARE SUB start ()\r
+DECLARE SUB getOperatorPriority (operatorCode!, priority!)\r
+DECLARE SUB shiftExpressionLeft (position!, count!)\r
+DECLARE SUB drainKeyboardBuffer ()\r
+DECLARE SUB operandLengthBefore (position!, outLength!)\r
+DECLARE SUB shiftExpressionRight (position!, count!)\r
+DECLARE SUB operandLengthAfter (position!, outLength!)\r
+DECLARE SUB addPrecedenceParens (exprStart!, exprEnd!, addedChars!)\r
+DECLARE SUB processEquation ()\r
+DECLARE SUB evaluateExpression (exprStart!, exprEnd!)\r
+DECLARE SUB solveEquation (exprStart, exprEnd)\r
+DECLARE SUB printText (column!, row!, foreground!, background!, text$)\r
+DECLARE SUB inputEquation ()\r
+DECLARE SUB initializeDisplay ()\r
DIM SHARED font(0 TO 7, 0 TO 7, 0 TO 122)\r
\r
' Logical expression storage and processing arrays\r
DIM SHARED variableNames(1 TO 8) ' Stores ASCII values of variable names\r
DIM SHARED resultValues(1 TO 100) ' Stores computed result values\r
DIM SHARED expressionPosition(0 TO 79) ' Stores screen positions of expression characters\r
-DIM SHARED xlahn\r
-DIM SHARED tehl\r
-DIM SHARED nm\r
-DIM SHARED prnp\r
+DIM SHARED xlahn ' Unused legacy variable (kept from the 2002 original)\r
+DIM SHARED combinationCount ' Number of truth-table rows: 2 ^ variableCount\r
+DIM SHARED variableCount ' Number of variables, plus 2 for the t/v constants\r
+DIM SHARED printRowOffset ' Vertical offset for printText (always 0)\r
\r
-start\r
+initializeDisplay\r
\r
13\r
-sist\r
-prepare\r
+inputEquation\r
+processEquation\r
GOTO 13\r
\r
-SUB clearScreenBuffer\r
-' Waits for user input to clear the screen buffer\r
-FOR a = 1 TO 50\r
- a$ = INKEY$\r
-NEXT a\r
+SUB drainKeyboardBuffer\r
+' Throws away up to 50 pending keystrokes.\r
+' NOTE: currently never called - kept from the 2002 original.\r
+FOR flushIndex = 1 TO 50\r
+ key$ = INKEY$\r
+NEXT flushIndex\r
END SUB\r
\r
-SUB getOperatorPriority (operator, priority)\r
-' Determines the priority of logical operators\r
-SELECT CASE operator\r
+SUB getOperatorPriority (operatorCode, priority)\r
+' Determines the priority of logical operators.\r
+' NOTE: only used by removeRedundancies, which itself is never called.\r
+SELECT CASE operatorCode\r
CASE 5 ' NOT\r
priority = 1\r
CASE 3, 4 ' OR, AND\r
END SUB\r
\r
\r
-SUB lahend (x1, x2)\r
-' Analyzes and prepares the logical equation for solving\r
-DIM muu(65 TO 122)\r
-FOR a = 65 TO 122\r
- muu(a) = 0\r
-NEXT a\r
-\r
-muu(116) = 1 ' t\r
-muu(118) = 1 ' v\r
-\r
-nm = 0\r
-FOR a = x1 TO x2\r
- b = logicalExpression(a)\r
- IF ((b >= 65) AND (b <= 90)) OR ((b >= 97) AND (b <= 122)) THEN\r
- IF muu(b) = 0 THEN\r
- nm = nm + 1\r
- variableNames(nm) = b\r
- muu(b) = 1\r
+SUB solveEquation (exprStart, exprEnd)\r
+' Analyzes the logical equation and computes the whole truth table:\r
+' collects the distinct variables, builds every input combination,\r
+' lets addPrecedenceParens parenthesize the expression by operator\r
+' priority, evaluates it for every row and prints the result column.\r
+DIM seenChar(65 TO 122)\r
+FOR charIndex = 65 TO 122\r
+ seenChar(charIndex) = 0\r
+NEXT charIndex\r
+\r
+seenChar(116) = 1 ' t (true constant, never a variable)\r
+seenChar(118) = 1 ' v (false constant, never a variable)\r
+\r
+variableCount = 0\r
+FOR charIndex = exprStart TO exprEnd\r
+ charCode = logicalExpression(charIndex)\r
+ IF ((charCode >= 65) AND (charCode <= 90)) OR ((charCode >= 97) AND (charCode <= 122)) THEN\r
+ IF seenChar(charCode) = 0 THEN\r
+ variableCount = variableCount + 1\r
+ variableNames(variableCount) = charCode\r
+ seenChar(charCode) = 1\r
END IF\r
END IF\r
-NEXT a\r
-\r
-variableNames(nm + 1) = 116 ' t\r
-variableNames(nm + 2) = 118 ' v\r
-\r
-f = 2 ^ nm\r
-tehl = f\r
-FOR a = 1 TO nm\r
- d = 1\r
- e = 1\r
- f = f / 2\r
- FOR b = 1 TO 2 ^ nm\r
- IF e > f THEN d = -d: e = 1\r
- IF d = 1 THEN c = ASC("t") ELSE c = ASC("v")\r
- variableValues(a, b) = c\r
- e = e + 1\r
- NEXT b\r
-NEXT a\r
-\r
-FOR a = 1 TO tehl\r
- variableValues(nm + 1, a) = 116 ' t\r
- variableValues(nm + 2, a) = 118 ' v\r
-NEXT a\r
-\r
-nm = nm + 2\r
-\r
-DIM bck(0 TO 79)\r
-FOR a = 0 TO 79\r
- bck(a) = logicalExpression(a)\r
- expressionPosition(a) = a\r
-NEXT a\r
+NEXT charIndex\r
+\r
+variableNames(variableCount + 1) = 116 ' t\r
+variableNames(variableCount + 2) = 118 ' v\r
+\r
+blockSize = 2 ^ variableCount\r
+combinationCount = blockSize\r
+FOR variableIndex = 1 TO variableCount\r
+ truthToggle = 1\r
+ runCount = 1\r
+ blockSize = blockSize / 2\r
+ FOR comboRow = 1 TO 2 ^ variableCount\r
+ ' Alternate runs of t and v; each next variable's runs are\r
+ ' half as long, so the first variable changes slowest.\r
+ IF runCount > blockSize THEN truthToggle = -truthToggle: runCount = 1\r
+ IF truthToggle = 1 THEN cellValue = ASC("t") ELSE cellValue = ASC("v")\r
+ variableValues(variableIndex, comboRow) = cellValue\r
+ runCount = runCount + 1\r
+ NEXT comboRow\r
+NEXT variableIndex\r
+\r
+FOR comboRow = 1 TO combinationCount\r
+ variableValues(variableCount + 1, comboRow) = 116 ' t\r
+ variableValues(variableCount + 2, comboRow) = 118 ' v\r
+NEXT comboRow\r
+\r
+variableCount = variableCount + 2\r
+\r
+DIM expressionBackup(0 TO 79)\r
+FOR charIndex = 0 TO 79\r
+ expressionBackup(charIndex) = logicalExpression(charIndex)\r
+ expressionPosition(charIndex) = charIndex\r
+NEXT charIndex\r
\r
LOCATE 5, 1\r
-teeslg x1, x2, a\r
+' charIndex returns how many parenthesis characters addPrecedenceParens\r
+' inserted, so evaluateExpression receives the extended expression range.\r
+addPrecedenceParens exprStart, exprEnd, charIndex\r
\r
-tee x1, x2 + a\r
+evaluateExpression exprStart, exprEnd + charIndex\r
\r
-FOR a = 0 TO 79\r
- logicalExpression(a) = bck(a)\r
-NEXT a\r
+FOR charIndex = 0 TO 79\r
+ logicalExpression(charIndex) = expressionBackup(charIndex)\r
+NEXT charIndex\r
\r
-FOR a = 1 TO tehl\r
- printText x2 + 1, a, 14, 0, CHR$(resultValues(a))\r
-NEXT a\r
+FOR comboRow = 1 TO combinationCount\r
+ printText exprEnd + 1, comboRow, 14, 0, CHR$(resultValues(comboRow))\r
+NEXT comboRow\r
\r
END SUB\r
\r
-SUB lendm (x1, m)\r
-' Measures the length of a logical expression enclosed in parentheses\r
-IF logicalExpression(x1) <> 41 THEN m = 1: GOTO 19\r
-c = x1\r
-d = 1\r
+SUB operandLengthBefore (position, outLength)\r
+' Measures the length of the operand ending just before position:\r
+' either a single character, or a parenthesized group scanned backwards.\r
+IF logicalExpression(position) <> 41 THEN outLength = 1: GOTO 19\r
+scanPos = position\r
+parenDepth = 1\r
20\r
-c = c - 1\r
-IF logicalExpression(c) = 40 THEN d = d - 1\r
-IF logicalExpression(c) = 41 THEN d = d + 1\r
-IF d > 0 THEN GOTO 20\r
-m = x1 - c\r
+scanPos = scanPos - 1\r
+IF logicalExpression(scanPos) = 40 THEN parenDepth = parenDepth - 1\r
+IF logicalExpression(scanPos) = 41 THEN parenDepth = parenDepth + 1\r
+IF parenDepth > 0 THEN GOTO 20\r
+outLength = position - scanPos\r
19\r
END SUB\r
\r
-SUB lendp (x1, m)\r
-' Measures the length of a logical expression enclosed in parentheses\r
-IF logicalExpression(x1) <> 40 THEN m = 1: GOTO 17\r
-c = x1\r
-d = 1\r
+SUB operandLengthAfter (position, outLength)\r
+' Measures the length of the operand starting at position:\r
+' either a single character, or a parenthesized group scanned forwards.\r
+IF logicalExpression(position) <> 40 THEN outLength = 1: GOTO 17\r
+scanPos = position\r
+parenDepth = 1\r
18\r
-c = c + 1\r
-IF logicalExpression(c) = 40 THEN d = d + 1\r
-IF logicalExpression(c) = 41 THEN d = d - 1\r
-IF d > 0 THEN GOTO 18\r
-m = c - x1 + 1\r
+scanPos = scanPos + 1\r
+IF logicalExpression(scanPos) = 40 THEN parenDepth = parenDepth + 1\r
+IF logicalExpression(scanPos) = 41 THEN parenDepth = parenDepth - 1\r
+IF parenDepth > 0 THEN GOTO 18\r
+outLength = scanPos - position + 1\r
17\r
END SUB\r
\r
-SUB mov (x1, n)\r
-' Moves a portion of the logical expression to the right\r
-FOR a = 79 - n TO x1 STEP -1\r
- logicalExpression(a + n) = logicalExpression(a)\r
- expressionPosition(a + n) = expressionPosition(a)\r
-NEXT a\r
+SUB shiftExpressionRight (position, count)\r
+' Moves a portion of the logical expression to the right,\r
+' opening a gap of count characters at position.\r
+FOR charIndex = 79 - count TO position STEP -1\r
+ logicalExpression(charIndex + count) = logicalExpression(charIndex)\r
+ expressionPosition(charIndex + count) = expressionPosition(charIndex)\r
+NEXT charIndex\r
END SUB\r
\r
-SUB movM (x1, n)\r
-' Moves a portion of the logical expression to the left\r
-FOR a = x1 TO 79 - n\r
- logicalExpression(a) = logicalExpression(a + n)\r
- expressionPosition(a) = expressionPosition(a + n)\r
-NEXT a\r
+SUB shiftExpressionLeft (position, count)\r
+' Moves a portion of the logical expression to the left,\r
+' deleting count characters at position.\r
+FOR charIndex = position TO 79 - count\r
+ logicalExpression(charIndex) = logicalExpression(charIndex + count)\r
+ expressionPosition(charIndex) = expressionPosition(charIndex + count)\r
+NEXT charIndex\r
END SUB\r
\r
-SUB prepare\r
-' Prepares the logical equation for processing\r
+SUB processEquation\r
+' Prepares the typed equation for processing: strips spaces and empty\r
+' cells, prints it, solves it, then waits for a keypress.\r
CLS\r
\r
-ln = 79\r
-FOR a = 0 TO 79\r
+lastCharIndex = 79\r
+FOR charIndex = 0 TO 79\r
5\r
- IF logicalExpression(a) = 32 OR logicalExpression(a) = 0 THEN\r
- FOR b = a TO 78\r
- logicalExpression(b) = logicalExpression(b + 1)\r
- NEXT b\r
- ln = ln - 1\r
- IF ln <= a - 1 THEN GOTO 6\r
+ IF logicalExpression(charIndex) = 32 OR logicalExpression(charIndex) = 0 THEN\r
+ FOR shiftIndex = charIndex TO 78\r
+ logicalExpression(shiftIndex) = logicalExpression(shiftIndex + 1)\r
+ NEXT shiftIndex\r
+ lastCharIndex = lastCharIndex - 1\r
+ IF lastCharIndex <= charIndex - 1 THEN GOTO 6\r
GOTO 5\r
END IF\r
-NEXT a\r
+NEXT charIndex\r
6\r
\r
CLS\r
\r
-FOR a = 0 TO ln\r
- printText a, 0, 13, 1, CHR$(logicalExpression(a))\r
-NEXT a\r
+FOR charIndex = 0 TO lastCharIndex\r
+ printText charIndex, 0, 13, 1, CHR$(logicalExpression(charIndex))\r
+NEXT charIndex\r
\r
printText 0, 1, 7, 0, SPACE$(79)\r
\r
-lahend 0, ln\r
+solveEquation 0, lastCharIndex\r
\r
-a$ = INPUT$(1)\r
+key$ = INPUT$(1)\r
\r
END SUB\r
\r
-SUB printText (x, y, c, c1, a$)\r
- ' Prints characters to the screen at location (x,y) with color c, background c1\r
- x1 = x * 8\r
- y1 = (y + prnp) * 8\r
-\r
- FOR b = 1 TO LEN(a$)\r
- LINE (x1, y1)-(x1 + 7, y1 + 7), c1, BF\r
- d = ASC(RIGHT$(LEFT$(a$, b), 1))\r
- IF d > 122 THEN GOTO 22\r
- FOR y2 = 0 TO 7\r
- FOR x2 = 0 TO 7\r
- c2 = font(x2, y2, d)\r
- IF c2 > 0 THEN PSET (x1 + x2, y1 + y2), c\r
- NEXT x2\r
- NEXT y2\r
-22 x1 = x1 + 8\r
- NEXT b\r
+SUB printText (column, row, foreground, background, text$)\r
+ ' Prints characters to the screen at location (column,row) with color\r
+ ' foreground, background background, using the 8x8 font bitmaps that\r
+ ' initializeDisplay captured at startup.\r
+ pixelX = column * 8\r
+ pixelY = (row + printRowOffset) * 8\r
+\r
+ FOR charIndex = 1 TO LEN(text$)\r
+ LINE (pixelX, pixelY)-(pixelX + 7, pixelY + 7), background, BF\r
+ glyphCode = ASC(RIGHT$(LEFT$(text$, charIndex), 1))\r
+ IF glyphCode > 122 THEN GOTO 22\r
+ FOR glyphRow = 0 TO 7\r
+ FOR glyphCol = 0 TO 7\r
+ pixelValue = font(glyphCol, glyphRow, glyphCode)\r
+ IF pixelValue > 0 THEN PSET (pixelX + glyphCol, pixelY + glyphRow), foreground\r
+ NEXT glyphCol\r
+ NEXT glyphRow\r
+22 pixelX = pixelX + 8\r
+ NEXT charIndex\r
\r
END SUB\r
\r
SUB removeRedundancies (startIndex, endIndex, removalCount)\r
' This procedure scans for parentheses that can be safely removed\r
' without changing the logic of the expression, then removes them.\r
+ ' NOTE: currently never called - an abandoned feature of the 2002\r
+ ' original, kept as reference.\r
\r
DIM currentEnd, parenthesesCount\r
currentEnd = endIndex\r
parenthesesCount = 0\r
\r
- a = startIndex\r
-26 IF logicalExpression(a) = 40 THEN\r
+ charIndex = startIndex\r
+26 IF logicalExpression(charIndex) = 40 THEN\r
' We found an opening parenthesis. Now let's see if it can be removed.\r
- IF a = startIndex THEN p1 = 100 ELSE getOperatorPriority logicalExpression(a - 1), p1\r
+ IF charIndex = startIndex THEN priorityBefore = 100 ELSE getOperatorPriority logicalExpression(charIndex - 1), priorityBefore\r
\r
- c = a\r
- d = 1\r
- p2 = 0\r
+ closePos = charIndex\r
+ parenDepth = 1\r
+ priorityInside = 0\r
\r
-25 c = c + 1\r
- IF logicalExpression(c) = 40 THEN d = d + 1\r
- IF logicalExpression(c) = 41 THEN d = d - 1\r
+25 closePos = closePos + 1\r
+ IF logicalExpression(closePos) = 40 THEN parenDepth = parenDepth + 1\r
+ IF logicalExpression(closePos) = 41 THEN parenDepth = parenDepth - 1\r
\r
- ' Once d returns to 1, we are back to one level of parentheses,\r
+ ' Once parenDepth returns to 1, we are back to one level of parentheses,\r
' meaning we can check operator priority inside.\r
- IF d = 1 THEN\r
- IF (logicalExpression(c) > 0) AND (logicalExpression(c) <= 5) THEN\r
- getOperatorPriority logicalExpression(c), b\r
- IF b > p2 THEN p2 = b\r
+ IF parenDepth = 1 THEN\r
+ IF (logicalExpression(closePos) > 0) AND (logicalExpression(closePos) <= 5) THEN\r
+ getOperatorPriority logicalExpression(closePos), opPriority\r
+ IF opPriority > priorityInside THEN priorityInside = opPriority\r
END IF\r
END IF\r
\r
- IF d > 0 THEN GOTO 25\r
+ IF parenDepth > 0 THEN GOTO 25\r
\r
- IF c + 1 > currentEnd THEN p3 = 100 ELSE getOperatorPriority logicalExpression(c + 1), p3\r
+ IF closePos + 1 > currentEnd THEN priorityAfter = 100 ELSE getOperatorPriority logicalExpression(closePos + 1), priorityAfter\r
\r
' If the operator outside is higher priority than what's inside,\r
' we can safely remove the parentheses.\r
- IF (p1 > p2) AND (p3 >= p2) THEN\r
- movM c, 1\r
- movM a, 1\r
+ IF (priorityBefore > priorityInside) AND (priorityAfter >= priorityInside) THEN\r
+ shiftExpressionLeft closePos, 1\r
+ shiftExpressionLeft charIndex, 1\r
parenthesesCount = parenthesesCount + 2\r
currentEnd = currentEnd - 2\r
- a = a - 1\r
+ charIndex = charIndex - 1\r
END IF\r
END IF\r
\r
- a = a + 1\r
- IF a <= currentEnd THEN GOTO 26\r
+ charIndex = charIndex + 1\r
+ IF charIndex <= currentEnd THEN GOTO 26\r
\r
removalCount = parenthesesCount\r
END SUB\r
\r
-SUB sist\r
-' Interacts with the user to input a logical equation\r
+SUB inputEquation\r
+' Interacts with the user to input a logical equation.\r
+' Digit keys 1..5 insert the operator characters CHR$(1)..CHR$(5),\r
+' letters insert variables, arrows move the cursor, Enter finishes.\r
CLS\r
printText 0, 0, 3, 0, "Enter equation (ESC to quit) keys: 1 - " + CHR$(1) + " 2 - " + CHR$(2) + " 3 - " + CHR$(3) + " 4 - " + CHR$(4) + " 5 - " + CHR$(5)\r
printText 0, 1, 3, 0, "Example: a" + CHR$(1) + "b" + CHR$(2) + "(g" + CHR$(3) + "b)"\r
\r
-FOR a = 0 TO 79\r
- logicalExpression(a) = 0\r
-NEXT a\r
+FOR charIndex = 0 TO 79\r
+ logicalExpression(charIndex) = 0\r
+NEXT charIndex\r
\r
-x = 0\r
+cursorPos = 0\r
1\r
-FOR a = 0 TO 79\r
- IF a = x THEN printText a, 2, 14, 1, CHR$(logicalExpression(a)) ELSE printText a, 2, 3, 0, CHR$(logicalExpression(a))\r
-NEXT a\r
+FOR charIndex = 0 TO 79\r
+ IF charIndex = cursorPos THEN printText charIndex, 2, 14, 1, CHR$(logicalExpression(charIndex)) ELSE printText charIndex, 2, 3, 0, CHR$(logicalExpression(charIndex))\r
+NEXT charIndex\r
2\r
-a$ = INKEY$\r
-IF a$ = "" THEN GOTO 2\r
+key$ = INKEY$\r
+IF key$ = "" THEN GOTO 2\r
\r
-IF a$ = CHR$(27) THEN SYSTEM\r
-IF a$ = CHR$(0) + "M" THEN x = x + 1\r
-IF a$ = CHR$(0) + "K" THEN x = x - 1\r
-IF x < 0 THEN x = 0\r
-IF x > 79 THEN x = 79\r
+IF key$ = CHR$(27) THEN SYSTEM\r
+IF key$ = CHR$(0) + "M" THEN cursorPos = cursorPos + 1\r
+IF key$ = CHR$(0) + "K" THEN cursorPos = cursorPos - 1\r
+IF cursorPos < 0 THEN cursorPos = 0\r
+IF cursorPos > 79 THEN cursorPos = 79\r
\r
-IF LEN(a$) = 1 THEN\r
- SELECT CASE ASC(a$)\r
+IF LEN(key$) = 1 THEN\r
+ SELECT CASE ASC(key$)\r
CASE 32, 40, 41, 65 TO 90, 97 TO 122\r
3\r
- FOR a = 78 TO x STEP -1\r
- logicalExpression(a + 1) = logicalExpression(a)\r
- NEXT a\r
- logicalExpression(x) = ASC(a$)\r
- x = x + 1\r
+ FOR charIndex = 78 TO cursorPos STEP -1\r
+ logicalExpression(charIndex + 1) = logicalExpression(charIndex)\r
+ NEXT charIndex\r
+ logicalExpression(cursorPos) = ASC(key$)\r
+ cursorPos = cursorPos + 1\r
CASE 8\r
- IF x > 0 THEN\r
- FOR a = x - 1 TO 78\r
- logicalExpression(a) = logicalExpression(a + 1)\r
- NEXT a\r
- x = x - 1\r
+ IF cursorPos > 0 THEN\r
+ FOR charIndex = cursorPos - 1 TO 78\r
+ logicalExpression(charIndex) = logicalExpression(charIndex + 1)\r
+ NEXT charIndex\r
+ cursorPos = cursorPos - 1\r
END IF\r
CASE 49 TO 53\r
- a$ = CHR$(ASC(a$) - 48)\r
+ key$ = CHR$(ASC(key$) - 48)\r
GOTO 3\r
CASE 13\r
GOTO 4\r
\r
END SUB\r
\r
-SUB start\r
-' Initializes the screen and font\r
-prnp = 0\r
+SUB initializeDisplay\r
+' Initializes the screen and captures the font: every displayable\r
+' character is PRINTed once in SCREEN 7 and read back pixel by pixel\r
+' into the font() array, so printText can draw text in graphics modes.\r
+' The operator characters CHR$(1)..CHR$(5) have no glyphs, so their\r
+' bitmaps are drawn by hand with LINE.\r
+printRowOffset = 0\r
\r
SCREEN 7\r
\r
-FOR a = 0 TO 122\r
+FOR charCode = 0 TO 122\r
LOCATE 1, 1\r
- SELECT CASE a\r
+ SELECT CASE charCode\r
CASE 7\r
CASE 1\r
LINE (0, 0)-(7, 7), 0, BF\r
LINE (4, 1)-(4, 7), 15\r
\r
CASE ELSE\r
- PRINT CHR$(a)\r
+ PRINT CHR$(charCode)\r
END SELECT\r
\r
- FOR y = 0 TO 7\r
- FOR x = 0 TO 7\r
- font(x, y, a) = POINT(x, y)\r
- NEXT x\r
- NEXT y\r
-NEXT a\r
+ FOR pixelRow = 0 TO 7\r
+ FOR pixelCol = 0 TO 7\r
+ font(pixelCol, pixelRow, charCode) = POINT(pixelCol, pixelRow)\r
+ NEXT pixelCol\r
+ NEXT pixelRow\r
+NEXT charCode\r
\r
SCREEN 12\r
\r
END SUB\r
\r
-SUB tee (x1, x2)\r
-' Processes the logical equation and applies logical operations\r
-DIM opr(1 TO 2, 1 TO tehl)\r
-ng = 0\r
-ngx = 0\r
-oprm = 1\r
-oe = 0\r
-oex = 0\r
-\r
-FOR a = x1 TO x2\r
- b = logicalExpression(a)\r
- SELECT CASE b\r
+SUB evaluateExpression (exprStart, exprEnd)\r
+' Evaluates the logical equation for every truth-table row.\r
+' Parenthesized groups are evaluated recursively; the two operand\r
+' columns accumulate in operandValues(1..2, row) and every binary\r
+' operator combines them left to right back into slot 1. The final\r
+' column lands in resultValues().\r
+DIM operandValues(1 TO 2, 1 TO combinationCount)\r
+notPending = 0\r
+notPosition = 0\r
+operandSlot = 1\r
+pendingOperator = 0\r
+operatorPosition = 0\r
+\r
+FOR charIndex = exprStart TO exprEnd\r
+ charCode = logicalExpression(charIndex)\r
+ SELECT CASE charCode\r
CASE 40\r
- c = a\r
- d = 1\r
+ scanPos = charIndex\r
+ parenDepth = 1\r
10\r
- c = c + 1\r
- IF logicalExpression(c) = ASC("(") THEN d = d + 1\r
- IF logicalExpression(c) = ASC(")") THEN d = d - 1\r
- IF d = 0 THEN GOTO 11\r
+ scanPos = scanPos + 1\r
+ IF logicalExpression(scanPos) = ASC("(") THEN parenDepth = parenDepth + 1\r
+ IF logicalExpression(scanPos) = ASC(")") THEN parenDepth = parenDepth - 1\r
+ IF parenDepth = 0 THEN GOTO 11\r
GOTO 10\r
11\r
- tee a + 1, c - 1\r
- a = c\r
- FOR c = 1 TO tehl\r
- opr(oprm, c) = resultValues(c)\r
- NEXT c\r
+ evaluateExpression charIndex + 1, scanPos - 1\r
+ charIndex = scanPos\r
+ FOR comboRow = 1 TO combinationCount\r
+ operandValues(operandSlot, comboRow) = resultValues(comboRow)\r
+ NEXT comboRow\r
GOTO 12\r
CASE 5\r
- ng = 1\r
- ngx = a\r
+ notPending = 1\r
+ notPosition = charIndex\r
CASE 1 TO 4\r
- oe = b\r
- oex = a\r
+ pendingOperator = charCode\r
+ operatorPosition = charIndex\r
CASE 65 TO 90, 97 TO 122\r
- FOR c = 1 TO nm\r
- IF variableNames(c) = b THEN d = c: GOTO 8\r
- NEXT c\r
+ FOR lookupIndex = 1 TO variableCount\r
+ IF variableNames(lookupIndex) = charCode THEN variableIndex = lookupIndex: GOTO 8\r
+ NEXT lookupIndex\r
8\r
- FOR c = 1 TO tehl\r
- opr(oprm, c) = variableValues(d, c)\r
- printText expressionPosition(a), c, 3, 0, CHR$(variableValues(d, c))\r
- NEXT c\r
+ FOR comboRow = 1 TO combinationCount\r
+ operandValues(operandSlot, comboRow) = variableValues(variableIndex, comboRow)\r
+ printText expressionPosition(charIndex), comboRow, 3, 0, CHR$(variableValues(variableIndex, comboRow))\r
+ NEXT comboRow\r
12\r
- IF ng = 1 THEN GOSUB mkneg\r
- IF oprm = 2 THEN\r
- SELECT CASE oe\r
+ IF notPending = 1 THEN GOSUB mkneg\r
+ IF operandSlot = 2 THEN\r
+ SELECT CASE pendingOperator\r
CASE 1\r
- FOR c = 1 TO tehl\r
- d = opr(1, c)\r
- e = opr(2, c)\r
- IF d = e THEN f = ASC("t") ELSE f = ASC("v")\r
- opr(1, c) = f\r
- printText expressionPosition(oex), c, 12, 0, CHR$(f)\r
- NEXT c\r
+ FOR comboRow = 1 TO combinationCount\r
+ leftValue = operandValues(1, comboRow)\r
+ rightValue = operandValues(2, comboRow)\r
+ IF leftValue = rightValue THEN resultValue = ASC("t") ELSE resultValue = ASC("v")\r
+ operandValues(1, comboRow) = resultValue\r
+ printText expressionPosition(operatorPosition), comboRow, 12, 0, CHR$(resultValue)\r
+ NEXT comboRow\r
CASE 2\r
- FOR c = 1 TO tehl\r
- d = opr(1, c)\r
- e = opr(2, c)\r
- f = ASC("t")\r
- IF (d = ASC("t")) AND (e = ASC("v")) THEN f = ASC("v")\r
- opr(1, c) = f\r
- printText expressionPosition(oex), c, 12, 0, CHR$(f)\r
- NEXT c\r
+ FOR comboRow = 1 TO combinationCount\r
+ leftValue = operandValues(1, comboRow)\r
+ rightValue = operandValues(2, comboRow)\r
+ resultValue = ASC("t")\r
+ IF (leftValue = ASC("t")) AND (rightValue = ASC("v")) THEN resultValue = ASC("v")\r
+ operandValues(1, comboRow) = resultValue\r
+ printText expressionPosition(operatorPosition), comboRow, 12, 0, CHR$(resultValue)\r
+ NEXT comboRow\r
CASE 3\r
- FOR c = 1 TO tehl\r
- d = opr(1, c)\r
- e = opr(2, c)\r
- f = ASC("t")\r
- IF (d = ASC("v")) AND (e = ASC("v")) THEN f = ASC("v")\r
- opr(1, c) = f\r
- printText expressionPosition(oex), c, 12, 0, CHR$(f)\r
- NEXT c\r
+ FOR comboRow = 1 TO combinationCount\r
+ leftValue = operandValues(1, comboRow)\r
+ rightValue = operandValues(2, comboRow)\r
+ resultValue = ASC("t")\r
+ IF (leftValue = ASC("v")) AND (rightValue = ASC("v")) THEN resultValue = ASC("v")\r
+ operandValues(1, comboRow) = resultValue\r
+ printText expressionPosition(operatorPosition), comboRow, 12, 0, CHR$(resultValue)\r
+ NEXT comboRow\r
CASE 4\r
- FOR c = 1 TO tehl\r
- d = opr(1, c)\r
- e = opr(2, c)\r
- f = ASC("v")\r
- IF (d = ASC("t")) AND (e = ASC("t")) THEN f = ASC("t")\r
- opr(1, c) = f\r
- printText expressionPosition(oex), c, 12, 0, CHR$(f)\r
- NEXT c\r
+ FOR comboRow = 1 TO combinationCount\r
+ leftValue = operandValues(1, comboRow)\r
+ rightValue = operandValues(2, comboRow)\r
+ resultValue = ASC("v")\r
+ IF (leftValue = ASC("t")) AND (rightValue = ASC("t")) THEN resultValue = ASC("t")\r
+ operandValues(1, comboRow) = resultValue\r
+ printText expressionPosition(operatorPosition), comboRow, 12, 0, CHR$(resultValue)\r
+ NEXT comboRow\r
END SELECT\r
ELSE\r
- oprm = oprm + 1\r
+ operandSlot = operandSlot + 1\r
END IF\r
END SELECT\r
-NEXT a\r
+NEXT charIndex\r
\r
GOTO 9\r
\r
mkneg:\r
' NOT operation (negation) is applied to the current operand\r
- FOR c = 1 TO tehl\r
- d = opr(oprm, c)\r
- IF d = ASC("t") THEN d = ASC("v") ELSE d = ASC("t")\r
- printText expressionPosition(ngx), c, 4, 0, CHR$(d)\r
- opr(oprm, c) = d\r
- NEXT c\r
- ng = 0\r
+ FOR comboRow = 1 TO combinationCount\r
+ negatedValue = operandValues(operandSlot, comboRow)\r
+ IF negatedValue = ASC("t") THEN negatedValue = ASC("v") ELSE negatedValue = ASC("t")\r
+ printText expressionPosition(notPosition), comboRow, 4, 0, CHR$(negatedValue)\r
+ operandValues(operandSlot, comboRow) = negatedValue\r
+ NEXT comboRow\r
+ notPending = 0\r
RETURN\r
9\r
\r
-FOR c = 1 TO tehl\r
- resultValues(c) = opr(1, c)\r
-NEXT c\r
+FOR comboRow = 1 TO combinationCount\r
+ resultValues(comboRow) = operandValues(1, comboRow)\r
+NEXT comboRow\r
END SUB\r
\r
-SUB teeslg (x1, x4, l)\r
-' Prepares the logical equation for solving by simplifying expressions within parentheses\r
-x2 = x4\r
-h = 0\r
-FOR e = 1 TO 4\r
- g = 1\r
- a = x1\r
+SUB addPrecedenceParens (exprStart, rangeEnd, addedChars)\r
+' Parenthesizes the logical equation so that operator priority is\r
+' explicit: four passes wrap NOT first, then OR/AND, then Implies\r
+' (Equivalent needs no parentheses, it is the loosest). Each pass only\r
+' wraps an operator when the operator before it was looser (lastOpClass\r
+' tracks the previous operator's class) - otherwise plain left-to-right\r
+' evaluation already gives the right grouping.\r
+'\r
+' NOTE: the DECLARE statement names the second parameter exprEnd while\r
+' this definition names it rangeEnd. In QBasic DECLARE parameter names\r
+' are just placeholders, so exprEnd here is a plain local and only\r
+' addedChars carries the insertion count back to solveEquation.\r
+' (crtbasic versions before 2026-09-26 registered the argument cell\r
+' under both names, aliasing exprEnd to the caller's variable; fixed\r
+' to match QB45.)\r
+exprEnd = rangeEnd\r
+insertedTotal = 0\r
+FOR passIndex = 1 TO 4\r
+ lastOpClass = 1\r
+ charIndex = exprStart\r
21\r
- b = logicalExpression(a)\r
- IF b = 40 THEN\r
- c = a\r
- d = 1\r
+ charCode = logicalExpression(charIndex)\r
+ IF charCode = 40 THEN\r
+ scanPos = charIndex\r
+ parenDepth = 1\r
14\r
- c = c + 1\r
- IF logicalExpression(c) = ASC("(") THEN d = d + 1\r
- IF logicalExpression(c) = ASC(")") THEN d = d - 1\r
- IF d = 0 THEN GOTO 15\r
+ scanPos = scanPos + 1\r
+ IF logicalExpression(scanPos) = ASC("(") THEN parenDepth = parenDepth + 1\r
+ IF logicalExpression(scanPos) = ASC(")") THEN parenDepth = parenDepth - 1\r
+ IF parenDepth = 0 THEN GOTO 15\r
GOTO 14\r
15\r
- IF e = 1 THEN teeslg a + 1, c - 1, l ELSE l = 0\r
- a = c + l\r
- x2 = x2 + l\r
- h = h + l\r
+ IF passIndex = 1 THEN addPrecedenceParens charIndex + 1, scanPos - 1, addedChars ELSE addedChars = 0\r
+ charIndex = scanPos + addedChars\r
+ exprEnd = exprEnd + addedChars\r
+ insertedTotal = insertedTotal + addedChars\r
GOTO 16\r
END IF\r
\r
- IF (b = 5) AND (e = 1) AND (g > 1) THEN\r
- mov a, 1\r
- logicalExpression(a) = 40\r
- lendp a + 2, f\r
- mov a + 2 + f, 1\r
- logicalExpression(a + 2 + f) = 41\r
- h = h + 2\r
- x2 = x2 + 2\r
- a = a + 2 + f\r
+ IF (charCode = 5) AND (passIndex = 1) AND (lastOpClass > 1) THEN\r
+ shiftExpressionRight charIndex, 1\r
+ logicalExpression(charIndex) = 40\r
+ operandLengthAfter charIndex + 2, operandLength\r
+ shiftExpressionRight charIndex + 2 + operandLength, 1\r
+ logicalExpression(charIndex + 2 + operandLength) = 41\r
+ insertedTotal = insertedTotal + 2\r
+ exprEnd = exprEnd + 2\r
+ charIndex = charIndex + 2 + operandLength\r
GOTO 16\r
END IF\r
\r
- IF (b = 3 OR b = 4) AND (e = 2) AND (g > 2) THEN\r
- lendm a - 1, f\r
- mov a - f, 1\r
- logicalExpression(a - f) = 40\r
- lendp a + 2, f\r
- mov a + 2 + f, 1\r
- logicalExpression(a + 2 + f) = 41\r
- h = h + 2\r
- x2 = x2 + 2\r
- a = a + 2 + f\r
+ IF (charCode = 3 OR charCode = 4) AND (passIndex = 2) AND (lastOpClass > 2) THEN\r
+ operandLengthBefore charIndex - 1, operandLength\r
+ shiftExpressionRight charIndex - operandLength, 1\r
+ logicalExpression(charIndex - operandLength) = 40\r
+ operandLengthAfter charIndex + 2, operandLength\r
+ shiftExpressionRight charIndex + 2 + operandLength, 1\r
+ logicalExpression(charIndex + 2 + operandLength) = 41\r
+ insertedTotal = insertedTotal + 2\r
+ exprEnd = exprEnd + 2\r
+ charIndex = charIndex + 2 + operandLength\r
GOTO 16\r
END IF\r
\r
- IF (b = 2) AND (e = 3) AND (g > 3) THEN\r
- lendm a - 1, f\r
- mov a - f, 1\r
- logicalExpression(a - f) = 40\r
- lendp a + 2, f\r
- mov a + 2 + f, 1\r
- logicalExpression(a + 2 + f) = 41\r
- h = h + 2\r
- x2 = x2 + 2\r
- a = a + 2 + f\r
+ IF (charCode = 2) AND (passIndex = 3) AND (lastOpClass > 3) THEN\r
+ operandLengthBefore charIndex - 1, operandLength\r
+ shiftExpressionRight charIndex - operandLength, 1\r
+ logicalExpression(charIndex - operandLength) = 40\r
+ operandLengthAfter charIndex + 2, operandLength\r
+ shiftExpressionRight charIndex + 2 + operandLength, 1\r
+ logicalExpression(charIndex + 2 + operandLength) = 41\r
+ insertedTotal = insertedTotal + 2\r
+ exprEnd = exprEnd + 2\r
+ charIndex = charIndex + 2 + operandLength\r
GOTO 16\r
END IF\r
\r
- SELECT CASE b\r
+ SELECT CASE charCode\r
CASE 5\r
- g = 1\r
+ lastOpClass = 1\r
CASE 3, 4\r
- g = 2\r
+ lastOpClass = 2\r
CASE 2\r
- g = 3\r
+ lastOpClass = 3\r
CASE 1\r
- g = 4\r
+ lastOpClass = 4\r
END SELECT\r
16\r
- a = a + 1\r
- IF a <= x2 THEN GOTO 21\r
-NEXT e\r
-l = h\r
+ charIndex = charIndex + 1\r
+ IF charIndex <= exprEnd THEN GOTO 21\r
+NEXT passIndex\r
+addedChars = insertedTotal\r
END SUB\r
-\r