summaryrefslogtreecommitdiff
path: root/src/toys/turing/js/tape.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/toys/turing/js/tape.js')
-rw-r--r--src/toys/turing/js/tape.js103
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;
+ }
+}