From: "andrew cooke" <andrew@...>
Date: Mon, 13 Aug 2007 21:04:14 -0400 (CLT)
Wrote this over the weekend, with help from people on various mailing lists and #scheme. Finally got it running just now. It shows the basic structure, but the code is still a mess - I need to write functions (possibly macros) to help simplify things. There seems to be more emphasis on higher order functions than I believe normal in Scheme, but really I don't have much of a clue (someone on #scheme didn't seem to follow it, but I probably didn't explain it well, and they may have been a student). ":";exec snow -- "$0" "$@" (package* napito/v1.0.0 (provide: (define (nap-line x1 y1 x2 y2)) (define (nap-generate engine drawing))) (author: "Andrew Cooke <andrew at acooke.org>") (homepage: "http://www.acooke.org") (description: "Napito drawing description language") (keywords: graphics) (license: gpl/v3.0) (require: napito-base/v1) (require: napito-test/v1)) ; napito is separated into a user api (this file), some common support ; code (napito-base), and various engines. the engines all implement ; (extend) the same "engine api". ; the user api is used to describe a drawing. the api looks like a ; program but, when evaluated, constructs a function that takes and ; returns an engine. ; in other words, a drawing has the type engine -> engine. ; this interface is ubiquitous, since drawings can be nested (for ; example, a single line is a drawing). ; when an engine is passed to a drawing the internal api is invoked to ; generate the image on the engine's output device (eg. by creating a ; postscript file). ; for simple actions the engine api is similar to the user api. for ; example, the user api to draw a line is the function (nap-line x1 y1 ; x2 y2). the engine api is (engine 'line (list x1 y1 x2 y2)). ; the job of the user api is to hide the engine api (which is generic ; with late binding) behind something that looks nice and simple. ; there are two advantages to this approach: ; first, late binding allows different engines to be used with the ; same drawing. ; second, the handling of the context (for example, the current ; coordinate transformation) is made implicit to the user, without ; losing referential transparency in the implementation. (define (nap-line x1 y1 x2 y2) (lambda (engine) (engine 'line (list x1 y1 x2 y2)))) (define (nap-generate engine drawing) (drawing engine)) (test* (expect* (equal? (let ((engine (nap-engine (nap-default-transform)))) ((nap-generate engine (nap-line 0 0 1 1)) 'results '())) '((line 0 0 1 1))))) (write (let ((engine (nap-engine (nap-default-transform)))) ((nap-generate engine (nap-line 0 0 1 1)) 'results '()))) ":";exec snow -- "$0" "$@" (package* napito-test/v1.0.0 (provide: (define (nap-engine initial-transform))) (author: "Andrew Cooke <andrew at acooke.org>") (homepage: "http://www.acooke.org") (description: "Napito drawing description language - test engine") (keywords: graphics) (license: gpl/v3.0) (require: napito-base/v1)) (define (nap-engine initial-transform) (let ((state (make-state '() initial-transform))) (new-engine state))) (define (new-engine state) (lambda (action args) (case action ((line) (new-engine (line state args))) ((scale) (new-engine (scale state args))) ((results) (get-results state)) ; specific to this engine (else (error "unsupported action" action))))) (define (get-results state) (car state)) (define (get-transform state) (cadr state)) (define (make-state results transform) (list results transform)) (define (line state args) (let ((results (get-results state)) (transform (get-transform state))) (make-state (cons `(line ,@args) results) transform))) (define (scale state args) (display "scale")) Andrew