[racket] programmatic file editing

From: Neil Van Dyke (neil at neilvandyke.org)
Date: Thu Jun 7 20:38:29 EDT 2012

Neil Van Dyke wrote at 06/07/2012 01:14 AM:
> That was toy example of a call to "progedit", like a test case, and 
> not very clear.  In a real program, you'd programmatically build the 
> list you pass to the "#:insert" keyword argument of "progedit".  So, 
> you would only add the (0 "#lang setup/infotab\n") to that list if you 
> found through other means that you needed to add a #lang line.  
> "progedit" itself only performs the inserts, deletes, and replaces.

This isn't a big point, but I might as well get more mileage out of my 
test cases by using them for illustration...

Here's an example of using "progedit" to programmatically edit the 
source file of a simple language to set the variable "name" to value 
"Jane", either by editing an existing form or adding a new form to the file.

Notice that the "#:inserts" and "#:replaces" arguments to "progedit" are 
constructed at run time, based on what we parse from the input file.

(define (demonstrate-define-name-as-jane in-str)
   (let ((in (open-input-string in-str)))
     (let loop ((name-stx #f))
       (let ((stx (read-syntax 'my-source in)))
         (if (eof-object? stx)
             (let-values (((inserts replaces)
                           (if name-stx
                               (values '()
                                       `((,name-stx ,#'"Jane")))
                               (values `((#f #\newline
                                             ,#'(define name "Jane")
                                             #\newline))
                                       '())))
                          ((out) (open-output-string)))
               (progedit-ports #:in-port  (open-input-string in-str)
                               #:out-port out
                               #:deletes  '()
                               #:inserts  inserts
                               #:replaces replaces)
               (get-output-string out))
             (syntax-parse stx
               (((~datum define) (~datum name) VAL)
                (if name-stx
                    (raise-syntax-error
                     'foo
                     "(define name VAL) occurred multiple times"
                     stx
                     #f
                     (list name-stx))
                    (loop #'VAL)))
               (_ (loop name-stx))))))))

In this case, we see that "name" is set to "John", so we replace "John" 
with "Jane":

(demonstrate-define-name-as-jane
  "(define honorific \"Dr.\")\n\n(define name \"John\")\n\n(define age 
29)\n")
;;==> "(define honorific \"Dr.\")\n\n(define name \"Jane\")\n\n(define 
age 29)\n"

In this case, "name" isn't set to anything, so we add a new "define 
name" form to the end of the file:

(demonstrate-define-name-as-jane
  "(define honorific \"Dr.\")\n\n(define age 29)\n")
;;==> "(define honorific \"Dr.\")\n\n(define age 29)\n\n(define name 
\"Jane\")\n"

Neil V.


Posted on the users mailing list.