@node 基本言語仕様, プリミティブ, はじめに, Top
@chapter 基本言語仕様

@menu
* 構文::                        XLの構文
* 変数と値と型::                変数と値と型
* 型::                          
* インタプリタ実行モデル::      
* プラグマ::                    
* 組み込み関数インターフェース::  
@end menu

@node 構文, 変数と値と型, 基本言語仕様, 基本言語仕様
@section 構文
XLの構文は、XML形式の構文とLISP形式の構文2種類がある。本来、XML 形式のみで十分であるが、それでは煩雑になるので、LISP形式も採用した。ただし、LISP形式はXML形式の要素の中に入れ子にすることが出来るが、その逆は出来ない。関数の仕様書などでは二つの形式を併記している。 
@sp 1
XLパーザには、XMLモードとLISPモードの二種類がある。XMLモードの時には、タグを認識するための構文解析が行われ、また、要素の中の@samp{(},@samp{)}、@samp{^}、および数字を認識すると、LISPモードになる。LISPモードでは、@samp{<}を認識すると、XMLモードになる。二つのモードはネストしており、モードの変化点はパーザによって記憶されており、ネストが終わると自動的に基のモードへもどる。 
@sp 1
XMLモードのときの要素は、空白や改行で区切られる。空白や改行は無視する化しないかは?xml:optionで指定することが出来る。もし、空白を無視しないことにした場合、空白のところの区切り目に空白だけの文字列が挿入される。 

@sp 1
@example
<tag>
    abc def
    hhh
    ^sym
    ghi
    (+ a b)
    jk 123
    lm
    (print stdout !
        <name data="eee"> hello </name>)
    nop
</tag>
@end example
@sp 1

@noindent
空白、改行を無視するモードでは、 

@sp 1
@example
(tag
    "abc" "def"
    "hhh"
    sym
    "ghi"
    (+ a b)
    "jk" 123
    "lm"
    (print stdout
        ([name data="eee"] "hello"))
    "nop"
tag)
@end example
@sp 1

@noindent
と等価である。空白、改行を無視しないモードでは、 

@sp 1
@example
(tag
    "abc" " " "def" "\n\t\t"
    "hhh"
    sym
    "ghi"
    (+ a b)
    "jk" 123
    "lm"
    (print stdout
        ([name data="eee"] "hello"))
    "nop"
tag)
@end example
@sp 1

@noindent
となる。タグのあと、@samp{(}や@samp{^}など、LISPモードへ変化する前後では空白や改行は無視されることに注意されたい。 

@node 変数と値と型, 型, 構文, 基本言語仕様
@section 変数と値と型

XL では、型は値に与えられる物であり、変数はどのような値も受け付ける。 

@node  型, インタプリタ実行モデル, 変数と値と型, 基本言語仕様
@section 型

@menu
* 型と値の標記::                
* 基本型::                      
* 文字コードについて::          
* 構造型(LISPのS式に対応)::     
* 環境::                        
@end menu

@node 型と値の標記, 基本型, 型, 型
@subsection 型と値の標記
XLで使用する値の種類は以下の通りである。 

@sp 1
@example
exp ::=
        fundamantal-exp         (基本型)
        structured-exp          (構造型)
        function-exp            (関数型)
        environment-exp         (環境型)
@end example
@sp 1

@node 基本型, 文字コードについて, 型と値の標記, 型
@subsection 基本型
XLで定義される基本型は以下の通りである。 

@sp 1
@example
fundamental-exp ::=
        integer
                表記：XML方式,LISP方式ともに同じ
        floating-point（浮動小数点）
                表記：XML方式,LISP方式ともに同じ
        string
                表記：	
                        XML     解析文字列
                        LISP    "..."
        symbol
                表記：
                        XML     ^... (最後に空白が入る)
                        LISP    以下参照。
        raw
                表記：
                        XML形式LISP形式ともに同じ
                        #byte数#...RAWデータ...
                        例：#5#!x$%~
        error
                表記：
                        %E(
                                エラーシーケン番号(行番号)
                                エラー発生siteのURL
                                エラー発生ファイル名
                                エラー発生関数名
                                エラーコード
                                exp (その他付加データ)
                           )
        environment
                表記：なし
@end example
@sp 1

@code{error}型は、エラーを処理するための特別な値で、エラーが発生した関数が戻り値として返す。一方、この値を受け取った関数は即座に同じエラーを返さなければならない。ただし、@code{ErrHandler}関数は例外である。この関数はエラーを補足してエラーの後処理をするための関数である。 

通常、インタープリタは、エラーが発生すると、それを@code{Result}関数の引数として表示する。しかし、@code{error}型のうち@code{ErrDontPrint}エラーは特殊なエラーで、インタプリターはこのエラーを受けるとなにも表示しない。このようにすることによって、ネットワークでエラーやデータがループする事態を回避している。

@node 文字コードについて, 構造型(LISPのS式に対応), 基本型, 型
@subsection 文字コードについて
XLの処理系内部ではあらゆる文字を32bit固定長で表現する。この文字コードを内部文字コードと呼ぶ。内部文字コードの割り当ては以下の通りである。

@sp 1
@example
ゾーン                          ゾーンの意味    割り当てられたコード 
00 00 00 00 〜 7F FF FF FF      4バイトコード   UCS 
80 00 00 00 〜 80 00 FF FF      2バイトコード   JIS 第一水準、第二水準 
80 00 00 00 〜 BF FF FF FF      2バイトコード   未使用 
C0 00 00 00 〜 DF FF FF FF      3バイトコード   未使用 
E0 00 00 00 〜 FF FF FF FF      1バイトコード   未使用 
@end example
@sp 1

UCSおよびJISの領域以外は、UCSやJISと異なるコンセプトのコードを割り当てることが出来る。現在はEUC、SJISは全てパーザによってJISに変換される。 


@node 構造型(LISPのS式に対応), 環境, 文字コードについて, 型
@subsection 構造型(LISPのS式に対応)

@sp 1
@example
LISP形式
structured-type ::=
    ()
    (exp exp ....)
    ([symbol 属性リスト])
    ([symbol 属性リスト] exp ....)


XML形式

([symbol 属性リスト] ....)
@end example
@sp 1

@noindent
は、

@sp 1
@example
<symbolと同じ文字列 属性リスト> </symbolと同じ文字列>
@end example
@sp 1

@noindent
と等価である。たとえば、 

@sp 1
@example
([greeting
    FirstName = "Hirohisa" 
    LastName = "Mori"] 
    "Hello World")
@end example
@sp 1

@noindent
は、 

@sp 1
@example
<greeting 
    FirstName="Hirohisa" 
    LastName = "Mori"> 
    Hello World
</greeting>
@end example
@sp 1

@noindent
と変換できる。この場合、Hello World は一続きの文字列として認識されるが、これを別々に分けたい場合は、間に \ をいれる。つまり、 

@sp 1
@example
<greeting 
    FirstName="Hirohisa" 
    LastName = "Mori"> 
    Hello \ World 
</greeting>
@end example
@sp 1

@noindent
は、 

@sp 1
@example
(greeting [
    FirstName = "Hirohisa" 
    LastName = "Mori"]
    "Hello" "World")
@end example
@sp 1

@noindent
と等価である。 
属性値にシンボルを定義したい場合は、 

@sp 1
@example
(greeting [FirstName = "^symbol" ]
    "Hello" "World")


<greeting FirstName = "^symbol"> Hello World </greeting>
@end example
@sp 1

@noindent
というように、属性値の頭に^を付ければよい。^を二つ続け、"^^symbol"とすると、それはシンボルではなく、"^symbol"という文字列と解釈される。 

@node 環境,  , 構造型(LISPのS式に対応), 型
@subsection 環境

変数と値の対応表。変数の名前は@code{symbol}で示される。つまり、@code{symbol}と値の対応表が環境である。 
@sp 1
通常一つの環境には、一つの親環境がある。以下に述べる、評価においては、親環境の情報も検索対象となる。 
@sp 1
しかし、関数の引数の評価時には、関数の引数評価用の環境を先に評価し、その後、関数のカレントの環境を評価する必要があるため、親環境を二つもった分岐環境が生成される。ただし、この環境にはシンボルや値をバインドすることは出来ない。 
@sp 1
評価時の分岐環境の検索順序は、まず、第一番目の親環境、次に第二番目の親環境となる。したがって、親環境に定義されているシンボルが優先される。 

@node インタプリタ実行モデル, プラグマ, 型, 基本言語仕様
@section インタプリタ実行モデル

@menu
* 評価::                        
* 関数::                        
* インタプリタのモデル::        
* RemoteとLocal::               
* エージェント::                
* XLサーバ::                    
@end menu

@node 評価, 関数, インタプリタ実行モデル, インタプリタ実行モデル
@subsection 評価
プリミティブ関数の一つである評価関数Evalを、次のように定義する。 

@sp 1
@example
(Eval environment-exp　exp)
@end example
@sp 1

@noindent
評価対象(exp)としては、あらゆる型を受け付ける。そして以下に示す通り、評価した結果を返す。このとき、@code{Eval}に与えられた環境(environment-exp)を評価対象のカレント環境と呼ぶ。 

@itemize @bullet
@item
評価対象が、@code{symbol}以外の基本型の値の場合は、その値をそのまま返す。

@item
評価対象が、@code{symbol}であった場合は、与えられた環境および、親環境、そのまた親環境と検索し、@code{symbol}に相当する変数が初めて見つかった時点で、対応した値を返す。検索に失敗したらエラーを返す。エラーはシステム構造型として後述する。

@item
評価対象が、関数であった場合は、その値をそのまま返す。

@item
評価対象が構造型であった場合、その最初のシンボル、あるいはタグに相当するシンボルを評価し、 

@itemize @minus
@item
その評価結果が関数でも環境でもでなかった場合は、エラーを返す。 

@item
その評価結果が環境の場合、その環境を親環境とした環境を新しく生成し、これへカレント環境を変更する。この環境には、@code{Local}というシンボルにまえのカレント環境をバインドする。

@item
その評価結果が関数であった場合は、関数が@code{normal-order}か、@code{applicateive-order}かを判定、

@item
@code{normal-order}であれば、構造型の値、あるいは、要素をそのまま関数へ渡し、 関数そのものを実行し、結果を返す。 

@item
@code{applicative-order}であれば、構造型の値の二項目以降あるいは、要素の中身を すべて評価し、これらを引数として関数に渡し、実行結果を返す。
@end itemize

@end itemize

@node 関数, インタプリタのモデル, 評価, インタプリタ実行モデル
@subsection 関数
引数を与えると、それに対し所定に計算をし値を返す。関数を呼び出すときには、 

@sp 1
@example
(func a b c)
@end example
@sp 1

@noindent
または、 

@sp 1
@example
<func> a b c </func>
@end example
@sp 1

@noindent
あるいは、属性リストをつかい 

@sp 1
@example
([func type="def"] a b c)

<func type="def"> a b c </func>
@end example
@sp 1

@noindent
というように、構造型を使う。この構造型全体を関数@code{func}への引数と呼び、@code{func, a, b, c}をそれぞれ、関数の1番目2番目.....の引数要素と呼ぶ。また、呼び出された側から、属性を参照する場合は、属性名に相当するシンボルを参照すればよい。 

関数の引数の評価の仕方は、@code{Normal}と、@code{Applicative}との2種類がある。@code{Normal}では、引数は評価されずに、そのまま渡される。環境は渡される時に生成され、新しいカレント環境となる。引数を評価するかしないかは、関数を実行する中で明示的に決めたいときにこのオプションを選ぶ。 

一方、@code{Applicative}では、引数は引数要素ごとに評価されてから渡される。引数評価のために、分岐環境が生成され、その一番目の親環境に関数定義時に定義した専用の環境がセットされ、第二番目の親環境に現在のカレント環境が設定される。評価する場合の環境の検索順序は関数定義時の環境、現在のカレント環境という順番になる。この仕組みは、 

@sp 1
@example
<SetAgent>
    "nichibun-agent"
    <User> hirohisa </User>
    <Passwd> abc </Passwd>
</SetAgent>
@end example

@sp 1

という呼び出しを考えたとき、@code{User}, @code{Passwd}を、@code{SetAgent}外部で使用したときと、別の処理をしたいときに有効である。評価用の環境でこの特殊な処理の関数を定義すればよい。つまり、以下のように@code{Sequence}を使って新しい環境を定義してしようする。 

@sp 1

@example
<Sequece>
    <Define> ^Env <CurrentEnvironment/> </Define>
    <Define> ^User
        ()
        <Arguments> ^name </Argments>
        ..... User の定義
    </Define>
    <Define> ^Passwd
        ()
    <Arguments> ^password </Arguments>
        ..... Passwd の定義
    </Define>
</Sequence>

<Define> ^SetAgent
    ^Env
    <Arguments> ^db ^user ^passwd　</Argements>
    .... SetAgent の定義
</Define>

....

<SetAgent>
    "nichibun-db"
    <User> hirohisa </User>
    <Passwd> abc </Passwd>
</SetAgent>
@end example

@sp 1

前段階が終わると、引数が評価され、関数本体へ実行が移る。 
おおかたの関数は、前段階なしのApplicative関数であろう。 

また関数には、インタプリタにあらかじめ組み込まれたプリミティブ関数と、スクリプトによって定義できる、マクロ関数がある。マクロ関数の定義の仕方はプリミティブ関数lambdaおよびdefineで実現される。 


@node インタプリタのモデル, RemoteとLocal, 関数, インタプリタ実行モデル
@subsection インタプリタのモデル

一つのインタプリタが起動される時には、必ず一組の入力ストリームがある。たとえば、標準入出力から駆動されたり、またはファイルであったり、あるいはネットワークの外からコネクションがアクセプトされ、インタプリタが起動されるなどである。入力ストリームの先が、ファイルであったり、標準入出力であったりした場合は、このインタプリタをルート・インタプリタと呼ぶ。 

インタプリタは、初期化時に、一つの環境を作り、この環境にいくつかのデフォルトの変数を定義し、入力ストリームからXLスクリプトが来るのを待つ。来たスクリプトを一つ一つの関数ごとに、先の環境と一緒にEval関数へ渡す。 

駆動されたインタプリターは入力ストリームからルート要素が来るのを待つ。XLストリームの場合にはいくつものルート要素があっても良い。つまり、いくつものXMLファイルが送り込むことが出来る。 

インタープリタは入力ストリームから送り込まれた要素を評価し、その結果を、もし対応する出力ストリームがあれば、そこへ返す。 

インタプリタを生成する関数は、OpenInterpreter である。この関数によりインタプリタを生成すると、インタプリタID (IID)が返ってくる。IIDは正のの正数である。インタプリタを終了するときにはこのIIDを使ってCloseInterpreter を呼べばよい。 

インタプリタの駆動方式には何種類かがある。ファイルや標準入出力を入力ストリームとして指定する方法、ネットワークのアクセプトポートを指定する方法。また、どこかのXLサーバのアクセプトポートを指定し、コネクションを貼る方法である。 

ファイルや標準入出力を入力ストリームとして指定した場合はその入力ストリームがそのままインタプリタの入力ストリームとなる。 

アクセプトポートを指定した場合は、そのアクセプトポートに接続要求が来るたびにソケットとインタプリタを生成し、ソケットの入力ストリームをインタプリタの入力ストリームとする。 

コネクションを指定した場合は、指定するアクセプトポートに接続し、生成されたソケットのの入力ストリームをインタプリタの入力ストリームとする。 

このように、ネットワークのソケットの場合、ソケットは全二重なので、ソケットの両側にインタプリタが対称に生成されることになる。 


@node RemoteとLocal, エージェント, インタプリタのモデル, インタプリタ実行モデル
@subsection RemoteとLocal

XLの特徴的なネットワーク操作関数にRemoteとLocal関数がある。Remote はIIDによって与えられたインタプリタの出力ストリームへ、与えられた命令群（XML要素群）を送り込み、その結果が入力ストリームより返ってくるのを待つ。ソケットの出力ストリームは反対側が入力ストリームとなっており、インタプリタがあるので、このインタプリタが送り込まれたXML要素群を解釈し、結果を返す。返ってきた結果は、そのままRemote の戻り値となる。 

Remote をネストすると、次々に新しいインタプリタへソケットを通じてコネクトすることが出来る。複数のインタプリタをネットワークでカスケードし、情報をやりとりする並列実行が可能となる。 

Localは、Remote のXML要素を評価中のインタプリタ（リモートインタプリタ）から、ソケットの逆向きのストリームへ要素を送り込む仕組みである。このLocalからの要素を評価するのはRemoteを発行したインタプリタ（ローカルインタプリタ）ではないが、評価の時の環境を。ローカルインタプリタとそろえることにより、ローカルインタプリタのデータにアクセスすることが出来る。Remoteが何段にもカスケードしている場合は、Localも同様にカスケードする事が出来る。 

Localにより送り込まれた要素は、Remoteを呼び出したインタプリタの環境で評価をしなければならないので、まず、Remote関数はあらかじめ、出力ストリームへ送り出す要素の先頭の行番号と、現在の環境をセットにしてデータベースに記憶する。一方、Localは直接、評価対象の要素を送り込むことはせず、もとの環境を検索し評価する機能をもったLocalEval関数に評価対象の要素をネストして出力ストリームへ送り込む。 

Localが二重にネストすると、二度目のLocalは最後にRemoteを発行したインタプリタの出力インタプリタへ評価要素を送り込まなければならない。そこで、上述の環境と行番号のセットに、さらにRemoteを呼び出したインタプリタへのポインタを記憶しておく。この構造により次に出力すべき出力ストリームが認識できる。 


@node エージェント, XLサーバ, RemoteとLocal, インタプリタ実行モデル
@subsection エージェント

エージェントは環境を共有する一つまたは複数のインタプリタ群からなるプロセスのことを言う。これらインタプリタはこのプロセスのスレッドとして実現される。 

エージェントはまず、プロセス駆動時に、その引数として渡されるファイルを入力ストリームとしたインタプリタを生成する。このファイルからはエージェントで実行可能な全ての関数が見える状態である。このファイルでOpenInterpreter を実行すると様々なインタプリタが生成され、実行可能になる。 


@node XLサーバ,  , エージェント, インタプリタ実行モデル
@subsection XLサーバ

SetAgentとその他のAgent検索機能、クライアントの認証を備えたエージェントのことである。 

ユーザこと、クライアント側は駆動したいエージェントの名前と、接続モードを指定する。接続モードとは、エージェントの機能をどこまで使用可能かを決める物である。通常考えられる接続モードの決め方としては、rootモードとuserモードの二種類を用意することである。接続モードはサーバの管理者が自由に決めることが出来る。二種類のみならず、 Agent要素を使って様々な種類を用意することが出来る。

また、ユーザのアカウントはエージェントひとつひとつに与えられるというよりは、いくつかのエージェントをまとめたグループを決め、このグループごとに定義する。これにより、一つの機関のエージェントは統一的に管理できるメリットがある。このグループも Agent要素で定義できる。

認証は、クライアントのIPアドレスと、ユーザのパスワードによる認証の二種類がある

サーバはまず、acceptタイプのインタプリタを駆動する。クライアントからのコネクション要求に対して、まず、クライアントのIPアドレスの認証を行う。 

IPアドレスの認証はacceptタイプのOpenInterpreterにおいてPermissionにより指定される情報により行われる。 

認証に失敗すると、クライアント側にDenyが送られ、接続はサーバから強制的に切断される。 

IPが認証されると、対応する接続モードのリストが検索され、コネクションが維持され、インタプリタが割り当てられる。以降クライアントのユーザ認証が行われるが、このとき、ユーザは検索された接続モード以外では接続できない。この機能を利用し、たとえば、 rootモードではネットワークごしに接続することは出来ないなどの指定が出来る。 

この時点で、クライアントからはSetAgentのみが実行可能になる。クライアントから SetAgentにより新しいエージェントを駆動することが出来る。SetAgentは以下のように指定する。 

@sp 1

@example
<SetAgent>
    エージェント名
    接続モード
</SetAgent>
@end example

@sp 1

SetAgent関数は、接続モードがIPの認証結果に違反していないか調べる。違反していれば、 Permission Deniedエラーを返す。 

次に、エージェント名から、エージェントの情報を検索する。このエージェントを駆動するのにパスワードが必要と判断された場合、GetUserInfo要素をクライアント側に送る。この関数の戻り値として、クライアントはユーザ名とパスワードを返し、サーバはこれによりユーザの認証を行う。認証に成功すれば、()が返され、エージェントが駆動される。認証に失敗すると、Permission Denied のエラーを返す。 

GetUserInfoの引数は、グループ、エージェント、接続モードである。クライアント側は、グループと接続モードから必要なユーザ名とパスワードを検索できるようなGetUserInfo 関数をあらかじめ用意しておき、結果を返す。クライアントが異なるサーバに接続する場合にはユーザ名、パスワードのテーブルを増やしていくことにより対応することが出来る。 

もし、対応するグループ、接続モードがなかった場合は、エラーを返えす。すると、 SetAgent は同じエラーを返す。 

ここで、ユーザに必要な認証情報がないことを示し、ユーザ名パスワードを求めるダイアログを出すようなスクリプトを用意すればよい。 


@node プラグマ, 組み込み関数インターフェース, インタプリタ実行モデル, 基本言語仕様
@section プラグマ

以下に挙げるタグはXLではプラグマと呼ばれ、XLパーザに対する命令と解釈される。 

@sp 1

@example
 <?xml version="1.0" .... ?> 
XMLのバージョンと文字コード指定。


 <?xl seq="数字" ?> 
文書の行数の固定


 <?xl code="...." ?> 
文字コードの変更


 <?xl appltag="ignore/remark" ?> 
文字コードの変更
@end example

@sp 1

そのほかの、 <? ... ?> は、appltagの指定に従い、無視されるか、されないかが決まる。appltag="remark"のときは、?から始まるシンボルを一つもった、長さ1の構造型として翻訳される。 


@node 組み込み関数インターフェース,  , プラグマ, 基本言語仕様
@section 組み込み関数インタフェース
データベースのフロントエンドをするインタプリタには、データベース操作を行う固有の関数を組み込まなければならない。その組み込み関数はCで記述し、インタプリタのコードとリンクする。 

まず以下のように、関数を定義する。 

@sp 1

@example
XLtype * pEmbeddedFunction(XLenv * e, XLtype * s) 
@{ s に関する処理。 return 結果 @} 
@end example

@sp 1

sは、List(引数0,引数1,引数2,....)という形を取っている。それぞれの引数は、以下に示す、cLambdaにおけるオプションがApplicativeであれば、評価されてから渡される。 

次に、インタプリタの初期の環境生成時に上記関数を環境に登録するコードを追加する。

@sp 1

@example
XLenv * RootEnv;

main()
@{

    Env0 = cNewEnvironment(0);
    ....
    cDefine(Env0,"EnbeddedFunction",
        cLambda(    // 関数を得る。
                 "Applicative",         // 関数の属性
                 0,                     // 引数評価のための環境
                 pEmbeddedFunction));   // 呼び出される関数名
    ....
@}
@end example

@sp 1

以下に述べるプリミティブは、インタプリタのソースコード内では、 

@sp 1

@example
XLtype *
_プリミティブ名(XLenv * e,XLtype * s)
{

}
@end example

@sp 1

という形で定義されている。新たなるプリミティブを定義するときにこの関数を使うことが出来る。 

また、このプリミティブをそのまま使うには、引数がsで示される構造型にすべてまとめられており、使いにくく、また、カレント環境を参照しない演算関数などでも、eが定義されている。従って、プリミティブを新たにコーディングするために使いやすく引数を整理した関数として、以下のような関数を定義している。 

@sp 1

@example
XLtype *
cプリミティブ名(....)
{

}
@end example

@sp 1

上述main関数の例のなかで定義しているcDefine,cLambdaはこの関数である。引数の評価はEvalが行うので、_プリミティブ名、および、cプリミティブ名、のいずれの形式に置いても、引数の評価はされないことに注意しなければならない。 

XLtype は、XLにおける基本型、構造型の内部形式であり、XLenvは環境の内部形式である。この実際の定義は別途インタプリタのソースコードのヘッダファイルにて明らかにする。
