memgraph/src/lisp/names.lisp
Lovro Lugovic dbd226d05d 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
2019-06-12 09:38:02 +02:00

195 lines
7.7 KiB
Common Lisp

(in-package #:lcp)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Name operations on strings
(defun uppercase-property-resolver (name)
(when (string= name "Uppercase")
#'upper-case-p))
(defun string-upcase-first (string)
"Upcase the first letter of the string STRING, if any."
(check-type string string)
(if (string= string "") "" (string-upcase string :end 1)))
(defun string-downcase-first (string)
"Downcase the first letter of the string STRING, if any."
(check-type string string)
(if (string= string "") "" (string-downcase string :end 1)))
(defun split-camel-case-string (string)
"Split the camelCase string STRING into a list of parts. The parts are
delimited by uppercase letters."
(check-type string string)
;; NOTE: We use a custom property resolver which handles the Uppercase
;; property by forwarding to UPPER-CASE-P. This is so that we avoid pulling
;; CL-PPCRE-UNICODE & co.
(let ((cl-ppcre:*property-resolver* #'uppercase-property-resolver))
;; NOTE: We use an explicit CREATE-SCANNER call in order to avoid issues
;; with CL-PPCRE's compiler macros which use LOAD-TIME-VALUE which evaluates
;; its forms within a null lexical environment (so our
;; CL-PPCRE:*PROPERTY-RESOLVER* binding would not be seen). Edi actually
;; hints at the problem within the documentation with the sentence "quiz
;; question - why do we need CREATE-SCANNER here?". :-)
;;
;; NOTE: This regex is a zero-width positive lookahead regex. It'll match
;; any zero-width sequence that is followed by an uppercase letter.
(cl-ppcre:split (cl-ppcre:create-scanner "(?=\\p{Uppercase})") string)))
(defun split-pascal-case-string (string)
"Split the PascalCase string STRING into a list of parts. The parts are
delimited by uppercase letters."
(check-type string string)
(split-camel-case-string string))
(defun split-snake-case-string (string)
"Split the snake_case string STRING into a list of parts. The parts are
delimited by underscores. The underscores are not preserved. Empty parts are
trimmed on both sides."
(check-type string string)
(cl-ppcre:split "_" string))
(defun split-kebab-case-string (string)
"Split the kebab-case string STRING into a list of parts. The parts are
delimited by dashes. The dashes are not preserved. Empty parts are trimmed on
both sides."
(check-type string string)
(cl-ppcre:split "-" string))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Name operations on "things"
(defun split-cased-thing (thing &key from-style)
"Split THING into a list of parts according to its type.
- If THING is a symbol, it is split using SPLIT-KEBAB-CASE-STRING.
- If THING is a string, it is split according to the FROM-STYLE keyword
argument. FROM-STYLE can be one of :CAMEL, :PASCAL, :SNAKE or :KEBAB and
denotes splitting using SPLIT-CAMEL-CASE-STRING, SPLIT-PASCAL-CASE-STRING,
SPLIT-SNAKE-CASE-STRING and SPLIT-KEBAB-CASE-STRING respectively. If
FROM-STYLE is omitted or NIL, it is treated as if :CAMEL was given."
(check-type thing (or symbol string))
(ctypecase thing
(symbol (split-kebab-case-string (string thing)))
(string
(ccase from-style
((nil :camel :pascal) (split-camel-case-string thing))
(:snake (split-snake-case-string thing))
(:kebab (split-kebab-case-string thing))))))
(defun camel-case-name (thing &key from-style)
"Return a camelCase string from THING.
The string is formed according to the parts of THING as returned by
SPLIT-CASED-THING. FROM-STYLE is passed to SPLIT-CASED-THING."
(check-type thing (or symbol string))
(string-downcase-first
(format nil "~{~A~}"
(mapcar (alexandria:compose #'string-upcase-first #'string-downcase)
(split-cased-thing thing :from-style from-style)))))
(defun pascal-case-name (thing &key from-style)
"Return a PascalCase string from THING.
The string is formed according to the parts of THING as returned by
SPLIT-CASED-THING. FROM-STYLE is passed to SPLIT-CASED-THING."
(check-type thing (or symbol string))
(string-upcase-first (camel-case-name thing :from-style from-style)))
(defun lower-snake-case-name (thing &key from-style)
"Return a lower_snake_case string from THING.
The string is formed according to the parts of THING as returned by
SPLIT-CASED-THING. FROM-STYLE is passed to SPLIT-CASED-THING."
(check-type thing (or symbol string))
(string-downcase
(format nil "~{~A~^_~}" (split-cased-thing thing :from-style from-style))))
(defun upper-snake-case-name (thing &key from-style)
"Return a UPPER_SNAKE_CASE string from THING.
The string is formed according to the parts of THING as returned by
SPLIT-CASED-THING. FROM-STYLE is passed to SPLIT-CASED-THING."
(check-type thing (or symbol string))
(string-upcase
(format nil "~{~A~^_~}" (split-cased-thing thing :from-style from-style))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Namestrings
(defun ensure-namestring-for (thing func)
"Return the namestring corresponding to the namestring designator THING.
- If THING is a symbol, return the result of calling FUNC on its name.
- If THING is a string, return it."
(check-type thing (or symbol string))
(ctypecase thing
(symbol (funcall func thing))
(string thing)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; C++ names and namestrings
(eval-when (:compile-toplevel :load-toplevel :execute)
(defparameter +cpp-namestring-docstring+
"Return the ~A namestring corresponding to the ~A namestring designator
THING.
- If THING is a symbol, return the result of calling ~A on its name.
- If THING is a string, return it."))
(defmacro define-cpp-name (cpp-object name-op)
"Define a name function and a namestring function for the C++ language element
named by the symbol CPP-OBJECT. Both functions rely on the function named by the
symbol NAME-OP to perform the actual operation.
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 ,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)))))
(define-cpp-name namespace lower-snake-case-name)
(define-cpp-name class pascal-case-name)
(define-cpp-name enum pascal-case-name)
(define-cpp-name type-param pascal-case-name)
(define-cpp-name variable lower-snake-case-name)
(define-cpp-name enumerator upper-snake-case-name)
(defun cpp-name-for-member (thing &key from-style structp)
"Just like CPP-NAME-FOR-VARIABLE except that the suffix \"_\" is added unless
STRUCTP is true."
(check-type thing (or symbol string))
(format nil "~A~@[_~]"
(cpp-name-for-variable thing :from-style from-style)
(not structp)))
(defun ensure-namestring-for-member (thing &key structp)
(check-type thing (or symbol string))
(ensure-namestring-for
thing (lambda (symbol) (cpp-name-for-member symbol :structp structp))))
(setf (documentation 'ensure-namestring-for-member 'function)
(format nil +cpp-namestring-docstring+
"member" "member" 'cpp-member-name))