最近の更新 (Recent Changes)

2014-01-01
2013-01-04
2012-12-22
2012-12-15
2012-12-09

Wikiガイド(Guide)

サイドバー (Side Bar)


← 前のページに戻る

3. 一式言語: PL/0の構文の改造と拡張

3.1 一式言語の改善点

さっそく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言語と大きくは変わったようには見えませんが、; や = が私の個人的感覚では書きやすくすっきりしたところが嬉しいところです。

まだ、多くの改善の余地はありますがそれは後に徐々に行っていきましょう。

3.2 一式言語の構文定義

まず新しい言語「一式言語」の構文定義をみてみましょう。

拡張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言語への変換機能を付けるとコンパイラになりますが、最初の改造なのでまずは上の構文定義で簡単に改造場所の変更について説明することとします。

3.2.1 プログラムの最後に'.' を置かなくてもよくする

元のPL/0ではこうでした。


<program>       <block> "." ;

それを、次のように "." を <block>の後からとるだけで実現できます。


<program>       <block>  ;

この変更は簡単ですね。

3.2.2 ';' を行の最後に必ずおくことにする

これにはまず<steatement>の中のbegin、end の定義を直します。

元のPL/0では、区切り文字用の ; の記述がありました。


                 "begin" <statement> {";" <statement> } "end" |

";" の記述を外して以下のようにします。


                 "begin" { <statement> } "end" |

代わりに<statement>の中のそれぞれの定義には、最後に";"を付けるようにします。

例えば代入文なら以下の通りです。


<statement>     [ <ident> "=" <expression> ";" |


<expression>の後に";" が追加したあります。

このような";"を、代入文、関数呼び出し、return文、およびprint文の最後につけるように定義しなおします。

if文、for文、およびwhile文は最後が<statement>で終っているので、重ねて";"を最後に付ける必要はありません。

3.2.3 代入記号を':='ではなく'='に変更する

この修正の対応は、<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);

代入記号が変わるだけでも随分と言語の印象が変わって見えます。そこにどのような意味づけをするかは言語の設計者の意志でしょう。

しかし、手っ取り早く慣れているのはやはり "=" でしょうか。

3.2.4 手続き呼び出しを関数に変え、引数と返り値を付ける

手続きの定義はPL/0言語では次のようでした。


procedure 手続き名; 
begin
   ...

end

これを一式言語では、次のような定義ができるようにします。


function 関数名(引数, 引数);
begin
     ...

end

関数になるので、procedure を function に変更します。 さらに、カッコで囲んだ引数の, カンマで区切られたリストをその後に付けます。

構文定義は以下のように変更しましょう。


PL/0言語

                { "procedure" <ident> ";" <block> ";" }

   ↓


一式言語
                { "function" <ident> "(" [<ident> {"," <ident> }] ")" ";"
                                                        <block> ";" }


3.2.5 関数の呼び出しは単独行で関数を置くか、数式の中で関数として呼び出す

手続きの手続き呼び出しはPL/0言語では次のようでした。


   call 手続き名; 

これを一式言語では、関数の呼び出しを単独行で関数を置くか、数式の中で関数として呼び出すことができるようにします。


単独行
   関数名 (引数, 引数);

数式の中で関数として呼び出す
   f = f + 関数名 (引数) * 2;

構文定義は以下のようにしましょう。


PL/0言語

                | "call" <ident> 

   ↓


一式言語 単独行

                 <ident> "(" [<expresssion> {"," <exporession>}] ")" ";" |


一式言語 数式の中で関数として呼び出す

<factor>        <ident> "(" [<expression> { "," <expression>}] ")" |
                <ident> | <number> | "(" <expression> ")";

単独行の方は簡単ですね。

数式の中の関数としては、<factor>の定義の中で、変数名、数値、あるいはカッコで囲まれた数式と並べて最初に定義しておきます。

最初に<ident>があるので、それが関数名なのか変数名なのかを判定しなければなりません。 そこで、<ident> の次に "(" があれば関数としてパターンマッチするので関数として処理します。 そうでなければ、単独の<ident> は変数名として処理します。

3.2.6 返り値を指定して復帰するreturn文を追加する

これは、<statement>の定義にreturnの構文を追加するだけです。


                 "return" <expression> ";" |

PL/0言語には無かった構文ですが、簡単に追加できてしまいます。


   return 数式 ;

3.2.7 繰り返し処理にfor文を追加する。C言語風の文法とする

これも、<statement>の定義にfor文の構文を追加します。 しかし、return文と違って少し複雑な構文です。


                 "for" <ident> "=" <expression> ";" <condition> ";"
                                <ident> = <exporession> "do" <statement> |

PASCALよりも、C言語のfor文に近い構文にしています。


   for 変数 = 数式 ; 条件文 ; 変数 = 数式 do 
      実行文

for文の初期値と繰り返しの次の値を設定する最初と最後の代入文は、<statement>としたほうが自由度が高くて良かったかもしれません。

3.2.8 コメント文を付けることができる

"//"から行末までをコメントとします。定義は次のようにしています。


<Comment>       "//" <SKIPCR>
                ;

<SKIPCR>は、デカルト言語の組み込み述語で改行コードを超えるまでの入力をすべてスキップします。 これにより、// より後の記述をすべてコメントとします。

この<comment> を、コンパイラの定義のいろいろなところに{}で囲んでおきます。

「3.2 一式言語の構文定義」の項で設定場所を確認してみてください。

3.3 一式言語のソース

一式言語のソースを以下に示します。 前項の構文の変更点と見比べてみてどこが変更されているか確認してみてください。


// 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 一式言語のソースのコンパイル方法

「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


実行できました。

これで、一式言語の説明は終わります。

次章では、純粋関数型言語である二式言語の説明をします。