Что касается "языковой осведомленности о коде", я не видел ничего лучше, чем 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.
В этом примере может показаться слишком большой работой, но способность делать такие вещи - хорошая сила, как показывает ваш вопрос.