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- (長さ 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ユーザー>
4

1 に答える 1

6

リーダー マクロ内から Lisp コードを読み取る慣用的な方法は、cl:read を呼び出すことです。あなたの例では、 #L を消費した後に read を呼び出すと、car がループしているリストが返され、そのリストを 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
  )

別の方法として、ユーザーに文字列を返す Lisp フォームを入力させて {eg (concatenate "bar" "baz")} 、印刷出力の代わりに eval の戻り値を収集する方法があります。

于 2014-03-29T01:39:52.433 に答える