#lang racket/base
(require (for-syntax racket/base
                     syntax/stx
                     racket/syntax
                     "rep-data.rkt"
                     "rep.rkt")
         "parse.rkt"
         "keywords.rkt"
         "runtime.rkt"
         "runtime-report.rkt")

(provide define-syntax-class
         define-splicing-syntax-class

         syntax-parse
         syntax-parser

         (except-out (all-from-out "keywords.rkt")
                     ~reflect
                     ~splicing-reflect
                     ~eh-var)

         attribute
         this-syntax

         define/syntax-parse

         ;;----
         syntax-parser/template
         parser/rhs)

(begin-for-syntax
 (define (defstxclass stx header rhss splicing?)
   (parameterize ((current-syntax-context stx))
     (let-values ([(name formals arity)
                   (let ([p (check-stxclass-header header stx)])
                     (values (car p) (cadr p) (caddr p)))])
       (let* ([the-rhs (parse-rhs rhss #f splicing? #:context stx)]
              [opt-rhs+def
               (and (andmap identifier? (syntax->list formals))
                    (optimize-rhs the-rhs (syntax->list formals)))]
              [the-rhs (if opt-rhs+def (car opt-rhs+def) the-rhs)])
         (with-syntax ([name name]
                       [formals formals]
                       [rhss rhss]
                       [parser (generate-temporary (format-symbol "parse-~a" name))]
                       [arity arity]
                       [attrs (rhs-attrs the-rhs)]
                       [(opt-def ...)
                        (if opt-rhs+def
                            (list (cadr opt-rhs+def))
                            '())]
                       [options (rhs-options the-rhs)]
                       [integrate-expr
                        (syntax-case (rhs-integrate the-rhs) ()
                          [#s(integrate predicate description)
                             #'(integrate (quote-syntax predicate)
                                          'description)]
                          [#f
                           #''#f])])
           #`(begin (define-syntax name
                      (stxclass 'name 'arity
                                'attrs
                                (quote-syntax parser)
                                '#,splicing?
                                options
                                integrate-expr))
                    opt-def ...
                    (define-values (parser)
                      ;; If opt-rhs, do not reparse:
                      ;; need to keep same generated predicate name
                      #,(if opt-rhs+def
                            (begin
                              ;; (printf "Integrable syntax class: ~s\n" (syntax->datum #'name))
                              #`(parser/rhs/parsed
                                 name formals attrs #,the-rhs
                                 #,(and (rhs-description the-rhs) #t)
                                 #,splicing? #,stx))
                            #`(parser/rhs
                               name formals attrs rhss #,splicing? #,stx))))))))))

(define-syntax (define-syntax-class stx)
  (syntax-case stx ()
    [(dsc header . rhss)
     (defstxclass stx #'header #'rhss #f)]))

(define-syntax (define-splicing-syntax-class stx)
  (syntax-case stx ()
    [(dssc header . rhss)
     (defstxclass stx #'header #'rhss #t)]))

;; ----

(define-syntax (parser/rhs stx)
  (syntax-case stx ()
    [(parser/rhs name formals attrs rhss splicing? ctx)
     (with-disappeared-uses
      (let ([rhs
             (parameterize ((current-syntax-context #'ctx))
               (parse-rhs #'rhss (syntax->datum #'attrs) (syntax-e #'splicing?)
                          #:context #'ctx))])
        #`(parser/rhs/parsed name formals attrs
                             #,rhs #,(and (rhs-description rhs) #t)
                             splicing? ctx)))]))

(define-syntax (parser/rhs/parsed stx)
  (syntax-case stx ()
    [(prp name formals attrs rhs rhs-has-description? splicing? ctx)
     #`(let ([get-description
              (lambda formals
                (if 'rhs-has-description?
                    #,(rhs-description (syntax-e #'rhs))
                    (symbol->string 'name)))])
         (parse:rhs rhs attrs formals splicing?
                    (if 'rhs-has-description?
                        #,(rhs-description (syntax-e #'rhs))
                        (symbol->string 'name))))]))

;; ====

(define-syntax (syntax-parse stx)
  (syntax-case stx ()
    [(syntax-parse stx-expr . clauses)
     (quasisyntax/loc stx
       (let ([x (datum->syntax #f stx-expr)])
         (parse:clauses x clauses body-sequence #,((make-syntax-introducer) stx))))]))

(define-syntax (syntax-parser stx)
  (syntax-case stx ()
    [(syntax-parser . clauses)
     (quasisyntax/loc stx
       (lambda (x)
         (let ([x (datum->syntax #f x)])
           (parse:clauses x clauses body-sequence #,((make-syntax-introducer) stx)))))]))

(define-syntax (syntax-parser/template stx)
  (syntax-case stx ()
    [(syntax-parser/template ctx . clauses)
     (quasisyntax/loc stx
       (lambda (x)
         (let ([x (datum->syntax #f x)])
           (parse:clauses x clauses one-template ctx))))]))

;; ====

(define-syntax (define/syntax-parse stx)
  (syntax-case stx ()
    [(define/syntax-parse pattern . rest)
     (let-values ([(rest pattern defs)
                   (parse-pattern+sides #'pattern
                                        #'rest
                                        #:splicing? #f
                                        #:decls (new-declenv null)
                                        #:context stx)])
       (let ([expr
              (syntax-case rest ()
                [( expr ) #'expr]
                [_ (raise-syntax-error #f "bad syntax" stx)])]
             [attrs (pattern-attrs pattern)])
         (with-syntax ([(a ...) attrs]
                       [(#s(attr name _ _) ...) attrs]
                       [pattern pattern]
                       [(def ...) defs]
                       [expr expr])
           #'(defattrs/unpack (a ...)
               (let* ([x expr]
                      [cx x]
                      [pr (ps-empty x x)]
                      [es null]
                      [fh0 (syntax-patterns-fail x)])
                 def ...
                 (#%expression
                  (with ([fail-handler fh0]
                         [cut-prompt fh0])
                    (parse:S x cx pattern pr es
                             (list (attribute name) ...)))))))))]))
