]> asedeno.scripts.mit.edu Git - cl-protobufs.git/commitdiff
Well, it turns out that the Protobufs 'group' feature,
authorScott McKay <swm@google.com>
Fri, 4 May 2012 20:00:39 +0000 (20:00 +0000)
committerScott McKay <swm@google.com>
Fri, 4 May 2012 20:00:39 +0000 (20:00 +0000)
which has been deprecated for years, is still in wide
use, e.g., in Chubby's bnsresolver.proto

So, implement support for groups:
 - Add a model class for it
 - Add .proto and .lisp printers
 - Add a 'define-group' macro
 - Make the .proto parser know how to parse them

Passes 'precheckin --full+', which makes sense since none
of this is used in normal use yet.

git-svn-id: http://svn.internal.itasoftware.com/svn/ita/trunk/qres/lisp/quux/protobufs@542500 f8382938-511b-0410-9cdd-bb47b084005c

define-proto.lisp
examples.lisp
model-classes.lisp
parser.lisp
printer.lisp
proto-pkgdcl.lisp
serialize.lisp
utilities.lisp

index 8ad72f289a945a71a280ee1b75d1906c99e14611..cfed1e01d8b291e2c2a98fd2a1bd326cc53d6004 100644 (file)
                      :package  package
                      :lisp-package (or lisp-pkg package)
                      :imports  imports
-                     :options  options
-                     :optimize optimize
+                     :options  (if optimize
+                                 (append options (list (make-instance 'protobuf-option
+                                                         :name  "optimize_for"
+                                                         :value (if (eq optimize :speed) "SPEED" "CODE_SIZE")
+                                                         :type  'symbol)))
+                                 options)
                      :documentation documentation))
          (*protobuf* protobuf)
          (*protobuf-package* (or (find-package lisp-pkg)
                                  (find-package (string-upcase lisp-pkg))
-                                *package*)))
+                                 *package*)))
     (apply #'process-imports imports)
     (with-collectors ((forms collect-form))
       (dolist (msg messages)
@@ -72,7 +76,7 @@
             ((define-message define-extend)
              (setf (proto-parent model) protobuf)
              (setf (proto-messages protobuf) (nconc (proto-messages protobuf) (list model)))
-             (when (proto-extension-p model)
+             (when (eql (proto-message-type model) :extends)
                (setf (proto-extenders protobuf) (nconc (proto-extenders protobuf) (list model)))))
             ((define-service)
              (setf (proto-services protobuf) (nconc (proto-services protobuf) (list model)))))))
                         collect (make-instance 'protobuf-option
                                   :name  key
                                   :value val)))
-         (index   0)
          (message (make-instance 'protobuf-message
                     :class type
                     :name  name
                     :conc-name (and conc-name (string conc-name))
                     :options  options
                     :documentation documentation))
+         (index 0)
          (*protobuf* message))
     (with-collectors ((slots collect-slot)
                       (forms collect-form))
       (dolist (field fields)
         (case (car field)
-          ((define-enum define-message define-extend define-extension)
-           (destructuring-bind (&optional progn type model definers)
+          ((define-enum define-message define-extend define-extension define-group)
+           (destructuring-bind (&optional progn type model definers extra-field extra-slot)
                (macroexpand-1 field env)
              (assert (eq progn 'progn) ()
                      "The macroexpansion for ~S failed" field)
                ((define-message define-extend)
                 (setf (proto-parent model) message)
                 (setf (proto-messages message) (nconc (proto-messages message) (list model)))
-                (when (proto-extension-p model)
+                (when (eql (proto-message-type model) :extends)
                   (setf (proto-extenders message) (nconc (proto-extenders message) (list model)))))
+               ((define-group)
+                (setf (proto-parent model) message)
+                (setf (proto-messages message) (nconc (proto-messages message) (list model)))
+                (when extra-slot
+                  (collect-slot extra-slot))
+                (setf (proto-fields message) (nconc (proto-fields message) (list extra-field))))
                ((define-extension)
                 (setf (proto-extensions message) (nconc (proto-extensions message) (list model)))))))
           (otherwise
                         collect (make-instance 'protobuf-option
                                   :name  key
                                   :value val)))
-         (index   0)
          (message   (find-message *protobuf* name))
          (conc-name (and message (proto-conc-name message)))
          (alias-for (and message (proto-alias-for message)))
                          :messages (copy-list (proto-messages message))
                          :fields   (copy-list (proto-fields message))
                          :options  (or options (copy-list (proto-options message)))
-                         :extension-p t                 ;this message is an extension
-                         :documentation documentation))))
+                         :message-type :extends         ;this message is an extension
+                         :documentation documentation)))
+         (index 0))
     (assert message ()
             "There is no message named ~A to extend" name)
     (assert (eq type (proto-class message)) ()
     (with-collectors ((forms collect-form))
       (dolist (field fields)
         (assert (not (member (car field)
-                             '(define-enum define-message define-extend define-extension))) ()
+                             '(define-enum define-message define-extend define-extension define-group))) ()
                 "The body of ~S can only contain field definitions" 'define-extend)
         (multiple-value-bind (field slot idx)
             (process-field field index :conc-name conc-name :alias-for alias-for)
               ;; This so that (de)serialization works
               (setf (proto-reader field) reader
                     (proto-writer field) writer)))
-          (setf (proto-extension-p field) t)            ;this field is an extension
+          (setf (proto-message-type field) :extends)    ;this field is an extension
           (setf (proto-fields extends) (nconc (proto-fields extends) (list field)))))
       `(progn
          define-extend
          ,extends
          ,forms))))
 
+(defmacro define-group (type (&key index arity name conc-name alias-for options documentation)
+                        &body fields &environment env)
+  "Define a message named 'type' and a Lisp 'defclass', *and* a field named type.
+   This is deprecated in Protobufs, but if you have to use it, you must give
+   'index' as the field index and 'arity' of :required, :optional or :repeated.
+   'name' can be used to override the defaultly generated Protobufs message name.
+   The body consists of fields, or 'define-enum' or 'define-message' forms.
+   'conc-name' will be used as the prefix to the Lisp slot accessors, if it's supplied.
+   If 'alias-for' is given, no Lisp class is defined. Instead, the message will be
+   used as an alias for a class that already exists in Lisp. This feature is intended
+   to be used to define messages that will be serialized from existing Lisp classes;
+   unless you get the slot names or readers exactly right for each field, it will be
+   the case that trying to (de)serialize into a Lisp object won't work.
+   'options' is a set of keyword/value pairs, both of which are strings.
+
+   Fields take the form (slot &key type name default reader)
+   'slot' can be either a symbol giving the field name, or a list whose
+   first element is the slot name and whose second element is the index.
+   'type' is the type of the slot.
+   'name' can be used to override the defaultly generated Protobufs field name.
+   'default' is the default value for the slot.
+   'reader' is a Lisp slot reader function to use to get the value, instead of
+   using 'slot-value'; this is often used when aliasing an existing class.
+   'writer' is a Lisp slot writer function to use to set the value."
+  (check-type index integer)
+  (check-type arity (member :required :optional :repeated))
+  (let* ((slot    (or (and name (proto->slot-name name)) type))
+         (name    (or name (class-name->proto type)))
+         (options (loop for (key val) on options by #'cddr
+                        collect (make-instance 'protobuf-option
+                                  :name  key
+                                  :value val)))
+         (mslot   (unless alias-for
+                    `(,slot ,@(case arity
+                                (:required
+                                 `(:type ,type))
+                                (:optional
+                                 `(:type (or ,type null)
+                                   :initform nil))
+                                (:repeated
+                                 `(:type (list-of ,type)
+                                   :initform ())))
+                            :initarg ,(kintern (symbol-name slot)))))
+         (mfield  (make-instance 'protobuf-field
+                    :name  (slot-name->proto slot)
+                    :value slot
+                    :type  name
+                    :class type
+                    ;; One of :required, :optional or :repeated
+                    :required arity
+                    :index index
+                    :message-type :group))
+         (message (make-instance 'protobuf-message
+                    :class type
+                    :name  name
+                    :alias-for alias-for
+                    :conc-name (and conc-name (string conc-name))
+                    :options  options
+                    :message-type :group                ;this message is a group
+                    :documentation documentation))
+         (index 0)
+         (*protobuf* message))
+    (with-collectors ((slots collect-slot)
+                      (forms collect-form))
+      (dolist (field fields)
+        (case (car field)
+          ((define-enum define-message define-extend define-extension define-group)
+           (destructuring-bind (&optional progn type model definers extra-field extra-slot)
+               (macroexpand-1 field env)
+             (assert (eq progn 'progn) ()
+                     "The macroexpansion for ~S failed" field)
+             (map () #'collect-form definers)
+             (ecase type
+               ((define-enum)
+                (setf (proto-enums message) (nconc (proto-enums message) (list model))))
+               ((define-message define-extend)
+                (setf (proto-parent model) message)
+                (setf (proto-messages message) (nconc (proto-messages message) (list model)))
+                (when (eql (proto-message-type model) :extends)
+                  (setf (proto-extenders message) (nconc (proto-extenders message) (list model)))))
+               ((define-group)
+                (setf (proto-parent model) message)
+                (setf (proto-messages message) (nconc (proto-messages message) (list model)))
+                (when extra-slot
+                  (collect-slot extra-slot))
+                (setf (proto-fields message) (nconc (proto-fields message) (list extra-field))))
+               ((define-extension)
+                (setf (proto-extensions message) (nconc (proto-extensions message) (list model)))))))
+          (otherwise
+           (multiple-value-bind (field slot idx)
+               (process-field field index :conc-name conc-name :alias-for alias-for)
+             (assert (not (find (proto-index field) (proto-fields message) :key #'proto-index)) ()
+                     "The field ~S overlaps with another field in ~S"
+                     (proto-value field) (proto-class message))
+             (setq index idx)
+             (when slot
+               (collect-slot slot))
+             (setf (proto-fields message) (nconc (proto-fields message) (list field)))))))
+      (if alias-for
+        ;; If we've got an alias, define a a type that is the subtype of
+        ;; the Lisp class that typep and subtypep work
+        (unless (or (eq type alias-for) (find-class type nil))
+          (collect-form `(deftype ,type () ',alias-for)))
+        ;; If no alias, define the class now
+        (collect-form `(defclass ,type () (,@slots)
+                         ,@(and documentation `((:documentation ,documentation))))))
+      `(progn
+         define-group
+         ,message
+         ,forms
+         ,mfield
+         ,mslot))))
+
 (defun process-field (field index &key conc-name alias-for)
   "Process one field descriptor within 'define-message' or 'define-extend'.
    Returns a 'proto-field' object, a CLOS slot form and the incremented field index."
index 897f7ce17d008c0ae24d3da6d8962b0bccca6b8c..d515ad7f2aef0a2adb32a98a794e82da29a0485e 100644 (file)
@@ -612,3 +612,99 @@ service ColorWheel {
     #+stubby (add-color request)
     #+ignore (add-color request)))
 ||#
+
+#||
+(let ((ps "syntax = \"proto2\";
+
+package color_wheel;
+
+option optimize_for = SPEED;
+
+message ColorWheel {
+  required string name = 1;
+  repeated Color colors = 2;
+  optional group Metadata = 3 {
+    optional string author = 1;
+    optional string revision = 2;
+    optional string date = 3;
+  }
+}
+
+message Color {
+  optional string name = 1;
+  required int64 r_value = 2;
+  required int64 g_value = 3;
+  required int64 b_value = 4;
+  extensions 1000 to max;
+}
+
+extend Color {
+  optional int64 opacity = 1000;
+}
+
+message GetColorRequest {
+  required ColorWheel wheel = 1;
+  required string name = 2;
+}
+
+message AddColorRequest {
+  required ColorWheel wheel = 1;
+  required Color color = 2;
+}
+
+service ColorWheel {
+  rpc GetColor (GetColorRequest) returns (Color) {
+    option deadline = \"1.0\";
+  }
+  rpc AddColor (AddColorRequest) returns (Color) {
+    option deadline = \"1.0\";
+  }
+}"))
+  (with-input-from-string (s ps)
+    (setq cw (proto:parse-protobuf-from-stream s))))
+
+(proto:define-proto color-wheel
+    (:package color-wheel
+     :optimize :speed
+     :documentation "Color wheel example, with groups")
+  (proto:define-message color-wheel ()
+    (name :type string)
+    (colors :type (list-of color))
+    (proto:define-group metadata
+        (:index 3
+         :arity :optional)
+      (author :type (or null string))
+      (revision :type (or null string))
+      (date :type (or null string))))
+  (proto:define-message color ()
+    (name :type (or null string))
+    (r-value :type integer)
+    (g-value :type integer)
+    (b-value :type integer))
+  (proto:define-message get-color-request ()
+    (wheel :type color-wheel)
+    (name :type string))
+  (proto:define-message add-color-request ()
+    (wheel :type color-wheel)
+    (color :type color))
+  (proto:define-service color-wheel ()
+    (get-color (get-color-request color))
+    (add-color (add-color-request color))))
+
+(proto:write-protobuf *color-wheel*)
+(proto:write-protobuf *color-wheel* :type :lisp)
+
+(progn ;with-rpc-channel (rpc)
+  (let* ((meta1  (make-instance 'metadata :revision "1.0"))
+         (wheel  (make-instance 'color-wheel :name "Colors" :metadata meta1))
+         (color1 (make-instance 'color :r-value 100 :g-value 0 :b-value 100))
+         (rqst1  (make-instance 'add-color-request :wheel wheel :color color1)))
+    #-ignore (progn
+               (format t "~2&Unextended~%")
+               (let ((ser1 (proto:serialize-object-to-stream rqst1 'add-color-request :stream nil)))
+                 (print ser1)
+                 (proto:print-text-format rqst1)
+                 (proto:print-text-format (proto:deserialize-object 'add-color-request ser1))))
+    #+stubby (add-color request)
+    #+ignore (add-color request)))
+||#
index 31bb3c7cc5167a93d9e62e96305019fafd2838e3..1f0d6dc2c418cf1e18e18e1c1dcd681f5573fd3c 100644 (file)
             :accessor proto-imports
             :initarg :imports
             :initform ())
-   (optimize :type (member nil :space :speed)
-             :accessor proto-optimize
-             :initarg :optimize
-             :initform nil)
    (enums :type (list-of protobuf-enum)         ;the set of enum types
           :accessor proto-enums
           :initarg :enums
    (value :type (or null string)                ;the value
           :accessor proto-value
           :initarg :value
-          :initform nil))
+          :initform nil)
+   (type :type (or null symbol)                 ;(optional) Lisp type,
+         :reader proto-type                     ;  one of string, integer, sybol (for now)
+         :initarg :type
+         :initform 'string))
   (:documentation
    "The model class that represents a Protobufs options, i.e., a keyword/value pair."))
 
   (print-unreadable-object (o stream :type t :identity t)
     (format stream "~A~@[ = ~S~]" (proto-name o) (proto-value o))))
 
+(defgeneric find-option (protobuf name)
+  (:documentation
+   "Given a protobuf schema, message, enum, etc and the name of an option,
+    returns the value of the option and its (Lisp) type."))
+
 (defmethod find-option ((protobuf base-protobuf) (name string))
   (let ((option (find name (proto-options protobuf) :key #'proto-name :test #'option-name=)))
-    (and option (proto-value option))))
+    (and option
+         (values (proto-value option) (proto-type option)))))
 
 (defmethod find-option ((options list) (name string))
   (let ((option (find name options :key #'proto-name :test #'option-name=)))
-    (and option (proto-value option))))
+    (and option
+         (values (proto-value option) (proto-type option)))))
 
 (defun option-name= (name1 name2)
   (let ((start1 (if (eql (char name1 0) #\() 1 0))
                :accessor proto-extensions
                :initarg :extensions
                :initform ())
-   (extension-p :type (member t nil)            ;true iff this message extends another message
-                :accessor proto-extension-p
-                :initarg :extension-p
-                :initform nil))
+   ;; :message is an ordinary message
+   ;; :group is a (deprecated) group (kind of an "implicit" message)
+   ;; :extends is an 'extends' to an existing message
+   (message-type :type (member :message :group :extends)
+                 :accessor proto-message-type
+                 :initarg :message-type
+                 :initform :message))
     (:documentation
    "The model class that represents a Protobufs message."))
 
 (defmethod initialize-instance :after ((message protobuf-message) &rest initargs)
   (declare (ignore initargs))
   ;; Record this message under just its Lisp class name
-  (with-slots (class extension-p) message
-    (when (and class (not extension-p))
+  ;; No need to record an extension, it's already been recorded
+  (with-slots (class message-type) message
+    (when (and class (not (eql message-type :extends)))
       (setf (gethash class *all-messages*) message))))
 
 (defmethod make-load-form ((m protobuf-message) &optional environment)
 
 (defmethod print-object ((m protobuf-message) stream)
   (print-unreadable-object (m stream :type t :identity t)
-    (format stream "~S~@[ (alias for ~S)~]~@[ (extended~*)~]"
-            (proto-class m) (proto-alias-for m) (proto-extension-p m))))
+    (format stream "~S~@[ (alias for ~S)~]~@[ (group~*)~]~@[ (extended~*)~]"
+            (proto-class m) (proto-alias-for m)
+            (eql (proto-message-type m) :group)
+            (eql (proto-message-type m) :extends))))
 
 (defmethod find-message ((message protobuf-message) (type symbol))
   ;; Extended messages "shadow" non-extended ones
            :accessor proto-packed
            :initarg :packed
            :initform nil)
-   (extension-p :type (member t nil)            ;true iff this field is an extension
-                :accessor proto-extension-p
-                :initarg :extension-p
-                :initform nil))
+   ;; Copied from 'proto-message-type' of the field
+   (message-type :type (member :message :group :extends)
+                 :accessor proto-message-type
+                 :initarg :message-type
+                 :initform :message))
   (:documentation
    "The model class that represents one field within a Protobufs message."))
 
 
 (defmethod print-object ((f protobuf-field) stream)
   (print-unreadable-object (f stream :type t :identity t)
-    (format stream "~S :: ~S = ~D~@[ (extended~*)~]"
-            (proto-value f) (proto-class f) (proto-index f) (proto-extension-p f))))
+    (format stream "~S :: ~S = ~D~@[ (group~*)~]~@[ (extended~*)~]"
+            (proto-value f) (proto-class f) (proto-index f)
+            (eql (proto-message-type f) :group)
+            (eql (proto-message-type f) :extends))))
 
 
 ;; An extension within a message
index 9ec9baa5153a33c5baccc25b0cd477d16330c452..e90361940ee46011ffed0c3711070b1d433cabe0 100644 (file)
   "If what appears next in the stream is a comment, skip it and any following comments,
    then skip any following whitespace."
   (loop
-    (unless (eql (peek-char nil stream nil) #\/)
-      (return)
+    (let ((ch (peek-char nil stream nil)))
+      (when (or (null ch) (not (eql ch #\/)))
+        (return-from maybe-skip-comments))
       (read-char stream)
       (case (peek-char nil stream nil)
         ((#\/)
          (skip-line-comment stream))
         ((#\*)
          (skip-block-comment stream))
+        ((nil)
+         (return-from maybe-skip-comments))
         (otherwise
          (error "Found a '~C' at position ~D to start a comment, but no following '~C' or '~C'"
                 #\/ (file-position stream) #\/ #\*)))))
                      :class class
                      :name  name))
          (*protobuf* protobuf)
-         (*protobuf-package* nil))
+         (*protobuf-package* *package*))
     (loop
       (skip-whitespace stream)
       (maybe-skip-comments stream)
                         (let* ((option (parse-proto-option stream protobuf))
                                (name   (and option (proto-name option)))
                                (value  (and option (proto-value option))))
-                          (when option
-                            (cond ((option-name= name "optimize_for")
-                                   (let ((value (cond ((string= value "SPEED") :speed)
-                                                      ((string= value "CODE_SIZE") :space)
-                                                      (t nil))))
-                                     (setf (proto-optimize protobuf) value)))
-                                  ((option-name= name "lisp_package")
-                                   (let ((package (or (find-package value)
-                                                      (find-package (string-upcase value)))))
-                                     (setf (proto-lisp-package protobuf) value)
-                                     (setq *protobuf-package* package)))))))
+                          (when (and option (option-name= name "lisp_package"))
+                            (let ((package (or (find-package value)
+                                               (find-package (string-upcase value))
+                                               *protobuf-package*)))
+                              (setf (proto-lisp-package protobuf) value)
+                              (setq *protobuf-package* package)))))
                        ((string= token "enum")
                         (parse-proto-enum stream protobuf))
                        ((string= token "extend")
     (unless (proto-lisp-package protobuf)
       (setf (proto-lisp-package protobuf) lisp-pkg))
     (let ((package (or (find-package lisp-pkg)
-                       (find-package (string-upcase lisp-pkg)))))
+                       (find-package (string-upcase lisp-pkg))
+                       *protobuf-package*)))
       (setq *protobuf-package* package))))
 
 (defun parse-proto-import (stream protobuf &optional (terminator #\;))
           (let ((alias (find-option enum "lisp_alias")))
             (when alias
               (setf (proto-alias-for enum) (make-lisp-symbol alias))))
-          (return-from parse-proto-enum))
+          (return-from parse-proto-enum enum))
         (if (string= name "option")
           (parse-proto-option stream enum #\;)
           (parse-proto-enum-value stream enum name))))))
                   :name  name
                   :index idx
                   :value (proto->enum-name name *protobuf-package*))))
-    (setf (proto-values enum) (nconc (proto-values enum) (list value)))))
+    (setf (proto-values enum) (nconc (proto-values enum) (list value)))
+    value))
 
 
-(defun parse-proto-message (stream protobuf)
+(defun parse-proto-message (stream protobuf &optional name)
   "Parse a Protobufs 'message' from 'stream'.
    Updates the 'protobuf' or 'protobuf-message' object to have the message."
   (check-type protobuf (or protobuf protobuf-message))
-  (let* ((name (prog1 (parse-token stream)
+  (let* ((name (prog1 (or name (parse-token stream))
                  (expect-char stream #\{ "message")
                  (maybe-skip-comments stream)))
          (message (make-instance 'protobuf-message
           (let ((alias (find-option message "lisp_alias")))
             (when alias
               (setf (proto-alias-for message) (make-lisp-symbol alias))))
-          (return-from parse-proto-message))
+          (return-from parse-proto-message message))
         (cond ((string= token "enum")
                (parse-proto-enum stream message))
               ((string= token "extend")
                          :enums    (copy-list (proto-enums message))
                          :messages (copy-list (proto-messages message))
                          :fields   (copy-list (proto-fields message))
-                         :extension-p t))))             ;this message is an extension
+                         :message-type :extends))))     ;this message is an extension
     (loop
       (let ((token (parse-token stream)))
         (when (null token)
           (let ((alias (find-option extends "lisp_alias")))
             (when alias
               (setf (proto-alias-for extends) (make-lisp-symbol alias))))
-          (return-from parse-proto-extend))
+          (return-from parse-proto-extend extends))
         (cond ((member token '("required" "optional" "repeated") :test #'string=)
                (parse-proto-field stream extends token message))
               ((string= token "option")
   "Parse a Protobufs field from 'stream'.
    Updates the 'protobuf-message' object to have the field."
   (check-type message protobuf-message)
-  (let* ((type (parse-token stream))
-         (name (prog1 (parse-token stream)
+  (let ((type (parse-token stream)))
+    (if (string= type "group")
+      (parse-proto-group stream message required extended-from)
+      (let* ((name (prog1 (parse-token stream)
+                     (expect-char stream #\= "message")))
+             (idx  (parse-int stream))
+             (opts (prog1 (parse-proto-field-options stream)
+                     (expect-char stream #\; "message")
+                     (maybe-skip-comments stream)))
+             (dflt   (find-option opts "default"))
+             (packed (find-option opts "packed"))
+             (ptype  (if (member type '("int32" "int64" "uint32" "uint64" "sint32" "sint64"
+                                        "fixed32" "fixed64" "sfixed32" "sfixed64"
+                                        "string" "bytes" "bool" "float" "double") :test #'string=)
+                       (kintern type)
+                       type))
+             (class  (if (keywordp ptype) ptype (proto->class-name type *protobuf-package*)))
+             (field  (make-instance 'protobuf-field
+                       :name  name
+                       :value (proto->slot-name name *protobuf-package*)
+                       :type  type
+                       :class class
+                       ;; One of :required, :optional or :repeated
+                       :required (kintern required)
+                       :index idx
+                       :default dflt
+                       :packed  (and packed (string= packed "true"))
+                       :message-type (proto-message-type message))))
+        (when extended-from
+          (assert (index-within-extensions-p idx extended-from) ()
+                  "The index ~D is not in range for extending ~S"
+                  idx (proto-class extended-from)))
+        (let ((slot (find-option opts "lisp_name")))
+          (when slot
+            (setf (proto-value field) (make-lisp-symbol type))))
+        (setf (proto-fields message) (nconc (proto-fields message) (list field)))
+        field))))
+
+(defun parse-proto-group (stream message required &optional extended-from)
+  "Parse a (deprecated) Protobufs group from 'stream'.
+   Updates the 'protobuf-message' object to have the group type and field."
+  (check-type message protobuf-message)
+  (let* ((type (prog1 (parse-token stream)
                  (expect-char stream #\= "message")))
+         (name (slot-name->proto (proto->slot-name type)))
          (idx  (parse-int stream))
-         (opts (prog1 (parse-proto-field-options stream)
-                 (expect-char stream #\; "message")
-                 (maybe-skip-comments stream)))
-         (dflt   (find-option opts "default"))
-         (packed (find-option opts "packed"))
-         (ptype  (if (member type '("int32" "int64" "uint32" "uint64" "sint32" "sint64"
-                                    "fixed32" "fixed64" "sfixed32" "sfixed64"
-                                    "string" "bytes" "bool" "float" "double") :test #'string=)
-                   (kintern type)
-                   type))
-         (class  (if (keywordp ptype) ptype (proto->class-name type *protobuf-package*)))
+         (msg  (parse-proto-message stream message type))
+         (class  (proto->class-name type *protobuf-package*))
          (field  (make-instance 'protobuf-field
                    :name  name
                    :value (proto->slot-name name *protobuf-package*)
                    ;; One of :required, :optional or :repeated
                    :required (kintern required)
                    :index idx
-                   :default dflt
-                   :packed  (and packed (string= packed "true"))
-                   :extension-p (proto-extension-p message))))
+                   :message-type :group)))
+    (setf (proto-message-type msg) :group)
     (when extended-from
       (assert (index-within-extensions-p idx extended-from) ()
               "The index ~D is not in range for extending ~S"
               idx (proto-class extended-from)))
-    (let ((slot (find-option opts "lisp_name")))
-      (when slot
-        (setf (proto-value field) (make-lisp-symbol type))))
-    (setf (proto-fields message) (nconc (proto-fields message) (list field)))))
+    (setf (proto-fields message) (nconc (proto-fields message) (list field)))
+    field))
 
 (defun parse-proto-field-options (stream)
   "Parse any options in a Protobufs field from 'stream'.
             "Expected 'to' in 'extensions' at position ~D" (file-position stream))
     (assert (or (integerp to) (string= to "max")) ()
             "Extension value is not an integer or 'max' as position ~D" (file-position stream))
-    (setf (proto-extensions message)
-          (nconc (proto-extensions message)
-                 (list (make-instance 'protobuf-extension
-                         :from from
-                         :to   (if (integerp to) to #.(1- (ash 1 29)))))))))
+    (let ((extension (make-instance 'protobuf-extension
+                       :from from
+                       :to   (if (integerp to) to #.(1- (ash 1 29))))))
+      (setf (proto-extensions message)
+            (nconc (proto-extensions message)
+                   (list extension)))
+      extension)))
 
 
 (defun parse-proto-service (stream protobuf)
           (expect-char stream #\} "service")
           (maybe-skip-comments stream)
           (setf (proto-services protobuf) (nconc (proto-services protobuf) (list service)))
-          (return-from parse-proto-service))
+          (return-from parse-proto-service service))
         (cond ((string= token "option")
                (parse-proto-option stream service #\;))
               ((string= token "rpc")
         (setf (proto-function method) (make-lisp-symbol name))))
     (assert (string= ret "returns") ()
             "Syntax error in 'message' at position ~D" (file-position stream))
-    (setf (proto-methods service) (nconc (proto-methods service) (list method)))))
+    (setf (proto-methods service) (nconc (proto-methods service) (list method)))
+    method))
 
 (defun parse-proto-method-options (stream)
   "Parse any options in a Protobufs method from 'stream'.
index 871b6496a055e7f5d8b86199760fddbc3ec8b912..823bba418a14a4f6ac9be85d68630b8b50a08c4e 100644 (file)
@@ -19,7 +19,7 @@
    (let ((*protobuf* protobuf))
      (write-protobuf-as type protobuf stream)))
 
-(defgeneric write-protobuf-as (type protobuf stream &key indentation more)
+(defgeneric write-protobuf-as (type protobuf stream &key indentation &allow-other-keys)
   (:documentation
    "Writes the protobuf object 'protobuf' (schema, message, enum, etc) onto
     the given stream 'stream' in the format given by 'type' (:proto, :text, etc).
@@ -35,9 +35,8 @@
 ;;; Pretty print a schema as a .proto file
 
 (defmethod write-protobuf-as ((type (eql :proto)) (protobuf protobuf) stream
-                              &key (indentation 0) more)
-  (declare (ignore more))
-  (with-prefixed-accessors (name documentation syntax package imports optimize options) (proto- protobuf)
+                              &key (indentation 0))
+  (with-prefixed-accessors (name documentation syntax package imports options) (proto- protobuf)
     (when documentation
       (write-protobuf-documentation type documentation stream :indentation indentation))
     (when syntax
@@ -49,9 +48,6 @@
         (format stream "~&import \"~A\";~%" import))
       (terpri stream))
     (write-protobuf-header type stream)
-    (when optimize
-      (format stream "~&option optimize_for ~A;~%~%"
-              (if (eq optimize :space) "CODE_SIZE" "SPEED")))
     (when options
       (dolist (option options)
         (format stream "~&option ~:/protobuf-option/;~%" option))
@@ -80,6 +76,8 @@
                          ("lisp_class"   "string" 195805)
                          ("lisp_slot"    "string" 195806)))
 
+(defvar *option-types* '(("optimize_for" symbol)))
+
 (defmethod write-protobuf-header ((type (eql :proto)) stream)
   (format stream "~&import \"net/proto2/proto/descriptor.proto\";~%~%")
   (format stream "~&extend proto2.MessageOptions {~%")
   (format stream "~&}~%~%"))
 
 (defun cl-user::protobuf-option (stream option colon-p atsign-p)
-  (cond (colon-p                                ;~:/protobuf-option/ -- .proto format
-         (if (find (proto-name option) *lisp-options* :key #'first :test #'string=)
-           (format stream "(~A)~@[ = ~S~]" (proto-name option) (proto-value option))
-           (format stream "~A~@[ = ~S~]" (proto-name option) (proto-value option))))
-        (atsign-p                               ;~@/protobuf-option/ -- .lisp format
-         (format stream "~S ~S" (proto-name option) (proto-value option)))
-        (t                                      ;~/protobuf-option/  -- keyword/value format
-         (format stream "~(:~A~) ~S" (proto-name option) (proto-value option)))))
+  (let ((type (or (second (find (proto-name option) *option-types* :key #'first :test #'string=))
+                  'string)))
+    (cond (colon-p                              ;~:/protobuf-option/ -- .proto format
+           (let ((fmt-control
+                  (cond ((find (proto-name option) *lisp-options* :key #'first :test #'string=)
+                         (if (eql type 'symbol) "(~A)~@[ = ~A~]" "(~A)~@[ = ~S~]"))
+                        (t
+                         (if (eql type 'symbol) "~A~@[ = ~A~]" "~A~@[ = ~S~]")))))
+             (format stream fmt-control (proto-name option) (proto-value option))))
+          (atsign-p                             ;~@/protobuf-option/ -- .lisp format
+           (format stream "~S ~S" (proto-name option) (proto-value option)))
+          (t                                    ;~/protobuf-option/  -- keyword/value format
+           (format stream "~(:~A~) ~S" (proto-name option) (proto-value option))))))
 
 (defmethod write-protobuf-as ((type (eql :proto)) (enum protobuf-enum) stream
                               &key (indentation 0) more)
 
 
 (defmethod write-protobuf-as ((type (eql :proto)) (message protobuf-message) stream
-                              &key (indentation 0) more)
-  (declare (ignore more))
-  (with-prefixed-accessors (name class alias-for extension-p documentation options) (proto- message)
-    (when documentation
-      (write-protobuf-documentation type documentation stream :indentation indentation))
-    (format stream "~&~@[~VT~]~A ~A {~%"
-            (and (not (zerop indentation)) indentation)
-            (if extension-p "extend" "message") name)
-    (let ((other (and class (not (string= name (class-name->proto class))) class)))
-      (when other
-        (format stream "~&~VToption (lisp_name) = \"~A:~A\";~%"
-                (+ indentation 2) (package-name (symbol-package class)) (symbol-name class))))
-    (when alias-for
-      (format stream "~&~VToption (lisp_alias) = \"~A:~A\";~%"
-              (+ indentation 2) (package-name (symbol-package alias-for)) (symbol-name alias-for)))
-    (dolist (option options)
-      (format stream "~&~VToption ~:/protobuf-option/;~%"
-              (+ indentation 2) option))
-    (cond (extension-p
-           (loop for (field . more) on (proto-fields message) doing
-             (when (proto-extension-p field)
-               (write-protobuf-as type field stream :indentation (+ indentation 2) :more more))))
-          (t
+                              &key (indentation 0) more index arity)
+  (declare (ignore more arity))
+  (with-prefixed-accessors (name class alias-for message-type documentation options) (proto- message)
+    (cond ((eql message-type :group)
+           ;; If we've got a group, the printer for fields has already
+           ;; printed a partial line (nice modularity, huh?)
+           (format stream "group ~A = ~D {~%" name index)
+           (let ((other (and class (not (string= name (class-name->proto class))) class)))
+             (when other
+               (format stream "~&~VToption (lisp_name) = \"~A:~A\";~%"
+                       (+ indentation 2) (package-name (symbol-package class)) (symbol-name class))))
+           (when alias-for
+             (format stream "~&~VToption (lisp_alias) = \"~A:~A\";~%"
+                     (+ indentation 2) (package-name (symbol-package alias-for)) (symbol-name alias-for)))
+           (dolist (option options)
+             (format stream "~&~VToption ~:/protobuf-option/;~%"
+                     (+ indentation 2) option))
            (loop for (enum . more) on (proto-enums message) doing
              (write-protobuf-as type enum stream :indentation (+ indentation 2) :more more))
-           (loop for (msg . more) on (proto-messages message) doing
-             (write-protobuf-as type msg stream :indentation (+ indentation 2) :more more))
            (loop for (field . more) on (proto-fields message) doing
-             (write-protobuf-as type field stream :indentation (+ indentation 2) :more more))
-           (loop for (extension . more) on (proto-extensions message) doing
-             (write-protobuf-as type extension stream :indentation (+ indentation 2) :more more))))
-    (format stream "~&~@[~VT~]}~%"
-            (and (not (zerop indentation)) indentation))))
+             (write-protobuf-as type field stream
+                                :indentation (+ indentation 2) :more more :message message))
+           (format stream "~&~@[~VT~]}~%"
+                   (and (not (zerop indentation)) indentation)))
+          (t
+           (when documentation
+             (write-protobuf-documentation type documentation stream :indentation indentation))
+           (format stream "~&~@[~VT~]~A ~A {~%"
+                   (and (not (zerop indentation)) indentation)
+                   (if (eql message-type :message) "message" "extend") name)
+           (let ((other (and class (not (string= name (class-name->proto class))) class)))
+             (when other
+               (format stream "~&~VToption (lisp_name) = \"~A:~A\";~%"
+                       (+ indentation 2) (package-name (symbol-package class)) (symbol-name class))))
+           (when alias-for
+             (format stream "~&~VToption (lisp_alias) = \"~A:~A\";~%"
+                     (+ indentation 2) (package-name (symbol-package alias-for)) (symbol-name alias-for)))
+           (dolist (option options)
+             (format stream "~&~VToption ~:/protobuf-option/;~%"
+                     (+ indentation 2) option))
+           (cond ((eql message-type :extends)
+                  (loop for (field . more) on (proto-fields message) doing
+                    (when (eql (proto-message-type field) :extends)
+                      (write-protobuf-as type field stream
+                                         :indentation (+ indentation 2) :more more
+                                         :message message))))
+                 (t
+                  (loop for (enum . more) on (proto-enums message) doing
+                    (write-protobuf-as type enum stream :indentation (+ indentation 2) :more more))
+                  (loop for (msg . more) on (proto-messages message) doing
+                    (unless (eql (proto-message-type msg) :group)
+                      (write-protobuf-as type msg stream :indentation (+ indentation 2) :more more)))
+                  (loop for (field . more) on (proto-fields message) doing
+                    (write-protobuf-as type field stream
+                                       :indentation (+ indentation 2) :more more
+                                       :message message))
+                  (loop for (extension . more) on (proto-extensions message) doing
+                    (write-protobuf-as type extension stream :indentation (+ indentation 2) :more more))))
+           (format stream "~&~@[~VT~]}~%"
+                   (and (not (zerop indentation)) indentation))))))
 
 (defparameter *protobuf-field-comment-column* 56)
 (defmethod write-protobuf-as ((type (eql :proto)) (field protobuf-field) stream
-                              &key (indentation 0) more)
+                              &key (indentation 0) more message)
   (declare (ignore more))
   (with-prefixed-accessors (name documentation required index default packed) (proto- field)
-    (let ((dflt (if (stringp default)
-                  (if (i= (length default) 0) nil default)
-                  default)))
-      (format stream "~&~@[~VT~]~(~A~) ~A ~A = ~D~@[ [default = ~A]~]~@[ [packed=true]~*~];~:[~*~*~;~VT// ~A~]~%"
-              (and (not (zerop indentation)) indentation)
-              required (proto-type field) name index dflt packed
-              documentation *protobuf-field-comment-column* documentation))))
+    (let ((group (let ((msg (find-message message (proto-class field))))
+                   (and msg (eql (proto-message-type msg) :group) msg)))
+          (dflt  (if (stringp default)
+                   (if (i= (length default) 0) nil default)
+                   default)))
+      (cond (group
+             (format stream "~&~@[~VT~]~(~A~) "
+                     (and (not (zerop indentation)) indentation) required)
+             (write-protobuf-as type group stream :indentation indentation :index index :arity required))
+            (t
+             (format stream "~&~@[~VT~]~(~A~) ~A ~A = ~D~@[ [default = ~A]~]~@[ [packed=true]~*~];~:[~*~*~;~VT// ~A~]~%"
+                     (and (not (zerop indentation)) indentation)
+                     required (proto-type field) name index dflt packed
+                     documentation *protobuf-field-comment-column* documentation))))))
 
 (defmethod write-protobuf-as ((type (eql :proto)) (extension protobuf-extension) stream
                               &key (indentation 0) more)
 ;;; Pretty print a schema as a .lisp file
 
 (defmethod write-protobuf-as ((type (eql :lisp)) (protobuf protobuf) stream
-                              &key (indentation 0) more)
-  (declare (ignore more))
-  (with-prefixed-accessors (name class documentation package lisp-package imports optimize options) (proto- protobuf)
-    (let* ((pkg      (and package (if (stringp package) package (string package))))
+                              &key (indentation 0))
+  (with-prefixed-accessors (name class documentation package lisp-package imports) (proto- protobuf)
+    (let* ((optimize (let ((opt (find-option protobuf "optimize_for")))
+                       (and opt (cond ((string= opt "SPEED") :speed)
+                                      ((string= opt "CODE_SIZE") :space)
+                                      (t nil)))))
+           (options  (remove "optimize_for" (proto-options protobuf) :test #'string-equal :key #'proto-name))
+           (pkg      (and package (if (stringp package) package (string package))))
            (lisp-pkg (and lisp-package (if (stringp lisp-package) lisp-package (string lisp-package))))
            (*protobuf-package* (or (find-package lisp-pkg)
-                                   (find-package (string-upcase lisp-pkg))))
-           (*package* (or *protobuf-package* *package*)))
+                                   (find-package (string-upcase lisp-pkg))
+                                   *package*))
+           (*package* *protobuf-package*))
       (when (or lisp-pkg pkg)
         (format stream "~&(in-package \"~A\")~%~%" (string-upcase (or lisp-pkg pkg))))
       (when documentation
 
 
 (defmethod write-protobuf-as ((type (eql :lisp)) (message protobuf-message) stream
-                              &key (indentation 0) more)
+                              &key (indentation 0) more index arity)
   (declare (ignore more))
-  (with-prefixed-accessors (name class alias-for conc-name extension-p documentation) (proto- message)
-    (when documentation
-      (write-protobuf-documentation type documentation stream :indentation indentation))
-    (format stream "~&~@[~VT~](proto:define-~A ~(~S~)"
-            (and (not (zerop indentation)) indentation)
-            (if extension-p "extend" "message") class)
-    (let ((other (and name (not (string= name (class-name->proto class))) name)))
-      (cond (extension-p
-             (format stream " ()"))
-            ((or alias-for conc-name documentation)
-             (format stream "~%~@[~VT~](~:[~*~*~;:name ~(~S~)~@[~%~VT~]~]~
+  (with-prefixed-accessors (name class alias-for conc-name message-type documentation) (proto- message)
+    (cond ((eql message-type :group)
+           (when documentation
+             (write-protobuf-documentation type documentation stream :indentation indentation))
+           (format stream "~&~@[~VT~](proto:define-group ~(~S~)"
+                   (and (not (zerop indentation)) indentation) class)
+           (let ((other (and name (not (string= name (class-name->proto class))) name)))
+             (format stream "~%~@[~VT~](:index ~D~@[~%~VT~]~
+                                        :arity ~(~S~)~@[~%~VT~]~
+                                        ~:[~*~*~;:name ~(~S~)~@[~%~VT~]~]~
                                         ~:[~*~*~;:alias-for ~(~S~)~@[~%~VT~]~]~
                                         ~:[~*~*~;:conc-name ~(~S~)~@[~%~VT~]~]~
                                         ~:[~*~;:documentation ~S~])"
                      (+ indentation 4)
+                     index (+ indentation 5)
+                     arity (and (or other alias-for conc-name documentation) (+ indentation 5))
                      other other (and (or alias-for conc-name documentation) (+ indentation 5))
                      alias-for alias-for (and (or documentation conc-name) (+ indentation 5))
                      conc-name conc-name (and documentation (+ indentation 5))
                      documentation documentation))
-            (t
-             (format stream " ()"))))
-    (cond (extension-p
-           (loop for (field . more) on (proto-fields message) doing
-             (when (proto-extension-p field)
-               (write-protobuf-as type field stream :indentation (+ indentation 2) :more more)
-               (when more
-                 (terpri stream)))))
-          (t
            (loop for (enum . more) on (proto-enums message) doing
              (write-protobuf-as type enum stream :indentation (+ indentation 2) :more more)
              (when more
                (terpri stream)))
-           (loop for (msg . more) on (proto-messages message) doing
-             (write-protobuf-as type msg stream :indentation (+ indentation 2) :more more)
-             (when more
-               (terpri stream)))
            (loop for (field . more) on (proto-fields message) doing
-             (write-protobuf-as type field stream :indentation (+ indentation 2) :more more)
+             (write-protobuf-as type field stream
+                                :indentation (+ indentation 2) :more more
+                                :message message)
              (when more
-               (terpri stream)))
-           (loop for (extension . more) on (proto-extensions message) doing
-             (write-protobuf-as type extension stream :indentation (+ indentation 2) :more more)
-             (when more
-               (terpri stream)))))
+               (terpri stream))))
+          (t
+           (when documentation
+             (write-protobuf-documentation type documentation stream :indentation indentation))
+           (format stream "~&~@[~VT~](proto:define-~A ~(~S~)"
+                   (and (not (zerop indentation)) indentation)
+                   (if (eql message-type :message) "message" "extend") class)
+           (let ((other (and name (not (string= name (class-name->proto class))) name)))
+             (cond ((eql message-type :extends)
+                    (format stream " ()"))
+                   ((or other alias-for conc-name documentation)
+                    (format stream "~%~@[~VT~](~:[~*~*~;:name ~(~S~)~@[~%~VT~]~]~
+                                               ~:[~*~*~;:alias-for ~(~S~)~@[~%~VT~]~]~
+                                               ~:[~*~*~;:conc-name ~(~S~)~@[~%~VT~]~]~
+                                               ~:[~*~;:documentation ~S~])"
+                            (+ indentation 4)
+                            other other (and (or alias-for conc-name documentation) (+ indentation 5))
+                            alias-for alias-for (and (or documentation conc-name) (+ indentation 5))
+                            conc-name conc-name (and documentation (+ indentation 5))
+                            documentation documentation))
+                   (t
+                    (format stream " ()"))))
+           (cond ((eql message-type :extends)
+                  (loop for (field . more) on (proto-fields message) doing
+                    (when (eql (proto-message-type field) :extends)
+                      (write-protobuf-as type field stream
+                                         :indentation (+ indentation 2) :more more
+                                         :message message)
+                      (when more
+                        (terpri stream)))))
+                 (t
+                  (loop for (enum . more) on (proto-enums message) doing
+                    (write-protobuf-as type enum stream :indentation (+ indentation 2) :more more)
+                    (when more
+                      (terpri stream)))
+                  (loop for (msg . more) on (proto-messages message) doing
+                    (unless (eql (proto-message-type msg) :group)
+                      (write-protobuf-as type msg stream :indentation (+ indentation 2) :more more)
+                      (when more
+                        (terpri stream))))
+                  (loop for (field . more) on (proto-fields message) doing
+                    (write-protobuf-as type field stream
+                                       :indentation (+ indentation 2) :more more
+                                       :message message)
+                    (when more
+                      (terpri stream)))
+                  (loop for (extension . more) on (proto-extensions message) doing
+                    (write-protobuf-as type extension stream :indentation (+ indentation 2) :more more)
+                    (when more
+                      (terpri stream)))))))
     (format stream ")")))
 
 (defparameter *protobuf-slot-comment-column* 56)
 (defmethod write-protobuf-as ((type (eql :lisp)) (field protobuf-field) stream
-                              &key (indentation 0) more)
-  (with-prefixed-accessors (value reader writer class documentation required default) (proto- field)
-    (let ((dflt (protobuf-default-to-clos-init default class))
-          (clss (let ((cl (case class
-                            ((:int32 :uint32 :int64 :uint64 :sint32 :sint64
-                              :fixed32 :sfixed32 :fixed64 :sfixed64) 'integer)
-                            ((:single) 'float)
-                            ((:double) 'double-float)
-                            ((:bool)   'boolean)
-                            ((:string) 'string)
-                            ((:symbol) 'symbol)
-                            (otherwise class))))
-                  (cond ((eq required :optional)
-                         `(or null ,cl))
-                        ((eq required :repeated)
-                         `(list-of ,cl))
-                        (t cl)))))
-      (format stream (if (keywordp class)
-                       ;; Keyword means a primitive type, print default with ~S
-                       "~&~@[~VT~](~(~S~) :type ~(~S~)~@[ :default ~S~]~
-                        ~@[ :reader ~(~S~)~]~@[ :writer ~(~S~)~])~:[~*~*~;~VT; ~A~]"
-                       ;; Non-keyword must mean an enum type, print default with ~A
-                       "~&~@[~VT~](~(~S~) :type ~(~S~)~@[ :default ~(:~A~)~]~
-                        ~@[ :reader ~(~S~)~]~@[ :writer ~(~S~)~])~:[~*~*~;~VT; ~A~]")
-              (and (not (zerop indentation)) indentation)
-              value clss dflt reader writer
-              ;; Don't write the comment if we'll insert a close paren after it
-              (and more documentation) *protobuf-slot-comment-column* documentation))))
+                              &key (indentation 0) more message)
+  (with-prefixed-accessors (value reader writer class required index documentation default) (proto- field)
+    (let ((group (let ((msg (find-message message (proto-class field))))
+                   (and msg (eql (proto-message-type msg) :group) msg)))
+          (dflt  (protobuf-default-to-clos-init default class))
+          (clss  (let ((cl (case class
+                             ((:int32 :uint32 :int64 :uint64 :sint32 :sint64
+                               :fixed32 :sfixed32 :fixed64 :sfixed64) 'integer)
+                             ((:single) 'float)
+                             ((:double) 'double-float)
+                             ((:bool)   'boolean)
+                             ((:string) 'string)
+                             ((:symbol) 'symbol)
+                             (otherwise class))))
+                   (cond ((eq required :optional)
+                          `(or null ,cl))
+                         ((eq required :repeated)
+                          `(list-of ,cl))
+                         (t cl)))))
+      (cond (group
+             (write-protobuf-as type group stream :indentation indentation :index index :arity required))
+            (t
+             (format stream (if (keywordp class)
+                              ;; Keyword means a primitive type, print default with ~S
+                              "~&~@[~VT~](~(~S~) :type ~(~S~)~@[ :default ~S~]~
+                               ~@[ :reader ~(~S~)~]~@[ :writer ~(~S~)~])~:[~*~*~;~VT; ~A~]"
+                              ;; Non-keyword must mean an enum type, print default with ~A
+                              "~&~@[~VT~](~(~S~) :type ~(~S~)~@[ :default ~(:~A~)~]~
+                               ~@[ :reader ~(~S~)~]~@[ :writer ~(~S~)~])~:[~*~*~;~VT; ~A~]")
+                     (and (not (zerop indentation)) indentation)
+                     value clss dflt reader writer
+                     ;; Don't write the comment if we'll insert a close paren after it
+                     (and more documentation) *protobuf-slot-comment-column* documentation))))))
 
 (defmethod write-protobuf-as ((type (eql :lisp)) (extension protobuf-extension) stream
                               &key (indentation 0) more)
index e1c40a5ddbb0a3a1bebed36ca9fa9b4432b10f4a..05facc7c91380ba7fa4ebae62e17c937e159406b 100644 (file)
@@ -48,6 +48,7 @@
    "DEFINE-MESSAGE"
    "DEFINE-EXTEND"
    "DEFINE-EXTENSION"
+   "DEFINE-GROUP"
    "DEFINE-SERVICE"
 
    ;; Upgradability testing
    "PROTO-EXTENSION-TO"
    "PROTO-EXTENDERS"
    "PROTO-EXTENSIONS"
-   "PROTO-EXTENSION-P"
    "PROTO-FIELDS"
    "PROTO-FUNCTION"
    "PROTO-IMPORTS"
    "PROTO-INPUT-NAME"
    "PROTO-INPUT-TYPE"
    "PROTO-LISP-PACKAGE"
+   "PROTO-MESSAGE-TYPE"
    "PROTO-MESSAGES"
    "PROTO-METHODS"
    "PROTO-NAME"
-   "PROTO-OPTIMIZE"
    "PROTO-OPTIONS"
    "PROTO-OUTPUT-NAME"
    "PROTO-OUTPUT-TYPE"
index ed4125cafaf299d2ec599faa914ffc1d7a7dff7c..2494b3532605a921936e17248b3dc56824d827e2 100644 (file)
                               (reader (and field (proto-reader field)))
                               (writer (and field (proto-writer field)))
                               msg)
-                        (if (null field)
+                         (if (null field)
                            ;; If there's no field descriptor for this index, just skip
                            ;; the next element in the buffer having the given wire type
                            (setq index (skip-element buffer index tag))
index a93db0cb96deddc5d3f133616facf6277b91d2e5..03486b9517838729b873cf10ac7d3ae8b12310a1 100644 (file)
 
 (defun make-lisp-symbol (string)
   "Intern a string of the 'package:string' and return the symbol."
-  (let* ((colon (position #\: string))
-         (pkg   (if colon (subseq string 0 colon) "KEYWORD"))
-         (sym   (if colon (subseq string (+ colon 1)) string)))
+  (let* ((string (string string))
+         (colon  (position #\: string))
+         (pkg    (if colon (subseq string 0 colon) "KEYWORD"))
+         (sym    (if colon (subseq string (+ colon 1)) string)))
     (intern sym pkg)))
 
 #-quux