#lang racket

;; ---------------------------------------------------------------------------------------------------
;; provide a mechanism for defining the shape of big-bang and universe clauses 
;; to specify individual clauses see syn-aux.rkt

(provide define-keywords DEFAULT)

(require (for-syntax syntax/parse))

(define-syntax (DEFAULT stx)
  (raise-syntax-error 'DEFAULT "used out of context" stx))

(define-syntax (define-keywords stx)
  (syntax-parse stx #:literals (DEFAULT) 
    [(_ the-list super-list define-create
        (kw:identifier 
         (~optional kw-alt:identifier #:defaults ((kw-alt #'kw)))
         (~optional (~seq DEFAULT default:expr))
         coerce:expr) ...)
     (let* ([defs (attribute default)])
       #`(begin
           ;; define and create list of keywords and associated values 
           (define-for-syntax the-list
             (list* (list #'kw #'kw-alt (coerce ''kw) default) ... super-list))
           ;; define and provide keywords
           (provide (rename-out (kw  kw-alt) ...))
           (provide kw ...) 
           (define-syntaxes (kw ...)
             (values (lambda (x) (raise-syntax-error 'kw "used out of context" x)) ...))
           
           ;; a macro for creating functions that instantiate the proper object
           ;; (define-create para ...) :: additional parameters for the new func
           (define-syntax (define-create stx)
             (syntax-case stx ()
               [(_ para (... ...))
                (let*-values
                    ([(kwds defs)
                      (values (map car the-list) '())]
                     ;; the defaults list defs is no longer needed
                     [(args) (lambda (para*) (append para* (foldr cons '() kwds)))]
                     [(body) (lambda (para*) (map (lambda (x) `(,x ,x)) (append para* kwds)))])
                  (let ([para* (syntax->list #'(para (... ...)))])
                    #`(lambda (%)
                        (lambda #,(args para*)
                          (lambda ()
                            (new % #,@(body para*)))))))]))))]))
