<?xml version="1.0" encoding="UTF-8"?>

<section id="regexp"><title>&clisp; &regexp-pac; module</title>

<para>The &regexp-pac; module implements the
 <ulink url="regexp.html">POSIX regular expressions</ulink>
via an &ffi-pac; call to the standard &c-lang; system facilities.
The syntax of these regular expressions is described in many places,
such as your local <function>regexp(5)</function> manual and &emacs;
info pages.</para>

<para>The &regexp-pac; package exports the following 9 symbols:</para>

<simplelist type="horiz" columns="3">
 <member>&re-match;</member>
 <member>&re-match-start;</member>
 <member>&re-match-end;</member>
 <member>&re-match-string;</member>
 <member>&re-regexp-quote;</member>
 <member>&re-regexp-compile;</member>
 <member>&re-regexp-exec;</member>
 <member>&re-regexp-split;</member>
 <member>&re-with-loop-split;</member>
</simplelist>

<variablelist><title>API</title>
<varlistentry id="re-match"><term><literal
    role="sexp">(&re-match; &pattern-r; &string-r; &key-amp;
     (&start-k; 0) &end-k; (&case-sensitive-k; t))</literal></term>
 <listitem><para>This macro returns as first value a &match-t; structure
  containing the indices of the start and end of the first match for the
  regular expression &pattern-r; in &string-r;;
  or &nil; if there is no match.
  Additionally, a &match-t; structure is returned for every matched
  <literal>"\(...\)"</literal> group in &pattern-r;, in the
  order that the open parentheses appear in &pattern-r;.
  If &start-r; is non-&nil;, the search starts at that index in &string-r;.
  If &end-r; is non-&nil;, only <literal role="sexp">(&subseq;
  &string-r; &start-r; &end-r;)</literal> is considered.
  <example><title>&re-match;</title>
   <programlisting>
    (&re-match; "quick" "The quick brown fox jumped quickly.")
     <literal role="sexp">#S(&match-t; :RM_SO 4 :RM_EO 9)</literal>
    (&re-match; "quick" "The quick brown fox jumped quickly." :start 8)
     <literal role="sexp">#S(&match-t; :RM_SO 27 :RM_EO 32)</literal>
    (&re-match; "quick" "The quick brown fox jumped quickly." :start 8 :end 30)
     &nil;
    (&re-match; "\\([a-z]*\\)[0-9]*\\(bar\\)" "foo12bar")
     <literal role="sexp">#S(&match-t; :RM_SO 0 :RM_EO 8) ;</literal>
     <literal role="sexp">#S(&match-t; :RM_SO 0 :RM_EO 3) ;</literal>
     <literal role="sexp">#S(&match-t; :RM_SO 5 :RM_EO 8)</literal>
   </programlisting></example>
 </para></listitem></varlistentry>

<varlistentry id="re-match-start"><term><literal
    role="sexp">(&re-match-start; &match-r;)</literal></term>
 <listitem><simpara>Extracts the start index of &match-r;.
 </simpara></listitem></varlistentry>

<varlistentry id="re-match-end"><term><literal
    role="sexp">(&re-match-end; &match-r;)</literal></term>
 <listitem><simpara>Extracts the end index of &match-r;.
 </simpara></listitem></varlistentry>

<varlistentry id="re-match-string"><term><literal
    role="sexp">(&re-match-string; &string-r; &match-r;)</literal></term>
 <listitem><simpara>Extracts the substring of &string-r; corresponding
  to the given pair of start and end indices of &match-r;.
  The result is shared with &string-r;.
  If you want a freshly consed &string-class;, use &copy-seq; or
  <literal role="sexp">(&coerce; (&re-match-string; ...)
  '&simple-string-type;)</literal>.
 </simpara></listitem></varlistentry>

<varlistentry id="re-regexp-quote"><term><literal
    role="sexp">(&re-regexp-quote; &string-r;)</literal></term>
 <listitem><para>This function returns a regular expression string
  that matches exactly &string-r; and nothing else.
  This allows you to request an exact string match when calling a
  function that wants a regular expression.
  <example><title>&re-regexp-quote;</title>
   <programlisting>
    (regexp-quote "^The cat$")
     <returnvalue>"\\^The cat\\$"</returnvalue>
   </programlisting></example>
  One use of &re-regexp-quote; is to combine an exact string match with
  context described as a regular expression.</para></listitem></varlistentry>

<varlistentry id="re-regexp-compile"><term><literal
    role="sexp">(&re-regexp-compile; &string-r; &optional-amp;
     (&case-sensitive-r; t))</literal></term>
 <listitem><simpara>Compile the regular expression &string-r; into an
  object suitable for &re-regexp-exec;.</simpara></listitem></varlistentry>

<varlistentry id="re-regexp-exec"><term><literal
    role="sexp">(&re-regexp-exec; &pattern-r; &string-r; &key-amp;
     (&start-k; 0) &end-k;)</literal></term>
 <listitem><simpara>Execute the &pattern-r;, which must be a compiled
  regular expression returned by &re-regexp-compile;, against the
  appropriate portion of the &string-r;.</simpara>
  <simpara>Negative &end-r; means <literal role="sexp">(&plus; (&length;
  &string-r;) &end-r;)</literal></simpara></listitem></varlistentry>

<varlistentry id="re-regexp-split"><term><literal
    role="sexp">(&re-regexp-split;  &pattern-r; &string-r; &key-amp;
     (&start-k; 0) &end-k; (&case-sensitive-k; t))</literal></term>
 <listitem><simpara>Return a list of substrings of &string-r; (all
  sharing the structure with &string-r;) separated by &pattern-r; (a
  regular expression string or a return value of &re-regexp-compile;)
 </simpara></listitem></varlistentry>

<varlistentry id="re-with-loop-split"><term><literal
    role="sexp">(&re-with-loop-split; (&var-r; &stream-r; &pattern-r;
     &key-amp; (&start-k; 0) &end-k; (&case-sensitive-k; t))
     &body-amp; &body-r;)</literal></term>
 <listitem><simpara>Read lines from &stream-r;, split them with
  &re-regexp-split; on &pattern-r;, and bind the resulting list to
  &var-r;.</simpara></listitem></varlistentry>

</variablelist>

<para>The following code computes the number of people who use a
 particular shell:
<programlisting>
(&defpackage; "REGEXP-TEST" (:use "LISP" "REGEXP"))
(&in-package; "REGEXP-TEST")
(let ((h (make-hash-table :test #'equal :size 10)) (n 0))
  (with-open-file (f "/etc/passwd")
    (with-loop-split (s f ":")
      (let ((sh (seventh s)))
        (if (gethash sh h)
            (incf (gethash sh h))
            (setf (gethash sh h) 1)))))
  (with-hash-table-iterator (i h)
    (loop (multiple-value-bind (r k v) (i)
            (unless r (return))
            (format t "[~d] ~s~30t== ~5:d~%" (incf n) k v)))))
</programlisting></para>

<para>The same is done by the following &perl;:
<programlisting>
#!/usr/local/bin/perl -w

use diagnostics;
use strict;

my $IN = $ARGV[0];
open(INF,"&lt; $IN") || die "$0: cannot read file [$IN]: $!\n;";
my %hash;
while (&lt;INF&gt;) {
  chop;
  my @all = split($ARGV[1]);
  my $shell = ($#all &gt;= 6 ? $all[6] : "");
  if ($hash{$shell}) { $hash{$shell} ++; }
  else { $hash{$shell} = 1; }
}
my $ii = 0;
for my $kk (keys(%hash)) {
  print "[",++$ii,"] \"",$kk,"\"  --  ",$hash{$kk},"\n";
}
</programlisting></para>

</section>
