view interps/linguine/linguine.py @ 11340:77399ae45cb1

<wob_jonas> slashlearn peace witch//Peace witches do alchemy: they turn mundane building material to gold. They\'re in the same universe where Bowser turned peaceful citizens of the Mushroom Kingdom to building material.
author HackBot
date Tue, 06 Feb 2018 23:37:00 +0000
parents 859f9b4339e6
children
line wrap: on
line source

#! /usr/bin/env python

"""
Linguine programming language interpreter
Copyright (c) 2005 by Jeffry Johnston

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation. See the file LICENSE
for more details.

Version History:
1.00    November 24, 2005       Initial version
1.10    November 25, 2005       Added jump dereferencing
1.20    November 26, 2005       Added `>' instruction
1.21    November 26, 2005       Fix negative y on `>' bug
1.22    November 26, 2005       Fix >> big number bug
1.30    November 27, 2005       Added `^' instruction  
"""

import sys
import time
from optparse import OptionParser

VERSION = "1.10"
glo_memory = {}




def read(filename):
   """
   Reads and parses the program, checking for errors.
   Returns:
     (firstline, program)
       firstline = line number of first program line
       program = {linenum: (command_list, goto, deref_goto), ...}
         linenum = line number 
         command_list = [(instr, x, deref_x, y, deref_y, ifjump, deref_if), ...] 
            instr = instruction, one of: =, +, -, |, ?, $, #, <, ~ 
            x = x value
            deref_x = number of times x value should be deref'd, or 0        
            y = y value
            deref_y = number of times y value should be deref'd, or 0       
            ifjump = ifjump line number 
            deref_if = number of times ifjump value should be deref'd, or 0       
         goto = jump line number
         deref_goto = number of times goto value should be deref'd, or 0       
   """
   fileline = 0
   program = {}
   firstline = None
   goto_list = []
   try: 
      infile = open(filename)
      while True:
         # read line
         line = infile.readline()
         fileline += 1
         if line == "":
            break
            
         # strip comments
         i = line.find("'")
         if i >= 0:
            line = line[0:i]
         line = line.strip()   
         
         # ignore blank lines
         if len(line) < 1:
            continue
            
         # get line number
         i = line.find("[")
         if i < 0:
            print >> sys.stderr, "Error [line " + str(fileline) + "]: missing `['"
            sys.exit(1)
         try:   
            linenum = int(line[0:i].strip())
            linenum = int(linenum)
            if linenum == 0:
               raise ValueError
            if firstline == None or linenum < firstline: 
               firstline = linenum
         except ValueError:
            print >> sys.stderr, "Error [line " + str(fileline) + "]: bad or missing line number `" + str(linenum) + "'"
            sys.exit(1)
         
         # get command    
         line = line[i:]
         i = line.find("]")
         if i < 0:
            print >> sys.stderr, "Error [line " + str(fileline) + "]: missing `]'"
            sys.exit(1)
         command = line[1:i].strip()
         if len(command) < 2:
            print >> sys.stderr, "Error [line " + str(fileline) + "]: missing or invalid command"
            sys.exit(1)
         goto = line[i + 1:].strip()
            
         # parse commands
         command_list = []  
         commands = command.split(",")
         for command in commands:
            command = command.strip()

            # get x deref 
            deref_x = 0
            while len(command) > 0 and command[0] == "*":
               deref_x += 1
               command = command[1:].strip()

            # find instruction
            for c in "=+|>?^#$<~-": 
               i = command.find(c, 1)
               if i >= 0:
                  instr = c
                  break
            else:
               print >> sys.stderr, "Error [line " + str(fileline) + "]: unrecognized command `" + command + "'"
               sys.exit(1)
            
            # get x  
            x = command[0:i].strip()
            command = command[i + 1:].strip()
            try:   
               x = int(x)  
            except ValueError:
               print >> sys.stderr, "Error [line " + str(fileline) + "]: bad x value `" + str(x) + "'"
               sys.exit(1)
         
            # get y deref 
            deref_y = 0
            while len(command) > 0 and command[0] == "*":
               deref_y += 1
               command = command[1:].strip()
         
            # get if jump 
            deref_if = 0
            if instr[0] == "<" or instr[0] == "~":
               i = command.find(":")
               if i < 0:
                  print >> sys.stderr, "Error [line " + str(fileline) + "]: missing if jump line number"
                  sys.exit(1)
               ifjump = command[i + 1:].strip()

               # get ifjump deref 
               while len(ifjump) > 0 and ifjump[0] == "*":
                  deref_if += 1
                  ifjump = ifjump[1:].strip()
                  
               command = command[0:i].strip()
               try:
                  ifjump = int(ifjump)
               except ValueError:
                  print >> sys.stderr, "Error [line " + str(fileline) + "]: bad if jump line number `" + str(ifjump) + "'"
                  sys.exit(1)
               if deref_if == 0:
                  goto_list += [(ifjump, fileline)]   
            else:
               ifjump = None
            
            # get y
            if instr[0] != "?" and instr[0] != "^" and instr[0] != "#" and instr[0] != "$":
               y = command.strip()
               try:
                  y = int(y)
               except ValueError:
                  print >> sys.stderr, "Error [line " + str(fileline) + "]: bad y value `" + str(y) + "'"
                  sys.exit(1)
            else:
               y = None
               if deref_y:
                  print >> sys.stderr, "Error [line " + str(fileline) + "]: bad dereference"
                  sys.exit(1)
         
            # add command to command list
            command_list += [(instr, x, deref_x, y, deref_y, ifjump, deref_if)]  
         
         # get goto line number
         deref_goto = 0
         while len(goto) > 0 and goto[0] == "*":
            deref_goto += 1
            goto = goto[1:].strip()
         if len(goto) < 1:
            print >> sys.stderr, "Error [line " + str(fileline) + "]: missing jump line number"
            sys.exit(1)
         try:
            goto = int(goto)
         except ValueError:
            print >> sys.stderr, "Error [line " + str(fileline) + "]: bad jump line number `" + str(goto) + "'"
            sys.exit(1)
         if deref_goto == 0:
            goto_list += [(goto, fileline)]   
            
         # add line to program dictionary
         try:
            program[linenum]
            print >> sys.stderr, "Error [line " + str(fileline) + "]: duplicate line number `" + str(linenum) + "'"
            sys.exit(1)
         except KeyError:
            program[linenum] = (command_list, goto, deref_goto)    
           
      infile.close()
   except IOError, e:
      print >> sys.stderr, "Error reading program: " + str(e)
      sys.exit(1)
      
   # check that there was at least one program line   
   if firstline == None:
      print >> sys.stderr, "Error: Program must have at least one command"
      sys.exit(1)
      
   # check that all jumps are to valid line numbers 
   for goto, fileline in goto_list:
      if goto != 0 and not program.has_key(goto):
         print >> sys.stderr, "Error [line " + str(fileline) + "]: jump to undefined line number `" + str(goto) + "'"
         sys.exit(1)
   goto_list = None      
      
   return (firstline, program)




def get_cell(index):
   """ 
   Returns the cell value at the specified index, or 0 if the cell is
   unused.
   """
   if glo_memory.has_key(index):
      return glo_memory[index]
   else:
      return 0




def set_cell(index, value):
   """
   Sets the value of the cell at the specified index.
   """
   glo_memory[index] = value
  



def interpret((firstline, program)):
   """
   Interprets the given Linguine program
   """
   line = firstline
   index = 0
   while line > 0:
      # get the current command
      commands = program[line][0]
      command = commands[index]
      instr = command[0]
      x = command[1]
      for i in xrange(command[2]):
         x = get_cell(x)
      y = command[3]
      for i in xrange(command[4]):
         y = get_cell(y)
      ifjump = command[5]
      for i in xrange(command[6]):
         ifjump = get_cell(ifjump)
      if ifjump != None and ifjump != 0 and not program.has_key(ifjump):
         print >> sys.stderr, "Runtime error [line number " + str(line) + "]: bad ifjump line number `" + str(ifjump) + "'"
         sys.exit(1)
      if index + 1 >= len(commands):
         goto = program[line][1]
         for i in xrange(program[line][2]):
            goto = get_cell(goto)
         if goto != 0 and not program.has_key(goto):
            print >> sys.stderr, "Runtime error [line number " + str(line) + "]: bad jump line number `" + str(goto) + "'"
            sys.exit(1)
         line = goto
         index = 0
      else:
         index += 1     
      
      # execute instruction
      if instr == "=":
         set_cell(x, y) 
      elif instr == "+":
         set_cell(x, get_cell(x) + y) 
      elif instr == "-":
         set_cell(x, get_cell(x) - y) 
      elif instr == "|":
         set_cell(x, ~(get_cell(x) & y)) 
      elif instr == ">":
         if y < 0:
            set_cell(x, get_cell(x) * (2**(-y))) 
         else:
            set_cell(x, get_cell(x) >> y) 
      elif instr == "?":
         ch = sys.stdin.read(1)
         if len(ch) < 1:
            ch = -1
         else:
            ch = ord(ch)
         set_cell(x, ch)
      elif instr == "^":
         set_cell(x, int(time.time()))   
      elif instr == "$":
         sys.stdout.write(chr(get_cell(x) & 255))
         sys.stdout.flush()
      elif instr == "#":
         sys.stdout.write(str(get_cell(x)))
         sys.stdout.flush()
      elif instr == "<" and get_cell(x) < y:
         line = ifjump
         index = 0
      elif instr == "~" and get_cell(x) == y:
         line = ifjump
         index = 0




def main():
   """
   Processes the command line, reads the program, and starts the Linguine 
   interpreter.
   """
   (options, args) = OptionParser(usage="linguine.py [options] program", \
                                  version=VERSION, \
                                  description="Interprets the specified Linguine program.").parse_args()
   if len(args) < 1:
      print >> sys.stderr, "Missing program filename."
      sys.exit(1)
   elif len(args) > 1:   
      print >> sys.stderr, "Too many filenames given."
      sys.exit(1)
   interpret(read(args[0]))




if __name__ == "__main__":
   main()