First running HLL program!

This commit is contained in:
Tony Garnock-Jones 2015-12-11 15:23:32 +13:00
parent 29042830e2
commit 3b161ef573
2 changed files with 182 additions and 48 deletions

View File

@ -11,17 +11,22 @@
assert!
retract!
send!
quit!
return!
return/no-link-result!
;; forall
%%boot
;;----------------------------------------
(struct-out actor-state)
pretty-print-actor-state
)
(require (for-syntax racket/base))
(require (for-syntax racket/sequence))
(require "support/dsl.rkt")
(require "pretty.rkt")
(define&provide-dsl-helper-syntaxes "state/until/forever form"
[on
@ -44,7 +49,8 @@
(require racket/set)
(require racket/match)
(require "core.rkt")
(require (except-in "core.rkt" assert)
(rename-in "core.rkt" [assert core:assert]))
(require "route.rkt")
(require "mux.rkt")
@ -64,7 +70,7 @@
;; An Instruction is one of
;; - (patch-instruction Patch (Void -> Instruction))
;; - (action-instruction Action (Void -> Instruction))
;; - (quit-instruction (Option Exn) (Listof Any))
;; - (return-instruction (Option (Listof Any)))
;; - (spawn-instruction LinkageKind (Symbol Symbol -> Spawn) (Void -> Instruction))
;; - (script-complete-instruction Variables)
;; and represents a side-effect for an actor to take in its
@ -77,7 +83,7 @@
;;
;; Patch Instructions are issued when the actor uses `assert!` and
;; `retract!`. Action instructions are issued when the actor uses
;; `do!`, and quit instructions when `quit!` is called.
;; `do!`, and return instructions when `return!` is called.
;; Script-complete instructions are automatically issued when a Script
;; terminates successfully.
;;
@ -95,7 +101,7 @@
;;
(struct patch-instruction (patch k) #:transparent)
(struct action-instruction (action k) #:transparent)
(struct quit-instruction (maybe-exn result-values) #:transparent)
(struct return-instruction (result-values) #:transparent)
(struct spawn-instruction (linkage-kind action-fn k) #:transparent)
(struct script-complete-instruction (variables) #:transparent)
@ -110,7 +116,10 @@
pending-patch ;; (Option Patch) - aggregate patch being accumulated
mux ;; Mux
)
#:prefab)
#:transparent
#:methods gen:prospect-pretty-printable
[(define (prospect-pretty-print s [p (current-output-port)])
(pretty-print-actor-state s p))])
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Linkage protocol
@ -149,7 +158,10 @@
(define (reply-to k)
(lambda reply-values
(call-with-continuation-prompt (lambda ()
(with-handlers [((lambda (e) #t)
(apply k reply-values)
(error 'reply-to "Script returned directly")
;; TODO: v
#;(with-handlers [((lambda (e) #t)
(lambda (exn)
(call-in-raw-context/abort
(lambda () (quit-instruction exn '())))))]
@ -168,33 +180,44 @@
prompt))
;; Returns void
(define (assert! P)
(define (assert! P #:meta-level [meta-level 0])
(call-in-raw-context
(lambda (k) (patch-instruction (patch (pattern->matcher #t P) (matcher-empty)) k))))
(lambda (k) (patch-instruction (core:assert P #:meta-level meta-level) k))))
;; Returns void
(define (retract! P)
(define (retract! P #:meta-level [meta-level 0])
(call-in-raw-context
(lambda (k) (patch-instruction (patch (matcher-empty) (pattern->matcher #t P)) k))))
(lambda (k) (patch-instruction (retract P #:meta-level meta-level) k))))
;; Returns void
(define (send! M) (do! (message M)))
(define (send! M #:meta-level [meta-level 0])
(do! (message (prepend-at-meta M meta-level))))
;; Returns void
(define (do! A)
(call-in-raw-context
(lambda (k) (action-instruction A k))))
;; Does not return
(define (quit! #:exception [maybe-exn #f] . result-values)
;; Does not return to caller; instead, terminates the current actor
;; after sending a link-result to the calling actor.
(define (return! . result-values)
(call-in-raw-context/abort
(lambda () (quit-instruction maybe-exn result-values))))
(lambda () (return-instruction result-values))))
;; Does not return to caller; instead, terminates the current actor
;; without sending a link-result to the calling actor.
(define (return/no-link-result!)
(call-in-raw-context/abort
(lambda () (return-instruction #f))))
;; Returns new variables, plus values from spawned actor if any.
(define (spawn! linkage-kind action-fn)
(call-in-raw-context
(lambda (k) (spawn-instruction linkage-kind action-fn k))))
;; TODO: syntax-classes for #:init and #:collect; then use these in state, until, forever etc.
;; TODO: syntax-class for #:meta-level and use it everywhere
;; Syntax for spawning a 'call-linked actor.
(define-syntax (state stx)
(syntax-parse stx
@ -225,7 +248,7 @@
(define-syntax (actor stx)
(syntax-parse stx
[(_ I ...)
(expand-state 'actor #'(I ... (quit!)) #'() #'() #'() #'())]))
(expand-state 'actor #'(I ... (return/no-link-result!)) #'() #'() #'() #'())]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Main behavior of HLL actors
@ -295,6 +318,14 @@
(script-complete-instruction new-variables)))))
(void))))
(define (%%boot thunk)
(match ((reply-to (lambda (dummy)
(thunk)
(error '%%boot "Reached end of boot thunk")))
(void))
[(spawn-instruction 'actor action-fn _get-next-instr)
(action-fn (gensym 'root-actor) (gensym 'boot-actor))]))
;; Transition Instruction -> Transition
(define (handle-actor-syscall t instr)
(match instr
@ -307,18 +338,22 @@
perform-pending-patch
(lambda (s) (transition s a)))
(get-next-instr (void)))]
[(quit-instruction maybe-exn result-values)
(log-error "Ignoring result-values: ~v" result-values)
[(return-instruction result-values)
(sequence-transitions t
perform-pending-patch
(lambda (s) (quit #:exception maybe-exn)))]
(lambda (s)
(if result-values
(quit (message (link-result (actor-state-caller-id s)
(actor-state-self-id s)
result-values)))
(quit))))]
[(spawn-instruction linkage-kind action-fn get-next-instr)
(define blocking? (eq? linkage-kind 'call))
(define next-t
(sequence-transitions t
perform-pending-patch
(lambda (s)
(define callee-id (gensym 'actor))
(define callee-id (gensym linkage-kind))
(transition (if blocking?
(store-continuation s callee-id get-next-instr)
s)
@ -340,6 +375,11 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Compilation of HLL actors
;; TODO: track
;; TODO: don't store aggregates for ongoings; instead, use the record of their interests in the mux
;; TODO: clean way of spawning low-level actors from hll
;; TODO: default to hll
(begin-for-syntax
(define (expand-state linkage-kind init-actions binding-names binding-inits ongoings edges)
;; ----------------------------------------
@ -378,15 +418,22 @@
(define (mapply v fs) (map (lambda (f) (f v)) fs))
(define (make-run-script-call state-stx I-stxs)
#`(run-script #,state-stx (match-lambda
[(vector #,@binding-names)
(call-with-values (lambda () #,@I-stxs) vector)])))
(if (zero? binding-count)
#`(run-script #,state-stx (match-lambda
[(vector)
#,@I-stxs
(vector)]))
#`(run-script #,state-stx (match-lambda
[(vector #,@binding-names)
(call-with-values (lambda () #,@I-stxs)
vector)]))))
(define (add-assertion-maintainer! endpoint-index
retract-stx
assert-stx
pat-stx
maybe-Pred-stx)
maybe-Pred-stx
L-stx)
(define aggregate-index (allocate-aggregate! #'(matcher-empty)))
(box-adjoin! assertion-maintainers
(lambda (evt-stx)
@ -400,20 +447,23 @@
(matcher-empty))
#`(pattern->matcher #t #,pat-stx)))
(and (not (eq? old-assertions new-assertions))
((extend-pending-patch #,endpoint-index
(patch-seq (#,retract-stx old-assertions)
(#,assert-stx new-assertions)))
((extend-pending-patch
#,endpoint-index
(patch-seq (#,retract-stx (embedded-matcher old-assertions)
#:meta-level #,L-stx)
(#,assert-stx (embedded-matcher new-assertions)
#:meta-level #,L-stx)))
(struct-copy actor-state s
[aggregates (hash-set (actor-state-aggregates s)
#,aggregate-index
new-assertions)])))))))
(define (analyze-asserted-or-retracted! endpoint-index asserted? P-stx I-stxs)
(define (analyze-asserted-or-retracted! endpoint-index asserted? P-stx I-stxs L-stx)
(define-values (proj-stx pat match-pat bindings) (analyze-pattern P-stx))
(add-assertion-maintainer! endpoint-index #'unsub #'sub pat #f)
(add-assertion-maintainer! endpoint-index #'unsub #'sub pat #f L-stx)
(add-event-handler!
(lambda (evt-stx)
#`(let ((proj #,proj-stx))
#`(let ((proj (compile-projection (prepend-at-meta #,proj-stx #,L-stx))))
(lambda (s)
(match #,evt-stx
[(? #,(if asserted? #'patch/added? #'patch/removed?) p)
@ -428,20 +478,31 @@
(lambda (s) #,(make-run-script-call #'s I-stxs))))]
[_ #f]))))))
(define (prepend-at-meta-stx stx level)
(if (zero? level)
stx
#`(at-meta #,(prepend-at-meta-stx stx (- level 1)))))
(define (analyze-message-subscription! endpoint-index P-stx I-stxs L-stx)
(define-values (proj pat match-pat bindings) (analyze-pattern P-stx))
(add-assertion-maintainer! endpoint-index #'unsub #'sub pat #f L-stx)
(add-event-handler!
(lambda (evt-stx)
#`(lambda (s)
(match #,evt-stx
[(message #,(prepend-at-meta-stx match-pat (syntax-e L-stx)))
#,(make-run-script-call #'s I-stxs)]
[_ #f])))))
(define (analyze-event! index E-stx I-stxs)
(syntax-parse E-stx
#:literals [asserted retracted message rising-edge]
[(asserted P) (analyze-asserted-or-retracted! index #t #'P I-stxs)]
[(retracted P) (analyze-asserted-or-retracted! index #f #'P I-stxs)]
[(message P)
(define-values (proj pat match-pat bindings) (analyze-pattern #'P))
(add-assertion-maintainer! index #'unsub #'sub pat #f)
(add-event-handler!
(lambda (evt-stx)
#`(lambda (s)
(match #,evt-stx
[(message #,match-pat) #,(make-run-script-call #'s I-stxs)]
[_ #f]))))]
[(asserted P #:meta-level L) (analyze-asserted-or-retracted! index #t #'P I-stxs #'L)]
[(asserted P) (analyze-asserted-or-retracted! index #t #'P I-stxs #'0)]
[(retracted P #:meta-level L) (analyze-asserted-or-retracted! index #f #'P I-stxs #'L)]
[(retracted P) (analyze-asserted-or-retracted! index #f #'P I-stxs #'0)]
[(message P #:meta-level L) (analyze-message-subscription! index #'P I-stxs #'L)]
[(message P) (analyze-message-subscription! index #'P I-stxs #'0)]
[(rising-edge Pred)
;; TODO: more kinds of Pred than just expr
(define aggregate-index (allocate-aggregate! #'#f))
@ -460,9 +521,9 @@
#,(make-run-script-call #'s I-stxs)
(transition s '())))))))]))
(define (analyze-assertion! index Pred-stx P-stx)
(define (analyze-assertion! index Pred-stx P-stx L-stx)
(define-values (proj pat match-pat bindings) (analyze-pattern P-stx))
(add-assertion-maintainer! index #'retract #'assert pat Pred-stx))
(add-assertion-maintainer! index #'retract #'core:assert pat Pred-stx L-stx))
(define (analyze-tracks! index track-spec-stxs I-stxs)
(error 'analyze-tracks! "unimplemented"))
@ -485,10 +546,14 @@
#:literals [on assert track]
[(on E I ...)
(analyze-event! ongoing-index #'E #'(I ...))]
[(assert #:when Pred P #:meta-level L)
(analyze-assertion! ongoing-index #'Pred #'P #'L)]
[(assert #:when Pred P)
(analyze-assertion! ongoing-index #'Pred #'P)]
(analyze-assertion! ongoing-index #'Pred #'P #'0)]
[(assert P #:meta-level L)
(analyze-assertion! ongoing-index #'#t #'P #'L)]
[(assert P)
(analyze-assertion! ongoing-index #'#t #'P)]
(analyze-assertion! ongoing-index #'#t #'P #'0)]
[(track [track-spec ...] I ...)
(void)]))
@ -497,7 +562,7 @@
(edge-index (in-naturals (length (syntax->list ongoings))))]
(syntax-parse edge
[(E I ...)
(analyze-event! edge-index #'E #'((call-with-values (lambda () I ...) quit!)))]))
(analyze-event! edge-index #'E #'((call-with-values (lambda () I ...) return!)))]))
;; ...and generic linkage-related behaviors.
(add-event-handler!
@ -508,7 +573,6 @@
#`(lambda (self-id caller-id)
(<spawn>
(lambda ()
;; ActorState -> Transition
(define ((maintain-assertions e) s)
(sequence-transitions0 s #,@(mapply #'e (unbox assertion-maintainers))))
@ -539,7 +603,7 @@
(define initial-subs
#,(if (eq? linkage-kind 'call)
#`(patch-seq sub-to-callees
(assert (link-active caller-id self-id)))
(core:assert (link-active caller-id self-id)))
#`sub-to-callees))
((extend-pending-patch *linkage-label* initial-subs) s))
@ -681,6 +745,35 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (pretty-print-actor-state s [p (current-output-port)])
(match-define
(actor-state continuation-table caller-id self-id variables aggregates pending-patch mux)
s)
(fprintf p "ACTOR id ~a (caller-id ~a):\n" self-id caller-id)
(fprintf p " - ~a pending continuations\n" (hash-count continuation-table))
(fprintf p " - variables:\n")
(for ((v variables))
(fprintf p " - ")
(display (indented-port-output 6 (lambda (p) (prospect-pretty-print v p)) #:first-line? #f) p)
(newline p))
(fprintf p " - aggregates:\n")
(for (((index a) (in-hash aggregates)))
(define leader (format " - ~a: " index))
(fprintf p "~a" leader)
(display (indented-port-output #:first-line? #f
(string-length leader)
(lambda (p) (prospect-pretty-print a p)))
p)
(newline p))
(fprintf p " - pending-patch:\n")
(display (indented-port-output 3 (lambda (p) (prospect-pretty-print pending-patch p))) p)
(newline p)
(fprintf p " - ")
(display (indented-port-output 3 (lambda (p) (prospect-pretty-print mux p)) #:first-line? #f) p)
(newline p))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(module+ test
(require racket/pretty (for-syntax racket/pretty))

View File

@ -0,0 +1,41 @@
#lang prospect
(require prospect/actor)
(require prospect/drivers/tcp)
(require (only-in racket/string string-trim))
(struct says (who what) #:prefab)
(struct present (who) #:prefab)
(define (spawn-session them us)
(actor (define (send-to-remote fmt . vs)
(send! (tcp-channel us them (string->bytes/utf-8 (apply format fmt vs)))
#:meta-level 1))
(define (say who fmt . vs)
(unless (equal? who user)
(send-to-remote "~a ~a\n" who (apply format fmt vs))))
(define user (gensym 'user))
(send-to-remote "Welcome, ~a.\n" user)
(until (retracted (advertise (tcp-channel them us ?)) #:meta-level 1)
(assert (present user))
(on (asserted (present $who)) (say who "arrived."))
(on (retracted (present $who)) (say who "departed."))
(on (message (says $who $what)) (say who "says: ~a" what))
(assert (advertise (tcp-channel us them ?)) #:meta-level 1)
(on (message (tcp-channel them us $bs) #:meta-level 1)
(send! (says user (string-trim (bytes->string/utf-8 bs))))))))
(spawn-tcp-driver)
(spawn-world
(%%boot
(lambda ()
(actor (define us (tcp-listener 5000))
(forever (assert (advertise (observe (tcp-channel ? us ?))) #:meta-level 1)
(on (asserted (advertise (tcp-channel $them us ?)) #:meta-level 1)
(spawn-session them us)))))))