Since each scripts are procedures, the best way to pass variables around is to pass them as arguments to each procedure call. Something like the following should work:
(include! "/path/to/script" var1 var2 ...)
This means we need a way to specify the arguments of the script! Something like:
;; inner script
(:args x y z)
;; ... the rest of the script
What we need then is to lift out the args clause (there should only be one!) during the compilation phase and compile it into part of the procedure signature. Let's see how this would work!
First we need to determine which particular expression is the argument expression, and then we need to filter out the arg expression from each of the script:
;; determining the args
(define (args-exp? term)
(and (pair? term) (equal? (car term) ':args)))
;; get the args expressions
(define (terms->args terms)
(define (helper args)
(cond ((null? args) '()) ;; if none just return null
((not (null? (cdr args))) ;; cannot have more than one
(error 'filter-args "multiple args statement: ~a" args))
(else (cdr (car args)))))
(helper (filter args-exp? terms)))
Then we should push the args expression into the eval statement:
;; terms->exps
(define (terms->exps terms)
(let ((exps (filter (lambda (exp)
(and (not (require-exp? exp))
(not (args-exp? exp))))
terms)))
(if (null? exps)
'("") ;; ensure there is at least one exp in the lambda.
exps)))
;; abstract the eval process
(define (evaluate-terms terms)
(require-modules! terms) ;; first register the required modules
;; then we filter out the required statement and evaluate the rest of the terms as a proc.
(eval `(lambda ,(terms->args terms)
. ,(terms->exps terms))
handler-namespace))
Note that previous we actually have
request
as the first argument, but since we already passed request
via parameter, we no longer need it to take up the spot in the procedure, so we should fix all of the places that calls evaluate-terms
:
(define (eval-script-if-changed! script)
(unless (not (file-exists? (script-path script)))
(let ((timestamp (file-or-directory-modify-seconds (script-path script))))
(when (> timestamp (script-timestamp script))
(set-script-timestamp! script timestamp)
(let ((proc
(evaluate-terms
(file->values (script-path script)))))
(proc))))))
(define-struct shp-handler (path default not-found required)
#:property prop:procedure
(lambda ($struct request)
;; evaluate if
(parameterize (($pathinfo ($pathinfo))
($request request)
($server $struct))
(eval-script-if-changed! (shp-handler-required ($server)))
(let ((proc (evaluate-terms
(file->values
(url->shp-path (request-uri request))))))
(make-response (proc))))))
(define (include! path . args)
(let ((proc (evaluate-terms
(file->values
(segments->path (path->segments path) #f)))))
(apply proc args)))
Notice that in both
shp-handler
and eval-script-if-changed
, the calling of proc takes zero arguments. It means that the required script and other top level scripts should not require any arguments. include!
takes a list of parameters, and it works for regular, optional, and rest parameters.Allright - with the declaration of formal arguments we can now pass variables around! We might enable the parameters to be bound by contracts in the future, but we'll tackle other problems first.
No comments:
Post a Comment