我正在用 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-用户>