PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/distel/elisp/patmatch.el

http://github.com/gebi/jungerl
Emacs Lisp | 232 lines | 195 code | 33 blank | 4 comment | 4 complexity | 813be1d7bfcfc26b5765f3acb6939b0f MD5 | raw file
Possible License(s): AGPL-1.0, JSON, LGPL-2.1, BSD-3-Clause
  1. ;; -*- comment-column: 32 -*-
  2. (eval-when-compile (require 'cl))
  3. (put 'mcase 'lisp-indent-function 1)
  4. (put 'pmatch 'lisp-indent-function 2)
  5. (put 'mlet 'lisp-indent-function 2)
  6. (defmacro mcase (object &rest clauses)
  7. "Pattern-matching case expression.
  8. The syntax is like the normal `case':
  9. (mcase EXPR
  10. (PATTERN . BODY)
  11. ...)
  12. The body of the first matching pattern is executed, with pattern
  13. variables bound to their matching values. If no patterns match, an
  14. error is signaled.
  15. See `mlet' for a description of pattern syntax."
  16. `(mcase* ,object ,(mcase-parse-clauses clauses)))
  17. (eval-and-compile
  18. (defun mcase-parse-clauses (clauses)
  19. `(list ,@(mapcar #'(lambda (clause)
  20. `(list ',(car clause)
  21. (lambda () ,@(cdr clause))))
  22. clauses))))
  23. (defmacro pmatch (&rest args)
  24. "Deprecated; see `mlet'."
  25. `(mlet ,@args))
  26. (defmacro mlet (pattern object &rest body)
  27. "Match PATTERN with OBJECT, and execute BODY with all bindings.
  28. The pattern syntax is:
  29. Trivial: t, nil, 42
  30. Testing with `equal'
  31. Pattern variable: x, my-variable
  32. Variable that the pattern should bind. If the same variable
  33. appears several times in a pattern, then all of its bindings must
  34. match.
  35. Within the body of a successful pattern match, lisp variables are
  36. bound for all pattern variables.
  37. Constant: 'symbol, '(1 2 3), ...
  38. Quoted constant, matched with `equal'.
  39. Bound variable: ,var
  40. Pre-bound Lisp variable, matched by value.
  41. Wild card: _ (underscore)
  42. Matches anything, with no binding.
  43. Sequence: (pat1 ...), [pat1 ...]
  44. Matches the \"shape\" of the pattern, as well as each individual
  45. subpattern."
  46. (let ((var (make-symbol "var")))
  47. `(let ((,var ,object)) ; so that we just eval `object' once
  48. (mcase ,var
  49. (,pattern ,@body)
  50. (_ (signal 'erl-exit-signal
  51. (list (tuple 'badmatch ',pattern ,var))))))))
  52. (defun mcase* (object clauses)
  53. (let ((clause (mcase-choose object clauses)))
  54. (if clause
  55. (funcall clause)
  56. (signal 'erl-exit-signal '(case-clause)))))
  57. (defun mcase-choose (object clauses)
  58. (if (null clauses)
  59. nil
  60. (let* ((clause (car clauses))
  61. (pattern (car clause))
  62. (action (cadr clause))
  63. (result (patmatch pattern object)))
  64. (if (eq result 'fail)
  65. (mcase-choose object (cdr clauses))
  66. `(lambda ()
  67. (let ,(alist-to-letlist result)
  68. (funcall ,action)))))))
  69. (defun alist-to-letlist (alist)
  70. "Convert an alist into `let' binding syntax, eg: ((A . B)) => ((A 'B))"
  71. (mapcar (lambda (cell)
  72. (list (car cell) (list 'quote (cdr cell))))
  73. alist))
  74. (defun pmatch-tail (seq)
  75. (if (consp seq)
  76. (cdr seq)
  77. (let ((new (make-vector (1- (length seq)) nil)))
  78. (dotimes (i (length new))
  79. (aset new i (aref seq (1+ i))))
  80. new)))
  81. (defun patmatch (pattern object &optional bindings)
  82. "Match OBJECT with PATTERN, and return an alist of bindings."
  83. (if (eq bindings 'fail)
  84. 'fail
  85. (cond ((pmatch-wildcard-p pattern)
  86. bindings)
  87. ((pmatch-constant-p pattern) ; '(x)
  88. (pmatch-constant pattern object bindings))
  89. ((pmatch-bound-var-p pattern) ; ,foo
  90. (pmatch-match-var pattern object bindings))
  91. ((pmatch-unbound-var-p pattern) ; foo
  92. (pmatch-bind-var pattern object bindings))
  93. ((pmatch-trivial-p pattern) ; nil, t, any-symbol
  94. (if (equal pattern object) bindings 'fail))
  95. ((consp pattern)
  96. (if (consp object)
  97. (patmatch (cdr pattern) (cdr object)
  98. (patmatch (car pattern) (car object) bindings))
  99. 'fail))
  100. ((vectorp pattern)
  101. (if (and (vectorp object)
  102. (= (length pattern) (length object)))
  103. (patmatch (coerce pattern 'list) (coerce object 'list) bindings)
  104. 'fail))
  105. (t
  106. 'fail))))
  107. (defun pmatch-wildcard-p (pat)
  108. (eq pat '_))
  109. (defun pmatch-trivial-p (pat)
  110. "Test for patterns which can always be matched literally with `equal'."
  111. (or (numberp pat)
  112. (equal pat [])
  113. (equal pat nil)
  114. (equal pat t)))
  115. (defun pmatch-constant-p (pat)
  116. "Test for (quoted) constant patterns.
  117. Example: (QUOTE QUOTE)"
  118. (and (consp pat)
  119. (= (length pat) 2)
  120. (eq (car pat) 'quote)))
  121. (defun pmatch-constant-value (pat)
  122. "The value of a constant pattern.
  123. (QUOTE X) => X"
  124. (cadr pat))
  125. (defun pmatch-constant (pat object bindings)
  126. "Match OBJECT with the constant pattern PAT."
  127. (if (equal (pmatch-constant-value pat) object)
  128. bindings
  129. 'fail))
  130. (defun pmatch-unbound-var-p (obj)
  131. "Unbound variable is any symbol except nil or t."
  132. (and (symbolp obj)
  133. (not (eq obj nil))
  134. (not (eq obj t))))
  135. (defun pmatch-unbound-var-symbol (sym)
  136. sym)
  137. (defun pmatch-bind-var (pat object bindings)
  138. "Add a binding of pattern variable VAR to OBJECT in BINDINGS."
  139. (if (eq object erl-tag)
  140. ;; `erl-tag' cannot bind to a variable; this is to prevent pids
  141. ;; or ports from matching tuple patterns.
  142. 'fail
  143. (let* ((var (pmatch-unbound-var-symbol pat))
  144. (binding (assoc var bindings)))
  145. (cond ((null binding)
  146. (acons var object bindings))
  147. ((equal (cdr binding) object)
  148. bindings)
  149. (t
  150. 'fail)))))
  151. (eval-when-compile (defvar pattern)) ; dynamic
  152. (defun pmatch-match-var (var object bindings)
  153. "Match the value of the Lisp variable VAR with OBJECT."
  154. (if (equal (symbol-value (pmatch-bound-var-name pattern)) object)
  155. bindings
  156. 'fail))
  157. (defun pmatch-bound-var-p (obj)
  158. (and (symbolp obj)
  159. (eq (elt (symbol-name obj) 0) ?,)))
  160. (defun pmatch-bound-var-name (sym)
  161. (intern (substring (symbol-name sym) 1)))
  162. (defun pmatch-alist-keysort (alist)
  163. (sort alist (lambda (a b)
  164. (string< (symbol-name (car a))
  165. (symbol-name (car b))))))
  166. ;;; Test suite
  167. (defun pmatch-expect (pattern object expected)
  168. "Assert that matching PATTERN with OBJECT yields EXPECTED.
  169. EXPECTED is either 'fail or a list of bindings (in any order)."
  170. (let ((actual (patmatch pattern object)))
  171. (if (or (and (eq actual 'fail)
  172. (eq actual expected))
  173. (and (listp expected)
  174. (listp actual)
  175. (equal (pmatch-alist-keysort actual)
  176. (pmatch-alist-keysort expected))))
  177. t
  178. (error "Patmatch: %S %S => %S, expected %S"
  179. pattern object actual expected))))
  180. (defun pmatch-test ()
  181. "Test the pattern matcher."
  182. (interactive)
  183. (pmatch-expect t t ())
  184. (pmatch-expect '(t nil 1) '(t nil 1) ())
  185. (let ((foo 'foo))
  186. (pmatch-expect '(FOO ,foo 'foo [FOO]) '(foo foo foo [foo])
  187. '((FOO . foo))))
  188. (pmatch-expect 1 2 'fail)
  189. (pmatch-expect '(x x) '(1 2) 'fail)
  190. (pmatch-expect '_ '(1 2) 'nil)
  191. (assert (equal 'yes
  192. (mcase '(call 42 lists length ((1 2 3)))
  193. (t 'no)
  194. (1 'no)
  195. ((call Ref 'lists 'length (_))
  196. 'yes)
  197. (_ 'no))))
  198. (message "Smooth sailing"))
  199. (provide 'patmatch)