On Thu, Nov 09, 2017 at 11:23:47PM -0800, Vincent Nys wrote:
Hi,

I have a domain-specific language with a non-parenthesis-based-syntax and
running programs written in this DSL sometimes leads to the creation of
some fairly large data structures. By "large" I mean that it is still
feasible to type them out, but an absolute drag. Doubly so when I have to
translate back and forth between the DSL syntax and Racket's syntax for the
data structures that are used internally. So I started working on a
language extension (called alpha-gamma) which allows me to switch to the
DSL syntax inside a #lang racket program by typing α(...) or γ(...), where
... is in non-Racket syntax.

I'm struggling a bit with the Racket reference, but by looking at some
other language extensions, I figured out how to do most of this with
make-meta-reader.  I don't understand how the whole process works, though,
and now I'm stuck with unbound identifiers in files using the extended
language.

Specifically, in a file called test.rkt written in #lang alpha-gamma racket
I have α(foo(bar)). The part between the outer parentheses is then parsed
as '(abstract-atom (abstract-atom-with-args "foo" "(" (abstract-term
(abstract-function-term "bar")) ")")), which is what I want. But the
bindings for abstract-atom, abstract-atom-with-args, abstract-term and
abstract-function-term are missing in test.rkt. I can get the value I want
by adding (require (only-in cclp/cclp-expander abstract-atom
abstract-atom-with-args abstract-term abstract-function-term)) at the top
of test.rkt, but I don't want to do that wherever I use the language
extension.

I guess these are the most important bits:

(define (wrap-reader reader)
 (define (rd . args) ; reader can read multiple datums at once...
   (parameterize ([current-readtable (make-αγ-readtable
(current-readtable))])
     (apply reader args)))
 rd)

(define-values (αγ-read αγ-read-syntax αγ-get-info)
 (make-meta-reader
  'αγ
  "language path"
  (λ (bstr)
    (let* ([str (bytes->string/utf-8 bstr)]
           [sym (string->symbol str)])
      (and (module-path? sym)
           (vector
            `(submod ,sym reader)
            (string->symbol (string-append str "/lang/reader"))))))
  wrap-reader
  wrap-reader
  (lambda (proc) ; don't really care too much about get-info right now
    (lambda (key defval)
      (define (fallback) (if proc (proc key defval) defval))
      (case key
        [else (fallback)])))))

What I get from this is that my extended language still produces a module
like a regular language, but (require (only-in cclp/cclp-expander
abstract-atom abstract-atom-with-args abstract-term abstract-function-term))
should be spliced into this module somehow. Any suggestions on how to pull
that off?

To have those identifiers available to everything in the module
(including both inside the α language and the γ language) you can have
your #%module-begin add a require form to the basic #%module-begin
form it creates.  But for the binding to be visible, it needs to have
a subset of the scopes that identifiers used in the module will have.
To do that you can make the syntax objects for the identifiers
required using `datum->syntax`, using the syntax of the #%module-begin
(or in this case just #f would make them visible to the whole module).

I would also recommend looking at two projects that attack similar
problems.  One is my Rash package[1], where I have a macro called
`rash` that lets you embed code in a particular non-s-expression
syntax inside normal Racket (and also allows escaping back into racket
and nesting arbitrarily deeply).  You can use it to write things like
this:

(let* ([project-dir "my-racket-project"]
      [rktfiles (rash «cd $project-dir
                       ls | grep rkt |> string-split _ "\n"»)])
 ...)

The key thing that I think you might find appealing about rash's
method of embedding is that rather than trying to have one reader at
the top level that can read multiple languages, it instead delimits
sections of code that a macro can apply a different reader to during
expansion time.

The other project you should look at is Michael Ballantyne's
multiscope package[2].  It essentially lets you switch back and forth
between two different namespaces within one module.  This could let
you eg. have different bindings visible inside the α and γ blocks (if
eg. α and γ use the same name for semantically different things).

[1] https://github.com/willghatch/racket-rash
[2] https://github.com/michaelballantyne/multiscope

--
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 racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to