def do(self, funcEnv, resourceId, *args, **kargs): """ Retract function handler implementation """ # convert <TYPE:value> to python value resourceId = Function.resolve( self, funcEnv, self.semplify(funcEnv, resourceId, types.Symbol, ("1", "symbol"))) if resourceId != "nil": try: resource = funcEnv.RESOURCES[resourceId] except KeyError: raise InvalidArgValueError( "Resource with logical name %s cannot be found" % str(resourceId)) else: # for fragment in args: # # # revolve variables and function calls # fragment = self.resolve(funcEnv, self.semplify(funcEnv, fragment)) # # resource.write(str(fragment)) resource.write("".join([ str(self.resolve(funcEnv, self.semplify(funcEnv, x))) for x in args ])) return types.NullValue()
def do(self, theEnv, aPath, *args, **kargs): """ function handler implementation """ aPath = self.resolve( theEnv, self.semplify(theEnv, aPath, types.Lexeme, ('1', 'symbol or string'))) aPath = os.path.abspath(aPath) if not exists(aPath): raise InvalidArgValueError( "Function load was unable to open file %s" % aPath) oldcwd = os.getcwd() os.chdir(os.path.dirname(aPath)) aFile = open(aPath, 'rU') aString = aFile.read() try: parsed = theEnv.network.getParser().parse(aString, extended=True) except Exception, e: print >> theEnv.RESOURCES['werror'], theEnv.network.getParser( ).ExceptionPPrint(e, aString) os.chdir(oldcwd) return types.Symbol('FALSE')
def do(self, theEnv, *args, **kargs): """ handler of the function @see: http://www.comp.rgu.ac.uk/staff/smc/teaching/clips/vol1/vol1-13.7.html#Heading450 """ args = list(args) args.reverse() try: for theModule in args: theModule = self.resolve( theEnv, self.semplify(theEnv, theModule, types.Symbol, ("ALL", "symbol"))) if not theEnv.modulesManager.isDefined(theModule): raise InvalidArgValueError("Unable to find defmodule %s" % theModule) else: theEnv.network.agenda.focusStack.append(theModule) return types.Symbol("TRUE") except IndexError: return types.Symbol("FALSE")
def do(self, theEnv, theValue, theComparison, theThen, *args, **kargs): """ handler of the Case function: check theValue against theComparison: if they match the actions in the case arguments are executed otherwise an InvalidCaseException is raised to return control to the outer switch function @see: http://www.comp.rgu.ac.uk/staff/smc/teaching/clips/vol1/vol1-12.6.html#Heading287 """ returnValue = types.Symbol("FALSE") # check for case format first: if not isinstance(theThen, types.Symbol) or theThen.pyEqual("then"): raise InvalidArgValueError("The `then` keyword is expected as second argument of a case clause") # resolve theComparison to its real value if isinstance(theComparison, (types.FunctionCall, types.Variable)): theComparison = self.resolve(theEnv, theComparison) # this is not the case if theValue != theComparison: raise InvalidCaseException() # CLIPS documentation: # the return value of a case function is the one returned from the last # action in the case arguments for action in args: # function are executed if isinstance(action, (types.FunctionCall, types.Variable)): returnValue = self.resolve(theEnv, action) else: returnValue = action return returnValue
def do(self, theEnv, theMultifield, theBegin, theEnd, *args, **kargs): """ handler of the function @see: http://www.comp.rgu.ac.uk/staff/smc/teaching/clips/vol1/vol1-12.2.html#Heading225 """ theMultifield = self.semplify(theEnv, theMultifield, list, ("1", "multifield")) theBegin = self.resolve(theEnv, self.semplify(theEnv, theBegin, types.Integer, ("2", "integer"))) theEnd = self.resolve(theEnv, self.semplify(theEnv, theEnd, types.Integer, ("3", "integer"))) try: if theBegin != theEnd: # remove a slice of the multifield # check for index values first if theBegin - 1 >= len(theMultifield) or theEnd > len(theMultifield): raise IndexError() return theMultifield[theBegin-1:theEnd] else: # remove a single item from the multifield return [theMultifield[theBegin-1]] except IndexError: # invalid field! raise InvalidArgValueError("Multifield index %s out of range 1..%d in function delete$"%( ("range %d..%d"%(theBegin, theEnd) if theBegin != theEnd else str(theBegin)), len(theMultifield) ))
def createFact(self, theEnv, arg): if isinstance(arg, types.OrderedRhsPattern): return OrderedFact( values=[ self.semplify(theEnv, v) # resolve to BaseParsedTypes if needed for v in arg.values ], moduleName=theEnv.modulesManager.currentScope.moduleName) elif isinstance(arg, types.TemplateRhsPattern): # convert it in a new Template Fact # the fact value is a dict with (slotName, slotValue) where slotValue # need to be resolved return TemplateFact( templateName=arg.templateName, values=dict([(v.slotName, self.semplify(theEnv, v.slotValue)) for v in arg.templateSlots]), moduleName=theEnv.modulesManager.currentScope.templates. getDefinition(arg.templateName).moduleName) else: raise InvalidArgValueError("Invalid fact format")
def do(self, theEnv, thePath, theName, theMode=None, *args, **kargs): """ handler of the function @see: http://www.comp.rgu.ac.uk/staff/smc/teaching/clips/vol1/vol1-12.4.html#Heading244 """ thePath = self.resolve(theEnv, thePath) if isinstance( thePath, (types.FunctionCall, types.Variable)) else thePath theName = self.resolve(theEnv, theName) if isinstance( theName, (types.FunctionCall, types.Variable)) else theName theMode = self.resolve(theEnv, theMode) if isinstance(theMode, (types.FunctionCall, types.Variable)) \ else theMode if theMode is not None else types.String("r") if not isinstance(thePath, (types.String, types.Symbol)): raise InvalidArgTypeError( "Function open expected argument #1 to be of type string or symbol" ) # normalize the string to a path. In documentation: # all special chars and \ in the path must be escaped # these means that the path is already a valid path # for python thePath = thePath.evaluate() if isinstance( thePath, types.Symbol) else thePath.evaluate()[1:-1] if not isinstance(theName, types.Symbol): raise InvalidArgTypeError( "Function open expected argument #2 to be of type symbol") # check if a resource with the same logical name is already used assert isinstance(theEnv, FunctionEnv) if theEnv.RESOURCES.has_key(theName.evaluate()): raise InvalidArgValueError( "Illegal logical name used for open function: %s" % theName.evaluate()) if not isinstance(theMode, types.String): raise InvalidArgTypeError( "Function open expected argument #3 to be of type string") # normalize the mode removing quotes if <String> theMode = theMode.evaluate()[1:-1] modeMap = {"r": "rU", "r+": "rU+", "w": "w", "a": "a"} import myclips try: theMode = modeMap[theMode] fileResource = open(thePath, theMode) theEnv.RESOURCES[theName.evaluate()] = fileResource return types.Symbol("TRUE") except KeyError: myclips.logger.error("Invalid mode for Open: %s", theMode) return types.Symbol("FALSE") except IOError, e: myclips.logger.error("IOError in Open: %s", e) return types.Symbol("FALSE")
def do(self, theEnv, theCondition, theThen, *args, **kargs): """ handler of the IfThenElse function: if-then-else conditional structure @see: http://www.comp.rgu.ac.uk/staff/smc/teaching/clips/vol1/vol1-12.6.html#Heading280 """ returnValue = types.NullValue() if not theThen.pyEqual("then"): raise InvalidArgValueError("Check appropriate syntax for if function: if expects second argument to be the `then` keyword") # try to split the then-actions from the else-actions thenActions = [] elseActions = [] workingOn = thenActions for action in args: if isinstance(action, types.Symbol) and action.pyEqual("else"): workingOn = elseActions else: workingOn.append(action) # if no else is found, not problem: elseActions is empty and all actions are in thenActions # time to evaluate the condition if isinstance(theCondition, types.FunctionCall): theCondition = self.resolve(theEnv, theCondition) # CLIPS documentation: # then-actions are executed if the result of theCondition is not <Symbol:FALSE> # (even <NullValue> is a true-like value) # else-actions are executed only if return of theCondition is exactly <Symbol:FALSE> if isinstance(theCondition, types.Symbol) and theCondition.pyEqual("FALSE"): # execute ELSE theActions = elseActions else: # execute THEN theActions = thenActions for action in theActions: # function are executed if isinstance(action, types.FunctionCall): returnValue = self.resolve(theEnv, action) else: # types work as return values returnValue = action # CLIPS documentation: # the if-then-else return value is always the value of the last execute action return returnValue
def do(self, theEnv, *args, **kargs): """ function handler implementation """ try: _thePNodes = [ theEnv.network.getPNode(ruleName) for ruleName in [ self.resolve( theEnv, self.semplify(theEnv, ruleName, types.Symbol, ( "ALL", "symbol"))) for ruleName in args ] ] except RuleNotFoundError, e: raise InvalidArgValueError(e.message)
def resolveFact(self, funcEnv, arg): """ Resolve integer, *, WME to a WME or a list of WME or raise exception if invalid arg """ if isinstance(arg, types.Integer): #convert the <Interger:INT> into a <WME:f-INT> return funcEnv.network.getWmeFromId(arg.evaluate()) elif isinstance(arg, types.Symbol) and arg.evaluate() == "*": # if (retract *) format is used, i have to retract all # fact in working memory that this scope can see return funcEnv.network.factsForScope() elif isinstance(arg, WME): return arg else: raise InvalidArgValueError("Invalid fact format")
def do(self, theEnv, theFact, *args, **kargs): """ Function handler implementation @see http://www.comp.rgu.ac.uk/staff/smc/teaching/clips/vol1/vol1-12.9.html#Heading303 """ # 1) resolve the theFact to a WME theFact = self.resolveFact( theEnv, self.semplify(theEnv, theFact, (WME, types.Integer), ('1', "fact-address or integer"))) # 2) check if it's a template fact if not isinstance(theFact.fact, TemplateFact): raise InvalidArgValueError("") # 3) get a copy of the fact inside the wme theBackup = theFact.fact # 4) retract the wme theEnv.network.retractFact(theFact) del theFact # 5) modify the fact with the new values for the slots: for theSlot in args: # each arg is a types.OrderedRhsQualcosa: index 0 is the slot name, then values assert isinstance(theSlot, types.OrderedRhsPattern) theSlotName = self.resolve( theEnv, self.semplify(theEnv, theSlot.values[0], types.Symbol)) if len(theSlot.values) > 2: # it's a multifield theSlotValues = self.semplify(theEnv, theSlot.values[1:None]) else: # it's a single field theSlotValues = self.semplify(theEnv, theSlot.values[1]) theBackup[theSlotName] = theSlotValues # 6) assert the new fact theWme, isNew = theEnv.network.assertFact(theBackup) return theWme if isNew else types.Symbol("FALSE")
def do(self, funcEnv, resourceId, theFormat, *args, **kargs): """ Function handler implementation """ resourceId = self.semplify(funcEnv, resourceId, (types.Symbol), ("1", "symbol")) theFormat = self.resolve( funcEnv, self.semplify(funcEnv, theFormat, (types.String), ("2", "string"))) try: if not resourceId.pyEqual('nil'): resource = funcEnv.RESOURCES[resourceId.evaluate()] else: resource = None except KeyError: raise InvalidArgValueError( "Resource with logical name %s cannot be found" % str(resourceId)) else: returnValueRaw = [] for fragment in args: # revolve variables and function calls returnValueRaw.append( self.resolve(funcEnv, self.semplify(funcEnv, fragment))) # execute replacement of special chars: # theFormat = theFormat.replace("%n", "\n")\ .replace("%r", "\r")\ #.replace("%%", "%") # theFormat is a string returnValueRaw = theFormat % tuple(returnValueRaw) if resource is not None: resource.write(returnValueRaw) return types.String(returnValueRaw)
def do(self, theEnv, theMultifield, theBegin, theEnd, *args, **kargs): """ handler of the function @see: http://www.comp.rgu.ac.uk/staff/smc/teaching/clips/vol1/vol1-12.2.html#Heading226 """ theMultifield = self.semplify(theEnv, theMultifield, list, ("1", "multifield")) theBegin = self.resolve( theEnv, self.semplify(theEnv, theBegin, types.Integer, ("2", "integer"))) theEnd = self.resolve( theEnv, self.semplify(theEnv, theEnd, types.Integer, ("3", "integer"))) try: if theBegin - 1 >= len(theMultifield) or theEnd > len( theMultifield): raise IndexError() theBegin = theMultifield[0:theBegin - 1] theEnd = theMultifield[theEnd:None] except IndexError: # invalid field! raise InvalidArgValueError( "Multifield index %s out of range 1..%d in function delete$" % (("range %d..%d" % (theBegin, theEnd) if theBegin != theEnd else str(theBegin)), len(theMultifield))) else: theInner = [] for theArg in args: theArg = self.semplify( theEnv, theArg, (list), ("1", "number, lexeme, fact-address or multifield")) if isinstance(theArg, list): theInner += theArg else: theInner.append(theArg) return theBegin + theInner + theEnd
def do(self, theEnv, theStrategy, *args, **kargs): """ handler of the function @see: http://www.comp.rgu.ac.uk/staff/smc/teaching/clips/vol1/vol1-13.7.html#Heading452 """ theStrategy = self.resolve( theEnv, self.semplify(theEnv, theStrategy, types.Symbol, ("1", "symbol"))) if strategies.factory.isValid(theStrategy): theOld = theEnv.network.agenda.changeStrategy( strategies.factory.newInstance(theStrategy)) return types.Symbol(theOld) else: raise InvalidArgValueError( "Function set-strategy expected argument #1 to be of type symbol " + "with value depth, breadth, lex, mea, complexity, simplicity, or random" )
def createFact(self, theEnv, arg): if isinstance(arg, types.OrderedRhsPattern): # convert it in a new Ordered Fact return OrderedFact(values=[self.semplify(theEnv, v, types.BaseParsedType) for v in arg.values], moduleName=theEnv.modulesManager.currentScope.moduleName) elif isinstance(arg, types.TemplateRhsPattern): # convert it in a new Template Fact # the fact value is a dict with (slotName, slotValue) where slotValue: # is a baseparsedtype if singlefield return TemplateFact(templateName=arg.templateName, values=dict([(v.slotName, self.semplify(theEnv, v.slotValue, types.BaseParsedType)) if isinstance(v, types.SingleFieldRhsSlot) # or a list if multifield (solved, this means is a list of base-parsed-type) else (v.slotName, self.semplify(theEnv, v.slotValue, list)) if isinstance(v, types.MultiFieldRhsSlot) else (v.slotName, v.slotValue) #don't know what to do FIXME for v in arg.templateSlots]), moduleName=theEnv.modulesManager.currentScope.templates.getDefinition(arg.templateName).moduleName) else: raise InvalidArgValueError("Unknown fact format in RHS pattern")
def do(self, theEnv, theExpression, *args, **kargs): """ handler of the Switch function: regroup a list of case functions and execute the right one for the theCondition value. Execute a default case if not case cases match if a default case exists, otherwise return None @see: http://www.comp.rgu.ac.uk/staff/smc/teaching/clips/vol1/vol1-12.6.html#Heading287 """ # theExpression have to be evaluated and compared to each case # if expression value match the case, it's executed # and its return value returned # if no expression match and a default is provided, default is executed # phase 1: # filter/check case function in args # and isolate the last default function (if any) theCases = [] theDefault = None if len(args) > 0: for index in range(0, len(args)): caseFunc = args[index] if isinstance(caseFunc, types.FunctionCall) and caseFunc.funcName == "case": theCases.append(caseFunc) elif isinstance(caseFunc, types.FunctionCall) and caseFunc.funcName == "default": if index == len(args) - 1: theDefault = caseFunc else: raise InvalidArgValueError("Default clause for a switch function have to be the last one") else: raise InvalidArgTypeError("Invalid argument for a switch function: %s"%str(caseFunc)) # phase 2: # calculate the value for the theExpression if isinstance(theExpression, (types.FunctionCall, types.Variable) ): theResult = self.resolve(theEnv, theExpression) else: theResult = theExpression returnValue = None # phase 3: # evaluate the "case function" arguments for caseFunc in theCases: # execute the case Function # if it return something not None, it's ok # if return is None = it's not the right case # (maybe it's better and exception) try: returnValue = caseFunc.funcDefinition.linkedType.__class__.execute(caseFunc.funcDefinition.linkedType, theEnv, theResult, *(caseFunc.funcArgs)) break # break the for at the first positive case clause executed except InvalidCaseException: pass # phase 4: # if returnValue == None, then no case match # try to execute the default if any # otherwise return None if returnValue is None and theDefault is not None: return theDefault.funcDefinition.linkedType.__class__.execute(theDefault.funcDefinition.linkedType, theEnv, theResult, *(theDefault.funcArgs)) else: return returnValue if returnValue is not None else types.Symbol("FALSE")
def do(self, theEnv, theName=None, *args, **kargs): """ handler of the function @see: http://www.comp.rgu.ac.uk/staff/smc/teaching/clips/vol1/vol1-12.4.html#Heading247 """ theName = self.resolve(theEnv, theName) if isinstance( theName, (types.FunctionCall, types.Variable)) else theName if theName is None or (isinstance(theName, types.Symbol) and theName.pyEqual("t")): theResource = theEnv.RESOURCES["stdin"] needSeek = False elif not isinstance(theName, types.Symbol): raise InvalidArgTypeError( "Function read expected argument #1 to be of type symbol") else: theResource = theEnv.RESOURCES[theName.evaluate()] needSeek = True # get a ConstantParser: it will read 1 single Lexeme|Number from a string constantParser = theEnv.network.getParser().getSParser( 'ConstantParser') quoteGuard = None try: theString = theResource.readline() # python file.readline() doc: # An empty string is returned only when EOF is encountered immediately # @see http://docs.python.org/release/2.4.4/lib/bltin-file-objects.html if theString == "": return types.Symbol("EOF") quoteGuard = theString[0] == '"' if quoteGuard: # need to read a <String> while True: theToken = constantParser.scanString(theString).next() theResult = theToken[0][0] # check if the Result is a String if isinstance(theResult, types.String): # <String> found, continue break else: # i've not found a string # this means i've not read enoght to find # the end quote for the string and the parser # read a symbol instead theOtherString = theResource.readline() if theOtherString == "": raise InvalidArgValueError( "Encountered End-Of-File while scanning a string: %s" % theString) theString += theOtherString else: # anything is ok # read only the first one theToken = constantParser.scanString(theString).next() theResult = theToken[0][0] if needSeek: # need to seek the resource read pointer # to the first not read char endPosition = theToken[2] theResource.seek(endPosition - len(theString) + 1, 1) # 1 == relative to the position return theResult except (StopIteration, EOFError): return types.Symbol("EOF") except IOError: return types.Symbol("*** READ ERROR ***")
def do(self, theEnv, theRange, *args, **kargs): """ handler of the Loop-For-Count function: classic for-i-from-1-to-n conditional structure @see: http://www.comp.rgu.ac.uk/staff/smc/teaching/clips/vol1/vol1-12.6.html#Heading282 """ returnValue = types.Symbol("FALSE") # theRange is the range specifier # it could be a list([<Variable>, <Integer:MIN>, <Integer:MAX>]) or a <Integer:MAX> if isinstance(theRange, list): # if it's list, then: # [0] is a variable # [1] is the min # [2] is the max if len(theRange) != 3 \ or not isinstance(theRange[0], types.SingleFieldVariable)\ or not isinstance(theRange[1], types.Integer)\ or not isinstance(theRange[2], types.Integer)\ : raise InvalidArgValueError( "Range specifier format for loop-for-count function must be a <Integer> or a [<Variable>, <Integer>, <Integer>]" ) theVarBind = theRange[0].evaluate() theMin = theRange[1].evaluate() theMax = theRange[2].evaluate() + 1 else: # function definition restriction ensure theRange to be a list or a <Integer> theMax = theRange.evaluate() + 1 theMin = 1 theVarBind = None theActions = [] # strip the optional DO from the args[0] if it's there if len(args) > 0: if isinstance(args[0], types.Symbol) and args[0].pyEqual("do"): theActions = list(args[1:]) else: theActions = list(args) # CLIPS documentation: # while will loop until theResult is a <Symbol:FALSE> or Return/Break functions are used # if return is used, the function execution halt # if break is used, loop break and while return a value # so, return is not caught here, but from the function caller # break are caught here instead try: for theVarValue in range(theMin, theMax): if theVarBind is not None: theEnv.variables[theVarBind] = types.Integer(theVarValue) for action in theActions: # function are executed if isinstance(action, types.FunctionCall): self.resolve(theEnv, action) except BreakException: # break caught, stop loooooooooop! pass # CLIPS documentation: # the if-then-else return value is always the value of the last execute action return returnValue