"""BLISP builtins Architecture: Every builtin is a class providing the instance method Call(). The two namespaces of builtin_macros and builtin_functiosn are created when the client calls MakeBuiltins. It's intended that it gets called when initializing an interpreter instance. The reason the builtins are function-objects instead of straight up Python functions is because some functions, like defun, need external data to work. Thus, these special_builtins have constructors that take in arguments supplied by the client calling MakeBuiltins. If you add any new builtins, make sure to inherit either BuiltinMacro or BuiltinFunction! It's important because I don't use a master list or table of all builtins; rather, they're dynamically generated by looping through globals(), using superclases as an informative trait. This also means it's easy to add new builtins: you only have to write in just -- 1 -- place! Just add another class in the middle section and it's good to go! As a bonus, the inheritance system provides a nifty naming system to promote my lazines; the interpreter will generate the lispnames for most functions for me! """ from __future__ import division from evaller import LispFunction, EvalExpr from copy import copy, deepcopy import Parser ############### Superclass infrastructure ################# ##def SplitByCap_Gen(s): ## word = s[0] ## for c in s[1:]: ## if c.islower class BuiltinAnything(object): def __init__(self ): #If no name, make one out of the Python name if not hasattr(self.__class__, 'lispname'): self.lispname = self.make_lispname() def make_lispname(self): return self.__class__.__name__.lower().replace("_", "-") ## words = [w.lower for w in SplitByCap(self.__class__.__name__)] class BuiltinFunction(BuiltinAnything): """You have to do the Call _after_ recursively evaluating all everything in the 2nd -> last positions in the s-expr. Once they're evalutaed, you've got lispargs.""" def Call(self, lispargs): print "%s needs to implement Call()!" %self class BuiltinMacro(BuiltinAnything): """Macros get called without their arguments being evaluated""" def Call(self, lispargs): print "%s needs to implement Call()!" %self ######### Definitions for builtin functions ############## class Eval(BuiltinFunction): """\ Lisp usage: (eval '(+ 1 2)) Python usage: Call( [['+', '1', '2']] ) << or rather, Eval.PyEval(['+', '1', '2']) >> not quoted becaues it's called AFTER EvalExpr'ing away the quote """ def Call(self, lispargs): assert len(lispargs) == 1 eval_this = lispargs[0] eval_this_copy = deepcopy(eval_this) return EvalExpr(eval_this_copy, {}) def PyEval(cls, lispexpr): cls.Call(cls(), [lispexpr]) PyEval = classmethod(PyEval) class Lambda(BuiltinMacro): def Call(self, lispargs): assert len(lispargs) == 2 params = lispargs[0]; assert not isinstance(params, str) evals_to = lispargs[1] return LispFunction(params, evals_to, "Anonymous Lambda Function") class Map(BuiltinFunction): def Call(self, lispargs): assert len(lispargs) == 2 return map(lispargs[1].Call, [ [itm] for itm in lispargs[0] ]) class Defun(BuiltinMacro): needed_external_data = ("defined_fn_ns",) def __init__(self, fn_ns): """Pass it the namespace dictionary where newly defined functions will go""" BuiltinMacro.__init__(self) self.defined_functions = fn_ns def Call(self, lispargs): assert len(lispargs) == 3 name = lispargs[0]; assert isinstance(name, str) params = lispargs[1]; assert not isinstance(params, str) evals_to = lispargs[2] if name in self.defined_functions: raise LispExeption, \ "defun error: Function with name '%s' already exists" % name self.defined_functions[name] = LispFunction(params, evals_to, name) return 0 class Quote(BuiltinMacro): def Call(self, lispargs): return lispargs[0] class If(BuiltinMacro): """Doesnt ayet work""" def Call(self, lispargs): assert len(lispargs) in (2,3) test, ontrue = lispargs[:2] if len(lispargs) == 3: onfalse = lispargs[2] else: onfalse = Parser.ParseString("(quote NIL)") testresult = Eval.PyEval(lispargs[0]) if testresult in ('t','T'): return Eval.PyEval(ontrue) elif testresult in ('nil', 'NIL', 'Nil'): return Eval.PyEval(onfalse) else: print "if: Oops! Instead of T or NIL, got '%s'! Returning NIL for the hell of it" %testresult return 'NIL' class Not(BuiltinFunction): def Call(self, lispargs): if lispargs[0] in ('t', 'T'): return 'NIL' if lispargs[0] in ('nil', 'NIL', 'Nil'): return 'T' raise LispException, "%s neither true nor false value" %lispargs[0] ## Math functions ## class Add(BuiltinFunction): lispname = '+' def __init__(self): BuiltinFunction.__init__(self) def Call(self, lispargs): assert len(lispargs) > 1 for e in lispargs: assert isinstance(e, str), e numeric_sum = 0 for str_num in lispargs: numeric_sum += int(str_num) return str(numeric_sum) class Mult(BuiltinFunction): lispname = '*' def __init__(self): BuiltinFunction.__init__(self) def Call(self, lispargs): assert len(lispargs) > 1 for e in lispargs: assert isinstance(e, str), e numeric_prod = 1 for str_num in lispargs: numeric_prod *= int(str_num) return str(numeric_prod) class Divide(BuiltinFunction): lispname = '/' def __init__(self): BuiltinFunction.__init__(self) def Call(self, lispargs): assert len(lispargs) == 2 for e in lispargs: assert isinstance(e, str), e return str(int(lispargs[0]) / int(lispargs[1])) class Subtract(BuiltinFunction): lispname = '-' def __init__(self): BuiltinFunction.__init__(self) def Call(self, lispargs): assert len(lispargs) > 1 for e in lispargs: assert isinstance(e, str), e numeric_sum = int(lispargs[0]) for str_num in lispargs[1:]: numeric_sum -= int(str_num) return str(numeric_sum) class Modulo(BuiltinFunction): lispname = "%" def Call(self, lispargs): assert len(lispargs) == 2 return str(int(lispargs[0]) % int(lispargs[1])) ## Interpreter control ## class Setopt(BuiltinMacro): needed_external_data = ("interper",) def __init__(self, interper): BuiltinMacro.__init__(self) self.interper = interper def Call(self, lispargs): option, newval = lispargs[:2] if option == 'debug': if newval == 'on': self.interper.DEBUG = 1 elif newval == 'off': self.interper.DEBUG = 0 ## NOTE: Since there are no cons cells in blisp, we only support lisp-lists. ## This means there are no dotted pairs and related things I don't know about ## that lisp is supposed to have. class Cons(BuiltinFunction): def Call(self,lispargs): assert len(lispargs) == 2 return [lispargs[0]] + lispargs[1] class Cdr(BuiltinFunction): def Call(self, lispargs): assert len(lispargs) == 1 return lispargs[0][1:] class Car(BuiltinFunction): def Call(self, lispargs): assert len(lispargs) == 1 return lispargs[0][0] ############### Dynamic (namespace generation system) ################## #These need arguments passed to their constructors; that is, #external references to important parts of the interpreter #beyond builtins.py all_builtins = [B for B in globals().values() if B.__class__ == type #Gotta be a class if issubclass(B, BuiltinAnything) #and a builtin if B.__class__ not in \ (BuiltinMacro, BuiltinFunction) #but not a parent class ] special_builtins = [B for B in all_builtins \ if hasattr(B, 'needed_external_data') ] def MakeBuiltins(**external_data): global special_builtins, all_builtins builtin_fns, builtin_macros = {}, {} for BuiltinClass in all_builtins: if BuiltinClass in special_builtins: args = ArgsForSpecialCase(BuiltinClass, external_data) else: args = [] new_builtin = BuiltinClass(*args) if issubclass(BuiltinClass, BuiltinFunction): builtin_fns[new_builtin.lispname] = new_builtin elif issubclass(BuiltinClass, BuiltinMacro): builtin_macros[new_builtin.lispname] = new_builtin return builtin_fns, builtin_macros def ArgsForSpecialCase(Builtin, external_data): return [external_data[data_name] for data_name in \ Builtin.needed_external_data]