对于“语言的代码意识”,我见过的没有比 Lisp 及其宏工具更好的了——特别是 Common Lisp。但是交易是大多数时候对象的类型在编译时或宏扩展时是未知的。对于文字,类型是已知的,因此您可以找到激进宏的示例,用于测试对象是否为文字,如果是,则以一种方式处理它——可能基于其类型——否则准备检测到的变量用于运行时类型检查。
这是我几年前改编自CLLIB库(CLOCC 库的一部分)的示例。目标是提供将前缀字符串从具有匹配前缀的其他字符串中剔除的函数。前缀可能在宏扩展时已知,也可能不知道。如果是,我们可以进行优化:首先计算前缀的长度并将其嵌入为文字,这样每次调用生成的函数时都不会重新计算它。宏起初令人生畏,但实际生成的代码很小。
(defmacro after-prefix-core (comparison-op prefix string &optional length)
"Similar to cllib:string-beg-with-cs."
(flet ((chop (prefix prefix-length string string-length)
`(when (and (>= ,string-length ,prefix-length)
(,comparison-op ,prefix ,string :end2 ,prefix-length))
(subseq ,string ,prefix-length ,string-length))))
(let* ((gstring (gensym "STRING-"))
(gstring-length (gensym "STRING-LENGTH-")))
`(let* ((,gstring ,string)
(,gstring-length ,(or length `(length ,gstring))))
,(if (stringp prefix)
;; Constant -- length known at expansion time.
(let ((prefix-length (length prefix)))
(chop prefix prefix-length gstring gstring-length))
;; Other form -- length not known at expansion time.
(let ((gprefix (gensym "PREFIX-"))
(gprefix-length (gensym "PREFIX-LENGTH-")))
`(let* ((,gprefix ,prefix)
(,gprefix-length (length ,gprefix)))
,(chop gprefix gprefix-length gstring gstring-length))))))))
(defmacro after-prefix (prefix string &optional length)
"Similar to cllib:string-beg-with."
`(after-prefix-core string-equal ,prefix ,string ,length))
(defmacro after-prefix-cs (prefix string &optional length)
"Similar to cllib:string-beg-with-cs."
`(after-prefix-core string= ,prefix ,string ,length))
见表格
(if (stringp prefix)
在中间?那是在宏扩展时检查第一个参数,并且根据参数是文字还是符号,它的类型可能是已知的,也可能是未知的。如果类型是一个符号,我们假设我们应该等到运行时重新考虑它是一个指向其他值的变量。
这是表单的扩展(after-prefix foo bar)
:
(LET* ((#:STRING-5340 BAR) (#:STRING-LENGTH-5341 (LENGTH #:STRING-5340)))
(LET* ((#:PREFIX-5342 FOO) (#:PREFIX-LENGTH-5343 (LENGTH #:PREFIX-5342)))
(WHEN
(AND (>= #:STRING-LENGTH-5341 #:PREFIX-LENGTH-5343)
(STRING-EQUAL #:PREFIX-5342 #:STRING-5340 :END2 #:PREFIX-LENGTH-5343))
(SUBSEQ #:STRING-5340 #:PREFIX-LENGTH-5343 #:STRING-LENGTH-5341))))
请注意,变量#:PREFIX-LENGTH-5343
绑定到 的计算长度,FOO
此处绑定到变量#:PREFIX-5342
。
现在查看表单的扩展(after-prefix "foo" bar)
,其中前缀现在是字符串文字:
(LET* ((#:STRING-5463 BAR) (#:STRING-LENGTH-5464 (LENGTH #:STRING-5463)))
(WHEN (AND (>= #:STRING-LENGTH-5464 3) (STRING-EQUAL "foo" #:STRING-5463 :END2 3))
(SUBSEQ #:STRING-5463 3 #:STRING-LENGTH-5464)))
现在没有计算“foo”的长度;它内联为 3。
在这个例子中看起来工作量太大了,但是正如你的问题所认为的那样,能够做这些事情是一种很好的能力。