racket / scribble

Other
197 stars 90 forks source link

examples does not treat escaping properly #307

Open Metaxal opened 3 years ago

Metaxal commented 3 years ago

Minimal failing example:

#lang racket
(require scribble/example)

(define ev (make-base-eval))

(define mods '(racket/string racket/list))

#;(examples #:eval ev
          #,`(require ,@mods))
;; fails with
;; examples: exception raised in example
;;  error: "unsyntax: illegal outside of quasisyntax"

(examples #:eval ev #:escape WOO
          (WOO `(require ,@mods)))
;; fails with
;; examples: exception raised in example
;;  error: "WOO: undefined;\n cannot reference an identifier before its definition"

(examples #:eval ev (first '(a b c)))

... if I'm reading the docs correctly.

mflatt commented 3 years ago

I think you're running into the way that a form in examples is used twice: once to typeset, and another to evaluate. In the example above, the escape makes sense for the typesetting copy, but not for the eval copy.

One solution is to use eval:alts to distinguish the two. Or maybe a maybe some macro over examples makes sense in this case, so mods can be replaced before examples sees the form at all.

Metaxal commented 3 years ago

For my case I could work around it (a little painfully) with scribble-eval-handler, but in general I think it might make more sense to provide an phase 1 examples/expr function that takes a s-expr that will be pretty-printed as code, possibly with a special symbol like (for example) 'NEWLINE that tells pretty-print to force a line break. This would make it far simpler to generate examples at phase 1. (Just an idea.)

Forcing the use of the macro system is probably not warranted here, since I think the syntax object caught by examples is only used for its source location, and not for its bindings, since the bindings are made only during eval, so it should be fine to provide a s-expr that is not a syntax object.

It seems that all the work is already done by do-interaction-eval in eval.rkt, but it is not exported.

Metaxal commented 3 years ago

Though I must admit I'm not clear on something: How would you use eval:alts here, assuming that mods are given as argument to a wrapping function? (Or maybe that's why you consider a macro next?)