#lang racket
(require (for-syntax syntax/parse)
racket/mpair
"environment.rkt"
"statistics.rkt"
"history.rkt"
"control.rkt")
(define-syntax-rule (with-simulation-environment simulation-environment
body ...)
(parameterize ((current-simulation-environment simulation-environment))
body ...))
(define-syntax-rule (with-new-simulation-environment
body ...)
(parameterize ((current-simulation-environment (make-simulation-environment)))
body ...))
(define-syntax-rule (with-new-child-simulation-environment
body ...)
(parameterize ((current-simulation-environment
(make-simulation-environment (current-simulation-environment))))
body ...))
(define-syntax (define-process-type stx)
(define-syntax-class field
#:attributes (field-id value)
(pattern (field-id:id value:expr))
(pattern field-id:id #:with value #'#f))
(syntax-parse stx
((define-process-type name:id super-name:id
(f:field ...))
(with-syntax ((id
(datum->syntax
(syntax define-process-type)
(string->symbol
(format "process-type:~a" (syntax->datum #'name)))))
(super-id
(datum->syntax
(syntax define-process-type)
(string->symbol
(format "process-type:~a" (syntax->datum #'super-name))))))
#'(begin
(struct name super-name (f.field-id ...) #:mutable)
(define id (process-type-info 'name
super-id
(append
(process-type-info-parameters super-id)
(list 'f.field-id ...))
(append
(process-type-info-inits super-id)
(list f.value ...))
name)))))
((define-process-type name:id
(f:field ...))
(with-syntax ((id (datum->syntax
(syntax define-process-type)
(string->symbol
(format "process-type:~a" (syntax->datum #'name))))))
#'(begin
(struct name process (f.field-id ...) #:mutable)
(define id (process-type-info 'name
#f
(list 'f.field-id ...)
(list f.value ...)
name)))))))
(define-syntax (define-process stx)
(syntax-parse stx
((define-process type:id (name:id . parameters)
. body)
(with-syntax ((type-id
(datum->syntax
(syntax define-process)
(string->symbol
(format "process-type:~a" (syntax->datum #'type)))))
(self
(datum->syntax
(syntax define-process)
'self)))
#'(define name (process-info
'name
type-id
(lambda parameters
(let ((self (current-simulation-process)))
. body))))))
((define-process (name:id . parameters)
. body)
(with-syntax ((self
(datum->syntax
(syntax define-process)
'self)))
#'(define name (process-info
'name
#f
(lambda parameters
(let ((self (current-simulation-process)))
. body))))))))
(define-syntax (define-process* stx)
(define-syntax-class field
#:attributes (field-id value)
(pattern (field-id:id value:expr))
(pattern field-id:id #:with value #'#f))
(syntax-parse stx
((define-process* type:id (name:id . parameters)
(f:field ...)
. body)
(with-syntax ((type-id
(datum->syntax
(syntax define-process-type)
(string->symbol
(format "process-type:~a" (syntax->datum #'type)))))
(make
(datum->syntax
(syntax define-process*)
(string->symbol
(format "make-~a" (syntax->datum #'name)))))
(self
(datum->syntax
(syntax define-process*)
'self)))
#'(begin
(struct name type (f.field-id ...)
#:mutable
#:constructor-name make
#:omit-define-syntaxes)
(define name (process-info
'name
(process-type-info 'name
type-id
(append
(process-type-info-parameters type-id)
(list 'f.field-id ...))
(append
(process-type-info-inits type-id)
(list f.value ...))
make)
(lambda parameters
(let ((self (current-simulation-process)))
. body)))))))
((define-process* (name:id . parameters)
(f:field ...)
. body)
(with-syntax ((make
(datum->syntax
(syntax define-process*)
(string->symbol
(format "make-~a" (syntax->datum #'name)))))
(self
(datum->syntax
(syntax define-process*)
'self)))
#'(begin
(struct name process (f.field-id ...)
#:mutable
#:constructor-name make
#:omit-define-syntaxes)
(define name (process-info
'name
(process-type-info 'name
#f
(list 'f.field-id ...)
(list f.value ...)
make)
(lambda parameters
(let ((self (current-simulation-process)))
. body)))))))))
(define-syntax monitor
(syntax-rules (before after variable-value set-variable-value!)
((monitor before (variable-value variable)
body ...)
(let ((mon (cons
'before
(lambda (variable)
body ...))))
(set-variable-get-monitors!
variable
(cons mon (variable-get-monitors variable)))))
((monitor after (variable-value variable)
body ...)
(let ((mon (cons
'after
(lambda (variable)
body ...))))
(set-variable-get-monitors!
variable
(cons mon (variable-get-monitors variable)))))
((monitor before (set-variable-value! variable value)
body ...)
(let ((mon (cons
'before
(lambda (variable value)
body ...))))
(set-variable-set-monitors!
variable
(cons mon (variable-set-monitors variable)))))
((monitor after (set-variable-value! variable value)
body ...)
(let ((mon (cons
'after
(lambda (variable value)
body ...))))
(set-variable-set-monitors!
variable
(cons mon (variable-set-monitors variable)))))))
(define-syntax accumulate
(syntax-rules (variable-statistics variable-vector-statistics
variable-history variable-vector-history)
((accumulate (variable-statistics variable))
(let ((s (make-statistics #t (current-simulation-time))))
(set-variable-statistics! variable s)))
((accumulate (variable-vector-statistics variable))
(for ((v (in-vector variable)))
(let ((s (make-statistics #t (current-simulation-time))))
(set-variable-statistics! v s))))
((accumulate (variable-history variable))
(let ((h (make-history #t (current-simulation-time))))
(set-variable-history! variable h)))
((accumulate (variable-vector-history variable))
(for ((v (in-vector variable)))
(let ((h (make-history #t (current-simulation-time))))
(set-variable-history! v h))))))
(define-syntax tally
(syntax-rules (variable-statistics variable-vector-statistics
variable-history variable-vector-history)
((tally (variable-statistics variable))
(let ((s (make-statistics #f (current-simulation-time))))
(when (not (eq? (variable-current-value variable) 'uninitialized))
(statistics-tally! s (variable-current-value variable)))
(set-variable-statistics! variable s)))
((tally (variable-vector-statistics variable))
(for ((v (in-vector variable)))
(let ((s (make-statistics #f (current-simulation-time))))
(unless (eq? (variable-current-value v) 'uninitialized)
(statistics-tally! s (variable-current-value v)))
(set-variable-statistics! v s))))
((tally (variable-history variable))
(let ((h (make-history #f (current-simulation-time))))
(unless (eq? (variable-current-value variable) 'uninitialized)
(history-tally! h (variable-current-value variable)))
(set-variable-history! variable h)))
((tally (variable-vector-history variable))
(for ((v (in-vector variable)))
(let ((h (make-history #f (current-simulation-time))))
(unless (eq? (variable-current-value v) 'uninitialized)
(history-tally! h (variable-current-value v)))
(set-variable-history! v h))))))
(define-syntax (schedule stx)
(define-splicing-syntax-class timing
#:attributes (time)
(pattern (~seq #:now)
#:with time #''#:now)
(pattern (~seq (~datum now))
#:with time #''#:now)
(pattern (~seq #:at time:expr))
(pattern (~seq ((~datum at) time:expr)))
(pattern (~seq #:in delta:expr)
#:with time #'(+ delta (current-simulation-time)))
(pattern (~seq ((~datum in) delta:expr))
#:with time #'(+ delta (current-simulation-time)))
(pattern (~seq #:when event:expr)
#:with time #'event)
(pattern (~seq ((~datum when) event:expr))
#:with time #'event)
(pattern (~seq time:expr)))
(syntax-parse stx
((schedule t:timing (function:id . arguments)
(~optional (~seq #:priority priority:expr)
#:defaults ((priority #'0))))
#'(if (process-info? function)
(let ((process (make-process function (list . arguments))))
(set-event-priority! (process-event process) priority)
(schedule-event (process-event process) t.time (current-simulation-environment))
process)
(let ((event (make-event t.time priority #f function (list . arguments))))
(schedule-event event t.time (current-simulation-environment))
event)))))
(define-syntax work/continuously
(syntax-rules (until)
((work/continuously
until condition
body ...)
(let/cc continue
(let ((process (current-simulation-process))
(event (current-simulation-event)))
(set-process-terminating-condition!
process
(lambda () condition))
(set-process-differentiation-function!
process
(lambda () body ...))
(set-process-state! process PROCESS-WORKING-CONTINUOUSLY)
(set-event-function! event continue)
(set-event-arguments! event '())
(schedule-event event '#:continuous))
(current-simulation-event #f)
(current-simulation-state-changed? #t)
((current-simulation-loop-next))))
((work/continuously
body ...)
(work/continuously
until #f
body ...))))
(define-syntax (call stx)
(syntax-parse stx
((call callee:expr (type:id . arguments)
(~optional (~seq #:priority priority:expr)
#:defaults ((priority #'0))))
#'(let* ((event (current-simulation-event))
(process (current-simulation-process)))
(unless process
(error 'call
"call only allowed inside a simulation process"))
(let ((rendezvous-event
(make-event (current-simulation-time)
priority
process
'type
(list . arguments))))
(let/cc continue
(set-event-function! event continue)
(set-event-arguments! event '())
(rendezvous rendezvous-event callee)
(event-list-add! (process-queue callee) rendezvous-event)
((current-simulation-loop-next))))))))
(define-syntax (accept stx)
(syntax-parse stx
((accept caller:expr (type:id . parameters))
#'(let/cc continue
(let ((event (current-simulation-event))
(process (current-simulation-process)))
(unless process
(error 'accept
"accept only allowed inside a simulation process"))
(set-event-function! event continue)
(set-event-arguments! event '())
(let ((acceptors (list (list 'type #f #f))))
(set-process-acceptors! process acceptors)
(mfor-each
(lambda (rendezvous-event)
(rendezvous rendezvous-event process))
(event-list-events (process-queue process))))
((current-simulation-loop-next)))))
((accept caller:expr (type:id . parameters) . body)
#'(let/cc continue
(let ((event (current-simulation-event))
(process (current-simulation-process)))
(unless process
(error 'select
"select only allowed inside a simulation process"))
(set-event-function! event continue)
(set-event-arguments! event '())
(let ((acceptors (list (list 'type
(lambda (caller . parameters)
. body)
#f))))
(set-process-acceptors! process acceptors)
(mfor-each
(lambda (rendezvous-event)
(rendezvous rendezvous-event process))
(event-list-events (process-queue process))))
((current-simulation-loop-next)))))))
(define-syntax (select stx)
(define-splicing-syntax-class timing
#:attributes (time)
(pattern (~seq #:now) #:with time #''#:now)
(pattern (~seq #:at time:expr))
(pattern (~seq #:in delta:expr) #:with time #'(+ delta (current-simulation-time)))
(pattern (~seq #:when event:expr) #:with time #'event))
(define-syntax-class accept-alternative
#:literals (when accept)
#:attributes (when-expr caller type parameters body1 body2)
(pattern ((when when-expr:expr
(accept caller:expr (type:id . parameters)))
. body2)
#:with body1 #'((void)))
(pattern ((when when-expr:expr
(accept caller:expr (type:id . parameters)
. body1))
. body2))
(pattern ((accept caller:expr (type:id . parameters))
. body2)
#:with when-expr #'#t
#:with body1 #'((void)))
(pattern ((accept caller:expr (type:id . parameters)
. body1)
. body2)
#:with when-expr #'#t))
(define-syntax-class call-alternative
#:literals (call)
#:attributes (callee type arguments priority body)
(pattern ((call callee:expr (type:id . arguments)
(~optional (~seq #:priority priority:expr)
#:defaults ((priority #'0)))))
#:with body #'((void)))
(pattern ((call callee:expr (type:id . arguments)
(~optional (~seq #:priority priority:expr)
#:defaults ((priority #'0))))
. body)))
(define-syntax-class else-alternative
#:literals (else)
#:attributes (time body)
(pattern (else t:timing
. body)
#:with time #'t.time))
(syntax-parse stx
((select aa:accept-alternative ...)
#'(let/cc continue
(let ((event (current-simulation-event))
(process (current-simulation-process)))
(set-event-function! event continue)
(set-event-arguments! event '())
(unless process
(error 'accept
"accept only allowed inside a simulation process"))
(let ((acceptors '()))
(when aa.when-expr
(set!
acceptors
(append
acceptors
(list (list 'aa.type
(lambda (aa.caller . aa.parameters) . aa.body1)
(lambda () (begin . aa.body2) (continue)))))))
...
(set-process-acceptors! process acceptors)
(mfor-each
(lambda (rendezvous-event)
(rendezvous rendezvous-event process))
(event-list-events (process-queue process))))
((current-simulation-loop-next)))))
((select aa:accept-alternative ... ea:else-alternative)
#'(begin
(let ((event (current-simulation-event))
(process (current-simulation-process)))
(let/cc continue
(set-event-function! event continue)
(set-event-arguments! event '())
(unless process
(error 'accept
"accept only allowed inside a simulation process"))
(let ((acceptors '()))
(when aa.when-expr
(set!
acceptors
(append
acceptors
(list (list 'aa.type
(lambda (aa.caller . aa.parameters) . aa.body1)
(lambda () (begin . aa.body2) (continue)))))))
...
(set-process-acceptors! process acceptors)
(mfor-each
(lambda (rendezvous-event)
(rendezvous rendezvous-event process))
(event-list-events (process-queue process))))
(schedule-event event ea.time)
((current-simulation-loop-next)))
(unless (null? (process-acceptors process))
(set-process-acceptors! process '())
. ea.body)
)))
((select ca:call-alternative ea:else-alternative)
#'(let ((event (current-simulation-event))
(process (current-simulation-process)))
(unless process
(error 'select
"select only allowed inside a simulation process"))
(let ((rendezvous-event
(make-event (current-simulation-time)
ca.priority
process
'ca.type
(list . ca.arguments))))
(let/cc continue
(set-event-function! event continue)
(set-event-arguments! event '())
(rendezvous rendezvous-event ca.callee)
(event-list-add! (process-queue ca.callee) rendezvous-event)
(schedule-event event ea.time)
((current-simulation-loop-next)))
(if (event-event-list rendezvous-event)
(begin
(event-list-remove! rendezvous-event)
. ea.body)
(begin
. ca.body)))))))
(provide (all-defined-out))