#lang zuo/base

(provide config-file->hash)

(define (config-file->hash path [vars (hash)])
  (unless (path-string? path) (arg-error 'config->hash "path string" path))
  (unless (hash? vars) (arg-error 'config->hash "hash table" vars))
  (define content (file->string path))
  (define no-cr-content (string-join (string-split content "\r") ""))
  (define lines (string-split (string-join (string-split no-cr-content "\\\n") "") "\n"))
  (define config
    (foldl (lambda (line accum)
             (define positions ; (list var-start var-end =-pos) or #f
               (let loop ([i 0] [start #f] [end #f])
                 (cond
                   [(= i (string-length line)) #f]
                   [else
                    (let ([c (string-ref line i)])
                      (cond
                        [(= (char "=") c) (and start (list start (or end i) i))]
                        [(or (= (char "_") c)
                             (and (<= (char "a") c)
                                  (<= c (char "z")))
                             (and (<= (char "A") c)
                                  (<= c (char "Z")))
                             (and (<= (char "0") c)
                                  (<= c (char "9"))))
                         (and (not end)
                              (loop (+ i 1) (or start i) #f))]
                        [(= (char " ") c)
                         (if start
                             (loop (+ i 1) start (or end i))
                             (loop (+ i 1) #f #f))]
                        [else #f]))])))
             (cond
               [positions
                (define var (string->symbol (substring line (car positions) (cadr positions))))
                (define rhs (substring line (+ (list-ref positions 2) 1) (string-length line)))
                (hash-set accum var (string-trim (remove-makefile-comment rhs)))]
               [else accum]))
           (hash)
           lines))
  (foldl (lambda (key config)
           (hash-set config key (hash-ref vars key) ))
         config
         (hash-keys vars)))

(define (remove-makefile-comment s)
  (define l (string-split s "#"))
  (cond
    [(= (length l) 1) s]
    [else
     ;; A `\` just before `#` escapes the `#`
     (let loop ([l l])
       (cond
         [(null? l) ""]
         [else
          (let* ([s (car l)]
                 [len (string-length s)])
            (cond
              [(= len 0) ""]
              [(= (char "\\") (string-ref s (- len 1)))
               (~a (substring s 0 (- len 1))
                   "#"
                   (loop (cdr l)))]
              [else s]))]))]))
