-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
220 lines (194 loc) · 6.75 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
## Turing Machine with I/O by MilkyWay90/a-github-account
## A better version of my previous esolang, Turing Machine But Way Worse
## Started progress on August 2020
## Finished somewhere on December 2020-January 2021
## First public release on May 2021
import sys # Library for reading cmd args
import ctypes # Library that is used for enabling ANSI
# Execute the code from a list of states
def execute(states):
tape = [0] # Tape that the Turing Machine operates on
offset = 0 # Anything that is below the offset is part of the negative tape.
#(for example if the offset was 2 and the list was [2, 3, 4, 5]
# then 2 and 3 would be part of the negative tape.
pos = 0 # Position that the Turing Machine is at.
# For example, if the tape was [2, 3, 4] and pos was 0, then
# the Turing Machine would be pointing at 2.
state = "0" # State that the Turing Machine is in
input_buffer = "" # Dunno if "_buffer" belongs here
output_buffer = "" # Same here
while True:
instructions = states[(str(tape[pos]), state)] # Fetch the list of instructions
# Replace instruction
replace = int(instructions[0])
if not replace + 1: # If the replace instruction is -1 (aka take input)
if input_buffer:
tape[pos] = int(input_buffer[0])
input_buffer = input_buffer[1:]
else:
temp_char = sys.stdin.read(1)
try:
tape[pos], input_buffer = int((temp_val := bin(ord(temp_char)))[2]), temp_val[3:] + '2'
del temp_val
except TypeError: # If STDIN has ended and stdin.read(1) returns a 0-length string, ord() would throw a TypeError
tape[pos] = 3
del temp_char
else: # If the replace instruction >= 0
tape[pos] = replace # Replace this position with the replace instruction
# Print/Output to STDOUT instruction
output = int(instructions[1])
if output:
try:
if output == 1:
if tape[pos] in [0, 1]:
output_buffer += str(tape[pos])
elif output == 2:
sys.stdout.write(chr(int(output_buffer, 2)))
output_buffer = ""
elif output == 3:
sys.stdout.write(chr(tape[pos]))
sys.stdout.flush()
except ValueError:
pass # Don't output if the character is out of Unicode range
# Move instruction
move = int(instructions[2])
pos += move # Move the pointer
# Utilize the offset variable to prevent negative pos issue
if pos == -1:
pos = 0
tape = [0] + tape
offset += 1
# If the position goes out of bounds
elif pos >= len(tape):
tape.append(0)
# Change state instruction
state = instructions[3]
# Halt instruction
halt = int(instructions[4])
if halt:
sys.exit(0)
# Parse the code and return a dictionary
def parse(code):
# Convert text to a dictionary
states = dict(((line.split(" ")[0], line.split(" ")[1]), line.split(" ")[2:]) for line in code.split("\n"))
return states
# Validate the code to make sure that it's valid (yeah pretty self-explanatory)
def validate(code):
instruction_sets = [i.split(" ") for i in code.split("\n")] # List of instruction sets
conditionals = {} # Keep track of states and cells so that there are no duplicates
error = False # Keep track of whether there's an error or not
for line in range(len(instruction_sets)):
instruction_set = instruction_sets[line]
if len(instruction_set) == 0:
print(
"Validation Error: Empty line on line {}; did you leave a trailing newline?".format(
str(line + 1)
),
file = sys.stderr
)
error = True
continue
elif len(instruction_set) != 7:
print(
"Validation Error: Incorrect number of instructions in instruction set {0} on line {1}".format(
" ".join(instruction_set),
str(line + 1)
),
file = sys.stderr
)
error = True
continue
# Check if the conditional value of the cell is a whole number
try:
assert int(instruction_set[0]) > -1
except:
print(
"Validation Error: Value of cell conditional, instruction 1 of instruction set {0} on line {1}, is not a whole number.".format(
" ".join(instruction_set),
str(line + 1)
),
file = sys.stderr
)
error = True
# Check for no duplicates within conditionals and if there are no duplicates, add it to the list of conditionals
try:
line_other = conditionals[(instruction_set[0], instruction_set[1])]
print(
"Validation Error: Value of cell + state conditions, instructions 1 and 2, respectively, are duplicated in instruction sets {1} and {3}, on lines {0} and {2}, respectively.".format(
str(line_other + 1),
" ".join(instruction_sets[line_other]),
str(line + 1),
" ".join(instruction_sets[line])
),
file = sys.stderr
)
error = True
except KeyError:
conditionals[(instruction_set[0], instruction_set[1])] = line
# Check for replace instruction validity (whole numbers and -1)
try:
assert int(instruction_set[2]) > -2
except:
print(
"Validation Error: Replace instruction, instruction 3 of instruction set {0} on line {1}, is not -1 or a whole number.".format(
" ".join(instruction_set),
str(line + 1)
),
file = sys.stderr
)
error = True
# Check for output instruction validity (numbers 0 - 3)
try:
assert -1 < int(instruction_set[3]) < 4
except:
print(
"Validation Error: Output instruction, instruction 4 of instruction set {0} on line {1}, is not a whole number inclusively between 0 and 3.".format(
" ".join(instruction_set),
str(line + 1)
),
file = sys.stderr
)
error = True
# Check for move instruction validity (-1, 0, or 1)
try:
assert -2 < int(instruction_set[4]) < 2
except:
print(
"Validation Error: Move instruction, instruction 5 of instruction set {0} on line {1}, is not -1, 0, or 1.".format(
" ".join(instruction_set),
str(line + 1)
),
file = sys.stderr
)
error = True
# Check for halt instruction validity (0 or 1)
try:
assert int(instruction_set[4]) in (0, 1)
except:
print(
"Validation Error: Halt instruction, instruction 6 of instruction set {0} on line {1}, is not 0 or 1.".format(
" ".join(instruction_set),
str(line + 1)
),
file = sys.stderr
)
error = True
if error == True:
sys.exit("Program terminated due to validation error(s).")
# Read a file's contents and return it
def readfile(filename):
file = open(filename, 'r')
contents = file.read()
file.close()
return contents
def main():
# Two lines of code that enables ANSI in Windows cmd that I got from Stack Overflow (https://stackoverflow.com/a/36760881 because CC BY-SA)
kernel32 = ctypes.windll.kernel32
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
args = sys.argv
contents = readfile(args[1]) # Get file contents
validate(contents)
states = parse(contents) # Get the converted form of the file
execute(states) # Execute the program
if __name__ == "__main__":
main()