#lang scheme/base
(require scheme/port
         "scheme-lexer.rkt")

(provide module-lexer)

(define (module-lexer in offset mode)
  (cond
   [(not mode)
    ;; Starting out: look for #lang:
    (let*-values ([(p) (peeking-input-port in)]
                  [(init) (file-position p)]
                  [(start-line start-col start-pos) (port-next-location p)])
      (let ([get-info (with-handlers ([exn:fail? (lambda (exn) 'fail)])
                        (read-language p (lambda () #f)))]
            [sync-ports (lambda ()
                          (read-bytes (- (file-position p) init) in))])
        (cond
         [(procedure? get-info)
          ;; Produce language as first token:
          (sync-ports)
          (let-values ([(end-line end-col end-pos) (port-next-location in)])
            (values
             "#lang"
             'other
             #f
             start-pos
             end-pos
             0
             (or (let ([v (get-info 'color-lexer #f)])
                   (and v
                        (if (procedure-arity-includes? v 3)
                            (cons v #f)
                            v)))
                 scheme-lexer)))]
         [(eq? 'fail get-info)
          (sync-ports)
          (let*-values ([(end-line end-col end-pos) (port-next-location in)])
            (values #f 'error #f start-pos end-pos 0 scheme-lexer))]
         [else
          ;; Start over using the Scheme lexer
          (module-lexer in offset scheme-lexer)])))]
   [(pair? mode)
    ;; #lang-selected language consumes and produces a mode:
    (let-values ([(lexeme type data new-token-start new-token-end backup-delta new-mode) 
                  ((car mode) in offset (cdr mode))])
      (values lexeme type data new-token-start new-token-end backup-delta (cons (car mode) new-mode)))]
   [else
    ;; #lang-selected language (or default) doesn't deal with modes:
    (let-values ([(lexeme type data new-token-start new-token-end) 
                  (mode in)])
      (values lexeme type data new-token-start new-token-end 0 mode))]))
