Fri Nov 9 13:43:20 UTC 2007 mantoniotti * Made several changes to improve MATCH-CASE (following a note from Ivan Made several changes to improve MATCH-CASE (following a note from Ivan Boldyrev from a long time ago), MATCHING and MATCH. Else-clauses are now handled correctly (AFAICT). Single variable templates in MATCH, MATCH-CASE and MATCHING clauses do not need to be quoted. MATCHING was generating one gensym'ed variable per clause without creating an appropriate enclosing LET. This is now fixed. diff -rN -u old-cl-unification-1/match-block.lisp new-cl-unification-1/match-block.lisp --- old-cl-unification-1/match-block.lisp 2013-07-25 20:30:52.000000000 +0000 +++ new-cl-unification-1/match-block.lisp 2013-07-25 20:30:52.000000000 +0000 @@ -1,4 +1,7 @@ -;;; -*- Mode: Lisp -*- +;;;; -*- Mode: Lisp -*- + +;;;; match-block.lisp -- +;;;; Various macros built on top of the unifier: MATCH, MATCHING and MATCH-CASE. (in-package "UNIFY") @@ -14,7 +17,8 @@ (errorp t) (error-value nil)) &body forms) - "Sets up a lexical environment to evaluate FORMS after a unification operation. + "Sets up a lexical environment to evaluate FORMS after an unification. + MATCH unifies a TEMPLATE and an OBJECT and then sets up a lexical environment where the variables present in the template are bound lexically. Note that both variable names '?FOO' and 'FOO' are bound @@ -31,10 +35,14 @@ " (let ((template-vars (collect-template-vars template)) (env-var (gensym "UNIFICATION-ENV-")) + (template (if (variablep template) + `',template ; Logical variables are special-cased. + template)) ) (flet ((generate-var-bindings () (loop for v in template-vars - nconc (list `(,v (find-variable-value ',v ,env-var)) + nconc (list `(,v (find-variable-value ',v + ,env-var)) `(,(clean-unify-var-name v) ,v)))) ) `(block nil @@ -42,7 +50,8 @@ (let* ((,env-var (unify ,template ,object ,substitution)) ,@(generate-var-bindings) ) - (declare (ignorable ,@(mapcar #'first (generate-var-bindings)))) + (declare (ignorable ,@(mapcar #'first + (generate-var-bindings)))) ,@forms) ;; Yes. The above is sligthly wasteful. @@ -60,9 +69,11 @@ (defmacro matching ((&key errorp - (default-substitution (make-empty-environment))) + (default-substitution + (make-empty-environment))) &rest match-clauses) "MATCHING sets up a COND-like environment for multiple template matching clauses. + The syntax of MATCHING comprises a number of clauses of the form ::= | @@ -90,7 +101,12 @@ " (declare (ignore default-substitution)) ; For the time being. (labels ((%%match%% (clause-var template object forms substitution) - (let ((template-vars (collect-template-vars template))) + (let ((template-vars (collect-template-vars template)) + (template (if (variablep template) + `',template ; Logical variables are + ; special-cased. + template)) + ) (flet ((generate-var-bindings () (loop for v in template-vars nconc (list `(,v (find-variable-value @@ -99,7 +115,7 @@ `(,(clean-unify-var-name v) ,v)))) ) `((setf ,clause-var - (ignore-errors (unify ',template + (ignore-errors (unify ,template ,object ,substitution))) (let* (,@(generate-var-bindings)) @@ -120,32 +136,49 @@ (> (count t match-clauses :key #'first) 1) (> (count 'otherwise match-clauses :key #'first) 1)) (error 'program-error)) - (let* ((default-clause (or (find t match-clauses :key #'first) - (find 'otherwise match-clauses :key #'first))) - (match-clauses (delete default-clause match-clauses)) ; EQL test suffices. + (let* ((default-clause (or (find t match-clauses + :key #'first) + (find 'otherwise match-clauses + :key #'first))) + (match-clauses (delete default-clause match-clauses)) ; EQL + ; test + ; suffices. + (match-clauses-env-vars (mapcar (lambda (mc) + (declare (ignore mc)) + (gensym "UNIFICATION-ENV-") + ) + match-clauses)) ) - `(block matching - (cond ,@(mapcar (lambda (match-clause match-clause-env-var) - (build-match-clause match-clause match-clause-env-var)) - match-clauses - (mapcar (lambda (mc) - (declare (ignore mc)) - (gensym "UNIFICATION-ENV-") - ) - match-clauses)) - (,errorp - (error 'unification-non-exhaustive - :format-control "Non exhaustive matching.")) - ,@(when default-clause (list default-clause))))) - )) + `(block matching + (let ,match-clauses-env-vars + (declare (dynamic-extent ,@match-clauses-env-vars)) + (cond ,@(mapcar (lambda (match-clause match-clause-env-var) + (build-match-clause match-clause + match-clause-env-var)) + match-clauses + match-clauses-env-vars) + (,errorp + (error 'unification-non-exhaustive + :format-control "Non exhaustive matching.")) + ,@(when default-clause (list default-clause)))))) + )) ;;; match-case -- ;;; Implementation provided by Peter Scott. +;;; +;;; Notes: +;;; +;;; [MA 20071109] +;;; When the construction of the inner MATCH clauses could be done +;;; more intelligently by supplying :ERRORP NIL, thus avoiding the +;;; HANDLER-CASEs, which are quite expensive. Any takers? -(defmacro match-case ((object &key errorp default-substitution) &rest clauses) +(defmacro match-case ((object &key errorp default-substitution) + &rest clauses) "MATCH-CASE sets up a CASE-like environment for multiple template matching clauses. + The syntax of MATCH-CASE comprises a number of clauses of the form ::= | @@ -183,8 +216,8 @@ (if otherwise-clause-present-p (first (last clauses)) (when errorp - `(error 'unification-non-exhaustive - :format-control "Non exhaustive matching.")))) + `(t (error 'unification-non-exhaustive + :format-control "Non exhaustive matching."))))) ) (labels ((generate-matchers (clauses) (if (null clauses) @@ -198,5 +231,34 @@ `(let ((,object-var ,object)) ,(generate-matchers non-otherwise-clauses))))) +;;;;--------------------------------------------------------------------------- +;;;; Testing. + +#| Tests + +(let ((n 42)) + (matching () + ((0 n) 1) + ((?x n) (* x (1- x))))) + + +(let ((n 42)) + (match-case (n) + (0 1) + (?x (* x (1- x))))) + + +(let ((n 42)) + (match-case (n) + (0 1) + (otherwise (* n (1- n))))) + +(defun fatt (x) + (match-case (x :errorp t) + (0 1) + (#T(number ?n) (* ?n (fatt (1- n)))) + )) + +|# -;;; end of file -- math-blocks.lisp -- +;;;; end of file -- math-blocks.lisp --