/collects/unstable/class-iop.rkt

http://github.com/gmarceau/PLT · Racket · 224 lines · 14 code · 2 blank · 208 comment · 1 complexity · 3d90f108cfdb11e892d20af8e9ea2331 MD5 · raw file

  1. #lang racket/base
  2. ;; owner: ryanc
  3. (require racket/class
  4. (for-syntax racket/base
  5. syntax/parse
  6. racket/syntax
  7. "private/class-iop-ct.rkt"))
  8. (provide define-interface
  9. define-interface/dynamic
  10. define-interface-expander
  11. (rename-out [send: send/i]
  12. [send*: send*/i]
  13. [send/apply: send/apply/i]
  14. [define: define/i]
  15. #| lambda: |#
  16. [init: init/i]
  17. [init-field: init-field/i]
  18. [init-private: init-private/i]))
  19. ;; Configuration
  20. (define-for-syntax warn-on-dynamic-interfaces? #f)
  21. (define-for-syntax warn-on-dynamic-object-check-generation? #f)
  22. (define-for-syntax define-dotted-names #f)
  23. ;; define-interface SYNTAX
  24. ;; (define-interface NAME (IDENTIFIER ...))
  25. ;; Defines NAME as an interface.
  26. (define-syntax (define-interface stx)
  27. (syntax-parse stx
  28. [(_ name:id (super:static-interface ...) (m:method-entry ...))
  29. (with-syntax ([((super-method ...) ...)
  30. (map static-interface-members (attribute super.value))])
  31. #'(define-interface/dynamic name
  32. (let ([name (interface (super ...) m.method ... ...)]) name)
  33. (super-method ... ... m.method ... ...)))]))
  34. ;; define-interface/dynamic SYNTAX
  35. ;; (define-interface/dynamic NAME EXPR (IDENTIFIER ...))
  36. ;; Defines NAME as a static interface containing the names listed.
  37. ;; The EXPR is used as the dynamic componenent of the interface, and
  38. ;; it should contain a superset of the names listed.
  39. (define-syntax (define-interface/dynamic stx)
  40. (syntax-parse stx
  41. [(_ name:id dynamic-interface:expr (mname:id ...))
  42. (with-syntax ([(dynamic-name) (generate-temporaries #'(name))])
  43. #'(begin (define dynamic-name
  44. (let ([dynamic-name dynamic-interface])
  45. (for-each
  46. (lambda (m)
  47. (unless (method-in-interface? m dynamic-name)
  48. (error 'name "dynamic interface missing method '~s'" m)))
  49. '(mname ...))
  50. dynamic-name))
  51. (define-syntax name
  52. (make-static-interface #'dynamic-name '(mname ...)))))]))
  53. (define-syntax (define-interface-expander stx)
  54. (syntax-parse stx
  55. [(_ name:id rhs:expr)
  56. #'(define-syntax name (make-interface-expander rhs))]))
  57. ;; Helper
  58. (begin-for-syntax
  59. (define (check-method-in-interface for-whom method si)
  60. (unless (member (syntax-e method) (static-interface-members si))
  61. (raise-syntax-error (syntax-e for-whom)
  62. "method not in static interface"
  63. method))))
  64. ;; Checked send
  65. (define-syntax (send: stx)
  66. (syntax-parse stx
  67. [(send: obj:expr iface:static-interface method:id . args)
  68. (begin (check-method-in-interface #'send: #'method (attribute iface.value))
  69. (syntax/loc stx
  70. (send (check-object<:interface send: obj iface)
  71. method . args)))]))
  72. (define-syntax (send*: stx)
  73. (syntax-parse stx
  74. [(send*: obj:expr iface:static-interface (method:id . args) ...)
  75. (begin (for ([method (syntax->list #'(method ...))])
  76. (check-method-in-interface #'send*: method (attribute iface.value)))
  77. (syntax/loc stx
  78. (send* (check-object<:interface send*: obj iface)
  79. (method . args) ...)))]))
  80. (define-syntax (send/apply: stx)
  81. (syntax-parse stx
  82. [(send/apply: obj:expr iface:static-interface method:id . args)
  83. (begin (check-method-in-interface #'send/apply: #'method (attribute iface.value))
  84. (syntax/loc stx
  85. (send/apply (check-object<:interface send/apply: obj iface)
  86. method . args)))]))
  87. ;;
  88. ;; check-object<:interface SYNTAX
  89. (define-syntax (check-object<:interface stx)
  90. (syntax-parse stx
  91. [(_ for-whom obj:checked-binding iface:static-interface)
  92. (if (eq? (checked-binding-iface (attribute obj.value)) (attribute iface.value))
  93. #'obj
  94. (syntax/loc stx
  95. (check-object<:interface for-whom
  96. (#%expression obj)
  97. (#%expression iface))))]
  98. [(_ for-whom obj:expr iface:expr)
  99. (begin
  100. (when warn-on-dynamic-object-check-generation?
  101. (printf "dynamic object check: ~s,~s\n"
  102. (syntax-source #'obj)
  103. (syntax-line #'obj)))
  104. #'(dynamic:check-object<:interface 'for-whom obj iface))]))
  105. (define (dynamic:check-object<:interface for-whom obj iface)
  106. (unless (is-a? obj iface)
  107. (error for-whom "interface check failed on: ~e" obj))
  108. obj)
  109. ;;
  110. (define-syntax (define: stx)
  111. (syntax-parse stx
  112. [(_ name:id iface:static-interface expr)
  113. (let ([si (attribute iface.value)])
  114. (with-syntax ([(name-internal) (generate-temporaries #'(name))]
  115. [(method ...) (static-interface-members si)]
  116. [(name.method ...)
  117. (map (lambda (m)
  118. (format-id #'name "~a.~a" (syntax-e #'name) m))
  119. (static-interface-members si))])
  120. #`(begin (define name-internal
  121. (check-object<:interface define: expr iface))
  122. (define-syntax name
  123. (make-checked-binding
  124. #'name-internal
  125. (syntax-local-value #'iface)))
  126. #,(if define-dotted-names
  127. #'(begin
  128. (define-syntax name.method
  129. (syntax-rules ()
  130. [(name.method . args)
  131. (send: name iface method . args)]))
  132. ...)
  133. #'(begin)))))]
  134. [(_ (f:id . args) . body)
  135. #'(define f (lambda: args . body))]))
  136. (define-syntax (lambda: stx)
  137. ;; FIXME: rewrite as stxclass
  138. (define (arg->define stx temp)
  139. (syntax-case stx ()
  140. [(arg : iface)
  141. (and (identifier? #'arg)
  142. (eq? ': (syntax-e #':)))
  143. #`(define: arg iface #,temp)]
  144. [arg
  145. (identifier? #'arg)
  146. #`(define-syntax arg (make-rename-transformer #'#,temp))]))
  147. (syntax-parse stx
  148. [(_ (arg ...) . body)
  149. (let ([temporaries (generate-temporaries #'(arg ...))])
  150. (with-syntax ([(temp ...) temporaries]
  151. [(checked-definition ...)
  152. (map arg->define
  153. (syntax->list #'(arg ...))
  154. temporaries)])
  155. #'(lambda (temp ...)
  156. (let ()
  157. checked-definition ...
  158. (let () . body)))))]))
  159. ;; FIXME: unsafe due to mutation
  160. (define-syntax (init-field: stx)
  161. (syntax-parse stx
  162. [(_ (name:id iface:static-interface . default) ...)
  163. #'(begin (init1: init-field name iface . default) ...)]))
  164. (define-syntax (init: stx)
  165. (syntax-parse stx
  166. [(_ (name:id iface:static-interface . default) ...)
  167. #'(begin (init1: init name iface . default) ...)]))
  168. (define-syntax (init1: stx)
  169. (syntax-parse stx
  170. [(_ init name:id iface:static-interface . default)
  171. (with-syntax ([(name-internal) (generate-temporaries #'(name))])
  172. #'(begin (init ((name-internal name) . default))
  173. (void (check-object<:interface init: name-internal iface))
  174. (define-syntax name
  175. (make-checked-binding
  176. #'name-internal
  177. (syntax-local-value #'iface)))))]))
  178. (define-syntax (init-private stx)
  179. (syntax-parse stx
  180. [(init-private form ...)
  181. #'(begin (init-private1 form) ...)]))
  182. (define-syntax (init-private1 stx)
  183. (syntax-parse stx
  184. [(init-private1 id:id)
  185. (with-syntax ([(id-internal) (generate-temporaries #'(id))])
  186. #'(begin (init (id-internal id))
  187. (define id id-internal)))]))
  188. (define-syntax (init-private: stx)
  189. (syntax-parse stx
  190. [(_ (name:id iface:static-interface) ...)
  191. #'(begin (init-private1: name iface) ...)]))
  192. (define-syntax (init-private1: stx)
  193. (syntax-parse stx
  194. [(_ name:id iface:static-interface)
  195. (with-syntax ([(id-internal) (generate-temporaries #'(id))])
  196. #'(begin (init (id-internal name))
  197. (define: name iface id-internal)))]))