logs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Logs-devel] rule macro attempt #1


From: Vijay Lakshminarayanan
Subject: Re: [Logs-devel] rule macro attempt #1
Date: Mon, 5 Jun 2006 01:59:16 -0500

Hi Jim

I have made one or two more changes.  I was shooting myself in the
foot trying to use cl-yacc.  I'm sure it's a great tool but I couldn't
get the hang of it to parse LoGS rules.  anyway, you were right about
it being a large hammer.  I think we can do without it.

I've extended set-env to accept a LOOP like AND syntax:

(rule with foo = "one" and bar = "two" do (lambda (x) (list foo bar)))

I have a few more questions.  I'll ask them in the morning.  I've
added a keyword FILTER and have a question there.

Thanks Jim!

;;; Paul Graham, On Lisp, p191
(defmacro aif (test-form then-form &optional else-form)
 `(let ((it ,test-form))
   (if it ,then-form ,else-form)))

;;; an 'accessor' for the handle-fns
(defmacro handle-fn (keyword)
 `(get ,keyword 'handle-fn))

(defmacro alias (keyword)
 `(get ,keyword 'alias))

;;; Influenced by Peter Norvig's LOOP implementation
(defstruct rool
 (name '())
 (match '())
 (timeout '())
 (relative-timeout '())
 (delete-rule '())
 (continuep '())
 (actions '())
 (environment '())
 (delete '()))

(defmacro rule (&rest exprs)
 (let ((r (make-rool)))
   (parse-rule r exprs)
   (fill-rule-template r)))

(defun fill-rule-template (rool)
 `(make-instance
   'logs::rule
   :environment ',(rool-environment rool)
   ,@(loop as (key fn) in '((:match rool-match)
                            (:name rool-name)
                            (:timeout rool-timeout)
                            (:continuep rool-continuep)
                            (:relative-timeout rool-relative-timeout)
                            (:actions rool-actions))
           as res = (funcall fn rool)
           if res append `(,key ,res))))

(defun parse-rule (rool exprs)
 (unless (null exprs)
   (parse-rule rool (parse-keyword rool (car exprs) (cdr exprs)))))

(defun parse-keyword (rool keyword exprs)
 (let* ((keyword (or (alias keyword) keyword)))
   (aif (handle-fn keyword)
        (funcall it rool exprs)
        (error "Unknown keyword ~S" keyword))))

(defun handle-name (rool exprs)
 (destructuring-bind (name . cdr) exprs
   ;; rule names don't have to be symbols
   ;; symbols are (obviously) a pretty good idea, but
   ;; strings, etc. should work as well
   (if (null (rool-name rool))
       (setf (rool-name rool) name)
       (error "Invalid name ~S" name))
   cdr))

(defun handle-match (rool exprs)
 (let ((matches (rool-match rool)))
   ;; For now, we ignore.  Later we add
   ;; facilities to add other messages
   (declare (ignore matches))
   (destructuring-bind (car . cdr) exprs
     (case car
       (regexp (let ((msg (gensym "MESSAGE"))
                     (matches (gensym))
                     (sub-matches (gensym))
                     (regex (pop cdr)))
                 (setf (rool-match rool)
                       `(lambda (,msg)
                         (multiple-value-bind (,matches ,sub-matches)
                             (cl-ppcre:scan-to-strings
                              ,regex
                              (logs::message ,msg))
                           (when ,matches
                             (values t
                                     '((sub-matches ,sub-matches)))))))
                 cdr))
       (message-length (let ((msg (gensym "MESSAGE"))
                             (relop (pop cdr))
                             (num (pop cdr)))
                         (setf (rool-match rool)
                               `(lambda (,msg)
                                 (,relop (length (logs::message
                                                  ,msg)) ,num)))
                         cdr))
       (t (setf (rool-match rool) car) cdr)))))

(defun handle-filter (rool exprs)
 (destructuring-bind (regexp regex . cdr) exprs
   (let ((exprs (handle-match rool exprs)))
     (setf (rool-continuep rool) t
           ;; There has to be some way to set the
           ;; whole to just NIL.
           ;; I see the following possibilities:
           ;; we parse EXPRS and remove all
           ;; action clauses.
           ;; We change the design of LoGS such
           ;; that actions is actually (by
           ;; default) (block NIL (list action-1
           ;; action-2)) and then we just do a
           ;; (setf (rool-actions rool) '(return))
           (rool-actions rool) nil))))

(defun handle-timeout (rool exprs)
 (destructuring-bind (preposition time . cdr) exprs
   (case preposition
     (in (setf (rool-relative-timeout rool) time))
     (at (setf (rool-timeout rool) time)))
   cdr))

(defun handle-setenv (rool exprs)
 (destructuring-bind (variable = value &rest rest) exprs
   (declare (ignore =))
   (pushnew (list variable value)
            (rool-environment rool)
            :test
            (lambda (x y) (equal (car x) (car y))))
   (if (eq (car rest) 'and)
       ;; More than one variable has been
       ;; defined.  Save that too.
       (handle-setenv rool (cdr rest))
       rest)))

;; add the given function to the actions list
(defun handle-do (rool exprs)
 (destructuring-bind (todo &rest rest) exprs
   (format t "rool: ~A~%todo: ~A~%rest: ~A~%~
              rool-actions:~S~%~50~~%"
           rool todo rest (rool-actions rool))
   (setf (rool-actions rool) (append (rool-actions rool) (list (eval todo))))
   rest))

;; set the continue slot to t
(defun handle-continue (rool exprs)
 (setf (rool-continuep rool) t)
 exprs)

;; set the delete slot to t
(defun handle-delete (rool exprs)
 (setf (rool-delete rool) t)
 exprs)

;;; Set aliases for keywords so we have synonyms
;;; and define the handler function
(defmacro defhandler (keywords alias handle-fn)
 `(setf ,@(if (consp keywords)
              (loop as keyword in keywords
                    appending `((alias ',keyword) ',alias))
              `((alias ',keywords) ',alias))
        (handle-fn ',alias) ,handle-fn))

(defhandler (match matching) :match #'handle-match)
(defhandler (name named) :name #'handle-name)
(defhandler (do doing) :do #'handle-do)
(defhandler continue :continue #'handle-continue)
(defhandler timeout :timeout #'handle-timeout)
(defhandler (set-env with) :set-env #'handle-setenv)
(defhandler delete :delete #'handle-delete)

;;; *eof*


On 6/4/06, Jim Prewett <address@hidden> wrote:

Hi Vijay,

I've had fun playing with your code today (unfortunately, some yard work,
etc. kept me busy for too much of it :).  I've made a few minor
modifications, and generally think your code has a nice feel to it :)
kudos!

Here is an example using some of the new things I've added:

(rule
        name 'foo
        match regexp ".+"
        ;; set variable var1 to 42
        setenv var1 = 42
        ;; first action
        doing (lambda (x) (format t "message: ~A var1: ~A~%" (message x)
var1))
        ;; second action
        doing #'print-message
        ;; set continuep slot to t
        continue)

;;; Paul Graham, On Lisp, p191
(defmacro aif (test-form then-form &optional else-form)
  `(let ((it ,test-form))
    (if it ,then-form ,else-form)))

;;; an 'accessor' for the handle-fns
(defmacro handle-fn (keyword)
  `(get ,keyword 'handle-fn))

;;; Influenced by Peter Norvig's LOOP implementation

(defstruct rool
  (name '())
  (match '())
  (timeout '())
  (relative-timeout '())
  (delete-rule '())
  (continuep '())
  (actions '())
  (environment '()))

(defmacro rule (&rest exprs)
  (let ((r (make-rool)))
    (parse-rule r exprs)
    (fill-rule-template r)))

(defun fill-rule-template (rool)
  `(make-instance
    'logs::rule
    :environment ',(rool-environment rool)
    :actions (rool-actions ,rool)
    ,@(loop as (key fn) in '((:match rool-match)
                             (:name rool-name)
                             (:timeout rool-timeout)
                             (:continuep rool-continuep)
                             (:relative-timeout rool-relative-timeout))
            as res = (funcall fn rool)
            if res append `(,key ,res))))

(defun parse-rule (rool exprs)
  (unless (null exprs)
    (parse-rule rool (parse-keyword rool (car exprs) (cdr exprs)))))

(defun parse-keyword (rool keyword exprs)
  (aif (handle-fn keyword)
       (funcall it rool exprs)
       (error "Unknown keyword ~S" keyword)))

(defun handle-name (rool exprs)
  (destructuring-bind (name . cdr) exprs
    ;; rule names don't have to be symbols
    ;; symbols are (obviously) a pretty good idea, but
    ;; strings, etc. should work as well
    (if (null (rool-name rool))
        (setf (rool-name rool) `,name)
        (error "Invalid name ~S" name))
    cdr))

(defun handle-match (rool exprs)
  (let ((matches (rool-match rool)))
    ;; For now, we ignore.  Later we add or something
    (declare (ignore matches))
    (destructuring-bind (car . cdr) exprs
      (case car
        (regexp (let ((msg (gensym "MESSAGE"))
                      (matches (gensym))
                      (sub-matches (gensym))
                      (regex (pop cdr)))
                  (setf (rool-match rool)
                        `(lambda (,msg)
                          (multiple-value-bind (,matches ,sub-matches)
                              (cl-ppcre:scan-to-strings ,regex (message ,msg))
                            (when ,matches
                              (values t
                                      '((sub-matches ,sub-matches)))))))
                  cdr))
        (message-length (let ((msg (gensym "MESSAGE"))
                              (relop (pop cdr))
                              (num (pop cdr)))
                          (setf (rool-match rool)
                                `(lambda (,msg)
                                  (,relop (length (message ,msg)) ,num)))
                          cdr))
        (t (setf (rool-match rool) car) cdr)))))

(defun handle-timeout (rool exprs)
  (destructuring-bind (preposition . (time . cdr)) exprs
    (case preposition
      (in (setf (rool-relative-timeout rool) time))
      (at (setf (rool-timeout rool) time)))
    cdr))

(defun handle-setenv (rool exprs)
  (destructuring-bind (variable equal value &rest rest) exprs
    (pushnew (list variable value)
             (rool-environment rool)
             :test
              (lambda (x y) (equal (car x) (car y))))
    rest))

;; add the given function to the actions list
(defun handle-do (rool exprs)
  (destructuring-bind (todo &rest rest) exprs
    (format t "rool: ~A todo: ~A rest: ~A~%"
            rool todo rest)
    (append (rool-actions rool) (list (eval todo)))
    rest))

;; set the continue slot to t
(defun handle-continue (rool exprs)
  (setf (rool-continuep rool) t)
  exprs)

(setf (handle-fn 'match) #'handle-match)
(setf (handle-fn 'name) #'handle-name)
(setf (handle-fn 'timeout) #'handle-timeout)

;; new
(setf (handle-fn 'continue) #'handle-continue)
(setf (handle-fn 'setenv) #'handle-setenv)
(setf (handle-fn 'doing) #'handle-do)


Jim

James E. Prewett                    address@hidden address@hidden
Systems Team Leader           LoGS: http://www.hpc.unm.edu/~download/LoGS/
Designated Security Officer         OpenPGP key: pub 1024D/31816D93
HPC Systems Engineer III   UNM HPC  505.277.8210

On Sun, 4 Jun 2006, Vijay Lakshminarayanan wrote:

> On 6/4/06, Jim Prewett <address@hidden> wrote:
> > > > I was wondering how you're envisioning this working for rules that
> > create
> > > > rules ... that create rules?
> > > >
> > > > (make-instance 'rule
> > > >         :actions
> > > >         (list
> > > >           (lambda (message)
> > > >              (enqueue *root-ruleset* (make-instance 'rule ...)))))
> > >
> > > We just use the macro again, I guess.
> > >
> > > (rule performing (lambda (message) (enqueue *root-ruleset* (rule
> > ...))))
> > >
> >
> > I was hoping that was the answer :)
>
> Oh so it's a Good Thing?  That's a relief :-)
>
> > > In time we should have a single function which does the enqueing.
> >
> > I don't think you're quite understanding the ruleset tree structure.
> > rulesets contain rules and other rulesets.  You may want to have a rule
> > that creates a new rule on a completely different part of the tree.
>
> You're right.  I completely forgot about this -- I was totally
> immersed into defining the macro's behavior itself.
>
> > In other words, I don't see specifying the ruleset you're adding the rule
> > to to be a loss.
> >
> > Jim
> >
>
> Will come up with more and better quality code soon, Jim.
>
> Thanks
> Vijay
>





reply via email to

[Prev in Thread] Current Thread [Next in Thread]