├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── main.d ├── tapes ├── input-01.tape ├── input-02.tape ├── input-03.tape ├── input-04.tape └── input-05.tape └── turds ├── add.turd ├── beaver.turd ├── counter.turd ├── dec.turd ├── filler.turd └── inc.turd /.gitignore: -------------------------------------------------------------------------------- 1 | turd 2 | *.o 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 © Alexey Kutepov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | turd: main.d 2 | dmd -of=turd main.d 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Turing Machine Interpreter 2 | 3 | Simple Turing Machine interpreter implemented in D based on Wikipedia article about Turing Machine: [https://en.wikipedia.org/wiki/Turing_machine](https://en.wikipedia.org/wiki/Turing_machine) 4 | 5 | ## Quick Start 6 | 7 | The build expects [dmd](https://dlang.org/download.html) available in `$PATH`. 8 | 9 | ```console 10 | $ make 11 | $ ./turd turds/add.turd tapes/input-05.tape 12 | 13 | ``` 14 | 15 | ## Turd File Format 16 | 17 | Turd files (examples are located in the [./turds/](./turds/) folder) are the files that contain instructions for the Turing Machine to interpret: 18 | 19 | - Each instruction is located on a separate line. 20 | - Correct instruction has the format: ` `: 21 | - `` is a sequence of non-space characters that represents the state in which this instruction is activated. 22 | - `` is a sequence of non-space characters that represents the symbol that is read by the head of the machine which along with a specific `` activates the instruction. 23 | - `` is a sequence of non-space characters that represents the symbol that is written to the current cell on the tape when the instruction is activated. 24 | - `` is either symbol `L` or `R` which indicates the direction in which the head of the Turing Machine should step after executing the instruction. 25 | - `` is a sequence of non-space characters that represents the state to which the Machine should switch after executing the instruction. 26 | - Any line may have any amount of leading or trailing whitespaces. All of them are stripped off before processing any instructions. 27 | - Any empty line after stripping whitespace is ignored. 28 | - Any line that starts with `#` after stripping whitespaces is ignored. 29 | 30 | ## Tape File Format 31 | 32 | Tape files (examples are located in the [./tapes/](./tapes/) folders) are the files that contain initial state of the Turing Machine tape. 33 | 34 | - A tape file consists of sequence of symbols separated by any amount of whitespace characters. 35 | - Each symbol is a sequence of non-space characters that represents the symbol stored in the corresponding cell of the Machine's tape. 36 | 37 | ## Execution Process 38 | 39 | Execution process starts with 40 | - loading the provided tape file into the tape of the Virtual Turing Machine, 41 | - setting the head position to 0 (can be changed with flag `-p`), 42 | - switch to the state `BEGIN` (can be change with flag `-s`). 43 | 44 | Then on each iteration of execution the machine finds the first instruction with the matching `` and `` and executes that instruction. If the machine cannot find an instruction with the matching `` and ``it halts. 45 | -------------------------------------------------------------------------------- /main.d: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | import std.file; 3 | import std.string; 4 | import std.algorithm; 5 | import std.ascii; 6 | import std.array; 7 | import std.exception; 8 | import std.conv; 9 | import std.range; 10 | import std.typecons; 11 | import core.stdc.stdlib; 12 | 13 | enum Step 14 | { 15 | L, 16 | R, 17 | } 18 | 19 | alias State = string; 20 | alias Symbol = string; 21 | 22 | struct Turd 23 | { 24 | State current; 25 | Symbol read; 26 | Symbol write; 27 | Step step; 28 | State next; 29 | } 30 | 31 | struct Machine 32 | { 33 | Symbol[] tape; 34 | ulong head; 35 | State state; 36 | 37 | bool next(Turd[] program) 38 | { 39 | foreach (turd; program) { 40 | if (turd.current == state && turd.read == tape[head]) { 41 | tape[head] = turd.write; 42 | final switch (turd.step) { 43 | case Step.L: 44 | if (head == 0) { 45 | head = tape.length - 1; 46 | } else { 47 | head -= 1; 48 | } 49 | break; 50 | case Step.R: 51 | head = (head + 1) % tape.length; 52 | break; 53 | } 54 | state = turd.next; 55 | return true; 56 | } 57 | } 58 | return false; 59 | } 60 | 61 | void dump_tape() 62 | { 63 | foreach (cell; tape) { 64 | write(cell, ' '); 65 | } 66 | writeln(); 67 | } 68 | 69 | void debug_dump() 70 | { 71 | writeln("STATE: ", state); 72 | dump_tape(); 73 | foreach (i, cell; tape) { 74 | if (i == head) write('^'); 75 | for (int j = 0; j < cell.length; ++j) { 76 | write(' '); 77 | } 78 | if (i != head) write(' '); 79 | } 80 | } 81 | } 82 | 83 | Turd parseTurd(string filepath, 84 | Tuple!(int, "index", string, "value") s) 85 | { 86 | auto tokens = s.value.split!isWhite.map!(x => x.strip).array; 87 | if (tokens.length != 5) { 88 | writeln(filepath, ":", s.index, ": A single turd is expected to have 5 tokens"); 89 | exit(1); 90 | } 91 | 92 | immutable int CURRENT = 0; 93 | immutable int READ = 1; 94 | immutable int WRITE = 2; 95 | immutable int STEP = 3; 96 | immutable int NEXT = 4; 97 | 98 | Turd turd; 99 | turd.current = tokens[CURRENT]; 100 | turd.read = tokens[READ]; 101 | turd.write = tokens[WRITE]; 102 | switch (tokens[STEP]) { 103 | case "L": 104 | turd.step = Step.L; 105 | break; 106 | case "R": 107 | turd.step = Step.R; 108 | break; 109 | default: 110 | writeln(filepath, ":", s[0], ": `", tokens[STEP], "` is not a correct step. Expected `L` or `R`."); 111 | exit(1); 112 | } 113 | turd.next = tokens[NEXT]; 114 | return turd; 115 | } 116 | 117 | void usage(File stream) 118 | { 119 | stream.writeln("Usage: turd [OPTIONS] "); 120 | stream.writeln("OPTIONS:"); 121 | stream.writeln(" --help|-h print this help to stdout and exit with 0 exit code"); 122 | stream.writeln(" --state|-s [STATE] start from a specific initial state (default: BEGIN)"); 123 | stream.writeln(" --head|-p [POSITION] start from a specific head position (default: 0)"); 124 | stream.writeln(" --non-interactively execute the program non-interactively"); 125 | } 126 | 127 | int main(string[] args) 128 | { 129 | bool non_interactively = false; 130 | 131 | Machine machine; 132 | machine.head = 0; 133 | machine.state = "BEGIN"; 134 | 135 | int args_index = 1; 136 | while (args_index < args.length && !args[args_index].empty && args[args_index][0] == '-') { 137 | auto flag = args[args_index++]; 138 | 139 | void expect_argument() { 140 | if (args_index >= args.length) { 141 | usage(stderr); 142 | stderr.writeln("ERROR: No argument provided for flag `", flag, "`"); 143 | exit(1); 144 | } 145 | } 146 | 147 | switch (flag) { 148 | case "--help": 149 | case "-h": 150 | usage(stdout); 151 | exit(0); 152 | break; 153 | 154 | case "--state": 155 | case "-s": 156 | expect_argument(); 157 | machine.state = args[args_index++]; 158 | break; 159 | 160 | case "--head": 161 | case "-p": 162 | expect_argument(); 163 | machine.head = args[args_index++].to!ulong; 164 | break; 165 | 166 | case "--non-interactively": 167 | non_interactively = true; 168 | break; 169 | 170 | default: 171 | usage(stderr); 172 | stderr.writeln("ERROR: unknown flag `", flag, "`"); 173 | exit(1); 174 | } 175 | } 176 | 177 | if (args_index >= args.length) { 178 | usage(stderr); 179 | stderr.writeln("ERROR: not turd input file is provided"); 180 | exit(1); 181 | } 182 | auto turd_filepath = args[args_index++]; 183 | 184 | if (args_index >= args.length) { 185 | usage(stderr); 186 | stderr.writeln("ERROR: not tape input file is provided"); 187 | exit(1); 188 | } 189 | auto tape_filepath = args[args_index++]; 190 | 191 | auto turds = readText(turd_filepath) 192 | .splitLines 193 | .map!(x => x.strip) 194 | .enumerate(1) 195 | // Ignore empty lines 196 | .filter!(x => !x.value.empty) 197 | // Ignore commented out lines 198 | .filter!(x => x.value[0] != '#') 199 | .map!(x => parseTurd(turd_filepath, x)) 200 | .array; 201 | 202 | machine.tape = readText(tape_filepath) 203 | .split!isWhite 204 | .map!(x => x.strip) 205 | .array; 206 | 207 | if (non_interactively) { 208 | while (machine.next(turds)) {} 209 | machine.dump_tape(); 210 | } else { 211 | do { 212 | machine.debug_dump(); 213 | readln(); 214 | } while (machine.next(turds)); 215 | } 216 | 217 | return 0; 218 | } 219 | -------------------------------------------------------------------------------- /tapes/input-01.tape: -------------------------------------------------------------------------------- 1 | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -------------------------------------------------------------------------------- /tapes/input-02.tape: -------------------------------------------------------------------------------- 1 | 0 0 0 0 0 0 0 0 0 0 1 0 0 2 | -------------------------------------------------------------------------------- /tapes/input-03.tape: -------------------------------------------------------------------------------- 1 | 1 1 1 0 0 1 0 -------------------------------------------------------------------------------- /tapes/input-04.tape: -------------------------------------------------------------------------------- 1 | * 0 0 0 0 0 0 0 -------------------------------------------------------------------------------- /tapes/input-05.tape: -------------------------------------------------------------------------------- 1 | * 1 1 0 0 * 0 1 0 0 * 2 | -------------------------------------------------------------------------------- /turds/add.turd: -------------------------------------------------------------------------------- 1 | # -*- mode: conf; -*- 2 | # This program sums up two numbers A and B. 3 | # Input expects a tape that looks roughly like this: 4 | # * 1 1 0 0 * 0 1 0 0 * 5 | # 6 | # - The tape starts with a maker * that is used by the BEGIN state to 7 | # find the begining of the first number A. 8 | # - Then follows digits of number A. The number is represented by base-2 9 | # numeral system with the least significant digits coming first. 10 | # - Then follows another maker * that separates number A from number B. 11 | # - Then follows the figits of number B represented by the same format as 12 | # number A 13 | # - The end of the tape is indicated by another marker * 14 | # 15 | # The algorithm computes A + B by continuesly decrementing A and then 16 | # incrementing B in a loop until A becomes equal to 0. The result of the 17 | # computation is located in the position of number B. At the end of a 18 | # successful computation number A should consist of all 1s. 19 | # 20 | # The algorithm starts from state BEGIN. 21 | 22 | # BEGIN: find the beginning of the number A 23 | BEGIN * * R DEC 24 | BEGIN 0 0 L BEGIN 25 | BEGIN 1 1 L BEGIN 26 | 27 | # DEC: decrement number A or HALT if A equals to 0 28 | DEC 1 0 R NEXT 29 | DEC 0 1 R DEC 30 | DEC * * R HALT 31 | 32 | # NEXT: find the beginning of number B from the left 33 | NEXT * * R INC 34 | NEXT 0 0 R NEXT 35 | NEXT 1 1 R NEXT 36 | 37 | # INC: increment number B 38 | INC 0 1 R PREV 39 | INC 1 0 R INC 40 | 41 | # PREV: find the begining number B from the right 42 | PREV * * L BEGIN 43 | PREV 0 0 L PREV 44 | PREV 1 1 L PREV 45 | -------------------------------------------------------------------------------- /turds/beaver.turd: -------------------------------------------------------------------------------- 1 | # -*- mode: conf; -*- 2 | # Take from https://en.wikipedia.org/wiki/Turing_machine#Formal_definition 3 | BEGIN 0 1 R B 4 | BEGIN 1 1 L C 5 | 6 | B 0 1 L BEGIN 7 | B 1 1 R B 8 | 9 | C 0 1 L B 10 | C 1 1 R HALT -------------------------------------------------------------------------------- /turds/counter.turd: -------------------------------------------------------------------------------- 1 | # -*- mode: conf; -*- 2 | BEGIN * * R INC 3 | BEGIN 0 0 L BEGIN 4 | BEGIN 1 1 L BEGIN 5 | 6 | INC 0 1 L BEGIN 7 | INC 1 0 R INC 8 | -------------------------------------------------------------------------------- /turds/dec.turd: -------------------------------------------------------------------------------- 1 | # -*- mode: conf; -*- 2 | BEGIN 1 0 R HALT 3 | BEGIN 0 1 R BEGIN -------------------------------------------------------------------------------- /turds/filler.turd: -------------------------------------------------------------------------------- 1 | # -*- mode: conf; -*- 2 | BEGIN 0 1 R BEGIN 3 | BEGIN 1 1 R HALT -------------------------------------------------------------------------------- /turds/inc.turd: -------------------------------------------------------------------------------- 1 | # -*- mode: conf; -*- 2 | BEGIN 0 1 R HALT 3 | BEGIN 1 0 R BEGIN --------------------------------------------------------------------------------