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