#lang scheme/base
(require (for-syntax scheme/base
scheme/match
scheme/pretty
scheme/provide-transform
srfi/26/cut
(planet untyped/unlib:3/syntax)
"base.ss"
"site-info.ss")
scheme/pretty
"base.ss"
"site-info.ss"
"struct.ss")
(define-for-syntax (parse-site-definition original-stx)
(define site-id-stx #f)
(define controller-id-stxs null)
(define rule-stxs null)
(define rule-not-found-stx #f)
(define (resolve-controller-id! id)
(or (ormap (lambda (id2)
(if (eq? (syntax->datum id) (syntax->datum id2)) id2 #f))
controller-id-stxs)
(begin (set! controller-id-stxs (cons id controller-id-stxs))
id)))
(define (parse-at-rules dispatch-stx args-stxs)
(for-each parse-at-rule (syntax->list dispatch-stx))
(parse-at-site-keywords args-stxs))
(define (parse-at-rule stx)
(syntax-case* stx (url) symbolic-identifier=?
[((url arg ...) controller)
(identifier? #'controller)
(set! rule-stxs (cons #`(make-rule (make-pattern arg ...) #,(resolve-controller-id! #'controller)) rule-stxs))]))
(define (parse-at-site-keywords args-stxs)
(match args-stxs
[(list) (void)]
[(list-rest kw-stx value-stx args-stxs)
(match (syntax->datum kw-stx)
['#:rule-not-found (set! rule-not-found-stx value-stx)]
['#:other-controllers (for-each resolve-controller-id! (syntax->list value-stx))])
(parse-at-site-keywords args-stxs)])
(parse-at-end))
(define (parse-at-end)
(with-syntax ([site site-id-stx]
[(controller ...) (reverse controller-id-stxs)]
[(rule ...) (reverse rule-stxs)])
(site-info-set! #'site (syntax->list #'(controller ...)))
(values (quasisyntax/loc original-stx
(define-values (site controller ...)
(let-values ([(site controllers) (make-site 'site '(controller ...))])
(let-values ([(controller ...) (apply values controllers)])
(set-site-rules! site (list rule ...))
#,@(if rule-not-found-stx
(list #`(set-site-rule-not-found! site #,rule-not-found-stx))
null)
(values site controller ...)))))
(cons #'site (syntax->list #'(controller ...))))))
(syntax-case original-stx ()
[(_ site-id rules arg ...)
(identifier? #'site-id)
(begin (set! site-id-stx #'site-id)
(parse-at-rules #'rules (syntax->list #'(arg ...))))]))
(define-syntax (define-site stx)
(define-values (definition-stx id-stxs)
(parse-site-definition stx))
#`(begin #,definition-stx))
(define-syntax (define/provide-site stx)
(define-values (definition-stx id-stxs)
(parse-site-definition stx))
#`(begin (begin #,definition-stx (provide #,@id-stxs))))
(define-syntax site-out
(make-provide-transformer
(lambda (stx modes)
(define (create-export id-stx)
(make-export id-stx (syntax->datum id-stx) 0 #f id-stx))
(syntax-case stx ()
[(_ id)
(let ([controllers (site-info-controller-ids (site-info-ref #'id (cut raise-syntax-error #f "No such site." stx #'id)))])
(map create-export (cons #'id controllers)))]))))
(define-syntax (define-controller stx)
(syntax-case stx ()
[(_ (id arg ...) expr ...)
(with-syntax ([pipeline-id (make-id #'id #'id '-controller-pipeline)]
[body-id (make-id #'id #'id '-controller-body)])
#'(define _
(if (controller-defined? id)
(raise-exn exn:fail:dispatch
(format "Controller ~a has already been defined." 'id))
(let ([pipeline-id null] [body-id (lambda (arg ...) expr ...)])
(set-controller-pipeline! id pipeline-id)
(set-controller-body! id body-id)))))]
[(_ id pipeline body)
(with-syntax ([pipeline-id (make-id #'id #'id '-controller-pipeline)]
[body-id (make-id #'id #'id '-controller-body)])
#'(define _
(if (controller-defined? id)
(raise-exn exn:fail:dispatch
(format "Controller ~a has already been defined." 'id))
(let ([pipeline-id pipeline] [body-id body])
(set-controller-pipeline! id pipeline-id)
(set-controller-body! id body-id)))))]))
(provide define-site
define/provide-site
define-controller
site-out)