summaryrefslogtreecommitdiff
path: root/cmamut.lisp
diff options
context:
space:
mode:
Diffstat (limited to 'cmamut.lisp')
-rw-r--r--cmamut.lisp171
1 files changed, 171 insertions, 0 deletions
diff --git a/cmamut.lisp b/cmamut.lisp
index 7b64756..97db0f5 100644
--- a/cmamut.lisp
+++ b/cmamut.lisp
@@ -1 +1,172 @@
(in-package #:cmamut)
+
+; Ideas:
+; Generate code that creates a package and puts all definitions there.
+; If symbol names are unaltered we can skip connecting everything together.
+; To avoid collisions the created package shouldn't import any symbols.
+; Maybe use straight defuns and ffi functions to get as close as possible to the C names without much work.
+; Only define functions from the target library, but get all other definitions.
+
+; TODO: Get constants from macro file generated by c2ffi -M
+; TODO: Generate enum definitions
+
+(defvar reference-table (make-hash-table :test #'equal :size 256))
+
+(defun get-spec ()
+ (json:from-file "~/common-lisp/cmamut/spec.json"))
+
+(defun cook-type (raw-type)
+ (let ((tag (gethash "tag" raw-type)))
+ (cond ((string= tag ":void")
+ 'sb-alien:void)
+ ((string= tag ":function-pointer")
+ 'sb-alien:function)
+ ((string= tag ":pointer")
+ (let ((pointed-type (gethash "type" raw-type)))
+ (if (string= (gethash "tag" pointed-type) ":char")
+ 'sb-alien:c-string
+ (list 'sb-alien:* (cook-type pointed-type)))))
+ ((string= tag ":float")
+ 'sb-alien:float)
+ ((string= tag ":double")
+ 'sb-alien:double)
+ ((string= tag ":unsigned-char")
+ 'sb-alien:unsigned-char)
+ ((string= tag ":unsigned-short")
+ 'sb-alien:unsigned-short)
+ ((string= tag ":unsigned-int")
+ 'sb-alien:unsigned-int)
+ ((string= tag ":unsigned-long")
+ 'sb-alien:unsigned-long)
+ ((string= tag ":char")
+ 'sb-alien:char)
+ ((string= tag ":short")
+ 'sb-alien:short)
+ ((string= tag ":int")
+ 'sb-alien:int)
+ ((string= tag ":long")
+ 'sb-alien:long)
+ ((string= tag ":struct")
+ (list 'sb-alien:struct (gethash "name" raw-type)))
+ ((string= tag ":union")
+ (list 'sb-alien:union (gethash "name" raw-type)))
+ ((string= tag "struct")
+ (cook-struct raw-type))
+ ((string= tag "union")
+ (cook-union raw-type))
+ (t tag))))
+
+(defun cook-function (raw-function &optional name-transformer)
+ (let ((raw-params (gethash "parameters" raw-function))
+ (cooked-params (queue:new)))
+ (dotimes (j (length raw-params))
+ (queue:add cooked-params
+ (list (intern (gethash "name" (aref raw-params j)))
+ (cook-type (gethash "type" (aref raw-params j))))))
+ `(sb-alien:define-alien-routine
+ ,(let ((function-name (gethash "name" raw-function)))
+ (if name-transformer
+ (list function-name (funcall name-transformer function-name))
+ function-name))
+ ,(cook-type (gethash "return-type" raw-function))
+ ,@(queue:to-list cooked-params))))
+
+(defun cook-typedef (raw-typedef)
+ (let* ((base-type (gethash "type" raw-typedef))
+ (new-name (gethash "name" raw-typedef))
+ (enum? (string= (gethash "tag" base-type) ":enum")))
+ (when enum?
+ (setf (gethash (gethash "id" base-type) reference-table)
+ new-name))
+ `(sb-alien:define-alien-type
+ ,new-name
+ ,(if enum?
+ `(enum ,new-name)
+ (cook-type base-type)))))
+
+(defun cook-struct (raw-struct)
+ (let ((raw-fields (gethash "fields" raw-struct))
+ (cooked-fields (queue:new)))
+ (dotimes (j (length raw-fields))
+ (queue:add cooked-fields
+ (list (intern (gethash "name" (aref raw-fields j)))
+ (cook-type (gethash "type" (aref raw-fields j))))))
+ `(sb-alien:struct
+ ,(gethash "name" raw-struct)
+ ,@(queue:to-list cooked-fields))))
+
+(defun cook-union (raw-union)
+ (let ((raw-fields (gethash "fields" raw-union))
+ (cooked-fields (queue:new)))
+ (dotimes (j (length raw-fields))
+ (queue:add cooked-fields
+ (list (intern (gethash "name" (aref raw-fields j)))
+ (cook-type (gethash "type" (aref raw-fields j))))))
+ `(sb-alien:union
+ ,(gethash "name" raw-union)
+ ,@(queue:to-list cooked-fields))))
+
+;(defun cook-enum (raw-enum)
+; (let ((name (gethash (gethash "id" raw-enum) reference-table))
+; (raw-fields (gethash "fields" raw-enum))
+; (cooked-fields (queue:new)))
+
+(defstruct spec
+ functions
+ typedefs
+ structs
+ unions
+ enums)
+
+(defun prepare-spec (spec)
+ (clrhash reference-table)
+ (let ((s (make-spec)))
+ (dotimes (i (length spec))
+ (cond ((string= (gethash "tag" (aref spec i)) "function")
+ (push (aref spec i) (spec-functions s)))
+ ((string= (gethash "tag" (aref spec i)) "typedef")
+ (push (aref spec i) (spec-typedefs s)))
+ ((string= (gethash "tag" (aref spec i)) "struct")
+ (push (aref spec i) (spec-structs s)))
+ ((string= (gethash "tag" (aref spec i)) "enum")
+ (push (aref spec i) (spec-enums s)))
+ ((string= (gethash "tag" (aref spec i)) "union")
+ (push (aref spec i) (spec-unions s)))
+ (t)))
+; typedefs must be generated first to build the reference table
+ (setf (spec-typedefs s) (mapcar #'cook-typedef (spec-typedefs s)))
+ (setf (spec-functions s) (mapcar #'cook-function (spec-functions s)))
+ ;(setf (spec-enums s) (mapcar #'cook-enum (spec-enums s)))
+ (setf (spec-structs s)
+ (mapcar (lambda (x)
+ `(sb-alien:define-alien-type nil ,(cook-struct x)))
+ (spec-structs s)))
+ (setf (spec-unions s)
+ (mapcar (lambda (x)
+ `(sb-alien:define-alien-type nil ,(cook-union x)))
+ (spec-unions s)))
+ s))
+
+(defun to-file (spec filename)
+ (with-open-file (f filename
+ :direction :output
+ :if-exists :overwrite
+ :if-does-not-exist :create)
+; definitions must be written in the following order to avoid "unknown type" errors
+ (princ ";;; GENERATED BY CMAMUT" f)
+ (terpri f)
+ (princ ";typedefs" f)
+ (terpri f)
+ (format f "~{~s~&~}" (spec-typedefs spec))
+ (princ ";enums" f)
+ (terpri f)
+ (format f "~{~s~&~}" (spec-enums spec))
+ (princ ";structs" f)
+ (terpri f)
+ (format f "~{~s~&~}" (spec-structs spec))
+ (princ ";unions" f)
+ (terpri f)
+ (format f "~{~s~&~}" (spec-unions spec))
+ (princ ";functions" f)
+ (terpri f)
+ (format f "~{~s~&~}" (spec-functions spec))))