Search code examples
modulenamespacesracketeval

How to obtain namespace of a custom lang in Racket?


I'm implementing a custom language in Racket in which I would like to provide an eval procedure bound to the namespace of this custom language.

my-lang.rkt:

#lang racket

(provide #%module-begin #%top #%datum #%app)
(provide quote)
(provide (rename-out [a b] [my-eval eval]))

(define-namespace-anchor anchor)
(define ns (namespace-anchor->namespace anchor)) ; wrong namespace

(define (my-eval x) (eval x ns))

(define a 1)

test.rkt:

#lang s-exp "my-lang.rkt"

(eval 'a)
(eval 'b)

As implemented, since ns is the namespace of my-lang.rkt, (eval 'a) evaluates to 1 while (eval 'b) fails.

I would like ns to be bound to the namespace of test.rkt so that (eval 'a) fails and (eval 'b) returns 1.

How should I define ns?


Solution

  • Here's a way using parameter and macro. There might be a better way:

    ;; my-lang.rkt
    #lang racket
    
    (provide #%top #%datum #%app (rename-out [@#%module-begin #%module-begin])
             quote
             (rename-out [a b] [my-eval eval]))
    
    (require syntax/parse/define
             racket/splicing)
    
    (define current-ns (make-parameter #f))
    
    (define-syntax-parser @#%module-begin
      [(_ . xs)
       #'(#%module-begin
          (define-namespace-anchor anchor)
          (splicing-parameterize ([current-ns (namespace-anchor->namespace anchor)])
            . xs))])
    
    (define (my-eval x) (eval x (current-ns)))
    
    (define a 1)
    
    ;; test.rkt
    #lang s-exp "my-lang.rkt"
    
    (eval 'b) ;=> 1
    (eval 'a)
    ;; a: undefined;
    ;; cannot reference an identifier before its definition