Advertisement
Maharba

Arborealis Interpreter in Python

Jun 15th, 2011
267
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.13 KB | None | 0 0
  1. #################
  2. # arborealis.py #
  3. #################
  4.  
  5. ## Interpreter written by Maharba for the Esolang wiki
  6. ## 14 Jun 2011 - 15 Jun 2011
  7.  
  8. ###
  9. # For the Arborealis language specification, see
  10. #  http://esoteric.voxelperfect.net/wiki/Arborealis
  11. #
  12. # The Arborealis language was designed by DocHerrings
  13. ###
  14.  
  15. ###
  16. # Command line usage:
  17. #  python arborealis.py program.arb
  18. ###
  19.  
  20. import sys
  21.  
  22. class ArborealisError(Exception):
  23.     """For errors processing or running Arborealis code.
  24.    """
  25.     pass
  26.  
  27. # Global variables `current` and `root`
  28. # for the current and root nodes of the tree
  29. current = root = None
  30. # Now a placeholder to be initialized in the `run` function
  31.  
  32. class Node(object):
  33.     r"""A node of the binary tree.
  34.    
  35.    Can be initialized with a given `parent`, otherwise
  36.        it is the root node and its own parent.
  37.    Has an integer `val` and `Node` children `leftc` and `rightc`.
  38.    The `val` starts at `0` and both children at `None`.
  39.    
  40.    Supports the following methods:
  41.    
  42.    Internal Name   Character   Command Name
  43.    `newlchild`     /           Left Child Create
  44.    `newrchild`     \          Right Child Create
  45.    `movel`         <           Move to Left Child
  46.    `mover`         >           Move to Right Child
  47.    `condl`         !           Conditional Left Move
  48.    `condr`         ?           Conditional Right Move
  49.    `incr`          +           Increment
  50.    `decr`          -           Decrement
  51.    `toroot`        ~           Go to Root
  52.    `lparadox`      (           Left Paradox
  53.    `rparadox`      )           Right Paradox
  54.    `checkl`        {           Check for Left Child
  55.    `checkr`        }           Check for Right Child
  56.    `outp`          .           Output
  57.    `inpu`          ,           Input
  58.    
  59.    See their individual docstrings or the Arborealis spec for descriptions.
  60.    
  61.    Also has the method `getval` which returns current node value.
  62.    """
  63.     def __init__(self, parent=None):
  64.         """Create the node.
  65.        
  66.        If no `parent` is given, becomes the root node.
  67.        """
  68.         if not parent: parent = self
  69.         self.val, self.parent, self.leftc, self.rightc = 0, parent, None, None
  70.     def newlchild(self):
  71.         """Create a new left child node if it does not exist.
  72.        """
  73.         if not self.leftc:
  74.             self.leftc = Node(self)
  75.     def newrchild(self):
  76.         """Create a new right child node if it does not exist.
  77.        """
  78.         if not self.rightc:
  79.             self.rightc = Node(self)
  80.     def movel(self):
  81.         """Move to the left child if it exists.
  82.        
  83.        Sets the global `current` variable to the left child.
  84.        """
  85.         global current
  86.         if self.leftc:
  87.             current = self.leftc
  88.     def mover(self):
  89.         """Move to the right child if it exists.
  90.        
  91.        Sets the global `current variable to the right child.
  92.        """
  93.         global current
  94.         if self.rightc:
  95.             current = self.rightc
  96.     def condl(self):
  97.         """Conditional left move.
  98.        
  99.        Tries the following actions in order until one succeeds:
  100.        If the left child does not exist, create and move to it.
  101.        If the current value is 0, move to the left child.
  102.        If the right child does not exist, create and move to it.
  103.        If the current value is not 0, move to the right child.
  104.        """
  105.         if not self.leftc:
  106.             self.newlchild()
  107.             self.movel()
  108.         elif not self.val:
  109.             self.movel()
  110.         elif not self.rightc:
  111.             self.newrchild()
  112.             self.mover()
  113.         elif self.val:
  114.             self.mover()
  115.     def condr(self):
  116.         """Conditional right move.
  117.        
  118.        Tries the following actions in order until one succeeds:
  119.        If the right child does not exist, create and move to it.
  120.        If the current value is 0, move to the right child.
  121.        If the left child does not exist, create and move to it.
  122.        If the current value is not 0, move to the left child.
  123.        """
  124.         if not self.rightc:
  125.             self.newrchild()
  126.             self.mover()
  127.         elif not self.val:
  128.             self.mover()
  129.         elif not self.leftc:
  130.             self.newlchild()
  131.             self.movel()
  132.         elif self.val:
  133.             self.movel()
  134.     def incr(self):
  135.         """Increment the current value.
  136.        
  137.        Wraps around at 256 to 0.
  138.        """
  139.         self.val += 1
  140.         self.val %= 256
  141.     def decr(self):
  142.         """Decrement the current value.
  143.        
  144.        Wraps around at -1 to 255.
  145.        """
  146.         self.val -= 1
  147.         self.val %= 256
  148.     def toroot(self):
  149.         """Moves to the root node.
  150.        
  151.        Sets the global `current` to the global `root`.
  152.        This is not node-specific, but easier to implement as a node method.
  153.        """
  154.         global current
  155.         current = root
  156.     def lparadox(self):
  157.         """Left paradox creation.
  158.        
  159.        If the left child does not exist,
  160.            sets it to the parent of the current node.
  161.        This is not a copy: moving to the left child is equivalent
  162.            to moving to the parent.
  163.        """
  164.         if not self.leftc:
  165.             self.leftc = self.parent
  166.     def rparadox(self):
  167.         """"Right paradox creation.
  168.        
  169.        If the right child does not exist,
  170.            sets it to the parent of the current node.
  171.        This is not a copy: moving to the right child is equivalent
  172.            to moving to the parent.
  173.        """
  174.         if not self.rightc:
  175.             self.rightc = self.parent
  176.     def checkl(self):
  177.         """Check for left child.
  178.        
  179.        Sets the current value to 1 if the left child exists, 0 otherwise.
  180.        """
  181.         self.val = int(bool(self.leftc))
  182.     def checkr(self):
  183.         """Check for right child.
  184.        
  185.        Sets the current value to 1 if the right child exists, 0 otherwise.
  186.        """
  187.         self.val = int(bool(self.rightc))
  188.     def outp(self):
  189.         """Outputs the current value as an ASCII character.
  190.        
  191.        Writes to stdout.
  192.        """
  193.         sys.stdout.write(chr(self.val))
  194.     def inpu(self):
  195.         """Inputs a character into the current value.
  196.        
  197.        Reads from stdin.
  198.        Returns 0 on EOF.
  199.        """
  200.         i = sys.stdin.read(1)
  201.         if i:
  202.             self.val = ord(i) % 256
  203.         else:
  204.             self.val = 0
  205.     def getval(self):
  206.         """Utility method to get current value.
  207.        
  208.        Not an Arborealis command.
  209.        """
  210.         return self.val
  211.  
  212. # The standard Arborealis commands,
  213. # mapped to their internal names.
  214. cmds = {'>': 'mover', '<': 'movel', '(': 'lparadox', ')': 'rparadox',
  215.     '/': 'newlchild', '\\': 'newrchild', '{': 'checkl', '}': 'checkr',
  216.     '+': 'incr', '-': 'decr', '!': 'condl', '?': 'condr', '~': 'toroot',
  217.     '.': 'outp', ',': 'inpu'}
  218.  
  219. # The Arborealis commands for beginnning and ending loops.
  220. loop_begin, loop_end = '[', ']'
  221.  
  222. def parse(code, _top=True):
  223.     """Parse Arborealis code into a list of commands.
  224.    
  225.    Loops are represented as nested lists.
  226.    The `_top` argument is private and should not be set.
  227.    """
  228.     l = []
  229.     if _top: code = list(code)
  230.     while True:
  231.         try:
  232.             c = code.pop(0)
  233.         except IndexError:
  234.             break
  235.         if c == loop_end:
  236.             if _top:
  237.                 raise ArborealisError('Unmatched looping.')
  238.             else:
  239.                 return l
  240.         if c == loop_begin:
  241.             l.append(parse(code, _top=False))
  242.             continue
  243.         if c not in cmds:
  244.             continue
  245.         l.append(c)
  246.     if _top:
  247.         return l
  248.     else:
  249.         raise ArborealisError('Unmatched looping.')
  250.  
  251. def do_parsed(code):
  252.     """Execute parsed Arborealis commands.
  253.    """
  254.     global current
  255.     for cmd in code:
  256.         if not isinstance(cmd, list):
  257.             getattr(current, cmds[cmd])()
  258.         else:
  259.             while current.getval():
  260.                 do_parsed(cmd)
  261.  
  262. def _flushinp():
  263.     """Flush standard input.
  264.    
  265.    Clears any unread characters in sys.stdin.
  266.    """
  267.     sys.stdin.seek(0)
  268.  
  269. def run(code):
  270.     """Runs Arborealis code.
  271.    
  272.    Calls `parse` and `do_parsed`.
  273.    Uses `_flushinp` to prevent some buffering problems.
  274.    """
  275.     global current
  276.     global root
  277.     current = root = Node()
  278.     _flushinp()
  279.     do_parsed(parse(code))
  280.     _flushinp()
  281.  
  282. def run_from_cmdline():
  283.     """Run a program file from a command-line argument.
  284.    
  285.    The first item in `sys.argv` after this file will be
  286.        treated as the name of a file containing an Arborealis program
  287.        and run.
  288.    Arborealis program files should have the extension `.arb`,
  289.        but this is not enforced.
  290.    """
  291.     argv = sys.argv
  292.     if len(argv) != 2:
  293.         raise ArborealisError('Wrong number of command-line arguments.')
  294.     try:
  295.         with open(argv[1]) as prog:
  296.             run(prog.read())
  297.     except EnvironmentError as e:
  298.         raise ArborealisError('Error opening program file: '+e.message)
  299.  
  300. if __name__ == '__main__':
  301.     run_from_cmdline()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement