diff --git a/collects/2htdp/private/check-aux.rkt b/collects/2htdp/private/check-aux.rkt index 90fbf697da..42d0fba967 100644 --- a/collects/2htdp/private/check-aux.rkt +++ b/collects/2htdp/private/check-aux.rkt @@ -1,5 +1,4 @@ - -#lang scheme +#lang racket (require htdp/error) diff --git a/collects/2htdp/private/keywords.rkt b/collects/2htdp/private/keywords.rkt index 9d1cda4005..47e81d1735 100644 --- a/collects/2htdp/private/keywords.rkt +++ b/collects/2htdp/private/keywords.rkt @@ -1,29 +1,40 @@ #lang racket -(require (for-syntax "syn-aux.ss") "syn-aux.ss" +(require (for-syntax "syn-aux.ss") + "syn-aux.ss" "syn-aux-aux.ss" + "check-aux.rkt" + (only-in "universe.rkt" make-bundle) (rename-in lang/prim (first-order->higher-order f2h))) -(provide (for-syntax AllSpec WldSpec UniSpec)) +(provide [for-syntax WldSpec UniSpec]) -(define-keywords AllSpec +(define-keywords AllSpec '() define-all ;; -- on-tick must specify a tick handler; it may specify a clock-tick rate - [on-tick (function-with-arity - 1 - except - [(_ f rate) - #'(list - (proc> 'on-tick (f2h f) 1) - (num> 'on-tick rate (lambda (x) (and (real? x) (positive? x))) - "positive number" "rate"))])] + [on-tick + DEFAULT #'#f + (function-with-arity + 1 + except + [(_ f rate) + #'(list + (proc> 'on-tick (f2h f) 1) + (num> 'on-tick rate (lambda (x) (and (real? x) (positive? x))) + "positive number" "rate"))])] ;; -- state specifies whether to display the current state - [state (expr-with-check bool> "expected a boolean (show state or not)")] + [state + DEFAULT #'#f + (expr-with-check bool> "expected a boolean (show state or not)")] ;; -- check-with must specify a predicate - [check-with (function-with-arity 1)]) + [check-with + DEFAULT #'True + (function-with-arity 1)]) -(define-keywords WldSpec +; (create-world world0) +(define-keywords WldSpec AllSpec create-world ;; -- on-draw must specify a rendering function; it may specify dimensions [on-draw to-draw + DEFAULT #'#f (function-with-arity 1 except @@ -32,32 +43,68 @@ (nat> 'to-draw width "width") (nat> 'to-draw height "height"))])] ;; -- on-mouse must specify a mouse event handler - [on-mouse (function-with-arity 4)] + [on-mouse + DEFAULT #'K + (function-with-arity 4)] ;; -- on-key must specify a key event handler - [on-key (function-with-arity 2)] + [on-key + DEFAULT #'K + (function-with-arity 2)] ;; -- on-release must specify a release event handler - [on-release (function-with-arity 2)] + [on-release + DEFAULT #'K + (function-with-arity 2)] ;; -- on-receive must specify a receive handler - [on-receive (function-with-arity 2)] + [on-receive + DEFAULT #'#f + (function-with-arity 2)] ;; -- stop-when must specify a predicate; it may specify a rendering function - [stop-when (function-with-arity - 1 - except - [(_ stop? last-picture) - #'(list (proc> 'stop-when (f2h stop?) 1) - (proc> 'stop-when (f2h last-picture) 1))])] + [stop-when + DEFAULT #'False + (function-with-arity + 1 + except + [(_ stop? last-picture) + #'(list (proc> 'stop-when (f2h stop?) 1) + (proc> 'stop-when (f2h last-picture) 1))])] ;; -- should the session be recorded and turned into PNGs and an animated GIF - [record? (expr-with-check bool> "expected a boolean (to record? or not)")] - [name (expr-with-check string> "expected a name (string) for the world")] + [record? + DEFAULT #'#f + (expr-with-check bool> "expected a boolean (to record? or not)")] + [name + DEFAULT #'#f + (expr-with-check string> "expected a name (string) for the world")] ;; -- register must specify the internet address of a host (e.g., LOCALHOST) - [register (expr-with-check ip> "expected a host (ip address)")]) + [register + DEFAULT #'#f + (expr-with-check ip> "expected a host (ip address)")]) -(define-keywords UniSpec +; (create-universe universe0) +(define-keywords UniSpec AllSpec create-universe ;; -- on-new must specify what happens when a world joins the universe - [on-new (function-with-arity 2)] + [on-new + DEFAULT #'"my-bad" + (function-with-arity 2)] ;; -- on-msg must specify what happens to a message from a world - [on-msg (function-with-arity 3)] + [on-msg + DEFAULT #'"my-bad" + (function-with-arity 3)] ;; -- on-disconnect may specify what happens when a world drops out - [on-disconnect (function-with-arity 2)] + [on-disconnect + ;; ****************************************************************** + DEFAULT #'(lambda (u w) (make-bundle u '() '())) + ;; this is the wrong default function + ;; instead of K there should be a function that produces a bundle + (function-with-arity 2) + ;; ****************************************************************** + ] ;; -- to-string specifies how to render the universe as a string for display - [to-string (function-with-arity 1)]) \ No newline at end of file + [to-string + DEFAULT #'#f + (function-with-arity 1)]) + +(provide new-world) +(define new-world (create-world world0)) + +(provide new-universe) +(define new-universe (create-universe universe0)) \ No newline at end of file diff --git a/collects/2htdp/private/syn-aux-aux.rkt b/collects/2htdp/private/syn-aux-aux.rkt index 538164f672..80a5e5ac66 100644 --- a/collects/2htdp/private/syn-aux-aux.rkt +++ b/collects/2htdp/private/syn-aux-aux.rkt @@ -1,6 +1,6 @@ -#lang scheme +#lang racket -(require htdp/error) +(require htdp/error "check-aux.rkt") ; ; diff --git a/collects/2htdp/private/syn-aux.rkt b/collects/2htdp/private/syn-aux.rkt index 30c569171c..4300136f66 100644 --- a/collects/2htdp/private/syn-aux.rkt +++ b/collects/2htdp/private/syn-aux.rkt @@ -1,53 +1,82 @@ -#lang scheme +#lang racket (provide define-keywords - ;; (define-keywords (name1:identifier ... spec:expr) ...) - ;; constraint: the first name is the original name + DEFAULT + ;; constraint: the first kw is the original one ;; and it is also the name of the field in the class - function-with-arity expr-with-check except err ->args + function-with-arity expr-with-check except err ->kwds-in clauses-use-kwd) (require - (for-template "syn-aux-aux.ss" - scheme + (for-syntax "syn-aux-aux.rkt" syntax/parse) + (for-template "syn-aux-aux.rkt" + racket (rename-in lang/prim (first-order->higher-order f2h)))) -(require (for-syntax syntax/parse)) -(define-syntax (define-keywords stx) - (syntax-parse stx - [(define-keywords the-list (kw:identifier ... coerce:expr) ...) - #'(begin - (provide kw ...) ... - (define-syntaxes (kw ...) - (values (lambda (x) - (raise-syntax-error 'kw "used out of context" x)) - ...)) - ... - (define-for-syntax the-list - (apply append - (list - (let* ([x (list (list #'kw ''kw) ...)] - [f (caar x)]) - (map (lambda (x) - (define clause-name (car x)) - (define clause-spec (cadr x)) - (list clause-name f (coerce clause-spec))) - x)) - ...))))])) +(define-syntax (DEFAULT stx) + (raise-syntax-error 'DEFAULT "used out of context" stx)) -#; -(define-syntax-rule - (define-keywords the-list (kw coerce) ...) - (begin - (provide kw ...) - (define-syntax kw - (lambda (x) - (raise-syntax-error 'kw "used out of context" x))) - ... - (define-for-syntax the-list - (list (list #'kw (coerce ''kw)) ...)))) +(define-syntax (define-keywords stx) + (syntax-parse stx #:literals (DEFAULT) + [(_ the-list super-list define-create + (kw:identifier + (~optional kw-alt:identifier + #:defaults ((kw-alt (datum->syntax stx (gensym))))) + (~optional (~seq DEFAULT default:expr)) + coerce:expr) ...) + (let* ([defs (attribute default)]) + #`(begin + ;; define and create list of keywords and associated values + (define-for-syntax the-list + (append super-list + (list + (list #'kw #'kw (coerce ''kw) default) + #; + (list #'kw-alt #'kw (coerce ''kw-alt) default)) + ...)) + ;; define and provide keywords + (provide (rename-out (kw kw-alt) ...)) + (provide kw ...) + (define-syntaxes (kw ...) + (values (lambda (x) + (raise-syntax-error 'kw "used out of context" x)) + ...)) + + (define-syntax (define-create stx) + (syntax-case stx () + [(_ para (... ...)) + (let* [[kwds (map cadr the-list)] + [defs (map cadddr the-list)] + [args (lambda (para*) + (append + para* + (foldr (lambda (x d rst) + (define k (string->keyword + (symbol->string + (syntax-e x)))) + ;; This 'if' doesn't work because + ;; I don't know how to use 'attribute' + ;; properly here and have default values + ;; for everything. big-bang and universe + ;; check already that defaults are provided. + ; (displayln x) + ; (displayln d) + (if d + (append (list k `(,x ,d)) rst) + (append (list k x) rst))) + '() + kwds + defs)))] + [body (lambda (para*) + (map (lambda (x) `(,x ,x)) (append para* kwds)))]] + (let ([para* (syntax->list #'(para (... ...)))]) + #`(lambda (%) + (lambda #,(args para*) + (lambda () + (define o (new % #,@(body para*))) + o)))))]))))])) #| transform the clauses into the initial arguments specification @@ -62,24 +91,26 @@ if anything fails, use the legal keyword to specialize the error message |# -(define (->args tag stx state0 clauses AllSpec PartSpec ->rec? legal) +(define (->args tag stx state0 clauses Spec ->rec? legal) (define msg (format "not a legal clause in a ~a description" legal)) - (define Spec (append AllSpec PartSpec)) (define kwds (map (compose (curry datum->syntax stx) car) Spec)) (define spec (clauses-use-kwd (syntax->list clauses) ->rec? msg kwds)) (duplicates? tag spec) (not-a-clause tag stx state0 kwds) - (map (lambda (x) - (define kw (car x)) - (define-values (key coercion) - (let loop ([kwds kwds][Spec Spec]) - (if (free-identifier=? (car kwds) kw) - ;; -- the original keyword, which is also the init-field name - ;; -- the coercion that comes with it - (values (cadar Spec) (caddar Spec)) - (loop (cdr kwds) (cdr Spec))))) - (list (mk-kwd key) (coercion (cdr x)))) - spec)) + (apply append + (map (lambda (x) + (define kw (car x)) + (define-values (key coercion) + (let loop ([kwds kwds][Spec Spec]) + (if (free-identifier=? (car kwds) kw) + ;; -- the original keyword, which is also the init-field name + ;; -- the coercion that comes with it + (values (cadar Spec) (caddar Spec)) + (loop (cdr kwds) (cdr Spec))))) + (list (mk-kwd key) (coercion (cdr x)))) + spec))) + +(define (tee x) (displayln 'tee) (displayln x) x) ;; Syntax -> Syntax ;; eventually: convert syntax to keyword @@ -87,7 +118,7 @@ (define key:id (symbol->string (syntax-e key))) (define key:wd (string->keyword key:id)) ; (displayln key:wd) - key) + key:wd) ;; Symbol Syntax Syntax [Listof Kw] -> true ;; effect: if state0 looks like a clause, raise special error diff --git a/collects/2htdp/private/timer.rkt b/collects/2htdp/private/timer.rkt index 1230cdcde2..830cd04830 100644 --- a/collects/2htdp/private/timer.rkt +++ b/collects/2htdp/private/timer.rkt @@ -1,33 +1,32 @@ -#lang scheme/gui +#lang racket/gui ;; The module provides a timer mixing for world and universe. -;; The interface ensures that super class provides start and stop method, -;; plus a call back for clock ticks. The super-init call provides the -;; on-tick parameter, which the super-class uses to define the callback. +;; The interface ensures that super class provides start and stop method, +;; BUT if fails to ensure that the class comes with a _tick_ field. +;; plus a call back for clock ticks. The super-init call sets the +;; tick field, which the super-class uses to define the callback. (require "check-aux.ss") (provide clock-mixin start-stop<%>) -(define start-stop<%> (interface () start! ptock stop!)) - -;; T = (U (World -> World) (list (World -> World) Nat)) -;; X [(list (World -> World) Nat) -> X] [(World -> World) -> X] -> [T -> X] -(define (selector default lchoice pchoice) - (lambda (on-tick) - (cond - [(cons? on-tick) (lchoice on-tick)] - [(procedure? on-tick) (pchoice on-tick)] - [else default]))) +(define start-stop<%> (interface () start! ptock pptock stop!)) (define clock-mixin (mixin (start-stop<%>) () (inherit ptock) (init-field [on-tick #f]) - (field [rate ((selector 0 second (lambda _ RATE)) on-tick)] + (field [rate 0] + [tick void] [timer (new timer% [notify-callback (lambda () (ptock))])]) + (cond + [(cons? on-tick) (set! rate (second on-tick)) + (set! tick (first on-tick))] + [(procedure? on-tick) (set! rate RATE) + (set! tick on-tick)] + [else (void)]) (define/override (start!) (unless (<= rate 0) (send timer start (number->integer (* 1000 rate) 'big-bang/universe 'clock-rate))) @@ -35,4 +34,6 @@ (define/override (stop! w) (send timer stop) (super stop! w)) - (super-new [tick ((selector void first (lambda (x) x)) on-tick)]))) + (define/override (pptock w) + (tick w)) + (super-new))) diff --git a/collects/2htdp/private/universe.rkt b/collects/2htdp/private/universe.rkt index 8bd4ec0798..0ad17b9084 100644 --- a/collects/2htdp/private/universe.rkt +++ b/collects/2htdp/private/universe.rkt @@ -1,4 +1,4 @@ -#lang scheme/gui +#lang racket/gui (require (for-syntax "syn-aux.ss") "checked-cell.ss" @@ -30,14 +30,16 @@ (define universe% (last-mixin (clock-mixin - (class* object% (start-stop<%>) (inspect #f) (super-new) + (class* object% (start-stop<%>) + (inspect #f) + (super-new) (init-field ;; type Result ; = (make-bundle [Listof World] Universe [Listof Mail]) universe0 ;; the initial state of the universe on-new ;; Universe World -> Result on-msg ;; Universe World Message -> Result - tick ;; Universe -> Result - (state #f) ;; Boolean + ;; tick ;; Universe -> Result + (state #f) ;; Boolean (on-disconnect ;; Universe World -> Result (lambda (u w) (make-bundle u '() '()))) (to-string #f) ;; Universe -> String @@ -98,7 +100,9 @@ (def/cback private (pdisconnect iworld) on-disconnect (kill iworld)) - (def/cback public (ptock) tick) + ;; tick, tock : deal with a tick event for this world + (def/cback pubment (ptock) (lambda (w) (pptock w))) + (define/public (pptock w) (void)) ;; IWorld -> Void ;; effect: remove from given iworld from iworlds diff --git a/collects/2htdp/private/world.rkt b/collects/2htdp/private/world.rkt index e24e5ac4d4..6a70974348 100644 --- a/collects/2htdp/private/world.rkt +++ b/collects/2htdp/private/world.rkt @@ -6,7 +6,8 @@ "checked-cell.ss" "stop.ss" "universe-image.ss" - "utilities.rkt" + ; "utilities.rkt" + "keywords.rkt" htdp/error mzlib/runtime-path mrlib/bitmap-label @@ -58,7 +59,7 @@ (state #f) ;; Boolean (register #f) ;; (U #f IP) (check-with True) ;; Any -> Boolean - (tick K)) ;; (U (World -> World) (list (World -> World) Nat)) + ) (init (on-key K) ;; World KeyEvent -> World @@ -227,74 +228,77 @@ (define draw# 0) (set-draw#!) - (define-syntax-rule (def/pub-cback (name arg ...) transform) + (define-syntax-rule + (def/cback pub (name arg ...) transform) ;; Any ... -> Boolean - (define/public (name arg ...) - (queue-callback - (lambda () - (with-handlers ([exn? (handler #t)]) - (define tag (format "~a callback" 'transform)) - (define nw (transform (send world get) arg ...)) - (define (d) (pdraw) (set-draw#!)) - ;; --- - ;; [Listof (Box [d | void])] - (define w '()) - ;; set all to void, then w to null - ;; when a high priority draw is scheduledd - ;; --- - (when (package? nw) - (broadcast (package-message nw)) - (set! nw (package-world nw))) - (if (stop-the-world? nw) - (begin - (set! nw (stop-the-world-world nw)) - (send world set tag nw) - (when last-picture - (set! draw last-picture)) - (when draw (pdraw)) - (callback-stop! 'name) - (enable-images-button)) - (let ([changed-world? (send world set tag nw)]) - ;; this is the old "Robby optimization" see checked-cell: - ; unless changed-world? - (when draw - (cond - [(not drawing) - (set! drawing #t) - (let ([b (box d)]) - (set! w (cons b w)) - ;; low priority, otherwise it's too fast - (queue-callback (lambda () ((unbox b))) #f))] - [(< draw# 0) - (set-draw#!) - (for-each (lambda (b) (set-box! b void)) w) - (set! w '()) - ;; high!! the scheduled callback didn't fire - (queue-callback (lambda () (d)) #t)] - [else - (set! draw# (- draw# 1))])) - (when (pstop) - (when last-picture - (set! draw last-picture) - (pdraw)) + (begin + (define/public (name arg ...) + (queue-callback + (lambda () + (with-handlers ([exn? (handler #t)]) + (define tag (format "~a callback" 'transform)) + (define nw (transform (send world get) arg ...)) + (define (d) (pdraw) (set-draw#!)) + ;; --- + ;; [Listof (Box [d | void])] + (define w '()) + ;; set all to void, then w to null + ;; when a high priority draw is scheduledd + ;; --- + (when (package? nw) + (broadcast (package-message nw)) + (set! nw (package-world nw))) + (if (stop-the-world? nw) + (begin + (set! nw (stop-the-world-world nw)) + (send world set tag nw) + (when last-picture + (set! draw last-picture)) + (when draw (pdraw)) (callback-stop! 'name) (enable-images-button)) - changed-world?))))))) + (let ([changed-world? (send world set tag nw)]) + ;; this is the old "Robby optimization" see checked-cell: + ; unless changed-world? + (when draw + (cond + [(not drawing) + (set! drawing #t) + (let ([b (box d)]) + (set! w (cons b w)) + ;; low priority, otherwise it's too fast + (queue-callback (lambda () ((unbox b))) #f))] + [(< draw# 0) + (set-draw#!) + (for-each (lambda (b) (set-box! b void)) w) + (set! w '()) + ;; high!! the scheduled callback didn't fire + (queue-callback (lambda () (d)) #t)] + [else + (set! draw# (- draw# 1))])) + (when (pstop) + (when last-picture + (set! draw last-picture) + (pdraw)) + (callback-stop! 'name) + (enable-images-button)) + changed-world?)))))))) ;; tick, tock : deal with a tick event for this world - (def/pub-cback (ptock) tick) + (def/cback pubment (ptock) (lambda (w) (pptock w))) + (define/public (pptock w) (void)) ;; key events - (def/pub-cback (pkey ke) key) + (def/cback pubment (pkey ke) key) ;; release events - (def/pub-cback (prelease ke) release) + (def/cback pubment (prelease ke) release) ;; mouse events - (def/pub-cback (pmouse x y me) mouse) + (def/cback pubment (pmouse x y me) mouse) ;; receive revents - (def/pub-cback (prec msg) rec) + (def/cback pubment (prec msg) rec) ;; ---------------------------------------------------------------------- ;; -> Void @@ -348,6 +352,8 @@ [(stop-the-world? w) (stop! (stop-the-world-world (send world get)))])))))) +; (define make-new-world (new-world world%)) + ;; ----------------------------------------------------------------------------- (define-runtime-path break-btn:path '(lib "icons/break.png")) (define break-button:label @@ -391,15 +397,18 @@ (set! event-history (cons (cons type stuff) event-history))) ;; --- new callbacks --- - (define-syntax-rule (def/over-cb (pname name arg ...)) - (define/override (pname arg ...) - (when (super pname arg ...) (add-event name arg ...)))) + (define-syntax-rule + (def/cb ovr (pname name arg ...)) + (begin + ; (ovr pname) + (define/override (pname arg ...) + (when (super pname arg ...) (add-event 'name arg ...))))) - (def/over-cb (ptock tick)) - (def/over-cb (pkey key e)) - (def/over-cb (prelease release e)) - (def/over-cb (pmouse mouse x y me)) - (def/over-cb (prec rec m)) + (def/cb augment (ptock tick)) + (def/cb augment (pkey key e)) + (def/cb augment (prelease release e)) + (def/cb augment (pmouse mouse x y me)) + (def/cb augment (prec rec m)) ;; --> Void ;; re-play the history of events; create a png per step; create animated gif diff --git a/collects/2htdp/universe.rkt b/collects/2htdp/universe.rkt index 4f246c994b..83e61c614b 100644 --- a/collects/2htdp/universe.rkt +++ b/collects/2htdp/universe.rkt @@ -134,13 +134,15 @@ [(V) (set! rec? #'V)] [_ (err '#'record? stx)])))] [args - (->args 'big-bang stx (syntax w) (syntax (clause ...)) AllSpec WldSpec ->rec? "world")]) - #`(let* ([esp (make-eventspace)] - [thd (eventspace-handler-thread esp)]) - (with-handlers ((exn:break? (lambda (x) (break-thread thd)))) - (parameterize ([current-eventspace esp]) - (let ([o (new (if #,rec? aworld% world%) [world0 w] #,@args)]) - (send o last))))))])) + (->args 'big-bang stx #'w #'(clause ...) WldSpec ->rec? "world")]) + #`(run-it ((new-world (if #,rec? aworld% world%)) w #,@args)))])) + +(require (only-in 2htdp/image circle)) +(define (main) + (big-bang 10 + (on-tick sub1) + (stop-when zero?) + (to-draw (lambda (x) (circle (+ 30 x) 'solid 'red))))) (define (run-simulation f) (check-proc 'run-simulation f 1 "first" "one argument") @@ -215,23 +217,45 @@ ) (define-syntax (universe stx) - (define legal "not a legal clause in a universe description") (syntax-case stx () [(universe) (raise-syntax-error #f "not a legal universe description" stx)] [(universe u) (raise-syntax-error #f "not a legal universe description" stx)] [(universe u bind ...) (let* - ([args (->args 'universe stx (syntax u) (syntax (bind ...)) AllSpec UniSpec void "universe")] - [domain (map (compose syntax-e car) args)]) + ([args (->args 'universe stx #'u #'(bind ...) UniSpec void "universe")] + [domain (map (lambda (x) + (if (keyword? x) + (string->symbol (keyword->string x)) + x)) + args)]) (cond [(not (memq 'on-new domain)) (raise-syntax-error #f "missing on-new clause" stx)] [(not (memq 'on-msg domain)) (raise-syntax-error #f "missing on-msg clause" stx)] [else ; (and (memq #'on-new domain) (memq #'on-msg domain)) - #`(let* ([esp (make-eventspace)] - [thd (eventspace-handler-thread esp)]) - (with-handlers ((exn:break? (lambda (x) (break-thread thd)))) - (parameterize ([current-eventspace esp]) - (send (new universe% [universe0 u] #,@args) last))))]))])) + #`(run-it ((new-universe universe%) u #,@args))]))])) +; +; +; +; ; ;;; +; ; +; ;;; ;; ;; ;;;;;; ;; ;;; ;;;; +; ; ;; ; ; ;; ; ; +; ; ; ; ; ; ;;;;; +; ; ; ; ; ; ; ; +; ; ; ; ; ; ; ;; +; ;;;;; ;;; ;;; ;;;;;; ;;;;; ;;; ;; +; +; +; +; + +;; (-> Object) -> Any +(define (run-it o) + (define esp (make-eventspace)) + (define thd (eventspace-handler-thread esp)) + (with-handlers ((exn:break? (lambda (x) (break-thread thd)))) + (parameterize ([current-eventspace esp]) + (send (o) last))))