]> asedeno.scripts.mit.edu Git - cl-protobufs.git/commitdiff
Get some stuff working better for Stubby purposes.
authorScott McKay <swm@google.com>
Fri, 25 May 2012 20:24:29 +0000 (20:24 +0000)
committerScott McKay <swm@google.com>
Fri, 25 May 2012 20:24:29 +0000 (20:24 +0000)
 - Better implementation of 'uncamel-case' gives better names.
 - Add an index slot to 'protobuf-method' so that we can more
   easily do automatic service registration.
 - Give a better type to the index slot in 'protobuf-message'
   and 'protobuf-enum'.
 - Parser and macros now assign an index to each service method.
 - Define 'find-service' and 'find-method' for use by Stubby.

Passes 'precheckin' with the new Protobufs unit tests in place.

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

api.lisp
define-proto.lisp
model-classes.lisp
parser.lisp
pkgdcl.lisp
printer.lisp
protobufs.asd
tests/pkgdcl.lisp
upgradable.lisp
utilities.lisp

index 9faffffbf784a149b55e9071e6f090c6c29d313a..221b6ff66c2bbc322d39aa4946aee7dd242c4b65 100644 (file)
--- a/api.lisp
+++ b/api.lisp
               "There is no Protobufs message for the class ~S" class)
       (assert (eq class (type-of source)) ()
               "The objects ~S and ~S are of not of the same class" object source)
-      ;;--- Do this (shuold return side-effected 'object', not 'source')
+      ;;--- Do this (should return side-effected 'object', not 'source')
       type
       source)))
index ffbba3f4c65358dc040db309c752f300973dc6a0..5d2cf846d5f640e273beef70ec28ac2c29468870 100644 (file)
                 (setf (proto-parent model) extends)
                 (setf (proto-messages extends) (nconc (proto-messages extends) (list model)))
                 (when extra-slot
-                  ;;--- Fix all this duplicated code!
+                  ;;--- Refactor to get rid of all this duplicated code!
                   (let* ((inits  (cdr extra-slot))
                          (sname  (car extra-slot))
                          (stable (fintern "~A-VALUES" sname))
                     :class type
                     :name  name
                     :options options
-                    :documentation documentation)))
+                    :documentation documentation))
+         (index 0))
     (with-collectors ((forms collect-form))
       (dolist (method method-specs)
         (destructuring-bind (function (input-type output-type) &key name options documentation) method
                             :input-name  (or input-name (class-name->proto input-type))
                             :output-type output-type
                             :output-name (or output-name (class-name->proto output-type))
+                            :index (iincf index)
                             :options options
                             :documentation documentation)))
             (setf (proto-methods service) (nconc (proto-methods service) (list method)))
index aa10dd23187dfe4fca2952507141f1856a40388e..19407b9529a9e0885b6b7d67cf9ff57d9d16ba17 100644 (file)
@@ -76,7 +76,7 @@
    "The base class for all Protobufs model classes."))
 
 
-;; A protobuf schema, corresponds to one .proto file
+;; A Protobufs schema, corresponds to one .proto file
 (defclass protobuf-schema (base-protobuf)
   ((syntax :type (or null string)               ;syntax, passed on but otherwise ignored
            :accessor proto-syntax
     (format stream "~@[~S~]~@[ (package ~A)~]"
             (proto-class s) (proto-package s))))
 
+(defgeneric find-enum (protobuf type)
+  (:documentation
+   "Given a Protobufs schema or message and the name of an enum type,
+    returns the Protobufs enum corresponding to the type."))
+
+(defmethod find-enum ((schema protobuf-schema) (type symbol))
+  (labels ((find-it (schema)
+             (let ((enum (find type (proto-enums schema) :key #'proto-class)))
+               (when enum
+                 (return-from find-enum enum))
+               (map () #'find-it (proto-imported-schemas schema)))))
+    (find-it schema)))
+
+(defmethod find-enum ((schema protobuf-schema) (name string))
+  (labels ((find-it (schema)
+             (let ((enum (find name (proto-enums schema) :key #'proto-name :test #'string=)))
+               (when enum
+                 (return-from find-enum enum))
+               (map () #'find-it (proto-imported-schemas schema)))))
+    (find-it schema)))
+
 (defgeneric find-message (protobuf type)
   (:documentation
-   "Given a protobuf schema or message and a type name or class name,
+   "Given a Protobufs schema or message and a type name or class name,
     returns the Protobufs message corresponding to the type."))
 
 (defmethod find-message ((schema protobuf-schema) (type symbol))
                (map () #'find-it (proto-imported-schemas schema)))))
     (find-it schema)))
 
-(defgeneric find-enum (protobuf type)
+(defgeneric find-service (protobuf name)
   (:documentation
-   "Given a protobuf schema or message and the name of an enum type,
-    returns the Protobufs enum corresponding to the type."))
+   "Given a Protobufs schema,returns the Protobufs service of the given name."))
 
-(defmethod find-enum ((schema protobuf-schema) type)
-  (labels ((find-it (schema)
-             (let ((enum (find type (proto-enums schema) :key #'proto-class)))
-               (when enum
-                 (return-from find-enum enum))
-               (map () #'find-it (proto-imported-schemas schema)))))
-    (find-it schema)))
+(defmethod find-service ((schema protobuf-schema) (name symbol))
+  (find name (proto-services schema) :key #'proto-class))
 
-(defmethod find-enum ((schema protobuf-schema) (name string))
-  (labels ((find-it (schema)
-             (let ((enum (find name (proto-enums schema) :key #'proto-name :test #'string=)))
-               (when enum
-                 (return-from find-enum enum))
-               (map () #'find-it (proto-imported-schemas schema)))))
-    (find-it schema)))
+(defmethod find-service ((schema protobuf-schema) (name string))
+  (find name (proto-services schema) :key #'proto-name :test #'string=))
 
 
 ;; We accept and store any option, but only act on a few: default, packed,
 
 (defgeneric find-option (protobuf name)
   (:documentation
-   "Given a protobuf schema, message, enum, etc and the name of an option,
+   "Given a Protobufs schema, message, enum, etc and the name of an option,
     returns the value of the option and its (Lisp) type. The third value is
     true if an option was found, otherwise it is false."))
 
 
 (defgeneric remove-options (protobuf &rest names)
   (:documentation
-   "Given a protobuf schema, message, enum, etc and a set of option names,
+   "Given a Protobufs schema, message, enum, etc and a set of option names,
     remove all of those options from the set of options."))
 
 (defmethod remove-options ((protobuf base-protobuf) &rest names)
     (string= name1 name2 :start1 start1 :end1 end1 :start2 start2 :end2 end2)))
 
 
-;; A protobuf enumeration
+;; A Protobufs enumeration
 (defclass protobuf-enum (base-protobuf)
   ((alias :type (or null symbol)                ;use this if you want to make this enum
           :accessor proto-alias-for             ;  be an alias for an existing Lisp enum
             (proto-class e) (proto-alias-for e))))
 
 
-;; A protobuf value within an enumeration
+;; A Protobufs value within an enumeration
 (defclass protobuf-enum-value (base-protobuf)
-  ((index :type (integer #.(- (ash 1 31)) #.(1- (ash 1 31)))
-          :accessor proto-index                 ;the index of the enum value
+  ((index :type (signed-byte 32)                ;the numeric value of the enum
+          :accessor proto-index
           :initarg :index)
-   (value :type (or null symbol)
-          :accessor proto-value                 ;the Lisp value of the enum
+   (value :type (or null symbol)                ;the Lisp value of the enum
+          :accessor proto-value
           :initarg :value
           :initform nil))
   (:documentation
             (proto-name v) (proto-index v))))
 
 
-;; A protobuf message
+;; A Protobufs message
 (defclass protobuf-message (base-protobuf)
   ((parent :type (or protobuf-schema protobuf-message)
            :accessor proto-parent
 
 (defgeneric find-field (message name)
   (:documentation
-   "Given a protobuf message and a slot name, field name or index,
+   "Given a Protobufs message and a slot name, field name or index,
     returns the Protobufs field having that name."))
 
 (defmethod find-field ((message protobuf-message) (name symbol))
 (defconstant $empty-list    'empty-list)
 (defconstant $empty-vector  'empty-vector)
 
-;; A protobuf field within a message
+;; A Protobufs field within a message
 ;;--- Support the 'deprecated' option (have serialization ignore such fields?)
 (defclass protobuf-field (base-protobuf)
   ((type :type string                           ;the name of the Protobuf type for the field
    (required :type (member :required :optional :repeated)
              :accessor proto-required
              :initarg :required)
-   (index :type (integer 1 #.(1- (ash 1 29)))   ;the index number for this field
-          :accessor proto-index
+   (index :type (unsigned-byte 29)              ;the index number for this field
+          :accessor proto-index                 ; which must be strictly positive
           :initarg :index)
    (value :type (or null symbol)                ;the Lisp slot holding the value within an object
           :accessor proto-value                 ;this also serves as the Lisp field name
 (defmethod initialize-instance :after ((field protobuf-field) &rest initargs)
   (declare (ignore initargs))
   (when (slot-boundp field 'index)
-    (assert (not (<= 19000 (proto-index field) 19999)) ()
-            "Protobuf field indexes between 19000 and 19999 are not allowed")))
+    (assert (and (plusp (proto-index field))
+                 (not (<= 19000 (proto-index field) 19999))) ()
+            "Protobuf field indexes must be positive and not between 19000 and 19999 (inclusive)")))
 
 (defmethod make-load-form ((f protobuf-field) &optional environment)
   (make-load-form-saving-slots f :environment environment))
             (proto-extension-from e) (proto-extension-from e))))
 
 
-;; A protobuf service
+;; A Protobufs service
 (defclass protobuf-service (base-protobuf)
   ((methods :type (list-of protobuf-method)     ;the methods in the service
             :accessor proto-methods
     (format stream "~A"
             (proto-name s))))
 
+(defgeneric find-method (service name)
+  (:documentation
+   "Given a Protobufs service and a method name,
+    returns the Protobufs method having that name."))
+
+(defmethod find-method ((service protobuf-service) (name symbol))
+  (find name (proto-methods service) :key #'proto-class))
 
-;; A protobuf method within a service
+(defmethod find-method ((service protobuf-service) (name string))
+  (find name (proto-methods service) :key #'proto-name :test #'string=))
+
+(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
    (oname :type (or null string)                ;the Protobufs name of the output
           :accessor proto-output-name
           :initarg :output-name
-          :initform nil))
+          :initform nil)
+   (index :type (unsigned-byte 32)              ;an identifying index for this method
+          :accessor proto-index                 ; (used by Stubby)
+          :initarg :index))
   (:documentation
    "The model class that represents one method with a Protobufs service."))
 
index 1d6409ea643c91b487cde503408c2b43478e121c..3d8b57c90a11910f6780d99db50e9e01277d812d 100644 (file)
                  (maybe-skip-comments stream)))
          (service (make-instance 'protobuf-service
                     :class (proto->class-name name *protobuf-package*)
-                    :name name)))
+                    :name name))
+         (index 0))
     (loop
       (let ((token (parse-token stream)))
         (when (null token)
         (cond ((string= token "option")
                (parse-proto-option stream service))
               ((string= token "rpc")
-               (parse-proto-method stream service))
+               (parse-proto-method stream service (iincf index)))
               (t
                (error "Unrecognized token ~A at position ~D"
                       token (file-position stream))))))))
 
-(defun parse-proto-method (stream service)
+(defun parse-proto-method (stream service index)
   "Parse a Protobufs method from 'stream'.
    Updates the 'protobuf-service' object to have the method."
   (check-type service protobuf-service)
                    :input-name  in
                    :output-type (proto->class-name out *protobuf-package*)
                    :output-name out
+                   :index index
                    :options opts)))
     (let ((name (find-option method "lisp_name")))
       (when name
index d0e7ca94b91d8725aea2b5122a22c4ef83157a39..b7f32e7fe1c56df03769b5db02d67eab9aaa2816 100644 (file)
   (:nicknames :proto-impl)
   (:use :common-lisp :protobufs)
 
+  (:shadow
+   "FIND-METHOD")
   (:import-from :closer-mop
    "CLASS-SLOTS"
    "CLASS-DIRECT-SLOTS"
    ;; Object lookup
    "*ALL-SCHEMAS*"
    "*ALL-MESSAGES*"
-   "FIND-SCHEMA"
-   "FIND-MESSAGE-FOR-CLASS"
-   "FIND-MESSAGE"
    "FIND-ENUM"
    "FIND-FIELD"
+   "FIND-MESSAGE"
+   "FIND-MESSAGE-FOR-CLASS"
+   "FIND-METHOD"                ;if you ":use proto-impl", watch for name clash
+   "FIND-SCHEMA"
+   "FIND-SERVICE"
    "FIND-OPTION"
    "REMOVE-OPTIONS"
 
index 7d2882fef4d933d543bb1c3cfc773f7cdb7e1cfb..cedcf47e081037995410c132b124efdcefcd3742 100644 (file)
 
 (defvar *option-types* '(("optimize_for"          symbol)
                          ("deprecated"            symbol)
+                         ;; Keep the rest of these in alphabetical order
+                         ("cc_api_version"       integer)
                          ("cc_generic_services"   symbol)
+                         ("ctype"                 symbol)
+                         ("go_package"            string)
+                         ("java_api_version"     integer)
                          ("java_generic_services" symbol)
-                         ("py_generic_services"   symbol)
-                         ("ctype"                 symbol)))
+                         ("java_java5_enums"     boolean)
+                         ("java_multiple_files"  boolean)
+                         ("java_outer_classname"  string)
+                         ("java_package"          string)
+                         ("java_use_javaproto2"  boolean)
+                         ("py_api_version"       integer)
+                         ("py_generic_services"   symbol)))
 
 (defmethod write-schema-header ((type (eql :proto)) (schema protobuf-schema) stream)
   (when (any-lisp-option schema)
index 56f17e0a8835a85b97b443e6efc5f4c51ffb815e..bd56eb3584259b9fb0cc1609d3daacb8eef7874e 100644 (file)
@@ -28,7 +28,7 @@
     :maintainer '("Scott McKay")
     :description      "Protobufs for Common Lisp"
     :long-description "Protobufs for Common Lisp"
-    :depends-on (:cl-ppcre :closer-mop :split-sequence :drakma :cl-unicode)
+    :depends-on (:closer-mop :babel)
     :serial t
     :components
       ((:module "packages"
@@ -75,4 +75,4 @@
                    (:file "asdf-support")
                    (:file "examples")))))
 
-
+(pushnew :protobufs *features*)
index 3ecc9b00957731949723e25e9ab71251c2d2b462..18505612218d858026f30c8d57ce1b476ea3dcb5 100644 (file)
@@ -15,7 +15,9 @@
 
 (defpackage protobufs-test
   (:use :common-lisp :protobufs :protobufs-implementation)
-  (:nicknames :proto-test))
+  (:nicknames :proto-test)
+  (:shadowing-import-from :protobufs-implementation
+   "FIND-METHOD"))
 
 (defpackage protobuf-unittest
   (:use :common-lisp :protobufs)
index 6c733d6843a3a3a2da02bff8764f3d8cdf6960a0..4454df29060ed9089ecd0b7452750369160401f9 100644 (file)
    (eql    (proto-output-type method1) (proto-output-type method2))
    (equalp (proto-input-name method1) (proto-input-name method2))
    (equalp (proto-output-name method1) (proto-output-name method2))
+   (eql    (proto-index method1) (proto-index method2))
    (= (length (proto-options method1)) (length (proto-options method2)))
    (loop for option1 in (proto-options method1)
          as option2 = (find (proto-name option1) (proto-options method2)
index 8ad1c43cd78574e99ff71ce829be2957d4024246..e86846e816a54bd17a48741b3a0a845a2cafda06 100644 (file)
   (let ((words (split-string string :separators separators)))
     (format nil "~(~A~)~{~@(~A~)~}" (car words) (cdr words))))
 
-;; (uncamel-case "CamelCase") => "Camel-Case"
-;; (uncamel-case "TCPConnection") => "Tcp-Connection"
-(defun uncamel-case (string &optional (separator #\-))
-  (format nil (format nil "~~{~~A~~^~C~~}" separator)
-          (cl-ppcre:split "(?<=[a-z])(?=[A-Z])" string)))
+
+;; (uncamel-case "CamelCase") => "CAMEL-CASE"
+;; (uncamel-case "TCPConnection") => "TCP-CONNECTION"
+;; (uncamel-case "NewTCPConnection") => "NEW-TCP-CONNECTION"
+;; (uncamel-case "new_RPC_LispService") => "NEW-RPC-LISP-SERVICE"
+;; (uncamel-case "RPC_LispServiceRequest_get_request") => "RPC-LISP-SERVICE-REQUEST-GET-REQUEST"
+;; (uncamel-case "TCP2Name3") => "TCP2-NAME3"
+(defun uncamel-case (name)
+  ;; We need a whole state machine to get this right
+  (labels ((uncamel (chars state result)
+             (let ((ch (first chars)))
+               (cond ((null chars)
+                      result)
+                     ((upper-case-p ch)
+                      (uncamel (rest chars) 'upper
+                               (case state
+                                 ((upper)
+                                  ;; "TCPConnection" => "TCP-CONNECTION"
+                                  (if (and (second chars) (lower-case-p (second chars)))
+                                    (list* ch #\- result)
+                                    (cons ch result)))
+                                 ((lower digit) (list* ch #\- result))
+                                 (otherwise (cons ch result)))))
+                     ((lower-case-p ch)
+                      (uncamel (rest chars) 'lower
+                               (cons (char-upcase ch) result)))
+                     ((digit-char-p ch)
+                      (uncamel (rest chars) 'digit 
+                               (cons ch result)))
+                     ((eql ch #\_)
+                      (uncamel (rest chars) '_
+                               (cons #\- result)))
+                     (t
+                      (error "Invalid name character: ~A" ch))))))
+    (concatenate 'string (nreverse (uncamel (concatenate 'list name) nil ())))))
 
 
 (defun split-string (line &key (start 0) (end (length line)) (separators '(#\-)))
 (defun proto->class-name (x &optional package)
   "Given a Protobufs message or enum type name, returns a Lisp class or type name.
    This resolves Protobufs qualified names as best as it can."
-  (let* ((xs (split-string (substitute #\- #\_ (string-upcase (uncamel-case x)))
+  (let* ((xs (split-string (substitute #\- #\_ (uncamel-case x))
                            :separators '(#\.)))
          (pkg (and (cdr xs) (find-package (first xs))))
          (package (or pkg package))
 (defun proto->enum-name (x &optional package)
   "Given a Protobufs enum value name, returns a Lisp enum value name.
    This resolves Protobufs qualified names as best as it can."
-  (let* ((xs (split-string (substitute #\- #\_ (string-upcase (uncamel-case x)))
+  (let* ((xs (split-string (substitute #\- #\_ (uncamel-case x))
                            :separators '(#\.)))
          (pkg (and (cdr xs) (find-package (first xs))))
          (package (or pkg package))
 (defun proto->slot-name (x &optional package)
   "Given a Protobufs field value name, returns a Lisp slot name.
    This resolves Protobufs qualified names as best as it can."
-  (let* ((xs (split-string (substitute #\- #\_ (string-upcase (uncamel-case x)))
+  (let* ((xs (split-string (substitute #\- #\_ (uncamel-case x))
                            :separators '(#\.)))
          (pkg (and (cdr xs) (find-package (first xs))))
          (package (or pkg package))