如何通过符号宏定义符号(和)的符号?

时间:2013-10-11 13:22:35

标签: macros common-lisp symbols

我正在尝试以下列方式定义符号a和b

a + 1 1 b
2

我试图通过使用define-symbol-macro

来做到这一点
(define-symbol-macro a '( )
(define-symbol-macro b ') )

但这种方式不起作用。

3 个答案:

答案 0 :(得分:22)

Lisp对源代码的作用

Common Lisp是一种非常灵活的语言,部分原因是它的源代码可以使用语言中使用的相同数据结构轻松表示。最常见的宏扩展形式将这些结构转换为其他结构。这些是您可以使用define-symbol-macrodefine-compiler-macrodefmacromacrolet定义的宏。然而,在可以执行任何这种宏扩展之前,系统首先需要从输入流(通常是文件或交互式提示)读取源。那是读者的责任。读者还可以在遇到某些字符时执行某些特殊操作,例如('。你想要做的事情可能需要在读者层面发生,如果你想要,例如,(read-from-string "a + 1 1 b")返回列表 (+ 1 1),这就是如果您希望(eval (read-from-string "a + 1 1 b"))返回2,则需要。也就是说,您还可以定义一种特殊的自定义语言(例如loop),其中ab被特别处理。

使用set-macro-character,而不是define-symbol-macro

这不是您使用符号宏进行的操作,而是使用宏字符。您可以使用恰当命名的set-macro-character设置宏字符。例如,在下文中,我将%的宏字符设置为使用应^终止的read-delimited-list读取列表的函数。 (在此处使用字符ab将非常困难,因为之后您将无法编写(set-macro-character ...)之类的内容;这就像编写(set-m(cro-ch(r(cter ...)一样,这不好。)

CL-USER> (set-macro-character #\% (lambda (stream ignore)
                                    (declare (ignore ignore))
                                    (read-delimited-list #\^ stream)))
T
CL-USER> % + 1 1 ^
2

相关的set-syntax-from-char

有一个相关的功能,几乎在这里做了你想做的事,set-syntax-from-char。您可以使用它来使一个角色表现得像另一个角色。例如,您可以将%表现得像(

CL-USER> (set-syntax-from-char #\% #\()
T
CL-USER> % + 1 1 )
2

但是,由于与(关联的宏字符不是在寻找与)具有相同语法的字符,而是实际 )您不能以同样的方式将)替换为^

CL-USER> (set-syntax-from-char #\^ #\))
T
CL-USER> % + 1 1 ^
; Evaluation aborted on #<SB-INT:SIMPLE-READER-ERROR "unmatched close parenthesis" {1002C66031}>.

set-syntax-from-char在有现有角色时更有用,它本身就是你要模仿的东西。例如,如果您想使!成为另一个引用字符:

CL-USER> (set-syntax-from-char #\! #\')
T
CL-USER> (list !a !(1 2 3))
(A (1 2 3))

或使%成为注释字符,就像在LaTeX中一样:

CL-USER> (set-syntax-from-char #\% #\;)
T
CL-USER> (list 1 2 % 3 4
               5 6)
(1 2 5 6)

但请考虑一下你为什么要这样做......

现在,即使你可以做所有这些,但对于遇到它的人来说,这似乎是完全令人惊讶的事情。 (也许你正在进行混淆的编码竞赛?;))由于上面所示的原因,使用ab等常用字符执行此操作也会使编写更多源代码变得非常困难码。定义一个全新的readtable可能是一个更好的选择,它可以完成你想要的,或者甚至可以编写一个新的解析器。尽管(Common)Lisp 重新定义了这种语言,但仍有一些事情可能让你独自留下。

答案 1 :(得分:3)

符号宏是代表另一种形式的符号。好像你想看看读者宏。

我会说第二个Rainer的评论,你想做什么?

答案 2 :(得分:2)

好的,所以我喜欢你对这个原因的评论,现在我知道这是为了'因为它是lisp'然后我完全在船上了!

好的,所以你认为lisp很适合制作新语言,因为我们只需要'编译'到有效的lisp代码,它就会运行。因此,虽然我们不能使用普通编译器将符号'a和'b转换为括号,但我们可以自己编写。

好的,让我们开始吧!

(defun symbol-name-equal (a b)
  (and (symbolp a) (symbolp b) (equal (symbol-name a) (symbol-name b))))

(defun find-matching-weird (start-pos open-symbol close-symbol code)
  (unless (symbol-name-equal open-symbol (nth start-pos code))
    (error "start-pos does not point to a weird open-symbol"))
  (let ((nest-index 0))
    (loop :for item :in (nthcdr start-pos code) 
       :for i :from start-pos :do
       (cond ((symbol-name-equal item open-symbol) (incf nest-index 1))
             ((symbol-name-equal item close-symbol) (incf nest-index -1)))
       (when (eql nest-index 0)
         (return i))
       :finally (return nil))))

(defun weird-forms (open-symbol close-symbol body)
  (cond ((null body) nil)
        ((listp body) 
         (let ((open-pos (position open-symbol body :test #'symbol-name-equal)))
           (if open-pos
               (let ((close-pos (find-matching-weird open-pos open-symbol close-symbol body)))
                 (if close-pos
                     (weird-forms open-symbol close-symbol
                                  `(,@(subseq body 0 open-pos) 
                                      (,@(subseq body (1+ open-pos) close-pos))
                                      ,@(subseq body (1+ close-pos))))
                     (error "unmatched weird brackets")))
               (if (find close-symbol body :test #'symbol-name-equal)
                   (error "unmatched weird brackets")
                   (loop for item in body collect 
                        (weird-forms open-symbol close-symbol item))))))
        (t body)))


(defmacro with-weird-forms ((open-symbol close-symbol) &body body)
  `(progn
     ,@(weird-forms open-symbol close-symbol body)))

因此,有几个部分。

首先我们有(symbol-name-equal),这是一个辅助函数,因为我们现在使用属于包的符号和符号。 symbol-name-equal为我们提供了一种检查符号是否具有相同名称的方法,忽略了它们所在的包。

其次我们有(找到 - 匹配 - 奇怪)。这是一个函数,它将列表和索引带到一个开放的怪异括号,并将索引返回到结束怪异括号。这确保我们即使使用嵌套也能得到正确的括号

接下来我们有(奇怪的形式)。这是多汁的一点,它的作用是递归遍历作为'body'参数传递的列表并执行以下操作:

  • 如果body是空列表,则返回
  • 如果body是一个列表那么
    • 找到我们的开放和关闭符号的位置。
    • 如果只找到其中​​一个,那么我们有无与伦比的括号。
    • 如果我们找到两个符号,则在嵌套列表中的起始位置和结束位置之间创建一个新列表。
    • 然后我们在这个结果上调用奇怪的形式,以防里面有更多奇怪的符号形式。
  • 没有奇怪的符号然后只是遍历列表中的项目并在它们上面调用奇怪的形式以保持搜索。

确定,以便该功能转换列表。例如,尝试:

(weird-forms 'a 'b '(1 2 3 a 4 5 b 6 7))

但是我们希望这是执行的正确的lisp代码,所以我们需要使用一个简单的宏。 (with-weird-forms)是一个宏,它接受调用奇怪形式的函数并将结果放入我们的源代码中,由lisp编译。所以,如果我们有这个:

(with-weird-forms (a b)
  (+ 1 2 3 a - a + 1 2 3 b 10 5 b 11 23))

然后它宏扩展到:

(PROGN (+ 1 2 3 (- (+ 1 2 3) 10 5) 11 23))

哪个是完全有效的lisp代码,所以它会运行!

CL-USER> (with-weird-forms (a b)
           (+ 1 2 3 a - a + 1 2 3 b 10 5 b 11 23))
31

最后,如果您已经确定了'a'和'b'括号,您可以编写另一个小宏:

(defmacro ab-lang (&rest code)
  `(with-weird-forms (a b) ,@code))

现在试试这个:

(ab-lang a let* a a d 1 b a e a * d 5 b b b a format t "this stupid test gives: ~a" e b b)

干杯交配,这写起来很有趣。很抱歉之前解决了这个问题。

这种编码非常重要,因为最终这是一个很小的编译器,用于我们奇怪的语言,其中符号可以是标点符号。编译器非常棒,没有任何语言可以像lisp一样轻松编写它们。

和平!