def analyzeFunction(theFunction, patternIndex, variables, inPatternVariables=None, fakeVariables = None, realToFakeMap = None, vIndex = None, fakeNames = None): ''' Analyze a FunctionCall inside a Test-CE to replace variables name with fake names an bound locations to improve reuse of test-nodes (normalize variables names) @param theFunction: a function call to replace @type theFunction: types.FunctionCall @param patternIndex: the index of the test-pattern in the chain of ce @type patternIndex: int @param variables: a dict of variable name => VariableReferences @type variables: dict @param fakeVariables: a dict of fake variables references or None (to make a new one) @type fakeVariables: dict @param realToFakeMap: a map of real variables names to fake ones @type realToFakeMap: dict @param vIndex: the index for fake variables name generation @type vIndex: int ''' inPatternVariables = [] if inPatternVariables is None else inPatternVariables aNewFunctionCallArgs = [] fakeVariables = {} if fakeVariables is None else fakeVariables fakeNames = {} if fakeNames is None else fakeNames realToFakeMap = {} if realToFakeMap is None else realToFakeMap vIndex = [] if vIndex is None else vIndex; if isinstance(theFunction, types.FunctionCall): assert isinstance(theFunction, types.FunctionCall) for aArg in theFunction.funcArgs: # globals are resolved at function-call execution time if isinstance(aArg, (types.SingleFieldVariable, types.MultiFieldVariable)): # i've already created a fake_var for this var? if not realToFakeMap.has_key(aArg.evaluate()) : # where i found the variable first? mainReference = getVar(aArg.evaluate(), variables, inPatternVariables) if mainReference is False: raise MyClipsException("Variable %s found in the expression %s was referenced in CE #%d before being defined."%( aArg.evaluate(), theFunction.toClipsStr(), patternIndex )) varReference = VariableReference() varReference.reference = mainReference varReference.relPatternIndex = mainReference.patternIndex - patternIndex theFakeName = "%"+str(len(vIndex)) vIndex.append(None) theFakeVar = aArg.__class__(types.Symbol(theFakeName)) fakeVariables[theFakeVar.evaluate()] = varReference realToFakeMap[aArg.evaluate()] = theFakeVar.evaluate() fakeNames[aArg.evaluate()] = theFakeVar else: theFakeVar = fakeNames[aArg.evaluate()] # replace the variable name with a fake name #aArg.content = theFakeName aNewFunctionCallArgs.append(theFakeVar) elif isinstance(aArg, types.FunctionCall): # recursion: replace arguments inside the function call # fakeReferences are ignored because the dict is automatically # updated by the recursion. aInnerNewFunctionCall, _ = analyzeFunction(aArg, patternIndex, variables, inPatternVariables, fakeVariables, realToFakeMap, vIndex, fakeNames) # replace the old function call with a new one with fake variables #theFunction.funcArgs[iArg] = aInnerNewFunctionCall aNewFunctionCallArgs.append(aInnerNewFunctionCall) else: aNewFunctionCallArgs.append(aArg) # get the current scope and backup it _tmp_scope = theFunction.scope.modules.currentScope # then change it to the original function call scope theFunction.scope.modules.changeCurrentScope(theFunction.scope.moduleName) # create the new function call newFunctionCall = types.FunctionCall(theFunction.funcName, theFunction.scope.modules, aNewFunctionCallArgs) # then restore the previous scope theFunction.scope.modules.changeCurrentScope(_tmp_scope.moduleName) return (newFunctionCall, fakeVariables) else: raise TypeError("AnalyzeFunction require a FunctionCall as first argument")
def _analyzeTerm(atomLocation, aTerm, variables, inPatternVariables): ''' Analyze a types.Term and create a list of pattern tests and join tests to describe the term @param atomLocation: the location of the term inside the LHS @param aTerm: a types.Term element @param variables: a dict of varName => VarLocation(s) in previous patterns @param inPatternVariables: a list of VarLocations in the current pattern ''' alphaTests = [] joinTests = [] isNegative = True if isinstance(aTerm, types.NegativeTerm) else False # dewrap: aTerm from a Term object to Term's content aTerm = aTerm.term if isinstance(aTerm, (types.MultiFieldVariable, types.SingleFieldVariable)): varLocation = VariableLocation.fromAtomLocation(aTerm.evaluate(), atomLocation) # check the main location of the variable # but if it's the same of this location, ignore the cc mainReference = getVar(aTerm.evaluate(), variables, inPatternVariables) if mainReference is not False: # create a reference to this variable varReference = VariableReference() varLocation.toVarReference(varReference) varReference.reference = mainReference varReference.isNegative = isNegative # calcolate the relPatternIndex if varLocation.patternIndex is not None: if mainReference.patternIndex is not None: varReference.relPatternIndex = mainReference.patternIndex - varLocation.patternIndex else: # TODO verificare!!!! varReference.relPatternIndex = 0 - varLocation.patternIndex else: varReference.relPatternIndex = 0 # make a new join test joinTests.append(VariableBindingTest(varReference)) else: # unknown variable! inPatternVariables.append(varLocation) elif isinstance(aTerm, types.BaseParsedType): # a pattern test is required, but created by _makeAlphaNetwork. so ignore # remove patternIndex location from the atomLocation # because alpha tests are always on the wme from the right atomLocationCopy = copy(atomLocation) atomLocationCopy.patternIndex = None aAlphaTest = ConstantValueAtIndexTest(atomLocationCopy, aTerm) alphaTests.append(aAlphaTest if not isNegative else NegativeAlphaTest(aAlphaTest)) elif isinstance(aTerm, types.FunctionCall): # well... this is a special case. This must be converted in a # (test (function-call)) _newFunc, _fakeVar = analyzeFunction(aTerm, atomLocation.patternIndex, variables, inPatternVariables) joinTests.append(DynamicFunctionTest(_newFunc, _fakeVar)) # unnamed multifield and single field are ignored return (alphaTests, joinTests)