Jack,
There exists a language that wasn't initially designed with racket in mind, but
could easily be a racket #lang. To interop with code already written in this
language, I wanted an easy way to run files that don't have the #lang line.
I had a very similar case when I had to create a command-line program
that runs files written in some DSL without #lang in them. Below is what
I came up with. The prefix-input-port and require-input-port routines
came from Matthew Flatt: he wrote them at my request in this newsgroup a
couple years ago. Thank you again Matthew, I still use them.
Best regards,
Dmitry
#lang racket
(require syntax/modread
errortrace
errortrace/errortrace-key)
;; prefix-input-port : bytes input-port -> input-port
;; Directs position requests to the given port after the
;; prefix is read.
;; Closes the given input port when the result port is closed.
(define (prefix-input-port prefix base-p)
(define-values (prefix-i prefix-o) (make-pipe))
(write-bytes prefix prefix-o)
(close-output-port prefix-o)
(define (prefix-done?)
(zero? (pipe-content-length prefix-i)))
(make-input-port
(object-name base-p)
;; read
(lambda (bstr)
(define n (read-bytes-avail!* bstr
(if (prefix-done?)
base-p
prefix-i)))
(if (equal? n 0)
(wrap-evt base-p (lambda (v) 0))
n))
;; peek
(lambda (bstr offset evt)
(define pre-n (pipe-content-length prefix-i))
(define n (if (offset . >= . pre-n)
(peek-bytes-avail!* bstr
(- offset pre-n)
#f
base-p)
(peek-bytes-avail!* bstr
offset
#f
prefix-i)))
(if (equal? n 0)
(wrap-evt base-p (lambda (v) 0))
n))
;; close
(lambda ()
(close-input-port base-p))
;; get-progress-evt
;; Difficult (impossible?) to support at the
;; prefix--base boundary.
#f
;; commit
#f
;; get-location
(lambda ()
(if (prefix-done?)
(port-next-location base-p)
(port-next-location prefix-i)))
;; count-lines!
(lambda ()
(port-count-lines! prefix-i)
(port-count-lines! base-p))))
(define (require-input-port p [name (gensym)])
(define module-name (make-resolved-module-path name))
(parameterize ([current-module-declare-name module-name])
(eval-syntax (check-module-form ; ensures that `module` is bound
(with-module-reading-parameterization
(lambda ()
(read-syntax (object-name p) p)))
'ignored
#f)))
(dynamic-require module-name #f))
(define (normalize-error-location location)
(list
;; decrement the line number to take our #lang line back
(sub1 (first location))
;; increment the column number: in Racket ports, column numbers
;; are 0-based, while we want 1-based
(add1 (second location))))
(define (run path)
(define p
(prefix-input-port
#"#lang mylang\n"
(open-input-file path)))
(port-count-lines! p)
(define path-norm (normalize-path path))
(define error-location (void))
(define error-message (void))
(with-handlers
((exn:fail:syntax?
(lambda (e)
(set! error-message (exn-message e))
(let ((stx (last (exn:fail:syntax-exprs e))))
(when (equal? (normalize-path (syntax-source stx)) path-norm)
(set! error-location
(normalize-error-location (list (syntax-line stx)
(syntax-column stx)))))
)))
(exn:fail?
(lambda (e)
(set! error-message (exn-message e))
(for ((stack-elem (continuation-mark-set->list
(exn-continuation-marks e) errortrace-key))
#:break (not (void? error-location)))
(let ((s (cadr stack-elem)))
(when (and (path? s)
(equal? (normalize-path s) path-norm))
(set! error-location
(normalize-error-location (cddr stack-elem)))))))))
(require-input-port p))
(unless (void? error-location)
(fprintf (current-error-port) "error at line ~a, column ~a:\n"
(first error-location)
(second error-location)))
(unless (void? error-message)
(fprintf (current-error-port) "~a\n" error-message)
(exit -1))
(void))
--
You received this message because you are subscribed to the Google Groups "Racket
Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.