# 文法

English version

## 目次

- 書式とコメント
- 型・素性と変数
- 型定義
- 素性と制約
- 単一化と述語
- 確定節とクエリー
- モジュール
- 特殊な型
- その他

## 書式とコメント

LiLFeSでは、次のものがトークンとして扱われ、文法の最小単位になります。

- 型名・素性名・変数名
- 数値（整数及び小数）
- 文字列（ `"` で囲まれたもの）
- コメント
- `?-` と`:-` 及び`<-`
- 区切り記号（英数字以外の１バイト文字）で、上の５つを表現するもの以外

これらのトークンの間には、任意の数の空白・タブ・改行を入れることができます。 但し、後述の通り、「素性名とその直後の `\` の間」及び「述語の型名と `(` の間」に空白を入れることはできません。 その他にもいくつかの例外がありますが、順次解説します。 コメントは、次の２つの形が利用できます。

- `%/*` が現れてから、最初の `*/` が現れるまで （コメントのネストは不可）
- `%` から行末まで（上記のコメントの場合を除く）

## 型・素性と変数

３章で述べたように、LiLFeSにおける定数は全て「型」として扱われます。 また、述語も pred 型のsubtypeとなります。型として、英数字・記号・空白・ ２バイト文字（漢字など）を含む全ての文字が使用可能です。 但し、次のような場合は、型名をプログラム中で '（アポストロフィ）で囲む必要があります。

- 英小文字または２バイト文字以外で始まる場合
- 英数字・下線( _ )・２バイト文字以外を含む場合

なお、アポストロフィ自体を含む型名を書くときには、 アポストロフィを２つ重ねて下さい。

プログラム中で使える型
    　　head_subject_schema
    　　東京大学
    　　'ABC'                  % ABC という型
    　　'Good morning!'        % Good morning! という型
    　　'don''t'               % don't という型

素性名として使えるのは、英字または２バイト文字で始まり、 英数字・下線(_)・２バイト文字のみからなる文字列です。 型と同じように，アポストロフィで囲めばどのような文字列も素性名とすることが出来ます． なお、素性をプログラム中に書く際には、必ず \ が直後に必要になります。 素性名と \ の間に空白や改行を置くことはできません。

プログラム中で使える素性
    　　F1\
    　　feature\
    　　素性\
        'Let me see...'\

変数は、英大文字または $ で始まり、英数字・下線・ ２バイト文字のみからなる文字列が使用できます。

プログラム中で使える変数
    　　X
    　　$var
    　　$1
    　　$変数

## 型定義

プログラム中で使用する型（定数及び述語）は予め、 supertype（親の型）とともに型定義がされている必要があります。 新たな素性を定義する場合は、型定義の際に用いる素性を記述します。 同名の型や素性を２度以上定義することはできません。
型定義

型 <- [ supertype１ , supertype２,…… ] + [ 素性１\ 型１( 優先度１), 素性２\ 型２( 優先度２),……].

素性の定義
(1) | `p <- pred.`
(2) | `正方形 <- [長方形 , 菱形].`
(3) | `employ <- [act] + [EMPLOYER\, EMPLOYEE\].`
(4) | `book <- [bot] + [TITLE\string(-5),AUTHOR\name(10),PUBLISHER\co(20)].`
|

<- の左辺には新しい型を書き、右辺には１つ以上のsupertypeを列挙し、 必要ならば + に続けて加える素性を列挙します。 supertypeが１つのみの場合、型名の左右の [ ] は省略できます。 　→例 (1)　２つ以上のsupertypeを , （コンマ）で区切って並べることにより、 多重継承させることができます。 　→例 (2)　素性をつける場合、その型や表示の優先度は省略可能です。 型が省略された場合は bot とみなされます。 また、素性構造の表示の際に、複数の素性がある場合はアルファベット順に表示されます。 　→例 (3)　：この場合、EMPLOYEE, EMPLOYERの順に素性が表示されます。 複数の素性の表示の順序を制御したい場合、括弧内に整数値を書くことにより、 値の小さい順に表示させることができます。なお、負の値も可能です。 　→例 (4)　：この場合、TITLE, AUTHOR, PUBLISHER の順に素性が表示されます。

## 素性と制約

素性を持つ型に素性の制約を加えるには、 `&`の後に素性名と、 素性の型または変数を書きます。２つめの例のように、 素性の中の型の素性も定義することができます。 下位の素性が複数ある場合、例のように括弧を使うことになります。

素性の制約と対応するＡＶＭ
`employ & EMPLOYER\"John" & EMPLOYEE\"Mary"` |
    |~employ         ~|
    | EMPLOYEE:"Mary" |
    |_EMPLOYER:"John"_|
    競走馬 & 名前\ ダンスインザダーク &
             系統\ 'Hail to Reason' &
             父\(種牡馬 & 名前\サンデーサイレンス &
                          系統\ 'Hail to Reason')
    |~競走馬                        ~|
    | 名前:ダンスインザダーク        |
    | 系統:Hail to Reason            |
    |    |~種牡馬                 ~| |
    | 父:| 名前:サンデーサイレンス | |
    |    |_系統:Hail to Reason    _| |
    |_母:bot                        _|

## 単一化と述語

単一化(unification)は、

（型または変数） = （型または変数）

の形で書かれます。 （型または変数）の部分には、素性による制約を書くこともできます。例を参照して下さい。

単一化とその効果
`X = man` | X に man が代入される。
`三角形 = 四角形` | 単一化不能。
`$馬 & 系統\$1 = 父\系統\$1` | $馬 という馬は、父親と同じ系統であることを規定。

述語(predicate)は、Prologと同様に、

述語名( 引数１, 引数２…… )

の形で書かれます。 述語の引数の数(arity)は、その述語が始めて使われた時のarityになります。 同名の述語で、arityが異なるものを指定したい場合の方法については、 Programming Tipsを参照して下さい。 なお、述語の arity の数は、15個までに制限されています。

## 確定節とクエリー

Prolog同様に、プログラムは次の形の確定節で表現します。
確定節

(1) | 述語`.`
(2) | 述語`:-` 構造１ , 構造２,…… .
(3) | `:-` 構造 .

「構造」とは、「単一化」「述語」「カット（後述）」のいずれかを意味します。 (1)の書式は、頭部のみからなる確定節で、述語が真となる引数を規定します。 (2)の書式は、頭部と本体からなる確定節で、左辺が成立する時に、 右辺の構造が成立するか否かを左から順に調べていきます。 (3)は特殊な書式で、頭部がありません。 これは、本体の構造を強制的に実行するもので、主に new_type や trace, strprintln などを実行させる時に使います。詳しくは第５章を参照して下さい。 一方、通常の節で、頭部が無く本体のみである目標節（ゴール）は、 クエリーの形で書きます。
クエリー（目標節）

?- 構造１ , 構造２,…….

`?-` はプロンプトではありません。 LiLFeSでは、Prologと違って、インタプリタへの入力とプログラムの区別がありません。 ファイルから読みこむ場合でも標準入力からの入力でも、いつでも型定義・確定節・ クエリーを書くことができます。 クエリーを入力すると、述語が成功した場合、 述語の中の全ての変数の値（最汎の型）が返され （変数が無い場合は yes ）、 更にバックトラックをして他の解を探すか否かを問われます。 `Enter ';' for more choices, otherwise press ENTER -->` ここで ; を入力すると、他の解を探します。また、述語が失敗した場合には no が返されます。 クエリーと強制実行（:-）の違いは、次の例を参照して下さい。

?- と :- の違い
    > p <- [pred].  q <- [pred].
    > a <- [bot].  b <- [bot].
    > p(a,b).
    > p(X,X) :- q(X).
    > p(b).

    > ?- p(X,Y), q(Y).
    X: a
    Y: b
    Enter ';' for more choices, otherwise press ENTER --> ;
    X: [$1] b
    Y: [$1] ...
    Enter ';' for more choices, otherwise press ENTER --> ;
    no
    （クエリーの場合、変数の値が全て表示され、; を入力するとバックトラックされる）

    > :- p(X,Y), X=Y, q(Y), print(Y).
    b
    （強制実行の場合、何も表示されないが、print 文などにより、
    一部の変数の値を表示することができる。なお、バックトラックは行われない。

カットは、Prologと同様に `!` と書き、そこから先のバックトラックを抑制します。

## モジュール

モジュール化されたLiLFeSプログラムは以下の図のような構成になります．

LiLFeSプログラム
        :- module("list:reverse").

        :- module_interface.
        % 必要なモジュールの読み込み
        :- ensure_loaded("list/append").
        % 他のモジュールから見える型の定義
        reverse <- [pred].

        :- module_implementation.
        % 他のモジュールから見えない型の定義
        reverse_sub <- [pred].
        % 述語の定義
        reverse_sub([], $X, $X).
        reverse_sub([$A|$B], $C, $D) :-
            reverse_sub($B, [$A|$C], $D).
        reverse($X, $Y) :-
            reverse($X, [], $Y).

module, module_interface, module_implementation などは予め定義されているキーワードで，ディレクティブと呼ばれています． これらを用いてプログラムをモジュール化します．

まずファイルの先頭に，モジュール名を宣言します． モジュール名は何でもかまいませんが，一般的には， ファイルの存在するパスの"/"を":"に変換した文字列を用います． 例えば，LILFES_PATH が ~/lilfes/ のとき，~/lilfes/list/reverse.lil というファイルのモジュール名(＝モジュール修飾子)は list:reverse とします．

次に，module_interface の下に，他のモジュールから見える型の定義をします． この例では，reverse 述語を他のモジュールから見えるようにしたいので， その型定義をここに書きます． ここで宣言された型は，他のモジュールからモジュール修飾子(この場合は list:reverse)なしで見えます．

最後に，module_implementation の下に他のモジュールから見えない型の定義と， 述語の定義を記述します． ここで宣言された型はそのままでは他のモジュールから見えませんが， モジュール修飾子をつけると見ることができます． 例えば，reverse_sub という型は他のモジュールからは見ることができませんが， list:reverse:reverse_sub という型は他のモジュールから見ることができます． 述語の定義はここに書くのが一般的ですが， 実際はプログラム中のどこに書いてもかまいません．

他のモジュールで定義されている型や述語を利用したいときは， ensure_loaded ディレクティブを用いてそのモジュールを読み込みます． 上の例では，list/append (モジュール名はおそらく "list:append") というモジュールを読み込んでいるので， そのモジュールで定義されている型や述語をここで使うことができます． ensure_loaded は module_interface 部にも module_implementation 部にも記述することができます (module_interface 部か module_implementation 部に記述するかによって， 他のモジュールから見える型が異なってきます． これは通常の型宣言と同様になります)．

### 注意

- ensure_loaded ディレクティブは，以下の順番でモジュールを検索します．
  - "ensure_loaded" が呼ばれたファイルが存在するパス．
  - lilfes が起動されたパス．
  - LILFES_PATH で指定されたパス．
  - デフォルトのモジュールパス (lilfesのインストール時に決定． デフォルトでは /usr/local/share/lilfes/)．
- デフォルトでは，あるモジュールで定義された型の subtype を別のモジュールで定義することができません． 他のモジュールでの subtype の定義を許すには， 型定義の後ろに "./ extendable" をつけます．

      二等辺三角形 <- [三角形] ./ extendable.

  あるモジュールで定義される全ての型について他のモジュールで subtype の定義を許すときは， module_extendable ディレクティブを用います．
- デフォルトでは，モジュール化されたプログラムでは assert で新たな確定節を追加することはできません．ある述語の assert を許したいときは， dynamicディレクティブを用います．
- デフォルトでは，述語の型定義と述語定義は， 同じモジュール内に記述しなければなりません． 他のモジュールでの述語定義を許すには， multilfileディレクティブを用います．

## 特殊な型

### リスト

リストは、次のいずれかの方法で表記できます。
リスト

[要素１ , 要素２ , ……]
[先頭の要素 | 後続のリスト]

これらの形で表記されたリストは、list型の subtype になります。

### 数値

数値として、整数・小数が扱えます。これらはそれぞれ、integer 型、float 型の subtype になります。

### 文字列

文字列は、 " " で囲われたもので、string 型の subtype になります。

## その他

### Don't care

下線（`_` ）で始まる変数は、Don't care項として扱われ、任意の型との単一化に成功します。 従って、確定節中の述語の引数のうち、参照しないものに関しては、 `_` で埋めておけば、値が何であれ実行に影響を与えません。 何とでも単一化できるので、bot と書くのと同じ意味になります。

### 論理和

確定節やクエリーの右辺の節集合（`,` で区切られたもの、即ち論理積）を、 `;` で区切ることにより、論理和を表現できます。 なお、
`p(a) = q(x), q(y) ; r(z).`
は、
`p(a) = q(x), q(y).
p(a) = r(z).`
と等価になります。

### if文

確定節の右辺の構造として、if 〜 then 〜 else 〜 に相当する構造を、次の 書式で書くことができます。
if文

条件の構造１ , 条件の構造２ , … -> 成功した場合の述語 ; 失敗した場合の述語

左辺（`->`の左側）が成功した場合、「成功した場合の述語」が、 失敗した場合、「失敗した場合の述語」が実行されます。なお、
`P :- Q -> R ; S.`
という式は、
`P :- Q , ! , R.`
`P :- S.`
と等価です。

if文
    > p <- [pred].
    > check <- [pred].
    > a <- [bot].
    > b <- [bot].
    > p(a).
    > check(X) :- p(X) -> strprintln("succeed") ; strprintln("failed").
    > ?- check(a).
    succeed
    ; (Current occupation: 9 heap, 3 stack, 10 trail cell(s), 39 instructions)
    yes
    > ?- check(b).
    failed
    ; (Current occupation: 9 heap, 3 stack, 8 trail cell(s), 62 instructions)
    yes

### 否定

Prologと同様に`\+` と書き、述語が失敗した時に成功するものです。

否定
    > p <- [pred].
    > a <- [bot].
    > b <- [bot].
    > p(a).
    > ?- \+ p(a).
    ; (Current occupation: 15 heap, 23 stack, 16 trail cell(s), 101 instructions)
    no
    > ?- \+ p(b).
    ; (Current occupation: 11 heap, 3 stack, 10 trail cell(s), 122 instructions)
    yes

### 単一化不能

確定節の本体の構造として、「単一化不能」が許されます。`\=` は`=` の逆で、単一化に失敗した場合に、成功となります。なお、 左辺の値と `\=` の前には空白が必要となるので注意して下さい。空 白が無い場合、素性とみなされてしまいます。

単一化不能
    > mammal <- bot.
    > birl <- bot.
    > cat <- mammal.
    > ?- mammal \= cat.
    ; (Current occupation: 10 heap, 20 stack, 16 trail cell(s), 12 instructions)
    no
    > ?- mammal \= bird.
    ; (Current occupation: 10 heap, 3 stack, 12 trail cell(s), 27 instructions)
    yes

3章 型と素性 5章 エラーメッセージ

目次 LiLFeSドキュメント LiLFeSホームページ 辻井研究室