LR parser on Prolog (cont'd - compile)

前回LR parser on Prolog - 鯨飲馬食コードで書いたものを、Prologを用いてコンパイル(?)するようなもののメモ。少し前「Prologのファイル入出力は変態だ」という意見を聞いたんだけど、どうなんだろう。

先にコードを貼り付けておく。

rule_compile(In_File,Out_File):-
  open(Out_File,write,OutStream),
  compile(In_File),
  compile_rule(OutStream),
  compile_rule_goto(OutStream),
  write_const(OutStream),
  close(OutStream).

compile_rule(OutStream):-
  entry(S,W,Act),
  format("S=~w W=~w Act=~w~n",[S,W,Act]),
  write1(S,W,Act,OutStream),
  fail.
compile_rule(OutStream):-format(OutStream,"~n",[]).

write1(S,W,sh(Sn),OutStream):-
  format(OutStream,
"lr([~w|Ss0],[~w|Ws0],Os,Out):- lr([~w,~w|Ss0],Ws0,Os,Out).
",[S,W,Sn,S]
  ).
write1(S,W,re(R),OutStream):-
  rule(R,LHS,RHS),
  length(RHS,Len),
  format(OutStream,
"lr([~w|Ss0],[~w|Ws0],Os,Out):-
  pop(~w,[~w|Ss0],[S0|Ss2]),entry(S0,~w,go(Sg)),lr([Sg,S0|Ss2],[~w|Ws0],[~w|Os],Out).
",[S,W,Len,S,LHS,W,R]
  ).
write1(S,W,accept,OutStream):-
  format(OutStream,
"lr([~w|_],[~w|_],Out,Out).
",[S,W]
  ).

compile_rule_goto(OutStream):-
  entry(S,W,go(Sg)),
  format(OutStream,
"entry(~w,~w,~w).
",[S,W,go(Sg)]
),fail.
compile_rule_goto(OutStream):-format(OutStream,"~n",[]).

write_const(OutStream):-
  start_state(S0),
  format(OutStream,
"pop(0,S,S).
pop(N,[_|S],Xs):-
  M is N-1, pop(M,S,Xs).

lr(Ws):- lr([~w],Ws,[],_).
lr(Ws,Out):- lr([~w],Ws,[],Out).

",[S0,S0]
  ).

前回挙げた、導出規則とLR表を述語で書いた例をもう一度。

rule(1,s,[np,vp]).
rule(2,np,[np,pp]).
rule(3,np,[det,n]).
rule(4,np,[n]).
rule(5,np,[pron]).
rule(6,vp,[v,np]).
rule(7,vp,[vp,pp]).
rule(8,pp,[p,np]).

start_state(0).

entry(0,det,sh(1)).
entry(0,n,sh(5)).
entry(0,pron,sh(2)).
entry(0,np,go(4)).
entry(0,s,go(3)).
entry(1,n,sh(6)).
entry(2,p,re(5)).
entry(2,v,re(5)).
entry(2,$,re(5)).
entry(3,$,accept).
entry(4,p,sh(7)).
entry(4,v,sh(8)).
entry(4,pp,go(10)).
entry(4,vp,go(9)).
entry(5,p,re(4)).
entry(5,v,re(4)).
entry(5,$,re(4)).
entry(6,p,re(3)).
entry(6,v,re(3)).
entry(6,$,re(3)).
entry(7,det,sh(1)).
entry(7,n,sh(5)).
entry(7,pron,sh(2)).
entry(7,np,go(11)).
entry(8,det,sh(1)).
entry(8,n,sh(5)).
entry(8,pron,sh(2)).
entry(8,np,go(12)).
entry(9,p,sh(7)).
entry(9,$,re(1)).
entry(9,pp,go(13)).
entry(10,p,re(2)).
entry(10,v,re(2)).
entry(10,$,re(2)).
entry(11,p,sh(7)).
entry(11,p,re(8)).
entry(11,v,re(8)).
entry(11,$,re(8)).
entry(11,pp,go(10)).
entry(12,p,sh(7)).
entry(12,p,re(6)).
entry(12,$,re(6)).
entry(12,pp,go(10)).
entry(13,p,re(7)).
entry(13,$,re(7)).
entry(13,pp,go(10)).

これらのデータを入力に用いると、こんな出力が得られる。

lr([0|Ss0],[det|Ws0],Os,Out):- lr([1,0|Ss0],Ws0,Os,Out).
lr([0|Ss0],[n|Ws0],Os,Out):- lr([5,0|Ss0],Ws0,Os,Out).
lr([0|Ss0],[pron|Ws0],Os,Out):- lr([2,0|Ss0],Ws0,Os,Out).
lr([1|Ss0],[n|Ws0],Os,Out):- lr([6,1|Ss0],Ws0,Os,Out).
lr([2|Ss0],[p|Ws0],Os,Out):-
  pop(1,[2|Ss0],[S0|Ss2]),entry(S0,np,go(Sg)),lr([Sg,S0|Ss2],[p|Ws0],[5|Os],Out).
lr([2|Ss0],[v|Ws0],Os,Out):-
  pop(1,[2|Ss0],[S0|Ss2]),entry(S0,np,go(Sg)),lr([Sg,S0|Ss2],[v|Ws0],[5|Os],Out).
lr([2|Ss0],[$|Ws0],Os,Out):-
  pop(1,[2|Ss0],[S0|Ss2]),entry(S0,np,go(Sg)),lr([Sg,S0|Ss2],[$|Ws0],[5|Os],Out).
lr([3|_],[$|_],Out,Out).
lr([4|Ss0],[p|Ws0],Os,Out):- lr([7,4|Ss0],Ws0,Os,Out).
lr([4|Ss0],[v|Ws0],Os,Out):- lr([8,4|Ss0],Ws0,Os,Out).
lr([5|Ss0],[p|Ws0],Os,Out):-
  pop(1,[5|Ss0],[S0|Ss2]),entry(S0,np,go(Sg)),lr([Sg,S0|Ss2],[p|Ws0],[4|Os],Out).
lr([5|Ss0],[v|Ws0],Os,Out):-
  pop(1,[5|Ss0],[S0|Ss2]),entry(S0,np,go(Sg)),lr([Sg,S0|Ss2],[v|Ws0],[4|Os],Out).
lr([5|Ss0],[$|Ws0],Os,Out):-
  pop(1,[5|Ss0],[S0|Ss2]),entry(S0,np,go(Sg)),lr([Sg,S0|Ss2],[$|Ws0],[4|Os],Out).
lr([6|Ss0],[p|Ws0],Os,Out):-
  pop(2,[6|Ss0],[S0|Ss2]),entry(S0,np,go(Sg)),lr([Sg,S0|Ss2],[p|Ws0],[3|Os],Out).
lr([6|Ss0],[v|Ws0],Os,Out):-
  pop(2,[6|Ss0],[S0|Ss2]),entry(S0,np,go(Sg)),lr([Sg,S0|Ss2],[v|Ws0],[3|Os],Out).
lr([6|Ss0],[$|Ws0],Os,Out):-
  pop(2,[6|Ss0],[S0|Ss2]),entry(S0,np,go(Sg)),lr([Sg,S0|Ss2],[$|Ws0],[3|Os],Out).
lr([7|Ss0],[det|Ws0],Os,Out):- lr([1,7|Ss0],Ws0,Os,Out).
lr([7|Ss0],[n|Ws0],Os,Out):- lr([5,7|Ss0],Ws0,Os,Out).
lr([7|Ss0],[pron|Ws0],Os,Out):- lr([2,7|Ss0],Ws0,Os,Out).
lr([8|Ss0],[det|Ws0],Os,Out):- lr([1,8|Ss0],Ws0,Os,Out).
lr([8|Ss0],[n|Ws0],Os,Out):- lr([5,8|Ss0],Ws0,Os,Out).
lr([8|Ss0],[pron|Ws0],Os,Out):- lr([2,8|Ss0],Ws0,Os,Out).
lr([9|Ss0],[p|Ws0],Os,Out):- lr([7,9|Ss0],Ws0,Os,Out).
lr([9|Ss0],[$|Ws0],Os,Out):-
  pop(2,[9|Ss0],[S0|Ss2]),entry(S0,s,go(Sg)),lr([Sg,S0|Ss2],[$|Ws0],[1|Os],Out).
lr([10|Ss0],[p|Ws0],Os,Out):-
  pop(2,[10|Ss0],[S0|Ss2]),entry(S0,np,go(Sg)),lr([Sg,S0|Ss2],[p|Ws0],[2|Os],Out).
lr([10|Ss0],[v|Ws0],Os,Out):-
  pop(2,[10|Ss0],[S0|Ss2]),entry(S0,np,go(Sg)),lr([Sg,S0|Ss2],[v|Ws0],[2|Os],Out).
lr([10|Ss0],[$|Ws0],Os,Out):-
  pop(2,[10|Ss0],[S0|Ss2]),entry(S0,np,go(Sg)),lr([Sg,S0|Ss2],[$|Ws0],[2|Os],Out).
lr([11|Ss0],[p|Ws0],Os,Out):- lr([7,11|Ss0],Ws0,Os,Out).
lr([11|Ss0],[p|Ws0],Os,Out):-
  pop(2,[11|Ss0],[S0|Ss2]),entry(S0,pp,go(Sg)),lr([Sg,S0|Ss2],[p|Ws0],[8|Os],Out).
lr([11|Ss0],[v|Ws0],Os,Out):-
  pop(2,[11|Ss0],[S0|Ss2]),entry(S0,pp,go(Sg)),lr([Sg,S0|Ss2],[v|Ws0],[8|Os],Out).
lr([11|Ss0],[$|Ws0],Os,Out):-
  pop(2,[11|Ss0],[S0|Ss2]),entry(S0,pp,go(Sg)),lr([Sg,S0|Ss2],[$|Ws0],[8|Os],Out).
lr([12|Ss0],[p|Ws0],Os,Out):- lr([7,12|Ss0],Ws0,Os,Out).
lr([12|Ss0],[p|Ws0],Os,Out):-
  pop(2,[12|Ss0],[S0|Ss2]),entry(S0,vp,go(Sg)),lr([Sg,S0|Ss2],[p|Ws0],[6|Os],Out).
lr([12|Ss0],[$|Ws0],Os,Out):-
  pop(2,[12|Ss0],[S0|Ss2]),entry(S0,vp,go(Sg)),lr([Sg,S0|Ss2],[$|Ws0],[6|Os],Out).
lr([13|Ss0],[p|Ws0],Os,Out):-
  pop(2,[13|Ss0],[S0|Ss2]),entry(S0,vp,go(Sg)),lr([Sg,S0|Ss2],[p|Ws0],[7|Os],Out).
lr([13|Ss0],[$|Ws0],Os,Out):-
  pop(2,[13|Ss0],[S0|Ss2]),entry(S0,vp,go(Sg)),lr([Sg,S0|Ss2],[$|Ws0],[7|Os],Out).

entry(0,np,go(4)).
entry(0,s,go(3)).
entry(4,pp,go(10)).
entry(4,vp,go(9)).
entry(7,np,go(11)).
entry(8,np,go(12)).
entry(9,pp,go(13)).
entry(11,pp,go(10)).
entry(12,pp,go(10)).
entry(13,pp,go(10)).

pop(0,S,S).
pop(N,[_|S],Xs):-
  M is N-1, pop(M,S,Xs).

lr(Ws):- lr([0],Ws,[],_).
lr(Ws,Out):- lr([0],Ws,[],Out).

こんな風に前処理しておけば探索の手間を減らせるので、一般的には高速になるはず。とはいえ、前回も書いたように、このLR構文解析器は実用には耐えないのだけれど。