]> asedeno.scripts.mit.edu Git - cl-protobufs.git/commitdiff
While implementing the Fortuneteller stubby server, I ran into
authorScott McKay <swm@google.com>
Mon, 11 Jun 2012 17:13:55 +0000 (17:13 +0000)
committerScott McKay <swm@google.com>
Mon, 11 Jun 2012 17:13:55 +0000 (17:13 +0000)
some things that needed to be addressed:
 - We need to do a more thorough search of namespaces so that
   qualified references to imported messages, etc, work properly.
   It's still not 100%, but it covers most of what I can find.
   - Name mappers now do their best to preserve qualified names.
   - 'find-xxx' methods search the qualified names.
 - Fix the importation process to cooperate better with ASDF, i.e.,
   don't bother re-importing things that have already compiled
   as part of an ASDF compile/load operation.
 - When generating .lisp from .proto, if no package has been
   declared, do it. And fill it with nice exports, too. This
   won't be exactly right, but it'll make it easier to create
   a proper package declaration, if you so desire.
 - Do a better job of generating client- and server-side stubs.

Passes 'precheckin' with the new Protobufs unit tests in place.
And more to the point, the Fortuneseeker server now works as a
Stubby client using my new Google3 http2 and rpc2 libraries.

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

asdf-support.lisp
cl-protobufs.rst
define-proto.lisp
model-classes.lisp
parser.lisp
pkgdcl.lisp
printer.lisp
utilities.lisp

index 1580a6c7ec8752ff6b0e18fcfcc9da8d9e710673..bd091000e922000d1c84a0cb1c2db65010d82cb5 100644 (file)
    If the file is a .proto file, it first parses it and writes a .lisp file.
    The .lisp file is the compiled and loaded."
   (dolist (import imports)
-    (let* ((base-path  (if *compile-file-pathname*
-                         (merge-pathnames (pathname import) *compile-file-pathname*)
-                         (pathname import)))
+    (let* ((import-dir  (pathname-directory (pathname import)))
+           (import-name (pathname-name (pathname import)))
+           (imported   (find-schema (class-name->proto import-name)))
+           ;;---*** This just isn't right, either in QRes or Google3
+           (base-dir   (ecase (car import-dir)
+                         (:relative
+                          (assert *compile-file-pathname* ()
+                                  "You need a compile-file pathname for relative imports")
+                          import-dir)
+                         (:absolute
+                          import-dir)))
+           (base-path  (make-pathname :name import-name :directory base-dir
+                                      :defaults *compile-file-pathname*))
            (proto-file (make-pathname :type "proto" :defaults base-path))
            (lisp-file  (make-pathname :type "lisp"  :defaults base-path))
            (fasl-file  (compile-file-pathname lisp-file))
                             (ignore-errors (file-write-date lisp-file))))
            (fasl-date  (and (probe-file fasl-file)
                             (ignore-errors (file-write-date fasl-file)))))
+      (when imported
+        (setf (proto-imported-schemas schema)
+              (nconc (proto-imported-schemas schema) (list imported)))
+        (return-from process-imports imported))
       (when (string= (pathname-type base-path) "proto")
         ;; The user asked to import a .proto file
         ;; If there's no .lisp file or an older .lisp file, parse the .proto file now
       (let* ((imported (find-schema base-path)))
         (when imported
           (setf (proto-imported-schemas schema)
-                (nconc (proto-imported-schemas schema) (list imported)))))
-      base-path)))
+                (nconc (proto-imported-schemas schema) (list imported))))
+        imported))))
index 50e8077ac23cf1c8c4d57e32dc100e78a0e7425b..4eb287e2c2a91aa56683fb1d2ffcd46ff60ea3fa 100644 (file)
@@ -24,6 +24,7 @@ Protobufs for Common Lisp
       2.3  Using .proto files directly
       2.4  Using the Protobufs macros
         2.4.1  Protobufs types
+        2.4.2  Protobufs service stubs
     3  Serializing and deserializing
       3.1  Wire format
       3.2  Text format
@@ -630,6 +631,38 @@ The following existing Lisp type correspond to other Protobufs types:
 Note that ``(or <T> null)`` corresponds to an optional field.
 
 
+Protobufs service stubs
+-----------------------
+
+When you use the ``proto:define-service`` macro to define a service
+with some methods, the macro defines "stubs" (CLOS generic functions)
+for each of the methods in the service. Each method gets a client stub
+and a server stub whose signatures are, respectively::
+
+  (rpc-channel input output &key callback) => output
+  (rpc-channel input output) => output
+
+The type of *rpc-channel* is unspecified, but is meant to be a
+"channel" over which some sort of RPC call will be done. The types of
+*input* and *output* are classes that were defined via
+Protobufs. *callback* is a function of two arguments, the RPC channel
+and the output; it is intended for use by asynchronous RPC calls.
+
+For example, this fragment defines four stubs::
+
+  (proto:define-service color-wheel ()
+    (get-color (get-color-request color))
+    (add-color (add-color-request color)))
+
+The client stubs are ``get-color`` and ``add-color``, the server stubs
+are ``do-get-color`` and ``do-add-color``. An RPC library will implement
+a method for the client stub. You must fill in the server stub yourself;
+it will implement the desired functionality.
+
+It is beyond the scope of this Protobufs library to provide the RPC
+service; that is the domain of another library.
+
+
 Serializing and deserializing
 =============================
 
index da38d120c55f01f5dfd6d8b4d08747c968f17253..d6b395874ef88bd12360745d062197ba4607626f 100644 (file)
@@ -53,9 +53,7 @@
                                  options)
                      :documentation documentation))
          (*protobuf* schema)
-         (*protobuf-package* (or (find-package lisp-pkg)
-                                 (find-package (string-upcase lisp-pkg))
-                                 *package*)))
+         (*protobuf-package* (or (find-proto-package lisp-pkg) *package*)))
     (apply #'process-imports schema imports)
     (with-collectors ((forms collect-form))
       (dolist (msg messages)
                                 collect (make-instance 'protobuf-option
                                           :name  (if (symbolp key) (slot-name->proto key) key)
                                           :value val)))
+                 (package   *protobuf-package*)
+                 (client-fn function)
+                 (server-fn (intern (format nil "~A-~A" 'do function) package))
                  (method  (make-instance 'protobuf-method
                             :class function
                             :name  (or name (class-name->proto function))
+                            :client-stub client-fn
+                            :server-stub server-fn
                             :input-type  input-type
                             :input-name  (or input-name (class-name->proto input-type))
                             :output-type output-type
                             :documentation documentation)))
             (setf (proto-methods service) (nconc (proto-methods service) (list method)))
             ;; The following are the hooks to CL-Stubby
-            (let* ((package   *protobuf-package*)
-                   (client-fn function)
-                   (server-fn (intern (format nil "~A-~A" 'do function) package))
-                   (vinput    (intern (format nil "~A-~A" (symbol-name input-type) 'in) package))
+            (let* ((vinput    (intern (format nil "~A-~A" (symbol-name input-type) 'in) package))
                    (voutput   (intern (format nil "~A-~A" (symbol-name output-type) 'out) package))
                    (vchannel  (intern (symbol-name 'channel) package))
                    (vcallback (intern (symbol-name 'callback) package)))
               ;; because we can just use multi-methods.
               ;; The CL-Stubby macros take care of serializing the input, transmitting the
               ;; request over the wire, waiting for input (or not if it's asynchronous),
-              ;; filling in the output, and calling the callback (if it's synchronous).
+              ;; filling in the output, and calling the callback (if it's asynchronous).
               ;; It's not very Lispy to side-effect an output object, but it makes
               ;; asynchronous calls simpler.
               (collect-form `(defgeneric ,client-fn (,vchannel ,vinput ,voutput &key ,vcallback)
               ;; The channel objects hold client identity information, deadline info,
               ;; etc, and can be side-effected to indicate success or failure
               ;; CL-Stubby provides the channel classes and does (de)serialization, etc
-              (collect-form `(defgeneric ,server-fn (,vchannel ,vinput ,voutput &key ,vcallback)
+              (collect-form `(defgeneric ,server-fn (,vchannel ,vinput ,voutput)
                                ,@(and documentation `((:documentation ,documentation)))
                                #-sbcl (declare (values ,output-type))))))))
       `(progn
index 06cad50a37370d94dbb4da4ef2ca00fab7bac67b..fb7501fa19633a68ff80bc2180eb4ef5f85506e5 100644 (file)
          :reader proto-name
          :initarg :name
          :initform nil)
+   (full-name :type (or null string)            ;the fully qualified name, e.g., "proto2.MessageSet"
+              :accessor proto-qualified-name
+              :initarg :qualified-name
+              :initform nil)
    (options :type (list-of protobuf-option)     ;options, mostly just passed along
             :accessor proto-options
             :initarg :options
   (:documentation
    "The base class for all Protobufs model classes."))
 
+(defun find-qualified-name (name protos
+                            &key (proto-key #'proto-name) (lisp-key #'proto-class))
+  "Find something by its string name.
+   First do a simple name match.
+   Failing that, exhaustively search qualified names."
+  (or (find name protos :key proto-key :test #'string=)
+      ;; Get desperate in the face of incomplete namespace support
+      ;;--- This needs to be more sophisticated than just using Lisp packages
+      (multiple-value-bind (name package path other)
+          (proto->class-name name)
+        (declare (ignore path))
+        (let* ((name   (string name))
+               (symbol (or (and package (find-symbol name package))
+                           (and other
+                                (find-proto-package other)
+                                (find-symbol name (find-proto-package other))))))
+          (when symbol
+            (find symbol protos :key lisp-key))))))
+
 
 ;; A Protobufs schema, corresponds to one .proto file
 (defclass protobuf-schema (base-protobuf)
 
 (defmethod find-enum ((schema protobuf-schema) (name string))
   (labels ((find-it (schema)
-             (let ((enum (find name (proto-enums schema) :key #'proto-name :test #'string=)))
+             (let ((enum (find-qualified-name name (proto-enums schema))))
                (when enum
                  (return-from find-enum enum))
                (map () #'find-it (proto-imported-schemas schema)))))
 
 (defmethod find-message ((schema protobuf-schema) (name string))
   (labels ((find-it (schema)
-             (let ((message (or (find name (proto-extenders schema) :key #'proto-name :test #'string=)
-                                (find name (proto-messages  schema) :key #'proto-name :test #'string=))))
+             (let ((message (or (find-qualified-name name (proto-extenders schema))
+                                (find-qualified-name name (proto-messages  schema)))))
                (when message
                  (return-from find-message message))
                (map () #'find-it (proto-imported-schemas schema)))))
   (find name (proto-services schema) :key #'proto-class))
 
 (defmethod find-service ((schema protobuf-schema) (name string))
-  (find name (proto-services schema) :key #'proto-name :test #'string=))
+  (find-qualified-name name (proto-services schema)))
 
 
 ;; We accept and store any option, but only act on a few: default, packed,
   (find-message message (class-name type)))
 
 (defmethod find-message ((message protobuf-message) (name string))
-  (or (find name (proto-extenders message) :key #'proto-name :test #'string=)
-      (find name (proto-messages message) :key #'proto-name :test #'string=)
+  (or (find-qualified-name name (proto-extenders message))
+      (find-qualified-name name (proto-messages message))
       (find-message (proto-parent message) name)))
 
 (defmethod find-enum ((message protobuf-message) type)
       (find-enum (proto-parent message) type)))
 
 (defmethod find-enum ((message protobuf-message) (name string))
-  (or (find name (proto-enums message) :key #'proto-name :test #'string=)
+  (or (find-qualified-name name (proto-enums message))
       (find-enum (proto-parent message) name)))
 
 (defgeneric find-field (message name)
   (find name (proto-fields message) :key #'proto-value))
 
 (defmethod find-field ((message protobuf-message) (name string))
-  (find name (proto-fields message) :key #'proto-name :test #'string=))
+  (find-qualified-name name (proto-fields message) :lisp-key #'proto-value))
 
 (defmethod find-field ((message protobuf-message) (index integer))
   (find index (proto-fields message) :key #'proto-index))
   (find name (proto-methods service) :key #'proto-class))
 
 (defmethod find-method ((service protobuf-service) (name string))
-  (find name (proto-methods service) :key #'proto-name :test #'string=))
+  (find-qualified-name name (proto-methods service)))
 
 (defmethod find-method ((service protobuf-service) (index integer))
   (find index (proto-methods service) :key #'proto-index))
 
 ;; A Protobufs method within a service
 (defclass protobuf-method (base-protobuf)
-  ((itype :type (or null symbol)                ;the Lisp type name of the input
-           :accessor proto-input-type
-           :initarg :input-type
-           :initform nil)
+  ((client-fn :type symbol                      ;the Lisp name of the client stb
+              :accessor proto-client-stub
+              :initarg :client-stub)
+   (server-fn :type symbol                      ;the Lisp name of the server stb
+              :accessor proto-server-stub
+              :initarg :server-stub)
+   (itype :type symbol                          ;the Lisp type name of the input
+          :accessor proto-input-type
+          :initarg :input-type)
    (iname :type (or null string)                ;the Protobufs name of the input
           :accessor proto-input-name
           :initarg :input-name
           :initform nil)
-   (otype :type (or null symbol)                ;the Lisp type name of the output
-           :accessor proto-output-type
-           :initarg :output-type
-           :initform nil)
+   (otype :type symbol                          ;the Lisp type name of the output
+          :accessor proto-output-type
+          :initarg :output-type)
    (oname :type (or null string)                ;the Protobufs name of the output
           :accessor proto-output-name
           :initarg :output-name
 (defmethod print-object ((m protobuf-method) stream)
   (print-unreadable-object (m stream :type t :identity t)
     (format stream "~S (~S) => (~S)"
-            (proto-function m) (proto-input-type m) (proto-output-type m))))
-
-;; The 'class' slot really holds the name of the function,
-;; so let's give it a better name
-(defmethod proto-function ((method protobuf-method))
-  (proto-class method))
-
-(defmethod (setf proto-function) (function (method protobuf-method))
-  (setf (proto-function method) function))
+            (proto-class m) (proto-input-type m) (proto-output-type m))))
index b996481dd8ac63807ac02e355aaba35bf42b645b..8a084b302cbd4a804c8f9985d64a78d10ace01e7 100644 (file)
                                (name   (and option (proto-name option)))
                                (value  (and option (proto-value option))))
                           (when (and option (option-name= name "lisp_package"))
-                            (let ((package (or (find-package value)
-                                               (find-package (string-upcase value))
-                                               *protobuf-package*)))
+                            (let ((package (or (find-proto-package value) *protobuf-package*)))
                               (setf (proto-lisp-package schema) value)
                               (setq *protobuf-package* package)))))
                        ((string= token "enum")
     (setf (proto-package schema) package)
     (unless (proto-lisp-package schema)
       (setf (proto-lisp-package schema) lisp-pkg))
-    (let ((package (or (find-package lisp-pkg)
-                       (find-package (string-upcase lisp-pkg))
-                       *protobuf-package*)))
+    (let ((package (or (find-proto-package lisp-pkg) *protobuf-package*)))
       (setq *protobuf-package* package))))
 
 (defun parse-proto-import (stream schema &optional (terminator #\;))
                    (expect-char stream #\; () "service"))
                  (maybe-skip-comments stream)
                  opts))
+         (stub   (proto->class-name name *protobuf-package*))
          (method (make-instance 'protobuf-method
-                   :class (proto->class-name name *protobuf-package*)
+                   :class stub
                    :name  name
                    :input-type  (proto->class-name in *protobuf-package*)
                    :input-name  in
                    :output-name out
                    :index index
                    :options opts)))
-    (let ((name (find-option method "lisp_name")))
-      (when name
-        (setf (proto-function method) (make-lisp-symbol name))))
+    (let* ((name (find-option method "lisp_name"))
+           (stub (or (and name (make-lisp-symbol name))
+                     stub)))
+      (setf (proto-class method) stub
+            (proto-client-stub method) stub
+            (proto-server-stub method) (intern (format nil "~A-~A" 'do stub) *protobuf-package*)))
     (assert (string= ret "returns") ()
             "Syntax error in 'message' at position ~D" (file-position stream))
     (setf (proto-methods service) (nconc (proto-methods service) (list method)))
index b99191f92a485b7f59e9f4012f3a5f65b34b03e3..0246a6516cdc0c6ec14af2371d69f74350813430 100644 (file)
    "BASE-PROTOBUF"
    "PROTO-ALIAS-FOR"
    "PROTO-CLASS"
+   "PROTO-CLIENT-STUB"
    "PROTO-DEFAULT"
    "PROTO-DOCUMENTATION"
    "PROTO-ENUMS"
    "PROTO-EXTENDERS"
    "PROTO-EXTENSIONS"
    "PROTO-FIELDS"
-   "PROTO-FUNCTION"
    "PROTO-IMPORTED-SCHEMAS"
    "PROTO-IMPORTS"
    "PROTO-INDEX"
    "PROTO-PACKAGE"
    "PROTO-PACKED"
    "PROTO-PARENT"
+   "PROTO-QUALIFIED-NAME"
    "PROTO-READER"
    "PROTO-REQUIRED"
+   "PROTO-SERVER-STUB"
    "PROTO-SERVICES"
    "PROTO-SLOT"
    "PROTO-SYNTAX"
index b1ddce2a4ac5ff484c00b1cbd5c4d07f637c1bdb..4784ba0b2e68f485249968c16a6481ce667dbe64 100644 (file)
            (lisp-pkg (and lisp-package (if (stringp lisp-package) lisp-package (string lisp-package))))
            (*show-lisp-enum-indexes* show-enum-indexes)
            (*show-lisp-field-indexes* show-field-indexes)
-           (*protobuf-package* (or (find-package lisp-pkg)
-                                   (find-package (string-upcase lisp-pkg))
-                                   *package*))
+           (*protobuf-package* (or (find-proto-package lisp-pkg) *package*))
            (*package* *protobuf-package*))
       (when (or lisp-pkg pkg)
         (let ((pkg (string-upcase (or lisp-pkg pkg))))
-          (format stream "~&(eval-when (:execute :compile-toplevel :load-toplevel) ~
-                          ~%  (unless (find-package \"~A\") ~
-                          ~%    (defpackage ~A (:use :COMMON-LISP :PROTOBUFS)))) ~
-                          ~%(in-package \"~A\")~%~%"
-                  pkg pkg pkg)))
+          (format stream "~&(cl:eval-when (:execute :compile-toplevel :load-toplevel) ~
+                          ~%  (unless (cl:find-package \"~A\") ~
+                          ~%    (cl:defpackage ~A (:use :COMMON-LISP)))) ~
+                          ~%(cl:in-package \"~A\") ~
+                          ~%(cl:export~%~{  ~S~^~%~})~%~%"
+                  pkg pkg pkg (collect-exports schema))))
       (when documentation
         (write-schema-documentation type documentation stream :indentation indentation))
       (format stream "~&(proto:define-schema ~(~A~)" (or class name))
                             &key (indentation 0) more)
   (declare (ignore more))
   (with-prefixed-accessors
-      (function documentation input-type output-type options) (proto- method)
+      (class documentation input-type output-type options) (proto- method)
     (when documentation
       (write-schema-documentation type documentation stream :indentation indentation))
     (format stream "~&~@[~VT~](~(~S~) (~(~S~) ~(~S~))"
             (and (not (zerop indentation)) indentation)
-            function input-type output-type)
+            class input-type output-type)
     (when options
       (format stream "~%~VT:options (~{~@/protobuf-option/~^ ~})"
               (+ indentation 2) options))
     (format stream ")")))
+
+
+;;; Collect symbols to be exported
+
+(defgeneric collect-exports (schema)
+  (:documentation
+   "Collect all the symbols that should be exported from a Protobufs package"))
+
+(defmethod collect-exports ((schema protobuf-schema))
+  (delete-duplicates
+   (delete-if #'(lambda (s) (string-equal s "NIL"))
+    (append (mapcan #'collect-exports (proto-enums schema))
+            (mapcan #'collect-exports (proto-messages schema))
+            (mapcan #'collect-exports (proto-services schema))))
+   :from-end t))
+
+;; Export just the type name
+(defmethod collect-exports ((enum protobuf-enum))
+  (list (symbol-name (proto-class enum))))
+
+;; Export the class name and all of the accessor names
+(defmethod collect-exports ((message protobuf-message))
+  (append (list (symbol-name (proto-class message)))
+          (mapcan #'collect-exports (proto-fields message))))
+
+;; Export just the slot accessor name
+(defmethod collect-exports ((field protobuf-field))
+  (list (symbol-name (proto-slot field))))
+
+;; Export the names of all the methods
+(defmethod collect-exports ((service protobuf-service))
+  (mapcan #'collect-exports (proto-methods service)))
+
+;; Export just the method name
+(defmethod collect-exports ((method protobuf-method))
+  (list (symbol-name (proto-class method))))
index 0fb858b97157e36655d614fba7b57547843f025f..4c8b1dcc1979a9b3c676e1053840c7025ad7924e 100644 (file)
                      ((digit-char-p ch)
                       (uncamel (rest chars) 'digit 
                                (cons ch result)))
-                     ((eql ch #\_)
-                      (uncamel (rest chars) '_
+                     ((or (eql ch #\-) (eql ch #\_))
+                      (uncamel (rest chars) 'dash
                                (cons #\- result)))
+                     ((eql ch #\.)
+                      (uncamel (rest chars) 'dot
+                               (cons #\. result)))
                      (t
                       (error "Invalid name character: ~A" ch))))))
     (concatenate 'string (nreverse (uncamel (concatenate 'list name) nil ())))))
          (intern (string-upcase x) (find-package "KEYWORD")))
         (t nil)))
 
+
 ;;; Collectors, etc
 
 (defmacro with-collectors ((&rest collection-descriptions) &body body)
     form))
 
 
+;;; Types
+
 ;; A parameterized list type for repeated fields
 ;; The elements aren't type-checked
 (deftype list-of (type)
 (deftype vector-of (type)
   (if (eq type 'null)
     'null
-    '(array *)))
+    '(array * (*))))            ;an 1-dimensional array of any type
 
 ;; This corresponds to the :bytes Protobufs type
-(deftype byte-vector () '(array (unsigned-byte 8)))
+(deftype byte-vector () '(array (unsigned-byte 8) (*)))
 
 (defun make-byte-vector (size)
   (make-array size :element-type '(unsigned-byte 8)))
 (defvar *proto-name-separators* '(#\- #\_ #\/ #\space))
 (defvar *camel-case-field-names* nil)
 
+(defun find-proto-package (name)
+  "A very fuzzy definition of 'find-package'."
+  (typecase name
+    ((or string symbol)
+     ;; Try looking under the given name and the all-uppercase name
+     (or (find-package (string name))
+         (find-package (string-upcase (string name)))))
+    ((cons)
+     ;; If 'name' is a list, it's actually a fully-qualified path
+     (or (find-proto-package (first name))
+         (find-proto-package (format nil "~{~A~^.~}" name))))))
+
 ;; "class-name" -> "ClassName", ("ClassName")
 ;; "outer-class.inner-class" -> "InnerClass", ("OuterClass" "InnerClass")
 (defun class-name->proto (x)
                                           (camel-case (format nil "~A" x) *proto-name-separators*))))
          (nx (car (last xs)))
          (name (remove-if-not #'alphanumericp (camel-case nx *proto-name-separators*))))
-    (values name (append ns (list name)))))
+    (values name (append ns (list name))
+            ;; This might be the name of a package, too
+            (format nil "~{~A~^.~}" (butlast xs)))))
 
 ;; "enum-value" -> "ENUM_VALUE", ("ENUM_VALUE")
 ;; "class-name.enum-value" -> "ENUM_VALUE", ("ClassName" "ENUM_VALUE")
          (name (remove-if-not #'(lambda (x) (or (alphanumericp x) (eql x #\_)))
                               (format nil "~{~A~^_~}"
                                       (split-string nx :separators *proto-name-separators*)))))
-    (values name (append ns (list name)))))
+    (values name (append ns (list name))
+            (format nil "~{~A~^.~}" (butlast xs)))))
 
 ;; "slot-name" -> "slot_name", ("slot_name") or "slotName", ("slotName")
 ;; "class-name.slot-name" -> "Class.slot_name", ("ClassName" "slot_name")
                  (remove-if-not #'(lambda (x) (or (alphanumericp x) (eql x #\_)))
                                 (format nil "~{~A~^_~}"
                                         (split-string nx :separators *proto-name-separators*))))))
-    (values name (append ns (list name)))))
+    (values name (append ns (list name))
+            (format nil "~{~A~^.~}" (butlast xs)))))
 
 
 ;; "ClassName" -> 'class-name
    This resolves Protobufs qualified names as best as it can."
   (let* ((xs (split-string (substitute #\- #\_ (uncamel-case x))
                            :separators '(#\.)))
-         (pkg (and (cdr xs) (find-package (first xs))))
-         (package (or pkg package))
-         (name (format nil "~{~A~^.~}" (if pkg (cdr xs) xs))))
-    (values (if package (intern name package) (make-symbol name)) package xs)))
+         (pkg1 (and (cdr xs) (find-proto-package (first xs))))
+         (pkgn (and (cdr xs) (find-proto-package (butlast xs))))
+         (package (or pkg1 pkgn package))
+         (name (format nil "~{~A~^.~}" (if pkg1 (cdr xs) (if pkgn (last xs) xs)))))
+    (values (if package (intern name package) (make-symbol name)) package xs
+            ;; This might be the name of a package, too
+            (format nil "~{~A~^.~}" (butlast xs)))))
 
 ;; "ENUM_VALUE" -> :enum-value
 ;; "cl-user.ENUM_VALUE" -> :enum-value
    This resolves Protobufs qualified names as best as it can."
   (let* ((xs (split-string (substitute #\- #\_ (uncamel-case x))
                            :separators '(#\.)))
-         (pkg (and (cdr xs) (find-package (first xs))))
-         (package (or pkg package))
-         (name (format nil "~{~A~^.~}" (if pkg (cdr xs) xs))))
-    (values (kintern name) package xs)))
+         (pkg1 (and (cdr xs) (find-proto-package (first xs))))
+         (pkgn (and (cdr xs) (find-proto-package (butlast xs))))
+         (package (or pkg1 pkgn package))
+         (name (format nil "~{~A~^.~}" (if pkg1 (cdr xs) (if pkgn (last xs) xs)))))
+    (values (kintern name) package xs
+            (format nil "~{~A~^.~}" (butlast xs)))))
 
 ;; "slot_name" or "slotName" -> 'slot-name
 ;; "cl-user.slot_name" or "cl-user.slotName" -> 'cl-user::slot-name
    This resolves Protobufs qualified names as best as it can."
   (let* ((xs (split-string (substitute #\- #\_ (uncamel-case x))
                            :separators '(#\.)))
-         (pkg (and (cdr xs) (find-package (first xs))))
-         (package (or pkg package))
-         (name (format nil "~{~A~^.~}" (if pkg (cdr xs) xs))))
-    (values (if package (intern name package) (make-symbol name)) package xs)))
+         (pkg1 (and (cdr xs) (find-proto-package (first xs))))
+         (pkgn (and (cdr xs) (find-proto-package (butlast xs))))
+         (package (or pkg1 pkgn package))
+         (name (format nil "~{~A~^.~}" (if pkg1 (cdr xs) (if pkgn (last xs) xs)))))
+    (values (if package (intern name package) (make-symbol name)) package xs
+            (format nil "~{~A~^.~}" (butlast xs)))))
+
 
+;;; Warnings
 
 (define-condition protobufs-warning (warning simple-condition) ())
 
   `(let ((dspec:*redefinition-action* :quiet)) ,@body))
 
 \f
-;;; Floating point utilities
+;;; Portable floating point utilities
 
 #+(or abcl allegro cmu sbcl lispworks)
 (defun single-float-bits (x)