242 lines
7.9 KiB
Racket
242 lines
7.9 KiB
Racket
;;; SPDX-License-Identifier: LGPL-3.0-or-later
|
|
;;; SPDX-FileCopyrightText: Copyright © 2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
|
|
|
#lang racket/base
|
|
|
|
(provide this-turn
|
|
this-facet
|
|
this-actor
|
|
|
|
action
|
|
|
|
entity
|
|
actor-system
|
|
|
|
with-fresh-turn
|
|
ref
|
|
react
|
|
define-field
|
|
stop-facet
|
|
stop-current-facet
|
|
on-start
|
|
on-stop
|
|
sync!
|
|
send!
|
|
spawn
|
|
|
|
begin/dataflow
|
|
define/dataflow
|
|
stop-when-true
|
|
|
|
this-target
|
|
at
|
|
assert
|
|
stop-when
|
|
(rename-out [event:when when])
|
|
during
|
|
during*)
|
|
|
|
(require racket/match)
|
|
(require racket/stxparam)
|
|
(require (for-syntax racket/base))
|
|
(require (for-syntax racket/syntax))
|
|
|
|
(require "actor.rkt")
|
|
(require (prefix-in actor: "actor.rkt"))
|
|
|
|
(require "event-expander.rkt")
|
|
(require "pattern.rkt")
|
|
|
|
(define-syntax-parameter this-turn
|
|
(lambda (stx)
|
|
(raise-syntax-error #f "Illegal use outside an Actor turn" stx)))
|
|
|
|
(define-syntax-rule (with-this-turn id expr ...)
|
|
(syntax-parameterize ([this-turn (make-rename-transformer #'id)])
|
|
expr ...))
|
|
|
|
(define-syntax this-facet
|
|
(syntax-id-rules ()
|
|
[_ (turn-active-facet this-turn)]))
|
|
|
|
(define-syntax this-actor
|
|
(syntax-id-rules ()
|
|
[_ (facet-actor this-facet)]))
|
|
|
|
(define-syntax-rule (action formals expr ...)
|
|
(lambda (turn . formals)
|
|
(with-this-turn turn expr ...)))
|
|
|
|
(define-syntax actor-system
|
|
(syntax-rules ()
|
|
[(_ #:name name expr ...)
|
|
(actor:actor-system #:name name (action () expr ...))]
|
|
[(_ expr ...)
|
|
(actor:actor-system (action () expr ...))]))
|
|
|
|
(define-syntax-rule (with-fresh-turn expr ...)
|
|
(turn-freshen this-turn (action () expr ...)))
|
|
|
|
(define-syntax-rule (ref e)
|
|
(turn-ref this-turn e))
|
|
|
|
(define-syntax-rule (react setup-expr ...)
|
|
(turn-facet! this-turn (action () setup-expr ...)))
|
|
|
|
(define-syntax-rule (define-field id initial-value)
|
|
(define id (turn-field! this-turn 'id initial-value)))
|
|
|
|
(define-syntax stop-facet
|
|
(syntax-rules ()
|
|
[(_ f) (turn-stop! this-turn f)]
|
|
[(_ f expr ...) (turn-stop! this-turn f (action () expr ...))]))
|
|
|
|
(define-syntax-rule (stop-current-facet expr ...)
|
|
(stop-facet this-facet expr ...))
|
|
|
|
(define-syntax-rule (on-start expr ...)
|
|
;; TODO: delay to end of turn (?)
|
|
(begin expr ...))
|
|
|
|
(define-syntax-rule (on-stop expr ...)
|
|
(facet-on-stop! this-facet (action () expr ...)))
|
|
|
|
(define-syntax-rule (sync! peer expr ...)
|
|
(turn-sync! this-turn peer (action (_reply) expr ...)))
|
|
|
|
(define-syntax-rule (send! peer assertion)
|
|
(turn-message! this-turn peer (:template assertion)))
|
|
|
|
(define-syntax spawn
|
|
(syntax-rules ()
|
|
[(_ #:name name setup-expr ...)
|
|
(turn-spawn! #:name name this-turn (action () setup-expr ...))]
|
|
[(_ setup-expr ...)
|
|
(turn-spawn! this-turn (action () setup-expr ...))]))
|
|
|
|
(define-syntax-rule (begin/dataflow expr ...)
|
|
(turn-dataflow! this-turn (action () expr ...)))
|
|
|
|
(define-syntax-rule (define/dataflow id expr)
|
|
(begin (define-field id #f)
|
|
(begin/dataflow (id expr))))
|
|
|
|
(define-syntax-rule (stop-when-true test expr ...)
|
|
(begin/dataflow
|
|
(when test
|
|
(stop-current-facet expr ...))))
|
|
|
|
;;---------------------------------------------------------------------------
|
|
|
|
(define-for-syntax orig-insp
|
|
(variable-reference->module-declaration-inspector (#%variable-reference)))
|
|
|
|
(define-syntax-parameter this-target
|
|
(lambda (stx)
|
|
(raise-syntax-error #f "Illegal use outside an Actor turn" stx)))
|
|
|
|
(define-syntax (at stx)
|
|
(syntax-case stx ()
|
|
[(_ target-expr items ...)
|
|
#`(let ((target target-expr))
|
|
(syntax-parameterize ([this-target (make-rename-transformer #'target)])
|
|
#,@(for/list [(item-stx (in-list (syntax->list #'(items ...))))]
|
|
(let loop ((item-stx item-stx))
|
|
(define disarmed-item-stx (syntax-disarm item-stx orig-insp))
|
|
(syntax-case disarmed-item-stx ()
|
|
[(expander args ...)
|
|
(event-expander-id? #'expander)
|
|
(event-expander-transform disarmed-item-stx
|
|
(lambda (r) (loop (syntax-rearm r item-stx))))]
|
|
[_
|
|
item-stx])))))]))
|
|
|
|
(define-event-expander assert
|
|
(syntax-rules ()
|
|
[(_ expr)
|
|
(turn-assert/dataflow! this-turn this-target (action () (:template expr)))]))
|
|
|
|
(define-event-expander stop-when
|
|
(syntax-rules ()
|
|
[(_ event expr ...)
|
|
(event:when event (stop-current-facet expr ...))]))
|
|
|
|
(require "schemas/gen/dataspace.rkt")
|
|
|
|
(define-event-expander event:when
|
|
(lambda (stx)
|
|
(syntax-case stx (message asserted retracted)
|
|
[(_ (message pat) expr ...)
|
|
#`(assert (Observe (:pattern pat)
|
|
(ref (entity #:message
|
|
(action (bindings)
|
|
(match-define (list #,@(analyse-pattern-bindings #'pat)) bindings)
|
|
expr ...)))))]
|
|
[(_ (asserted pat) expr ...)
|
|
#`(assert (Observe (:pattern pat)
|
|
(ref (entity #:assert
|
|
(action (bindings _handle)
|
|
(match-define (list #,@(analyse-pattern-bindings #'pat)) bindings)
|
|
expr ...)))))]
|
|
[(_ (retracted pat) expr ...)
|
|
#`(assert (Observe (:pattern pat)
|
|
(let ((assertion-map (make-hash)))
|
|
(ref (entity #:assert
|
|
(action (bindings handle)
|
|
(hash-set! assertion-map handle bindings))
|
|
#:retract
|
|
(action (handle)
|
|
(match-define (list #,@(analyse-pattern-bindings #'pat))
|
|
(hash-ref assertion-map handle))
|
|
(hash-remove! assertion-map handle)
|
|
expr ...))))))]))
|
|
(syntax-rules ()
|
|
[(_ test expr ...)
|
|
(when test expr ...)]))
|
|
|
|
(define-event-expander during
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
[(_ pat expr ...)
|
|
#`(assert (Observe (:pattern pat)
|
|
(ref (during* (action (bindings)
|
|
(match-define (list #,@(analyse-pattern-bindings #'pat)) bindings)
|
|
expr ...)))))])))
|
|
|
|
(define (during* f #:name [name '?])
|
|
(define assertion-map (make-hash))
|
|
(entity #:name name
|
|
#:assert
|
|
(action (value handle)
|
|
(let ((facet (react (facet-prevent-inert-check! this-facet)
|
|
(f this-turn value))))
|
|
(match (hash-ref assertion-map handle #f)
|
|
[#f
|
|
(hash-set! assertion-map handle facet)]
|
|
['dead
|
|
(hash-remove! assertion-map handle)
|
|
(stop-facet facet)]
|
|
[_
|
|
(error 'during "Duplicate assertion handle ~a" handle)])))
|
|
#:retract
|
|
(action (handle)
|
|
(match (hash-ref assertion-map handle #f)
|
|
[#f
|
|
(hash-set! assertion-map handle 'dead)]
|
|
['dead
|
|
(error 'during "Duplicate retraction handle ~a" handle)]
|
|
[facet
|
|
(hash-remove! assertion-map handle)
|
|
(stop-facet facet)]))))
|
|
|
|
;;---------------------------------------------------------------------------
|
|
;;; Local Variables:
|
|
;;; eval: (put 'action 'racket-indent-function 1)
|
|
;;; eval: (put 'actor-system/dataspace 'racket-indent-function 1)
|
|
;;; eval: (put 'at 'racket-indent-function 1)
|
|
;;; eval: (put 'react 'racket-indent-function 0)
|
|
;;; eval: (put 'spawn 'racket-indent-function 0)
|
|
;;; eval: (put 'stop-when 'racket-indent-function 1)
|
|
;;; eval: (put 'stop-when-true 'racket-indent-function 1)
|
|
;;; End:
|