diff options
Diffstat (limited to 'src/toys/turing/js/tape.js')
| -rw-r--r-- | src/toys/turing/js/tape.js | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/src/toys/turing/js/tape.js b/src/toys/turing/js/tape.js new file mode 100644 index 0000000..fd05366 --- /dev/null +++ b/src/toys/turing/js/tape.js @@ -0,0 +1,103 @@ +const ESCAPE_REGEX = /[.*+?^${}()|[\]\\]/g; + +function escapeForRegex(value) { + return value.replace(ESCAPE_REGEX, "\\$&"); +} + +export class Tape { + constructor({ + initialContent = "", + blankSymbol = "B", + minLength = 50, + padding = 40 + } = {}) { + this.blankSymbol = blankSymbol; + this.minLength = minLength; + this.padding = padding; + this.reset(initialContent); + } + + reset(initialContent = "") { + const targetLength = Math.max(this.minLength, initialContent.length + this.padding); + this.cells = Array(targetLength).fill(this.blankSymbol); + const startOffset = Math.floor((targetLength - initialContent.length) / 2); + for (let i = 0; i < initialContent.length; i++) { + this.cells[startOffset + i] = initialContent[i]; + } + this.headIndex = startOffset; + } + + get length() { + return this.cells.length; + } + + getHeadIndex() { + return this.headIndex; + } + + readHead() { + return this.getCell(this.headIndex); + } + + writeHead(symbol) { + this.cells[this.headIndex] = symbol || this.blankSymbol; + } + + readAt(index) { + return this.getCell(index); + } + + writeAt(index, symbol) { + if (index < 0) { + throw new Error("Cannot write to a negative tape index"); + } + this.ensureRightCapacity(index); + this.cells[index] = symbol || this.blankSymbol; + } + + setHead(index) { + if (index < 0) { + throw new Error("Head index cannot be negative"); + } + this.ensureRightCapacity(index); + this.headIndex = index; + } + + moveLeft() { + if (this.headIndex === 0) { + this.cells.unshift(this.blankSymbol); + } else { + this.headIndex -= 1; + return; + } + } + + moveRight() { + this.headIndex += 1; + if (this.headIndex >= this.cells.length) { + this.cells.push(this.blankSymbol); + } + } + + getCell(index) { + if (index < 0 || index >= this.cells.length) { + return this.blankSymbol; + } + return this.cells[index]; + } + + ensureRightCapacity(index) { + while (index >= this.cells.length) { + this.cells.push(this.blankSymbol); + } + } + + getContents({ trimTrailing = true } = {}) { + let snapshot = this.cells.join(""); + if (trimTrailing) { + const regex = new RegExp(`${escapeForRegex(this.blankSymbol)}+$`, "g"); + snapshot = snapshot.replace(regex, ""); + } + return snapshot; + } +} |
