996
|
1 #! /usr/bin/env python
|
|
2
|
|
3 """
|
|
4 Linguine programming language interpreter
|
|
5 Copyright (c) 2005 by Jeffry Johnston
|
|
6
|
|
7 This program is free software; you can redistribute it and/or
|
|
8 modify it under the terms of the GNU General Public License as
|
|
9 published by the Free Software Foundation. See the file LICENSE
|
|
10 for more details.
|
|
11
|
|
12 Version History:
|
|
13 1.00 November 24, 2005 Initial version
|
|
14 1.10 November 25, 2005 Added jump dereferencing
|
|
15 1.20 November 26, 2005 Added `>' instruction
|
|
16 1.21 November 26, 2005 Fix negative y on `>' bug
|
|
17 1.22 November 26, 2005 Fix >> big number bug
|
|
18 1.30 November 27, 2005 Added `^' instruction
|
|
19 """
|
|
20
|
|
21 import sys
|
|
22 import time
|
|
23 from optparse import OptionParser
|
|
24
|
|
25 VERSION = "1.10"
|
|
26 glo_memory = {}
|
|
27
|
|
28
|
|
29
|
|
30
|
|
31 def read(filename):
|
|
32 """
|
|
33 Reads and parses the program, checking for errors.
|
|
34 Returns:
|
|
35 (firstline, program)
|
|
36 firstline = line number of first program line
|
|
37 program = {linenum: (command_list, goto, deref_goto), ...}
|
|
38 linenum = line number
|
|
39 command_list = [(instr, x, deref_x, y, deref_y, ifjump, deref_if), ...]
|
|
40 instr = instruction, one of: =, +, -, |, ?, $, #, <, ~
|
|
41 x = x value
|
|
42 deref_x = number of times x value should be deref'd, or 0
|
|
43 y = y value
|
|
44 deref_y = number of times y value should be deref'd, or 0
|
|
45 ifjump = ifjump line number
|
|
46 deref_if = number of times ifjump value should be deref'd, or 0
|
|
47 goto = jump line number
|
|
48 deref_goto = number of times goto value should be deref'd, or 0
|
|
49 """
|
|
50 fileline = 0
|
|
51 program = {}
|
|
52 firstline = None
|
|
53 goto_list = []
|
|
54 try:
|
|
55 infile = open(filename)
|
|
56 while True:
|
|
57 # read line
|
|
58 line = infile.readline()
|
|
59 fileline += 1
|
|
60 if line == "":
|
|
61 break
|
|
62
|
|
63 # strip comments
|
|
64 i = line.find("'")
|
|
65 if i >= 0:
|
|
66 line = line[0:i]
|
|
67 line = line.strip()
|
|
68
|
|
69 # ignore blank lines
|
|
70 if len(line) < 1:
|
|
71 continue
|
|
72
|
|
73 # get line number
|
|
74 i = line.find("[")
|
|
75 if i < 0:
|
|
76 print >> sys.stderr, "Error [line " + str(fileline) + "]: missing `['"
|
|
77 sys.exit(1)
|
|
78 try:
|
|
79 linenum = int(line[0:i].strip())
|
|
80 linenum = int(linenum)
|
|
81 if linenum == 0:
|
|
82 raise ValueError
|
|
83 if firstline == None or linenum < firstline:
|
|
84 firstline = linenum
|
|
85 except ValueError:
|
|
86 print >> sys.stderr, "Error [line " + str(fileline) + "]: bad or missing line number `" + str(linenum) + "'"
|
|
87 sys.exit(1)
|
|
88
|
|
89 # get command
|
|
90 line = line[i:]
|
|
91 i = line.find("]")
|
|
92 if i < 0:
|
|
93 print >> sys.stderr, "Error [line " + str(fileline) + "]: missing `]'"
|
|
94 sys.exit(1)
|
|
95 command = line[1:i].strip()
|
|
96 if len(command) < 2:
|
|
97 print >> sys.stderr, "Error [line " + str(fileline) + "]: missing or invalid command"
|
|
98 sys.exit(1)
|
|
99 goto = line[i + 1:].strip()
|
|
100
|
|
101 # parse commands
|
|
102 command_list = []
|
|
103 commands = command.split(",")
|
|
104 for command in commands:
|
|
105 command = command.strip()
|
|
106
|
|
107 # get x deref
|
|
108 deref_x = 0
|
|
109 while len(command) > 0 and command[0] == "*":
|
|
110 deref_x += 1
|
|
111 command = command[1:].strip()
|
|
112
|
|
113 # find instruction
|
|
114 for c in "=+|>?^#$<~-":
|
|
115 i = command.find(c, 1)
|
|
116 if i >= 0:
|
|
117 instr = c
|
|
118 break
|
|
119 else:
|
|
120 print >> sys.stderr, "Error [line " + str(fileline) + "]: unrecognized command `" + command + "'"
|
|
121 sys.exit(1)
|
|
122
|
|
123 # get x
|
|
124 x = command[0:i].strip()
|
|
125 command = command[i + 1:].strip()
|
|
126 try:
|
|
127 x = int(x)
|
|
128 except ValueError:
|
|
129 print >> sys.stderr, "Error [line " + str(fileline) + "]: bad x value `" + str(x) + "'"
|
|
130 sys.exit(1)
|
|
131
|
|
132 # get y deref
|
|
133 deref_y = 0
|
|
134 while len(command) > 0 and command[0] == "*":
|
|
135 deref_y += 1
|
|
136 command = command[1:].strip()
|
|
137
|
|
138 # get if jump
|
|
139 deref_if = 0
|
|
140 if instr[0] == "<" or instr[0] == "~":
|
|
141 i = command.find(":")
|
|
142 if i < 0:
|
|
143 print >> sys.stderr, "Error [line " + str(fileline) + "]: missing if jump line number"
|
|
144 sys.exit(1)
|
|
145 ifjump = command[i + 1:].strip()
|
|
146
|
|
147 # get ifjump deref
|
|
148 while len(ifjump) > 0 and ifjump[0] == "*":
|
|
149 deref_if += 1
|
|
150 ifjump = ifjump[1:].strip()
|
|
151
|
|
152 command = command[0:i].strip()
|
|
153 try:
|
|
154 ifjump = int(ifjump)
|
|
155 except ValueError:
|
|
156 print >> sys.stderr, "Error [line " + str(fileline) + "]: bad if jump line number `" + str(ifjump) + "'"
|
|
157 sys.exit(1)
|
|
158 if deref_if == 0:
|
|
159 goto_list += [(ifjump, fileline)]
|
|
160 else:
|
|
161 ifjump = None
|
|
162
|
|
163 # get y
|
|
164 if instr[0] != "?" and instr[0] != "^" and instr[0] != "#" and instr[0] != "$":
|
|
165 y = command.strip()
|
|
166 try:
|
|
167 y = int(y)
|
|
168 except ValueError:
|
|
169 print >> sys.stderr, "Error [line " + str(fileline) + "]: bad y value `" + str(y) + "'"
|
|
170 sys.exit(1)
|
|
171 else:
|
|
172 y = None
|
|
173 if deref_y:
|
|
174 print >> sys.stderr, "Error [line " + str(fileline) + "]: bad dereference"
|
|
175 sys.exit(1)
|
|
176
|
|
177 # add command to command list
|
|
178 command_list += [(instr, x, deref_x, y, deref_y, ifjump, deref_if)]
|
|
179
|
|
180 # get goto line number
|
|
181 deref_goto = 0
|
|
182 while len(goto) > 0 and goto[0] == "*":
|
|
183 deref_goto += 1
|
|
184 goto = goto[1:].strip()
|
|
185 if len(goto) < 1:
|
|
186 print >> sys.stderr, "Error [line " + str(fileline) + "]: missing jump line number"
|
|
187 sys.exit(1)
|
|
188 try:
|
|
189 goto = int(goto)
|
|
190 except ValueError:
|
|
191 print >> sys.stderr, "Error [line " + str(fileline) + "]: bad jump line number `" + str(goto) + "'"
|
|
192 sys.exit(1)
|
|
193 if deref_goto == 0:
|
|
194 goto_list += [(goto, fileline)]
|
|
195
|
|
196 # add line to program dictionary
|
|
197 try:
|
|
198 program[linenum]
|
|
199 print >> sys.stderr, "Error [line " + str(fileline) + "]: duplicate line number `" + str(linenum) + "'"
|
|
200 sys.exit(1)
|
|
201 except KeyError:
|
|
202 program[linenum] = (command_list, goto, deref_goto)
|
|
203
|
|
204 infile.close()
|
|
205 except IOError, e:
|
|
206 print >> sys.stderr, "Error reading program: " + str(e)
|
|
207 sys.exit(1)
|
|
208
|
|
209 # check that there was at least one program line
|
|
210 if firstline == None:
|
|
211 print >> sys.stderr, "Error: Program must have at least one command"
|
|
212 sys.exit(1)
|
|
213
|
|
214 # check that all jumps are to valid line numbers
|
|
215 for goto, fileline in goto_list:
|
|
216 if goto != 0 and not program.has_key(goto):
|
|
217 print >> sys.stderr, "Error [line " + str(fileline) + "]: jump to undefined line number `" + str(goto) + "'"
|
|
218 sys.exit(1)
|
|
219 goto_list = None
|
|
220
|
|
221 return (firstline, program)
|
|
222
|
|
223
|
|
224
|
|
225
|
|
226 def get_cell(index):
|
|
227 """
|
|
228 Returns the cell value at the specified index, or 0 if the cell is
|
|
229 unused.
|
|
230 """
|
|
231 if glo_memory.has_key(index):
|
|
232 return glo_memory[index]
|
|
233 else:
|
|
234 return 0
|
|
235
|
|
236
|
|
237
|
|
238
|
|
239 def set_cell(index, value):
|
|
240 """
|
|
241 Sets the value of the cell at the specified index.
|
|
242 """
|
|
243 glo_memory[index] = value
|
|
244
|
|
245
|
|
246
|
|
247
|
|
248 def interpret((firstline, program)):
|
|
249 """
|
|
250 Interprets the given Linguine program
|
|
251 """
|
|
252 line = firstline
|
|
253 index = 0
|
|
254 while line > 0:
|
|
255 # get the current command
|
|
256 commands = program[line][0]
|
|
257 command = commands[index]
|
|
258 instr = command[0]
|
|
259 x = command[1]
|
|
260 for i in xrange(command[2]):
|
|
261 x = get_cell(x)
|
|
262 y = command[3]
|
|
263 for i in xrange(command[4]):
|
|
264 y = get_cell(y)
|
|
265 ifjump = command[5]
|
|
266 for i in xrange(command[6]):
|
|
267 ifjump = get_cell(ifjump)
|
|
268 if ifjump != None and ifjump != 0 and not program.has_key(ifjump):
|
|
269 print >> sys.stderr, "Runtime error [line number " + str(line) + "]: bad ifjump line number `" + str(ifjump) + "'"
|
|
270 sys.exit(1)
|
|
271 if index + 1 >= len(commands):
|
|
272 goto = program[line][1]
|
|
273 for i in xrange(program[line][2]):
|
|
274 goto = get_cell(goto)
|
|
275 if goto != 0 and not program.has_key(goto):
|
|
276 print >> sys.stderr, "Runtime error [line number " + str(line) + "]: bad jump line number `" + str(goto) + "'"
|
|
277 sys.exit(1)
|
|
278 line = goto
|
|
279 index = 0
|
|
280 else:
|
|
281 index += 1
|
|
282
|
|
283 # execute instruction
|
|
284 if instr == "=":
|
|
285 set_cell(x, y)
|
|
286 elif instr == "+":
|
|
287 set_cell(x, get_cell(x) + y)
|
|
288 elif instr == "-":
|
|
289 set_cell(x, get_cell(x) - y)
|
|
290 elif instr == "|":
|
|
291 set_cell(x, ~(get_cell(x) & y))
|
|
292 elif instr == ">":
|
|
293 if y < 0:
|
|
294 set_cell(x, get_cell(x) * (2**(-y)))
|
|
295 else:
|
|
296 set_cell(x, get_cell(x) >> y)
|
|
297 elif instr == "?":
|
|
298 ch = sys.stdin.read(1)
|
|
299 if len(ch) < 1:
|
|
300 ch = -1
|
|
301 else:
|
|
302 ch = ord(ch)
|
|
303 set_cell(x, ch)
|
|
304 elif instr == "^":
|
|
305 set_cell(x, int(time.time()))
|
|
306 elif instr == "$":
|
|
307 sys.stdout.write(chr(get_cell(x) & 255))
|
|
308 sys.stdout.flush()
|
|
309 elif instr == "#":
|
|
310 sys.stdout.write(str(get_cell(x)))
|
|
311 sys.stdout.flush()
|
|
312 elif instr == "<" and get_cell(x) < y:
|
|
313 line = ifjump
|
|
314 index = 0
|
|
315 elif instr == "~" and get_cell(x) == y:
|
|
316 line = ifjump
|
|
317 index = 0
|
|
318
|
|
319
|
|
320
|
|
321
|
|
322 def main():
|
|
323 """
|
|
324 Processes the command line, reads the program, and starts the Linguine
|
|
325 interpreter.
|
|
326 """
|
|
327 (options, args) = OptionParser(usage="linguine.py [options] program", \
|
|
328 version=VERSION, \
|
|
329 description="Interprets the specified Linguine program.").parse_args()
|
|
330 if len(args) < 1:
|
|
331 print >> sys.stderr, "Missing program filename."
|
|
332 sys.exit(1)
|
|
333 elif len(args) > 1:
|
|
334 print >> sys.stderr, "Too many filenames given."
|
|
335 sys.exit(1)
|
|
336 interpret(read(args[0]))
|
|
337
|
|
338
|
|
339
|
|
340
|
|
341 if __name__ == "__main__":
|
|
342 main()
|
|
343
|