From 84305f74238f62484fa0157eaeb5cba4dda3ce5c Mon Sep 17 00:00:00 2001 From: Teon Banek <teon.banek@memgraph.io> Date: Mon, 22 Oct 2018 10:00:49 +0200 Subject: [PATCH] Support looking for nested enums in LCP Reviewers: mtomic, llugovic Reviewed By: mtomic Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1686 --- src/lisp/lcp-test.lisp | 39 +++++++++++++++- src/lisp/lcp.lisp | 100 +++++++++++++++++++++++++++-------------- 2 files changed, 103 insertions(+), 36 deletions(-) diff --git a/src/lisp/lcp-test.lisp b/src/lisp/lcp-test.lisp index d798ab9f1..20e7beef2 100644 --- a/src/lisp/lcp-test.lisp +++ b/src/lisp/lcp-test.lisp @@ -69,7 +69,12 @@ :namespace '("std") :type-args '("void(int, bool)")) :double)) - :char)))) + :char))) + + (parse-test "::my_namespace::EnclosingClass::Thing" + (lcp::make-cpp-type "Thing" + :namespace '("" "my_namespace") + :enclosing-class "EnclosingClass"))) (subtest "printing" (decl-test "pair<T1, T2>" @@ -90,7 +95,37 @@ (decl-test "pair" (lcp::make-cpp-type "pair" :type-params '("TIntegral1 TIntegral2")) - :type-params nil))) + :type-params nil)) + + (subtest "finding defined enums" + (let ((lcp::*cpp-classes* nil) + (lcp::*cpp-enums* nil)) + (lcp:define-enum action (val1 val2)) + (lcp:define-class enclosing-class () + () + (:public + (lcp:define-enum action (other-val1 other-val2)) + (lcp:define-class another-enclosing () + () + (:public + (lcp:define-enum action (deep-val1 deep-val2)))))) + (lcp:namespace my-namespace) + (lcp:define-enum action (third-val1 third-val2)) + (lcp:pop-namespace) + + (decl-test "Action" (lcp::find-cpp-enum "::Action")) + (decl-test "EnclosingClass::Action" (lcp::find-cpp-enum "EnclosingClass::Action")) + (decl-test "EnclosingClass::Action" (lcp::find-cpp-enum "::EnclosingClass::Action")) + (decl-test "EnclosingClass::AnotherEnclosing::Action" + (lcp::find-cpp-enum "EnclosingClass::AnotherEnclosing::Action")) + (decl-test "my_namespace::Action" (lcp::find-cpp-enum "my_namespace::Action")) + (decl-test "my_namespace::Action" (lcp::find-cpp-enum "::my_namespace::Action")) + (ok (lcp::find-cpp-enum "Action")) + (ok (lcp::find-cpp-enum 'action)) + (ok (not (lcp::find-cpp-enum "NonExistent"))) + (ok (not (lcp::find-cpp-enum ""))) + (ok (not (lcp::find-cpp-enum "my_namespace::NonExistent"))) + (ok (not (lcp::find-cpp-enum "::NonExistent")))))) (deftest "unsupported" (subtest "cv-qualifiers" diff --git a/src/lisp/lcp.lisp b/src/lisp/lcp.lisp index 2bd4cf459..17a5279dc 100644 --- a/src/lisp/lcp.lisp +++ b/src/lisp/lcp.lisp @@ -75,7 +75,7 @@ :documentation "A list of symbols or strings defining the full namespace. A single symbol may refer to a `CPP-CLASS' which encloses this type.") - (enclosing-class :type (or null symbol) :initarg :enclosing-class + (enclosing-class :type (or null symbol string) :initarg :enclosing-class :initform nil :accessor cpp-type-enclosing-class :documentation "A symbol that is a designator for the type of the enclosing class of this type, or NIL if the type has @@ -97,12 +97,20 @@ (defun make-cpp-type (name &key namespace enclosing-class type-params type-args) "Create an instance of CPP-TYPE given the arguments." - (make-instance 'cpp-type - :name name - :namespace namespace - :enclosing-class enclosing-class - :type-params type-params - :type-args (mapcar #'cpp-type type-args))) + (let ((namespace (if (and namespace + (string= (string-trim +whitespace-chars+ (car namespace)) "")) + (cdr namespace) + namespace))) + (loop for ns in namespace + when (or (find-if (lambda (c) (member c +whitespace-chars+ :test #'char=)) ns) + (string= ns "")) + do (error "Invalid namespace name ~S in ~S" ns namespace)) + (make-instance 'cpp-type + :name name + :namespace namespace + :enclosing-class enclosing-class + :type-params type-params + :type-args (mapcar #'cpp-type type-args)))) (defun cpp-type= (a b) (let ((a (cpp-type a)) @@ -113,8 +121,9 @@ (equalp (cpp-type-name a) (cpp-type-name b)) (and (= (length args1) (length args2)) (every #'cpp-type= args1 args2)) - (eq (cpp-type-enclosing-class a) - (cpp-type-enclosing-class b))))))) + (string= + (cpp-type-name (cpp-type-enclosing-class a)) + (cpp-type-name (cpp-type-enclosing-class b)))))))) (defmethod print-object ((cpp-type cpp-type) stream) (print-unreadable-object (cpp-type stream :type t) @@ -208,8 +217,7 @@ produces: (let ((ptr-pos (position #\* type-decl :from-end t))) (when (and ptr-pos (not (cl-ppcre:scan "[()<>]" type-decl :start ptr-pos))) (return-from parse-cpp-type-declaration - (make-instance 'cpp-type - :name (subseq type-decl ptr-pos) + (make-cpp-type (subseq type-decl ptr-pos) :type-args (list (parse-cpp-type-declaration (subseq type-decl 0 ptr-pos))))))) ;; Other cases @@ -235,11 +243,24 @@ produces: (subseq template arg-start (1- match-end))) type-args) (setf arg-start (1+ match-end))))))) - (make-instance 'cpp-type - :ns (when (cdr namespace-split) - (butlast namespace-split)) - :name name - :type-args (reverse type-args))))) + (let (namespace enclosing-class namespace-done-p) + (when (cdr namespace-split) + (dolist (ns (butlast namespace-split)) + ;; Treat capitalized namespace as designating an enclosing class. + ;; Only the final enclosing class is taken, because we assume that + ;; we can get enclosing classes recursively via `FIND-CPP-CLASS'. + ;; This won't work if the classes are not defined in LCP. + (cond + ((and (string/= "" ns) (upper-case-p (aref ns 0))) + (setf namespace-done-p t) + (setf enclosing-class ns)) + ((not namespace-done-p) + (push ns namespace)))) + (setf namespace (reverse namespace))) + (make-cpp-type name + :namespace namespace + :enclosing-class enclosing-class + :type-args (reverse type-args)))))) (defun cpp-type-decl (cpp-type &key (type-params t) (namespace t)) "Return the fully qualified name of given CPP-TYPE." @@ -335,20 +356,20 @@ produces: ;; TODO: use CPP-TYPE, CPP-TYPE= and CPP-PRIMITIVE-TYPE-P in the rest of the ;; code (defun cpp-type (type-designator) - "Coerce the CPP-TYPE designator TYPE-DESIGNATOR into a CPP-TYPE instace. + "Coerce the CPP-TYPE designator TYPE-DESIGNATOR into a CPP-TYPE instance. -- if TYPE-DESIGNATOR is an instance of CPP-TYPE, CPP-PRIMITIVE-TYPE or - CPP-CLASS, just return it +- If TYPE-DESIGNATOR is an instance of CPP-TYPE, CPP-PRIMITIVE-TYPE or + CPP-CLASS, just return it. -- if TYPE-DESIGNATOR is one of the keywords in +CPP-PRIMITIVE-TYPE-KEYOWRDS+, +- If TYPE-DESIGNATOR is one of the keywords in +CPP-PRIMITIVE-TYPE-KEYWORDS+, return an instance of CPP-PRIMITIVE-TYPE with the name being the result - of (string-downcase type-designator) + of (string-downcase type-designator). -- if TYPE-DESIGNATOR is any other symbol, return an instance of CPP-TYPE with - the name being the result of (remove #\- (string-capitalize type-designator)) +- If TYPE-DESIGNATOR is any other symbol, return an instance of CPP-TYPE with + the name being the result of (remove #\- (string-capitalize type-designator)). -- if TYPE-DESIGNATOR is a string, return an instance of CPP-TYPE with the name - being that string" +- If TYPE-DESIGNATOR is a string, return an instance of CPP-TYPE with the name + being that string." (etypecase type-designator ((or cpp-type cpp-primitive-type cpp-class) type-designator) @@ -377,7 +398,8 @@ produces: "Find CPP-ENUM in *CPP-ENUMS* by CPP-ENUM-NAME" (declare (type (or symbol string) cpp-enum-name)) (if (stringp cpp-enum-name) - (find cpp-enum-name *cpp-enums* :key #'cpp-type-name :test #'string=) + (or (find (parse-cpp-type-declaration cpp-enum-name) *cpp-enums* :test #'cpp-type=) + (find cpp-enum-name *cpp-enums* :key #'cpp-type-name :test #'string=)) (find cpp-enum-name *cpp-enums* :key #'cpp-type-base-name))) (defun direct-subclasses-of (cpp-class) @@ -953,19 +975,23 @@ encoded as union inheritance in Cap'n Proto." namespace '("capnp"))) (t ;; Just append capnp as final namespace (setf namespace (append namespace '("capnp"))))) - (make-instance 'cpp-type - :name name :namespace namespace + (make-cpp-type name :namespace namespace :enclosing-class (cpp-type-enclosing-class cpp-type)))) -(defun capnp-save-default (member-name member-type member-builder capnp-name) +(defun capnp-save-default (member-name member-type member-builder capnp-name &key cpp-class) "Generate the default call to save for member. MEMBER-NAME and MEMBER-TYPE are strings describing the member being serialized. MEMBER-BUILDER is the name of the builder variable. CAPNP-NAME is the name of the member in Cap'n Proto schema." (declare (type string member-name member-type member-builder capnp-name)) + (declare (type cpp-class cpp-class)) (let* ((type (parse-cpp-type-declaration member-type)) (type-name (cpp-type-base-name type)) - (cpp-enum (find-cpp-enum member-type))) + (cpp-enum (or + ;; Look for potentially nested enum first + (find-cpp-enum + (concatenate 'string (cpp-type-decl cpp-class) "::" member-type)) + (find-cpp-enum member-type)))) (cond (cpp-enum (funcall @@ -1041,7 +1067,8 @@ Proto schema." (cpp-code (funcall (cpp-member-capnp-save member) member-builder member-access capnp-name))) (write-line (capnp-save-default member-access (cpp-member-type member) - member-builder capnp-name) + member-builder capnp-name + :cpp-class cpp-class) s)))))))))) (defun capnp-save-function-code (cpp-class) @@ -1191,14 +1218,18 @@ Proto schema." :returns "void" :type-params (cpp-type-type-params cpp-class)))) -(defun capnp-load-default (member-name member-type member-reader capnp-name) +(defun capnp-load-default (member-name member-type member-reader capnp-name &key cpp-class) "Generate default load call for member. MEMBER-NAME and MEMBER-TYPE are strings describing the member being loaded. MEMBER-READER is the name of the reader variable. CAPNP-NAME is the name of the member in Cap'n Proto schema." (declare (type string member-name member-type member-reader)) (let* ((type (parse-cpp-type-declaration member-type)) (type-name (cpp-type-base-name type)) - (cpp-enum (find-cpp-enum member-type))) + (cpp-enum (or + ;; Look for potentially nested enum first + (find-cpp-enum + (concatenate 'string (cpp-type-decl cpp-class) "::" member-type)) + (find-cpp-enum member-type)))) (cond (cpp-enum (funcall @@ -1268,7 +1299,8 @@ example, INSTANCE-ACCESS could be `my_struct->`" member-reader member-access capnp-name))) (write-line (capnp-load-default member-access (cpp-member-type member) - member-reader capnp-name) s)))))))))) + member-reader capnp-name :cpp-class cpp-class) + s)))))))))) (defun capnp-load-function-code (cpp-class) "Generate Cap'n Proto load code for CPP-CLASS."