view interps/linguine/linguine.py @ 12500:e48c08805365 draft default tip

<b_jonas> ` learn \'The password of the month is Cthulhuquagdonic Mothraquagdonic Narwhalicorn.\' # https://logs.esolangs.org/libera-esolangs/2024-04.html#lKE Infinite craft
author HackEso <hackeso@esolangs.org>
date Wed, 01 May 2024 06:39:10 +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()