javascript/C言語/アセンブラを用い、 字句解析、構文解析、インタプリタ、コンパイラのプログラムをスクラッチから作りながら、 「プログラミング言語の作り方」を解説する。
先に処理してほしい部分に指定する、グループ化の丸カッコに対応しよう。
言語仕様として、関数呼び出しとは異なる、優先されるグループ化の丸カッコを追加する。
丸カッコが優先されるか。
//カッコが優先されるか 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