Thanks for the link to the GitHub repo; that makes it more clear what you
want to do. I think, with your proposed `koan` form, you don't necessarily
even need a custom #lang, let along a custom reader: `koan` and `_____` can
be macros that work together, provided by a normal library.

Here's a little sketch to help you on your way:
#lang racket

(require rackunit
         syntax/parse/define
         syntax/macro-testing
         (for-syntax racket/base
                     racket/list
                     racket/syntax
                     syntax/parse))

(provide (all-from-out rackunit)
         koan
         _____)

(define-syntax (_____ stx)
  (raise-syntax-error #f "not allowed outside of a koan form" stx))

(define-for-syntax (syntax->identifier-list stx)
  (define (convert-syntax-object stx)
    (cond
      [(identifier? stx)
       (list stx)]
      [else
       (define datum
         (syntax-e stx))
       (if (pair? datum)
           (convert-syntax-pair datum)
           null)]))
  (define (convert-syntax-pair datum)
    (define the-cdr (cdr datum))
    (cons (convert-syntax-object (car datum))
          (cond
            [(null? the-cdr)
             null]
            [(pair? the-cdr)
             (convert-syntax-pair the-cdr)]
            [else
             (convert-syntax-object the-cdr)])))
  (flatten
   (convert-syntax-object stx)))


(define-syntax-parser koan
  ;; koans with blanks
  [(_ body ...)
   #:do [(define ids (syntax->identifier-list #'(body ...)))]
   #:fail-unless (member #'_____ ids free-identifier=?)
   "if no blanks, fall through to next case"
   (with-disappeared-uses
    (for ([id (in-list ids)]
          #:when (free-identifier=? #'_____ id))
      (record-disappeared-uses id))
    #'(fail "koan contains _____"))]
  ;; koans without blanks
  [(_ body ...)
   #'(check-not-exn
      (λ ()
        (convert-compile-time-error
         (let () body ...))))])

(koan #:does-not-compile)

(koan
 (let ([p _____])
   (check-pred racketeer? p)
   (check-pred programmer? p)
   (check-pred struct? p)
   (check-pred procedure? set-programmer-salary!)
   (check-equal? (programmer-confusion-level p) 10)
   (check-equal? (racketeer-desire-for-racket-jobs p) 9001)))

(koan
 (struct programmer (confusion-level [salary #:mutable])
   #:transparent)
 (struct racketeer programmer (desire-for-racket-jobs)
   #:transparent)
 (let ([p (racketeer 10 0 9001)])
   (check-pred racketeer? p)
   (check-pred programmer? p)
   (check-pred struct? p)
   (check-pred procedure? set-programmer-salary!)
   (check-equal? (programmer-confusion-level p) 10)
   (check-equal? (racketeer-desire-for-racket-jobs p) 9001)))

-Philip


On Sun, Aug 26, 2018 at 7:16 PM Sage Gerard <zyrolast...@gmail.com> wrote:

> Hi Phillip, thanks for the quick and detailed reply.
>
> Context @ zyrolasting/racket-koans#24
> <https://github.com/zyrolasting/racket-koans/issues/24>, except WIP
> implementation does not provide (koan) form. Re:
> convert-compile-time-error, that traces back to 2.ii. in that issue and
> You's suggestion in this thread
> <https://groups.google.com/forum/?q=convert-compile-time-error#!searchin/racket-users/convert-compile-time-error%7Csort:date/racket-users/NZNn44E0Yxs/dMIb9EAtAQAJ>.
> The intent is to reject code with blanks, and to treat code without blanks
> as Racket code that still might not compile. If said code didn't compile I
> didn't want that to stop other unit tests covering different topics from
> running. Add to that a need for consistency in reporting, namely
> blanks/compile-time errors looking like rackunit failures for the first
> pass. I used (display) as a stopgap in the gist when I realized said unit
> test errors would not share the correct source location. Your macro
> suggestion should take care of that among other things, so I'll take that
> to heart.
>
> Finally, all-from-out rackunit is an obvious improvement, so no worries on
> adding that.
>
> Had a little trouble following the rest, but that's just a matter of
> needing to read more. Appreciate your narrowing this down for me. Will
> follow-up.
>
>
>
>
>
> On Sun, Aug 26, 2018 at 7:38 PM Philip McGrath <phi...@philipmcgrath.com>
> wrote:
>
>> In Racket 7, at least, the first error I get is:
>> has-blanks: arity mismatch;
>>  the expected number of arguments does not match the given number
>>   expected: 0
>>   given: 1
>>
>> After fixing that, it looks like, in your `read-syntax`, the
>> `with-handlers` form catching `has-blanks?` emits a `module` form without
>> necessarily consuming the entire input port, which is incorrect behavior: I
>> believe `read-syntax` will end up being called again, thus producing
>> multiple `module` forms. If you really want to read the whole body at once,
>> you might look at the `#:whole-body-readers?` option with `
>> syntax/module-reader
>> <http://docs.racket-lang.org/syntax/reader-helpers.html?q=syntax%2Fmodule-reader#%28mod-path._syntax%2Fmodule-reader%29>
>> `.
>>
>> Another potential problem is that you provide `read` from Racket.
>> Generally, `read` should be consistent with `read-syntax`. You can write a
>> function like:
>> (define (koan-read in)
>>   (syntax->datum #f (koan-read-syntax #f in)))
>> and export it as `read` via `rename-out`. I recommend also calling your
>> `read-syntax` `koan-read-syntax` internally: shadowing `read` and
>> `read-syntax` is a recipie for confusion later, especially as e.g. your
>> `read-syntax` is implemented using Racket's `read`.
>>
>> More broadly, I am very suspicious of the decision to recognize "blanks"
>> (i.e. `_____`) at the reader level. I think it will probably turn out
>> better to `(define-syntax _____ ...)` in your language implementation and
>> bind `_____` to a macro that raises an error. You might look at the
>> "todo-list" package (http://docs.racket-lang.org/todo-list/index.html)
>> for a particularly slick way to handle it. If there is a compelling reason
>> to do it at the reader level, I would suggest using `raise-read-error
>> <http://docs.racket-lang.org/syntax/reader-helpers.html#(def._((lib._syntax%2Freaderr..rkt)._raise-read-error))>`
>> rather than emitting code to raise an exception at run-time.
>>
>> I'm similarly not sure about the decision to use
>> `convert-compile-time-error` in your `#%module-begin`, but maybe you have a
>> good reason to do it that way.
>>
>> Also, why not `(provide (all-from-out rackunit))` from your language?
>>
>> -Philip
>>
>>
>> On Sun, Aug 26, 2018 at 5:42 PM Sage Gerard <zyrolast...@gmail.com>
>> wrote:
>>
>>> Hello all,
>>>
>>> First crack at a module language and stuck on a "expected only a
>>> `module' declaration; found an extra form" error. This is not a
>>> duplicate of this question <https://stackoverflow.com/q/37952163/394397>
>>> since the error is slightly different and the error reproduces in the REPL.
>>>
>>> Here's my WIP implementation:
>>> https://gist.github.com/zyrolasting/716aa9b09dd9f2436374428068a68aed
>>>
>>> I'm not sure what I'm missing since my reader does provide a single
>>> module declaration via the datums->module shown in the gist, and AFAIK I'm
>>> running an implied module below.
>>>
>>> What was my mistake? Apologies if I just went blind on a simple error.
>>>
>>> #lang reader "racket-koans-lang.rkt"
>>> (require rackunit)
>>>
>>> (struct _____)
>>> (struct _____)
>>> (let ([p _____])
>>>   (check-pred racketeer? p)
>>>   (check-pred programmer? p)
>>>   (check-pred struct? p)
>>>   (check-pred procedure? set-programmer-salary!)
>>>   (check-equal? (programmer-confusion-level p) 10)
>>>   (check-equal? (racketeer-desire-for-racket-jobs p) 9001))
>>>
>>>
>>>
>>> --
>>> 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.
>>>
>>

-- 
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