1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;; Confidential and proprietary information of ITA Software, Inc. ;;;
5 ;;; Copyright (c) 2012 ITA Software, Inc. All rights reserved. ;;;
7 ;;; Original author: Scott McKay ;;;
9 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
11 (in-package "PROTO-IMPL")
14 ;;; Protocol buffer defining macros
16 ;; Define a schema named 'name', corresponding to a .proto file of that name
17 (defmacro define-proto (name (&key proto-name syntax package import options documentation)
18 &body messages &environment env)
19 "Define a schema named 'name', corresponding to a .proto file of that name.
20 'proto-name' can be used to override the defaultly generated name.
21 'syntax' and 'package' are as in .proto files.
22 'imports' is a list of pathname strings to be imported.
23 'options' is a property list, i.e., (\"key1\" \"val1\" \"key2\" \"val2\" ...).
24 The body consists of 'define-enum', 'define-message' or 'define-service' forms."
25 (with-collectors ((enums collect-enum)
29 (dolist (msg messages)
30 (assert (and (listp msg)
31 (member (car msg) '(define-enum define-message define-service))) ()
32 "The body of ~S must be one of ~{~S~^ or ~}"
33 'define-proto '(define-enum define-message define-service))
34 ;; The macro-expander will return a form that consists
35 ;; of 'progn' followed by a symbol naming what we've expanded
36 ;; (define-enum, define-message, define-service), followed by
37 ;; by a (optional) Lisp defining form (deftype, defclass),
38 ;; followed by a form that creates the model object
39 (destructuring-bind (&optional progn type model definers)
40 (macroexpand-1 msg env)
41 (assert (eq progn 'progn) ()
42 "The macroexpansion for ~S failed" msg)
43 (map () #'collect-form definers)
50 (collect-svc model)))))
51 ;;--- This should warn if the old one isn't upgradable to the new one
52 (let ((vname (fintern "*~A*" name))
53 (pname (or proto-name (class-name->proto name)))
55 (options (loop for (key val) on options by #'cddr
56 collect `(make-instance 'protobuf-option
62 (let ((protobuf (make-instance 'protobuf
65 :package ,(if (stringp package) package (string-downcase (string package)))
66 :imports ',(if (listp import) import (list import))
68 :options (list ,@options)
70 :messages (list ,@msgs)
71 :services (list ,@svcs)
72 :documentation ,documentation)))
73 (setq ,vname protobuf)
74 (setf (gethash ',pname *all-protobufs*) protobuf)
75 (setf (gethash ',cname *all-protobufs*) protobuf)
78 ;; Define an enum type named 'name' and a Lisp 'deftype'
79 (defmacro define-enum (name (&key proto-name conc-name options documentation) &body values)
80 "Define an enum type named 'name' and a Lisp 'deftype'.
81 'proto-name' can be used to override the defaultly generated name.
82 The body consists of the enum values in the form (name &key index)."
83 (with-collectors ((vals collect-val)
88 (let* ((idx (if (listp val) (second val) (incf index)))
89 (name (if (listp val) (first val) val))
90 (val-name (kintern (if conc-name (format nil "~A~A" conc-name name) (symbol-name name))))
91 (enum-name (if conc-name (format nil "~A~A" conc-name name) (symbol-name name))))
92 (collect-val val-name)
93 (collect-eval `(make-instance 'protobuf-enum-value
94 :name ,(enum-name->proto enum-name)
97 (collect-form `(deftype ,name () '(member ,@vals)))
98 (let ((options (loop for (key val) on options by #'cddr
99 collect `(make-instance 'protobuf-option
104 (make-instance 'protobuf-enum
105 :name ,(or proto-name (class-name->proto name))
107 :options (list ,@options)
108 :values (list ,@evals)
109 :documentation ,documentation)
112 ;; Define a message named 'name' and a Lisp 'defclass'
113 (defmacro define-message (name (&key proto-name conc-name options documentation)
114 &body fields &environment env)
115 "Define a message named 'name' and a Lisp 'defclass'.
116 'proto-name' can be used to override the defaultly generated name.
117 The body consists of fields, or 'define-enum' or 'define-message' forms.
118 Fields take the form (name &key type default index)."
119 (with-collectors ((enums collect-enum)
123 (forms collect-form))
127 ((define-enum define-message define-extension)
128 (destructuring-bind (&optional progn type model definers)
129 (macroexpand-1 fld env)
130 (assert (eq progn 'progn) ()
131 "The macroexpansion for ~S failed" fld)
132 (map () #'collect-form definers)
135 (collect-enum model))
139 (collect-msg model)))))
141 (destructuring-bind (slot &key type default) fld
142 (let* ((idx (if (listp slot) (second slot) (incf index)))
143 (slot (if (listp slot) (first slot) slot))
144 (reqd (clos-type-to-protobuf-required type))
145 (accessor (intern (if conc-name (format nil "~A~A" conc-name slot) (symbol-name slot))
146 (symbol-package slot))))
147 (multiple-value-bind (ptype pclass)
148 (clos-type-to-protobuf-type type)
149 (collect-slot `(,slot :type ,type
151 :initarg ,(kintern (symbol-name slot))
152 ,@(and default (list :initform default))))
153 (collect-field `(make-instance 'protobuf-field
154 :name ,(slot-name->proto slot)
160 :default ,(and default (format nil "~A" default))
161 :packed ,(and (eq reqd :repeated)
162 (packed-type-p pclass)))))))))))
163 (collect-form `(defclass ,name () (,@slots)))
164 (let ((options (loop for (key val) on options by #'cddr
165 collect `(make-instance 'protobuf-option
170 (make-instance 'protobuf-message
171 :name ,(or proto-name (class-name->proto name))
173 :conc-name ,(and conc-name (string conc-name))
174 :options (list ,@options)
175 :enums (list ,@enums)
176 :messages (list ,@msgs)
177 :fields (list ,@flds)
178 :documentation ,documentation)
181 (defmacro define-extension (from to)
182 "Define an extension range within a message.
183 The \"body\" is the start and end of the range, both inclusive."
186 (make-instance 'protobuf-extension
191 ;; Define a service named 'name' and a Lisp 'defun'
192 (defmacro define-service (name (&key proto-name options documentation) &body rpc-specs)
193 "Define a service named 'name' and a Lisp 'defun'.
194 'proto-name' can be used to override the defaultly generated name.
195 The body consists of a set of RPC specs of the form (name input-type output-type)."
196 (with-collectors ((rpcs collect-rpc))
197 (dolist (rpc rpc-specs)
198 (destructuring-bind (name input-type output-type &key options) rpc
199 (let ((options (loop for (key val) on options by #'cddr
200 collect `(make-instance 'protobuf-option
203 (collect-rpc `(make-instance 'protobuf-rpc
204 :name ,(class-name->proto name)
206 :input-type ,(and input-type (class-name->proto input-type))
207 :output-type ,(and output-type (class-name->proto output-type))
208 :options (list ,@options))))))
209 (let ((options (loop for (key val) on options by #'cddr
210 collect `(make-instance 'protobuf-option
215 (make-instance 'protobuf-service
216 :name ,(or proto-name (class-name->proto name))
218 :options (list ,@options)
220 :documentation ,documentation)
221 ())))) ;---*** define Lisp stub here