#!/usr/bin/clisp -ansi -norc -q -E iso-8859-1
;; -*- mode:lisp;coding:iso-8859-1 -*-
(setf *print-right-margin* 80
      *print-pretty* t
      *print-case* :upcase)

(defun split-string (string &optional (separators " ") (remove-empty nil))
STRING:         A sequence.

SEPARATOR:      A sequence.

RETURN:         A list of subsequence of STRING, split upon any element of SEPARATORS.
                Separators are compared to elements of the STRING with EQL.

NOTE:           It's actually a simple split-sequence now.

EXAMPLES:       (split-string '(1 2 0 3 4 5 0 6 7 8 0 9) '(0))
                --> ((1 2) (3 4 5) (6 7 8) (9))
                (split-string #(1 2 0 3 4 5 0 6 7 8 0 9) #(0))
                --> (#(1 2) #(3 4 5) #(6 7 8) #(9))
                (split-string \"1 2 0 3 4 5 0 6 7 8\" '(#\space #\0))
                --> (\"1\" \"2\" \"\" \"\" \"3\" \"4\" \"5\" \"\" \"\" \"6\" \"7\" \"8\")
    :with strlen = (length string)
    :for position = 0 :then (1+ nextpos)
    :for nextpos = (position-if (lambda (e) (find e separators)) string :start position)
    :unless (and remove-empty
                 (or (and (= position strlen) (null nextpos ))
                     (eql position nextpos)))
    :collect (subseq string position nextpos)
    :while (and nextpos (< position strlen))))

(defun maptree (fun &rest trees)
DO:     Calls FUN on each non-null atom of the TREES.
PRE:    The trees in TREES must be congruent, or else the result is
        pruned like the smallest tree.
RETURN: A tree congruent to the TREES, each node being the result of
        FUN (it may be a subtree).
  (cond ((null trees) nil)
        ((every (function null)  trees) nil)
        ((every (function atom)  trees) (apply fun trees))
        ((every (function consp) trees)
         (cons (apply (function maptree) fun (mapcar (function car) trees))
               (apply (function maptree) fun (mapcar (function cdr) trees))))
        (t nil)))

(defpackage "CFFI"

(defparameter *clang-package-name* "COM.OGAMITA.CLANG")
(make-package "COM.OGAMITA.CLANG" :use '())

(defparameter *lispified* (make-hash-table))

(defun lispify-name (csym)
  (flet ((lispify-name (cname)
           (with-output-to-string (*standard-output*)
               :with state = :out
               :for ch :across cname
               :do (if (alpha-char-p ch)
                     (ecase state
                        (setf state (cond
                                      ((upper-case-p ch) :upper)
                                      ((lower-case-p ch) :lower)
                                      (t                 state)))
                        (princ (string-upcase ch)))
                        (when (lower-case-p ch)
                          (setf state :lower))
                        (princ (string-upcase ch)))
                        (when (upper-case-p ch)
                          (setf state :upper)
                          (princ "-"))
                        (princ (string-upcase ch))))
                       (setf state :out)
                       (case ch
                         ((#\_)     (princ "-"))
                         (otherwise (princ ch)))))))))
    (let ((cname (symbol-name csym)))
        ((and (< 6 (length cname))
              (string= "clang_" cname :end2 6))
         (lispify-name (subseq cname 6)))
        ((and (< 2 (length cname))
              (string= "CX" cname :end2 2))
         (lispify-name (subseq cname 2)))
         (lispify-name cname))))))

(defun lispify-clang-symbol (symbol)
  (or (gethash symbol *lispified*)
      (setf (gethash symbol *lispified*)
            (intern (lispify-name symbol) *clang-package-name*))))

(defparameter *sexps*
        :with eof = '#:eof
        :initially (setf (readtable-case *readtable*) :invert)
        :for sexp = (read *standard-input* nil eof)
        :until (eq sexp eof)
        :collect sexp)
    (setf (readtable-case *readtable*) :upcase)))

;; lispify symbols
;; export symbols from com.ogamita.clang

(defun lispify (atom)
  (gethash atom *lispified* atom))

(defun lispify-sexp-1 (sexp)
  (if (atom sexp)
    (case (first sexp)
       (destructuring-bind (op (cname lisp-name) res-type &rest parameters) sexp
         `(,op (,cname ,(lispify-clang-symbol lisp-name)) ,(lispify res-type)
               ,@(mapcar (lambda (param)
                             `(,(first param) ,(lispify (second param))))
       (destructuring-bind (op name expr) sexp
         `(,op ,(lispify-clang-symbol name) ,(maptree (function lispify) expr))))
       (destructuring-bind (op name &rest constants) sexp
         `(,op ,(lispify-clang-symbol name)
               ,@(mapcar (lambda (constant)
                             (if (atom constant)
                               (intern (lispify-name constant) "KEYWORD")
                               `(,(intern (lispify-name (first constant)) "KEYWORD")
                                  ,(second constant))))
       (destructuring-bind (op name ctype) sexp
         `(,op ,(lispify-clang-symbol name) ,(lispify ctype))))
       (destructuring-bind (op name &rest slots) sexp
         `(,op ,(lispify-clang-symbol name)
               ,@(mapcar (lambda (slot)
                             `(,(lispify-clang-symbol (first slot))
                               ,(lispify (second slot))))

(defun lispify-sexp-2 (sexp)
  (if (atom sexp)
    (lispify sexp)
    (case (first sexp)
      ((cl:defconstant) sexp)
      ((cffi:defcfun) sexp)
      ((cffi:defcenum) sexp)
      ((cffi:defctype) sexp)
      ((cffi:defcstruct) sexp)
       (maptree (function lispify) sexp)))))

(setf *sexps* (mapcar (function lispify-sexp-1) *sexps*))
(setf *sexps* (mapcar (function lispify-sexp-2) *sexps*))

(defparameter *clang-exports*  (let ((syms '()))
                                 (do-symbols (symbol *clang-package-name* syms)
                                   (push symbol syms))))
(export *clang-exports* *clang-package-name*)

(format t ";;;; -*- mode:lisp; coding:utf-8 -*-~2%")

 `(defpackage ,*clang-package-name*
    (:export ,@(mapcar (function symbol-name) *clang-exports*))))

(dolist (sexp *sexps*)
  (pprint sexp))

(ext:exit 0)

sed	-e 1i\\ -e '(in-package "COM.OGAMITA.CLANG")' \
	-e 's/(cl:defconstant CINDEX_VERSION .*)/(cl:defconstant CINDEX_VERSION (cl:+ (cl:* CINDEX_VERSION_MAJOR 10000) (cl:* CINDEX_VERSION_MINOR 1)))/' \
	-e 's/#\.(cl:logior CXGlobalOpt_ThreadBackgroundPriorityForIndexing CXGlobalOpt_ThreadBackgroundPriorityForEditing)/3/' \