예제 #1
0
    def __init__(self):
        self.stack = []
        # last element in array is always top of stack.
        self.funcStack = []

        self.rootNode = None
        # also contains additional data
        self.protObjName = None
        # textual names of each endpoint
        self.endpoint1 = None
        self.endpoint2 = None
        # the line number that the names appear on
        self.endpoint1LineNo = None
        self.endpoint2LineNo = None
        # the ast nodes associated with each endpoint
        self.endpoint1Ast = None
        self.endpoint2Ast = None

        # handles keeping track of msgSend and msgReceive functions
        # (both their specification in the trace section as well as
        # their definitions in endpoint sections.
        self.traceManager = TraceLineManager(self)

        # this keeps track of which endpoint body section we're type
        # checking through.  It's None if we are not currently type
        # checking an endpoint's body section.  It's a string
        # otherwise.  Used for type checking with trace lines (in
        # self.traceManager).
        self.currentEndpointName = None

        self.inOnComplete = False
        self.inSequencesSection = False
        # array of tuples: first element is string endpoint name,
        # second element is string function name.
        self.sequenceSectionNameTuples = []

        # used to type check return statements to ensure that a
        # function actually returns the type that it says it will.
        #      msg_send_seq_func, msg_recv_seq_func, public_func,
        #      on_create, private_func
        self.currentFunctionNode = None

        # allowed to have placeholder extAssigns and extCopies in lhs
        # of assignment statement to accommodate tuples.  Ie, you're
        # allowed to do extAssign _ to some_ext = some_func(); In the
        # rest of the code, you're not allowed to use and lhs_assign
        # statement.
        self.in_lhs_assign = False

        # indices are the struct names.  values are the types of the
        # struct with that name
        self.struct_type_dict = {}

        # initialize endpoint builtin methods
        self.initBuiltinMethods()
예제 #2
0
class TypeCheckContextStack(object):
    def __init__ (self):
        self.stack     = []; #last element in array is always top of stack.
        self.funcStack = [];

        self.rootNode = None;
        #also contains additional data
        self.protObjName = None;
        # textual names of each endpoint
        self.endpoint1 = None;
        self.endpoint2 = None;
        # the line number that the names appear on
        self.endpoint1LineNo = None;
        self.endpoint2LineNo = None;
        # the ast nodes associated with each endpoint
        self.endpoint1Ast = None;
        self.endpoint2Ast = None;
        
        #handles keeping track of msgSend and msgReceive functions
        #(both their specification in the trace section as well as
        #their definitions in endpoint sections.
        self.traceManager = TraceLineManager(self);

        #this keeps track of which endpoint body section we're type
        #checking through.  It's None if we are not currently type
        #checking an endpoint's body section.  It's a string
        #otherwise.  Used for type checking with trace lines (in 
        #self.traceManager).
        self.currentEndpointName = None;

        self.inOnComplete = False;
        self.inSequencesSection = False;
        # array of tuples: first element is string endpoint name,
        # second element is string function name.
        self.sequenceSectionNameTuples = [];
        
        # used to type check return statements to ensure that a
        # function actually returns the type that it says it will.
        #      msg_send_seq_func, msg_recv_seq_func, public_func,
        #      on_create, private_func
        self.currentFunctionNode = None;

        # allowed to have placeholder extAssigns and extCopies in lhs
        # of assignment statement to accommodate tuples.  Ie, you're
        # allowed to do extAssign _ to some_ext = some_func(); In the
        # rest of the code, you're not allowed to use and lhs_assign
        # statement.
        self.in_lhs_assign = False

        # indices are the struct names.  values are the types of the
        # struct with that name
        self.struct_type_dict = {}

        
    def setRootNode(self,root):
        if self.rootNode != None:
            errMsg = '\nBehram error: should not set root node after it has ';
            errMsg += 'already been set.\n';
            print(errMsg);
            assert(False);
        self.rootNode = root;

        
    def checkRepeatedSequenceLine(self):
        '''
        @return{None or TypeCheckError} -- None if no error,
        TypeCheckError if more than one trace line have the same name.
        '''
        return self.traceManager.checkRepeatedSequenceLine();
    

    def getCurrentEndpoint(self):
        if self.currentEndpointName == None:
            errMsg = '\nBehram error: should be type checking an endpoint ';
            errMsg += 'to call getCurrentEndpoint in typestack.\n';
            print(errMsg);
            assert(False);

        if self.currentEndpointName == self.endpoint1:
            return self.endpoint1Ast;
        elif self.currentEndpointName == self.endpoint2:
            return self.endpoint2Ast;

        errMsg = '\nBehram error: no matching endpoint for currentEndpointName.  ';
        errMsg += 'Cannot return current endpoint.\n';
        print(errMsg);
        assert(False);
        
    def getOtherEndpointName(self):
        if (self.currentEndpointName == None) or (self.endpoint1 == None) or (self.endpoint2 == None):
            errMsg = '\nBehram error: should not call getOtherEndpointName in ';
            errMsg += 'astTypeCheckStack.py unless we are currently in an endpoint ';
            errMsg += 'and all endpoints are defined.\n';
            errPrint(errMsg);
            assert(False);

        if (self.currentEndpointName != self.endpoint1):
            return self.endpoint1;
        return self.endpoint2;


    def add_struct_type(self,struct_name,struct_type):
        '''
        @returns {None or String} --- String if there's an error (the
        string is the error message).  None if there's no error.
        '''
        err_msg = None
        if struct_name in self.struct_type_dict:
            err_msg = 'Error.  Already have a struct named '
            err_msg += struct_name + '.'
        
        self.struct_type_dict[struct_name] = struct_type
        return err_msg

    def get_struct_type(self,struct_name,external):
        '''
        @param {bool} external
        
        @returns{type dict or None} --- None if struct_name has not
        been declared by user.  type dict if it has (where type dict
        is the declared type of that node).
        '''
        s_type = self.struct_type_dict.get(struct_name,None)
        if s_type == None:
            return s_type

        # deep copy type so that marking as external or not will not
        # affect any other type.
        s_type = pickle.loads(pickle.dumps(s_type))
        set_external(s_type,external)
        return s_type
        
    def addCurrentFunctionNode(self,node):
        '''
        Sets the current function we're in so that can check return
        types when we get to them.
        '''
        if ((node.label != AST_PUBLIC_FUNCTION) and (node.label != AST_PRIVATE_FUNCTION) and
            (node.label != AST_ONCREATE_FUNCTION) and
            (node.label != AST_MESSAGE_SEND_SEQUENCE_FUNCTION ) and
            (node.label != AST_MESSAGE_RECEIVE_SEQUENCE_FUNCTION) and
            (node.label != AST_ONCOMPLETE_FUNCTION)):
            
            errMsg = '\nBehram error: adding internal or public node with incorrect ';
            errMsg += 'type.\n';
            errPrint(errMsg);
            assert(False);

        self.currentFunctionNode = node;
        
    def checkReturnStatement(self,returnNode,check_type_mismatch_func):
        '''
        @param {function} check_type_mismatch_func ... should just be
        typeCheck.checkTypeMismatch.
        '''
        if (returnNode.label != AST_RETURN_STATEMENT):
            errMsg = '\nBehram error: trying to check a ';
            errMsg += 'return statement without a return statement node.\n';
            errPrint(errMsg);
            assert(False);

            
        if self.currentFunctionNode == None:
            errMsg = '\nBehram error: any return statement should have a ';
            errMsg += 'currentFunctionNode to compare to.\n';
            print(errMsg);
            assert(False);


        return_tuple_node = returnNode.children[0]
        # list of the types that we're actually returning
        return_type_list = []
        for single_node in return_tuple_node.children:

            # takes care of case where we are returning a function
            # call
            if ((single_node.label == AST_FUNCTION_CALL) and
                (not is_wildcard_type(single_node.type))):
                func_returned_type_array = get_type_array_from_func_call_returned_tuple_type(
                    single_node.type)
                for ind_tuple_return_type in func_returned_type_array:
                    return_type_list.append(ind_tuple_return_type)
            else:
                return_type_list.append(single_node.type)
            
        returnStatementType = returnNode.children[0].type;
        if ((self.currentFunctionNode.label == AST_MESSAGE_SEND_SEQUENCE_FUNCTION) or
            (self.currentFunctionNode.label == AST_MESSAGE_RECEIVE_SEQUENCE_FUNCTION)):

            if len(return_type) > 0:
                err_msg = 'Error.  Cannot return a value from a sequence function.'
                nodes= [returnNode];
                return TypeCheckError(nodes,err_msg);

            # no error because message sequences are not declared to
            # return anything and this return statement does not.
            return None

        # check public and privates for return statement...
        function_returns_type_node = self.currentFunctionNode.children[1];
        # the declared return type of this function
        function_returns_type_list = []
        for single_type in function_returns_type_node.children:
            function_returns_type_list.append(single_type.type)

        funcName = self.currentFunctionNode.children[0].value;

        if len(function_returns_type_list) != len(return_type_list):
            err_msg = 'Error.  ' + funcName + ' expects to return '
            err_msg += str(len(function_returns_type_list)) + ' arguments.  '
            err_msg += 'Instead, you returned ' + str(len(return_type_list))
            err_msg += ' arguments.'
            err_nodes = [returnNode, function_returns_type_node]
            return TypeCheckError(err_nodes,err_msg)

        
        for return_index in range(0,len(return_type_list)):
            
            declared_return_type = function_returns_type_list[return_index]
            actual_return_type = return_type_list[return_index]

            if check_type_mismatch_func(
                returnNode,declared_return_type,actual_return_type,self,''):

                err_msg = 'Incorrect return type in ' + funcName + '.  '
                if len(return_type_list) == 1:
                    err_msg += 'Expected type '
                    err_msg += dict_type_to_str(declared_return_type)
                    err_msg += ', but actually returned type '
                    err_msg += dict_type_to_str(actual_return_type)
                    err_msg += '.'
                else:
                    err_msg += 'The ' + str(return_index + 1) + ' tuple return '
                    err_msg += 'element expected a type of '
                    err_msg += dict_type_to_str(declared_return_type)
                    err_msg += ', but actually returned type of '
                    err_msg += dict_type_to_str(actual_return_type)
                    err_msg += '.'

                err_nodes = [returnNode, function_returns_type_node]
                return TypeCheckError(err_nodes, err_msg)

        return None;

    
    def checkTraceItemInputOutput(self):
        '''
        run through all trace lines to see if the message outputs
        match the message inputs.

        @return {None or TypeCheckError} -- None if inputs and outputs
        agree.  TypeCheckError if they do not.
        '''
        return self.traceManager.checkTraceItemInputOutput();

        
    def pushContext(self):
        self.stack.append(Context());
        self.funcStack.append(FuncContext());

    def popContext(self):
        if (len(self.stack) <= 0):
            errPrint('\nBehram Error.  Empty type context stack.  Cannot pop\n');
            assert(False);
        self.stack.pop();
        self.funcStack.pop();
        self.currentFunctionNode = None;

        
    def getIdentifierType(self,identifierName):
        '''
        @param {String} identifierName The name of the identifier that
        we're looking up in the memory store.

        @returns {tuple} (None,None) if have no type information for
        that identifier.  Otherwise, ( a, b).
        a:  String with type name.
        b:  String name of the endpoint that controls the shared
            variable (or None, if no one controls variable.
        '''
        for s in reversed(range(0,len(self.stack))):
            lookupType, controlledBy= self.stack[s].getIdentifierType(identifierName);
            if (lookupType != None):
                return lookupType,controlledBy;
        return None,None;


    def checkUndefinedTraceItems(self):
        '''
        Runs through all elements in trace line to ensure that if a
        msgSend or msgReceive is declared in the trace section, it was
        actually defined in an endpoint section.

        @return{None or TypeCheckError} -- None if no error,
        TypeCheckError otherwise.
        '''
        return self.traceManager.checkUndefinedMsgSendOrReceive();
    

    def checkCollision(self,identifierName,astNode):
        '''
        @param {string} identifierName -- The name of the
        variable/function we want to determine if there is a collision
        for.

        @param {AstNode} astNode -- Needed to pass into constructor of
        CollisionObject (returned if there is a collision; see below).
        
        @return none if no variable or function in the current or
        parent contexts has the same name as identifierName.
        Otherwise, returns a CollisionObject, from which can get error
        message information.
        '''

        if (identifierName == 'Print'):
            return CollisionObject(astNode,None,None);

        
        idElement = self.getIdentifierElement(identifierName);
        funcMatchObj = self.getFuncIdentifierType(identifierName);

        if (idElement == None) and (funcMatchObj == None):
            #no collision
            return None;

        funcElement = None;
        if (funcMatchObj != None):
            funcElement = funcMatchObj.element;

        #there was a collision: generate CollisionObject and return.
        return CollisionObject(astNode,idElement,funcElement);


    def getIdentifierElement(self,identifierName):
        '''
        Usage: should only be called internal to this class.
        
        @param {String} identifierName The name of the identifier that
        we're looking up in the memory store.

        @returns None if have no type information for that identifier.
        Otherwise, returns ContextElement
        '''
        for s in reversed(range(0,len(self.stack))):
            lookupType= self.stack[s].getIdentifierElement(identifierName);
            if (lookupType != None):
                return lookupType;
        return None;

    
    def getFuncIdentifierType(self,identifierName):
        '''
        @returns {FuncMatch object or None}
           -None if there is no declared function with same name
           -FuncMatch object if there is a function with the same name.
        '''
        for s in reversed(range(0,len(self.funcStack))):
            lookupType = self.funcStack[s].getFuncIdentifierType(identifierName);
            if (lookupType != None):
                return lookupType;

        return None;

    
    def addIdentifier(self,identifierName,identifierType,controlledBy,astNode,lineNum = None):
        '''
        @param {String} controlledBy --- name of the endpoint that is
        authoritative for this variable if it's a shared variable.
        None if it's not a shared variable or if no one is
        authoritative for it.
        '''
        
        if(len(self.stack) <= 1):
            errPrint('\nBehram Error.  Cannot insert into type check stack because stack is empty.\n');
            assert(False);
        self.stack[-1].addIdentifier(identifierName,identifierType,controlledBy,astNode,lineNum);

    def isEndpoint(self,endpointName):
        return ((endpointName == self.endpoint1) or (endpointName == self.endpoint2));

    def addFuncIdentifier(self,functionName,functionType,functionArgTypes,astNode,lineNum=None):
        '''
        @param {string} functionName: name of function

        @param{list of type dicts} functionType --- The return type of
        this function.  We're using a list to support returning
        tuples.  Each element of the list is the return type of that
        element of the tuple.
        

        @param {list} functionArgTypes: ordered (from left to right)
        list of types for function arguments.
        
        @param {int} lineNum: line number that function was declared on.

        @returns {None or TyepCheckError} -- None if nothing is wrong
        with trace, TypeCheckError otherwise.
        
        '''
        if len(self.funcStack) <= 1:
            errMsg = '\nBehram Error.  Cannot insert into type ';
            errMsg += 'check stack because stack is empty.\n';
            errPrint(errMsg);
            assert(False);


        #if it's a msgSend function or msgReceive function, then
        #notify the traceLineManager that a msgSend or msgreceive
        #function has been defined;
        currentEndpointName = self.currentEndpointName;
        if (currentEndpointName == None):
            errMsg = '\nBehram error: should ony be adding a ';
            errMsg += 'func identifier in the body of an endpoint section.\n';
            errPrint(errMsg);
            assert(False);

        traceError = None;

        

        if astNode.label == AST_MESSAGE_SEND_SEQUENCE_FUNCTION:
            traceError = self.traceManager.addMsgSendFunction(astNode,currentEndpointName);
        elif astNode.label == AST_MESSAGE_RECEIVE_SEQUENCE_FUNCTION:
            traceError = self.traceManager.addMsgRecvFunction(astNode, currentEndpointName);

        if (traceError != None):
            return traceError;

        #add the function identifier itself to function context.
        traceError = self.funcStack[-1].addFuncIdentifier(
            functionName,functionType,functionArgTypes,astNode,lineNum);
        
        return traceError;
        
        

    def setCurrentEndpointName(self,endpointName):
        '''
        When begin type checking the body of an endpoint function,
        call this with the endpoint's name. 
        '''
        self.currentEndpointName = endpointName;
            
            
    def unsetCurrentEndpointName(self):
        '''
        After finished type checking the body of an endpoint function,
        call this function to unset currentEndpointname.
        '''
        self.currentEndpointName = None;


    def setAstTraceSectionNode(self,traceSectionAstNode):
        '''
        @param {AstNode} traceSectionAstNode
        '''
        self.traceManager.setAstTraceSectionNode(traceSectionAstNode);

        
    def addTraceLine(self,traceLineAstNode):
        '''
        @param{AstNode} traceLineAst
        '''
        return self.traceManager.addTraceLine(traceLineAstNode);