say yes .. nprolog

say yes

前回からの積み残しで、say yes 問題(オイラーが勝手にこう呼んでいるだけだけど)を何とかしたい。

p(A,B,C) :- A < B, A^2 + B^2 == C^2.

?- p(3,4,5).
no

ピタゴラスの三角形が成り立っているかの問い。say yes を希望するも、つれない返事。

prologの資料を呼んだよ。根本解決は

?- 3^2 + 4^2 =:= 5^2.
yes

数値用の比較を使えって事。こんな記号の羅列は誰の発案だ?

お約束のgdb観光。

(gdb) bt
#0  b_numeq (arglist=7000017, rest=174) at builtin.c:1655
#1  0x1503e32e in prove (goal=7000018, bindings=0, rest=174, n=1) at main.c:407
#2  0x1503d424 in prove_all (goals=7000003, bindings=0, n=1) at main.c:370
#3  0x1503cc64 in query (x=902) at main.c:299
#4  0x1503c8c2 in main (argc=1, argv=0xcf7eb424) at main.c:234

builtin.cに、関数が登録されてた。

void initbuiltin(void){
    definfix(">",b_greater,700,XFX);
    definfix("<",b_smaller,700,XFX);
    definfix("=<",b_eqsmaller,700,XFX);
    definfix(">=",b_eqgreater,700,XFX);
    definfix("=:=",b_numeq,700,XFX);
    definfix("=\\=",b_notnumeq,700,XFX);
    definfix("=/=",b_notnumeq,700,XFX);
    definfix("@<",b_atsmaller,700,XFX);
     :

prolog側の関数とC語側の関数の対応表。700,XFXとかは、参照した資料に説明が有った記憶があったけど、意味忘れた。C語のビルトイン関数は、b_ を付ける事にしてるようだ。

  int b_numeq(int arglist, int rest){
      int n,arg1,arg2;

B =>  n = length(arglist);
      if(n == 2){
          arg1 = eval(car(arglist));
          arg2 = eval(cadr(arglist));
          if(!numberp(arg1))
              error(NOT_NUM,"=:= ",arg1);
          if(!numberp(arg2))
              error(NOT_NUM,"=:= ",arg2);

          if(numeqp(arg1,arg2))
              return(YES);
          else
              return(NO);
      }
      return(NO);
  }

非常に素直な関数になっている。ここに来た時は、α簡約が済んでいると見受けた。

ちなみに == での比較は b_equalp にあって

if(anoymousp(arg1) && anoymousp(arg2))
    return(NO);
else if(equalp(arg1,arg2))

数値を除くものを何でも比較しちゃう。何でもってのは、null、structure、listの比較になる。今回の場合なら、リストが長さが違うから、等しく無いよって事だろうね。

gprolog詐欺解消

emacsと連携する為、nplをgprologと名称変更して使ってた。けど、オレオレ詐欺みたいで気分が悪い。大体gprologとnplの作者さんに失礼である。

prolog.elをざっと見した結果、prologでもplでもよさそうって推測出来たんで、慎ましくplを名乗る事にした。emacsの設定ファイルに書いておいた、(setq prolog-system 'gnu)を削除したよ。これでも、ちゃんとplを見つけてくれて、ちゃんと使えた。肩の荷が下りたよ。

nplは足が速くて、Ver 1.4 で、traceやspyが導入されたけど、OpenBSDでは、面倒なので1.2のまま使ってる。いや、機能が増えると目移りするんで、基本を押さえるのさ。新しいmakefileを見ると、地味にmake installが出来るようになってた。

DEST = /usr/local/bin

install: $(NPL)
        install -s  $(NPL) $(DEST)

install時に、余計な物(debug情報等)を削除しつつ実行するんだな。これは、オイラーみたいなgdb好きには、困った挙動だ。

それとmain.c中に

fp = fopen("library/compiler.pl","r");
if(fp != NULL){
    fclose(fp);
    b_consult(list1(makeconst("library/compiler.pl")),NIL);
    predicates = NIL;
}

こういう、コンパイラーを呼んでるけど、ちと困りそう。Verが上がって性能が出るようになったら、本格的に対処されるのかな?

-ldl

マニュアルによると、 compile_file('xxx.pl') すると、C語に変換してgccでそれをオブジェクトファイルにコンパイルするとな。xxx.oが出来るんだな。後はそれをconsult('xxx.o')すれば使えるとな。

それで、ダイナミックロードのライブラリィーが必要になるのね(only need linux)。

grep  -nH -e gcc library/*
library/compiler.pl:58:    jump_invoke_gcc(X).
library/compiler.pl:61:    jump_invoke_gcc(X).
library/compiler.pl:99:gcc -O3 -w -shared -fPIC -o <filenam>.c <filename>.o <option>
library/compiler.pl:101:jump_invoke_gcc(X) :-
library/compiler.pl:108:    atom_concat('gcc -O3 -w -shared -fPIC -o ',Files,Sys),

gccを呼び出して、コンパイルするまで、prolog語で書かれている。後はそれの読み込みだな。

grep  -nH -e dlopen *.c
link.c:28:    hmod = dlopen(str, RTLD_LAZY);

この近辺を見ると、必須の手続きを呼び出している(のかな?)。引数無しから、4引数までに対応だな。

edit

マニュアルによると、edit関数で、prologの中からeditorを呼び出せるようだ。 で、使うeditorは、nanoって言う、リナなら必ず入っているやつみたい。 オイラーは、リナをインストールすると、直ぐにEDITOR=emacs って口だから、環境変数で手軽に切り替えられるといいね。

特に、スーパーユーザーになって、何かしようとしてnanoがやって来るとげんなりだ。本当は軽いviあたりがスーパーユーザー向けなんだろうけど、リナのviはもどきでvimなんで、これも困った口だ(オイラーの個人的な意見だから、石投げないでね)。

編集関数は、builtin.c に有った。ズバリの関数名 b_nano でね。 何か所か、nanoって文字列が出て来るけど、本質部分は、

char str0[STRSIZE] = "nano ";

だけだ。他にある"nano"は、"editor"ぐらいで、ごまかしてしまっても良いはず。後でOpenBSDでやってみよう。何たってOpenBSDにはnano君は居ないはずだから。

compile

debian:nprolog$ cat z.pl
p(A,B,C) :- A < B, A^2 + B^2 =:= C^2.

Bugが取れた、超ミニをコンパイルしてみる。勿論、library/が見える所でやらないといけない(だろう)、上の考察によればね。gitから落としたその中です。

debian:nprolog$ ./npl
N-Prolog Ver 1.4
?- compile_file('z.pl').
pass1
pass2
Existence error

compile_file が赤字になって、危険と知らせてきた。構わず実行すると、見事にエラー。

debian:nprolog$ cat z.c
#include "jump.h"
jump_pred_data(p,type1)debian:nprolog$

C語ファイル作成途中のようだなあ。それにjump.h も無いようだし。

compiler.plをチラ見。いきなりC語(の雛型)が出てきたのでびっくり。めげずに見て行くと

compile_file(X) :-
    jump_pass1(X),
    jump_pass2(X),
    jump_invoke_gcc(X).

2passでC語を作成、それをgccにかけている。pass1は無事に通過して、pass2で引っかかっているのだな。pass1はtail recursive optimize、pass2がC語作成とコメントが入ってた。

マニュアルにある例を試しにやってみる。

?- compile_file('queens.pl').
pass1
pass2
compiling test16
compiling test
compiling test1
compiling queen
compiling queen_2
compiling qdelete
compiling tail nodiag
invoke GCC
queens.c:1:10: fatal error: jump.h: No such file or directory
 #include "jump.h"
          ^~~~~~~~
compilation terminated.
yes

今度はベタなC語ファイルが出来た。indent queens.c して整形する。

#include "jump.h"
int b_test16 (int arglist, int rest);
int
b_test16 (int arglist, int rest)
{
  int varX, n, body, save1, save2;
  save2 = Jget_sp ();
  n = Jlength (arglist);
  if (n == 0)
    {
      varX = Jmakevariant ();
      save1 = Jget_wp ();
       :

ふむ、私設のjump.hを要求してるんだな。testsの下にひっそりと置いてあった。これをqueens.cと同列の場所(今の場合だと/tmpの実験場)に引っ張り出してみる。 そしたら、ちゃんとコンパイル出来て、オブジェクトファイルが作成された。

debian:tmp$ npl
N-Prolog Ver 1.4
?- ['queens.o'].
yes
?- test.
[1,3,6,8,2,4,9,7,5]
[1,3,7,2,8,5,9,4,6]
  :

とか、

debian:tmp$ npl -c queens.o
N-Prolog Ver 1.4
?- test.
[1,3,6,8,2,4,9,7,5]
[1,3,7,2,8,5,9,4,6]
  :

アプリのように、-c でロードしたら、いきなり走り出すようには、出来ないのかな。ロードモジュールに、そんな危険な事をさせるって、いかがなものかと、OpenBSD小僧は密かに、思ったり、思わなかったり。。。

いや、Linuxなら、イケイケ・ドンドン、勇ましくが信条だから、そういうのも許す(かな)。

それはそうと、サンプルがちゃんとコンパイル出来て、オイラーの書いたピタゴラスの三角形チェックがエラーになるのは、数値比較がまだ実装されていないのではなかろうか。 急ぐ事は無いので、コンパイラーの骨格をしっかり固めてください。

そして最後は、コンパイラー自身をコンパイルするって言う、究極の試験に合格する事を祈ります。

at OpenBSD

ごちゃごちゃ書いたのを、OpenBSDで試してみる。

make install

DEST = /home/sakae/bin

install: $(NPL)
        mv $(NPL) $(DEST)/pl

これは、オイラーの所用のインストールタグ。installコマンドの使用を止めて、ただmvするだけ。installコマンドのオプション具合からshell scriptだろうと思ったらelfなファイルだった。暇なら、ソースを拝んでおk。

edit

一応、viとemacsで、仕様通りの動きをする事確認済。環境変数から取り込むのは試していない。環境変数は、一般的に、EDITORあたりだな。凝るならEDITOR4NPL あたりだろうか。

compile with cc

オリジナルのgccを使ったやつはOK。ちゃんとオブジェクトファイルでも読み込んで実行出来た。OpenBSDでは、-ldl が必要無いってのが確認出来た。

気を良くして、OpenBSDのデフォのコンパイラーであるccでも試してみた。無事に動いたよ。

-rwxr-xr-x  1 sakae  wheel  10924 Nov  8 15:56 queens.o*  ;; by cc
-rwxr-xr-x  1 sakae  wheel  15276 Nov  8 16:00 queens.o*  ;; by gcc

こんな具合に、オブジェクトファイルのサイズに差が付いた。

お遊びで、自分自身をコンパイルして、変身出来るか確認。

?- compile_file('compiler.pl').
pass1
pass2
Existence error

どんなC語を吐き出した?

vbox$ cat compiler.c
#include "jump.h"
jump_pred_data(compile_file,type1)vbox$

ピタゴラスの三角形の時と同じ症状だなあ。予想では、何かが足りないなんだけどな。少々探検してみる。

目標は、ピタゴラスの三角形の =:= が有るかだな。

jump_eval_form(X + Y) :-
        write('Jplus('),
    jump_eval_form(X),
    write(','),
    jump_eval_form(Y),
    write(')').

こういうのがづらーと並んでいるけど、=:= 相当は見当たらない。この例だと、JplusってC語の関数を作り出している。 この定義は、jump.hに有るな。

#define Jcons(x,y)   (f2[0])(x,y)
#define Jplus(x,y)   (f2[1])(x,y)
#define Jminus(x,y)  (f2[2])(x,y)
  :

2引数の関数ばかりを集めた関数テーブルとな。そして、本体系との対応は、link.cにあるんだな。

//argument-2 type
init_f2(0,(tpred)cons);
init_f2(1,(tpred)f_plus);
init_f2(2,(tpred)f_minus);
 :

これは、大変だ。3ヶ所のファイルをいじらないと、増設出来ないとな。機械的な作業で済みそうだけど、間違いなくやるの大変そう。オイラーには絶対に無理です。断言出来ます。


This year's Index

Home