LCP: Minor improvements

Summary:
- Add a unified `generate_lcp` target
- Simplify SLK-ERROR and CLONE-ERROR
- Rework WITH-VARS
- Expect CPP-CLASS, not CPP-TYPE
- Fix CPP-TYPE-REFERENCE-P
- Add CPP-GENSYM
- Add util.lisp to LCP's source files in CMake
- Make the CMake variable `lcp_src_files` a cache (persistent) variable
- Add `lcp_src_files` as a dependency to `test_lcp`
- Rename `lcp_compile` to `compile-lcp`
- Fetch docstrings for CPP-NAME-* functions at run-time
- Remove existing C++ entities on redefinition

Reviewers: mtomic, teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2094
This commit is contained in:
Lovro Lugovic 2019-05-24 14:16:42 +02:00
parent 0732264fd5
commit dbd226d05d
10 changed files with 185 additions and 108 deletions

View File

@ -321,6 +321,9 @@ target_compile_definitions(mg-single-node-ha PUBLIC MG_SINGLE_NODE_HA)
# END Memgraph Single Node High Availability
# ----------------------------------------------------------------------------
add_custom_target(generate_lcp)
add_dependencies(generate_lcp generate_lcp_single_node generate_lcp_single_node_ha generate_lcp_distributed)
string(TOLOWER ${CMAKE_BUILD_TYPE} lower_build_type)
# STATIC library used to store key-value pairs

View File

@ -3,7 +3,7 @@
# Don't forget to repeat this list below in `define_add_lcp`.
set(lcp_src_files
${CMAKE_SOURCE_DIR}/src/lisp/lcp.asd
${CMAKE_SOURCE_DIR}/src/lisp/lcp-compile
${CMAKE_SOURCE_DIR}/src/lisp/compile-lcp
${CMAKE_SOURCE_DIR}/src/lisp/package.lisp
${CMAKE_SOURCE_DIR}/src/lisp/names.lisp
${CMAKE_SOURCE_DIR}/src/lisp/types.lisp
@ -13,12 +13,17 @@ set(lcp_src_files
${CMAKE_SOURCE_DIR}/src/lisp/lcp.lisp
${CMAKE_SOURCE_DIR}/src/lisp/debug.lisp
${CMAKE_SOURCE_DIR}/src/lisp/test.lisp
${CMAKE_SOURCE_DIR}/src/lisp/util.lisp
${CMAKE_SOURCE_DIR}/tools/lcp)
# Make `lcp_src_files` a persistent (cache) variable so that
# tests/unit/CMakeLists.txt can see it.
set(lcp_src_files "${lcp_src_files}" CACHE INTERNAL "")
add_custom_target(lcp
DEPENDS ${lcp_src_files}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/lcp-compile)
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/compile-lcp)
# Define `add_lcp` function named `name` for registering a lcp file for generation.
#
@ -53,7 +58,7 @@ macro(define_add_lcp name main_src_files generated_lcp_files)
# not visible when invoked in another file.
set(lcp_src_files
${CMAKE_SOURCE_DIR}/src/lisp/lcp.asd
${CMAKE_SOURCE_DIR}/src/lisp/lcp-compile
${CMAKE_SOURCE_DIR}/src/lisp/compile-lcp
${CMAKE_SOURCE_DIR}/src/lisp/package.lisp
${CMAKE_SOURCE_DIR}/src/lisp/names.lisp
${CMAKE_SOURCE_DIR}/src/lisp/types.lisp
@ -63,6 +68,7 @@ macro(define_add_lcp name main_src_files generated_lcp_files)
${CMAKE_SOURCE_DIR}/src/lisp/lcp.lisp
${CMAKE_SOURCE_DIR}/src/lisp/debug.lisp
${CMAKE_SOURCE_DIR}/src/lisp/test.lisp
${CMAKE_SOURCE_DIR}/src/lisp/util.lisp
${CMAKE_SOURCE_DIR}/tools/lcp)
add_custom_command(OUTPUT ${h_file} ${cpp_file}
COMMAND ${CMAKE_SOURCE_DIR}/tools/lcp ${lcp_file} ${slk_serialize}

View File

@ -1,18 +1,14 @@
(in-package #:lcp.clone)
(define-condition clone-error (error)
((message :type string :initarg :message :reader clone-error-message)
(format-args :type list :initform nil :initarg :format-args :reader clone-error-format-args))
(:report (lambda (condition stream)
(apply #'format stream
(clone-error-message condition)
(clone-error-format-args condition)))))
(define-condition clone-error (simple-error)
())
(defun clone-error (message &rest format-args)
(error 'clone-error :message message :format-args format-args))
(defun clone-error (format-control &rest format-arguments)
(error 'clone-error :format-control format-control
:format-arguments format-arguments))
(defun cloning-parent (cpp-class)
(check-type cpp-class lcp::cpp-type)
(check-type cpp-class lcp::cpp-class)
(let ((supers (lcp::cpp-class-super-classes cpp-class))
(opts (lcp::cpp-class-clone-opts cpp-class)))
(unless opts
@ -29,14 +25,14 @@
(car supers)))))
(defun cloning-root (cpp-class)
(check-type cpp-class lcp::cpp-type)
(check-type cpp-class lcp::cpp-class)
(let ((parent-class (cloning-parent cpp-class)))
(if parent-class
(cloning-root parent-class)
cpp-class)))
(defun members-for-cloning (cpp-class)
(check-type cpp-class lcp::cpp-type)
(check-type cpp-class lcp::cpp-class)
(alexandria:flatten
(reverse
(loop :for current := cpp-class :then (cloning-parent current)
@ -117,7 +113,7 @@
(lcp::cpp-type-decl object-type))))))
(defun clone-vector (elem-type source-name dest-name &key args)
(lcp::with-vars ((loop-counter "i"))
(lcp::with-cpp-gensyms ((loop-counter "i"))
(format nil
"~A.resize(~A.size());
for (auto ~A = 0; ~A < ~A.size(); ++~A) { ~A }"
@ -129,7 +125,8 @@
:args args))))
(defun clone-map (key-type value-type source-name dest-name &key args)
(lcp::with-vars ((loop-var "kv") (entry-var "entry"))
(lcp::with-cpp-gensyms ((loop-var "kv")
(entry-var "entry"))
(let ((entry-type (lcp::make-cpp-type
"pair"
:namespace '("std")
@ -146,7 +143,7 @@
dest-name entry-var))))
(defun clone-optional (value-type source-name dest-name &key args)
(lcp::with-vars ((value-var "value"))
(lcp::with-cpp-gensyms ((value-var "value"))
(format nil
"if (~A) {
~A ~A;
@ -165,7 +162,8 @@
dest-name)))
(defun clone-pair (first-type second-type source-name dest-name &key args)
(lcp::with-vars ((first-var "first") (second-var "second"))
(lcp::with-cpp-gensyms ((first-var "first")
(second-var "second"))
(with-output-to-string (cpp-out)
(lcp::with-cpp-block-output (cpp-out)
(format cpp-out

View File

@ -65,23 +65,72 @@ context which binds OPEN-NAMESPACE-FUN function for opening namespaces."
(cl-ppcre:regex-replace-all
(string #\Newline) documentation (format nil "~%/// "))))
(defvar *variable-idx* 0 "Used to generate unique variable names")
(defvar *cpp-gensym-counter* 0 "Used to generate unique variable names")
(defmacro with-vars (vars &body body)
"Generates unique variable names for use in generated code by
appending an index to desired variable names. Useful when generating
loops which might reuse counter names.
(defun cpp-gensym (&optional (prefix "var"))
"Generate a unique C++ name.
Usage example:
(with-vars ((loop-counter \"i\"))
The name is constructed by concatenating the string PREFIX with the current
value of *CPP-GENSYM-COUNTER*. Afterwards, the value of *CPP-GENSYM-COUNTER* is
incremented by 1.
Despite the suggestive name \"gensym\", this function cannot guarantee that the
name is globally unique (because C++ has no concept equivalent to uninterned
symbols). The name is only unique across all of the names generated by the
function."
(prog1 (format nil "~A~A" prefix *cpp-gensym-counter*)
(incf *cpp-gensym-counter*)))
(defmacro with-cpp-gensyms (vars &body body)
"Evaluate and return the result of the implicit progn BODY with the variables
specified within VARS bound to strings representing unique C++ names.
Each element of VARS is either a symbol SYMBOL or a pair (SYMBOL NAME). Bare
symbols are equivalent to the pair (SYMBOL SYMBOL-NAME) where SYMBOL-NAME is the
result of (cpp-name-for-variable SYMBOL).
Each pair (SYMBOL NAME) specifies a single unique C++ name. SYMBOL should be a
symbol naming the variable to which the generated C++ name will bound. NAME
should be a prefix that will be used to construct the name using CPP-GENSYM.
Example:
(with-cpp-gensyms ((loop-counter \"i\"))
(format nil \"for (auto ~A = 0; ~A < v.size(); ++~A) {
// do something
}\"
loop-counter loop-counter loop-counter))"
`(let* ((*variable-idx* (1+ *variable-idx*))
,@(loop :for var :in vars :collecting
`(,(first var)
(format nil "~A~A" ,(second var) *variable-idx*))))
loop-counter loop-counter loop-counter))
;;; >>
;;; for (auto i0 = 0; i0 < v.size(); ++i0) {
;;; // do something
;;; }
Example:
Assume *CPP-GENSYM-COUNTER* is 0.
(defun gen1 ()
(with-cpp-gensyms ((hello \"hello\"))
(format t \"int ~a;~%\" hello)))
(defun gen2 ()
(with-cpp-gensyms ((hello \"hello\"))
(gen1)
(format t \"int ~a;~%\" hello)))
(gen2)
;;; >>
;;; int hello1;
;;; int hello0;"
`(let* (,@(mapcar
(lambda (var)
(destructuring-bind (sym &optional name)
(alexandria:ensure-list var)
(let ((name (or name (cpp-name-for-variable sym))))
`(,sym (cpp-gensym ,name)))))
vars))
,@body))
(defun cpp-member-reader-name (cpp-member)

View File

@ -9,4 +9,5 @@ echo \
"
(load \"${quicklisp_install_dir}/setup.lisp\")
(ql:quickload :lcp :silent t)
(ql:quickload :lcp/test :silent t)
" | sbcl --script

View File

@ -154,19 +154,20 @@ The name function's name is of the form CPP-<CPP-OBJECT>-NAME.
The namestring function's name is of the form
ENSURE-NAMESTRING-FOR-<CPP-OBJECT>."
(let ((cpp-name-for (alexandria:symbolicate 'cpp-name-for- cpp-object)))
`(progn
(defun ,(alexandria:symbolicate 'cpp-name-for- cpp-object)
(thing &key from-style)
,(documentation name-op 'function)
(defun ,cpp-name-for (thing &key from-style)
(check-type thing (or symbol string))
(,name-op thing :from-style from-style))
(setf (documentation ',cpp-name-for 'function)
(documentation ',name-op 'function))
(defun ,(alexandria:symbolicate 'ensure-namestring-for- cpp-object) (thing)
,(format nil +cpp-namestring-docstring+
(string-downcase cpp-object)
(string-downcase cpp-object)
name-op)
(check-type thing (or symbol string))
(ensure-namestring-for thing #',name-op))))
(ensure-namestring-for thing #',name-op)))))
(define-cpp-name namespace lower-snake-case-name)
(define-cpp-name class pascal-case-name)

View File

@ -4,16 +4,12 @@
(in-package #:lcp.slk)
(define-condition slk-error (error)
((message :type string :initarg :message :reader slk-error-message)
(format-args :type list :initform nil :initarg :format-args :reader slk-error-format-args))
(:report (lambda (condition stream)
(apply #'format stream
(slk-error-message condition)
(slk-error-format-args condition)))))
(define-condition slk-error (simple-error)
())
(defun slk-error (message &rest format-args)
(error 'slk-error :message message :format-args format-args))
(defun slk-error (format-control &rest format-arguments)
(error 'slk-error :format-control format-control
:format-arguments format-arguments))
;;; CPP-CLASS serialization generation

View File

@ -182,8 +182,10 @@ CPP-TYPE-DECL."
'(#\Newline)
(uiop:run-program "clang-format -style=file" :input s :output '(:string :stripped t)))))
(defun is-generated (got expected)
(is (clang-format got) (clang-format expected) :test #'string=))
(defmacro is-generated (got expected)
`(is (let ((lcp::*cpp-gensym-counter* 0))
(clang-format ,got))
(clang-format ,expected) :test #'string=))
(defun undefine-cpp-types ()
(setf lcp::*cpp-classes* nil)
@ -784,9 +786,9 @@ CPP-TYPE-DECL."
"Filter Clone(ExpressionStorage *exp_storage) const {
Filter object;
object.expressions_.resize(expressions_.size());
for (auto i1 = 0; i1 < expressions_.size(); ++i1) {
object.expressions_[i1] =
expressions_[i1] ? expressions_[i1]->Clone(exp_storage) : nullptr;
for (auto i0 = 0; i0 < expressions_.size(); ++i0) {
object.expressions_[i0] =
expressions_[i0] ? expressions_[i0]->Clone(exp_storage) : nullptr;
}
return object;
}")))
@ -841,15 +843,15 @@ CPP-TYPE-DECL."
"object.member_ = member_;")
(single-member-test (member "std::vector<Klondike>")
"object.member_.resize(member_.size());
for (auto i1 = 0; i1 < member_.size(); ++i1) {
object.member_[i1] = member_[i1].Clone();
for (auto i0 = 0; i0 < member_.size(); ++i0) {
object.member_[i0] = member_[i0].Clone();
}")
(single-member-test (member "std::vector<std::vector<Klondike>>")
"object.member_.resize(member_.size());
for (auto i1 = 0; i1 < member_.size(); ++i1) {
object.member_[i1].resize(member_[i1].size());
for (auto i2 = 0; i2 < member_[i1].size(); ++i2) {
object.member_[i1][i2] = member_[i1][i2].Clone();
for (auto i0 = 0; i0 < member_.size(); ++i0) {
object.member_[i0].resize(member_[i0].size());
for (auto i1 = 0; i1 < member_[i0].size(); ++i1) {
object.member_[i0][i1] = member_[i0][i1].Clone();
}
}"))
(subtest "optional"
@ -857,9 +859,9 @@ CPP-TYPE-DECL."
"object.member_ = member_;")
(single-member-test (member "std::optional<Klondike>")
"if (member_) {
Klondike value1;
value1 = (*member_).Clone();
object.member_.emplace(std::move(value1));
Klondike value0;
value0 = (*member_).Clone();
object.member_.emplace(std::move(value0));
} else {
object.member_ = std::nullopt;
}"))
@ -869,52 +871,52 @@ CPP-TYPE-DECL."
(single-member-test (member "std::unordered_map<int32_t, std::unordered_map<int32_t, std::string>>")
"object.member_ = member_;")
(single-member-test (member "std::unordered_map<int32_t, Klondike>")
"for (const auto &kv1 : member_) {
"for (const auto &kv0 : member_) {
std::pair<int32_t, Klondike> entry1;
{
int32_t first2;
first2 = kv1.first;
Klondike second2;
second2 = kv1.second.Clone();
entry1 = std::make_pair(std::move(first2), std::move(second2));
first2 = kv0.first;
Klondike second3;
second3 = kv0.second.Clone();
entry1 = std::make_pair(std::move(first2), std::move(second3));
}
object.member_.emplace(std::move(entry1));
}")
(single-member-test (member "std::unordered_map<int32_t, std::unordered_map<int32_t, Klondike>>")
"for (const auto &kv1 : member_) {
"for (const auto &kv0 : member_) {
std::pair<int32_t, std::unordered_map<int32_t, Klondike>> entry1;
{
int32_t first2;
first2 = kv1.first;
std::unordered_map<int32_t, Klondike> second2;
for (const auto &kv3 : kv1.second) {
std::pair<int32_t, Klondike> entry3;
first2 = kv0.first;
std::unordered_map<int32_t, Klondike> second3;
for (const auto &kv4 : kv0.second) {
std::pair<int32_t, Klondike> entry5;
{
int32_t first4;
first4 = kv3.first;
Klondike second4;
second4 = kv3.second.Clone();
entry3 = std::make_pair(std::move(first4), std::move(second4));
int32_t first6;
first6 = kv4.first;
Klondike second7;
second7 = kv4.second.Clone();
entry5 = std::make_pair(std::move(first6), std::move(second7));
}
second2.emplace(std::move(entry3));
second3.emplace(std::move(entry5));
}
entry1 = std::make_pair(std::move(first2), std::move(second2));
entry1 = std::make_pair(std::move(first2), std::move(second3));
}
object.member_.emplace(std::move(entry1));
}")
(single-member-test (member "std::unordered_map<Klondike, Klondike>")
"for (const auto &kv1 : member_) {
"for (const auto &kv0 : member_) {
std::pair<Klondike, Klondike> entry1;
{
Klondike first2;
first2 = kv1.first.Clone();
Klondike second2;
second2 = kv1.second.Clone();
entry1 = std::make_pair(std::move(first2), std::move(second2));
first2 = kv0.first.Clone();
Klondike second3;
second3 = kv0.second.Clone();
entry1 = std::make_pair(std::move(first2), std::move(second3));
}
object.member_.emplace(std::move(entry1));
@ -924,42 +926,42 @@ CPP-TYPE-DECL."
"object.member_ = member_;")
(single-member-test (member "std::pair<int32_t, Klondike>")
"{
int32_t first1;
first1 = member_.first;
int32_t first0;
first0 = member_.first;
Klondike second1;
second1 = member_.second.Clone();
object.member_ = std::make_pair(std::move(first1), std::move(second1));
object.member_ = std::make_pair(std::move(first0), std::move(second1));
}")
(single-member-test (member "std::pair<Klondike, int32_t>")
"{
Klondike first1;
first1 = member_.first.Clone();
Klondike first0;
first0 = member_.first.Clone();
int32_t second1;
second1 = member_.second;
object.member_ = std::make_pair(std::move(first1), std::move(second1));
object.member_ = std::make_pair(std::move(first0), std::move(second1));
}")
(single-member-test (member "std::pair<Klondike, Klondike>")
"{
Klondike first1;
first1 = member_.first.Clone();
Klondike first0;
first0 = member_.first.Clone();
Klondike second1;
second1 = member_.second.Clone();
object.member_ = std::make_pair(std::move(first1), std::move(second1));
object.member_ = std::make_pair(std::move(first0), std::move(second1));
}")
(single-member-test (member "std::pair<std::string, std::pair<int32_t, Klondike>>")
"{
std::string first1;
first1 = member_.first;
std::string first0;
first0 = member_.first;
std::pair<int32_t, Klondike> second1;
{
int32_t first2;
first2 = member_.second.first;
Klondike second2;
second2 = member_.second.second.Clone();
second1 = std::make_pair(std::move(first2), std::move(second2));
Klondike second3;
second3 = member_.second.second.Clone();
second1 = std::make_pair(std::move(first2), std::move(second3));
}
object.member_ = std::make_pair(std::move(first1), std::move(second1));
object.member_ = std::make_pair(std::move(first0), std::move(second1));
}"))
(subtest "pointers"
(single-member-test (member "Klondike *")

View File

@ -825,7 +825,7 @@ not an instance of UNSUPPORTED-CPP-TYPE)."
(defun cpp-type-reference-p (cpp-type)
"Test whether CPP-TYPE represents a reference type."
(check-type cpp-type cpp-type)
(string= (cpp-type-name cpp-type) "*"))
(string= (cpp-type-name cpp-type) "&"))
(defun cpp-type-smart-pointer-p (cpp-type)
"Test whether CPP-TYPE represents a smart pointer type."
@ -1038,10 +1038,26 @@ defined.")
"A list of strings naming the enclosing classes of the current class being
defined. The names are ordered from outermost to innermost enclosing class.")
(defun cons-or-replace (element list &key (test #'eql) (key #'identity))
"Cons the ELEMENT to the LIST and remove existing elements.
All elements that compare equal (under TEST) with ELEMENT are removed. KEY will
be applied to each element of LIST before calling TEST."
(cons element (remove-if (lambda (other) (funcall test element other))
list :key key)))
(defun register-enum (cpp-enum)
"Register the given CPP-ENUM instance with the enum registry."
(check-type cpp-enum cpp-enum)
(prog1 cpp-enum
(push cpp-enum *cpp-enums*)
;; Add or redefine the enum.
(setf *cpp-enums*
(cons cpp-enum
(delete-if
(lambda (other)
(string= (cpp-type-decl cpp-enum) (cpp-type-decl other)))
*cpp-enums*)))
;; Add to the parent's inner types.
(unless (eq *cpp-inner-types* :toplevel)
(push cpp-enum *cpp-inner-types*))))
@ -1050,7 +1066,12 @@ defined. The names are ordered from outermost to innermost enclosing class.")
(check-type cpp-class cpp-class)
(prog1 cpp-class
;; Add or redefine the class.
(push cpp-class *cpp-classes*)
(setf *cpp-classes*
(cons cpp-class
(delete-if
(lambda (other)
(string= (cpp-type-decl cpp-class) (cpp-type-decl other)))
*cpp-classes*)))
;; Add to the parent's inner types.
(unless (eq *cpp-inner-types* :toplevel)
(push cpp-class *cpp-inner-types*))))

View File

@ -378,7 +378,7 @@ target_link_libraries(${test_prefix}auth mg-auth kvstore_lib)
add_custom_command(
OUTPUT test_lcp
DEPENDS lcp test_lcp.lisp
DEPENDS ${lcp_src_files} lcp test_lcp.lisp
COMMAND sbcl --script ${CMAKE_CURRENT_SOURCE_DIR}/test_lcp.lisp)
add_custom_target(test_lcp ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/test_lcp)
add_test(test_lcp ${CMAKE_CURRENT_BINARY_DIR}/test_lcp)