Add LCP tests

Summary: Create an ASDF system for LCP. Add LCP tests.

Reviewers: teon.banek, mtomic

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1645
This commit is contained in:
Lovro Lugovic 2018-10-15 11:01:57 +02:00
parent 8c2ca44cb9
commit a6a621e08b
14 changed files with 359 additions and 102 deletions

View File

@ -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 <id> 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 <id> 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)

View File

@ -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<member>` 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.

5
init
View File

@ -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

View File

@ -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)

57
src/lisp/CMakeLists.txt Normal file
View File

@ -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 <id> 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 <id> 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)

12
src/lisp/lcp-compile Executable file
View File

@ -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

105
src/lisp/lcp-test.lisp Normal file
View File

@ -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<my_space::MyClass<std::function<void(int, bool)>, 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<T1, T2>"
(lcp::make-cpp-type
"pair"
:type-args
(list
(lcp::make-cpp-type "T1")
(lcp::make-cpp-type "T2"))))
(decl-test "pair<int, double>"
(lcp::make-cpp-type "pair" :type-args '(:int :double)))
(decl-test "pair<TIntegral1, TIntegral2>"
(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]")))

14
src/lisp/lcp.asd Normal file
View File

@ -0,0 +1,14 @@
(defsystem "lcp"
:description "LCP: The Lisp C++ Preprocessor"
:version "0.0.1"
:author "Teon Banek <teon.banek@memgraph.io>"
: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)))

View File

@ -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 @@
<TValue> 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>`, '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>`, '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)

20
src/lisp/package.lisp Normal file
View File

@ -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))

View File

@ -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))

View File

@ -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)

13
tests/unit/test_lcp.lisp Normal file
View File

@ -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)

View File

@ -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