有没有办法在Common Lisp中更改文档字符串的分隔符?

时间:2017-09-20 17:11:07

标签: documentation common-lisp

我有时会将函数调用及其输出的示例放在函数定义的文档字符串中。

(defun js-[] (&rest args)
  "Javascript array literal statement.
  (js-[] 1 2 3)
  > \"[1, 2, 3]\"
  "
  (format nil "[~{~A~^, ~}]" (mapcar #'js-expr args)))

但有时函数的输出是一个字符串。所以我必须在示例输出中转义双引号。这很快就会变得单调乏味。

有没有办法将文档字符串分隔符从双引号更改为其他内容,以便我不必继续转义它们?

请注意,有时它比仅仅逃避一次更糟糕:

(defun js-~ (str)
  "Javascript string statement. This is needed so that double quotes are inserted.
  (js-~ \"string\")
  > \"\\\"string\\\"\"
  "
  (format nil "\"~A\"" str))

这里还有一个问题。阅读文档是很困难的。

4 个答案:

答案 0 :(得分:6)

TL; DR

,您可以 没有 ,您不想这样做。

不,CL只有一种字符串

的语法

在Common Lisp中表示字符串的唯一方法是使用 Double-Quote "

是的,您可以修改阅读器,以便其他内容表示字符串

,例如,假设您希望通过@来启动和停止字符串。 (这是一个很少用于符号名称的普通字符, 与%$相反,后者通常用于实现内部符号。)

@设置"的属性:

(multiple-value-bind (function non-terminating-p)
    (get-macro-character #\")
  (set-macro-character #\@ function non-terminating-p))

现在:

(read-from-string "@123@")
==>  "123" ; 5
(read-from-string @"123"@)
==> "123" ; 5

不要忘记restore the input syntax to standard Common Lisp syntax

(setq *readtable* (copy-readtable nil))

请参阅Reader

您可以修改打印机

该标准不要求打印标准物体 (例如string) 使用修改的。

您可以尝试定义print-object方法:

(defmethod print-object ((o string) (d stream))
  ...)

然而,

不,你不想这样做

代码存在供人们阅读。

更改Lisp语法会让其他人更难阅读您的代码。

它还会混淆您使用的各种工具(编辑器& c)。

CL有很多疣,但这不是其中之一; - )

PS。另请参阅documentationdescribe,以及评论语法Sharpsign Vertical-BarSemicolon

答案 1 :(得分:5)

你可以制作一个读取器宏,它会像这样在多行字符串中徘徊:

(defun hash-slash-reader (stream slash arg)
  (declare (ignore slash arg))
  (loop :with s := (make-string-output-stream)
        :for c := (read-char stream)
        :if (and (eql #\/ c) (eql #\# (peek-char nil stream)))
             :do (read-char stream) (return (get-output-stream-string s))
        :if (eql #\Newline c)
             :do (peek-char t stream)
        :do (princ c s)))

(set-dispatch-macro-character #\# #\/ #'hash-slash-reader)

现在你可以做到:

(defun js-~ (str)
  #/ --------------------------
  Javascript string statement. 
  This is needed so that double quotes are inserted.

  (js-~ "string")
  > "\"string\""      
  -------------------------- /#
  (format nil "\"~A\"" str))

将添加文档字符串,就像您使用双引号编写它一样。 这实际上与更改字符串的分隔符相同!。实际上,这是一种 附加 分隔字符串的方式。

这就是为什么你可以在常规的lisp代码中使用它(不推荐使用),而不仅仅是出于文档目的。

使用/作为调度宏的子字符,有助于使其在概念上接近多行注释,但避免被读者完全忽略。

答案 2 :(得分:2)

另一个想法。像往常一样写下你的文档字符串,没有例子。

(defun js-~ (str)
  "Javascript array literal statement."
  ...)

定义测试。这可以很简单:

(defparameter *tests*
  '(((js-~ "string") . "\"string\"")
     ...))

使用该列表执行测试:

(loop for (form . expected) in *tests*
      for (fn . args) = form
      for actual = (apply fn args)
      do (assert (equalp actual expected)))

...并更新文档。请注意,这会附加到现有的文档字符串,因此请不要运行两次。

(loop for (form . expected) in *tests*
      for (fn . args) = form
      do (setf (documentation fn 'function)
               (format nil
                       "~a~%~%    ~S~%    => ~S"
                       (documentation fn 'function)
                       form
                       expected)))

答案 3 :(得分:2)

您可以(ab)使用cl-interpol。虽然库的目的是启用字符串插值,但如果您不介意使用#?预先校对字符串,它也允许自定义字符串分隔符。 e.j。

CL-USER> (cl-interpol:enable-interpol-syntax)
; No values
CL-USER> #?'foo'
"foo"
CL-USER> #?/foo/
"foo"
CL-USER> #?{foo}
"foo"
CL-USER> 

所以启用interpol reader宏后你可以写

(defun js-[] (&rest args)
  #?'Javascript array literal statement.
  (js-[] 1 2 3)
  > "[1, 2, 3]"
  '