diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2021-02-03 19:02:28 -0800 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2021-02-03 19:02:28 -0800 |
commit | 8bc0ac3a54d248ba2d4a8a045dc2bc0619a60886 (patch) | |
tree | 2639b008221362f3c06f329ac7a4f63cf3cf0d3b | |
parent | 06f994c1fd99290535a918e4b0954b6350684966 (diff) | |
download | txr-8bc0ac3a54d248ba2d4a8a045dc2bc0619a60886.tar.gz txr-8bc0ac3a54d248ba2d4a8a045dc2bc0619a60886.tar.bz2 txr-8bc0ac3a54d248ba2d4a8a045dc2bc0619a60886.zip |
matcher: reimplementation of lambda-match.
This patch replaces the argument-list-based lambda match with
one that matches fixed arguments without consing. Instead of
generating a variadic function with zero fixed arguments, it
generates a function with required, optional and rest argument
based on considering the arity of all the matches.
* share/txr/stdlib/match.tl (compile-exprs-match): New
function.
(compile-match): Wire a new pattern operator called sys:exprs,
for internal use. This matches a list-like pattern against the
values of multiple expressions, rather than a single
expression.
(when-exprs-mach): New internal macro for matching a sequence
of patterns against a sequence of expressions of the same
arity.
(lambda-clause): New structure.
(parse-lambda-match-clause, expand-lambda-match): New functions.
(lambda-match, defun-match): Redefine using
expand-lambda-match.
-rw-r--r-- | share/txr/stdlib/match.tl | 104 |
1 files changed, 98 insertions, 6 deletions
diff --git a/share/txr/stdlib/match.tl b/share/txr/stdlib/match.tl index adb85ea9..e2e1ac4a 100644 --- a/share/txr/stdlib/match.tl +++ b/share/txr/stdlib/match.tl @@ -501,6 +501,19 @@ obj-var obj-var guard-chain (cons guard (mappend .guard-chain hash-matches)))))) +(defun compile-exprs-match (exprs-syntax exprs var-list) + (let ((pats (cdr exprs-syntax)) + (temps (mapcar (ret (gensym)) exprs))) + (let* ((matches (mapcar (op compile-match @1 @2 var-list) + pats temps))) + (new compiled-match + pattern exprs-syntax + obj-var nil + guard-chain (cons (new match-guard + pure-temps temps + pure-temp-exprs exprs) + (mappend .guard-chain matches)))))) + (defun compile-match (pat : (obj-var (gensym)) (var-list (new var-list))) (cond ((consp pat) @@ -524,6 +537,7 @@ (hash (compile-hash-match exp obj-var var-list)) (rcons (compile-range-match exp obj-var var-list)) (dwim (compile-dwim-predicate-match exp obj-var var-list)) + (exprs (compile-exprs-match exp obj-var var-list)) (t (compile-predicate-match exp obj-var var-list))) (compile-error *match-form* "unrecognized pattern syntax ~s" pat)))) @@ -571,15 +585,93 @@ ,*clause-code ,result)))) +(defmacro when-exprs-match (pats exprs . forms) + (let ((em (compile-match ^@(exprs ,*pats) exprs))) + ^(let* (,*em.(get-vars)) + ,em.(wrap-guards . forms)))) + +(defstruct lambda-clause () + orig-syntax + fixed-patterns + variadic-pattern + nfixed + forms + + (:postinit (me) + (set me.nfixed (len me.fixed-patterns)))) + +(defun parse-lambda-match-clause (clause) + (mac-param-bind *match-form* (args . body) clause + (cond + ((atom args) (new lambda-clause + orig-syntax args + variadic-pattern args + forms body)) + ((proper-list-p args) + (let* ((vpos (pos-if (lop meq 'sys:expr 'sys:var) args))) + (tree-bind (fixed-pats . variadic-pat) (split args vpos) + (new lambda-clause + orig-syntax args + fixed-patterns fixed-pats + variadic-pattern (car variadic-pat) + forms body)))) + (t (new lambda-clause + orig-syntax args + fixed-patterns (butlast args 0) + variadic-pattern (last args 0) + forms body))))) + +(defun expand-lambda-match (clauses) + (let* ((parsed-clauses [mapcar parse-lambda-match-clause clauses]) + (max-args (or [find-max parsed-clauses : .nfixed].?nfixed 0)) + (min-args (or [find-min parsed-clauses : .nfixed].?nfixed 0)) + (variadic [some parsed-clauses .variadic-pattern]) + (fix-arg-temps (mapcar (op gensym `arg-@1`) + (range* 0 min-args))) + (opt-arg-temps (mapcar (op gensym `arg-@1`) + (range* min-args max-args))) + (rest-temp (if variadic (gensym `rest`))) + (present-p-temps (mapcar (op gensym `have-@1`) + (range* min-args max-args))) + (arg-temps (append fix-arg-temps opt-arg-temps)) + (present-vec (vec-list (append (repeat '(t) min-args) + present-p-temps))) + (matched-p-temp (gensym "matched-p")) + (result-temp (gensym "result")) + (ex-clauses (collect-each ((pc parsed-clauses) + (counter 0)) + (let* ((vp pc.variadic-pattern) + (exp ^(when-exprs-match + (,*pc.fixed-patterns + ,*(if vp (list vp))) + (,*[arg-temps 0..pc.nfixed] + ,*(if vp + ^((list* ,*[arg-temps pc.nfixed..:] + ,rest-temp)))) + (set ,matched-p-temp t) + (set ,result-temp (progn ,*pc.forms))))) + (when (> pc.nfixed min-args) + (set exp ^(when ,[present-vec (pred pc.nfixed)] + ,exp))) + (when (< pc.nfixed max-args) + (set exp ^(unless ,[present-vec pc.nfixed] + ,exp))) + (unless (zerop counter) + (set exp ^(unless ,result-temp ,exp))) + exp)))) + ^(lambda (,*fix-arg-temps + ,*(mapcar (ret ^(,@1 nil ,@2)) opt-arg-temps present-p-temps) + . ,rest-temp) + (let (,matched-p-temp ,result-temp) + ,*ex-clauses + ,result-temp)))) + (defmacro lambda-match (. clauses) - (with-gensyms (args) - ^(lambda (. ,args) - (match-case ,args ,*clauses)))) + (expand-lambda-match clauses)) (defmacro defun-match (name . clauses) - (with-gensyms (args) - ^(defun ,name (. ,args) - (match-case ,args ,*clauses)))) + (tree-bind (lambda args . body) (expand-lambda-match clauses) + ^(defun ,name ,args . ,body))) (defun non-triv-pat-p (syntax) t) |