descartes-src (ソースパッケージ descartes-src-0.26.0.tar.gz) | 2012-09-09 20:57 |
descartes-win (Windows用バイナリパッケージ descartes-win-0.26.0.zip) | 2012-09-09 20:52 |
会話キャラクター: ツンデレ アプリケーション (会話キャラ:ツンデレ v1.0 for Windows) | 2010-04-29 13:41 |
会話キャラクター: 2人の女の子 ダブルキャラクター (会話キャラクター 2人の女の子 ダブルキャラクター 1.0 for Windows) | 2011-10-02 22:23 |
会話キャラクター: Eliza風英語版 (会話キャラ:Eliza風英語版 v1.0 for Windows) | 2010-05-11 01:06 |
会話キャラクター: 猫耳メイド アプリケーション (会話キャラ:猫耳メイド v1.0 for Windows) | 2010-04-27 21:15 |
会話キャラクター: イライザ風日本語版 (会話キャラ:イライザ風日本語版 v1.0 for Windows) | 2010-04-30 21:53 |
経済指標表示プログラム for Windows (経済指標表示プログラム V1.0) | 2011-08-18 22:04 |
ニュースヘッドライン表示プログラム (ニュースヘッドライン表示プログラム V1.0 for Windows) | 2011-08-16 12:31 |
デカルト言語 example (デカルト言語の例題 example-0.7.0.zip) | 2009-03-01 19:47 |
電力状況表示プログラム for Windows (2011年夏版 全国電力供給状況表示プログラム V1.0) | 2011-08-15 13:25 |
さっそくPL/0を改造してみましょう。
まずは、以下のような改造を考えます。
- プログラムの最後に'.' を置かなくてもよくする。 - ';' を行の最後に必ずおくことにする。 - 代入記号を':='ではなく'='に変更する。 - 手続き呼び出しを関数に変え、引数と返り値を付ける。 - 関数の呼び出しは単独行で関数を置くか、数式の中で関数として呼び出す。 - 返り値を指定して復帰するreturn文を追加する。 - 繰り返し処理にfor文を追加する。C言語風の文法とする。 - コメント文を付けることができる。
C言語風のプログラム言語が全盛の時代では、Pascal風の'.'や';'の扱いは間違いやすいので直してしまいましょう。
手続き呼び出しも引数も返り値もないのでは使い難いので関数として実装しなおします。 関数になると再帰呼び出しも可能になりプログラミングの幅が広がりますし。
関数になり返り値が必要になるのでreturn文も追加します。
繰り返し処理がwhile文だけだと寂しいので、C言語風のfor文も追加します。
プログラムにコメントを付けられるようにコメント文も追加します。コメントは、// から後の行末までとします。
この新しい言語のサンプルプログラムを見てみましょう。
// sample program of enhanced PL/0 const x = 2; var a, b, i; function test(); begin a = 10; a = a + x; for i=0; i<10; i=i+1 do begin print "i=", i; end return a; end begin b = test(); print b; end
あまりPL/0言語と大きくは変わったようには見えませんが、; や = が私の個人的感覚では書きやすくすっきりしたところが嬉しいところです。
まだ、多くの改善の余地はありますがそれは後に徐々に行っていきましょう。
まず新しい言語「一式言語」の構文定義をみてみましょう。
拡張BNFで記述したものは以下のようになります。
program = block . block = { comment } [ "const" ident "=" number {"," ident "=" number} ";"] { comment } [ "var" ident {"," ident} ";"] { comment } { "function" ident "(" [ident {"," ident}] ")" ";" block } { comment } statement . statement = { comment } [ ident "=" expression | ident "(" [expression {"," expression}] ")" ";" "return" expression ";" | "begin" { statement } "end" | "if" condition "then" statement ["else" statement] | "while" condition "do" statement| "for" ident "=" expression ";" condition ";" ident = expression "do" statement | "print" [(ident | number | strings) { "," (ident | number | strings)}] ";"]. condition = "odd" expression | expression ("="|"#"|"<"|"<="|">"|">=") expression . expression = [ "+"|"-"] term { ("+"|"-") term}. term = factor {("*"|"/") factor}. factor = ident "(" [expression {"," expression }] ")" | ident | number | "(" expression ")".
前の第1章のもともとのPL/0の構文定義 1. プログラミング言語PL/0 の「1.3 拡張バッカス・ナウア記法(EBNF: Extended Backus–Naur Form)」と見比べてみてください。
次にデカルト言語の構文定義に変換すると以下のようになります。
<program> {<Comment>} <block> {<Comment>} ; <block> [ "const" <ident> "=" <number> {"," <ident> "=" <number>} ";"] {<Comment>} [ "var" <ident> {"," <ident>} ";"] {<Comment>} { "function" <ident> "(" [<ident> {"," <ident> }] ")" ";" <block> ";" } {<Comment>} <statement> ; <statement> {<Comment>} [ <ident> "=" <expression> ";" | <ident> "(" [<expresssion> {"," <exporession>}] ")" ";" | "return" <expression> ";" | "begin" <statement> {";" <statement> } "end" | "if" <condition> "then" <statement> ["else" <statement>] | "for" <ident> "=" <expression> ";" <condition> ";" <ident> = <exporession> "do" <statement> | "while" <condition> "do" <statement> | "print" [(<ident #id> | <number #s #n> | <strings #str>) { "," (<ident #id2> | <number #s2 #n2>| <strings #str2>)}]";"]; <condition> "odd" <expression> | <expression> ("="|"#"|"<"|"<="|">"|">=") <expression> ; <expression> [ "+"|"-"] <term> { ("+"|"-") <term>}; <term> <factor> {("*"|"/") <factor>}; <factor> <ident> "(" [<expression> { "," <expression>}] ")" | <ident> | <number> | "(" <expression> ")"; <ident> <ID>; <number> <NUM>; <strings> <STRINGS>; <Comment> "//" <SKIPCR> ;
これにC言語への変換機能を付けるとコンパイラになりますが、最初の改造なのでまずは上の構文定義で簡単に改造場所の変更について説明することとします。
元のPL/0ではこうでした。
<program> <block> "." ;
それを、次のように "." を <block>の後からとるだけで実現できます。
<program> <block> ;
この変更は簡単ですね。
これにはまず<steatement>の中のbegin、end の定義を直します。
元のPL/0では、区切り文字用の ; の記述がありました。
"begin" <statement> {";" <statement> } "end" |
";" の記述を外して以下のようにします。
"begin" { <statement> } "end" |
代わりに<statement>の中のそれぞれの定義には、最後に";"を付けるようにします。
例えば代入文なら以下の通りです。
<statement> [ <ident> "=" <expression> ";" |
<expression>の後に";" が追加したあります。
このような";"を、代入文、関数呼び出し、return文、およびprint文の最後につけるように定義しなおします。
if文、for文、およびwhile文は最後が<statement>で終っているので、重ねて";"を最後に付ける必要はありません。
この修正の対応は、<statement>の中の代入文の定義を修正します。
PL/0言語 <statement> [ <ident> ":=" <expression> | ↓ 一式言語 <statement> [ <ident> "=" <expression> ";" |
中央あたりにある ":=" を "=" に変換するだけですから簡単ですね。
この記号を"="ではなく自分の作る言語では違う記号にしたいというような場合は、例えば"<-" やさらに "←" や "≡" のようなASCII以外の記号にすることも可能です。
代入文の例(仮言語) ab <- a + (2 * b); z : x + u; df ← x * y; dt → 3.14 * r * r; mt ≡ h * (b + 100);
代入記号が変わるだけでも随分と言語の印象が変わって見えます。そこにどのような意味づけをするかは言語の設計者の意志でしょう。
しかし、手っ取り早く慣れているのはやはり "=" でしょうか。
手続きの定義はPL/0言語では次のようでした。
procedure 手続き名; begin ... end
これを一式言語では、次のような定義ができるようにします。
function 関数名(引数, 引数); begin ... end
関数になるので、procedure を function に変更します。 さらに、カッコで囲んだ引数の, カンマで区切られたリストをその後に付けます。
構文定義は以下のように変更しましょう。
PL/0言語 { "procedure" <ident> ";" <block> ";" } ↓ 一式言語 { "function" <ident> "(" [<ident> {"," <ident> }] ")" ";" <block> ";" }
手続きの手続き呼び出しはPL/0言語では次のようでした。
call 手続き名;
これを一式言語では、関数の呼び出しを単独行で関数を置くか、数式の中で関数として呼び出すことができるようにします。
単独行 関数名 (引数, 引数); 数式の中で関数として呼び出す f = f + 関数名 (引数) * 2;
構文定義は以下のようにしましょう。
PL/0言語 | "call" <ident> ↓ 一式言語 単独行 <ident> "(" [<expresssion> {"," <exporession>}] ")" ";" | 一式言語 数式の中で関数として呼び出す <factor> <ident> "(" [<expression> { "," <expression>}] ")" | <ident> | <number> | "(" <expression> ")";
単独行の方は簡単ですね。
数式の中の関数としては、<factor>の定義の中で、変数名、数値、あるいはカッコで囲まれた数式と並べて最初に定義しておきます。
最初に<ident>があるので、それが関数名なのか変数名なのかを判定しなければなりません。 そこで、<ident> の次に "(" があれば関数としてパターンマッチするので関数として処理します。 そうでなければ、単独の<ident> は変数名として処理します。
これは、<statement>の定義にreturnの構文を追加するだけです。
"return" <expression> ";" |
PL/0言語には無かった構文ですが、簡単に追加できてしまいます。
return 数式 ;
これも、<statement>の定義にfor文の構文を追加します。 しかし、return文と違って少し複雑な構文です。
"for" <ident> "=" <expression> ";" <condition> ";" <ident> = <exporession> "do" <statement> |
PASCALよりも、C言語のfor文に近い構文にしています。
for 変数 = 数式 ; 条件文 ; 変数 = 数式 do 実行文
for文の初期値と繰り返しの次の値を設定する最初と最後の代入文は、<statement>としたほうが自由度が高くて良かったかもしれません。
"//"から行末までをコメントとします。定義は次のようにしています。
<Comment> "//" <SKIPCR> ;
<SKIPCR>は、デカルト言語の組み込み述語で改行コードを超えるまでの入力をすべてスキップします。 これにより、// より後の記述をすべてコメントとします。
この<comment> を、コンパイラの定義のいろいろなところに{}で囲んでおきます。
「3.2 一式言語の構文定義」の項で設定場所を確認してみてください。
一式言語のソースを以下に示します。 前項の構文の変更点と見比べてみてどこが変更されているか確認してみてください。
// Enhanced PL/0 /* * Deletion of the last '.' * ';' is not put up to the delimitation of the line. ';' is put up at the end of the line. * Procedure was changed into function, and the return value is enabled. The argument is added to function. Syntax of call is deleted. The syntax of the call of function is added. * It changed from ':=' to '='. * "return, for, and else" are added to the syntax. * The syntax of the comment is added. */ <program> <setVar Line 1> <print "#include <stdio.h>"> <print "#include <stdlib.h>"> <print> <print "int main() "> <print "{"> {<Comment>} <block> <print "}"> {<Comment>} ; <block> [ "const" <emsg "constant name."> <printf "const int "> <ident> <emsg "constant definition."> "=" <printf " = "> <number> {"," <printf ", "> <ident> <emsg "constant definition."> "=" <printf " = "> <number> } <emsg "';' is missing."> ";" <print ";"> ] {<Comment>} [ "var" <emsg "variable name."> <printf "int "> <ident> <emsg "variable definition."> {"," <printf ", "> <ident> <emsg "variable definition."> } ";" <print ";"> ] {<Comment>} { "function" <emsg "function name."> <printf "int "> <ident> <emsg "function definition."> "(" <printf "("> [<ident #id1> <printf "int " #id1> { "," <printf ", "> <ident #id2> <printf "int " #id2> } ] ")" <print ")"> ";" <print "{"> <block> <print "}"> } {<Comment>} <statement> ; <statement> <SKIPSPACE> ::sys <line #Line> <setVar Line #Line> {<Comment>} ( <ident #var> "=" <emsg "expression."> <printf #var " = "> <expression> <print ";"> ";" <emsg "syntax error."> | <ident #func> <emsg "syntax error."> "(" <emsg "function call."> <printf #func "("> [<expression> {"," <expression>}] ")" <printf ")"> <print ";"> ";" <emsg "syntax error."> | "return" <printf "return "> <expression> <print ";"> ";" <emsg "syntax error."> | "begin" <print "{"> { <statement> } "end" <print "}"> | "if" <emsg "if sentence."> <printf "if ("> <condition> "then" <print ") {"> <statement> ["else" <print "} else {"> <statement> ] <print "}"> | "while" <emsg "while sentence."> <printf "while ("> <condition> "do" <print ") {"> <statement> <print "}"> | "for" <emsg "for sentence."> <printf "for ("> <ident> "=" <printf " = "> <expression> ";" <printf ";"> <condition> ";" <printf ";"> <ident> "=" <printf " = "> <expression> "do" <printf ") {"> <statement> <print "}"> | "print" <emsg "print sentence. "> [ ( <strings #str> <printf 'printf("%s ", "'#str'" );'><print> | <printf 'printf("%d ", '> <expression> <print ');'> ) { "," ( <strings #str2> <printf 'printf("%s ", "'#str2'" );'><print> | <printf 'printf("%d ", '> <expression> <print ');'> ) } ] <print 'printf("\n");'> ";" <emsg "syntax error."> ) ; <condition> "odd" <emsg "odd syntax."> <printf "(("> <expression> <printf ") & 1)"> | <expression> ("=" <printf " == "> |"#" <printf " != "> |"<=" <printf " <= "> |"<" <printf " < "> |">=" <printf " >= "> |">" <printf " > "> ) <expression> ; <expression> [ "+" <printf "+"> |"-" <printf "-"> ] <term> { ("+" <printf "+"> |"-" <printf "-"> ) <term>}; <term> <factor> { ("*" <printf "*"> |"/" <printf "/"> ) <factor>}; <factor> <ident #f> "(" <printf #f "("> [ <expression> {"," <printf ", "> <expression>}] ")" <printf ")"> | <ident> | <number> | "(" <printf "("> <expression> ")" <printf ")"> ; <ident #id> <ID #id> <reserved #id> ; <ident> <ident #id> <printf #id> ; <number #sign #n> ["+" <is #sign ""> | "-" <is #sign "-"> | <is #sign ""> ] <NUM #n> ; <number> <number #sign #n> <printf #sign #n> ; <strings #str> <STRINGS #str> ; <strings> <strings #str> <printf #str> ; <Comment> "//" <SKIPCR> ; <reserved #id> <not ::sys <member #id (var begin end if then while for do function return print odd)>> ; <emsg #x> <x <getVar #Line Line> <warn "error : " #Line ":" #x> <exit>> ; <emsg2 #x> <getVar #Line Line> <warn "error : " #Line ":" #x> <exit> ; <compile> ::sys<args #x> ::sys<nth #inputfile #x 1> ::sys<suffix #outputfile #inputfile c> <print inputfile #inputfile> <print outputfile #outputfile> ::sys<openw #outputfile ::sys<openr #inputfile <program>>> ; ? <compile>;
「3.3 一式言語のソース」で示したソースを、"pl01.dec"という名前で保存しましょう。
そして次に示すサンプルのPL/0プログラムは、"a.pl0"という名前で保存しておきます。
// sample program of enhanced PL/0 const x = 2; var a, b, i; function test(); begin a = 10; a = a + x; for i=0; i<10; i=i+1 do begin print "i=", i; end return a; end begin b = test(); print b; end
どうでしょうか。サンプル・プログラムは新言語らしくなっているでしょうか?
では、コンパイルを実行してみましょう。
$ descartes pl01.dec a.pl0 inputfile a.pl0 outputfile a.c result -- <compile> -- true
"a.pl0"を引数に指定すると、自動的にa.cというC言語のソースが出力されます。
"a.c"の中を見てみましょう。
#include <stdio.h> #include <stdlib.h> int main() { const int x = 2; int a, b, i; int test() { { a = 10; a = a+x; for (i = 0;i < 10;i = i+1) {{ printf("%s ", "i=" ); printf("%d ", i); printf("\n"); } } return a; } } { b = test(); printf("%d ", b); printf("\n"); } }
インデントを直してみます。indentコマンドが使える場合はそれを使うとよいでしょう。
#include <stdio.h> #include <stdlib.h> int main() { const int x = 2; int a, b, i; int test() { { a = 10; a = a + x; for (i = 0; i < 10; i = i + 1) { { printf("%s ", "i="); printf("%d ", i); printf("\n"); } } return a; } } { b = test(); printf("%d ", b); printf("\n"); } }
このC言語のソースをGCCを使ってコンパイルして実行してみましょう。 インデントは直さなくてもコンパイルはできます。
$ gcc a.c -o a.out $ ./a.out i= 0 i= 1 i= 2 i= 3 i= 4 i= 5 i= 6 i= 7 i= 8 i= 9 12
実行できました。
これで、一式言語の説明は終わります。
次章では、純粋関数型言語である二式言語の説明をします。
[ページ情報]
更新日時: 2012-11-20 22:25:06, 更新者: hniwa
[ライセンス]
クリエイティブ・コモンズ 2.1 表示
[権限]
表示:無制限, 編集:ログインユーザ, 削除/設定:ログインユーザ