6

我正在用 Common Lisp 编写自己的 x86-64 汇编程序,它为 x86-64 的子集生成正确的二进制代码。我使用自定义阅读器宏将汇编代码转换为语法树,它按预期工作。

我试图完成的是允许在汇编代码中使用 Lisp 代码,这样我就可以将Lisp 用作我的汇编器的宏语言。#a用作宏调度字符并#e为读者发出结束信号。内部阅读器#l更改为 Lisp 模式并#a返回到汇编模式,#e(为阅读器宏发出信号结束)应该在两种模式下都可以工作。

我不明白的是如何将评估代码的结果输出回输入流(在其余代码之前处理),或者如何让 Lisp 代码输出被再次读取,以便输出可以适当地处理 Lisp 代码(它将是汇编代码)(与汇编代码的其余部分相同)。我怎样才能达到那个目标?

旁注:这是我的第一个阅读器宏,因此可能存在设计缺陷。我认为我将 Lisp 代码读入字符串的方法不一定是最好的方法,如果有一些更短和更惯用的方法来做到这一点。

这是我的阅读器宏的简化版本:

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun get-last-character-string (my-string)
    “此函数返回一个由输入字符串的最后一个字符组成的字符串。”
    (subseq my-string (1- (length my-string))))

  (defun get-string-without-last-character (my-string)
    “这个函数返回一个没有输入字符串最后一个字符的字符串。”
    (subseq my-string 0 (1- (length my-string))))

  (defun get-string-without-invalid-last-character (my-string invalid-last-characters)
    “如果字符串的最后一个字符无效,则返回没有它的字符串,否则完全返回。”
    (在无效最后一个字符中循环无效最后一个字符
          do (if (equal (get-last-character-string my-string) invalid-last-character)
               (setf my-string (get-string-without-last-character my-string))))
    我的字符串)

  (defun 转换代码到字符串 (stream sub-char numarg)
    “此函数将汇编代码转换为字符串。
     #l 标记更改为 Lisp 代码。#a 标记返回到 asm。#e 标记结束。
     部分基于:http://weitz.de/macros.lisp"
    (声明(忽略子字符 numarg))
    (让*
      (((invalid-last-characters (list "'" " " "(" ")"))
       (电流模式“asm”)
       (is-there-code-on-this-line nil)
       (当前阶段“线开始”)
       (我的字符串“(列表”)
       (lisp-code-string ""))
      ;; 循环通过流。
      (loop for my-char = (coerce (list (read-char stream t nil t)) 'string)
            做(条件
                 (((等电流模式“asm”)
                  (条件
                    (((相等的当前阶段“哈希符号读取”)
                     ;; 是字符 e 吗?
                     ;; 如果是,我们就完成了,修复右括号并返回。
                     (条件
                       ((等于我的字符“e”)
                        (从转换代码返回到字符串
                                     (连接'字符串(获取字符串没有无效的最后一个字符
                                                            (获取字符串没有无效的最后一个字符
                                                              我的字符串无效的最后一个字符)
                                                            无效的最后一个字符)“))”)))
                       ;; 是字符 l 吗?
                       ;; 如果是,请更改为 Lisp 模式。
                       ((等于我的字符“l”)
                        ;; 可以在这里阅读和评估 Lisp 代码吗
                        ;; 不将其读入字符串?
                        (预测
                          (setf 当前模式“Lisp”)
                          (setf is-there-code-on-this-line nil)
                          (setf lisp-code-string "")
                          (setf current-phase "beginning-of-line")))
                       ;; 否则,打印错误。
                       (t (error "in asm mode undefined control character after #"))))
                    ;; 是字符#?
                    ;; 如果是,则标记哈希符号已读。
                    ((等于我的字符“#”)
                     (setf 当前阶段“哈希符号读取”))
                    ;; 是字符换行符吗?
                    (((equal my-char (coerce (list #\Newline) 'string))
                     (预测
                       (条件
                         ;; 这条线上有_no_代码吗?
                         ;; 如果为真,则不输出任何内容。
                         ((不是这一行的代码)
                          (setf current-phase "beginning-of-line"))
                         ;; 我们是在指令内部还是在参数内部?
                         ;; 如果为真,则输出")
                         ((或(等电流相“内部指令”)
                              (等电流相位“内部参数”))
                          (预测
                            (setf current-phase "beginning-of-line")
                            (setf is-there-code-on-this-line nil)
                            (setf my-string (concatenate 'string my-string "\")"))))
                         ;; 否则输出)
                         (t (预测
                              (setf current-phase "beginning-of-line")
                              (setf is-there-code-on-this-line nil)
                              (setf my-string (concatenate 'string my-string ")")))))))
                    ;; 我们在评论里吗?
                    ;; 如果是,则不输出任何内容。
                    (((等电流相位“内部评论”)
                     零)
                    ;; 我们在行的开头吗?
                    (((等电流相位“线起点”)
                     (条件
                       ;; 这是行首的空格吗?
                       ;; 如果是,则不输出任何内容。
                       ((等于我的字符“”)
                        零)
                       ;; 这是指令的第一个字符而不是(或)?
                       ;; 如果是,则在此行标记有代码,将第一个字符标记为打印,输出“和当前字符。
                       ((和
                          (not (equal my-char "("))
                          (not (equal my-char ")")))
                        (预测
                          (setf current-phase "inside-instruction")
                          (setf is-there-code-on-this-line t)
                          (setf my-string (concatenate 'string my-string "'(\"" my-char))))
                       (无)))
                    ;; 是性格;?
                    ;; 如果是,不要输出任何东西,开始评论。
                    ((等于我的字符“;”)
                     (setf current-phase "inside-comment"))
                    ;; 是字符空间还是逗号?
                    ((或(等于我的字符“”)
                         (等于我的字符“,”))
                     (条件
                       ;; 是字符空格还是逗号,最后一个字符是_not_空格、逗号还是左括号?
                       ;; 如果是,则输出 " 和空格。
                       ((和
                          (not (equal (get-last-character-string my-string) " "))
                          (not (equal (get-last-character-string my-string) ","))
                          (not (equal (get-last-character-string my-string) "(")))
                        (预测
                          (setf current-phase "in-space")
                          (setf my-string (concatenate 'string my-string "\" "))))
                       (无)))
                    ;; 是否打印指令,这是参数的第一个字符?
                    ((和
                       (不是(等电流相“内部指令”))
                       (或(等于(get-last-character-string my-string)“”)
                           (等于 (get-last-character-string my-string) ",")))
                     (条件
                       ;; 标记我们在参数中,输出“和当前字符。
                       (t (预测
                            (setf current-phase "inside-parameters")
                            (setf my-string (concatenate 'string my-string "\"" my-char))))))
                    ;; 否则输出字符。
                    (t (setf my-string (concatenate 'string my-string my-char)))))
                 (((等电流模式“Lisp”)
                  ;; 在 Lisp 模式下,读取文本直到到达 #e 或 #a 并对其进行评估。
                  (条件
                    (((相等的当前阶段“哈希符号读取”)
                     (条件
                       ;; 是字符 e 吗?
                       ;; 如果是,我们就完成了,修复右括号并返回。
                       ((等于我的字符“e”)
                        (预测
                          (连接 'string "#a" (eval lisp-code-string) "#e") ; 这应该是不同的东西。
                          (从转换代码返回到字符串
                                       (连接'字符串(获取字符串没有无效的最后一个字符
                                                              (获取字符串没有无效的最后一个字符
                                                                我的字符串无效的最后一个字符)
                                                              无效的最后一个字符)“))”))))
                       ;; 是字符 a 吗?
                       ;; 如果是,请更改为 asm 模式。
                       ((等于我的字符“a”)
                        (预测
                          (setf 当前模式“asm”)
                          (setf is-there-code-on-this-line nil)
                          (setf current-phase "beginning-of-line")
                          (连接 'string "#a" (eval lisp-code-string) "#e") ; 这应该是不同的东西。
                          ;; 否则,将 # 和字符添加到要评估的 Lisp 代码中。
                          (t (预测
                               (setf 当前相位 "")
                               (setf my-string (concatenate 'string lisp-code-string "#" my-char))))))
                       ;; 是字符#?
                       ;; 如果是,则标记哈希符号已读。
                       ((等于我的字符“#”)
                        (setf 当前阶段“哈希符号读取”))
                       ;; 否则将字符添加到要评估的 Lisp 代码中。
                       (t (setf my-string (concatenate 'string lisp-code-string my-char)))))
                    (t (error "invalid current mode"))))))

      ;;; #a 是启动自定义阅读器的输入。
      (set-dispatch-macro-character #\# #\a #'transform-code-to-string))

这是一些没有 Lisp 代码的示例汇编代码,可以工作:

(defparameter *example-code-x64*
  #一种
  公司 r10 ; 递增寄存器 r10。
  移动 r11,r12 ; 将 r12 的值存储到 r11 中。
  #e)

这是一些内部带有 Lisp 代码的汇编代码,失败(请参阅下面的编译错误)。在这一点中,Lisp 代码在汇编代码之后,但应允许将汇编代码和 Lisp 代码自由混合,使用#a#l作为分隔符。

(defparameter *example-code-x64-with-lisp-fails*
  #一种
  公司 r10 ; 递增寄存器 r10。
  移动 r11,r12 ; 将 r12 的值存储到 r11 中。
  #l
  (循环中的当前指令(列表“inc”“dec”)
        do (loop for current-arg in (list "r13" "r14" "r15")
                 做(原则(连接'字符串
                                        当前指令
                                        " "
                                        当前参数
                                        (coerce (list #\Newline) 'string)))))
  #e)

上述代码的 Lisp 部分应在自定义阅读器中进行评估,以便它产生与以下代码相同的结果:

(defparameter *example-code-x64-with-lisp-fails*
  #一种
  公司 r10 ; 递增寄存器 r10。
  移动 r11,r12 ; 将 r12 的值存储到 r11 中。
  公司r13
  公司r14
  公司r15
  十二月 r13
  14 月 14 日
  15 月 15 日
  #e)

但是编译失败:

CL-用户>; 编译文件“/home/user/code/lisp/lisp-asm-reader-for-stackoverflow.lisp”(写于 2014 年 3 月 28 日晚上 10:11:29):
;
; 抓到错误:
; 编译文件期间的读取错误:
;   
; 值 -1 不是类型(MOD 4611686018427387901)。
;   
; (表格从第 1 行开始,列:0,文件位置:0)
;
; 编译单元中止
; 捕获 1 个致命的错误情况
; 捕获 1 个错误条件
; 编译在 0:00:00.004 后中止

1 编译说明:

/home/user/code/lisp/lisp-asm-reader-for-stackoverflow.lisp:10487
  读取错误:在编译文件期间读取错误:

  值 -1 不是类型(MOD 4611686018427387901)。

  (表格从第 1 行开始,列:0,文件位置:0)

CL-用户>
4

1 回答 1

6

从 reader 宏中读取 lisp 代码的惯用方法是调用 cl:read。在您的示例中,在使用 #L 后调用 read 将返回其汽车为循环的列表,并且该列表可以传递给 eval。

要收集评估期间创建的输出,您可以绑定 *standard-output*。因此,一种选择是在阅读器宏中使用类似于以下内容的内容:

(let ((lisp-printed-string
       (with-output-to-string (*standard-output*)
         (eval (read stream t t t)))))
  ;; concatenate the lisp printed string onto your 
  ;; hand parsed string here
  )

另一种方法是让用户输入一个返回字符串 {eg (concatenate "bar" "baz")} 的 lisp 表单,并收集 eval 的返回值而不是其打印输出。

于 2014-03-29T01:39:52.433 回答