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

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

グループ化の丸カッコ対応
javascript版プログラミング言語の作り方(コンパイラ開発)

先に処理してほしい部分に指定する、グループ化の丸カッコに対応しよう。

ページメニュー

現時点の言語仕様

言語仕様として、関数呼び出しとは異なる、優先されるグループ化の丸カッコを追加する。

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

丸カッコが優先されるか。

//カッコが優先されるか
a=(5-2)*3;
print("a = (5-2)*3=",a);

//カッコが優先されるか
b = 2 * (3 - 5);
print("b = 2 * (3 - 5)=",b);

//単項の符号と丸カッコ
c = -(2+1) * 3;
print("c = -(2+1) * 3=",c);

変更点

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

コンパイラの実行

compiler.jsを実行する。

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

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

 ./compiler.js
処理前tokens =[
  'a',              '=',     '(',
  '5',              '-',     '2',
  ')',              '*',     '3',
  ';',              'print', '(',
  '"a = (5-2)*3="', ',',     'a',
  ')',              ';',     'b',
  '=',              '2',     '*',
  '(',              '3',     '-',
  '5',              ')',     ';',
  'print',          '(',     '"b = 2 * (3 - 5)="',
  ',',              'b',     ')',
  ';',              'c',     '=',
  '-',              '(',     '2',
  '+',              '1',     ')',
  '*',              '3',     ';',
  'print',          '(',     '"c = -(2+1) * 3="',
  ',',              'c',     ')',
  ';'
]



抽象構文木ast={
  left: {
    left: {
      left: {
        left: {
          left: {
            left: {
              left: 'a',
              op: '=',
              right: {
                left: { left: '5', op: '-', right: '2' },
                op: '*',
                right: '3'
              }
            },
            op: ';',
            right: {
              left: 'print',
              op: '()',
              right: { left: '"a = (5-2)*3="', op: ',', right: 'a' }
            }
          },
          op: ';',
          right: {
            left: 'b',
            op: '=',
            right: {
              left: '2',
              op: '*',
              right: { left: '3', op: '-', right: '5' }
            }
          }
        },
        op: ';',
        right: {
          left: 'print',
          op: '()',
          right: { left: '"b = 2 * (3 - 5)="', op: ',', right: 'b' }
        }
      },
      op: ';',
      right: {
        left: 'c',
        op: '=',
        right: {
          left: { left: '0', op: '-', right: '1' },
          op: '*',
          right: {
            left: { left: '2', op: '+', right: '1' },
            op: '*',
            right: '3'
          }
        }
      }
    },
    op: ';',
    right: {
      left: 'print',
      op: '()',
      right: { left: '"c = -(2+1) * 3="', op: ',', right: 'c' }
    }
  },
  op: ';',
  right: undefined
}

a = (5-2)*3=9
b = 2 * (3 - 5)=-4
c = -(2+1) * 3=-9

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

コンパイラが出力したアセンブラ(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

#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

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

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

#.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

#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

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

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

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

#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

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

#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)
addsd xmm0,xmm1

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

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

#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

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

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

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

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

#.s3で定義されたリテラル文字列取得
lea r10,[.s3]
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 5
.n2: .double 2
.n3: .double 3
.n4: .double 2
.n5: .double 3
.n6: .double 5
.n7: .double 0
.n8: .double 1
.n9: .double 2
.n10: .double 1
.n11: .double 3

#文字列リテラル.s連番で定義
.s1: .string "a = (5-2)*3="
.s2: .string "b = 2 * (3 - 5)="
.s3: .string "c = -(2+1) * 3="

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

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