#!/usr/bin/env python """ basics.py - Pluvo Basic Functions Author: Sean B. Palmer, inamidst.com """ import sys, re from datatypes import Table, Variable, Symbol, String, Number def weakFunction(block): if not isinstance(block, Table): return False return block.get(String('kind')) == String('Function') def add(env, table, item): if weakFunction(item): item = env.evaluate(item) # E.g. in add salutations { ... } in test/007-case.lu # This makes it act like push on a non-existent array in Perl if isinstance(table, Symbol): name = table.name table = Table() env.bind(name, table) table.append(item) def check(env, func): from cStringIO import StringIO if (not isinstance(func, Table)) or (func.get('kind') != 'Function'): raise ValueError("Requires a function") for com in func: one = com[0] if hasattr(one, 'name') and one.name == 'example': block = com[1] stdout = sys.stdout sys.stdout = StringIO() reference = [] test = Table({ 'kind': 'Function' }) for command in block: first = command[0] if hasattr(first, 'name') and first.name == '=>': reference.append(str(command[1]) + '\n') else: test.append(command) env.evaluate(test) sys.stdout.seek(0) result = sys.stdout.read() sys.stdout = stdout if result == ''.join(reference): print func.name, 'check: pass' else: print func.name, 'check: FAIL' def comparesEqual(env, p, q): return p == q def dot(env, var, attr, *args): attrname = attr.name if var.has_key(String("variables")): # @@ Look this up properly! value = var[String("variables")][attrname][1] if args: block = Table({'kind': 'Function'}) command = Table({'kind': 'Command'}) command.append(value) command.extend(args) block.append(command) else: block = value if weakFunction(block): block = env.evaluate(block) return block # @@ Arnia's fix else: return var[attrname] def elsethen(env, block): this = env.stack[-1] if this[String("status")] == String("Condition Failed"): result = env.evaluate(block) return result def equals(env, var, *args): if len(args) == 1: # print "*** name = object (assign) ***" value = args[0] if hasattr(var, 'name'): if weakFunction(value): value = env.evaluate(value) env.bind(var.name, value) else: name = var[0][0].name # @@ assert value is a block env.bind(name, value) elif len(args) == 2: if isinstance(args[0], Table) and weakFunction(args[1]): # print "*** name = argspec block (create function) ***" argspec, function = args # Pass on the argspec and the variables to make it a closure and to # ensure static (lexical) scoping function[String("argspec")] = argspec env.bind(var.name, function) elif weakFunction(args[0]): # Instantiate a new prototype copy # print "*** name = proto args (instantiate proto) ***" proto, args = args proto = proto.copy() command = Table({'kind': 'Command'}) command.append(proto) command.extend(args) env.perform(command) env.bind(var.name, proto) else: # print "*** name = py object (type object) ***" py, object = args env.bind(var.name, object, condition=py) else: raise ValueError("Must have one or two args") def example(env, block): pass def get(env, table, item): if weakFunction(table): table = env.evaluate(table) return table.get(item, None) def help(env): flagspec = env.lookup('flagspec') sys.stdout.write(str(flagspec["doc"])) sys.exit(0) def iff(env, condition, block): conditionResult = env.evaluate(condition) if conditionResult: ans = env.evaluate(block) # print ans # print block[String("control")] # raise Exception return (ans, String(""), block[String("control")]) else: return (None, String("Condition Failed")) def join(env, string, table): return String(string.join([str(arg) for arg in table])) def lessthan(env, p, q): return p.num < p.num def loop(env, *args): if len(args) == 2: argspec = Table([Variable("arg")]) table, block = args elif len(args) in (3, 4): if len(args) == 3: arg, table, block = args else: arg, invar, table, block = args if hasattr(arg, 'name'): argspec = Table([Variable(arg.name)]) else: argspec = Table([Variable(a.name) for a in arg]) else: raise ValueError("Excepected a different amount of args") if weakFunction(table): table = env.evaluate(table) for item in table: block[String("argspec")] = argspec if len(argspec) > 1: # @@ For now args = tuple(item) else: args = (item,) env.evaluate(block, args) def method(env, var, eq, argspec, block): block[String("argspec")] = argspec env.bind(var.name, block, scope=env.stack[-1]) def minus(env, p, q): return p - q def morethan(env, p, q): return p.num > q.num def multiply(env, p, q): if weakFunction(p): p = env.evaluate(p) if weakFunction(q): q = env.evaluate(q) return Number(p.num * q.num) def nott(env, arg): if hasattr(arg, '__call__'): arg = arg(env) if arg: return False return True def orr(env, *args): this = env.stack[-1] if this[String("status")] == String("Condition Failed"): block = Table({ 'kind': 'Function', 'variables': this['variables'] }) command = Table(list(args)) block.append(command) result = env.evaluate(block) return result def out(env, *args): sys.stdout.write(' '.join(str(arg) for arg in args)) def pipe(env, *args): if len(args) == 2: var = Variable("line") sh, block = args if weakFunction(sh): sh = env.evaluate(sh) import subprocess p = subprocess.Popen(str(sh), shell=True, stdout=subprocess.PIPE) for line in p.stdout: block[String("argspec")] = Table([var]) args = (String(line),) env.evaluate(block, args) def plus(env, *args): evaled = [] for arg in args: if weakFunction(arg): evaled.append(env.evaluate(arg)) else: evaled.append(arg) args = evaled if isinstance(args[0], String): return String('').join([str(arg) for arg in args]) elif isinstance(args[0], Number): return Number(sum(i.num for i in args)) else: raise Exception("Not implemented") def power(env, p, q): if weakFunction(p): p = env.evaluate(p) if weakFunction(q): q = env.evaluate(q) return Number(p.num ** q.num) def quit(env, status=0): sys.exit(int(status)) def range(env, start, finish, step=None): start = int(start.num) finish = int(finish.num) if step is not None: step = int(step.num) result = xrange(start, finish, step) return Table([Number(i) for i in result]) if finish < start: result = reversed(list(xrange(finish, start + 1))) return Table([Number(i) for i in result]) result = xrange(start, finish + 1) return Table([Number(i) for i in result]) def ret(env, result=None): if weakFunction(result): result = env.evaluate(result) return (result, "", "return/exit") def run(env, block): # Hmm, let's make run soft for now if weakFunction(block): block = env.evaluate(block) return block def say(env, *args): saylist = [] for arg in args: if weakFunction(arg): result = env.evaluate(arg) saylist.append(str(result)) else: saylist.append(str(arg)) sys.stdout.write(' '.join(saylist) + '\n') def script(env, function): if hasattr(function, '__call__'): function(sys.argv) elif weakFunction(function): function[String("args")] = sys.argv env.evaluate(function) else: raise ValueError("Arg must be a Function: %s" % function) def split(env, sep, string): return Table([String(arg) for arg in string.split(sep)]) def usage(env, block): # @@ usage autohelp, etc. first, rest = block[0], block[1:] flagspec = Table() spec = str(first[0]) specargs = [arg for arg in spec.split(' ')[1:] if arg != '[options]'] def error(): msg = "Error: Did not satisfy the argument specification" print >> sys.stderr, msg print >> sys.stderr, spec sys.exit(1) args = env.lookup('@args') if len([arg for arg in args if not arg.startswith('-')]): todo = len(args) for i, sa in enumerate(specargs): if sa.endswith('+'): pos = list(args[i:]) if not pos: error() todo -= len(pos) args[String(sa[:-1])] = Table(pos) else: pos = args[i] if pos is None: error() todo -= 1 args[String(sa)] = String(pos) if todo > 0: error() env.bind('@args', args) doc = spec + "\n" for flags in rest: if len(flags) == 4: short, slash, long, flagdoc = flags[0], flags[1], flags[2], flags[3] flagspec[String(short.value)] = flagdoc flagspec[String(long.value)] = flagdoc doc += "%s/%s %s\n" % (short.value, long.value, flagdoc) flagspec[String("doc")] = String(doc) env.bind("@flagspec", flagspec) # This table will be used as the main builtins environment # Therefore only stock it with really useful builtins. # basics = Table({ ":": get, "=": equals, "==": comparesEqual, "+": plus, "-": minus, "*": multiply, "**": power, "<": lessthan, ">": morethan, ".": dot, "|": pipe, "add": add, # @@ push? "args": Table(sys.argv[2:]), "check": check, "def": equals, "else": elsethen, "example": example, "for": loop, "help": help, "if": iff, "join": join, "method": method, "not": nott, "or": orr, "out": out, "prog": String(sys.argv[:2].pop()), "quit": quit, "range": range, "return": ret, "run": run, "say": say, "script": script, "split": split, "usage": usage }) def main(): import cgi print '

Pluvo: Basic Functions

' words, symbols = [], [] for key in sorted(basics.keys()): if key.isalpha(): words.append(key) else: symbols.append(key) for key in (words + symbols): basic = basics[key] if hasattr(basic, '__doc__') and basic.__doc__: doc = basic.__doc__ r_key = re.compile(r'(?<=[ \b])(%s)(?= |\b)' % key) print '

' for line in doc.splitlines(): if not line.strip(' \t\r\n'): continue def strong(m): return '%s' % m.group(1) line = cgi.escape(line) line = line.replace('->', '→') print r_key.sub(strong, line) + '
' print '

' print else: print '

No documentation for %s.

' % key print '
' print 'Sean B. Palmer' print '
' if __name__ == '__main__': main()