diff --git a/racket/info.rkt b/racket/info.rkt index 2dd3de9..5b60271 100644 --- a/racket/info.rkt +++ b/racket/info.rkt @@ -14,6 +14,7 @@ "pict-lib" "sgl" "struct-defaults" + "auxiliary-macro-context" )) (define build-deps '("racket-doc" "scribble-lib")) diff --git a/racket/syndicate/actor.rkt b/racket/syndicate/actor.rkt index 6f051b1..b4fb3f1 100644 --- a/racket/syndicate/actor.rkt +++ b/racket/syndicate/actor.rkt @@ -565,6 +565,30 @@ (define-syntax-rule (define/query-hash id P x ...) (define id (query-hash id P x ...))) (define-syntax-rule (define/query-hash-set id P x ...) (define id (query-hash-set id P x ...))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(require auxiliary-macro-context) + +(define-auxiliary-macro-context + #:context-name event-expander + #:prop-name prop:event-expander + #:prop-predicate-name event-expander? + #:prop-accessor-name event-expander-proc + #:macro-definer-name define-event-expander + #:introducer-parameter-name current-event-expander-introducer + #:local-introduce-name syntax-local-event-expander-introduce + #:expander-id-predicate-name event-expander-id? + #:expander-transform-name event-expander-transform) + +(provide (for-syntax + prop:event-expander + event-expander? + event-expander-proc + syntax-local-event-expander-introduce + event-expander-id? + event-expander-transform) + define-event-expander) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Syntax-time support @@ -637,9 +661,23 @@ #`(for [(entry (in-set entry-set))] #,entry-handler-stx)))]))))) -(define-for-syntax (analyze-event outer-expr-stx event-stx terminal? script-stx priority-stx) +(define-for-syntax orig-insp + (variable-reference->module-declaration-inspector (#%variable-reference))) + +(define-for-syntax (analyze-event outer-expr-stx armed-event-stx terminal? script-stx priority-stx) + (define event-stx (syntax-disarm armed-event-stx orig-insp)) (syntax-parse event-stx #:literals [core:message asserted retracted rising-edge] + [(expander args ...) + #:when (event-expander-id? #'expander) + (event-expander-transform + event-stx + (lambda (result) + (analyze-event outer-expr-stx + (syntax-rearm result event-stx) + terminal? + script-stx + priority-stx)))] [(core:message P L:meta-level) (define-values (proj pat bindings _instantiated) (analyze-pattern event-stx #'P)) diff --git a/racket/syndicate/drivers/web.rkt b/racket/syndicate/drivers/web.rkt index 57f2d71..e399270 100644 --- a/racket/syndicate/drivers/web.rkt +++ b/racket/syndicate/drivers/web.rkt @@ -18,6 +18,14 @@ (struct-out web-response-chunk) (struct-out websocket-message) + web-request-incoming + web-request-get + websocket-connection-closed + websocket-message-recv + websocket-message-send! + web-respond/bytes! + web-respond/xexpr! + spawn-web-driver) (require net/url) @@ -41,6 +49,7 @@ (require (only-in web-server/private/util lowercase-symbol!)) (require web-server/dispatchers/dispatch) (require struct-defaults) +(require xml) (require/activate "timer.rkt") @@ -79,6 +88,45 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(define-event-expander web-request-incoming + (syntax-rules () + [(_ (id req) vh method path) + (message (web-request ($ id _) + 'inbound + ($ req (web-request-header method (web-resource vh `path) _ _)) + _))])) + +(define-event-expander web-request-get + (syntax-rules () + [(_ (id req) vh path) + (web-request-incoming (id req) vh 'get path)])) + +(define-event-expander websocket-connection-closed + (syntax-rules () + [(_ id) + (retracted (observe (websocket-message id 'outbound _)))])) + +(define-event-expander websocket-message-recv + (syntax-rules () + [(_ id str) + (message (websocket-message id 'inbound str))])) + +(define (websocket-message-send! id str) + (send! (websocket-message id 'outbound str))) + +(define (web-respond/bytes! id #:header [header (make-web-response-header)] body-bytes) + (send! (web-response-complete id header body-bytes))) + +(define (web-respond/xexpr! id + #:header [header (make-web-response-header)] + #:preamble [preamble #""] + body-xexpr) + (web-respond/bytes! id #:header header + (bytes-append preamble + (string->bytes/utf-8 (xexpr->string body-xexpr))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + (define web-server-max-waiting (make-parameter 511)) ;; sockets (define web-server-connection-manager (make-parameter #f)) (define web-server-initial-connection-timeout (make-parameter 30)) ;; seconds @@ -141,11 +189,9 @@ (stop-when (message (timer-expired (list 'web-req id) _)) (do-response-complete control-ch id - (web-response-header 404 - #"Not found" - (current-seconds) - #"text/plain" - '()) + (make-web-response-header + #:code 404 + #:message #"Not found") '())) (stop-when (message (web-response-complete id $rh $body)) (do-response-complete control-ch id rh body)) diff --git a/racket/syndicate/examples/actor/web-demo.rkt b/racket/syndicate/examples/actor/web-demo.rkt new file mode 100644 index 0000000..23dc152 --- /dev/null +++ b/racket/syndicate/examples/actor/web-demo.rkt @@ -0,0 +1,34 @@ +#lang syndicate/actor +;; Simple demo of web driver. See web-demo.rkt for a more realistic example. + +(require/activate syndicate/drivers/timer) +(require/activate syndicate/drivers/web) +(require net/url) + +(actor #:name 'server + (react + (define vh (web-virtual-host "http" ? 9090)) + + (assert vh) + + (on (web-request-incoming (id req) vh _ ("ws" ())) + (actor + (react + (assert (web-response-websocket id)) + (stop-when (websocket-connection-closed id) (log-info "Connection dropped")) + (stop-when (websocket-message-recv id "quit") (log-info "Received quit command")) + (on (websocket-message-recv id $str) + (log-info "Got ~v" str) + (websocket-message-send! id str))))) + + (field [counter 0]) + (on (web-request-get (id req) vh ("foo" ,$path)) + (define req-num (counter)) + (counter (+ (counter) 1)) + (web-respond/xexpr! id + `(html + (body + (h1 "Hi there.") + (p ,(format "Your path was ~v, and this is request ~a" + path + req-num)))))))) diff --git a/racket/syndicate/pattern.rkt b/racket/syndicate/pattern.rkt index cd7efef..baa7ae9 100644 --- a/racket/syndicate/pattern.rkt +++ b/racket/syndicate/pattern.rkt @@ -16,6 +16,30 @@ (require "treap.rkt") (require "core.rkt") +(require auxiliary-macro-context) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define-auxiliary-macro-context + #:context-name assertion-expander + #:prop-name prop:assertion-expander + #:prop-predicate-name assertion-expander? + #:prop-accessor-name assertion-expander-proc + #:macro-definer-name define-assertion-expander + #:introducer-parameter-name current-assertion-expander-introducer + #:local-introduce-name syntax-local-assertion-expander-introduce + #:expander-id-predicate-name assertion-expander-id? + #:expander-transform-name assertion-expander-transform) + +(provide (for-syntax + prop:assertion-expander + assertion-expander? + assertion-expander-proc + syntax-local-assertion-expander-introduce + assertion-expander-id? + assertion-expander-transform) + define-assertion-expander) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (struct predicate-match (predicate sub-pattern) #:transparent) @@ -29,8 +53,7 @@ [(_ (capture sub)) (match (walk v sub '()) [#f #f] - ['() (cons v captures-rev)] - [_ (error 'match-value/captures "Bindings in capture sub-patterns not supported")])] + [nested-captures-rev (append nested-captures-rev (list v) captures-rev)])] [(_ (predicate-match pred? sub)) #:when (pred? v) (walk v sub captures-rev)] [((== ?) _) @@ -60,6 +83,9 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(define-for-syntax orig-insp + (variable-reference->module-declaration-inspector (#%variable-reference))) + (begin-for-syntax (define (dollar-id? stx) (and (identifier? stx) @@ -69,9 +95,16 @@ (and (dollar-id? stx) (datum->syntax stx (string->symbol (substring (symbol->string (syntax-e stx)) 1))))) + (define (is-message-pattern? outer-expr-stx) + (syntax-parse outer-expr-stx + #:literals [message] + [(message _ ...) '#t] + [_ #f])) + ;; Syntax -> (Values Projection AssertionSetPattern (ListOf Identifier) Syntax) (define (analyze-pattern outer-expr-stx pat-stx0) - (let walk ((pat-stx pat-stx0)) + (let walk ((armed-pat-stx pat-stx0)) + (define pat-stx (syntax-disarm armed-pat-stx orig-insp)) (syntax-case pat-stx ($ ? quasiquote unquote quote) ;; Extremely limited support for quasiquoting and quoting [(quasiquote (unquote p)) (walk #'p)] @@ -90,29 +123,34 @@ [($ v p) (let () (define-values (pr g bs _ins) (walk #'p)) - (when (not (null? bs)) - (raise-syntax-error #f "nested bindings not supported" outer-expr-stx pat-stx)) + (when (and (not (null? bs)) (not (is-message-pattern? outer-expr-stx))) + (raise-syntax-error #f + "Nested bindings only supported in message events" + outer-expr-stx + pat-stx)) (values #`(?! #,pr) g - (list #'v) + (cons #'v bs) #'v))] [(? pred? p) ;; TODO: support pred? in asserted/retracted as well as message events (let () - (syntax-parse outer-expr-stx - #:literals [message] - [(message _ ...) 'ok] - [_ (raise-syntax-error #f - "Predicate '?' matching only supported in message events" - outer-expr-stx - pat-stx)]) + (when (not (is-message-pattern? outer-expr-stx)) + (raise-syntax-error #f + "Predicate '?' matching only supported in message events" + outer-expr-stx + pat-stx)) (define-values (pr g bs ins) (walk #'p)) (values #`(predicate-match pred? #,pr) g bs ins))] + [(expander args ...) + (assertion-expander-id? #'expander) + (assertion-expander-transform pat-stx (lambda (r) (walk (syntax-rearm r pat-stx))))] + [(ctor p ...) (let () (define parts (if (identifier? #'ctor) #'(p ...) #'(ctor p ...)))