javascript/C言語/アセンブラを用い、 字句解析、構文解析、インタプリタ、コンパイラのプログラムをスクラッチから作りながら、 「プログラミング言語の作り方」を解説する。
単項演算子の符号に対応しよう。
言語仕様として、単項演算子の符号を追加する。
単項符号がちゃんと計算できるか。 掛け算の後に符号が来ることで、演算子が連続しても動くか。
//単項の符号 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