NAME

Inline::CLIPS - Perl interface to run CLIPS programs

SYNOPSIS

use Inline::CLIPS;

my $clips = Inline::CLIPS->new;
my $result = $clips->run_program(q{
  (deftemplate animal (slot name) (slot class))
  (deffacts initial (animal (name "penguin") (class bird)))
  (defrule print-animal
    (animal (name ?n) (class ?c))
    =>
    (printout t ?n " is a " ?c crlf))
}, '(run)');

print $result->{stdout};
warn $result->{stderr} if $result->{stderr};
exit $result->{status};

DESCRIPTION

Inline::CLIPS provides a small Perl API for executing CLIPS programs by wrapping a CLIPS executable. It does not embed a CLIPS engine; instead it writes a temporary .clp file and invokes the CLIPS interpreter with that file. The captured output, errors, and exit status are returned as a plain hash reference.

The module discovers the CLIPS executable in the following order:

  1. The executable argument passed to new.

  2. The INLINE_CLIPS_EXECUTABLE environment variable.

  3. A command named clips found in $PATH.

  4. The binary directory reported by Alien::CLIPS, if installed.

INSTALLATION

Installing the Perl module

The distribution follows the standard ExtUtils::MakeMaker build process:

perl Makefile.PL
make
make test
make install

Making CLIPS available to Perl

Inline::CLIPS needs a working CLIPS executable. You can make one available in any of these ways.

Option 1: Use a system CLIPS on $PATH

Install CLIPS from your operating system package manager, or build CLIPS from source and copy the clips binary into a directory on your $PATH. Once clips is executable and discoverable, Inline::CLIPS will find it automatically.

# Verify CLIPS is visible
which clips
clips -v

Option 2: Set INLINE_CLIPS_EXECUTABLE

If CLIPS is installed in a non-standard location, set the environment variable to the absolute path of the executable:

export INLINE_CLIPS_EXECUTABLE=/opt/clips/6.4.2/clips
perl my_script.pl

You can also set this in your Perl code before constructing the object:

$ENV{INLINE_CLIPS_EXECUTABLE} = '/opt/clips/6.4.2/clips';
my $clips = Inline::CLIPS->new;

Option 3: Use Alien::CLIPS

If this distribution is installed with its companion module Alien::CLIPS, the module will fall back to a CLIPS binary discovered through the Alien infrastructure. Alien::CLIPS can locate a system CLIPS installation or, when no system copy is found, build CLIPS from the FuzzyCLIPS source repository:

L<https://github.com/jtrujil43/FuzzyCLIPS>

Verifying the installation

use Inline::CLIPS;

my $clips = Inline::CLIPS->new;
die "CLIPS executable not found\n" unless $clips->executable;

my $result = $clips->run_program(q{
  (printout t "CLIPS is available" crlf)
}, '(exit)');

print $result->{stdout};

METHODS

new(%args)

Constructor. Accepts the following optional arguments:

executable

Path to the CLIPS executable. If omitted, the executable is discovered using the rules in "INSTALLATION".

library

Path to the CLIPS shared library. This is exposed for callers that need to know where CLIPS libraries live; it is not used directly by the executable wrapper.

my $clips = Inline::CLIPS->new(
  executable => '/usr/local/bin/clips',
);

executable

Returns the resolved path to the CLIPS executable, or the empty string if none was found.

my $path = $clips->executable;

library

Returns the resolved path to the CLIPS shared library, or the empty string if none was found. The library is resolved from the library constructor argument, the INLINE_CLIPS_LIB environment variable, or Alien::CLIPS.

my $lib = $clips->library;

run_program($program, @commands)

Executes an inline CLIPS program. The $program string is written to a temporary .clp file followed by (reset), each command in @commands, and finally (exit). Returns a hash reference:

{
  status => 0,          # CLIPS exit status
  stdout => "...",      # everything printed to STDOUT
  stderr => "...",      # everything printed to STDERR
}

my $result = $clips->run_program(q{
  (deffacts numbers (number 1) (number 2) (number 3))
  (defrule sum-numbers
    (number ?n)
    =>
    (printout t "number: " ?n crlf))
}, '(reset)', '(run)');

run_file($file)

Executes an existing CLIPS source file. The file must exist and be readable. The CLIPS interpreter is invoked as clips -f2 $file.

my $result = $clips->run_file('src/main.clp');

This is useful for projects that already keep their rules, templates, and facts in separate .clp files. See "Running a real process-flow-check project" for a complete example.

ENVIRONMENT

INLINE_CLIPS_EXECUTABLE

Full path to the CLIPS executable. Overrides the $PATH search and the Alien::CLIPS fallback.

INLINE_CLIPS_LIB

Full path to the CLIPS shared library. Used by library().

INLINE_CLIPS_BIN

Directory containing the CLIPS binary. Consulted by Alien::CLIPS when no system executable is found.

ALIEN_CLIPS_LIB, ALIEN_CLIPS_BIN

Fallback variables read by Alien::CLIPS.

EXAMPLES

Basic inline program

use Inline::CLIPS;

my $clips = Inline::CLIPS->new;
my $result = $clips->run_program(q{
  (deftemplate animal (slot name) (slot class))
  (deffacts seed
    (animal (name "penguin") (class bird))
    (animal (name "salmon") (class fish)))
  (defrule describe-animal
    (animal (name ?n) (class ?c))
    =>
    (printout t ?n " is a " ?c crlf))
}, '(run)');

print $result->{stdout};

Running a real process-flow-check project

Suppose you have the process-flow-check CLIPS project at /home/jovan/devel/Clips_code/process-flow-check. It defines templates for flow-step, tool-capability, and violation, loads rules from src/core/*.clp, and provides a check-flow deffunction in src/io/run-validation.clp. You can drive that project from Perl with run_file or with a self-contained inline program.

Using run_file with the project loader

use Inline::CLIPS;

my $clips = Inline::CLIPS->new;
my $project_dir = '/home/jovan/devel/Clips_code/process-flow-check';

# CLIPS file paths are relative to its current directory, so run from there.
chdir $project_dir or die "Cannot chdir: $!";

my $result = $clips->run_file('src/main.clp');
print $result->{stdout};
warn $result->{stderr} if $result->{stderr};

Using an inline copy of the same rules

The project checks semiconductor-style process flows. The CLIPS snippet below is a self-contained, simplified version of the real rules that validates a short photolithography and poly-etch flow.

use Inline::CLIPS;

my $clips = Inline::CLIPS->new;
my $result = $clips->run_program(
  q{
    (deftemplate flow-step
      (slot step-id (type INTEGER))
      (slot step-name)
      (slot layer)
      (slot tool-id)
      (slot recipe-id)
      (slot queue-time-min (type INTEGER))
      (slot max-wait-min (type INTEGER))
      (slot rework-allowed (allowed-values TRUE FALSE))
      (slot prev-step-id (type INTEGER) (default -1)))

    (deftemplate tool-capability
      (slot tool-id)
      (slot allowed-layers)
      (multislot allowed-recipes))

    (deftemplate violation
      (slot code)
      (slot message)
      (slot step-id)
      (slot severity (allowed-values info warning error)))

    (deffacts reference-capabilities
      (tool-capability (tool-id CT-01)  (allowed-layers PHOTO)
                       (allowed-recipes CT-PR-193))
      (tool-capability (tool-id EX-05)  (allowed-layers PHOTO)
                       (allowed-recipes EX-193-NA13))
      (tool-capability (tool-id DEV-02) (allowed-layers PHOTO)
                       (allowed-recipes DEV-TMAH))
      (tool-capability (tool-id INS-01) (allowed-layers PHOTO)
                       (allowed-recipes INS-BF))
      (tool-capability (tool-id ETCH-07) (allowed-layers POLY)
                       (allowed-recipes ETCH-Cl2-BCl3)))

    (defrule check-tool-layer
      (flow-step (step-id ?id) (layer ?layer) (tool-id ?tool))
      (tool-capability (tool-id ?tool) (allowed-layers ?allowed))
      (test (neq ?layer ?allowed))
      =>
      (assert (violation (code CONS-001)
                         (message "Tool not qualified for layer")
                         (step-id ?id)
                         (severity error))))

    (defrule check-queue-time
      (flow-step (step-id ?id) (queue-time-min ?q) (max-wait-min ?m))
      (test (> ?q ?m))
      =>
      (assert (violation (code TIME-001)
                         (message "Queue time exceeds maximum wait")
                         (step-id ?id)
                         (severity error))))

    (deffunction print-report ()
      (bind ?errs (find-all-facts ((?v violation)) TRUE))
      (if (eq (length$ ?errs) 0)
        then
          (printout t "No violations found." crlf)
        else
          (printout t "Violations:" crlf)
          (foreach ?vf ?errs
            (printout t "[" (fact-slot-value ?vf code) "] @ step "
                      (fact-slot-value ?vf step-id) " — "
                      (fact-slot-value ?vf message) crlf))))

    (deffacts current-flow
      (flow-step (step-id 10) (step-name Coat)    (layer PHOTO)
                 (tool-id CT-01)  (recipe-id CT-PR-193)
                 (queue-time-min 10) (max-wait-min 60)  (rework-allowed FALSE)
                 (prev-step-id -1))
      (flow-step (step-id 20) (step-name Expose)  (layer PHOTO)
                 (tool-id EX-05)  (recipe-id EX-193-NA13)
                 (queue-time-min 12) (max-wait-min 60)  (rework-allowed FALSE)
                 (prev-step-id 10))
      (flow-step (step-id 30) (step-name Develop) (layer PHOTO)
                 (tool-id DEV-02) (recipe-id DEV-TMAH)
                 (queue-time-min 8)  (max-wait-min 45)  (rework-allowed FALSE)
                 (prev-step-id 20))
      (flow-step (step-id 40) (step-name Inspect) (layer PHOTO)
                 (tool-id INS-01) (recipe-id INS-BF)
                 (queue-time-min 5)  (max-wait-min 30)  (rework-allowed TRUE)
                 (prev-step-id 30))
      (flow-step (step-id 50) (step-name Etch)    (layer POLY)
                 (tool-id ETCH-07) (recipe-id ETCH-Cl2-BCl3)
                 (queue-time-min 15) (max-wait-min 120) (rework-allowed FALSE)
                 (prev-step-id 40)))
  },
  '(reset)',
  '(run)',
  '(print-report)',
);

print $result->{stdout};
warn $result->{stderr} if $result->{stderr};
exit $result->{status};

DIAGNOSTICS

CLIPS executable is not available; set INLINE_CLIPS_EXECUTABLE or install CLIPS/Alien::CLIPS

Thrown by run_file and run_program when no CLIPS executable could be discovered. Follow the steps in "INSTALLATION".

program text is required

run_program was called without a program string.

file path is required / CLIPS file not found: ...

run_file was called with a missing, empty, or non-existent file path.

SEE ALSO

Alien::CLIPS

Companion Alien module that can locate or build CLIPS.

CLIPS home page

https://www.clipsrules.net/

FuzzyCLIPS source used by the Alien fallback

https://github.com/jtrujil43/FuzzyCLIPS

AUTHOR

Inline-CLIPS contributors.

LICENSE

This library is free software; you may redistribute it and/or modify it under the same terms as Perl itself.