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
def onParse(token): return unquoteStr(token[0])