-
Notifications
You must be signed in to change notification settings - Fork 0
/
mindfuck.py
244 lines (197 loc) · 7.28 KB
/
mindfuck.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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
import sys
class Mindfuck:
def __init__(self, code, ascii = True, trace = False):
self.code = code
self.dataLimit = 30000
self.data = self.getFreshData()
self.name = ''
self.pointer = 0
self.ascii = ascii
self.trace = trace
self.output = ''
def getFreshData(self):
return [0 for x in range(self.dataLimit)]
def run(self):
skipper = None
idx = 0
while idx < len(self.code):
if skipper != None: # skipping up to and over skipperIdx
idx = skipper+1
skipper = None
continue
line = self.code[idx]
if self.trace:
self.tracePrint(line)
lineResponse = self.exeLine(line, idx)
if type(lineResponse) == int:
skipper = lineResponse # skip to idx given in lineResponse
elif lineResponse == True:
idx += 1 # normal response
else:
print('err lineResponse: ' + lineResponse + ' on index: ' + idx) # failed response, shouldnt get here unless syntax is fucked
sys.exit()
print('Out: ' + self.output)
def exeLine(self, line, idx):
if line == '>':
self.pointer+=1
self.pointerRangeCheck()
elif line == '<':
self.pointer-=1
self.pointerRangeCheck()
elif line == '+':
self.data[self.pointer]+=1
self.dataValueRangeCheck()
elif line == '-':
self.data[self.pointer]-=1
self.dataValueRangeCheck()
elif line == '.':
val = self.data[self.pointer]
if self.ascii:
val = chr(val)
else:
val = str(val) + ' ' # if not ascii space out the output
print(val) # TODO check for function to run instead
self.output = self.output + val
elif line == ',':
inVal = input('input decimal: ')
try:
inVal = int(inVal)
except ValueError:
print("That's not an int!")
sys.exit()
self.data[self.pointer] = inVal
self.dataValueRangeCheck()
elif line == '[':
if self.data[self.pointer] == 0:
# if zero jump to end of loop, else ignore
return idx + self.getBlockContentEndIdx(self.code[idx:],'[',']')
elif line == ']':
if self.data[self.pointer] != 0:
# if not zero jump to start of loop
return self.getBlockContentStartIdx(self.code[:idx+1],'[',']')
elif line == '#':
jumpTo = False
try:
jumpTo = self.code.index('\n', idx+1) # find first occourance of \n after idx and skip over it
except ValueError:
return len(self.code) # couldn't find end of line, which means that we must skip the rest of the code
return jumpTo
elif line == '/':
jumpTo = False
try:
jumpTo = self.code.find('/', idx+1) # find first occourance of / after idx and skip over it
except ValueError:
print('couldn\'t find end of area comment, index: ' + idx)
sys.exit()
return jumpTo
elif line == '@':
self.pointer = self.data[self.pointer]
elif line == '~':
self.data = self.getFreshData()
elif line == '*':
self.name = self.name + chr(self.data[self.pointer])
elif line == '!':
self.name = ''
elif line == ';':
ch = False
try:
f = open(self.name)
txt = f.read()
ch = ord(txt[self.data[self.pointer]])
except: # if errors finding file, finding index, or converting ascii -> decimal we set to zero
ch = 0
self.data[self.pointer] = ch
elif line == ':':
with open(self.name, "a") as file:
file.write(chr(self.data[self.pointer]))
elif line == '?':
f = open(self.name)
txt = f.read()
self.code = self.code[:idx] + txt + self.code[1+idx:] # replace ? with code from file
return idx-1 # move back a slot so we next run the character which is now where ? was and continue from there
elif line == '^':
return len(self.code) # ends execution by setting skipper to end
elif line == '\n' or line == ' ': # we skip over newlines and spaces
pass
else:
return False
return True
def getBlockContentEndIdx(self, str, blockOpen, blockClose):
if str[0] != blockOpen:
print('provided block must start with: [')
sys.exit()
counter = 0
for idx, line in enumerate(str):
if line == blockOpen:
counter+=1
elif line == blockClose:
counter-=1
if counter == 0:
return idx
return False
def getBlockContentStartIdx(self, str, blockOpen, blockClose):
str = str[::-1]
if str[0] != blockClose:
print('provided block must end with: ]')
sys.exit()
counter = 0
for idx, line in enumerate(str):
if line == blockClose:
counter+=1
elif line == blockOpen:
counter-=1
if counter == 0:
return len(str) - (idx+1) # reversed string so take away from len +1 to place index value on other side of char
return False
def pointerRangeCheck(self):
if (self.pointer < 0 or self.pointer > self.dataLimit-1):
print('pointer out of bounds')
sys.exit()
def dataValueRangeCheck(self): # within 16 bits
if self.data[self.pointer] < 0:
self.data[self.pointer] += 0x100
elif self.data[self.pointer] > 0xff:
self.data[self.pointer] -= 0x100
def tracePrint(self, line):
nonZeroIdxs = [i for i, e in enumerate(self.data) if e != 0]
finalIdx = None
try:
finalIdx = nonZeroIdxs[-1]
except IndexError:
pass
if finalIdx == None or finalIdx < 5:
finalIdx = 5
print('# ' + str( self.data[:finalIdx]) ) # prints shortened version of self.data
pointStr = '# '
for i in range(self.pointer):
pointStr = pointStr + ' '
print(pointStr + '^')
print('next line: ' + line)
args = sys.argv
args.pop(0)
code = ''
if (len(args) > 1):
print('expected a maximum of 1 argument')
sys.exit()
elif (len(args) < 1):
code = input('If you wish to execute a file then cancel this execution and pass the filename as an argument.\nOr paste your code here: ')
else:
with open(args[0], 'r') as myfile:
code=myfile.read()
ascii = input('Want output as ascii or decimal: ')
if ascii == 'ascii':
ascii = True
elif ascii == 'decimal':
ascii = False
else:
print('Invalid input: ' + ascii)
sys.exit()
trace = input('Turn on trace: ')
if trace == 'yes':
trace = True
elif trace == 'no':
trace = False
else:
print('Invalid input: ' + trace)
sys.exit()
Mindfuck(code, ascii, trace).run()