最近の更新 (Recent Changes)

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

Wikiガイド(Guide)

サイドバー (Side Bar)

--
← 前のページに戻る

5.2 Closure Basicの構文の詳細

5.2.1 program 構文定義

では、順に構文を見ていきましょう。


<ClosureBasic>
        <program>
        ;

最初の構文宣言は<ClosureBasic>は<program>であることを示しています。 この宣言は1対1の定義で一見無意味とも思えますが、決して無駄な定義ではありません。

実際のコンパイラにする場合にはこの処理の中で、さらに初期化処理やプログラムが中断した場合のエラー処理およびSTOP命令の生成などの処理を行います。

このように最初の構文のエントリの定義を行えば、初期化、エラーおよび終了処理をまとめて行えます。このような定義の仕方は、デカルト言語でコンパイラを作るときのコツと言えます。

ソースの説明を後に行いますが、そこでこのあたりの実際の処理を説明しましょう。


<program>
        {<sentence> {":" <sentence>} }
        ;

次に<program>は、<sentence>を":"をいくつか繋いだものを、さらに繋げたものです。

{}の括弧は0回以上の繰り返しを意味します。

<sentence>は、代入文やif文などの一つ一つのプログラムの処理の塊に相当します。

この定義により、複数行のプログラムや、一行に":"で括られた複数処理のプログラムを可能としています。 例えば以下のようなものです。


a = "hello!"
b = 1000: c = 10000: for i = 0 to 10: print i: next
d = b + c

5.2.2 sentence 構文定義

次に<sentence>の定義を見てみましょう。


<sentence>
        (<If> | <For> | <While> | <Print> | <InputNum> | <Input>
                | <Return> | <DefArray> | <Gosub>
                | <Assignment> | <Comment> )
        ;

<sentence>は、if文、for文、while文、print文、input#文、input文、return文、dim文(<DefArray>に相当)、gosub(またはcall)文、 代入文(<Assignment>に相当)、およびコメント文(<Comment>に相当)のいずれかで構成されます。

"|"は、論理和(OR)を示し、まず<if>文に致するか試し、そうでなければ<For>文かどうかを調べ、というように順に処理します。 どの構文に相当するのか試しながら、合致すれば対応する処理を実行するのです。

次にそれぞれの構文の処理について見ていきます。

5.2.3 if 構文定義

最初にif文の定義です。


<If>
        "if" <Conditional> "then"
        <program>
        { "else" "if" <Conditional> "then"
          <program>}
        [ "else"
          <program>]
        "end"
        ;

<Conditional>には、条件判定式を書きます。 この<Conditioanl>については、後に構文定義の説明をしましょう。

if文の構文定義で注意しなければならないのは、else節です。

CやJAVAのような、if文の処理が{}のようなブロックを取れる場合は簡単です。


  if (co == 0) {
    ci = 0;
    co = -1;
  } else if (co == 1) {
    ci = -1;
    co = -2;
  } else if (co == 2) {
    ci = -2;
    co = -3;
  }

ClosureBasicでは、if ~ then ~ else ~ endのようなthenやelseの後ろに複数の文を書くことを許しているため、 単純にif文の構文定義をしてしまうと上と同じプログラムが、以下のようなことになってしまいます。


  if co == 0 then
    ci = 0;
    co = -1;
  else if co == 1 then
    ci = -1;
    co = -2;
  else if co == 2 then
    ci = -2;
    co = -3;
  end:end:end

問題なのは、最後のend文です。if ~ then ~ else ~ endが必ず対応しなければならないため、 最後のend文はif文と同じ数だけ必ず書かなければならなくなります。

それでは、面倒で間違えやすいし格好も悪い気がしますね。

そこで、else if節を新設し専用の構文定義を導入しています。


        { "else" "if" <Conditional> "then"
          <program>}

一つのif文の中に複数のelse if節を許す(または、無くてもよい)ために、0回以上の繰り返しを表す{}で括っています。

そして、最後にelse節だけを処理する構文が定義されます。


        [ "else"
          <program>]
        "end"

else節は最後に1つあるか、あるいは無くてもよいので、省略可能なことを表す[]で括っています。

これらの定義により先に示したif文の例が次に示すように書けるようになります。


  if co == 0 then
    ci = 0;
    co = -1;
  else if co == 1 then
    ci = -1;
    co = -2;
  else if co == 2 then
    ci = -2;
    co = -3;
  end

5.2.4 for 構文定義

for文は、一定回数をシーケンシャルに繰り返すものです。


<For>
        "for" <VARIABLE> "=" <Expression> "to" <Expression>
        <program>
        "next"
        ;

for文については、構文は素直に定義できます。<VARIABLE>は変数です。 <Expression>は数式であり初期値と終了値を指定します。

しかし、これが実際に動くコードを出力にしようとするとかなりややこしいことになります。 C言語やJAVAのようなfor(;;)と同様な構文にするとすっきりするのですが、ここではあくまでBASIC言語らしい構文のためにfor文を残しています。

5.2.5 while 構文定義

for文だけでは、繰り返し処理(ループ)に不便なので、while構文もあります。


<While>
        "while" <Conditional> "do"
        <program>
        "end"
        ;

構文の定義もそのまま素直に書けます。 さらに、コード出力でもfor文ほど難しくなく実行効率も良い構文です。

5.2.6 print 構文定義

print文は、複数の要素を","カンマで区切って表示できます。 また、最後に";"セミコロンがあると、改行しないで出力します。 無い場合には、一連の指定された項目を出力後に改行します。 引数のないprint文の場合は、改行のみ行われます。


<Print>
        "print" 
        (
         <CR>
         |
         <Displayitem> {"," <Displayitem>} [";"]
        )
        ;

<Displayitem>
        <Exp_closure> | <Expression> | <Exp_strings>
        ;

<CR>は、改行コードを意味します。 "print"の後に改行しかない場合の定義をしています。

出力する項目がある場合は、項目を<Displayitem>として定義します。 表示できる項目は、クロージャ、数式、または文字列式です。

最後の;は、省略可能な";"セミコロンの定義です。

5.2.7 input#, input 構文定義

input#は、数値の入力であり、inputは文字列の入力を行います。

役割は異なるのですが構文的には、どちらも違いはありません。


<InputNum>
        "input#" [<STRINGS> "," ] <VARIABLE>
        ;

<Input>
        "input" [<STRINGS> "," ] <VARIABLE>
        ;

<STRINGS>は、入力する前にプロンプトされる文字列です。 []で囲まれているので、なくても構いません。

<VARIABLE>で指定された変数に入力値が設定されることになります。

5.2.8 gosub, call, return 構文定義

gosubまたは、callは関数(クロージャ)を呼び出す構文です。 gosubとcallはまったく同じ機能であり、引数として評価の結果として関数名となる式(Expression)を指定できます。


<Gosub>
        ("gosub" | "call") <Expression>
        ;

returnは、関数の返り値を式(Expression)として受けます。


<Return>
        "return" <Expression>
        ;

5.2.9 dim 構文定義

dimは配列を定義します。


<DefArray>
        "dim" <ID> "[" <NUM> "]" { "," <ID> "[" <NUM> "]"}
        ;

","カンマで区切って複数の配列の定義もできるように定義しているのがわかるでしょうか。

<ID>は、配列名を識別するIDを意味します。配列の長さは<NUM>で数値を指定します。

配列の長さが式では指定できないことに注意してください。 これは、現状ではコンパイル時に静的に配列サイズを決めてしまう必要があるためです。

改良して動的に配列の確保を行うことも可能なのですが複雑になるので今回のClosure Basicではこのようになっています。 改善点の一つとしてとっておきましょう。

5.2.10 fun 構文定義

fun構文は、関数(クロージャ)の定義を行う構文です。


<Fun>
        "{" "fun" "(" <FunParm> ")" <program> "}"
        ;

<FunParm>
        [<VARIABLE> {"," <VARIABLE>}]
        ;

funは、元来のBASICの文法には含まれないので、思い切って他のプログラム言語とは異なる構文にしました。

本来ならば、fun(引数) { プログラム ... } のような定義が他のプログラム言語では普通です。

しかし、ここでは関数がクロージャであることを感覚的に表すために、全体をカッコで括った{fun(引数) プログラム... }としています。

自分でプログラム言語を設計する場合には、このように構文を自由に自分好みに設定できるのがなんとなく嬉しいですね。 新言語設計の醍醐味です。

FunParmは、関数(クロージャ)の引数を定義するものです。

','で区切られた複数の引数をとる構文になっています。

5.2.11 条件判定 構文定義

if分やwhile文の条件判定式の構文定義です。


<Conditional>
        <cond_or>
        ;

<cond_or>
        <cond_and> { "or" <cond_and>}
        ;

<cond_and>
        <cond> { "and" <cond> }
        ;

<cond>
        "(" <Conditional> ")" | <Compare>
        ;

<Compare>
        <Expression> ( "==" <Expression> | "=" <Expression> |
        "!=" <Expression> | "<>" <Expression> | ">="<Expression> |
        ">" <Expression> | "<=" <Expression> | "<" <Expression> )
        ;

一番下にある<Compare>から上に見ていくと判りやすいでしょう。

<Compare>は、数式同士の比較式を定義しています。 大小比較や一致・不一致を単純に判定する式について定義します。 ">"や"<="のような大小比較演算子の処理を行います。

次にその上の<cond>は、条件判定式をカッコで括ったものか、あるいは<Compare>の比較式を定義します。 これにより、条件判定式をカッコで括ったものを処理の対象とできるようになるのです。

その上の<cond_and>は、and演算子の処理を定義します。 <cond>をandで繋いだものが、<cond_and>です。

その上の<cond_or>は、or演算子の処理を定義します。 and演算子で繋がれた式を、orで繋いだものが、<cond_or>です。

<cond_and>と<cond_or>がこの順番になっているのは、and演算子のほうがor演算子よりも優先度が高いためです。 and演算子で繋がれた判定式を、or演算子でさらに繋ぐ格好になるため、and演算子のほうが優先されるのです。

条件判定<Conditional>は、<cond_or>と同じです。

5.2.12 文字列式 構文定義

文字列の演算のための構文を定義します。 ここでは、文字列に対しては、あまり難しい演算は定義していません。


<Exp_strings>
        <StringsTerm> { "+" <StringsTerm> }
        ;


<StringsTerm>
        (<VARIABLE> | <STRINGS>)
        ;

<StringsTerm>で、文字列は変数に入っているか、"~"で括られた文字列定数であることを定義します。 変数が数値であった場合には、その数値を文字列に変換した文字列が返ります。

<Exp_strings>が文字列の演算を定義していて、+演算子によって文字列の連結ができるようになっています。

本来は文字列に対する操作はもっと充実させることができます。 拡張方法については、ここの定義を基に考えてみてください。

5.2.13 演算式 構文定義

演算式の構文定義について説明します。

Closure Basicの演算子には以下のようなものがあります。


優先度高↑

 単項+ 単項ー
 *   /
 +   -

優先度低↑

これらの演算子および括弧により組みたてられる演算式の定義を以下に示しましょう。


<Expression>
        <expradd>
        ;

<expradd>
        <exprmul> {"+" <exprmul> | "-" <exprmul>}
        ;

<exprmul>
        <exprID> {"*" <exprID> | "/" <exprID>}
        ;

<exprID>
        ("+" <exprterm> | "-" <exprterm> | <exprterm>)
        ;

<exprterm>
        <exprterm2> {"(" <Parm> ")"}
        ;

<Parm>
        [<Expression> {"," <Expression>]]
        ;

<exprterm2>
        ("(" <Expression> ")" | <Fun> | <NUM>
                | <STRINGS> | <Builtin> | <VARIABLE>)
        ;


一番下の<exprterm2>から見ていきましょう。 これは演算子を除いた数式の最もプリミティブな要素について定義しています。 それぞれの要素を|(OR)で繋いで、いずれかに該当した場合に数式の要素として認定されます。 要素は順に、()で括られた数式、関数定義、数値、文字列、組み込み関数、あるいは変数であることを宣言しています。

<exprterm>と<Parm>は、関数呼び出しを定義します。<exprterm2>で定義された値が関数(クロージャ)であった場合に (引数, 引数, ...)が付いていた場合には、関数として呼び出します。

<exprID>は、単項+と単項-の演算子を定義します。

<exprmul>は、*と/の演算子を定義します。

<expradd>は、+と-の演算子を定義します。

「5.2.11 条件判定 構文定義」で説明したのと同様に、この順番で定義することにより、演算子の優先度を設定します。

5.2.14 組み込み関数 構文定義

現在のClosure Basicでは、組み込み関数は乱数を返すrandom()だけしかありません。 しかし、簡単な構文定義の追加で、いくらでも組み込み関数を追加していくことが出来ます。


<Builtin>
        ("random" "(" <Expression> ")")
        ;

上でrandom関数は、「random ( 数式 ) 」の構文をそのまま定義しています。

例えば、これにsin関数を追加するには次に示すようにすれば追加できます。


<Builtin>
        ("random" "(" <Expression> ")")
        |
        ("sin" "(" <Expression> ")")
        ;

cos関数をさらに追加してみましょう。


<Builtin>
        ("random" "(" <Expression> ")")
         |
        ("sin" "(" <Expression> ")")
         |
        ("cos" "(" <Expression> ")")
       ;

このように、'|'でつないでいけば、いくらでも組み込み関数を拡張していくことが出来ます。

5.2.15 変数・配列 構文定義

変数および配列へアクセスする構文の定義です。


<VARIABLE>
        <ID> ["[" <Expression> "]" ]
        ;

<ID>は、デカルト言語の組み込み述語であり、先頭が数字以外の文字で、2文字目以降は数字も含めた文字にマッチするものです。

その構文に続いて[]があれば、配列として解釈されます。 なければ、単純な変数として解釈されます。

5.2.16 コメント 構文定義

注釈コメントは、'シングルクォートから行末までです。


<Comment>
        "'" <SKIPCR>
        ;

<SKIPCR>は、デカルト言語の組み込み述語であり、改行コードまで1行読み飛ばします。

さあ、これでClosure Basicの構文定義について一通り説明を終えました。

しかし、構文定義だけではコンパイラとしては不足です。変数やクロージャの管理と実行コードの生成などの処理が必要になります。

次章からはそれらについて説明していきましょう。