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- (長さ my-string)))) (defun get-string-without-invalid-last-character (my-string invalid-last-characters) 「文字列の最後の文字が無効な場合、文字列はそれなしで返されます。それ以外の場合は完全に返されます。」 (invalid-last-characters 内の invalid-last-character のループ do (if (equal (get-last-character-string my-string) invalid-last-character) (setf my-string (get-string-without-last-character my-string)))) 私の文字列) (defun transform-code-to-string (stream sub-char numarg) "この関数は、アセンブリ コードを文字列に変換します。 #l は Lisp コードへの変更を示します。#a マークは asm に戻ります。#eマーク終了。 部分的に基づく: http://weitz.de/macros.lisp" (宣言 (sub-char numarg を無視)) (させて* ((無効な最後の文字 (リスト "'" " " "(" ")")) (現在のモード「asm」) (is-there-code-on-this-line nil) (現在のフェーズの「行頭」) (my-string "(list ") (lisp-code-string "")) ;; ストリームをループします。 (ループ for my-char = (coerce (list (read-char stream t nil t)) 'string) する(条件 ((等電流モード「asm」) (状態 ((等しい現在のフェーズ「ハッシュ符号読み取り」) ;; 文字はeですか? ;; はいの場合は完了です。閉じ括弧を修正して戻ります。 (状態 ((equal my-char "e") (変換コードから文字列への戻り値 (concatenate 'string (get-string-without-invalid-last-character) (get-string-without-invalid-last-character my-string 無効な最後の文字) 無効な最後の文字) "))"))) ;; 文字 l ですか? ;; はいの場合、Lisp モードに変更します。 ((等しい my-char "l") ;; ここでLispコードを読み取って評価できますか ;; それを文字列に読み込まずに? (プログ (setf current-mode "Lisp") (setf is-there-code-on-this-line nil) (setf lisp-code-string "") (setf current-phase "beginning-of-line"))) ;; そうでない場合は、印刷エラー。 (t (エラー "in asm mode undefined control character after #")))) ;; は文字 # ですか? ;; はいの場合、ハッシュ記号を既読としてマークします。 ((等しい my-char "#") (setf current-phase "hash-sign-read")) ;; 文字は改行ですか? ((equal my-char (coerce (list #\Newline) 'string)) (プログ (状態 ;; この行にコードはありませんか? ;; true の場合、何も出力しません。 ((この行にあるコードではありません) (setf current-phase "beginning-of-line")) ;; 私たちは命令の中にいますか、それともパラメーターの中にいますか? ;; true の場合、出力 ") ((または (等しい電流フェーズ「内部命令」) (等しい電流位相「内部パラメータ」)) (プログ (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 ")"))))))) ;; 私たちはコメントの中にいますか? ;; はいの場合、何も出力しません。 ((等しい電流フェーズ「内部コメント」) なし) ;; 私たちは列の先頭にいますか? ((等しい現在のフェーズ「ラインの始まり」) (状態 ;; これは行頭のスペースですか? ;; はいの場合、何も出力しません。 ((equal my-char " ") なし) ;; これは ( または ) ではなく、命令の最初の文字ですか? ;; はいの場合、この行にコードがあることをマークし、最初の文字を印刷済みとしてマークし、" と現在の文字を出力します。 ((と (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)))) (t nil))) ;; は文字です。? ;; はいの場合、何も出力せず、コメントを開始します。 ((等しい my-char ";") (setf current-phase "inside-comment")) ;; 文字スペースまたはコンマですか? ((または (equal my-char " ") (等しい my-char ",")) (状態 ;; は文字スペースまたはコンマであり、最後の文字はスペース、コンマ、または開き括弧ではありませんか? ;; はいの場合、" とスペースを出力します。 ((と (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 電流位相「空間内」) (setf my-string (concatenate 'string my-string "\" ")))) (t nil))) ;; 命令が出力され、これがパラメーターの最初の文字ですか? ((と (not (等しい電流位相「内部命令」)) (または (equal (get-last-character-string my-string) " ") (equal (get-last-character-string my-string) ","))) (状態 ;; パラメータ内にいることをマークし、" と現在の文字を出力します。 (t (プログ (setf 電流位相「内部パラメータ」) (setf my-string (concatenate 'string my-string "\"" my-char)))))) ;; それ以外の場合は文字を出力します。 (t (setf my-string (concatenate 'string my-string my-char))))) ((等電流モード「Lisp」) ;; Lisp モードでは、#e または #a に到達するまでテキストを読み取り、それを評価します。 (状態 ((等しい現在のフェーズ「ハッシュ符号読み取り」) (状態 ;; 文字はeですか? ;; はいの場合は完了です。閉じ括弧を修正して戻ります。 ((equal my-char "e") (プログ (concatenate 'string "#a" (eval lisp-code-string) "#e") ; これは何か違うはずです。 (変換コードから文字列への戻り値 (concatenate 'string (get-string-without-invalid-last-character) (get-string-without-invalid-last-character my-string 無効な最後の文字) 無効な最後の文字) "))")))) ;; は文字 a ですか? ;; ある場合は、asm モードに変更します。 ((等しい my-char "a") (プログ (setf current-mode "asm") (setf is-there-code-on-this-line nil) (setf current-phase "beginning-of-line") (concatenate 'string "#a" (eval lisp-code-string) "#e") ; これは何か違うはずです。 ;; それ以外の場合は、評価する Lisp コードに # と文字を追加します。 (t (プログ (setf 電流位相 "") (setf my-string (concatenate 'string lisp-code-string "#" my-char)))))) ;; は文字 # ですか? ;; はいの場合、ハッシュ記号を既読としてマークします。 ((等しい my-char "#") (setf current-phase "hash-sign-read")) ;; それ以外の場合は、評価される Lisp コードに文字を追加します。 (t (setf my-string (concatenate 'string lisp-code-string my-char))))) (t (エラー「無効な現在のモード」)))))) ;;; #a は、カスタム リーダーを開始する入力です。 (set-dispatch-macro-character #\# #\a #'transform-code-to-string))
以下は、Lisp コードを含まないアセンブリ コードの例です。動作します。
(defparameter *example-code-x64* #a inc r10 ; レジスタr10をインクリメントします。 mov r11,r12 ; r12 の値を r11 に格納します。 #e)
そして、これは Lisp コードを内部に含むアセンブリ コードで、失敗します(以下のコンパイル エラーを参照してください)。この例では、Lisp コードはアセンブリ コードの後にありますが、アセンブリ コードと Lisp コードは区切り記号として#a
とを使用して自由に混在させることができます。#l
(defparameter *example-code-x64-with-lisp-fails* #a inc r10 ; レジスタr10をインクリメントします。 mov r11,r12 ; r12 の値を r11 に格納します。 #l (現在の命令のループ (list "inc" "dec") do (ループ for current-arg in (list "r13" "r14" "r15") do (princ (concatenate '文字列 現在の命令 " " 現在の引数 (coerce (list #\Newline) '文字列))))) #e)
上記のコードの Lisp 部分は、カスタム リーダーで評価して、以下のコードと同じ結果を生成する必要があります。
(defparameter *example-code-x64-with-lisp-fails* #a inc r10 ; レジスタr10をインクリメントします。 mov r11,r12 ; r12 の値を r11 に格納します。 株式会社r13 株式会社r14 インク r15 r13 12月 12 月 r14 r15 12月 #e)
しかし、代わりにコンパイルは失敗します:
CL-ユーザー> ; ファイル「/home/user/code/lisp/lisp-asm-reader-for-stackoverflow.lisp」のコンパイル (2014 年 3 月 28 日 10:11:29 PM に書き込み): ; ; エラーをキャッチ: ; COMPILE-FILE 中の読み取りエラー: ; ; 値 -1 は型 (MOD 4611686018427387901) ではありません。 ; ; (フォームの行: 1、列: 0、ファイル位置: 0) ; ; コンパイル ユニットが中止されました ; 致命的な ERROR 状態を 1 つキャッチ ; 1 個の ERROR 状態をキャッチ ; コンパイルは 0:00:00.004 後に中止されました 1 コンパイラー・ノート: /home/user/code/lisp/lisp-asm-reader-for-stackoverflow.lisp:10487 read-error: COMPILE-FILE 中の READ エラー: 値 -1 は型 (MOD 4611686018427387901) ではありません。 (フォームの行: 1、列: 0、ファイル位置: 0) CLユーザー>