![]() |
|
![]() |
|
|
Thread Tools | Display Modes |
|
|
#1 |
|
Programming Guru
![]() Join Date: Aug 2005
Location: England
Posts: 1,499
Rep Power: 4
![]() |
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)$ 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'] # 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() |
|
|
|
![]() |
| Bookmarks |
| Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
| Thread Tools | |
| Display Modes | |
|
|