summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2021-05-24 08:41:58 -0700
committerKaz Kylheku <kaz@kylheku.com>2021-05-24 08:41:58 -0700
commitf2f951350516e4b959ef28f220e31b91b466fa16 (patch)
treeee4958ffcac59dcfb909ca5e217d5a74a42b1569
parent7b587fe3450c949f454778bfaf71870a5a872f6b (diff)
downloadtxr-f2f951350516e4b959ef28f220e31b91b466fa16.tar.gz
txr-f2f951350516e4b959ef28f220e31b91b466fa16.tar.bz2
txr-f2f951350516e4b959ef28f220e31b91b466fa16.zip
matcher: allow hash pattern to omit values.
The @(hash ...) operator now allows key-only patterns like (42) or (@x), where x could be bound or unbound. This has separate semantics from when a value is present. * share/txr/stdlib/match.tl (compile-hash-match): Implement. * tests/011/patmatch.tl: Test. * txr.1: Document.
-rw-r--r--share/txr/stdlib/match.tl36
-rw-r--r--tests/011/patmatch.tl7
-rw-r--r--txr.124
3 files changed, 59 insertions, 8 deletions
diff --git a/share/txr/stdlib/match.tl b/share/txr/stdlib/match.tl
index 0d99e7cf..fa0ccb80 100644
--- a/share/txr/stdlib/match.tl
+++ b/share/txr/stdlib/match.tl
@@ -469,16 +469,32 @@
(let* ((hash-alist-var (gensym "hash-alist-"))
(hash-alt-val ^',(gensym "alt"))
(need-alist-p nil)
+ (hash-keys-var (gensym "hash-keys-"))
+ (need-keys-p nil)
(hash-matches
(collect-each ((pair pairs))
- (mac-param-bind *match-form* (key val) pair
+ (mac-param-bind *match-form* (key : (val nil val-p)) pair
(let ((key-pat-p (non-triv-pat-p key))
(val-pat-p (non-triv-pat-p val))
(key-var-sym (var-pat-p key)))
(cond
+ ((and (not val-p) key-var-sym var-list.(exists key-var-sym))
+ (let ((guard (new match-guard
+ test-expr ^(inhash ,obj-var
+ ,key-var-sym))))
+ (new compiled-match
+ guard-chain (list guard))))
+ ((and (not val-p) (not key-pat-p))
+ (let ((guard (new match-guard
+ test-expr ^(inhash ,obj-var
+ ',key))))
+ (new compiled-match
+ guard-chain (list guard))))
+ ((not val-p)
+ (set need-keys-p t)
+ (compile-match key hash-keys-var var-list))
((and key-var-sym var-list.(exists key-var-sym))
- (let ((vm (compile-match val (gensym "val") var-list))
- (val-sym (gensym "val")))
+ (let ((vm (compile-match val (gensym "val") var-list)))
vm.(add-guards-pre
(new match-guard
vars (list vm.obj-var)
@@ -511,10 +527,16 @@
vm)))))))
(guard (new match-guard
guard-expr ^(hashp ,obj-var)
- vars (if need-alist-p
- (list hash-alist-var))
- var-exprs (if need-alist-p
- (list ^(hash-alist ,obj-var))))))
+ vars (append
+ (if need-alist-p
+ (list hash-alist-var))
+ (if need-keys-p
+ (list hash-keys-var)))
+ var-exprs (append
+ (if need-alist-p
+ (list ^(hash-alist ,obj-var)))
+ (if need-keys-p
+ (list ^(hash-keys ,obj-var)))))))
(new compiled-match
pattern hash-expr
obj-var obj-var
diff --git a/tests/011/patmatch.tl b/tests/011/patmatch.tl
index 85adb352..071243fb 100644
--- a/tests/011/patmatch.tl
+++ b/tests/011/patmatch.tl
@@ -187,6 +187,13 @@
(42))
(mtest
+ (when-match @(hash (a)) #H(() (a b)) t) t
+ (when-match @(hash (c)) #H(() (a b)) t) nil
+ (let ((x 'a)) (when-match @(hash (@x)) #H(() (a b)) t)) t
+ (let ((x 'd)) (when-match @(hash (@x)) #H(() (a b)) t)) nil
+ (when-match @(hash (@x)) #H(() (a b)) x) (a))
+
+(mtest
(if-match #R(10 20) 10..20 :yes :no) :yes
(if-match #R(10 20) #R(10 20) :yes :no) :yes
(if-match #R(10 20) #R(1 2) :yes :no) :no
diff --git a/txr.1 b/txr.1
index f0ff1866..85374e50 100644
--- a/txr.1
+++ b/txr.1
@@ -41159,7 +41159,7 @@ object's structure type: the type itself, rather than its symbolic name.
.coNP Pattern operator @ hash
.synb
-.mets @(hash >> {( key-pattern << value-pattern )}*)
+.mets @(hash >> {( key-pattern <> [ value-pattern ])}*)
.syne
.desc
The
@@ -41188,6 +41188,9 @@ pair against that object as described below. Each of
the pairs must successfully match, otherwise the overall
match fails.
+The following requirements apply to key-value pattern pairs in which
+the value pattern is specified.
+
If
.meta key-pattern
is a trivial pattern, then the semantics of the match is that
@@ -41267,6 +41270,25 @@ such that the semantics can then be understood in terms of the
.code coll
operator matching against an association list.
+The following requirements apply when the
+.meta value-pattern
+is omitted.
+
+If
+.meta key-pattern
+is a nontrivial pattern other than a variable pattern
+for a variable which has an existing binding, then the pattern
+is applied against the list of keys from the hash table, which
+are retrieved as if using the
+.code hash-keys
+function.
+
+If
+.meta key-pattern
+is a variable pattern referring to an existing binding, then that pattern is
+taken as a literal object. The match is successful if that object occurs as a
+key in the hash table.
+
.TP* Example:
.verb