aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRose Hogenson <rhogenson@posteo.net>2022-01-09 11:16:17 -0800
committerRose Hogenson <rhogenson@posteo.net>2022-01-09 11:16:17 -0800
commite7d0eefb6f766968fe0de26556da1353ec9bd0ab (patch)
treeaf8b4015ddc64690655219e8367da1b7249f8a3a
parent45024698a5f11d4dddbbf7be75d96490d188f0ae (diff)
downloadchromatopelma-e7d0eefb6f766968fe0de26556da1353ec9bd0ab.tar.zst
Add a library for pattern matching.
-rw-r--r--match-test.csc42
-rw-r--r--match.csc60
2 files changed, 102 insertions, 0 deletions
diff --git a/match-test.csc b/match-test.csc
new file mode 100644
index 0000000..3fed862
--- /dev/null
+++ b/match-test.csc
@@ -0,0 +1,42 @@
+(import (scheme base)
+ (only (csc testing) define-test errorf subtest)
+ (csc match))
+
+
+(define-test (test-match t)
+ (define-record-type <test-case>
+ (test-case name expr want)
+ test-case?
+ (name name)
+ (expr expr)
+ (want want))
+ (let ((tests (list
+ (test-case
+ "simple"
+ (match 3
+ (0 0)
+ (1 1)
+ (2 2)
+ (3 3)
+ (else 4))
+ 3)
+ (test-case
+ "match-list"
+ (match '(1 2 3)
+ ('() 0)
+ ((1 2) 1)
+ ((1 2 3) 2)
+ ((1 2 3 4) 3)
+ (else 4))
+ 2)
+ (test-case
+ "binding"
+ (match '(1 2 3)
+ ((1 (! x) 3) x))
+ 2))))
+ (for-each
+ (lambda (tc)
+ (subtest t (name tc)
+ (unless (equal? (expr tc) (want tc))
+ (errorf t "match = {}, want {}." (expr tc) (want tc)))))
+ tests)))
diff --git a/match.csc b/match.csc
new file mode 100644
index 0000000..95b2b32
--- /dev/null
+++ b/match.csc
@@ -0,0 +1,60 @@
+(define-library (csc match)
+ (export match)
+ (import (scheme base))
+ (begin
+
+
+ (define-syntax matches?
+ (syntax-rules (!)
+ ((matches? x '())
+ (null? x))
+ ((matches? x (! bind))
+ #t)
+ ((matches? x ((! bind)))
+ (= 1 (length x)))
+ ((matches? x (lit))
+ (and (= 1 (length x))
+ (eqv? lit (car x))))
+ ((matches? x ((! bind) pattern1 pattern2 ...))
+ (and (pair? x)
+ (matches? (cdr x) (pattern1 pattern2 ...))))
+ ((matches? x (lit pattern1 pattern2 ...))
+ (and (pair? x)
+ (eqv? lit (car x))
+ (matches? (cdr x) (pattern1 pattern2 ...))))
+ ((matches? x lit)
+ (eqv? lit x))))
+
+
+ (define-syntax bind-pattern
+ (syntax-rules (!)
+ ((bind-pattern x '() result1 result2 ...)
+ (begin result1 result2 ...))
+ ((bind-pattern x (! bind) result1 result2 ...)
+ (let ((bind x))
+ result1 result2 ...))
+ ((bind-pattern x ((! bind)) result1 result2 ...)
+ (let ((bind (car x)))
+ result1 result2 ...))
+ ((bind-pattern x (lit) result1 result2 ...)
+ (begin result1 result2 ...))
+ ((bind-pattern x ((! bind) pattern1 pattern2 ...) result1 result2 ...)
+ (let ((bind (car x)))
+ (bind-pattern (cdr x) (pattern1 pattern2 ...) result1 result2 ...)))
+ ((bind-pattern x (lit pattern1 pattern2 ...) result1 result2 ...)
+ (bind-pattern (cdr x) (pattern1 pattern2 ...) result1 result2 ...))
+ ((bind-pattern x lit result1 result2 ...)
+ (begin result1 result2 ...))))
+
+
+ (define-syntax match
+ (syntax-rules (else)
+ ((match x (else result1 result2 ...))
+ (begin result1 result2 ...))
+ ((match x (pattern result1 result2 ...))
+ (if (matches? x pattern)
+ (bind-pattern x pattern result1 result2 ...)))
+ ((match x (pattern result1 result2 ...) clause1 clause2 ...)
+ (if (matches? x pattern)
+ (bind-pattern x pattern result1 result2 ...)
+ (match x clause1 clause2 ...)))))))