aboutsummaryrefslogblamecommitdiffstats
path: root/mop/__init__.py
blob: 5281f967087a374984851000dcb6100b77eecebb (plain) (tree)
1
2
3
4
5

                                                  


                                                                               









                                         

                                                 



                                                                            
                       
                                   
                 

                                            
                           

                                                           
 






                                                                        

                                         
 
                                                 
                             







                                         
 







                                            
 
                                                  



                                       
                                   

             







                                                           











                                                                              






                                                                                



                                                                               



                                                   
                                                 
                                                
                                
     
                                                            
                                                          

                                                                 


                                                                         
                                             
                          
     
                                                       
                                                       



                                                                              
 
                                                  


                                            
                          
                                             
                                              
      
 

                                 
                      
                                  
                  
                                    
                  
                                             
                  
      
 
                                              
                                             
                                                    
      
 

                                             
                       

                                                   
                         
                                             
                                        
      
 
                                           
                                                 
                                        

      


                                                   



                                                 
                                                    

      
                             
                                                 
                                  



                                             
                                               



                                                 
                                  
                                      

                                                   
                                     

                                   
                                          
                                                           
                 
                                                                     
                       


                                             
 


                                                                              

                                           
                                             


                       
                                                                                
 
                                  
                                                   


                                             
 
                                                        
                                                     

                                      



                                                              

                                                                           

                                                
 
                                                      
 

                                          
                                
                                


                                      
                                                                               
                         
                                                                             
 
                             
                                            
      
                             
                                            
      
 



                                                                         
 
                            
                                            
      
 
                            
                                                        
      
 
                          
                    

                                             
                      
                            
                                            

      
                            

                                                           
                            

                                                     
                            

                                                          
 
                       

                                                              
                            


                                      
                         
                                  
                           
                             

                            

                               
                                                            
                             

                            
 
                                                      
 
                                 
                                                    
                            

                                          
 





                                                                          

                                                            





                                                                             
                                                                  
                                                           
                             


                                    

                                                                        
                            
                                                        
      
                            
                                                              
      
                                
                                                  
      
                                
                                            

      
           
# Phase 0: absolute basics that we must start with

# things that must be provided by the underlying system: a place to store the
# mop itself, a data structure for instance data (with no associated behavior),
# and a way to run a chunk of code, given an invocant and argument list
Class     = None
Object    = None
Method    = None
Attribute = None

class BasicInstance(object):
    def __init__(self, metaclass, slots):
        self.metaclass = metaclass
        self.slots = slots

def execute_method(body, invocant, args, kwargs):
    return body(invocant, *args, **kwargs)

# shim layer to interface with python - in a real system, this wouldn't be
# necessary, but this allows us to pass python-level method calls through to
# our mop infrastructure
UNDERLYING_CLASSES = {}
def python_class_for(c, name=None):
    key = hash(c)
    if key not in UNDERLYING_CLASSES.keys():
        if name is None:
            name = c.name()
        UNDERLYING_CLASSES[key] = type(name, (object,), {})
    return UNDERLYING_CLASSES[key]

def python_install_method(c, name, method):
    setattr(
        python_class_for(c),
        name,
        lambda self, *args, **kwargs: method.execute(self, args, kwargs)
    )

def bootstrap():
    # Phase 1: construct the core classes

    def bootstrap_create_class(name, superclass):
        return BasicInstance(
            globals().get("Class"),
            {
                "name": name,
                "superclass": superclass,
                "methods": {},
                "attributes": {},
            },
        )

    def bootstrap_create_method(name, body):
        return BasicInstance(
            globals().get("Method"),
            {
                "name": name,
                "body": body,
            }
        )

    def bootstrap_create_attribute(name, default):
        return BasicInstance(
            globals().get("Attribute"),
            {
                "name": name,
                "default": default,
            }
        )

    global Class, Object, Method, Attribute

    Class     = bootstrap_create_class('Class', None)
    Object    = bootstrap_create_class('Object', None)
    Method    = bootstrap_create_class('Method', Object)
    Attribute = bootstrap_create_class('Attribute', Object)

    # need to make sure we call this explicitly with the class name during the
    # bootstrap, since we won't have name() yet
    python_class_for(Class,     'Class')
    python_class_for(Object,    'Object')
    python_class_for(Method,    'Method')
    python_class_for(Attribute, 'Attribute')

    Class.__class__     = python_class_for(Class)
    Object.__class__    = python_class_for(Class)
    Method.__class__    = python_class_for(Class)
    Attribute.__class__ = python_class_for(Class)

    # Phase 2: tie the knot

    Class.metaclass = Class
    Class.slots["superclass"] = Object

    # Phase 3: manually assemble enough scaffolding to allow object construction

    # this add_method implementation is temporary, since it touches the slots
    # directly and fiddles with method.__class__ and such - once the full mop
    # is complete, we won't need to do those things (and they might even be the
    # wrong things to do), so we will replace it at the end
    def add_method(self, method):
        name = method.slots["name"]
        self.slots["methods"][name] = method
        method.__class__ = python_class_for(Method)
        python_install_method(self, name, method)
    method_add_method = bootstrap_create_method(
        "add_method", add_method
    )
    Class.slots["methods"]["add_method"] = method_add_method
    method_add_method.__class__ = python_class_for(Method)
    python_install_method(Class, "add_method", method_add_method)

    # same here
    def execute(self, invocant, args, kwargs):
        return execute_method(self.slots["body"], invocant, args, kwargs)
    method_execute = bootstrap_create_method(
        "execute", execute
    )
    Method.slots["methods"]["execute"] = method_execute
    method_execute.__class__ = python_class_for(Method)
    # note: not using python_install_method here, since that installs a method
    # which calls method.execute, and this is where we have the recursion base
    # case
    setattr(python_class_for(Method), "execute", method_execute.slots["body"])

    # temporary, we'll have a better version later
    def gen_reader(name):
        return lambda self: self.slots[name]

    # mro needs superclass
    Class.add_method(bootstrap_create_method(
        "superclass", gen_reader("superclass")
    ))

    # all_attributes requires mro
    def mro(self):
        mro = [ self ]
        parent = self.superclass()
        if parent:
            mro.extend(parent.mro())
        return mro
    Class.add_method(bootstrap_create_method(
        "mro", mro
    ))

    # all_attributes requires local_attributes
    Class.add_method(bootstrap_create_method(
        "local_attributes", gen_reader("attributes")
    ))

    # create_instance requires all_attributes
    def all_attributes(self):
        attributes = {}
        for c in reversed(self.mro()):
            attributes.update(c.local_attributes())
        return attributes
    Class.add_method(bootstrap_create_method(
        "all_attributes", all_attributes
    ))

    # default_for_instance requires default
    Attribute.add_method(bootstrap_create_method(
        "default", gen_reader("default")
    ))

    # create_instance requires default_for_instance
    def default_for_instance(self):
        default = self.default()
        if callable(default):
            default = default()
        return default
    Attribute.add_method(bootstrap_create_method(
        "default_for_instance", default_for_instance
    ))

    # set_value requires name
    Attribute.add_method(bootstrap_create_method(
        "name", gen_reader("name")
    ))

    # create_instance requires set_value
    def set_value(self, instance, new_value):
        instance.slots[self.name()] = new_value
    Attribute.add_method(bootstrap_create_method(
        name="set_value", body=set_value
    ))

    # new requires create_instance
    def create_instance(self, kwargs):
        instance = BasicInstance(self, {})
        instance.__class__ = python_class_for(self)
        attrs = self.all_attributes()
        for attr_name in attrs:
            attr = attrs[attr_name]
            if attr_name in kwargs.keys():
                attr.set_value(instance, kwargs[attr_name])
            else:
                attr.set_value(instance, attr.default_for_instance())
        return instance
    Class.add_method(bootstrap_create_method(
        "create_instance", create_instance
    ))

    # using __call__ in order to make the syntax look like normal python
    # constructors, but this is really just a normal static method, it's still
    # not anything python-specific
    def new(self, **kwargs):
        return self.create_instance(kwargs)
    Class.add_method(bootstrap_create_method(
        "__call__", new
    ))

    # Phase 4: Object construction works, just need attributes to construct with

    def add_attribute(self, attr):
        self.local_attributes()[attr.name()] = attr
    Class.add_method(bootstrap_create_method(
        "add_attribute", add_attribute
    ))

    attr_name = bootstrap_create_attribute("name", None)
    attr_name.__class__ = python_class_for(Attribute)
    Attribute.add_attribute(attr_name)

    attr_default = bootstrap_create_attribute("default", None)
    attr_default.__class__ = python_class_for(Attribute)
    Attribute.add_attribute(attr_default)

    # and now object creation works! add the method attributes now to allow
    # creating method objects
    Method.add_attribute(Attribute(name="name"))
    Method.add_attribute(Attribute(name="body"))

    # Phase 5: now we can populate the rest of the mop

    def value(self, instance):
        return instance.slots[self.name()]
    Attribute.add_method(Method(
        name="value", body=value
    ))

    # here's the better implementation
    # we'll replace the accessors generated by the earlier implementation later
    def gen_reader(name):
        return lambda self: self.metaclass.all_attributes()[name].value(self)

    Method.add_method(Method(
        name="name", body=gen_reader("name")
    ))
    Method.add_method(Method(
        name="body", body=gen_reader("body")
    ))

    Class.add_attribute(Attribute(name="name"))
    Class.add_attribute(Attribute(name="superclass"))
    Class.add_attribute(Attribute(name="attributes", default=lambda: {}))
    Class.add_attribute(Attribute(name="methods", default=lambda: {}))

    Class.add_method(Method(
        name="name", body=gen_reader("name")
    ))

    Class.add_method(Method(
        name="local_methods", body=gen_reader("methods")
    ))

    def all_methods(self):
        methods = {}
        for c in reversed(self.mro()):
            methods.update(c.local_methods())
        return methods
    Class.add_method(Method(
        name="all_methods", body=all_methods
    ))

    Class.add_method(Method(
        name="attribute_class", body=lambda self: Attribute
    ))
    Class.add_method(Method(
        name="method_class", body=lambda self: Method
    ))
    Class.add_method(Method(
        name="base_object_class", body=lambda self: Object
    ))

    def finalize(self):
        for method in self.all_methods().values():
            python_install_method(self, method.name(), method)
    Class.add_method(Method(
        name="finalize", body=finalize
    ))

    def isa(self, other):
        mro = self.metaclass.mro()
        return other in mro
    Object.add_method(Method(
        name="isa", body=isa
    ))

    def can(self, method_name):
        return self.metaclass.all_methods().get(method_name)
    Object.add_method(Method(
        name="can", body=can
    ))

    # Phase 6: now we have to clean up after ourselves

    def add_method(self, method):
        self.local_methods()[method.name()] = method
    Class.add_method(Method(
        name="add_method", body=add_method
    ))

    Class.finalize()
    Object.finalize()
    Attribute.finalize()

    # we can't call Method.finalize(), since that would overwrite our base
    # implementation of Method.execute and lead to infinite recursion
    for method in Object.local_methods().values():
        python_install_method(Method, method.name(), method)

    # we add the better version of Method.execute to the internal method map,
    # but we don't actually install it. this way, all method subclasses will
    # use this implementation, but the base Method class will not (which is
    # safe because we know exactly how the base Method class is implemented)
    def execute(self, invocant, args, kwargs):
        body = self.metaclass.all_attributes()["body"].value(self)
        return execute_method(body, invocant, args, kwargs)
    Method.add_method(Method(
        name="execute", body=execute
    ))

    # do the same thing with accessor methods that we installed with our
    # temporary version of gen_reader
    Class.add_method(Method(
        name="superclass", body=gen_reader("superclass")
    ))
    Class.add_method(Method(
        name="local_attributes", body=gen_reader("attributes")
    ))
    Attribute.add_method(Method(
        name="default", body=gen_reader("default")
    ))
    Attribute.add_method(Method(
        name="name", body=gen_reader("name")
    ))

bootstrap()