chiark / gitweb /
src/method-impl.lisp: Initialize `suppliedp' flags properly.
[sod] / src / codegen-proto.lisp
index 6b1f947f3f7fd669d453e89f96b1e8583b4aa335..856e44e7f999ea188e061a2f48ec3fcaea54fad2 100644 (file)
@@ -7,7 +7,7 @@
 
 ;;;----- Licensing notice ---------------------------------------------------
 ;;;
-;;; This file is part of the Sensble Object Design, an object system for C.
+;;; This file is part of the Sensible Object Design, an object system for C.
 ;;;
 ;;; SOD is free software; you can redistribute it and/or modify
 ;;; it under the terms of the GNU General Public License as published by
@@ -64,6 +64,15 @@ (defparameter *sod-master-ap*
   (make-instance 'temporary-name :tag "sod__master_ap"))
 (defparameter *sod-tmp-ap*
   (make-instance 'temporary-name :tag "sod__tmp_ap"))
+(defparameter *sod-tmp-val*
+  (make-instance 'temporary-name :tag "sod__t"))
+(defparameter *sod-keywords*
+  (make-instance 'temporary-name :tag "sod__kw"))
+(defparameter *sod-key-pointer*
+  (make-instance 'temporary-name :tag "sod__keys"))
+
+(export '*null-pointer*)
+(defparameter *null-pointer* "NULL")
 
 ;;;--------------------------------------------------------------------------
 ;;; Instructions.
@@ -122,8 +131,8 @@ (defmacro definst (code (streamvar &key export) args &body body)
      * Instance slots named after the ARGS, with matching keyword initargs,
        and `inst-ARG' readers.
 
-     * A constructor `make-CODE-inst' which accepts the ARGS (in order, not
-       with keywords) as arguments and returns a fresh instance.
+     * A constructor `make-CODE-inst' which accepts the ARGS (as an ordinary
+       BVL) as arguments and returns a fresh instance.
 
      * A print method, which prints a diagnostic dump if `*print-escape*' is
        set, or invokes the BODY (with STREAMVAR bound to the output stream)
@@ -133,66 +142,43 @@ (defmacro definst (code (streamvar &key export) args &body body)
    If EXPORT is non-nil, then export the `CODE-inst' and `make-CODE-inst'
    symbols."
 
-  (let ((inst-var (gensym "INST"))
-       (class-name (symbolicate code '-inst))
-       (constructor-name (symbolicate 'make- code '-inst))
-       (keys (mapcar (lambda (arg) (intern (symbol-name arg) :keyword))
-                     args)))
+  (let* ((inst-var (gensym "INST"))
+        (class-name (symbolicate code '-inst))
+        (constructor-name (symbolicate 'make- code '-inst))
+        (slots (mapcan (lambda (arg)
+                         (if (listp arg) (list (car arg))
+                             (let ((name (symbol-name arg)))
+                               (if (and (plusp (length name))
+                                        (char/= (char name 0) #\&))
+                                   (list arg)
+                                   nil))))
+                       args))
+        (keys (mapcar (lambda (arg) (intern (symbol-name arg) :keyword))
+                      slots)))
     `(progn
        (defclass ,class-name (inst)
-        ,(mapcar (lambda (arg key)
-                   `(,arg :initarg ,key :reader ,(symbolicate 'inst- arg)))
-                 args keys))
+        ,(mapcar (lambda (slot key)
+                   `(,slot :initarg ,key
+                           :reader ,(symbolicate 'inst- slot)))
+                 slots keys))
        (defun ,constructor-name (,@args)
-        (make-instance ',class-name ,@(mappend #'list keys args)))
+        (make-instance ',class-name ,@(mappend #'list keys slots)))
        (defmethod inst-metric ((,inst-var ,class-name))
-        (with-slots (,@args) ,inst-var
-          (+ 1 ,@(mapcar (lambda (arg) `(inst-metric ,arg)) args))))
+        (with-slots (,@slots) ,inst-var
+          (+ 1 ,@(mapcar (lambda (slot) `(inst-metric ,slot)) slots))))
        (defmethod print-object ((,inst-var ,class-name) ,streamvar)
-        (with-slots (,@args) ,inst-var
+        (with-slots (,@slots) ,inst-var
           (if *print-escape*
               (print-unreadable-object (,inst-var ,streamvar :type t)
                 (format stream "~@<~@{~S ~@_~S~^ ~_~}~:>"
-                        ,@(mappend #'list keys args)))
-              (progn ,@body))))
+                        ,@(mappend #'list keys slots)))
+              (block ,code ,@body))))
        ,@(and export `((export '(,class-name ,constructor-name
-                                ,@(mapcar (lambda (arg)
-                                            (symbolicate 'inst- arg))
-                                          args)))))
+                                ,@(mapcar (lambda (slot)
+                                            (symbolicate 'inst- slot))
+                                          slots)))))
        ',code)))
 
-;; Important instruction classes.
-
-;; HACK: use a gensym for the `expr' and `type' slots to avoid leaking the
-;; slot names, since the symbol `expr' is exported from our package and
-;; `type' belongs to the `common-lisp' package.
-
-(definst var (stream :export t) (name #1=#:type init)
-  (pprint-c-type #1# stream name)
-  (when init
-    (format stream " = ~A" init))
-  (write-char #\; stream))
-(definst set (stream :export t) (var #1=#:expr)
-  (format stream "~@<~A = ~@_~2I~A;~:>" var #1#))
-(definst update (stream :export t) (var op #1=#:expr)
-  (format stream "~@<~A ~A= ~@_~2I~A;~:>" var op #1#))
-(definst return (stream :export t) (#1=#:expr)
-  (format stream "return~@[ (~A)~];" #1#))
-(definst break (stream :export t) ()
-  (format stream "break;"))
-(definst continue (stream :export t) ()
-  (format stream "continue;"))
-(definst expr (stream :export t) (#1=#:expr)
-  (format stream "~A;" #1#))
-(definst block (stream :export t) (decls body)
-  (format stream "{~:@_~@<  ~2I~@[~{~A~:@_~}~:@_~]~{~A~^~:@_~}~:>~:@_}"
-         decls body))
-(definst function (stream :export t) (name #1=#:type body)
-  (pprint-logical-block (stream nil)
-    (princ "static " stream)
-    (pprint-c-type #1# stream name)
-    (format stream "~:@_~A~:@_~:@_" body)))
-
 ;; Formatting utilities.
 
 (defun format-compound-statement* (stream child morep thunk)
@@ -209,13 +195,13 @@ (defun format-compound-statement* (stream child morep thunk)
           (pprint-indent :block 2 stream)
           (pprint-newline :linear stream)
           (princ child stream)
-          (pprint-indent :block 0 stream)
-          (case morep
-            (:space
-             (write-char #\space stream)
-             (pprint-newline :linear stream))
-            ((t)
-             (pprint-newline :mandatory stream)))))))
+          (pprint-indent :block 0 stream))
+        (case morep
+          (:space
+           (write-char #\space stream)
+           (pprint-newline :linear stream))
+          ((t)
+           (pprint-newline :mandatory stream))))))
 
 (export 'format-compound-statement)
 (defmacro format-compound-statement
@@ -228,6 +214,124 @@ (defmacro format-compound-statement
   `(format-compound-statement* ,stream ,child ,morep
                               (lambda (,stream) ,@body)))
 
+(export 'format-banner-comment)
+(defun format-banner-comment (stream control &rest args)
+  (format stream "~@</~@<* ~@;~?~:>~_ */~:>" control args))
+
+;; Important instruction classes.
+
+;; HACK: Some of the slot names we'd like to use are external symbols in our
+;; package or the `common-lisp' package.  Use gensyms for these slot names to
+;; prevent them from leaking.
+
+(definst var (stream :export t) (name #1=#:type &optional init)
+  (pprint-logical-block (stream nil)
+    (pprint-c-type #1# stream name)
+    (when init
+      (format stream " = ~2I~_~A" init))
+    (write-char #\; stream)))
+
+(definst function (stream :export t)
+    (name #1=#:type body &optional #2=#:banner &rest banner-args)
+  (pprint-logical-block (stream nil)
+    (when #2#
+      (apply #'format-banner-comment stream #2# banner-args)
+      (pprint-newline :mandatory stream))
+    (princ "static " stream)
+    (pprint-c-type #1# stream name)
+    (format stream "~:@_~A~:@_~:@_" body)))
+
+;; Expression statements.
+(definst expr (stream :export t) (#1=#:expr)
+  (format stream "~A;" #1#))
+(definst set (stream :export t) (var #1=#:expr)
+  (format stream "~@<~A = ~2I~_~A;~:>" var #1#))
+(definst update (stream :export t) (var op #1=#:expr)
+  (format stream "~@<~A ~A= ~2I~_~A;~:>" var op #1#))
+
+;; Special kinds of expressions.
+(definst call (stream :export t) (#1=#:func &rest args)
+  (format stream "~@<~A~4I~_(~@<~{~A~^, ~_~}~:>)~:>" #1# args))
+(definst cond (stream :export t) (#1=#:cond conseq alt)
+  (format stream "~@<~A ~2I~@_~@<? ~A ~_: ~A~:>~:>" #1# conseq alt))
+
+;; Simple statements.
+(definst return (stream :export t) (#1=#:expr)
+  (format stream "return~@[ (~A)~];" #1#))
+(definst break (stream :export t) ()
+  (format stream "break;"))
+(definst continue (stream :export t) ()
+  (format stream "continue;"))
+
+;; Compound statements.
+
+(defvar *first-statement-p* t
+  "True if this is the first statement in a block.
+
+   This is used to communicate between `block-inst' and `banner-inst' so that
+   they get the formatting right between them.")
+
+(definst banner (stream :export t) (control &rest args)
+  (pprint-logical-block (stream nil)
+    (unless *first-statement-p* (pprint-newline :mandatory stream))
+    (apply #'format-banner-comment stream control args)))
+
+(export 'emit-banner)
+(defun emit-banner (codegen control &rest args)
+  (emit-inst codegen (apply #'make-banner-inst control args)))
+
+(definst block (stream :export t) (decls body)
+  (write-char #\{ stream)
+  (pprint-newline :mandatory stream)
+  (pprint-logical-block (stream nil)
+    (let ((newlinep nil))
+      (flet ((newline ()
+              (if newlinep
+                  (pprint-newline :mandatory stream)
+                  (setf newlinep t))))
+       (pprint-indent :block 2 stream)
+       (write-string "  " stream)
+       (when decls
+         (dolist (decl decls)
+           (newline)
+           (write decl :stream stream))
+         (when body (newline)))
+       (let ((*first-statement-p* t))
+         (dolist (inst body)
+           (newline)
+           (write inst :stream stream)
+           (setf *first-statement-p* nil))))))
+  (pprint-newline :mandatory stream)
+  (write-char #\} stream))
+
+(definst if (stream :export t) (#1=#:cond conseq &optional alt)
+  (let ((stmt "if"))
+    (loop (format-compound-statement (stream conseq (if alt t nil))
+           (format stream "~A (~A)" stmt #1#))
+         (typecase alt
+           (null (return))
+           (if-inst (setf stmt "else if"
+                          #1# (inst-cond alt)
+                          conseq (inst-conseq alt)
+                          alt (inst-alt alt)))
+           (t (format-compound-statement (stream alt)
+                (format stream "else"))
+              (return))))))
+
+(definst while (stream :export t) (#1=#:cond body)
+  (format-compound-statement (stream body)
+    (format stream "while (~A)" #1#)))
+
+(definst do-while (stream :export t) (body #1=#:cond)
+  (format-compound-statement (stream body :space)
+    (write-string "do" stream))
+  (format stream "while (~A);" #1#))
+
+(definst for (stream :export t) (init #1=#:cond update body)
+  (format-compound-statement (stream body)
+    (format stream "for (~@<~@[~A~];~@[ ~_~A~];~@[ ~_~A~]~:>)"
+           init #1# update)))
+
 ;;;--------------------------------------------------------------------------
 ;;; Code generation.
 
@@ -300,13 +404,15 @@ (defgeneric temporary-var (codegen type)
    cleanup automatically."))
 
 (export 'codegen-build-function)
-(defun codegen-build-function (codegen name type vars insts)
+(defun codegen-build-function
+    (codegen name type vars insts &optional banner &rest banner-args)
   "Build a function and add it to CODEGEN's list.
 
    Returns the function's name."
   (codegen-add-function codegen
-                       (make-function-inst name type
-                                           (make-block-inst vars insts)))
+                       (apply #'make-function-inst name type
+                              (make-block-inst vars insts)
+                              banner banner-args))
   name)
 
 (export 'codegen-pop-block)
@@ -318,15 +424,17 @@ (defgeneric codegen-pop-block (codegen)
       (make-block-inst vars insts))))
 
 (export 'codegen-pop-function)
-(defgeneric codegen-pop-function (codegen name type)
+(defgeneric codegen-pop-function
+    (codegen name type &optional banner &rest banner-args)
   (:documentation
    "Makes a function out of the completed code in CODEGEN.
 
    The NAME can be any object you like.  The TYPE should be a function type
    object which includes argument names.  The return value is the NAME.")
-  (:method (codegen name type)
+  (:method (codegen name type &optional banner &rest banner-args)
     (multiple-value-bind (vars insts) (codegen-pop codegen)
-      (codegen-build-function codegen name type vars insts))))
+      (apply #'codegen-build-function codegen name type vars insts
+            banner banner-args))))
 
 (export 'with-temporary-var)
 (defmacro with-temporary-var ((codegen var type) &body body)
@@ -334,10 +442,13 @@ (defmacro with-temporary-var ((codegen var type) &body body)
 
    During BODY, VAR will be marked in-use; when BODY ends, VAR will be marked
    available for re-use."
-  `(let ((,var (temporary-var ,codegen ,type)))
-     (unwind-protect
-         (progn ,@body)
-       (setf (var-in-use-p ,var) nil))))
+  (multiple-value-bind (doc decls body) (parse-body body :docp nil)
+    (declare (ignore doc))
+    `(let ((,var (temporary-var ,codegen ,type)))
+       ,@decls
+       (unwind-protect
+           (progn ,@body)
+        (setf (var-in-use-p ,var) nil)))))
 
 ;;;--------------------------------------------------------------------------
 ;;; Code generation idioms.
@@ -401,4 +512,9 @@ (defun convert-stmts (codegen target type func)
                  (emit-inst codegen (make-return-inst nil)))
     (t (funcall func target))))
 
+(export 'deliver-call)
+(defun deliver-call (codegen target func &rest args)
+  "Emit a statement to call FUNC with ARGS and deliver the result to TARGET."
+  (deliver-expr codegen target (apply #'make-call-inst func args)))
+
 ;;;----- That's all, folks --------------------------------------------------