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:
parent
8c2ca44cb9
commit
a6a621e08b
@ -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)
|
||||
|
@ -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
5
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
|
||||
|
@ -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
57
src/lisp/CMakeLists.txt
Normal 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
12
src/lisp/lcp-compile
Executable 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
105
src/lisp/lcp-test.lisp
Normal 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
14
src/lisp/lcp.asd
Normal 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)))
|
@ -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
20
src/lisp/package.lisp
Normal 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))
|
@ -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))
|
||||
|
@ -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
13
tests/unit/test_lcp.lisp
Normal 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)
|
Loading…
Reference in New Issue
Block a user