Skip to content

pinnacol/clipr

Repository files navigation

= Clipr

{CLIPS}[http://clipsrules.sourceforge.net/] bindings for Ruby.

== Description

The Clipr gem provides {FFI}[http://github.com/ffi/ffi] bindings to the CLIPS
API, and a set of classes which act as wrappers for the various CLIPS
constructs. Clipr allows callbacks to Ruby from CLIPS so that application
logic and rules logic may be kept separate.

==== Notes

{CLIPS}[http://clipsrules.sourceforge.net/] (C Language Integrated Production
System) is an open-source expert system widely used throughout the government,
industry, and academia. CLIPS is powerful and fast, but requires some study
because it uses a programming style that many people will find unusual.

Users are encouraged to consult the {extensive and accessible CLIPS
documentation}[http://clipsrules.sourceforge.net/OnlineDocs.html] for
clarification of what the Clipr constructs are intended to do, as well as a
great deal more information regarding the CLIPS API.

== Usage

=== Clipr::Api

The Api is organized into modules as described in the Advanced Programming
Guide (apg.pdf). All of the methods bind the contextual, environment-aware
forms of the API methods when possible.

  require 'clipr/api'
  include Clipr::Api
  
  # create an environment
  env_ptr = Environment::CreateEnvironment()
  
  # make the function call "(> 1 2)"
  obj = DataObject.new
  Environment::EnvFunctionCall(env_ptr, ">", "1 2", obj)
  
  # get the result out of the DataObject
  node = Struct::SymbolHashNode.new(obj[:value])
  node[:contents]          # => "FALSE"
  
  # cleanup the environment
  Environment::DestroyEnvironment(env_ptr)
  
Direct usage of the Api requires knowledge of the CLIPS API and an attention
to detail. The rest of Clipr provides a wrapper to make the Api more
accessible.

  require 'clipr'
  Clipr::Env.open do |env|
    env.call(">", "1 2").value   # => false
  end

=== Clipr

Clipr is built around the Env class which represents a CLIPS environment.
Basic usage is similar to the console:

  env = Clipr::Env.new
  facts = env.facts
  
  env.assert "(animal-is duck)"
  env.build  "(defrule duck (animal-is duck) => (assert (sound-is quack)))"
  
  facts.list  # => ["(initial-fact)", "(animal-is duck)"]
  
  env.run
  facts.list  # => ["(initial-fact)", "(animal-is duck)", "(sound-is quack)"]
  
  env.clear
  facts.list  # => ["(initial-fact)"]

More complex constructs can be defined in Ruby:

  class Animal < Clipr::Fact
    deftemplate "animal"
    slot :sound
  end
  Animal.construct_str  # => "(deftemplate animal (slot sound))"

  class Quack < Clipr::Rule
    defrule "quack"
    lhs.match "animal", :sound => :quack
    rhs.assert "(sound-was quack)"
  end
  Quack.construct_str   # => "(defrule quack (animal (sound quack)) => (assert (sound-was quack)))"
  
  env.clear
  env.build(Animal)
  env.build(Quack)
  
  facts.assert(:animal, {:sound => :quack})
  env.run
  facts.list            # => ["(initial-fact)", "(animal (sound quack))", "(sound-was quack)"]

Clipr provides a callback system that lets CLIPS use blocks written in Ruby.
This is a way of writing an equivalent quack rule (albeit with a very
different CLIPS construct):

  class QuackCall < Clipr::Rule
    defrule "quack_call"
    
    lhs.match "animal", :sound do |sound|
      sound == :quack
    end
    
    rhs.callback do |env|
      env.assert "(sound-was quack)"
    end
  end
  QuackCall.construct_str  # => "(defrule quack_call (animal (sound ?sound)) (test (ruby-call ... ?sound)) => (ruby-call ...))"

  env.clear
  env.build(Animal)
  env.build(QuackCall)
  
  facts.assert(:animal, {:sound => :quack})
  env.run
  facts.list               # => ["(initial-fact)", "(animal (sound quack))", "(sound-was quack)"]

The Fact and Rule DSLs are flexible enough to define almost all CLIPS
constructs.

== Info 

Developer:: {Simon Chiang}[http://bahuvrihi.wordpress.com]
License:: {MIT-Style}[link:files/License_txt.html]

Releases

No releases published

Packages

No packages published