root/hodgestar/WhitespaceCode/pbradbury/Program.py

Revision 425, 14.1 kB (checked in by hodgestar, 22 months ago)

Playing around with Whitespace.

  • Property svn:mime-type set to text/python-source
  • Property svn:eol-style set to native
Line 
1import sys
2
3class Program:
4        def __init__(self):
5                # static data
6                self.programdata = []
7                self.labels = {}
8                # dynamic data
9                self.stack = []
10                self.heap = {}
11                self.programcounter = 0
12                self.pcstack = []
13        def __str__(self):
14                return "<Program, %d opcodes (incl %d labels) - at %d, %d stack length, %d call depth, %d heap size>" % (len(self.programdata), len(self.labels), self.programcounter, len(self.stack), len(self.pcstack), len(self.heap))
15        def __repr__(self):
16                return "%s\nProgram Data: %s\nLabels: %s\nData Stack: %s\nHeap: %s\nProgram counter: %d\nPC Stack: %s" % (str(self),self.programdata,self.labels,self.stack,self.heap,self.programcounter,self.pcstack)
17
18class Opcodes: # the numbers themselves are meaningless - they just have to be unique
19        # Axx - stack ops
20        Push = 0            # AA num    - push num to stack
21        Dup = 1             # ACA       - push copy of TOS (top of stack)
22        Swap = 2            # ACB       - swap TOS and TOS-1
23        Discard = 3         # ACC       - pop and discard TOS
24        Ref = 4             # ABA num   - push copy of TOS-num
25        Slide = 5           # ABC num   - pop and discard TOS-1 through TOS-num - keep TOS
26                            #           - equiv to "ACB ACC" num times
27
28        # BAxx - math ops
29        Plus = 6            # BAAA      - push TOS-1 + TOS
30        Minus = 7           # BAAB      - push TOS-1 - TOS
31        Times = 8           # BAAC      - push TOS-1 * TOS
32        Divide = 9          # BABA      - push TOS-1 / TOS (floored)
33        Modulo = 10         # BABB      - push TOS-1 % TOS
34
35        # BBx - heap ops
36        Store = 11          # BBA       - write TOS to address TOS-1
37        Retrieve = 12       # BBB       - read from address TOS
38
39        # Cxx - flow ops
40        Label = 13          # CAA label - mark a label
41        Call = 14           # CAB label - call a subroutine
42        Jump = 15           # CAC label - jump to a label
43        JumpZero = 16       # CBA label - jump if TOS is zero
44        JumpNeg = 17        # CBB label - jump if TOS is <0
45        Return = 18         # CBC       - return from subroutine
46        Trace = 24          # CCB       - print core dump
47        End = 19            # CCC       - stop executing program
48
49        # BCxx - I/O ops
50        OutputChar = 20     # BCAA      - output char from TOS
51        OutputNum = 21      # BCAB      - output int from TOS
52        InputChar = 22      # BCBA      - input char to address at TOS
53        InputNum = 23       # BCBB      - input int to address at TOS
54
55class Instruction:
56        opcode = -1
57        def __init__(self, codeloc):
58                self.codeloc = codeloc
59        def __call__(self, program):
60                raise "Hmm, calling a non-opcode? Strange tastes..."
61
62class Push(Instruction):
63        opcode = Opcodes.Push
64        def __init__(self, codeloc, value):
65                self.codeloc = codeloc
66                self.value = value
67        def __call__(self, program):
68                program.stack.append(self.value)
69        def __str__(self):
70                return "<Push %d>" % self.value
71        __repr__ = __str__
72class Dup(Instruction):
73        opcode = Opcodes.Dup
74        def __init__(self, codeloc):
75                self.codeloc = codeloc
76        def __call__(self, program):
77                try:
78                        program.stack.append(program.stack[-1])
79                except IndexError:
80                        print "Error: Dup in empty stack at byte 0x%X (line %d char %d)" % self.codeloc
81                        program.programcounter = None
82        def __str__(self):
83                return "<Dup>"
84        __repr__ = __str__
85class Swap(Instruction):
86        opcode = Opcodes.Swap
87        def __init__(self, codeloc):
88                self.codeloc = codeloc
89        def __call__(self, program):
90                try:
91                        program.stack[-1],program.stack[-2] = program.stack[-2],program.stack[-1]
92                except IndexError:
93                        if len(program.stack) == 0:
94                                print "Error: Swap in stack with only one element at byte 0x%X (line %d char %d)" % self.codeloc
95                        else:
96                                print "Error: Swap in empty stack at byte 0x%X (line %d char %d)" % self.codeloc
97                        program.programcounter = None
98        def __str__(self):
99                return "<Swap>"
100        __repr__ = __str__
101class Discard(Instruction):
102        opcode = Opcodes.Discard
103        def __init__(self, codeloc):
104                self.codeloc = codeloc
105        def __call__(self, program):
106                try:
107                        program.stack.pop()
108                except IndexError:
109                        print "Error: Discard from empty stack at byte 0x%X (line %d char %d)" % self.codeloc
110                        program.programcounter = None
111        def __str__(self):
112                return "<Discard>"
113        __repr__ = __str__
114class Ref(Instruction):
115        opcode = Opcodes.Ref
116        def __init__(self, codeloc, location):
117                self.codeloc = codeloc
118                self.location = -location - 1
119        def __call__(self, program):
120                try:
121                        program.stack.append(program.stack[self.location])
122                except IndexError:
123                        print "Error: Ref value larger than stack size at byte 0x%X (line %d char %d)" % self.codeloc
124                        program.programcounter = None
125        def __str__(self):
126                return "<Ref %d>" % -self.location
127        __repr__ = __str__
128class Slide(Instruction):
129        opcode = Opcodes.Slide
130        def __init__(self, codeloc, quantity):
131                self.codeloc = codeloc
132                self.quantity = -quantity - 1
133        def __call__(self, program):
134                try:
135                        program.stack = program.stack[0:self.quantity] + [program.stack[-1]]
136                except IndexError:
137                        if len(program.stack) == 0:
138                                print "Error: Slide from empty stack at byte 0x%X (line %d char %d)" % self.codeloc
139                        else:
140                                print "Error: Slide value larger than stack size at byte 0x%X (line %d char %d)" % self.codeloc
141                        program.programcounter = None
142        def __str__(self):
143                return "<Slide %d>" % (-self.quantity + 1)
144        __repr__ = __str__
145
146class Plus(Instruction):
147        opcode = Opcodes.Plus
148        def __init__(self, codeloc):
149                self.codeloc = codeloc
150        def __call__(self, program):
151                try:
152                        y = program.stack.pop()
153                        x = program.stack.pop()
154                        program.stack.append(x + y)
155                except IndexError:
156                        if len(program.stack) == 0:
157                                print "Error: Plus with empty stack at byte 0x%X (line %d char %d)" % self.codeloc
158                        else:
159                                print "Error: Plus with only one stack entry at byte 0x%X (line %d char %d)" % self.codeloc
160                        program.programcounter = None
161        def __str__(self):
162                return "<Plus>"
163        __repr__ = __str__
164class Minus(Instruction):
165        opcode = Opcodes.Minus
166        def __init__(self, codeloc):
167                self.codeloc = codeloc
168        def __call__(self, program):
169                try:
170                        y = program.stack.pop()
171                        x = program.stack.pop()
172                        program.stack.append(x - y)
173                except IndexError:
174                        if len(program.stack) == 0:
175                                print "Error: Minus with empty stack at byte 0x%X (line %d char %d)" % self.codeloc
176                        else:
177                                print "Error: Minus with only one stack entry at byte 0x%X (line %d char %d)" % self.codeloc
178                        program.programcounter = None
179        def __str__(self):
180                return "<Minus>"
181        __repr__ = __str__
182class Times(Instruction):
183        opcode = Opcodes.Times
184        def __init__(self, codeloc):
185                self.codeloc = codeloc
186        def __call__(self, program):
187                try:
188                        y = program.stack.pop()
189                        x = program.stack.pop()
190                        program.stack.append(x * y)
191                except IndexError:
192                        if len(program.stack) == 0:
193                                print "Error: Times with empty stack at byte 0x%X (line %d char %d)" % self.codeloc
194                        else:
195                                print "Error: Times with only one stack entry at byte 0x%X (line %d char %d)" % self.codeloc
196                        program.programcounter = None
197        def __str__(self):
198                return "<Times>"
199        __repr__ = __str__
200class Divide(Instruction):
201        opcode = Opcodes.Divide
202        def __init__(self, codeloc):
203                self.codeloc = codeloc
204        def __call__(self, program):
205                try:
206                        y = program.stack.pop()
207                        x = program.stack.pop()
208                        program.stack.append(x / y)
209                except IndexError:
210                        if len(program.stack) == 0:
211                                print "Error: Divide with empty stack at byte 0x%X (line %d char %d)" % self.codeloc
212                        else:
213                                print "Error: Divide with only one stack entry at byte 0x%X (line %d char %d)" % self.codeloc
214                        program.programcounter = None
215        def __str__(self):
216                return "<Divide>"
217        __repr__ = __str__
218class Modulo(Instruction):
219        opcode = Opcodes.Modulo
220        def __init__(self, codeloc):
221                self.codeloc = codeloc
222        def __call__(self, program):
223                try:
224                        y = program.stack.pop()
225                        x = program.stack.pop()
226                        program.stack.append(x % y)
227                except IndexError:
228                        if len(program.stack) == 0:
229                                print "Error: Modulo with empty stack at byte 0x%X (line %d char %d)" % self.codeloc
230                        else:
231                                print "Error: Modulo with only one stack entry at byte 0x%X (line %d char %d)" % self.codeloc
232                        program.programcounter = None
233        def __str__(self):
234                return "<Modulo>"
235        __repr__ = __str__
236
237class Store(Instruction):
238        opcode = Opcodes.Store
239        def __init__(self, codeloc):
240                self.codeloc = codeloc
241        def __call__(self, program):
242                try:
243                        val = program.stack.pop()
244                        addr = program.stack.pop()
245                        program.heap[addr] = val
246                except IndexError:
247                        if len(program.stack) == 0:
248                                print "Error: Store with empty stack at byte 0x%X (line %d char %d)" % self.codeloc
249                        else:
250                                print "Error: Store with only one stack entry at byte 0x%X (line %d char %d)" % self.codeloc
251                        program.programcounter = None
252        def __str__(self):
253                return "<Store>"
254        __repr__ = __str__
255class Retrieve(Instruction):
256        opcode = Opcodes.Retrieve
257        def __init__(self, codeloc):
258                self.codeloc = codeloc
259        def __call__(self, program):
260                try:
261                        addr = program.stack.pop()
262                        program.stack.append(program.heap[addr])
263                except IndexError:
264                        print "Error: Retrieve with empty stack at byte 0x%X (line %d char %d)" % self.codeloc
265                        program.programcounter = None
266                except KeyError:
267                        print "Error: Retrieve from an address not stored to yet at byte 0x%X (line %d char %d)" % self.codeloc
268                        program.programcounter = None
269        def __str__(self):
270                return "<Retrieve>"
271        __repr__ = __str__
272
273class Label(Instruction):
274        opcode = Opcodes.Label
275        def __init__(self, codeloc, label):
276                self.codeloc = codeloc
277                self.label = label
278        def __call__(self, program):
279                pass # a label is a no-op
280        def __str__(self):
281                return "<Label %s>" % repr(self.label)
282        __repr__ = __str__
283class Call(Instruction):
284        opcode = Opcodes.Call
285        def __init__(self, codeloc, label):
286                self.codeloc = codeloc
287                self.label = label
288        def __call__(self, program):
289                try:
290                        program.pcstack.append(program.programcounter)
291                        program.programcounter = program.labels[self.label]
292                except KeyError:
293                        print "Error: Call to undefined label at byte 0x%X (line %d char %d)" % self.codeloc
294                        program.programcounter = None
295        def __str__(self):
296                return "<Call %s>" % repr(self.label)
297        __repr__ = __str__
298class Jump(Instruction):
299        opcode = Opcodes.Jump
300        def __init__(self, codeloc, label):
301                self.codeloc = codeloc
302                self.label = label
303        def __call__(self, program):
304                try:
305                        program.programcounter = program.labels[self.label]
306                except KeyError:
307                        print "Error: Jump to undefined label at byte 0x%X (line %d char %d)" % self.codeloc
308                        program.programcounter = None
309        def __str__(self):
310                return "<Jump %s>" % repr(self.label)
311        __repr__ = __str__
312class JumpZero(Instruction):
313        opcode = Opcodes.JumpZero
314        def __init__(self, codeloc, label):
315                self.codeloc = codeloc
316                self.label = label
317        def __call__(self, program):
318                try:
319                        if program.stack.pop() == 0:
320                                program.programcounter = program.labels[self.label]
321                except KeyError:
322                        print "Error: JumpZero to undefined label at byte 0x%X (line %d char %d)" % self.codeloc
323                        program.programcounter = None
324        def __str__(self):
325                return "<JumpZero %s>" % repr(self.label)
326        __repr__ = __str__
327class JumpNeg(Instruction):
328        opcode = Opcodes.JumpNeg
329        def __init__(self, codeloc, label):
330                self.codeloc = codeloc
331                self.label = label
332        def __call__(self, program):
333                try:
334                        if program.stack.pop() < 0:
335                                program.programcounter = program.labels[self.label]
336                except KeyError:
337                        print "Error: JumpNeg to undefined label at byte 0x%X (line %d char %d)" % self.codeloc
338                        program.programcounter = None
339        def __str__(self):
340                return "<JumpNeg %s>" % repr(self.label)
341        __repr__ = __str__
342class Return(Instruction):
343        opcode = Opcodes.Return
344        def __init__(self, codeloc):
345                self.codeloc = codeloc
346        def __call__(self, program):
347                try:
348                        program.programcounter = program.pcstack.pop()
349                except IndexError:
350                        print "Error: Return without Call at byte 0x%X (line %d char %d)" % self.codeloc
351                        program.programcounter = None
352        def __str__(self):
353                return "<Return>"
354        __repr__ = __str__
355class End(Instruction):
356        opcode = Opcodes.End
357        def __init__(self, codeloc):
358                self.codeloc = codeloc
359        def __call__(self, program):
360                program.programcounter = None
361        def __str__(self):
362                return "<End>"
363        __repr__ = __str__
364
365class OutputChar(Instruction):
366        opcode = Opcodes.OutputChar
367        def __init__(self, codeloc):
368                self.codeloc = codeloc
369        def __call__(self, program):
370                try:
371                        sys.stdout.write(chr(program.stack.pop()))
372                except ValueError:
373                        print "Error: OutputChar value not in range 0-255 at byte 0x%X (line %d char %d)" % self.codeloc
374                        program.programcounter = None
375                except IndexError:
376                        print "Error: OutputChar on empty stack at byte 0x%X (line %d char %d)" % self.codeloc
377                        program.programcounter = None
378        def __str__(self):
379                return "<OutputChar>"
380        __repr__ = __str__
381class OutputNum(Instruction):
382        opcode = Opcodes.OutputNum
383        def __init__(self, codeloc):
384                self.codeloc = codeloc
385        def __call__(self, program):
386                try:
387                        sys.stdout.write(str(program.stack.pop()))
388                except IndexError:
389                        print "Error: OutputNum on empty stack at byte 0x%X (line %d char %d)" % self.codeloc
390                        program.programcounter = None
391        def __str__(self):
392                return "<OutputNum>"
393        __repr__ = __str__
394class InputChar(Instruction):
395        opcode = Opcodes.InputChar
396        def __init__(self, codeloc):
397                self.codeloc = codeloc
398        def __call__(self, program):
399                try:
400                        a = sys.stdin.read(1)
401                        if (a == ''):
402                                program.heap[program.stack.pop()] = -1
403                        else:
404                                program.heap[program.stack.pop()] = ord(a)
405                except IndexError:
406                        print "Error: InputChar on empty stack at byte 0x%X (line %d char %d)" % self.codeloc
407                        program.programcounter = None
408        def __str__(self):
409                return "<InputChar>"
410        __repr__ = __str__
411class InputNum(Instruction):
412        opcode = Opcodes.InputNum
413        def __init__(self, codeloc):
414                self.codeloc = codeloc
415        def __call__(self, program):
416                try:
417                        program.heap[program.stack.pop()] = int(sys.stdin.readline())
418                except IndexError:
419                        print "Error: InputNum on empty stack at byte 0x%X (line %d char %d)" % self.codeloc
420                        program.programcounter = None
421                except ValueError:
422                        print "Error: Entered value not a number for InputNum at byte 0x%X (line %d char %d)" % self.codeloc
423                        program.programcounter = None
424        def __str__(self):
425                return "<InputNum>"
426        __repr__ = __str__
427class Trace(Instruction):
428        opcode = Opcodes.Trace
429        def __init__(self, codeloc):
430                self.codeloc = codeloc
431        def __call__(self, program):
432                print repr(program)
433        def __str__(self):
434                return "<Trace>"
435        __repr__ = __str__
436
437def vm(prog):
438        while prog.programcounter >= 0:
439                a = prog.programdata[prog.programcounter]
440                prog.programcounter += 1
441                #sys.stdout.write(str(a)) # uncomment to perform trace
442                a(prog)
Note: See TracBrowser for help on using the browser.