예제 #1
0
파일: cmdParse.py 프로젝트: csayres/lcoTCC
    def parseLine(self, inputLine):
        """!Parse an input line, return a ParsedCmd Object

        @param[in] inputLine  line to parse
        @return parsedCmd, a ParsedCmd object
        @throw ParseError if command cannot be parsed.
        """
        # try:
        # pyparsing, returns object with
        # verb, params, quals as previously defined attributes
        ppOut = self.genericCmd.parseString(inputLine, parseAll=True)
        # find correct command definition
        cmdNames = self.cmdMatchList.getAllMatches(ppOut.cmdVerb)
        if len(cmdNames) == 0:
            raise ParseError("Unrecognized command %r" % (ppOut.cmdVerb,))
        elif len(cmdNames) > 1:
            listStr = ", ".join(cmdNames)
            raise ParseError("%r not unique; could be any of %s" % (ppOut.cmdVerb, listStr))
        cmdName = cmdNames[0]

        cmdDef = self.cmdDefDict[cmdName]
        parsedCmd = ParsedCmd(cmdDef.name) # initialize

        if hasattr(cmdDef, "subCmdList"):
            # find the secondary command in the cmdList (the first name in the first slot)
            # alternate parsing enabled in this case
            secCmd = ppOut.parameters[0][0][0]
            # match with the name of the first parameter in the first slot
            ind = getUniqueAbbrevIndex(
                secCmd,
                [item.paramList[0].paramElementList[0].name for item in cmdDef.subCmdList]
            )
            # (re) set cmdDef to this and carry on
            cmdDef = cmdDef.subCmdList[ind]

        ################## add and validate qualifiers. #################
        # set value = None if no value was passed, values are always a list if not None
        for qual in ppOut.qualifiers:
            boolValue = True
            qualName = qual[0]
            try:
                # if a value was passed, it will be in index=1
                #qualVal = qual[1][0]
                qualVal = [q[0] for q in qual[1]]
            except IndexError:
                # there was no value passed
                qualVal = None
            try:
                ind = getUniqueAbbrevIndex(qualName, [item.name for item in cmdDef.qualifierList])
            except ValueError:
                # match wasn't found, try again using negated qualifiers (where allowed)
                nameList=[]
                for tempQual in cmdDef.qualifierList:
                    if tempQual.negatable:
                        nameList.append('No' + tempQual.name)
                    else:
                        nameList.append(tempQual.name)
                ind = getUniqueAbbrevIndex(qualName, nameList)
                boolValue = False # we just encountered a negated qualifier
            qualDef = cmdDef.qualifierList[ind]

            if qualDef.valType == None:
                # this is a flag-like qualifier with no values
                if qualVal != None:
                    raise ParseError('Expected no value for qualifier: %s' % (qualDef.name,))
                parsedCmd.addQual(qualDef.name, boolValue, boolValueDefaulted=False)
            elif callable(qualDef.valType):
                # this qualifier accepts a simple valueList of castable elements
                if qualVal == None:
                    # no value was passed, set the default (which may in fact be empty)...
                    parsedCmd.addQual(
                        qualDef.name,
                        boolValue,
                        boolValueDefaulted=False,
                        valueList = qualDef.defValueList,
                        valueListDefaulted = True ,
                    )
                else:
                    # values were passed with this qualifier...check they cast correctly
                    try:
                        valueList = [qualDef.valType(item) for item in qualVal]
                    except ValueError:
                        raise ParseError('Could not cast values %s to correct type %s' % \
                            (qualVal, qualDef.valType))
                    else:
                        # check if amount of values are allowed
                        if qualDef.numValueRange[1] == None:
                            if len(valueList) < qualDef.numValueRange[0]:
                                raise ParseError('Not Enough values for qualifer %s, got %i, expected at least %i' \
                                    % (qualDef.name, len(valueList), qualDef.numValueRange[0]))
                        else:
                            if not (qualDef.numValueRange[0] <= len(valueList) <= qualDef.numValueRange[1]):
                                raise ParseError('Invalid number of values for qualifier %s.' \
                                     'Got: %i, should be between %i and %i' % \
                                     (
                                        qualDef.name, len(valueList),
                                        qualDef.numValueRange[0],
                                        qualDef.numValueRange[1])
                                     )
                        # amount of values is ok
                        parsedCmd.addQual(
                            qualDef.name,
                            boolValue,
                            boolValueDefaulted=False,
                            valueList = valueList,
                            valueListDefaulted = False,
                        )
            else:
                # this qualifier has values that belong to a specific set of keywords
                if qualVal == None:
                    # no value was passed, add the default value list (which may be empty)
                    parsedCmd.addQual(
                        qualDef.name,
                        boolValue,
                        boolValueDefaulted=False,
                        valueList = qualDef.defValueList,
                        valueListDefaulted = True ,
                    )
                else:
                    # search each value for it's full (non-abbreviated) representation
                    # in the parsedCmd valType field
                    valueList = [RO.Alg.MatchList(valueList=qualDef.valType).getUniqueMatch(keyword) for keyword in qualVal]
                    # check if amount of values are allowed
                    if qualDef.numValueRange[1] == None:
                        if len(valueList) < qualDef.numValueRange[0]:
                            raise ParseError('Not Enough values for qualifer %s, got %i, expected at least %i' \
                                % (qualDef.name, len(valueList), qualDef.numValueRange[0]))
                    else:
                        if not (qualDef.numValueRange[0] <= len(valueList) <= qualDef.numValueRange[1]):
                            raise ParseError('Invalid number of values for qualifier %s. ' \
                                'Got: %i, should be between %i and %i' % \
                                (
                                    qualDef.name, len(valueList),
                                    qualDef.numValueRange[0],
                                    qualDef.numValueRange[1])
                                )
                    parsedCmd.addQual(
                        qualDef.name,
                        boolValue,
                        boolValueDefaulted=False,
                        valueList = valueList,
                        valueListDefaulted = False,
                    )

        # now append the remaining (un commanded) qualifiers
        for qual in cmdDef.qualifierList:
            if qual.name.lower() not in parsedCmd.qualDict:
                parsedCmd.addQual(
                    qual.name,
                    boolValue = qual.defBoolValue,
                    boolValueDefaulted = True,
                    valueList = qual.defValueList,
                    valueListDefaulted = True,
                )

        ############### add and validate parameters ######################
        if len(cmdDef.paramList) < len(ppOut.parameters):
            raise ParseError('Too many parameters for command %r' % (inputLine,))
        if len(ppOut.parameters) < cmdDef.minParAmt:
            raise ParseError('Too few parameters for command %r' % (inputLine,))
        for paramSlotDef, paramSlotGot in itertools.izip_longest(cmdDef.paramList, ppOut.parameters):
            paramSlotName = paramSlotDef.name
            paramList = []
            if not paramSlotGot:
            # if got no slot, look for a default
                if paramSlotDef.defaultParamList: # is not None:
                    for paramDef in paramSlotDef.defaultParamList: # default list may be empty
                        validatedName = paramDef.name # may be None, if not a keyword type parameter
                        valueList = paramDef.defValueList # may be None, if not specified in definitions
                        if validatedName:
                            # keyword type parameter
                            paramList.append(ParsedKeyword(validatedName, valueList, defaulted=True))
                            parsedCmd.addParam(paramSlotName, valueList = paramList, defaulted=True, boolValue=False)
                        elif valueList is not None:
                            # param is a simple value list
                            paramList = valueList
                            parsedCmd.addParam(paramSlotName, valueList = paramList, defaulted=True, boolValue=False)
                        else:
                            # no defaults were defined, do nothing
                            raise RuntimeError("Conor believes this is a bug, investigate")
                            #continue
                            #parsedCmd.addParam(paramSlotName, valueList=None)
                else:
                    # no defaults specified, add an empty entry in the parsed parameter dictionary
                    parsedCmd.addParam(paramSlotName, valueList=(), boolValue=False)
            elif paramSlotDef.matchList:
                # got a slot, and it is keywords parse it
                if paramSlotDef.numParamRange[1] == None:
                    # no upper bound on range
                    correctParamAmt = paramSlotDef.numParamRange[0] <= len(paramSlotGot)
                else:
                    # there is an upper bound
                    correctParamAmt = (paramSlotDef.numParamRange[0] <= len(paramSlotGot) <= paramSlotDef.numParamRange[1])
                if not correctParamAmt:
                    raise ParseError('Too many elements in parameter slot for command %r' % (inputLine,))
                for paramGot in paramSlotGot:
                    paramDef = paramSlotDef.paramElementList[getUniqueAbbrevIndex(paramGot.pop(0), paramSlotDef.matchList)]
                    validatedName = paramDef.name
                    # are values associated with this keyword? They will be left over in paramGot
                    if paramGot:
                        # values were passed by user in association with this keyword
                        if paramDef.numValueRange[1] == None:
                            # no upper value limit
                            correctValueAmt = paramDef.numValueRange[0] <= len(paramGot)
                        else:
                            # there is an upper limit
                            correctValueAmt = paramDef.numValueRange[0] <= len(paramGot) <= paramDef.numValueRange[1]
                        if not correctValueAmt:
                            raise ParseError('Incorrect amt of values passed for %s param. Commanded: %r ' % (paramDef.name, inputLine))
                        # zero (p[0]) index is a pyparsing definition, could figure out how to fix it...but for now it works
                        valueList = [paramDef.castVals(p[0]) for p in paramGot]
                    else:
                        # no values passed by user, but a default value list is
                        # defined for this parameter.
                        valueList = paramDef.defValueList # will be empty if no default list
                    defaulted = True if valueList else False
                    paramList.append(ParsedKeyword(validatedName, valueList, defaulted))
                parsedCmd.addParam(paramSlotName, valueList = paramList)
            else:
                # this is a simple value (non-keyword) parameter
                # treat this slot as a single list of values
                # do this because of pyparsing behavior doesn't wrap in an outer list
                # unless it is a list of keyword=valueList
                paramGot = paramSlotGot
                assert len(paramSlotDef.paramElementList) == 1
                paramDef = paramSlotDef.paramElementList[0]
                if paramDef.numValueRange[1] == None:
                    # no upper value limit
                    correctValueAmt = paramDef.numValueRange[0] <= len(paramGot)
                else:
                    # there is an upper limit
                    correctValueAmt = paramDef.numValueRange[0] <= len(paramGot) <= paramDef.numValueRange[1]
                if not correctValueAmt:
                    raise ParseError('Too many elements in parameter slot for command %r' % (inputLine,))
                validatedName = None
                if paramGot[0][0]=='"'==paramGot[0][-1]:
                    # special case, this parameter is a quoted string, only used in
                    # talk and broadcast commands; dequote and send along
                    paramList = [paramDef.castVals(unquoteStr(paramGot[0]))]

                else:
                    paramList = [paramDef.castVals(par) for par, in paramGot]
                parsedCmd.addParam(paramSlotName, valueList=paramList)

        parsedCmd.addCall(cmdDef.callFunc)
        return parsedCmd
예제 #2
0
 def onParse(token):
     return unquoteStr(token[0])