Ohhh, thank you so much, Sorawee! Now I have wealth of code to study.
By the way, I tried to send my full code but apparently once again I hit
the "reply" button instead of "reply all" and I only sent it to David, so
here it is again in case anyone wants to play with it. I haven't
implemented field accessors yet because those are trivial (and since I keep
field names somewhere else, I could implement them differently, like (get
'field mycard), though that would probably be slower).
#lang racket/base
(require (for-syntax racket/base
syntax/parse
racket/syntax)
syntax/parse/define)
(require (only-in racket ~a))
(provide card) ;;;; create a card-out thingy
(define-for-syntax (parse-args xs [rs '()])
(define (fn xs)
(if [null? xs] '[]
[let [(a (car xs))
(bs (cdr xs))]
(cond [(keyword? a) (fn bs)]
[(list? a) (cons (car a) (fn bs))]
[else (cons a (fn bs))])]))
(let [(parsed (fn xs))
(dotlist rs)]
(if [null? dotlist]
parsed [append parsed (list dotlist)])))
(define keyword (string->keyword "~a"))
(define keyword-prefix "#:")
(define keyword-suffix "")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
(require (only-in racket/struct
make-constructor-style-printer))
(define-syntax (protocard stx)
(syntax-case stx ()
([_ super (name fields ... . xs)]
(with-syntax [(descr (format-id #'name "record:~a" #'name))
(pred? (format-id #'name "~a?" #'name))
(maker (format-id #'name "make-~a" #'name))
(ref (format-id #'name "~a-ref" #'name))
(set (format-id #'name "~a-set!" #'name))
(field-names (format-id #'name "*~a-fields" #'name))
(explain (format-id #'name "explain-~a" #'name))
(tag-params (tag-args (syntax->datum #'(fields ...))
(syntax->datum #'xs)))
(plain-params
(cons list (map [λ (x) (format-id #'name "~a" x)]
[parse-args (syntax->datum #'(fields
...))
(syntax->datum #'xs)])))]
;;; éste debería de salir de tag-params
#`[begin
(define field-names '(fields ... . xs))
(define-values (descr maker pred? ref set)
(make-struct-type
'name super (length 'tag-params) 0 #f ;; (- (length
super-fields))
(list (cons prop:custom-write
(make-constructor-style-printer
(λ (obj)
(apply string-append
(symbol->string 'name)
(for/list ((arg 'tag-params) (i
(in-naturals 0)))
(print-params (ref obj i) arg))))
(λ (obj) '()) )))))
(define (explain obj)
(for/list [(p (cdr 'plain-params)) (i (in-naturals 0))]
(list p (ref obj i))))
(define (name fields ... . xs)
(apply maker plain-params))
;;; accessors!!
#|(define whatev
(append 'field-names
'super-fields))|#
;#,@[if ]
]))))
(define-syntax (card stx)
(syntax-case stx ()
([_ (name fields ... . xs)]
#'[protocard #f (name fields ... . xs)])
([_ super (name fields ... . xs)]
#'[protocard super (name fields ... . xs)])))
;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
(define-for-syntax (tag-args xs [rs '()])
(define parsed
(if [null? xs] '[]
[let ((a (car xs))
(bs (cdr xs)))
(cond ((symbol? a) (cons a (tag-args bs)))
((list? a) (cons a (tag-args bs)))
((keyword? a)
(cons (list a (car bs))
(tag-args (cdr bs)))))]))
(if [null? rs] parsed [append parsed (list rs)]))
;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (write-if-not-default a-pair val)
(if [equal? (cadr a-pair) val] ""
[string-append " " (~a val)]))
(define (print-params val arg)
(cond
((symbol? arg)
(string-append " " (~a val)))
((keyword? (car arg))
(let ((x (cadr arg))
(res (string-append " " keyword-prefix
(keyword->string (car arg))
keyword-suffix " " (~a val))))
(if (list? x) ;; if it's a default arg, x (cadr arg) appears as a
list
(if (eq? (cadr x) val) "" ;; don't show if it has the default
value (cadr x)
res) res)))
((list? arg)
(write-if-not-default arg val))))
On Saturday, 18 September 2021 at 03:06:36 UTC+2 [email protected] wrote:
> 2) (card (line . xs)) has only one field, xs. Of course, you could also
>> define it as a normal field which contains a list, but there's some other
>> scenarios where I found it more elegant to represent it as a dotted
>> argument (like representing s-expressions as a struct).
>>
> Oh sorry, that was a typo. I meant currently you expect
>
> > (card (line . xs))
> > (line 1 2 3 4 5 6 7 8 9)
> (line 1 2 3 4 5 6 7 8 9)
>
> to be the output, but I was asking if:
>
> > (card (line . xs))
> > (line 1 2 3 4 5 6 7 8 9)
> (line '(1 2 3 4 5 6 7 8 9))
>
> makes more sense. In any case, your response clears things up that there
> is indeed only one field. You simply want it to be printed like that.
>
> This is actually a pretty fun problem. Here’s a quick prototype. Dropping
> it here in case anyone is interested:
>
> #lang racket
>
> (require syntax/parse/define
> (for-syntax syntax/parse/lib/function-header
> racket/syntax
> racket/list
> racket/struct-info))
>
> (begin-for-syntax
> (struct my-struct-info (fields args ctor)
> #:property prop:procedure
> (λ (inst stx)
> (syntax-parse stx
> [(_ args ...) #`(#,(my-struct-info-ctor inst) args ...)]
> [x:id #'#,(my-struct-info-ctor inst)]))))
>
> (define-syntax-parse-rule (define-accessors+predicate
> {~var struct-id (static values #f)}
> name:id)
> #:with (fields ...) (struct-field-info-list (attribute struct-id.value))
> #:do [(define the-struct-info (extract-struct-info (attribute
> struct-id.value)))]
> #:with predicate (list-ref the-struct-info 2)
> #:with (accessors ...) (list-ref the-struct-info 3)
> #:with new-predicate (format-id #'name "~a?" #'name)
> #:with (new-accessors ...)
> (map (λ (id) (format-id #'name "~a-~a" #'name id)) (attribute fields))
>
> (begin
> (define new-predicate predicate)
> (define new-accessors accessors) ...))
>
> (define-syntax-parse-rule
> (card
> {~optional (~var super-id (static my-struct-info? "card type"))}
> {~and header:function-header (_:id . args)})
>
> #:with ((all-fields ...) all-args)
> (let ([info (attribute super-id.value)])
> (cond
> [info
> (unless (list? (syntax-e (my-struct-info-args info)))
> (raise-syntax-error 'card
> "supertype can't have variadic fields"
> this-syntax))
> #`(({~@ . #,(my-struct-info-fields info)} . header.params)
> ({~@ . #,(my-struct-info-args info)} . args))]
> [else #'(header.params args)]))
>
> #:fail-when (check-duplicates (attribute all-fields) #:key syntax-e)
> "duplicate field name"
>
> (begin
> (struct shadow (all-fields ...)
> #:transparent
> ;; TODO: implement gen:custom-write (probably with
> make-constructor-style-printer)
> ;; to customize struct value printing
> #:reflection-name 'header.name)
> (define-accessors+predicate shadow header.name)
> (define (shadow-ctor . all-args)
> (shadow all-fields ...))
> (define-syntax header.name
> (my-struct-info #'(all-fields ...)
> #'all-args
> #'shadow-ctor))))
>
> (let ()
> (card (hola a b #:c c))
> (println (hola 1 2 #:c 3))
>
> (card (ciao a [b 3]))
> (println (ciao 7))
> (println (ciao 7 4))
>
> (card (line . xs))
> (println (line 1 2 3 4 5 6 7 8 9)))
>
> (let ()
> (card (hola a #:b b))
> (card hola (ciao c))
> (define v (ciao 1 #:b 2 3))
> (println v)
> (println (list (ciao-a v) (ciao-b v) (ciao-c v)))
> (println (list (ciao? v) (hola? v))))
>
> (let ()
> (card (foo . xs))
> ;; uncomment should result in a syntax error
> (card #;foo (bar . ys))
>
> (card (a xs))
> ;; uncomment should result in a syntax error
> (card #;a (b xs))
>
> (void))
>
> What I did not implement is making the struct value printed in the way you
> want, but that can be adjusted by using gen:custom-write. Note that I
> didn’t (re)use struct‘s supertype feature since you want fields in the
> opposite order.
>
>
--
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 [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/racket-users/0a94926b-5a19-46e9-b7ea-92b8c83f16fan%40googlegroups.com.