Syntax-case implementation Andre van Tonder (23 Feb 2005 09:19 UTC)
|
Re: Syntax-case implementation
bear
(24 Feb 2005 01:39 UTC)
|
Re: Syntax-case implementation
Andre van Tonder
(25 Feb 2005 15:08 UTC)
|
Below is a syntax-case implementation (MzScheme version) for comment. Apologies for the long text. Andre ;=========================================================================================== ; Syntax-Case (MzScheme version) Implementation: ; ; Andre van Tonder, 2005. ; ;============================================================================================ (module registry mzscheme (provide register make-entry lookup-entry lookup-scheme? lookup-getter lookup-setter lookup-labels lookup-supers lookup-copier lookup-predicate) (require (prefix s1: (lib "1.ss" "srfi"))) (define reg '()) (define (make-entry name is-scheme? predicate supers labels pos-labels fields copier) (vector name is-scheme? predicate supers labels pos-labels fields copier)) (define (entry.name entry) (vector-ref entry 0)) (define (entry.is-scheme? entry) (vector-ref entry 1)) (define (entry.predicate entry) (vector-ref entry 2)) (define (entry.supers entry) (vector-ref entry 3)) (define (entry.labels entry) (vector-ref entry 4)) (define (entry.pos-labels entry) (vector-ref entry 5)) (define (entry.fields entry) (vector-ref entry 6)) (define (entry.copier entry) (vector-ref entry 7)) (define (register name entry) (cond ((s1:assoc name reg free-identifier=?) => (lambda (pair) (set-cdr! pair entry))) (else (set! reg (cons (cons name entry) reg))))) (define (lookup-entry name) (s1:assoc name reg free-identifier=?)) (define (lookup-getter name label) (cond ((s1:assoc label (entry.fields (cdr (lookup-entry name))) free-identifier=?) => cadr) (else #f))) (define (lookup-setter name label) (cond ((s1:assoc label (entry.fields (cdr (lookup-entry name))) free-identifier=?) => caddr) (else #f))) (define (lookup-scheme? name) (entry.is-scheme? (cdr (lookup-entry name)))) (define (lookup-labels name) (entry.labels (cdr (lookup-entry name)))) (define (lookup-supers name) (entry.supers (cdr (lookup-entry name)))) (define (lookup-copier name) (entry.copier (cdr (lookup-entry name)))) (define (lookup-predicate name) (entry.predicate (cdr (lookup-entry name)))) ) ; registry (module records mzscheme (provide define-record-type define-record-scheme record-update record-update! record-compose show) (require (prefix s9: (lib "9.ss" "srfi"))) (require-for-syntax (prefix s1: (lib "1.ss" "srfi"))) (require-for-syntax registry) (define-syntax define-record-type (syntax-rules () ((define-record-type . body) (parse-declaration #f . body)))) (define-syntax define-record-scheme (syntax-rules () ((define-record-scheme . body) (parse-declaration #t . body)))) (define-syntax parse-declaration (syntax-rules () ((parse-declaration is-scheme? (name super ...) (constructor pos-label ...) predicate field-clause ...) (build-record (constructor pos-label ...) #f (super ...) (field-clause ...) name predicate is-scheme?)) ((parse-declaration is-scheme? (name super ...) constructor predicate field-clause ...) (build-record (constructor) #t (super ...) (field-clause ...) name predicate is-scheme?)) ((parse-declaration is-scheme? (name super ...) constructor-clause) (parse-declaration is-scheme? (name super ...) constructor-clause #f)) ((parse-declaration is-scheme? (name super ...)) (parse-declaration is-scheme? (name super ...) #f #f)) ((parse-declaration is-scheme? name . rest) (parse-declaration is-scheme? (name) . rest)))) (define-syntax record-update! (lambda (stx) (syntax-case stx () ((_ record name (label exp) ...) (with-syntax (((setter ...) (map (lambda (label) (lookup-setter #`name label)) (syntax->list #`(label ...))))) #`(let ((r record)) (setter r exp) ... r)))))) (define-syntax record-update (lambda (stx) (syntax-case stx () ((_ record name (label exp) ...) (if (lookup-scheme? #`name) (with-syntax ((copier (lookup-copier #`name))) #`(let ((new (copier record))) (record-update! new name (label exp) ...))) #`(record-compose (name record) (name (label exp) ...))))))) (define-syntax record-compose (lambda (stx) (syntax-case stx () ((record-compose (export-name (label exp) ...)) #`(export-name (label exp) ...)) ((record-compose (import-name record) import ... (export-name (label exp) ...)) (with-syntax (((copy-label ...) (s1:lset-intersection free-identifier=? (lookup-labels #`export-name) (s1:lset-difference free-identifier=? (lookup-labels #`import-name) (syntax->list #`(label ...)))))) (with-syntax (((getter ...) (s1:map (lambda (label) (lookup-getter #`import-name label)) (syntax->list #`(copy-label ...))))) #`(let ((r record)) (record-compose import ... (export-name (copy-label (getter r)) ... (label exp) ...))))))))) (define-syntax build-record (let () (define (build-record stx) (syntax-case stx () ((build-record (constructor pos-label ...) default-order? (super ...) ((field-label . accessors) ...) name predicate is-scheme?) (with-syntax (((label ...) (s1:delete-duplicates (s1:fold-right append (syntax->list #`(pos-label ... field-label ...)) (map lookup-labels (syntax->list #`(super ...)))) free-identifier=?)) ((super ...) (s1:delete-duplicates (s1:fold-right append '() (map lookup-supers (syntax->list #`(super ...)))) free-identifier=?))) (with-syntax (((pos-label ...) (if (syntax-e #`default-order?) #`(label ...) #`(pos-label ...))) (((field-label getter setter) ...) (append (map augment-field (syntax->list #`((field-label . accessors) ...))) (map (lambda (label) (maybe-generate #`name `(,label getter setter))) (s1:lset-difference free-identifier=? (syntax->list #`(label ...)) (syntax->list #`(field-label ...))))))) (with-syntax ((supers #`(super ...)) ((pos-temp ...) (generate-temporaries #`(pos-label ...))) ((constructor predicate maker copier) (maybe-generate #`name `(,#`constructor ,#`predicate maker copier)))) (begin (register #`name (make-entry #`name (syntax-e #`is-scheme?) #`predicate (syntax->list #`(super ... name)) (syntax->list #`(label ...)) (syntax->list #`(pos-label ...)) (map syntax->list (syntax->list #`((field-label getter setter) ...))) #`copier)) (if (syntax-e #`is-scheme?) #`(begin (define-generic (predicate x) (lambda (x) #f)) (define-generic (getter x)) ... (define-generic (setter x v)) ... (define-generic (copier x))) #`(begin (s9:define-record-type internal-name (maker field-label ...) predicate (field-label getter setter) ...) (define constructor (lambda (pos-temp ...) (populate maker (field-label ...) (pos-label pos-temp) ...))) (extend-predicates supers predicate) (extend-accessors supers field-label predicate getter setter) ... (define (copier x) (maker (getter x) ...)) (extend-copiers supers copier predicate) (define-method (show (r predicate)) (list 'name (list 'field-label (getter r)) ...)) (define-syntax name (syntax-rules () ((name . bindings) (populate maker (field-label ...) . bindings)))) ))))))))) ; build-record (define (maybe-generate context maybe-identifiers) (map (lambda (elem) (if (identifier? elem) elem (datum->syntax-object context (if (symbol? elem) (gensym elem) (gensym))))) maybe-identifiers)) (define (augment-field clause) (syntax-case clause () ((label) `(,#`label ,@(maybe-generate #`label `( getter setter)))) ((label getter) `(,#`label ,@(maybe-generate #`label `(,#`getter setter)))) ((label getter setter) `(,#`label ,@(maybe-generate #`label `(,#`getter ,#`setter)))))) build-record)) (define-syntax extend-predicates (lambda (stx) (syntax-case stx () ((extend-predicates (super ...) new-type) (with-syntax (((predicate ...) (map lookup-predicate (syntax->list #`(super ...))))) #`(begin (define-method predicate (new-type) (x) any?) ...)))))) (define-syntax extend-copiers (lambda (stx) (syntax-case stx () ((extend-copiers (super ...) copy new-type) (with-syntax (((copier ...) (map lookup-copier (syntax->list #`(super ...))))) #`(begin (define-method copier (new-type) (x) copy) ...)))))) (define-syntax extend-accessors (lambda (stx) (syntax-case stx () ((extend-accessors (super ...) label new-type selector modifier) (with-syntax (((getter ...) (s1:filter (lambda (id) (not (eqv? id #f))) (map (lambda (super) (lookup-getter super #`label)) (syntax->list #`(super ...))))) ((setter ...) (s1:filter (lambda (id) (not (eqv? id #f))) (map (lambda (super) (lookup-setter super #`label)) (syntax->list #`(super ...)))))) #`(begin (define-method getter (new-type) (x) selector) ... (define-method setter (new-type any?) (x v) modifier) ...)))))) (define-syntax populate (lambda (stx) (define (order ordering bindings default) (if (null? (s1:lset-difference free-identifier=? (map car bindings) ordering)) (map (lambda (label) (cond ((s1:assoc label bindings free-identifier=?) => (lambda (x) x)) (else `(,label ,default)))) ordering) (raise-syntax-error #f "Illegal labels in" stx))) (syntax-case stx () ((populate maker labels . bindings) (with-syntax ((((label exp) ...) (order (syntax->list #`labels) (map syntax->list (syntax->list #'bindings)) #`'<undefined>))) #`(maker exp ...)))))) ; Simple generic functions suitable for our disjoint base record types: (define-syntax define-generic (syntax-rules () ((define-generic (name arg ...)) (define-generic (name arg ...) (lambda (arg ...) (error "Inapplicable method:" 'name "Arguments:" (show arg) ... )))) ((define-generic (name arg ...) proc) (define name (make-generic (arg ...) proc))))) (define-syntax define-method (syntax-rules () ((define-method (generic (arg pred?) ...) . body) (define-method generic (pred? ...) (arg ...) (lambda (arg ...) . body))) ((define-method generic (pred? ...) (arg ...) procedure) (let ((next ((generic) 'get-proc)) (proc procedure)) (((generic) 'set-proc) (lambda (arg ...) (if (and (pred? arg) ...) (proc arg ...) (next arg ...)))))))) (define-syntax make-generic (syntax-rules () ((make-generic (arg arg+ ...) default-proc) (let ((proc default-proc)) (case-lambda ((arg arg+ ...) (proc arg arg+ ...)) (() (lambda (msg) (case msg ((get-proc) proc) ((set-proc) (lambda (new) (set! proc new))))))))))) (define-generic (show x) (lambda (x) x)) (define (any? x) #t) ) ; records ;============================================================================================ ; Examples: (require records) ; A simple record declaration: (define-record-type point (make-point x y) point? (x point.x point.x-set!) (y point.y point.y-set!)) (define p (make-point 1 2)) (point? p) ;==> #t (point.y p) ;==> 2 (point.y-set! p 7) (point.y p) ;==> 7 ; Simple record schemes. ; Record schemes don't have constructors. ; The predicates and accessors are polymorphic. (define-record-scheme <point #f <point? (x <point.x) (y <point.y)) (define-record-scheme <color #f <color? (hue <color.hue)) ; Concrete instances of the above schemes. ; Constructors may be declared. ; Predicates and accessors, when provided, are monomorphic. (define-record-type (point <point) make-point point? (x point.x) (y point.y)) (define-record-type (color <color) make-color) (define-record-type (color-point <color <point) (make-color-point x y hue) color-point? (extra color-point.extra)) (define cp (make-color-point 1 2 'blue)) (<point? cp) ;==> #t (<color? cp) ;==> #t (color-point? cp) ;==> #t ;(point.x cp) ;==> error (<point.y cp) ;==> 2 (<color.hue cp) ;==> blue (color-point.extra cp) ;==> <undefined> ; Constructing records by field labels: (define p (point (x 1) (y 2))) (define cp (color-point (hue 'blue) (x 1) (y 2))) ; Monomorphic functional update: (show (record-update p point (x 7))) ;==> (point (x 7) (y 2)) (show p) ;==> (point (x 1) (y 2)) - original unaffected ; Polymorphic functional update: (show (record-update cp <point (x 7))) ;==> (color-point (extra <undefined>) (hue blue) (x 7) (y 2)) (show cp) ;==> (color-point (extra <undefined>) (hue blue) (x 1) (y 2)) ; In-place update: (show (record-update! cp <point (x 7))) ;==> color-point (extra <undefined>) (hue blue) (x 7) (y 2)) (show cp) ;==> color-point (extra <undefined>) (hue blue) (x 7) (y 2)) ; Use record-compose for updates polymorphic in argument but monomorphic in result type: (show (record-compose (<point cp) (point (x 8)))) ;==> (point (x 8) (y 2)) (show cp) ;==> (color-point (extra <undefined>) (hue blue) (x 7) (y 2)) ; More general record composition example: (define cp (make-color-point 1 2 'green)) (define c (make-color 'blue)) (show (record-compose (<point cp) ; polymorphic import - only fields x and y of cp taken (color c) ; monomorphic import (color-point (x 8) ; override imported field (extra 'hi)))) ;==> (color-point (extra hi) (hue blue) (x 8) (y 2)) ; Small module-functor example: (define-record-type monoid #f #f (mult monoid.mult) (one monoid.one)) (define-record-type abelian-group #f #f (add group.add) (zero group.zero) (sub group.sub)) (define-record-type ring #f #f (mult ring.mult) (one ring.one) (add ring.add) (zero ring.zero) (sub ring.sub)) (define integer-monoid (monoid (mult *) (one 1))) (define integer-group (abelian-group (add +) (zero 0) (sub -))) (define (make-ring g m) ; simple "functor" (record-compose (monoid m) (abelian-group g) (ring))) (define integer-ring (make-ring integer-group integer-monoid)) ((ring.add integer-ring) 1 2) ;==> 3 ; Example of tree data type (define-record-scheme <tree #f <tree?) (define-record-type (node <tree) make-node node? (lhs node.lhs) (rhs node.rhs)) (define-record-type (leaf <tree) make-leaf leaf? (val leaf.val)) (define (tree->list t) (cond ((leaf? t) (leaf.val t)) ((node? t) (cons (tree->list (node.lhs t)) (tree->list (node.rhs t)))))) (define t (make-node (make-node (make-leaf 1) (make-leaf 2)) (make-leaf 3))) (<tree? t) ;==> #t (tree->list t) ;==> ((1 . 2) . 3)