プログラミング言語の作り方

javascript/C言語/アセンブラを用い、 字句解析、構文解析、インタプリタ、コンパイラのプログラムをスクラッチから作りながら、 「プログラミング言語の作り方」を解説する。

符号対応
javascript版プログラミング言語の作り方(コンパイラ開発)

単項演算子の符号に対応しよう。

ページメニュー

現時点の言語仕様

言語仕様として、単項演算子の符号を追加する。

自作言語で記述されたソース

単項符号がちゃんと計算できるか。 掛け算の後に符号が来ることで、演算子が連続しても動くか。

//単項の符号
a = -2 * 3;
print("a = -2 *  3=",a);
//単項の符号2つ
b = -2 * -3;
print("b = -2 * -3=",b);

変更点

インタプリタ作成時にすでにparser.jsが変更されており、 それをそのまま使うため、コンパイラ作成にあたって 変更箇所はない。

コンパイラの実行

compiler.jsを実行する。

まず、tokensとastが意図どおりに分割されているか、念のため表示しているので、確認しよう。

問題なく、自作プログラミング言語を実装できていることが分かる。

$ ./compiler.js
処理前tokens =[
  'a',              '=',     '-',
  '2',              '*',     '3',
  ';',              'print', '(',
  '"a = -2 *  3="', ',',     'a',
  ')',              ';',     'b',
  '=',              '-',     '2',
  '*',              '-',     '3',
  ';',              'print', '(',
  '"b = -2 * -3="', ',',     'b',
  ')',              ';'
]
抽象構文木ast={
  left: {
    left: {
      left: {
        left: {
          left: 'a',
          op: '=',
          right: {
            left: { left: '0', op: '-', right: '1' },
            op: '*',
            right: { left: '2', op: '*', right: '3' }
          }
        },
        op: ';',
        right: {
          left: 'print',
          op: '()',
          right: { left: '"a = -2 *  3="', op: ',', right: 'a' }
        }
      },
      op: ';',
      right: {
        left: 'b',
        op: '=',
        right: {
          left: { left: '0', op: '-', right: '1' },
          op: '*',
          right: {
            left: '2',
            op: '*',
            right: {
              left: { left: '0', op: '-', right: '1' },
              op: '*',
              right: '3'
            }
          }
        }
      }
    },
    op: ';',
    right: {
      left: 'print',
      op: '()',
      right: { left: '"b = -2 * -3="', op: ',', right: 'b' }
    }
  },
  op: ';',
  right: undefined
}

a = -2 *  3=-6
b = -2 * -3=6

コンパイラが出力したアセンブラ

コンパイラが出力したアセンブラ(source.s)を載せておく。

$ cat source.s

#intel表記を使う
.intel_syntax noprefix

#エントリポイント
.global _start
_start:

#.n1で定義されたリテラル数値取得
movdqu xmm0,.n1

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#.n2で定義されたリテラル数値取得
movdqu xmm0,.n2

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm1,[rsp]
add rsp,16

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm0,[rsp]
add rsp,16

#引き算xmm0(数値left), xmm1(数値right)
subsd xmm0,xmm1

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#.n3で定義されたリテラル数値取得
movdqu xmm0,.n3

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#.n4で定義されたリテラル数値取得
movdqu xmm0,.n4

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm1,[rsp]
add rsp,16

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm0,[rsp]
add rsp,16

#掛け算xmm0(数値left), xmm1(数値right)
mulsd xmm0,xmm1

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm1,[rsp]
add rsp,16

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm0,[rsp]
add rsp,16

#掛け算xmm0(数値left), xmm1(数値right)
mulsd xmm0,xmm1

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm0,[rsp]
add rsp,16

#代入演算子 .ga=xmm0(数値)
movdqu .ga,xmm0

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#グローバル変数.gaを取得
movdqu xmm0,[.ga]

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#.s1で定義されたリテラル文字列取得
lea r10,[.s1]
push r10

#アライメント8バイト伸ばす
sub rsp,8

#アライメント8バイト戻し
add rsp,8
pop rdi

#文字列をprintfするときは、rax=0でないとエラー
mov rax,0

#文字列表示
call printf

#printfのフォーマット%g(数値用)
lea rdi,[.fmtg]

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm0,[rsp]
add rsp,16

#数値をprintfするときは、rax=1でないとエラー
mov rax,1

#数値表示
call printf

#改行コード.newline表示
lea rdi,[.newline]
call printf

#.n5で定義されたリテラル数値取得
movdqu xmm0,.n5

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#.n6で定義されたリテラル数値取得
movdqu xmm0,.n6

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm1,[rsp]
add rsp,16

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm0,[rsp]
add rsp,16

#引き算xmm0(数値left), xmm1(数値right)
subsd xmm0,xmm1

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#.n7で定義されたリテラル数値取得
movdqu xmm0,.n7

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#.n8で定義されたリテラル数値取得
movdqu xmm0,.n8

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#.n9で定義されたリテラル数値取得
movdqu xmm0,.n9

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm1,[rsp]
add rsp,16

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm0,[rsp]
add rsp,16

#引き算xmm0(数値left), xmm1(数値right)
subsd xmm0,xmm1

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#.n10で定義されたリテラル数値取得
movdqu xmm0,.n10

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm1,[rsp]
add rsp,16

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm0,[rsp]
add rsp,16

#掛け算xmm0(数値left), xmm1(数値right)
mulsd xmm0,xmm1

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm1,[rsp]
add rsp,16

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm0,[rsp]
add rsp,16

#掛け算xmm0(数値left), xmm1(数値right)
mulsd xmm0,xmm1

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm1,[rsp]
add rsp,16

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm0,[rsp]
add rsp,16

#掛け算xmm0(数値left), xmm1(数値right)
mulsd xmm0,xmm1

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm0,[rsp]
add rsp,16

#代入演算子 .gb=xmm0(数値)
movdqu .gb,xmm0

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#グローバル変数.gbを取得
movdqu xmm0,[.gb]

#push スタック伸ばしてxmmを保存
sub rsp,16
movdqu [rsp],xmm0

#.s2で定義されたリテラル文字列取得
lea r10,[.s2]
push r10

#アライメント8バイト伸ばす
sub rsp,8

#アライメント8バイト戻し
add rsp,8
pop rdi

#文字列をprintfするときは、rax=0でないとエラー
mov rax,0

#文字列表示
call printf

#printfのフォーマット%g(数値用)
lea rdi,[.fmtg]

#popx スタックからxmmへコピー後、スタック縮める
movdqu xmm0,[rsp]
add rsp,16

#数値をprintfするときは、rax=1でないとエラー
mov rax,1

#数値表示
call printf

#改行コード.newline表示
lea rdi,[.newline]
call printf

#exit(0)で終了
end:
mov rdi,0
call exit

#printfで使う文字列リテラルを定義
.newline: .string "\n"
.fmtg: .string "%g"

#数値リテラル.n連番で定義
.n1: .double 0
.n2: .double 1
.n3: .double 2
.n4: .double 3
.n5: .double 0
.n6: .double 1
.n7: .double 2
.n8: .double 0
.n9: .double 1
.n10: .double 3

#文字列リテラル.s連番で定義
.s1: .string "a = -2 *  3="
.s2: .string "b = -2 * -3="

#グローバル変数.g変数名のエリアを8 or 16バイト確保
.comm .ga, 16
.comm .gb, 16


このページの目次へ戻るサイトの最上位へ戻る