diff --git a/cmake/functions.cmake b/cmake/functions.cmake index a53468f5a..2891a67ab 100644 --- a/cmake/functions.cmake +++ b/cmake/functions.cmake @@ -108,51 +108,3 @@ macro(define_add_capnp main_src_files generated_capnp_files) set(${main_src_files} ${${main_src_files}} ${cpp_file} PARENT_SCOPE) endfunction(add_capnp) endmacro(define_add_capnp) - -# Lisp C++ Preprocessing - -set(lcp_exe ${CMAKE_SOURCE_DIR}/tools/lcp) -set(lcp_src_files ${CMAKE_SOURCE_DIR}/src/lisp/lcp.lisp ${lcp_exe}) - -# Define `add_lcp` function named `name` for registering a lcp file for generation. -# -# The `define_add_lcp` expects 3 arguments: -# * name -- name for the function, you usually want `add_lcp` -# * main_src_files -- variable to be updated with generated cpp files -# * generated_lcp_files -- variable to be updated with generated hpp, cpp and capnp files -# -# The `add_lcp` function expects at least a single argument, path to lcp file. -# Each added file is standalone and we avoid recompiling everything. -# You may pass a CAPNP_SCHEMA keyword argument to generate the Cap'n Proto -# serialization code from .lcp file. You still need to add the generated capnp -# file through `add_capnp` function. To generate the use `capnp id` -# invocation, and specify it here. This preserves correct id information across -# multiple schema generations. If this wasn't the case, wrong typeId -# information will break serialization between different compilations. -macro(define_add_lcp name main_src_files generated_lcp_files) - function(${name} lcp_file) - set(one_value_kwargs CAPNP_SCHEMA) - set(multi_value_kwargs DEPENDS) - # NOTE: ${${}ARGN} syntax escapes evaluating macro's ARGN variable; see: - # https://stackoverflow.com/questions/50365544/how-to-access-enclosing-functions-arguments-from-within-a-macro - cmake_parse_arguments(KW "" "${one_value_kwargs}" "${multi_value_kwargs}" ${${}ARGN}) - string(REGEX REPLACE "\.lcp$" ".hpp" h_file - "${CMAKE_CURRENT_SOURCE_DIR}/${lcp_file}") - if (KW_CAPNP_SCHEMA) - string(REGEX REPLACE "\.lcp$" ".capnp" capnp_file - "${CMAKE_CURRENT_SOURCE_DIR}/${lcp_file}") - set(capnp_id ${KW_CAPNP_SCHEMA}) - set(capnp_depend capnproto-proj) - set(cpp_file ${CMAKE_CURRENT_SOURCE_DIR}/${lcp_file}.cpp) - # Update *global* main_src_files - set(${main_src_files} ${${main_src_files}} ${cpp_file} PARENT_SCOPE) - endif() - add_custom_command(OUTPUT ${h_file} ${cpp_file} ${capnp_file} - COMMAND ${lcp_exe} ${lcp_file} ${capnp_id} - VERBATIM - DEPENDS ${lcp_file} ${lcp_src_files} ${capnp_depend} ${KW_DEPENDS} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - # Update *global* generated_lcp_files - set(${generated_lcp_files} ${${generated_lcp_files}} ${h_file} ${cpp_file} ${capnp_file} PARENT_SCOPE) - endfunction(${name}) -endmacro(define_add_lcp) diff --git a/docs/dev/lcp.md b/docs/dev/lcp.md index 8fa3a8137..727ac5814 100644 --- a/docs/dev/lcp.md +++ b/docs/dev/lcp.md @@ -282,8 +282,8 @@ For example: The above should produce expected results. You can add a base classes after the class name. The name should be a Lisp -symbol for bases classes defined through `lcp:define-class`, so that LCP -tracks the inheritance. Otherwise, it should be a string. +symbol for base classes defined through `lcp:define-class`, so that LCP tracks +the inheritance. Otherwise, it should be a string. For example: @@ -412,7 +412,7 @@ code for serialization. Primary purpose of LCP was to make serialization of types easier. Our serialization library of choice for C++ is Cap'n Proto. LCP provides generation and tuning of its serialization code. Previously, LCP supported -boost serialization, but it was removed. +Boost.Serialization, but it was removed. To specify a class or structure for serialization, you may pass a `:serialize :capnp` option when defining such type. (Note that @@ -506,7 +506,7 @@ will not be able to generate correct serialization code. The cases so far have been only with classes that are pure interface and need no serialization code. This is signaled to LCP by passing the option `:base t` -to `:serialie :capnp`. LCP will treat such classes as actually being the base +to `:serialize :capnp`. LCP will treat such classes as actually being the base class of a hierarchy. For example: @@ -589,11 +589,11 @@ Sometimes the default serialization is not adequate and you may wish to provide your own serialization code. For those reasons, LCP provides `:capnp-save`, `:capnp-load` and `:capnp-init` options on each class member. -The simplest is `:capnp-init` which when set to `nil` will not generate a -`initMember` call on a builder. Cap'n Proto requires that compound types are -initialized before beginning to serialize its members. `:capnp-init` allows -you to delay the initialization to your custom save code. You rarely want to -set `:capnp-init nil`. +The simplest is `:capnp-init` which when set to `nil` will not generate an +`init` call on a builder. Cap'n Proto requires that compound types are +initialized before beginning to serialize its members. `:capnp-init` allows you +to delay the initialization to your custom save code. You rarely want to set +`:capnp-init nil`. Custom save code is added as a value of `:capnp-save`. It should be a function which takes 3 arguments. @@ -636,7 +636,7 @@ With custom serialization code, you may want to get additional details through extra arguments to `Save` and `Load` functions. This is described in the next section. -There are also cases where you always need a custom serialization code. LCP +There are also cases where you always need custom serialization code. LCP provides helper functions for abstracting some common details. These functions are listed further down in this document. diff --git a/init b/init index 598c44de4..eac27bc7a 100755 --- a/init +++ b/init @@ -116,16 +116,19 @@ quicklisp_install_dir="$HOME/quicklisp" if [[ -v QUICKLISP_HOME ]]; then quicklisp_install_dir="${QUICKLISP_HOME}" fi + +# TODO: move the installation of LCP's dependencies into ./setup.sh if [[ ! -f "${quicklisp_install_dir}/setup.lisp" ]]; then wget -nv https://beta.quicklisp.org/quicklisp.lisp -O quicklisp.lisp || exit 1 echo \ " (load \"${DIR}/quicklisp.lisp\") (quicklisp-quickstart:install :path \"${quicklisp_install_dir}\") - (ql:quickload :cl-ppcre :silent t) + (ql:quickload '(:cl-ppcre :prove) :silent t) " | sbcl --script || exit 1 rm -rf quicklisp.lisp || exit 1 fi +ln -fs "$DIR/src/lisp" "${quicklisp_install_dir}/local-projects/lcp" # setup libs (download) cd libs diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1b4944521..3c6e4fc7f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,7 @@ # CMake configuration for the main memgraph library and executable # add memgraph sub libraries, ordered by dependency +add_subdirectory(lisp) add_subdirectory(utils) add_subdirectory(requests) add_subdirectory(integrations) diff --git a/src/lisp/CMakeLists.txt b/src/lisp/CMakeLists.txt new file mode 100644 index 000000000..cad769efd --- /dev/null +++ b/src/lisp/CMakeLists.txt @@ -0,0 +1,57 @@ +# Lisp C++ Preprocessing + +set(lcp_exe ${CMAKE_SOURCE_DIR}/tools/lcp) +set(lcp_src_files + ${CMAKE_SOURCE_DIR}/src/lisp/lcp.asd + ${CMAKE_SOURCE_DIR}/src/lisp/package.lisp + ${CMAKE_SOURCE_DIR}/src/lisp/lcp.lisp + ${CMAKE_SOURCE_DIR}/src/lisp/lcp-test.lisp + ${lcp_exe}) + +add_custom_target(lcp + DEPENDS ${lcp_src_files} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/lcp-compile) + +# Define `add_lcp` function named `name` for registering a lcp file for generation. +# +# The `define_add_lcp` expects 3 arguments: +# * name -- name for the function, you usually want `add_lcp` +# * main_src_files -- variable to be updated with generated cpp files +# * generated_lcp_files -- variable to be updated with generated hpp, cpp and capnp files +# +# The `add_lcp` function expects at least a single argument, path to lcp file. +# Each added file is standalone and we avoid recompiling everything. +# You may pass a CAPNP_SCHEMA keyword argument to generate the Cap'n Proto +# serialization code from .lcp file. You still need to add the generated capnp +# file through `add_capnp` function. To generate the use `capnp id` +# invocation, and specify it here. This preserves correct id information across +# multiple schema generations. If this wasn't the case, wrong typeId +# information will break serialization between different compilations. +macro(define_add_lcp name main_src_files generated_lcp_files) + function(${name} lcp_file) + set(one_value_kwargs CAPNP_SCHEMA) + set(multi_value_kwargs DEPENDS) + # NOTE: ${${}ARGN} syntax escapes evaluating macro's ARGN variable; see: + # https://stackoverflow.com/questions/50365544/how-to-access-enclosing-functions-arguments-from-within-a-macro + cmake_parse_arguments(KW "" "${one_value_kwargs}" "${multi_value_kwargs}" ${${}ARGN}) + string(REGEX REPLACE "\.lcp$" ".hpp" h_file + "${CMAKE_CURRENT_SOURCE_DIR}/${lcp_file}") + if (KW_CAPNP_SCHEMA) + string(REGEX REPLACE "\.lcp$" ".capnp" capnp_file + "${CMAKE_CURRENT_SOURCE_DIR}/${lcp_file}") + set(capnp_id ${KW_CAPNP_SCHEMA}) + set(capnp_depend capnproto-proj) + set(cpp_file ${CMAKE_CURRENT_SOURCE_DIR}/${lcp_file}.cpp) + # Update *global* main_src_files + set(${main_src_files} ${${main_src_files}} ${cpp_file} PARENT_SCOPE) + endif() + add_custom_command(OUTPUT ${h_file} ${cpp_file} ${capnp_file} + COMMAND ${CMAKE_SOURCE_DIR}/tools/lcp ${lcp_file} ${capnp_id} + VERBATIM + DEPENDS ${lcp_file} lcp ${capnp_depend} ${KW_DEPENDS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + # Update *global* generated_lcp_files + set(${generated_lcp_files} ${${generated_lcp_files}} ${h_file} ${cpp_file} ${capnp_file} PARENT_SCOPE) + endfunction(${name}) +endmacro(define_add_lcp) diff --git a/src/lisp/lcp-compile b/src/lisp/lcp-compile new file mode 100755 index 000000000..dc4002f3f --- /dev/null +++ b/src/lisp/lcp-compile @@ -0,0 +1,12 @@ +#!/bin/bash + +quicklisp_install_dir="$HOME/quicklisp" +if [[ -v QUICKLISP_HOME ]]; then + quicklisp_install_dir="${QUICKLISP_HOME}" +fi + +echo \ +" +(load \"${quicklisp_install_dir}/setup.lisp\") +(ql:quickload :lcp :silent t) +" | sbcl --script \ No newline at end of file diff --git a/src/lisp/lcp-test.lisp b/src/lisp/lcp-test.lisp new file mode 100644 index 000000000..d798ab9f1 --- /dev/null +++ b/src/lisp/lcp-test.lisp @@ -0,0 +1,105 @@ +(defpackage #:lcp-test + (:use #:cl #:prove)) + +(in-package #:lcp-test) + +(defun same-type-test (a b) + "Test whether A and B are the same C++ type under LCP::CPP-TYPE=." + (is a b :test #'lcp::cpp-type=)) + +(defun parse-test (type-decl cpp-type) + "Test whether TYPE-DECL parses as the C++ type designated by CPP-TYPE." + (is (lcp::parse-cpp-type-declaration type-decl) cpp-type + :test #'lcp::cpp-type=)) + +(defun decl-test (type-decl type &key (type-params t) (namespace t)) + "Test whether the C++ type designated by TYPE prints as TYPE-DECL." + (is (lcp::cpp-type-decl type + :type-params type-params + :namespace namespace) + type-decl)) + +(defun different-parse-test (type-decl1 type-decl2) + (isnt (lcp::parse-cpp-type-declaration type-decl1) + (lcp::parse-cpp-type-declaration type-decl2) + :test #'lcp::cpp-type=)) + +(plan nil) + +(deftest "supported" + (subtest "designators" + (mapc (lambda (sym) + (let ((type (lcp::make-cpp-primitive-type (string-downcase sym)))) + (same-type-test sym type) + (same-type-test (string-downcase sym) type) + (same-type-test (string-upcase sym) type) + (same-type-test (string-capitalize sym) type) + (same-type-test (intern (string sym)) type) + (same-type-test (intern (string-downcase sym)) type) + (same-type-test (intern (string-upcase sym)) type) + (same-type-test (intern (string-capitalize sym)) type) + (same-type-test (lcp::make-cpp-primitive-type + (string-downcase sym)) + type))) + lcp::+cpp-primitive-type-keywords+) + (mapc (lambda (sym) + (let ((type (lcp::make-cpp-type "MyClass"))) + (same-type-test sym type))) + `(:my-class :|mY-cLASS| my-class "MyClass" + ,(lcp::make-cpp-type "MyClass")))) + + (subtest "parsing" + (parse-test "char*" + (lcp::make-cpp-type "*" :type-args '(:char))) + + (parse-test "char *" + (lcp::make-cpp-type "*" :type-args '(:char))) + + (parse-test "::std::pair, double>, char>" + (lcp::make-cpp-type + "pair" + :namespace'("" "std") + :type-args + `(,(lcp::make-cpp-type + "MyClass" + :namespace '("my_space") + :type-args + `(,(lcp::make-cpp-type + "function" + :namespace '("std") + :type-args '("void(int, bool)")) + :double)) + :char)))) + + (subtest "printing" + (decl-test "pair" + (lcp::make-cpp-type + "pair" + :type-args + (list + (lcp::make-cpp-type "T1") + (lcp::make-cpp-type "T2")))) + + (decl-test "pair" + (lcp::make-cpp-type "pair" :type-args '(:int :double))) + + (decl-test "pair" + (lcp::make-cpp-type + "pair" :type-params '("TIntegral1" "TIntegral2"))) + + (decl-test "pair" + (lcp::make-cpp-type + "pair" :type-params '("TIntegral1 TIntegral2")) + :type-params nil))) + +(deftest "unsupported" + (subtest "cv-qualifiers" + (different-parse-test "const char" "char const") + (different-parse-test "volatile char" "char volatile") + (different-parse-test "const volatile char" "char const volatile") + (different-parse-test "const char *" "char const *") + (different-parse-test "volatile char *" "char volatile *")) + + (subtest "arrays" + (different-parse-test "char (*)[]" "char (*) []") + (different-parse-test "char (*)[4]" "char (*) [4]"))) diff --git a/src/lisp/lcp.asd b/src/lisp/lcp.asd new file mode 100644 index 000000000..1f802ac9a --- /dev/null +++ b/src/lisp/lcp.asd @@ -0,0 +1,14 @@ +(defsystem "lcp" + :description "LCP: The Lisp C++ Preprocessor" + :version "0.0.1" + :author "Teon Banek " + :depends-on ("cl-ppcre") + :serial t + :components ((:file "package") + (:file "lcp")) + :in-order-to ((test-op (test-op "lcp/test")))) + +(defsystem "lcp/test" + :depends-on ("lcp" "prove") + :components ((:file "lcp-test")) + :perform (test-op :after (op s) (symbol-call :prove :run-test-package :lcp-test))) diff --git a/src/lisp/lcp.lisp b/src/lisp/lcp.lisp index 9bdc2e1c0..2bd4cf459 100644 --- a/src/lisp/lcp.lisp +++ b/src/lisp/lcp.lisp @@ -1,29 +1,9 @@ -(defpackage #:lcp - (:use #:cl) - (:export #:define-class - #:define-struct - #:define-enum - #:define-rpc - #:cpp-list - #:in-impl - #:namespace - #:pop-namespace - #:capnp-namespace - #:capnp-import - #:capnp-type-conversion - #:capnp-save-optional - #:capnp-load-optional - #:capnp-save-vector - #:capnp-load-vector - #:capnp-save-enum - #:capnp-load-enum - #:process-file)) - (in-package #:lcp) -(defconstant +whitespace-chars+ '(#\Newline #\Space #\Return #\Linefeed #\Tab)) -(defconstant +vim-read-only+ "vim: readonly") -(defconstant +emacs-read-only+ "-*- buffer-read-only: t; -*-") +(eval-when (:compile-toplevel :load-toplevel :execute) + (defvar +whitespace-chars+ '(#\Newline #\Space #\Return #\Linefeed #\Tab))) +(defvar +vim-read-only+ "vim: readonly") +(defvar +emacs-read-only+ "-*- buffer-read-only: t; -*-") (defstruct raw-cpp (string "" :type string :read-only t)) @@ -96,7 +76,10 @@ namespace. A single symbol may refer to a `CPP-CLASS' which encloses this type.") (enclosing-class :type (or null symbol) :initarg :enclosing-class - :initform nil :accessor cpp-type-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 + no enclosing class.") (name :type (or symbol string) :initarg :name :reader cpp-type-base-name :documentation "Base name of this type.") (type-params :type list :initarg :type-params :initform nil @@ -106,10 +89,46 @@ class vector`, 'TValue' is type parameter.") (type-args :type list :initarg :type-args :initform nil :reader cpp-type-type-args - :documentation "List of already applied template arguments. For - example in `std::vector`, 'int' is a type argument.")) + :documentation "List of CPP-TYPE instances that represent the + template type arguments used within the instantiation of the + template. For example in `std::vector`, 'int' is a template + type argument.")) (:documentation "Base class for meta information on C++ types.")) +(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))) + +(defun cpp-type= (a b) + (let ((a (cpp-type a)) + (b (cpp-type b))) + (with-accessors ((args1 cpp-type-type-args)) a + (with-accessors ((args2 cpp-type-type-args)) b + (and (equalp (cpp-type-namespace a) (cpp-type-namespace b)) + (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))))))) + +(defmethod print-object ((cpp-type cpp-type) stream) + (print-unreadable-object (cpp-type stream :type t) + (with-accessors ((name cpp-type-base-name) + (ns cpp-type-namespace) + (params cpp-type-type-params) + (args cpp-type-type-args)) + cpp-type + (format stream ":name ~S" name) + (format stream "~@[ ~{~@?~^ ~}~]" + `(,@(when ns `(":namespace ~S" ,ns)) + ,@(when params `(":type-params ~S" ,params)) + ,@(when args `(":type-args ~S" ,args))))))) + (defgeneric cpp-type-name (cpp-type) (:documentation "Get C++ style type name from `CPP-TYPE' as a string.")) @@ -123,21 +142,31 @@ (deftype cpp-primitive-type-keywords () "List of keywords that specify a primitive type in C++." - `(member :bool :int :int16_t :int32_t :int64_t :uint :uint16_t :uint32_t :uint64_t :float :double)) + `(member :bool :char :int :int16_t :int32_t :int64_t :uint :uint16_t + :uint32_t :uint64_t :float :double)) -(defconstant +cpp-primitive-type-keywords+ - '(:bool :int :int16_t :int32_t :int64_t :uint :uint16_t :uint32_t :uint64_t :float :double)) +(defvar +cpp-primitive-type-keywords+ + '(:bool :char :int :int16_t :int32_t :int64_t :uint :uint16_t + :uint32_t :uint64_t :float :double)) (defmethod cpp-type-name ((cpp-type symbol)) "Return PascalCase of CPP-TYPE symbol or lowercase if it is a primitive type." (if (typep cpp-type 'cpp-primitive-type-keywords) - (string-downcase (string cpp-type)) - (remove #\- (string-capitalize (string cpp-type))))) + (string-downcase cpp-type) + (remove #\- (string-capitalize cpp-type)))) (defclass cpp-primitive-type (cpp-type) ((name :type cpp-primitive-type-keywords)) (:documentation "Represents a primitive type in C++.")) +(defun make-cpp-primitive-type (name) + "Create an instance of CPP-PRIMITIVE-TYPE given the arguments." + (make-instance 'cpp-primitive-type :name name)) + +(defun cpp-primitive-type-p (type-decl) + "Whether the C++ type designated by TYPE-DECL is a primitive type." + (typep (cpp-type type-decl) 'cpp-primitive-type)) + (defun parse-cpp-type-declaration (type-decl) "Parse C++ type from TYPE-DECL string and return CPP-TYPE. @@ -173,7 +202,8 @@ produces: :test #'string-equal))) (when type-keyword (return-from parse-cpp-type-declaration - (make-instance 'cpp-primitive-type :name (car type-keyword))))) + (make-instance 'cpp-primitive-type :name (string-downcase + (car type-keyword)))))) ;; Check if pointer (let ((ptr-pos (position #\* type-decl :from-end t))) (when (and ptr-pos (not (cl-ppcre:scan "[()<>]" type-decl :start ptr-pos))) @@ -211,7 +241,7 @@ produces: :name name :type-args (reverse type-args))))) -(defun cpp-type-decl (cpp-type &key (type-args t) (namespace t)) +(defun cpp-type-decl (cpp-type &key (type-params t) (namespace t)) "Return the fully qualified name of given CPP-TYPE." (declare (type cpp-type cpp-type)) (flet ((enclosing-classes (cpp-type) @@ -234,9 +264,13 @@ produces: (when namespace (format s "~{~A::~}" (cpp-type-namespace cpp-type))) (format s "~{~A~^::~}" (enclosing-classes cpp-type)) - (when (and type-args (cpp-type-type-params cpp-type)) - ;; TODO: What about applied type args? - (format s "<~{~A~^, ~}>" (mapcar #'cpp-type-name (cpp-type-type-params cpp-type)))))))))) + (cond + ((cpp-type-type-args cpp-type) + (format s "<~{~A~^, ~}>" (mapcar #'cpp-type-name + (cpp-type-type-args cpp-type)))) + ((and type-params (cpp-type-type-params cpp-type)) + (format s "<~{~A~^, ~}>" (mapcar #'cpp-type-name + (cpp-type-type-params cpp-type))))))))))) (defclass cpp-enum (cpp-type) ((values :type list :initarg :values :initform nil :reader cpp-enum-values) @@ -298,6 +332,36 @@ produces: (abstractp :initarg :abstractp :initform nil :reader cpp-class-abstractp)) (:documentation "Meta information on a C++ class (or struct).")) +;; 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. + +- 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+, + return an instance of CPP-PRIMITIVE-TYPE with the name being the result + 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 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) + (cpp-primitive-type-keywords + (make-cpp-primitive-type (string-downcase type-designator))) + ((or symbol string) + (if (member type-designator +cpp-primitive-type-keywords+ :test #'string-equal) + (make-cpp-primitive-type (string-downcase type-designator)) + (make-cpp-type + (if (symbolp type-designator) + (remove #\- (string-capitalize type-designator)) + type-designator)))))) + (defvar *cpp-classes* nil "List of defined classes from LCP file") (defvar *cpp-enums* nil "List of defined enums from LCP file") @@ -859,8 +923,8 @@ encoded as union inheritance in Cap'n Proto." (let* ((parents (capnp-union-parents-rec cpp-class)) (top-parent-class (if parents - (cpp-type-decl (find-cpp-class (car (last parents))) :type-args nil :namespace nil) - (cpp-type-decl cpp-class :type-args nil :namespace nil))) + (cpp-type-decl (find-cpp-class (car (last parents))) :type-params nil :namespace nil) + (cpp-type-decl cpp-class :type-params nil :namespace nil))) (self-arg (list 'self (format nil "const ~A &" (cpp-type-decl cpp-class :namespace nil)))) @@ -1111,8 +1175,8 @@ Proto schema." (declare (type cpp-class cpp-class)) (let* ((parents (capnp-union-parents-rec cpp-class)) (top-parent-class (if parents - (cpp-type-decl (find-cpp-class (car (last parents))) :type-args nil :namespace nil) - (cpp-type-decl cpp-class :type-args nil :namespace nil))) + (cpp-type-decl (find-cpp-class (car (last parents))) :type-params nil :namespace nil) + (cpp-type-decl cpp-class :type-params nil :namespace nil))) (reader-arg (list (if (or parents (capnp-union-subclasses cpp-class)) 'base-reader 'reader) diff --git a/src/lisp/package.lisp b/src/lisp/package.lisp new file mode 100644 index 000000000..b90866758 --- /dev/null +++ b/src/lisp/package.lisp @@ -0,0 +1,20 @@ +(defpackage #:lcp + (:use #:cl) + (:export #:define-class + #:define-struct + #:define-enum + #:define-rpc + #:cpp-list + #:in-impl + #:namespace + #:pop-namespace + #:capnp-namespace + #:capnp-import + #:capnp-type-conversion + #:capnp-save-optional + #:capnp-load-optional + #:capnp-save-vector + #:capnp-load-vector + #:capnp-save-enum + #:capnp-load-enum + #:process-file)) diff --git a/tests/apollo_runs.py b/tests/apollo_runs.py index 0b8879d10..9c2424d07 100755 --- a/tests/apollo_runs.py +++ b/tests/apollo_runs.py @@ -26,6 +26,7 @@ CTEST_DELIMITER = "__" for row in ctest_output.split("\n"): # Filter rows only containing tests. if not re.match("^\s*Test\s+#", row): continue + if not row.count("memgraph"): continue test_name = row.split(":")[1].strip() name = test_name.replace("memgraph" + CTEST_DELIMITER, "") path = os.path.join(TESTS_DIR_REL, name.replace(CTEST_DELIMITER, "/", 1)) @@ -66,4 +67,11 @@ for test in tests: "outfile_paths": outfile_paths, }) +runs.append({ + "name": "test_lcp", + "cd": os.path.join(TESTS_DIR_REL, "unit"), + "commands": "./test_lcp", + "infiles": ["test_lcp"], +}) + print(json.dumps(runs, indent=4, sort_keys=True)) diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index b349fa23f..5861cda24 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -314,3 +314,12 @@ target_link_libraries(${test_prefix}utils_watchdog mg-utils) add_unit_test(auth.cpp) target_link_libraries(${test_prefix}auth mg-auth kvstore_lib) + +# Test LCP + +add_custom_target(test_lcp ALL + BYPRODUCTS test_lcp + COMMAND sbcl --script ${CMAKE_CURRENT_SOURCE_DIR}/test_lcp.lisp + DEPENDS lcp) +add_test(test_lcp ${CMAKE_CURRENT_BINARY_DIR}/test_lcp) +add_dependencies(memgraph__unit test_lcp) diff --git a/tests/unit/test_lcp.lisp b/tests/unit/test_lcp.lisp new file mode 100644 index 000000000..0b3ac08be --- /dev/null +++ b/tests/unit/test_lcp.lisp @@ -0,0 +1,13 @@ +(require 'asdf) + +(let ((home (or (uiop:getenvp "QUICKLISP_HOME") + (concatenate 'string (uiop:getenvp "HOME") "/quicklisp")))) + (load (concatenate 'string home "/setup.lisp"))) + +(ql:quickload "lcp/test") +(setf uiop:*image-entry-point* + (lambda () + (let ((prove:*default-reporter* :fiveam)) + (unless (prove:run-test-package :lcp-test) + (uiop:quit 1))))) +(uiop:dump-image "test_lcp" :executable t) diff --git a/tools/lcp b/tools/lcp index a02e74efb..42101855a 100755 --- a/tools/lcp +++ b/tools/lcp @@ -28,8 +28,7 @@ fi echo \ " (load \"${quicklisp_install_dir}/setup.lisp\") -(ql:quickload :cl-ppcre :silent t) -(load \"$script_dir/../src/lisp/lcp.lisp\") +(ql:quickload :lcp :silent t) (lcp:process-file \"$lcp_file\" $capnp) " | sbcl --script