Programming Forums
User Name Password Register
 

RSS Feed
FORUM INDEX | TODAY'S POSTS | UNANSWERED THREADS | ADVANCED SEARCH

Reply
 
Thread Tools Display Modes
Old Sep 22nd, 2005, 4:37 PM   #1
Arevos
Programming Guru
 
Arevos's Avatar
 
Join Date: Aug 2005
Location: England
Posts: 1,499
Rep Power: 5 Arevos is on a distinguished road
Hybrid python shell

I've been interested in unix shell design for a while now, ever since I attempted to redesign Bash during uni. It didn't turn out that well at the time, but I did come to the opinion that the problem with Bash was not Bash per se, but its environment. Piping is a rather limited way of passing data between commands, and error codes are also pretty limiting.

So I began to think, what if the key functionality of a shell, such as file copying, file reading, file searching etc. were part of the shell, rather than external executables. External software could still be executed, but it wouldn't be native to the shell; you'd need a "run" or "exec" command.

I didn't think about this further for about a year or so, but recently I've been thinking about it again. It occurs to me that Python has all the functionality Bash has, and a lot more besides. But whilst Python is powerful, its syntax is less convenient than Bash; "cp a b" is a lot easier to type than "copy('a', 'b')"

And this is where Harpy comes in; it provides a concise, lispish syntax that can access callable python objects, and has the ability to import python modules.

In case anyone's still interested after all that spiel, I'll give you an example of Harpy usage, and the code for Harpy 0.1. Harpy needs PyParsing to run.

Example: Let's say you have a module "test.py":
import os

def echo(*l):
   for s in l:
      print s,
   print

def list_files(directory = "."):
   return os.listdir(directory)
The functions in this module can be accessed in harpy:
$ import test
$ test:echo hello world
hello world
$ from test
$ echo thanks for all the fish
thanks for all the fish
$ echo (list_files)
['test.pyc', 'test.py', 'harpy.py']
$ echo (list_files).__len__
<method-wrapper object at 0xb7c1404c>
$ echo ((list_files).__len__)
3
$ echo (list_files /)
['bin', 'dev', 'etc', 'lib', 'mnt', 'opt', 'srv', 'tmp', 'sys', 'var', 'usr',
'.dev', 'boot', 'home', 'proc', 'sbin', 'root', 'cdrom', 'media',
'initrd.img', 'initrd', 'vmlinuz']
Code of Harpy 0.1 (163 lines, 3.4k):
# Harpy 0.1 (jreeves@monkeyengines.co.uk)
import sys
import readline
import traceback

from pyparsing import *

def evaluate(expression):
   if hasattr(expression, '__eval__'):
      return expression.__eval__()
   else:
      return expression

class Interpreter:

   def __init__(self, namespace = {}):
      self.parse = self.__parser__()
      
      self.__namespace__ = namespace
      self.__namespace__['__interpreter__'] = self
      self.__namespace__.update(self.__builtins__())


   def load_module(self, name):
      module = __import__(name).__dict__
      try:
         module.update(module['__namespace__'])
      except KeyError:
         pass
      return module


   def __builtins__(self):
      def import_module(name):
         self.__namespace__[name] = self.load_module(name)

      def from_module(name, members = []):
         module = self.load_module(name)
         if members == []:
            self.__namespace__.update(module)
         else:   
            for member in members:
               self.__namespace__[member] = module[member]
               
      return { 'import' : import_module, 'from' : from_module }

   
   class Command:
      def __init__(self, namespace, element):
         self.namespace = namespace
         self.element   = element

      def __eval__(self):
         command = evaluate(self.element[0])
         if type(command) is str:
            command = self.namespace[command]
            
         for value in self.element[1:]:
            command = evaluate(getattr(command, value))
         return command

   
   class Argument:
      def __init__(self, element):
         self.element = element

      def __eval__(self):
         element = evaluate(self.element[0])
         for value in self.element[1:]:
            element = evaluate(getattr(element, value))

         return element
   
   
   class List:
      def __init__(self, head, tail):
         self.command   = head
         self.arguments = tail

      def __eval__(self):
         return evaluate(self.command)(
            *[evaluate(arg) for arg in self.arguments])
   
   
   def __parser__(self):      
      word  = CharsNotIn('"\'(): \n')
      quote = quotedString
      atom  = word | quote

      list     = Forward()
      molecule = Suppress('(') + list + Suppress(')')
      object   = atom | molecule
      
      expression = object + ZeroOrMore(Suppress(':') + atom)

      head = Empty() + expression
      tail = ZeroOrMore(expression)

      list << head + tail
      
      line = StringStart() + list + StringEnd()
      line.ignore("#" + restOfLine)

      def action(function):
         def wrapper(string, location, tokens):
            return function(tokens)
         return wrapper

      def quote_action(tokens):
         return [tokens[0][1:-1]]

      def head_action(tokens): 
         return self.Command(self.__namespace__, tokens[0])

      def tail_action(tokens):
         return [[self.Argument(t) for t in tokens]]
         
      def list_action(tokens):
         return self.List(tokens[0], tokens[1])
      
      def expression_action(tokens):
         return [tokens.asList()]

      quote.setParseAction( action(quote_action) )
      head.setParseAction( action(head_action) )
      tail.setParseAction( action(tail_action) )
      list.setParseAction( action(list_action) )
      expression.setParseAction( action(expression_action) )

      def parser(string):
         return line.parseString(string)[0]

      return parser


   def prompt(self):
      if not sys.stdin.isatty():
         return ""
      else:
         return "$ "


   def interact(self):
      while True:
         try:
            line = raw_input(self.prompt())
         except EOFError:
            break
            
         self.push(line)


   def push(self, line):
      try:
         parse_tree = self.parse(line)
         evaluate(parse_tree)
      except:
         traceback.print_exc()


if __name__ == '__main__':
   shell = Interpreter()
   shell.interact()
Arevos is offline   Reply With Quote
Reply

Bookmarks

« Previous Thread in Forum | Next Thread in Forum »

Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump




DaniWeb IT Discussion Community
All times are GMT -5. The time now is 2:52 AM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC