;; TODO
(defun _map (f xs)
  (if (nil? xs)
    nil
    (cons (f (car xs)) (_map f (cdr xs))))
  )

(defun _red/syntax-make-keyword-rule (syntax pattern category prev-state next-state)
  `(red/syntax/define-keyword-rule ,syntax ,prev-state ,pattern ,category ,next-state))
(defun _red/syntax-rule-keyword (syntax clause)
  ;; (:keyword "PATTERNS"... :category "CATEGORY" :prev-state N :next-state M)
  (let (
        prev-state 0
        next-state nil
        patterns nil
        category nil
        )
    (while (not (nil? clause))
      (cond
        ; :prev-state N
        ((= (car clause) ':prev-state)
         (progn
           (setq prev-state (cadr clause))
           (setq clause (cdr clause))
           )
         )
        ; :next-state N
        ((= (car clause) ':next-state)
         (progn
           (setq next-state (cadr clause))
           (setq clause (cdr clause))
           )
         )
        ; :category "CATEGORY"
        ((= (car clause) ':category)
         (progn
           (setq category (cadr clause))
           (setq clause (cdr clause))
           )
         )
        ; "PATTERNS"...
        (&otherwise (setq patterns (cons (car clause) patterns)))
        )
      (setq clause (cdr clause))
      )

    ; (when (or (nil? category) (nil? patterns))
    ;   (error "Invalid clause")
    ;   )

    (cons
      'progn
      (_map
        (lambda (pattern) (_red/syntax-make-keyword-rule syntax pattern category prev-state next-state))
        patterns
        )
      )
    )
  )
(defun _red/syntax-rule-regex (syntax clause)
  ;; (:regex "REGEX" "CATEGORY" :prev-state N :next-state M)
  (let (
        prev-state 0
        next-state nil
        regex (car clause)
        category (cadr clause)
        )
    (setq clause (cdr (cdr clause)))
    (while (not (nil? clause))
      (cond
        ; :prev-state N
        ((= (car clause) ':prev-state)
         (progn
           (setq prev-state (cadr clause))
           (setq clause (cdr clause))
           )
         )
        ; :next-state M
        ((= (car clause) ':next-state)
         (progn
           (setq next-state (cadr clause))
           (setq clause (cdr clause))
           )
         )
        )
      (setq clause (cdr clause))
      )
    `(red/syntax/define-regex-rule ,syntax ,prev-state ,regex ,category ,next-state)
    )
  )
(defun _red/syntax-style-category (syntax clause)
  ;; ("CATEGORY" :foreground 'COLOR :background 'COLOR :bold)
  (let (category (car clause) foreground nil background nil bold nil)
    (setq clause (cdr clause))
    (while (not (nil? clause))
      (cond
        ; :foreground 'COLOR
        ((or (= (car clause) ':foreground) (= (car clause) ':fg))
         (progn
           (setq foreground (cadr clause))
           (setq clause (cdr clause))
           )
         )
        ; :background 'COLOR
        ((or (= (car clause) ':background) (= (car clause) ':bg))
         (progn
           (setq background (cadr clause))
           (setq clause (cdr clause))
           )
         )
        ; :bold
        ((= (car clause) ':bold) (setq bold #t))
        )
      (setq clause (cdr clause))
      )
    (when (not (or foreground background bold))
      (error "Empty clause")
      )
    `(red/syntax/define-category-style ,syntax ,category ,foreground ,background ,bold)
    )
  )

(defun _red/define-syntax (syntax clauses)
  (let (output nil current-state nil)
    (while (cons? clauses)
      (let (clause (car clauses))
        (cond
          ((= clause ':styles) (setq current-state 'styles))
          ((= clause ':rules) (setq current-state 'rules))
          ;; ("style" ...) clauses
          ((= current-state 'styles) (setq output (cons (_red/syntax-style-category syntax clause) output)))
          ;; (:regex ...) clauses
          ((and
             (= current-state 'rules)
             (cons? clause)
             (= (car clause) ':regex)
             (cons? (cdr clause))
             )
           (setq output (cons (_red/syntax-rule-regex syntax (cdr clause)) output))
           )
          ;; (:keyword ...) clauses
          ((and
             (= current-state 'rules)
             (cons? clause)
             (= (car clause) ':keyword)
             (cons? (cdr clause))
             )
           (setq output (cons (_red/syntax-rule-keyword syntax (cdr clause)) output))
           )
          )
        )
      (setq clauses (cdr clauses))
      )
    (if (nil? output)
      nil
      (cons 'progn output)
      )
    )
  )
(defmacro define-syntax (syntax &rest clauses)
  (_red/define-syntax syntax clauses)
  )

;; Load syntax files
;; TODO glob
(import "syntax/lysp.lysp")
(import "syntax/rust.lysp")

(red/syntax/reset)
