Skip to content
Browse files

Added with-match-paramters. Now match is hygienic.

  • Loading branch information...
1 parent f35b4d6 commit dbae21e8b74e39c552ab734e2ef7ba9722f5d18d Tomohiro Matsuyama committed Apr 19, 2011
Showing with 156 additions and 74 deletions.
  1. +132 −53 README.markdown
  2. +1 −1 cl-pattern.asd
  3. +2 −1 src/{condition.lisp → conditions.lisp}
  4. +15 −4 src/match.lisp
  5. +1 −9 src/pattern.lisp
  6. +5 −6 tests/match.lisp
View
185 README.markdown
@@ -4,79 +4,158 @@ CL-PATTERN
CL-PATTERN is a very fast ML-like pattern-matching library for Common
Lisp.
-Usage
------
+API
+---
### Macro: `match`
match value &body clauses
-`match` macro tries to match `value` with `clauses` and raise errors
-if no clauses matched. Clause have to be a form of `(pattern form*)`,
-where `pattern` is a symbol for binding values, an atom for matching
-values, and a composed form of them. Binding to `_` will be ignored
-(i.e. `(declare (ignore _))`) automatically and its binding can't be
-used anywhere. If `pattern` is `_`, its clause will be matched with
-any value. `pattern` can have `&optional` patterns, which will be a
-variable `*unbound*`, which is usually `nil`, if patterns are not
-matched.
+`match` macro tries to match `value` with `clauses` and raise an error
+if no clauses matched. A clause have to be a form of `(pattern
+form*)`, where `pattern` is a pattern (see "Patterns").
+
+`match` macro establishes new match parameters of that `test` function
+is `equal` and `unbound` is nil. See "Macro Parameters" for details.
#### Examples
- (match 1
- (x x))
- ; => 1
-
(match '(1 2)
((x y) (+ x y)))
- ; => 3
-
- (match '(:bar 1)
- ((:foo _) "Foo!")
- ((:bar _) "Bar!"))
- ; => "Bar!"
-
+ ;;=> 3
+
(match '(x 1)
(('x x) x))
- ; => 1
-
- (match 5
- (1 'one)
- (2 'two)
- (3 'three)
- (4 'four))
- ; => match error
-
- (match 5
- (1 'one)
- (2 'two)
- (3 'three)
- (4 'four)
- (t 'otherwise))
- ; => OTHERWISE
-
- (match '(1)
- ((1 &optional a) a))
- ; => NIL
-
+ ;;=> 1
+
(match '(1 2)
((1 &optional a) a))
- ; => 2
+ ;;=> 2
+
+### Macro: `match*`
+
+ match* value &body clauses
+
+Same as `match` macro except that `match*` accepts the current
+(lexical) match parameters. That is, you must be careful of the
+current lexical context if you use this macro. See "Match Parameters"
+for details.
+
+#### Examples
+
+ (with-match-parameters (:unbound t)
+ (match* ()
+ ((&optional v) v)))
+ ;;=> T
+
+ (with-match-parameters (:test string-equal)
+ (match* :foo
+ ("foo" 'matched)))
+ ;;=> MATCHED
+
+### Macro: `with-match-parameters`
+
+ with-match-parameters (&key test unbound) &body body
+
+Establishes match parameters for lexical scope of `body`. This only
+affects on `match*` macro because `match` establishes another match
+parameters by itself.
+
+`test` keyword parameter will be used for comparing a constant patten
+(e.g. 1, "a", :x, etc) with a matching variable. For example,
+
+ (with-match-parameters (test string-equal)
+ ...)
- (defun sum (lst)
- (match lst
- (() 0)
- ((x . xs) (+ x (sum xs)))))
- (sum '(1 2 3))
- ; => 6
+makes compare expressions within its body to use `string-equal` for
+comparing patterns and values. A value of this keyword parameter must
+be a function name or a lambda expression to achieve better
+performance. The default value is `equal`.
+
+`unbound` keyword parameter will be used for specifying a value of
+unbound variables. Optional variables in a pattern might be unbound
+even if the pattern is matched. In that case, such optional variables
+have a value specified by `unbound` keyword parameter. The default
+value is `nil`.
### Macro: `lambda-match`
lambda-match &body clauses
-`lambda-match` is a shorthand for lambda and match. For example,
-`(lambda-match (('foo a) a))` will be expanded as `(lambda (arg)
-(match arg (('foo a) a)))`.
+`lambda-match` is a shorthand of `lambda` and `match`. For example,
+
+ (lambda-match
+ (('foo a) a))
+
+will be expaneded to
+
+ (lambda (arg)
+ (match arg
+ (('foo a) a)))
+
+### Patterns
+
+A pattern must be one of a symbol, a cons, and an atom. If the pattern
+is symbol, the pattern is called a variable pattern, which can be
+matched with any value. The variable can be used in a body of a
+clause. Here is an example:
+
+ (match 1
+ (x x))
+ ;;=> 1
+
+If the pattern is a cons, there is two cases you have to know. If a
+`car` of the cons is `quote`, the pattern is called a constant
+pattern, which can be matched with a same value of a `cadr` of the
+cons. Here is an example:
+
+ (match 'x
+ ('x 1))
+ ;;=> 1
+
+In another case, the pattern is called a structure pattern, which can
+be matched if a `car` of the pattern and a `cdr` of the pattern are
+matched with a value. Here is an example:
+
+ (match '(1 . 2)
+ ((x . y) (+ x y)))
+ ;;=> 1
+
+As you may guess, the `car` of the pattern and the `cdr` of the
+pattern are also patterns. So you can nest patterns infinitely.
+
+If the pattern is an atom, the pattern is called a constant pattern
+too. As we said, the pattern can be matched with a same value of the
+pattern. Here is an example:
+
+ (match 1
+ (1 'matched))
+ ;;=> MATCHED
+
+### Match Parameters
+
+Match parameters controls a meaning of pattern-matching in
+lexical. This will be used when you want to
+
+* change a default equal function for comparing patterns and values.
+* change a default value of optional variables.
+
+Note that `match` macro establishes a new match parameters by itself,
+meaning that doesn't accept the current lexical match parameters. In
+contrast, `match*` accept the current lexical match parameters. This
+is the main difference between `match` and `match*`.
+
+See `with-match-parameters` for details.
+
+Supported Implementations
+-------------------------
+
+* Allegro CL v8.2
+* SBCL v1.0.47
+* CMU CL v20b
+* Clozure CL v1.6
+* ECL v11.1.1
+* GNU CLISP v2.48
----
View
2 cl-pattern.asd
@@ -15,7 +15,7 @@
((:module "src"
:serial t
:components ((:file "package")
- (:file "condition")
+ (:file "conditions")
(:file "pattern")
(:file "case")
(:file "compile")
View
3 src/condition.lisp → src/conditions.lisp
@@ -2,7 +2,8 @@
(use-syntax annot-syntax)
@export
-(define-condition match-error (error) ())
+(define-condition match-error (error)
+ ())
(defun %match-error ()
(error (make-condition 'match-error)))
View
19 src/match.lisp
@@ -5,8 +5,7 @@
(let ((groups (partition-match-clauses clauses)))
(compile-match-groups vars groups else)))
-@export
-(defmacro match* (args &body clauses)
+(defmacro match-values (args &body clauses)
(loop for arg in args
for var = (gensym "VAR")
if (atom arg)
@@ -22,12 +21,24 @@
then)))))
@export
-(defmacro match (arg &body clauses)
- `(match* (,arg)
+(defmacro with-match-parameters ((&key (test 'equal) unbound) &body body)
+ `(macrolet ((pattern-equal (pattern value)
+ (list ',test pattern value)))
+ (symbol-macrolet ((pattern-unbound ,unbound))
+ ,@body)))
+
+@export
+(defmacro match* (arg &body clauses)
+ `(match-values (,arg)
,@(loop for (pattern . then) in clauses
collect `((,pattern) ,@then))))
@export
+(defmacro match (arg &body clauses)
+ `(with-match-parameters ()
+ (match* ,arg ,@clauses)))
+
+@export
(defmacro lambda-match (&body clauses)
(with-gensyms (arg)
`(lambda (,arg)
View
10 src/pattern.lisp
@@ -1,13 +1,6 @@
(in-package :cl-pattern)
(use-syntax annot-syntax)
-@export
-(defvar *unbound* nil)
-
-@export
-(defmacro pattern-equal (pattern value)
- `(equal ,pattern ,value))
-
(defmacro %equal (pattern value)
(typecase pattern
(cons `(pattern-equal ',(cadr pattern) ,value))
@@ -31,8 +24,7 @@
(free-variables (cdr pattern))))))
(defun free-variables-bindings (vars)
- (mapcar (lambda (var)
- (list var '*unbound*))
+ (mapcar (lambda (var) (list var 'pattern-unbound))
vars))
(defun optional-patterns (pattern)
View
11 tests/match.lisp
@@ -74,21 +74,20 @@
((((&optional a))) a))
nil
"match optional nest 3")
-(is (let ((*unbound* 1))
- (match ()
+(is (with-match-parameters (:unbound 1)
+ (match* ()
((&optional a) a)))
1
"match optional *unbound*")
(is (match '((1) "a")
(((1 &optional a) "a" &optional b) (list a b)))
'(nil nil)
"match complex")
-(is (macrolet ((pattern-equal (a b)
- `(string-equal ,a ,b)))
- (match :a
+(is (with-match-parameters (:test string-equal)
+ (match* :a
("a" 1)))
1
- "match with different equality")
+ "match with custom equal function")
(defun sum (list)
(match list

0 comments on commit dbae21e

Please sign in to comment.
Something went wrong with that request. Please try again.