最近の更新 (Recent Changes)

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

Wikiガイド(Guide)

サイドバー (Side Bar)


← 前のページに戻る

3.2 ソース解説

日本語プログラミング言語「紫」のソースについて詳細に説明してみましょう。

ソースの全体については、3.1 ソース全体を参照してください。

3.2.1 サブルーチン

それでは、ソースの中の処理を見てみましょう。

最初のcheckword述語は、引数#wordの中に"、"が含まれていないかチェックします。

これは、構文解釈して取り出した語(#word)の中に句読点が途中に含まれるのは変であり、誤まっているので構文解釈をやり直すために定義しているサブルーチンです。

check述語は、引数のリスト(#l)の中のアトムが、途中で句読点が含まれているような誤まった解釈をしていないかをチェックするものです。 内部でcheckword述語を呼び出しています。

引数のリストを再帰的に処理してリストに含まれるアトムをすべてcheckword述語でチェックします。


/*** サブルーチン ***/
<checkword #word>
	::sys <char #l #word>
	::sys <isUnknown ::sys <member "、" #l>>
	;

<check ()>
	;
<check #l>
	::sys <isAtom #l> <checkword #l>
	;

<check (#l:#out)>
	( ::sys <isAtom #l> <checkword #l> <check #out>
	 |<check #l> <check #out>)
	;

errormsg述語は、エラーを検出したときのエラーメッセージを表示するものです。

エラーメッセージには、エラーの発生した場所の行番号も付けて表示されます。


<errormsg #msg>
	<InputStr #str>
	<LineNo #ln>
	<warn>
	<warn #ln "行目 *** ERROR *** " #msg "↓" >
	<warn>
	<warn #str>
	<exit>
	;

3.2.2 構文解析部

3.2.2.1 アンド文の構文解析

アンド文とは、「~と~と~と...」というような、「と」で語と語を繋いだ構文の解析を行います。


/*
 * 構文解析部分 
 */

<アンド文 (#x1 : #x2)>
	<* #x1> と ["、"|","] <check #x1>
	<アンド文 #x2> 
	;
<アンド文 (#x1)>
	<* #x1> 
	;


最初の文で、『 * と ["、"|","] 』のような入力文を解析し、"と"の前の語の取り出しを行います。

<* #x1>というのは、任意の文字列を取り出す正規表現での"*"に対応する述語です。

次にcheck述語では、取り出した後が正しい文かどうかチェックします。チェックして失敗した場合には、*述語をやり直します。

最後にアンド文述語を再帰的に呼び出すことによって、"と"で繋がった語を繰り返して解釈します。 結果は、#x1と#x2を繋いだリストとして返されます。

次のアンド文の定義では、"と"が文の中に含まれない最後の語を取り出す処理を表します。

3.2.2.2 単文組み込み述語の構文解析

日本語プログラミング言語「紫」には次に示すような特殊な組み込み述語があります。 これらは、数式をプログラム中で扱うことを目的に実装されています。


計算 : 数式の計算をする

 #n2=#n-2*#n1を計算する。


比較 : 数式の比較をする。
成立 : 比較の別名。数式の比較をする。

 #x < #y を比較した場合、

 :#a >= #bが成立し、

上記の組み込み述語を構文解析するのが以下です。


<単文 let #y>
	<* #y> を  ["、"|","] <check #y>
		計算
	;

<単文 compare #y>
	<* #y> を  ["、"|","] <check #y>
		比較
	;

<単文 compare #y>
	<* #y> が  ["、"|","] <check #y>
		成立
	;

~を計算する、~を比較する、~が成立する、というような構文を解釈します。

単文述語の第一引数にlet, compareと返すことにより、組み込み述語であることとその種類を通知します。

3.2.2.3 単文M1,M2,M3,M4,M5の構文解析

M1構文は次に示すような構文です。


M1構文 : '主語' (は | が) { ~ と} '目的語' (が | を) '述語' 単文末 。

この文を構文解析するのは次に示すようなプログラム部分です。


<単文 M1 #x #y #z>
	<* #y> (は | が)  ["、"|","] <check #y>
		<アンド文 #z> (が | を)  ["、"|","] <check #z>
		<* #x> <check #x>
	;


この構文解析が成功すると、第一引数に"M1"が返されることにより、M1構文の文と判定されます。 第二引数からは、構文解析された結果として、#xに述語、#yに主語、および#zに目的語が設定されて返されます。

同様に、M2, M3, M4, M5構文についても見てみましょう。


M2構文 : '主語' (は | が) { ~ と} '目的語' の '述語' 単文末 。

M3構文 : '主語' (は | が) { ~ と} '目的語' に '述語' 単文末 。

M4構文 : '主語' (は | が) '述語' 単文末 。

M5構文 : '目的語' (を|に|の) '述語' 単文末 。


この文を構文解析するのは次に示すようなプログラム部分です。


<単文 M2 #x #y #z>
	<* #y> (は | が)  ["、"|","] <check #y>
		<アンド文 #z> の ["、"|","] <check #z>
		<* #x> <check #x>
	;

<単文 M3 #x #y #z>
	<* #y> (は | が)  ["、"|","] <check #y>
		<アンド文 #z>  に  ["、"|","] <check #z>
		<* #x> <check #x>
	;

<単文 M4 #x #y ()>
	<* #y>  (は | が ) ["、"|","] <check #y>
		<* #x> <check #x>
	; 

<単文 M5 #x #y ()>
	<* #y>  (を|に|の) ["、"|","] <check #y>
		<* #x> <check #x>
	; 

「~と」の構文は、前に説明したアンド文述語を使って構文解析します。 それ以外の違いは使われる助詞が各構文ごとに異なります。

これらの構文解析が成功すると、第一引数に対応するM2,M3,M4またはM5が返されることにより、どの構文の文であるか判定できます。 第二引数からは、構文解析された結果として、#xに述語、#yに主語、および#zに目的語が設定されて返されます。

#zには、「~と~と...」というような複数の単語を繋いだ文が指定できます。そのため、#zには、複数の"と"で繋がれた単語がリストに入れられて設定されます。


#xは、(#y : #list)と#l1と(#y : #l2)のqsplitです。

  ↓変換

<M2 qsplit #x ((#y : #list) #l1 (#y : #l2))> 

上の例では、述語がqsplitで、主語が#xで、目的語が((#y : #list) #l1 (#y : #l2))です。

目的語#zの不要なM4とM5構文では、#zの位置に()を返します。


プラトンは人間である。

  ↓変換

<M4 人間 プラトン ()>

3.2.2.4 単文末の構文解析

この構文は、単文の文末を構文解析します。


<単文末 #flag>
	(
	   (であるすべて | すべて)    ("。" | "?" | "?")
	   <is #flag 疑問すべて>
	 |
	   (ですか|か|であるか|するか|したか|しますか)  ("。" | "?" | "?")
	   <is #flag 疑問>
	 |
	   (してください | して下さい | して | しろ | せよ)  "。"
	   <is #flag 命令>
	 |
	    (です|だ|である|する|した|します)  "。"
	    <is #flag 宣言>
	 |
	    "。"
	    <is #flag 宣言>
	 )
	;

文末が、"であるすべて" あるいは "すべて"の場合は、"疑問すべて"の文と判定して#flag引数に返します。 この文は、すべての解を検索して返すための構文で使われます。

文末が、"ですか" 、"か" 、"であるか" 、"するか" 、"したか" あるいは、"しますか"の場合は、"疑問"文と判定して#flag引数に返します。 この文は、解をひとつ検索して返すための疑問文の構文で使われます。

文末が、"してください" 、"して下さい" 、"して" 、"しろ" あるいは、"せよ" の場合は、"命令"文と判定して#flag引数に返します。 この文は、解をひとつ検索して返すための命令文の構文で使われます。

文末が、"です" 、"だ" 、"である" 、"する" 、"した" あるいは、"します"の場合は、"宣言"文と判定して#flag引数に返します。 この文は、解をひとつ検索して返すための宣言文の構文で使われます。

文末がなく、"。"で終わっている場合も、"宣言"文と判定して#flag引数に返します。 この文も、解をひとつ検索して返すための宣言文の構文で使われます。

3.2.2.5 複文の構文解析

この構文は、複文を構文解析します


<複文 #andxyz>
	    <単文 :#xyz>
	    (
	      (である場合 | であれば | であるとき | の場合 | のとき | するならば | ならば
			| するとき | したとき | する場合 | した場合) ["、"|","]
		<is #andxyz (#xyz)>
	     |
	      (であるかつ | であり | でかつ | するかつ | かつ | したかつ 
		| し | で)  ["、"|","]
		<複文 #andxyz2>
		<is #andxyz (#xyz : #andxyz2)>
	    )
	;


複文の2つの構文を上記の構文解析で行い、結果が#andxyzに設定されます。

#andxyzには、単文の結果をリストに連結したものが返されます。


複文の構文1:

"単文" (である場合 | であれば | であるとき | の場合 | のとき | するならば | ならば | するとき | したとき | する場合 | した場合)
"単文" "単文末" "。"

複文の構文2:

{"単文" (であるかつ | であり | でかつ | するかつ | かつ | したかつ  | し | で)}
"単文" (である場合 | であれば | であるとき | の場合 | のとき | するならば | ならば | するとき | したとき | する場合 | した場合)
"単文" "単文末" "。"


3.2.2.5 定義文の構文解析

定義文で、構文解析処理はすべて説明したことになります。

それでは、ソースの中の処理を見てみましょう。


<定義文 #xyz #flag>
	<単文 :#Mxyz1>
	(
	   (である場合 | であれば | であるとき | の場合 | のとき | するならば | ならば 
			| するとき | したとき | する場合 | した場合) ["、"|","]
	   <単文 :#Mxyz2> 
	   <単文末 #flag>
	   <is #xyz (#Mxyz2 #Mxyz1)>
	 |
	   <単文末 #flag>
	   <is #xyz (#Mxyz1)>
	 |
	   (であるかつ | であり | でかつ | するかつ | かつ | したかつ 
		| し | で)  ["、"|","]
	   <複文 #andxyz>
	   <単文 :#Mxyz2> 
	   <単文末 #flag>
	   <is #xyz (#Mxyz2 #Mxyz1 : #andxyz)>
	)
	;

定義文は、最初に単文の構文解析が成功した後に、3つの種類の文に振り分けられています。

最初が、単文の次が(である場合 | であれば | であるとき | の場合 | のとき | するならば | ならば | するとき | したとき | する場合 | した場合)のいずれかである場合に、「単文1 である場合、単文2 である。」というような単文を二つ繋いだ文です。 引数#xyzには、結果として(単文2 単文1)繋いだリストを返します。

これは以下の様なプログラムとなります。


<単文2> <単文1> ;

次に2つ目です。単文の次が単文末の場合は、「単文1 である」というような単文一つの文となります。 引数#xyzには結果として(単文1)のようなリストを返します。


<単文1> ;

3つ目は、(であるかつ | であり | でかつ | するかつ | かつ | したかつ | し | で) のいずれかである場合です。 これは、この判定の後に複文の構文を呼んでいるので、3つ以上の単文を繋いだものとなります。 「単文1 であるかつ、単文2 であるかつ、単文3 であるかつ、 ... 単文n-1 である場合、単文n である。」というような構文を解析します。

引数#xyzには、結果として(単文n 単文1 単文2 単文3 ... 単文n-1)のようなリストを返します。


<単文2> <単文1> <単文2> <単文3> ... <単文n-1> ;

いずれの構文でも引数#flagには、前項で説明した単文末を構文した結果が入ります。 これにより疑問すべて、疑問、宣言または、命令のいずれの定義文であるかを返します。

3.2.3 構文解析後のコンパイル結果出力

次の処理では、構文解析して得られた結果をデカルト言語として実行できるプログラムの形式に変換します。

構文解析した結果はリスト形式で返されます。しかし、そのままでは唯のリストでしかないため、述語形式に変換してやらねばなりません。 また、このようにコンパイルした結果は、まとめてtempfile.txtという中間ファイルに出力されます。 この辺りの処理の詳細は後のメイン処理で行いますが、ここでは、結果を変換して出力する処理に着目して説明します。

それでは、ソースの中の処理を見てみましょう。

3.2.3.1 定義文のコンパイル出力処理

定義文のコンパイル出力処理です。


/*
 * 解析結果のデカルトプログラムをファイルに出力
 */
<定義文プリント #out>
	::sys <mkpredlist #outprd #out>
	<printlist <quote #outprd>>
	<printf <\_t> ";" <\_n>>
	;

まずは、定義文を出力する処理です。

引数の#outに構文解析の結果のリストが入っています。 それをmkpredlist述語で最初に述語形式に変換します。


#xは、言葉を使うならば、#xは論理的に思考する。

  ↓構文解析

(M3 思考 #x (論理的)) (M1 使う #x (言葉))

  ↓述語に変換


<M3 思考 #x (論理的)> <M1 使う #x (言葉)>

変換された述語は、勝手に実行されないように、quote述語をつけておいてprintlist述語で出力します。 そして、最後に述語の連なりの区切りとして";"を出力します。

このときプリントアウトの出力先は、この処理を呼ぶ前に一時ファイル先に切り替えられています。


<M3 思考 #x (論理的)> <M1 使う #x (言葉)>

  ↓結果のデカルトプログラム形式での出力

<M3 思考 #x (論理的)> <M1 使う #x (言葉)>
       ;

3.2.3.2 疑問文、命令文のコンパイル出力処理

疑問文と命令文のコンパイル出力処理です。


<回答プリント (#out1 :#out)>
	::sys <append #o #out (#out1)>
	::sys <mkpredlist #outprd #o>
	<printf "? " >
	<printf "<回答 " >
	<printf <quote #outprd>>
	<printf ">;" <\_n>>
	;

引数の(#out1 :#out)に構文解析の結果のリストが入っています。

回答は、疑問文や命令文の場合に使われるものです。

構文解析処理では、定義文と同様に処理されているため、引数に渡された構文解析の結果のリストの順番が異なります。 そのため、最初のリストの要素を最後に写したものを#o変数に設定してこれを基にmkpredlist述語で述語の形式に変換します。

構文解析結果には、解を探すための"?"と回答述語が出力されます。構文解析した結果には誤まって解釈されないためにquote述語をつけてprintfされます。


[回答文の処理の流れ]

宇宙人が存在する場合、UFOは存在しますか?

  ↓構文解析

(M4 存在 UFO ()) (M4 存在 宇宙人 ())

  ↓結果のデカルトプログラム形式での出力(リストの先頭が最後に移動しているのに注意!)

? <回答 (<M4 存在 宇宙人 ()> <M4 存在 UFO ()>)>;


[定義文の処理の流れ]

宇宙人が存在する場合、UFOは存在します。

  ↓構文解析

(M4 存在 UFO ()) (M4 存在 宇宙人 ())

  ↓結果のデカルトプログラム形式での出力

<M4 存在 UFO ()> <M4 存在 宇宙人 ()>;


回答文の出力には、処理結果を検索するための"?"と、結果を受けて回答を日本語に変換する回答述語をつけて変換されます。 そして、最後に述語の連なりの区切りとして";"を出力します。

3.2.3.3 疑問文すべてのコンパイル出力処理

すべての解を検索する「疑問文すべて」構文のコンパイル出力処理です。


<回答すべてプリント (#out1 :#out)>
	::sys <append #o #out (#out1)>
	::sys <mkpredlist #outprd #o>
	<printf "? " >
	<printf "<回答すべて ">
	<printf <quote #outprd>>
	<printf ">;" <\_n>>
	;


引数の(#out1 :#out)に構文解析の結果のリストが入っています。

回答すべては、疑問文や命令文のすべての解を検索する場合に使われるものです。

構文解析処理では、定義文と同様に処理されているため、引数に渡された構文解析の結果のリストの順番が異なります。 そのため、最初のリストの要素を最後に写したものを#o変数に設定してこれを基にmkpredlist述語で述語の形式に変換します。

構文解析結果には、解を探すための"?"と「回答すべて」述語が出力されます。構文解析した結果には誤まって解釈されないためにquote述語をつけてprintfされます。

3.2.3 実行結果の出力

回答述語や回答すべて述語の引数の述語を実行します。そしてその実行結果を受けて、回答述語や回答すべて述語が日本語の回答文を作成します。

まず最初は"と"でつないだアンドの処理です。

引数のリストを"と"で繋いで表示します。


/*** 実行結果の出力 ***/
<回答アンド (#y1 :#y)>
	<printf #y1>
	<回答アンド2 #y>
	;
<回答アンド2 (#y1 :#y)>
	<printf "と" #y1>
	<回答アンド2 #y>
	;
<回答アンド2 ()>
	;

ソースを見てもらうとわかると思いますが、引数のリストの要素を再帰処理で"と"でつないでプリントしています。

処理としては単純です。

その次は回答の単文のM0~M5の各構文を元の日本語の形式に戻してプリントします。

引数がリストであり、そのリストが回答の単文そのものです。 リストの最初には、M0~M5が設定されており、それにしたがって戻す日本語の形式を選択しています。


<回答単文 (M0 数式 #z #y)>
	<printf #z "は、数式">
	;

<回答単文 (M1 #x #z #y)>
	<printf #z "は、">
	<回答アンド #y>
	<printf "を" #x >
	;

<回答単文 (M2 #x #z #y)>
	<printf #z "は、">
	<回答アンド #y>
	<printf "の" #x >
	;

<回答単文 (M3 #x #z #y)>
	<printf #z "は、">
	<回答アンド #y>
	<printf "に" #x >
	;
<回答単文 (M4 #x #z #y)>
	<printf #z "は、">
	<printf #x >
	;
<回答単文 (M5 #x #z #y)>
	<printf #z "を">
	<printf #x >
	;

M1, M2, およびM3の構文は複数の目的語を持つため、それをアンド文として呼び出して出力していることに注意してください。


<回答文 ((#M #x #z #y) : #out)>
	<回答単文 (#M #x #z #y)>
	(  <is #out ()> <print "です。">
	 | <is #out ((#M2 #x2 #z2 #y2))> <printf "である場合、"> <回答文 #out>
	 | <printf "であり、"> <回答文 #out>)
	;

回答文は、回答が正しく実行され解が得られたときに実行されて結果を日本語に戻して出力します。 回答単文を組み合わせた複文に対応します。


<不明文 ((#M #x #z #y) : #out)>
	<回答単文 (#M #x #z #y)>
	(  <is #out ()> <print "ではありません。">
	 | <is #out ((#M2 #x2 #z2 #y2))> <printf "である場合、"> <不明文 #out>
	 | <printf "であり、"> <不明文 #out>)
	;

不明文は、解が得られなかった場合に、「~ではありません」と表示します。

ここでは、回答が不明な場合に呼び出される構文であり、不明であるものは否定されたとして回答を表示します。

3.2.4 実行とタイムアウトと回答

3.2.4.1 回答述語

ここで説明する回答述語は、プログラムを実際に実行し、成功・失敗した場合の結果を表示するプログラミング言語「紫」の中心的な処理です。

まず、timeout述語の中で、引数#outprd2に渡された述語をプログラムとして実行します。

成功した場合には、次の回答文述語に処理がわたり、結果が表示されます。

このようにプログラムを実行するときtimeout述語には10秒(10000000μ秒)のタイムアウトが設定されます。 これは、無限ループや処理に時間がかかりすぎて何時まで経っても終らないプログラムの場合に対応するためのものであり、設定されたタイムアウト時間を過ぎてもプログラムが終らない場合には強制的に実行が失敗させられます。

プログラムが結果の検索に失敗した場合やタイムアウトした場合には、|(オア)の次の不明文に処理が移り、結果が不明で失敗したことを表示します。


<回答 #outprd2>
	(
	  <timeout 10000000 #outprd2>
	  <回答文 #outprd2>
	| <不明文 #outprd2>
	)
	;

timeout述語の必要性についてすこし説明してみます。

プログラミング言語「紫」は、一階述語論理をベースにしたコンピュータ言語です。

プログラムの停止性について考えると、容易に無限ループに陥るプログラムが書けてしまいます。

例えば、以下のように論理が循環している場合には、無限ループに陥ります。


これはループである場合、これはループです。

これはループか?

日本語で普通に考えても、1行目は同じことを言っているだけであり、論理が循環してますね。

しかし、プログラミング言語「紫」では、このような場合には、実行が失敗して結果が出るようになっています。

実行結果は以下のようになります。


$ descartes murasaki infloop.mrs

これはループか?
これは、ループではありません。

3.2.4.2 回答すべて述語

ここで説明する回答すべて述語は、すべての解を求めるまでプログラムを実行しつづけます。

まず、timeout述語の中で、さらにfindall述語の中で、引数#outprd2に渡された述語をプログラムとして実行します。

findall述語は、デカルト言語の組み込み述語であり、引数の述語の実行をすべて終るまでバックトラックしながら実行を続行させます。 そして、述語は実行され成功した直後にすぐ回答を回答文述語によって表示されます。 実行結果をトレースするとあたかも樹形図のようになるでしょう。

この処理も無限ループや長すぎる実行時間を抑制するためにtimeout述語が設定されています。

プログラムがタイムアウトした場合には、|(オア)の次の不明文に処理が移り、結果が不明で失敗したことを表示します。

このとき、引数の述語#outprd2が失敗した場合には、不明文述語は呼ばれずそのまま終了することに注意してください。

findall述語は引数の実行が失敗しても常にfindall自体の実行は成功して返ってきます。


<回答すべて #outprd2>
	(
	 <timeout 10000000 
	   <findall
	    #outprd2
	    <回答文 #outprd2>
	   >
	 >
	| <不明文 #outprd2>
	)
	;

3.2.5 文の読み込み処理

3.2.5.1 コメント文の読み込み

プログラム部分とは別にコメント文を読み込んで構文解釈します。

コメントは、//で始まる1行です。 この構文をプログラムのソースから読み込むと、なにも処理をしないで次の文の読み込み処理を行います。


/*
 * 入力ファイルから文を一つ切り出す 
 */
<コメント>
	"//" <SKIPCR>
	;

SKIPCR述語は、デカルト言語の組み込み述語であり、改行が出てくるまで入力行を読み飛ばします。

3.2.5.2 文読み込み

文読み込み述語は、コメント述語が成功し、コメントを読み込んだ場合には、第一引数に()を返します。 ()が返った場合には、それ以上の構文解釈は行われず、次の文の読み込みに移ります。

コメントでなかった場合には、"。"、 "?" 、 "?" までの一文を読み込み、#lを引数に設定して返します。 ここで得られた#lの文字列に対して、さらにプログラミング言語「紫」のプログラムとしての構文解釈を行います。


<文読み込み ()>
	<コメント>
	;

<文読み込み #l>
	<TOKEN #l <* #x> ("。" | "?" | "?" )>
	;

TOKEN述語はレキシカル解析(字句解析)を行うデカルト言語の組み込み述語です。

TOKEN述語を使用して、任意の文字列に合致するトークンを合成できます。(正規表現の代替となります。)

3.2.6 メイン処理

3.2.6.1 行番号設定

行番号設定述語は、ソースプログラムの読み込み中にエラーが発生した場合に表示する行番号を変数LineNoに設定します。

行番号は、デカルト言語のline述語によって獲得します。


<行番号設定>
	<SKIPSPACE>
	::sys <line #ln>
	<setVar LineNo #ln>
	;

3.2.6.2 メイン処理本体の読み込み

次に示す読み込み述語は、日本語プログラミング言語「紫」のメイン処理となります。


/*
 * メイン処理
 */
<読み込み>
	{ 
		<行番号設定>
		<文読み込み #in> 
		<setVar InputStr #in>
		::sys <strdelcntl #i #in>
		(
		   <is #i ()>
		 |
		   ::sys <syntax #i <NULLLINE>>
		 | 
		   ::sys <syntax #i
			 (  <定義文 #out #flag> 
			    ::sys <switch #flag
				宣言    <定義文プリント #out>
				疑問 (
					<printf "? <printf '" #i "' <\_n>>; " <\_n>>
					<回答プリント #out> 
					<print "? <print>;">)
				命令 (
					<printf "? <printf '" #i "' <\_n>>; " <\_n>>
					<回答プリント #out> 
					<print "? <print>;">)
				疑問すべて (
					<printf "? <printf '" #i "' <\_n>>; " <\_n>>
					<回答すべてプリント #out> 
					<print "? <print>;">)
			    >
			    [::sys <isUndefVar #out> 
				<errormsg "構文が解釈できません。"> ]
			 | <errormsg "構文に誤りがあります。">
			 )
		   > 
		)
		::sys <flush>
	}
	;

大事な処理なので細かく見ていきましょう。


		<行番号設定>
		<文読み込み #in> 
		<setVar InputStr #in>
		::sys <strdelcntl #i #in>

まず「3.2.6.1 行番号設定」で説明した、行番号設定述語を呼び出して、読み込むソースプログラムの行番号位置を設定します。

次に「3.2.5.2 文読み込み」で説明した、文読み込み述語を呼び出して、一つの文をソースプログラムから読み込みます。

その次に呼ばれているstrdelcntl述語は、デカルト言語の組み込み述語であり、コントロール文字などの表示されない文字を読み込まれた文字列から削除します。

こうして入力された一つの文の抜き出しを行いました。

次に、入力された文が()と同じか判定します。


		(
		   <is #i ()>
		 |

これは、読み込んだ文がコメント文であったことを示します。 このときは何もしません。

次に、読み込んだ行が空行であるか判定します。


		   ::sys <syntax #i <NULLLINE>>
		 | 

これは、入力行#iをsyntax述語を使って、NULLLINE述語で構文解釈できるかどうかで判定しています。 syntax述語もNULLLINE述語もデカルト言語の組み込み述語です。 このときも何もしません。

そして、以上の条件以外のものは意味のある入力行の可能性が高いです。 その入力行を定義文述語で構文解釈できるか実行します。


		 | 
		   ::sys <syntax #i
			 (  <定義文 #out #flag> 

定義文については、「3.2.2.5 定義文の構文解析」で説明したとおりです。 #outに構文解析された結果をデカルト言語のプログラムに変換したものが返ります。 #flagには、文の種類の解析結果として「宣言」、「疑問」、「命令」あるいは「疑問すべて」のいずれかが設定されて返ります。

次に#flagの値にしたがってswitch述語により処理を切り分けます。


			    ::sys <switch #flag
				宣言    <定義文プリント #out>
				疑問 (
					<printf "? <printf '" #i "' <\_n>>; " <\_n>>
					<回答プリント #out> 
					<print "? <print>;">)
				命令 (
					<printf "? <printf '" #i "' <\_n>>; " <\_n>>
					<回答プリント #out> 
					<print "? <print>;">)
				疑問すべて (
					<printf "? <printf '" #i "' <\_n>>; " <\_n>>
					<回答すべてプリント #out> 
					<print "? <print>;">)
			    >

結果はそれぞれに種類の処理に切り分けられて、デカルト言語の形体に変換されて出力されます。

どの処理にも属さない場合にはエラーメッセージを表示します。


			    [::sys <isUndefVar #out> 
				<errormsg "構文が解釈できません。"> ]
			 | <errormsg "構文に誤りがあります。">

最後に出力をすべてフラッシュします。


		::sys <flush>

デカルト言語では出力はキャッシュされていますが、flush述語により完全に出力されます。

ここの処理は後に説明する処理の中から呼び出されるときに、出力先を一時ファイルとしてオープンした後に実行されています。 そのため、一時ファイルには、コンパイルされた結果のデカルト言語のプログラムがどんどんと書き出されていきます。

ここで説明した読み込み述語内の処理は、{}大カッコで囲まれているため、入力行がなくなるまで繰り返し1行ずつ処理されていきます。

3.2.6.3 組み込み述語の組み込み

組み込み述語のうち、以下の3つをコンパイル結果の先頭に組み込んでおきます。


#xは、リストです。

改行を表示する。

#xを表示する。

これらの定義を次のようにprintf述語で出力しておきます。


<組み込み>
	<printf
		"<M4 リスト #x ()> ::sys <isList #x>;" <\_n>
		"<M5 表示 改行 ()> <print>;" <\_n>
		"<M5 表示 #x ()> <print #x>;" <\_n>
	>
	;

同様にこの述語の中に事前に定義を入れておけば、それが新たな組み込み述語となります。

3.2.6.3 起動処理

最後に起動処理について説明しましょう。

この処理は、日本語プログラミング言語「紫」で最初に起動される処理です。


<起動>
	::sys <PrintResultOff>
	::sys <args #args> <is #args (_ #prg :_)>
	::sys <openr #prg 
		::sys <openw "tempfile.txt" 
			<組み込み>
			<読み込み>>>
	<load "tempfile.txt">;
	;

? <起動>;

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

まず、PrintResultOff述語を呼び出すことによって、述語の実行結果を表示するのを抑制します。 これは、アプリとしてこのプログラムは実行されるので、実行結果はprint述語などで明示して表示される情報以外は不要なためです。

次に、args述語を呼び出して、「紫」を実行したときの引数を取得します。


$ descartes murasaki プログラム名

上のようにプログラムが呼び出されると、args述語の#argsには、(murasaki プログラム名)と設定されます。

さらに、次の<is #args (_ #prg :_)>で#args変数の中身をパターンマッチさせることによって、$prg変数には"プログラム名"の文字列が設定されます。

この#prgを使い、openr述語で標準入力を"プログラム名"に変更します。

openw述語では、標準出力を"tempfile.txt"に変更します。 このファイルには、"プログラム名"の入力ソースをコンパイルした結果が、組み込み述語と読み込み述語の結果として出力されます。

出力されたコンパイル結果はデカルト言語のプログラム形式となっているので、load述語で読み込むと自動的に実行されて結果が出力されます。

最後の行の、「? <起動>;」はすべてのプログラムを最初に起動する起点です。


--

--