THRIFT-82: Add Common Lisp support
Client: cl

There's framed and buffered socket transport, binary protocol, multiplex, simple
server, cross-tests, self-tests, tutorial, CL library, CL code generator. Only
SBCL is supported for now.

This closes #1412
diff --git a/tutorial/Makefile.am b/tutorial/Makefile.am
index d8ad09c..0499460 100755
--- a/tutorial/Makefile.am
+++ b/tutorial/Makefile.am
@@ -78,6 +78,10 @@
 SUBDIRS += rs
 endif
 
+if WITH_CL
+SUBDIRS += cl
+endif
+
 #
 # generate html for ThriftTest.thrift
 #
diff --git a/tutorial/cl/Makefile.am b/tutorial/cl/Makefile.am
new file mode 100755
index 0000000..fb6e83a
--- /dev/null
+++ b/tutorial/cl/Makefile.am
@@ -0,0 +1,47 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+gen-cl: $(top_srcdir)/tutorial/tutorial.thrift
+	$(THRIFT) --gen cl -r $<
+
+TutorialServer: make-tutorial-server.lisp
+	$(SBCL) --script make-tutorial-server.lisp
+
+TutorialClient: make-tutorial-client.lisp
+	$(SBCL) --script make-tutorial-client.lisp
+
+all-local: gen-cl TutorialClient TutorialServer
+
+tutorialserver: all
+	./TutorialServer
+
+tutorialclient: all
+	./TutorialClient
+
+clean-local:
+	$(RM) -r gen-*
+	$(RM) TutorialServer
+	$(RM) TutorialClient
+
+EXTRA_DIST = \
+	tutorial-implementation.lisp \
+	shared-implementation.lisp \
+	thrift-tutorial.asd \
+	make-tutorial-server.lisp \
+	make-tutorial-client.lisp
diff --git a/tutorial/cl/make-tutorial-client.lisp b/tutorial/cl/make-tutorial-client.lisp
new file mode 100644
index 0000000..59450a2
--- /dev/null
+++ b/tutorial/cl/make-tutorial-client.lisp
@@ -0,0 +1,51 @@
+(in-package #:cl-user)
+
+;;;; Licensed under the Apache License, Version 2.0 (the "License");
+;;;; you may not use this file except in compliance with the License.
+;;;; You may obtain a copy of the License at
+;;;;
+;;;;     http://www.apache.org/licenses/LICENSE-2.0
+;;;;
+;;;; Unless required by applicable law or agreed to in writing, software
+;;;; distributed under the License is distributed on an "AS IS" BASIS,
+;;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+;;;; See the License for the specific language governing permissions and
+;;;; limitations under the License.
+
+(require "asdf")
+(load (merge-pathnames "../../lib/cl/load-locally.lisp" *load-truename*))
+(asdf:load-system :net.didierverna.clon)
+(asdf:load-asd (merge-pathnames "gen-cl/shared/thrift-gen-shared.asd" *load-truename*))
+(asdf:load-asd (merge-pathnames "gen-cl/tutorial/thrift-gen-tutorial.asd" *load-truename*))
+(asdf:load-asd (merge-pathnames "thrift-tutorial.asd" *load-truename*))
+(asdf:load-system :thrift-tutorial)
+
+(net.didierverna.clon:nickname-package)
+
+(defun main ()
+  "Entry point for the binary."
+  (thrift:with-client (prot #u"thrift://127.0.0.1:9090")
+    (tutorial.calculator:ping prot)
+    (format t "ping()~%")
+    (format t "1 + 1 = ~a~%" (tutorial.calculator:add prot 1 1))
+    (let ((work-instance (tutorial:make-work :num1 5
+                                             :num2 0
+                                             :op tutorial:operation.divide
+                                             :comment "Booya!")))
+      (handler-case (format t
+                            "5 / 0 = ~a - Oh, really? An exception should have been thrown here.~%"
+                            (tutorial.calculator:calculate prot 1 work-instance))
+        (tutorial:invalidoperation (e)
+          (format t "---~%(Expected) Invalid Operation caught: ~%~a~%---~%" e))))
+    (let ((work-instance (tutorial:make-work :num1 15
+                                             :num2 10
+                                             :op tutorial:operation.subtract
+                                             :comment "Playing nice this time.")))
+      (handler-case (format t
+                            "15 - 10 = ~a~%"
+                            (tutorial.calculator:calculate prot 1 work-instance))
+        (tutorial:invalidoperation (e)
+          (format t "---~%(Unexpected) Invalid Operation caught: ~%~a~%---~%" e))))
+    (format t "Check log: ~a~%" (shared.shared-service:get-struct prot 1))))
+
+(clon:dump "TutorialClient" main)
diff --git a/tutorial/cl/make-tutorial-server.lisp b/tutorial/cl/make-tutorial-server.lisp
new file mode 100644
index 0000000..5621ff3
--- /dev/null
+++ b/tutorial/cl/make-tutorial-server.lisp
@@ -0,0 +1,29 @@
+(in-package #:cl-user)
+
+;;;; Licensed under the Apache License, Version 2.0 (the "License");
+;;;; you may not use this file except in compliance with the License.
+;;;; You may obtain a copy of the License at
+;;;;
+;;;;     http://www.apache.org/licenses/LICENSE-2.0
+;;;;
+;;;; Unless required by applicable law or agreed to in writing, software
+;;;; distributed under the License is distributed on an "AS IS" BASIS,
+;;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+;;;; See the License for the specific language governing permissions and
+;;;; limitations under the License.
+
+(require "asdf")
+(load (merge-pathnames "../../lib/cl/load-locally.lisp" *load-truename*))
+(asdf:load-system :net.didierverna.clon)
+(asdf:load-asd (merge-pathnames "gen-cl/shared/thrift-gen-shared.asd" *load-truename*))
+(asdf:load-asd (merge-pathnames "gen-cl/tutorial/thrift-gen-tutorial.asd" *load-truename*))
+(asdf:load-asd (merge-pathnames "thrift-tutorial.asd" *load-truename*))
+(asdf:load-system :thrift-tutorial)
+
+(net.didierverna.clon:nickname-package)
+
+(defun main ()
+  "Entry point for the binary."
+  (thrift:serve #u"thrift://127.0.0.1:9090" tutorial:calculator))
+
+(clon:dump "TutorialServer" main)
diff --git a/tutorial/cl/shared-implementation.lisp b/tutorial/cl/shared-implementation.lisp
new file mode 100644
index 0000000..c197626
--- /dev/null
+++ b/tutorial/cl/shared-implementation.lisp
@@ -0,0 +1,25 @@
+(in-package #:shared-implementation)
+
+;;;; Licensed under the Apache License, Version 2.0 (the "License");
+;;;; you may not use this file except in compliance with the License.
+;;;; You may obtain a copy of the License at
+;;;;
+;;;;     http://www.apache.org/licenses/LICENSE-2.0
+;;;;
+;;;; Unless required by applicable law or agreed to in writing, software
+;;;; distributed under the License is distributed on an "AS IS" BASIS,
+;;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+;;;; See the License for the specific language governing permissions and
+;;;; limitations under the License.
+
+(defvar *structs* (make-hash-table))
+
+(defun shared.shared-service-implementation:get-struct (key)
+  (format t "getStruct(~a)~%" key)
+  (gethash key *structs*))
+
+(defun add-log (key value)
+  (setf (gethash key *structs*)
+        (make-instance 'shared:sharedstruct
+                       :key key
+                       :value (write-to-string value))))
diff --git a/tutorial/cl/thrift-tutorial.asd b/tutorial/cl/thrift-tutorial.asd
new file mode 100644
index 0000000..8a03537
--- /dev/null
+++ b/tutorial/cl/thrift-tutorial.asd
@@ -0,0 +1,17 @@
+;;;; Licensed under the Apache License, Version 2.0 (the "License");
+;;;; you may not use this file except in compliance with the License.
+;;;; You may obtain a copy of the License at
+;;;;
+;;;;     http://www.apache.org/licenses/LICENSE-2.0
+;;;;
+;;;; Unless required by applicable law or agreed to in writing, software
+;;;; distributed under the License is distributed on an "AS IS" BASIS,
+;;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+;;;; See the License for the specific language governing permissions and
+;;;; limitations under the License.
+
+(asdf:defsystem #:thrift-tutorial
+  :depends-on (#:thrift-gen-tutorial)
+  :serial t
+  :components ((:file "shared-implementation")
+               (:file "tutorial-implementation")))
diff --git a/tutorial/cl/tutorial-implementation.lisp b/tutorial/cl/tutorial-implementation.lisp
new file mode 100644
index 0000000..5c92fe4
--- /dev/null
+++ b/tutorial/cl/tutorial-implementation.lisp
@@ -0,0 +1,41 @@
+(in-package #:tutorial-implementation)
+
+;;;; Licensed under the Apache License, Version 2.0 (the "License");
+;;;; you may not use this file except in compliance with the License.
+;;;; You may obtain a copy of the License at
+;;;;
+;;;;     http://www.apache.org/licenses/LICENSE-2.0
+;;;;
+;;;; Unless required by applicable law or agreed to in writing, software
+;;;; distributed under the License is distributed on an "AS IS" BASIS,
+;;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+;;;; See the License for the specific language governing permissions and
+;;;; limitations under the License.
+
+(defun tutorial.calculator-implementation:ping ()
+  (format t "ping()~%"))
+
+(defun tutorial.calculator-implementation:add (num1 num2)
+  (format t "add(~a, ~a)~%" num1 num2)
+  (+ num1 num2))
+
+(defun tutorial.calculator-implementation:calculate (logid work)
+  (format t "calculate(~a, ~a)~%" logid work)
+  (handler-case
+      (let* ((num1 (tutorial:work-num1 work))
+             (num2 (tutorial:work-num2 work))
+             (op (tutorial:work-op work))
+             (result
+              (cond
+                ((= op tutorial:operation.add) (+ num1 num2))
+                ((= op tutorial:operation.subtract) (- num1 num2))
+                ((= op tutorial:operation.multiply) (* num1 num2))
+                ((= op tutorial:operation.divide) (/ num1 num2)))))
+        (shared-implementation::add-log logid result)
+        result)
+    (division-by-zero () (error 'tutorial:invalidoperation
+                                :why "Division by zero."
+                                :what-op (tutorial:work-op work)))))
+
+(defun tutorial.calculator-implementation:zip ()
+  (format t "zip()~%"))
diff --git a/tutorial/shared.thrift b/tutorial/shared.thrift
index 3cc1bb3..f1685bd 100644
--- a/tutorial/shared.thrift
+++ b/tutorial/shared.thrift
@@ -22,6 +22,7 @@
  * these definitions.
  */
 
+namespace cl shared
 namespace cpp shared
 namespace d share // "shared" would collide with the eponymous D keyword.
 namespace dart shared
diff --git a/tutorial/tutorial.thrift b/tutorial/tutorial.thrift
index f8c5320..e027546 100644
--- a/tutorial/tutorial.thrift
+++ b/tutorial/tutorial.thrift
@@ -62,6 +62,8 @@
  * Thrift files can namespace, package, or prefix their output in various
  * target languages.
  */
+
+namespace cl tutorial
 namespace cpp tutorial
 namespace d tutorial
 namespace dart tutorial