initial
This commit is contained in:
commit
e2b077766e
2 changed files with 361 additions and 0 deletions
223
match.sls
Normal file
223
match.sls
Normal file
|
@ -0,0 +1,223 @@
|
|||
;; extensible, syntax property-based matching for chez scheme a la trivia
|
||||
(library (meh match)
|
||||
(export
|
||||
match if-let when-let
|
||||
define-m%-expander define-pattern define-pattern-expander
|
||||
m%-expander pattern-expander
|
||||
|
||||
; built-in patterns
|
||||
fail and or not guard
|
||||
access satisfies cons
|
||||
list list*
|
||||
vector vector*
|
||||
= < > <= >=
|
||||
quote quasiquote
|
||||
)
|
||||
(import (chezscheme))
|
||||
|
||||
|
||||
;; binding the scrutinee once makes the expanded code nicer to read
|
||||
(define-syntax match
|
||||
(syntax-rules ()
|
||||
[(match scrutinee clause* ...)
|
||||
(let ([s scrutinee])
|
||||
(match% s clause* ...))]))
|
||||
|
||||
;; i just like rust's thing too much
|
||||
(define-syntax if-let
|
||||
(syntax-rules ()
|
||||
[(_ pattern scrutinee then) (if-let pattern scrutinee then (void))]
|
||||
[(_ pattern scrutinee then else)
|
||||
(match scrutinee
|
||||
[pattern then]
|
||||
[_ else])]))
|
||||
|
||||
(define-syntax when-let
|
||||
(syntax-rules ()
|
||||
[(_ pattern scrutinee body* ...)
|
||||
(if-let pattern scrutinee (begin body* ...))]))
|
||||
|
||||
;; no unless-let because come on
|
||||
|
||||
(define-syntax match%
|
||||
(syntax-rules ()
|
||||
[(match% scrutinee) (void)]
|
||||
[(match% scrutinee [pattern body* ...] clause* ...)
|
||||
(m% scrutinee pattern
|
||||
(begin body* ...)
|
||||
(match% scrutinee clause* ...))]))
|
||||
|
||||
(define m%-expander)
|
||||
(define pattern-expander)
|
||||
|
||||
(define-syntax m%
|
||||
(lambda (e)
|
||||
(lambda (lookup)
|
||||
(define (find-m%-expander name)
|
||||
(and (identifier? name) (lookup name #'m%-expander)))
|
||||
(define (find-pattern-expander name)
|
||||
(and (identifier? name) (lookup name #'pattern-expander)))
|
||||
;; - is a decent choice because _ isn't allowed and * implies something else
|
||||
(syntax-case e (-)
|
||||
[(_ s - ok fail) #'ok]
|
||||
|
||||
[(_ s pat ok fail) (identifier? #'pat)
|
||||
#'(let ([pat s]) ok)]
|
||||
|
||||
;; before atom? because vectors are (rightly) atoms
|
||||
[(_ s #(pat* ...) ok fail)
|
||||
#'(m% s (vector pat* ...) ok fail)]
|
||||
|
||||
[(_ s pat ok fail) (atom? (syntax->datum #'pat))
|
||||
#'(if (equal? s pat) ok fail)]
|
||||
|
||||
[(_ s (patf pata* ...) ok fail) (find-m%-expander #'patf)
|
||||
((find-m%-expander #'patf) #'(patf s ok fail pata* ...))]
|
||||
|
||||
[(_ s (patf pata* ...) ok fail) (find-pattern-expander #'patf)
|
||||
#`(m% s #,((find-pattern-expander #'patf) #'(patf pata* ...))
|
||||
ok fail)]
|
||||
|
||||
[(e s (patf pata* ...) ok fail)
|
||||
#`(syntax-violation
|
||||
'm%
|
||||
#,(format "no m%-expander or pattern-expander for ~a"
|
||||
(syntax->datum #'patf))
|
||||
#'(e s (patf pata* ...) ok fail)
|
||||
#'(patf pata* ...))]))))
|
||||
|
||||
;; TODO: define-m% consistent with define-pattern
|
||||
(define-syntax define-m%-expander
|
||||
(syntax-rules ()
|
||||
[(_ name [(arg* ...) out] ...)
|
||||
(define-property name m%-expander
|
||||
(syntax-rules ()
|
||||
[(_ arg* ...) out] ...))]))
|
||||
|
||||
;; sufficient for most uses
|
||||
(define-syntax define-pattern
|
||||
(syntax-rules ()
|
||||
[(_ (name arg* ...) out)
|
||||
(define-pattern name
|
||||
(syntax-rules ()
|
||||
[(_ arg* ...) out]))]
|
||||
[(_ name body body* ...)
|
||||
(define-property name pattern-expander
|
||||
(begin body body* ...))]))
|
||||
|
||||
(define-syntax define-pattern-expander
|
||||
(syntax-rules ()
|
||||
[(_ name [(arg* ...) out] ...)
|
||||
(define-pattern name
|
||||
(syntax-rules ()
|
||||
[(_ arg* ...) out] ...))]))
|
||||
|
||||
(define fail)
|
||||
(define-m%-expander fail
|
||||
[(s ok fail) fail])
|
||||
|
||||
(define-m%-expander and
|
||||
[(s ok fail) ok]
|
||||
[(s ok fail pat) (m% s pat ok fail)]
|
||||
[(s ok fail pat pat* ...)
|
||||
(m% s pat
|
||||
(m% s (and pat* ...)
|
||||
ok fail)
|
||||
fail)])
|
||||
|
||||
(define-m%-expander or
|
||||
[(s ok fail) fail]
|
||||
[(s ok fail pat) (m% s pat ok fail)]
|
||||
[(s ok fail pat pat* ...)
|
||||
(m% s pat
|
||||
ok
|
||||
(m% s (or pat* ...)
|
||||
ok fail))])
|
||||
|
||||
(define-m%-expander not
|
||||
[(s ok fail pat) (m% s pat fail ok)])
|
||||
|
||||
(define-m%-expander guard
|
||||
[(s ok fail subpat win?)
|
||||
(m% s subpat
|
||||
(if win?
|
||||
ok
|
||||
fail)
|
||||
fail)])
|
||||
|
||||
(define access)
|
||||
(define-m%-expander access
|
||||
[(s ok fail fun pat)
|
||||
(m% (fun s) pat
|
||||
ok fail)])
|
||||
|
||||
;; intermediate vars here are very unnecessary but they get optimized out extremely easily
|
||||
(define-pattern (quote val)
|
||||
(guard x (equal? x 'val)))
|
||||
|
||||
(define satisfies)
|
||||
(define-pattern (satisfies pred?)
|
||||
(guard x (pred? x)))
|
||||
|
||||
(define-pattern (cons a d)
|
||||
(and (satisfies pair?)
|
||||
(access car a)
|
||||
(access cdr d)))
|
||||
|
||||
(define-pattern-expander list
|
||||
[() '()]
|
||||
[(x x* ...) (cons x (list x* ...))])
|
||||
|
||||
(define-pattern-expander list*
|
||||
[() '()]
|
||||
[(x) x]
|
||||
[(x x* ...) (cons x (list* x* ...))])
|
||||
|
||||
(define-property vector pattern-expander
|
||||
(lambda (e)
|
||||
(syntax-case e ()
|
||||
[(_) #'(and (satisfies vector?) (guard x (zero? (vector-length x))))]
|
||||
[(_ x* ...)
|
||||
(let* ([pats (syntax->list #'(x* ...))]
|
||||
[len (length pats)])
|
||||
#`(and (satisfies vector?)
|
||||
(guard x (= #,len (vector-length x)))
|
||||
#,@(map
|
||||
(lambda (i p) #`(access (lambda (s) (vector-ref s #,i)) #,p))
|
||||
(iota (length pats))
|
||||
pats)))])))
|
||||
|
||||
(define vector*)
|
||||
(define-property vector* pattern-expander
|
||||
(lambda (e)
|
||||
(syntax-case e ()
|
||||
[(_) #'(and (satisfies vector?) (guard x (zero? (vector-length x))))]
|
||||
[(_ x* ...)
|
||||
(let* ([pats (syntax->list #'(x* ...))]
|
||||
[len (length pats)])
|
||||
#`(and (satisfies vector?)
|
||||
;; v only difference
|
||||
(guard x (<= #,len (vector-length x)))
|
||||
#,@(map
|
||||
(lambda (i p) #`(access (lambda (s) (vector-ref s #,i)) #,p))
|
||||
(iota (length pats))
|
||||
pats)))])))
|
||||
|
||||
;; no !=
|
||||
;; you can already do (satisfies (partial = n))
|
||||
(define-pattern (= n) (and (satisfies number?) (guard x (= n x))))
|
||||
|
||||
(define-pattern (< n) (and (satisfies real?) (guard x (< x n))))
|
||||
(define-pattern (> n) (and (satisfies real?) (guard x (> x n))))
|
||||
(define-pattern (<= n) (and (satisfies real?) (guard x (<= x n))))
|
||||
(define-pattern (>= n) (and (satisfies real?) (guard x (>= x n))))
|
||||
|
||||
;; ,@ is not likely to happen
|
||||
(define-pattern quasiquote
|
||||
(syntax-rules (unquote)
|
||||
[(_ (unquote pat)) pat]
|
||||
;; this specialization *probably* has no perf benefit
|
||||
[(_ (x* ...)) (list `x* ...)]
|
||||
[(_ (a . d)) (cons `a `d)]
|
||||
[(_ x) 'x]))
|
||||
)
|
138
std.sls
Normal file
138
std.sls
Normal file
|
@ -0,0 +1,138 @@
|
|||
;; amalgam of useful stuff
|
||||
(library (meh std)
|
||||
(export
|
||||
let1 -> ->> as->
|
||||
|
||||
define-syntax-rule
|
||||
|
||||
λ
|
||||
|
||||
let/cc let/1cc
|
||||
|
||||
identity const const*
|
||||
thunk thunk*
|
||||
compose compose1 partial
|
||||
negate conjoin disjoin)
|
||||
|
||||
(import (chezscheme))
|
||||
|
||||
(export (import (meh match)))
|
||||
|
||||
;;; SYNTAX
|
||||
|
||||
(define-syntax let1
|
||||
(lambda (e)
|
||||
(syntax-case e ()
|
||||
[(let1 name val body ...) (identifier? #'name)
|
||||
#'(let ([name val])
|
||||
body ...)])))
|
||||
|
||||
;; i H8TE that (-> x (partial + 1)) is (partial x + 1)
|
||||
;; maybe there should be a proc version
|
||||
(define-syntax ->
|
||||
(syntax-rules ()
|
||||
[(-> x) x]
|
||||
[(-> x (f x* ...) f* ...)
|
||||
(-> (f x x* ...) f* ...)]
|
||||
[(-> x f f* ...)
|
||||
(-> (f x) f* ...)]))
|
||||
|
||||
(define-syntax ->>
|
||||
(syntax-rules ()
|
||||
[(->> x) x]
|
||||
[(->> x (f x* ...) f* ...)
|
||||
(->> (f x* ... x) f* ...)]
|
||||
[(->> x f f* ...)
|
||||
(->> (f x) f* ...)]))
|
||||
|
||||
;; iirc, the clojure as-> puts the expression in place (i.e. no binding)
|
||||
(define-syntax as->
|
||||
(lambda (e)
|
||||
(syntax-case e ()
|
||||
[(as-> name x) (identifier? #'name) #'x]
|
||||
[(as-> name x (f x* ...) f* ...) (identifier? #'name)
|
||||
#'(let* ([name x]
|
||||
[name (f x* ...)])
|
||||
(as-> name name f* ...))]
|
||||
[(as-> name x f f* ...) (identifier? #'name)
|
||||
#'(as-> name x (f name) f* ...)])))
|
||||
|
||||
;; straight from racket; canonical
|
||||
(define-syntax define-syntax-rule
|
||||
(syntax-rules ()
|
||||
[(define-syntax-rule (id . pattern) templ)
|
||||
(define-syntax id
|
||||
(syntax-rules ()
|
||||
[(id . pattern) templ]))]))
|
||||
|
||||
(alias λ lambda)
|
||||
|
||||
;;; CONTINUATIONS
|
||||
(define-syntax-rule (let/cc name body* ...) (call/cc (lambda (name) body* ...)))
|
||||
(define-syntax-rule (let/1cc name body* ...) (call/1cc (lambda (name) body* ...)))
|
||||
|
||||
;;; PROCEDURE MANIP
|
||||
|
||||
;; could just be values, but this is a pinch better
|
||||
(define (identity x) x)
|
||||
|
||||
(define (const x) (rec const-fn (lambda _ x)))
|
||||
(define (const* . xs) (rec const*-fn (lambda _ (apply values xs))))
|
||||
|
||||
(define-syntax-rule (thunk body ...) (lambda () body ...))
|
||||
(define-syntax-rule (thunk* body ...) (lambda _ body ...))
|
||||
|
||||
;; ((compose f g h) x) = (f (g (h x)))
|
||||
(define (compose . fs)
|
||||
(if (null? fs)
|
||||
values
|
||||
(let ([f (car fs)]
|
||||
[rest (apply compose (cdr fs))])
|
||||
;; gives the procedure a better name
|
||||
(rec compose-fn
|
||||
(lambda xs
|
||||
(call-with-values
|
||||
(lambda () (apply rest xs))
|
||||
f))))))
|
||||
|
||||
(define (compose1 . fs)
|
||||
(if (null? fs)
|
||||
identity
|
||||
(let ([f (car fs)]
|
||||
[rest (apply compose1 (cdr fs))])
|
||||
(rec compose1-fn
|
||||
(lambda (x)
|
||||
(f (rest x)))))))
|
||||
|
||||
(define (partial f . xs)
|
||||
(rec partial-fn
|
||||
(lambda as
|
||||
(apply f (append xs as)))))
|
||||
|
||||
;; maybe nouns would be preferable? eh why not be consistent with racket
|
||||
(define (negate f)
|
||||
(rec negate-fn (lambda xs (not (apply f xs)))))
|
||||
|
||||
(define conjoin
|
||||
(case-lambda
|
||||
[() (const #t)]
|
||||
[(f) f]
|
||||
[(f . fs)
|
||||
(let ([rest (apply conjoin fs)])
|
||||
(rec conjoin-fn
|
||||
(lambda xs
|
||||
(and (apply f xs) (apply rest xs)))))]))
|
||||
|
||||
;; could be (negate conjoin) but i like good proc names
|
||||
(define disjoin
|
||||
(case-lambda
|
||||
[() (const #f)]
|
||||
[(f) f]
|
||||
[(f . fs)
|
||||
(let ([rest (apply disjoin fs)])
|
||||
(rec disjoin-fn
|
||||
(lambda xs
|
||||
(or (apply f xs) (apply rest xs)))))]))
|
||||
)
|
||||
|
||||
|
Loading…
Reference in a new issue