6

General question:

Can I invoke the current racket executable from within a running Racket script?

Basically, I'd like a replacement for (system "racket ...") in the case that (find-executable-path "racket") does not return a path to the Racket executable I'm currently using.

Context:

What I really want is to try compiling a few expressions and assert that they raise compilation errors. This is for unit testing.

Ben Greenman
  • 1,945
  • 12
  • 22
  • 1
    BTW, I know Typed Racket solves this problem, but I don't understand the solution. (What is `dr` [here](https://github.com/racket/typed-racket/blob/master/typed-racket-test/main.rkt#L66)?) – Ben Greenman Dec 13 '15 at 02:14
  • 1
    Dear past Ben, you would have liked `find-console-bin-dir` and `find-exe`. – Ben Greenman Jul 04 '16 at 05:07

3 Answers3

5

I don't believe you need to step outside of the executable here. Try this:

#lang racket

(require syntax/modread)

;; define a namespace anchor to attach a namespace to:
(define-namespace-anchor anchor)
;; define a namespace for expansion:
(define target-namespace (namespace-anchor->namespace anchor))

(define program-to-compile
  "#lang racket
(+ 3 4)")

;; go ahead and expand
(with-module-reading-parameterization
 (λ()
   (parameterize ([current-namespace target-namespace])
   (expand
    (read-syntax
     "bogus-filename"
     (open-input-string program-to-compile))))))

I think I'm correct when I say that Racket is singularly clean in its ability to provide the compiler to running programs in a disciplined way.

John Clements
  • 16,895
  • 3
  • 37
  • 52
4

If your goal is just to compile some racket expressions, you can do that just with either compile or compile-syntax. An example file would be:

#lang racket
(require rackunit)

(define tests
  (list #'(+ 1 "3")
        #'(void void)
        #'(string-append 4)))

(for/list ([t (in-list test)])
  (check-exn exn:fail?
     (lambda () (compile t))))

Where exn:fail? is whatever exception you are looking for.

Furthermore, if you have some common syntax context you want to run your test in, you can use #` #,. So your code would end up something like this:

#lang racket
(require rackunit)

(define tests
  (list #'(+ 1 "3")
        #'(void void)
        #'(string-append 4)))

(for/list ([t (in-list test)])
  (check-exn exn:fail?
     (lambda () (compile #`(module anonymous racket
                             #,t)))))

Finally, if your code is stored on your computer, you can use John's solution, while using file->string to convert the file into a string.

Leif Andersen
  • 21,580
  • 20
  • 67
  • 100
2

For small tests, you can also use convert-compile-time-error from the syntax/macro-testing library. It turns an expression that causes a compile-time error into an expression that raises a run-time error when evaluated. The expression uses the environment where it occurs in the module, including local bindings; you don't have to fiddle with namespaces and eval.

(check-exn #rx"bad syntax"
            (lambda () (convert-compile-time-error (lambda))))

There's also convert-syntax-error (on the same page).

Ryan Culpepper
  • 10,495
  • 4
  • 31
  • 30