Contributed by Nick Nicholas, nsn@mullian.ee.mu.oz.au, 1993-8-7.

This is the code to my Lojban Semantic Analyser, as reported on in lojban-list
and _ju'i lobypli_. It is written in NU-Prolog (for enquiries on getting this
version of Prolog, mail lee@munta.cs.mu.oz.au); its input file is the output
of running the Lojban Parser on the input text with the -dl option, and then
filtering this through the following LEX program:


%%
"2;0;30moi".+\n	;
"Copyright 1991".+\n	;
"Space used".+\n	;
"lexing (selma'o CMENE): ".+\n	{printf("cmene ");
				yyless(24);};
"lexing (selma'o BRIVLA): ".+\n	{printf("brivla ");
				yyless(25);};
"lexing (selma'o EOT): EOT"\n		{printf("end_of_lex_list\n");};
"lexing".+\n	;
"{"	;
"("	;
"["	;
"}"	;
")"	;
"]"	;
"<"	;
">"	;
"'"	output('h');
[A-Z]	output(tolower(yytext[0]));
%%
char *lowerstring(char *x)
{int i;
for(i=0;x[i]!='\0';i++)
x[i]=tolower(x[i]);
return x;}

***

% Here follows the Prolog code.
?- pure(bridi_tail_50,4).
?- pure(ask1,2).
?- pure(permuteQ1,3).
?- pure(deleteQ,6).
?- pure(statement_42,3).
?- pure(tail_terms_71,4).
?- pure(terms_80,6).
?- pure(selbri,5).
?- pure(tanru_unit_B_152,5).
?- pure(se_596,3).
?- pure(anaphorise,4).
?- pure(ask,3).
?- dynamic uttered/1.
/* This subsection of lojban grammar implements predications involving
proper names, variable names, or values satisfying predicates as arguments,
also, restrictive relative clauses, conjunctions, and event abstractions; 
it implements the following BNF subset of lojban:

text = [I|NIhO...] paragraphs FAhO
paragraphs = paragraph [NIhO paragraphs]
paragraph = paragraph1 [I [jek] paragraph1]...
paragraph1 = sentence
sentence = briditail | sentence1
sentence1 = term... [CU] briditail | gek sentence1 gik sentence
briditail = briditail3 [gihek briditail3 tailterms]
briditail3 = selbri tailterms
tailterms = [term] VAU
term = sumti | (tag | FA ) (sumti | KU)
sumti = sumti_1
sumti_1 = sumti_4 [joik-ek sumti_4] ...
sumti_4 = [quantifier] sumti_5 [relative-clauses]
sumti_5 = KOhA | letteral-string BOI | LA CMENE... | LE sumti-tail KU
sumti-tail = selbri
relative-clauses = relative-clause
relative-clause = NOI sentence KUhO
selbri = tanru_unit_1
tanru_unit_1 = tanru_unit_2 [linkargs]
tanru_unit_2 = BRIVLA | SE tanru_unit_2 | NU sentence KEI
linkargs = BE term [links] BEhO
links = BEI term [links]
number = PA [PA]...
ek = A
gihek = GIhA
joik-ek = ek
gek = GA
gik = GI
quantifier = number BOI
letteral-string = letteral [PA | letteral] ...
letteral = BY
tag = BAI

(A note on vocabulary: bridi is lojban for predication; selbri = predicate;
sumti = argument; cmene = name). */

text_0 --> 		text_B_2(Answers),
			{print('Answers to the questions: '),
			print(Answers),nl},
			text_B_2_a.
text_B_2_a -->		[];[faho].
text_B_2(Answer) --> 	i_819(_), text_B_2(Answer);
	     		para_mark_410, text_C_3(Answer);
	     		text_C_3(Answer).
text_C_3(Answers) --> 	paragraphs_4(_,Answers).
paragraphs_4(P,Answers) --> 	
			paragraph_A_10(statement,P,P,Answers1),
			{postproc(P,P1),
			assert(uttered(P1)),
			print(P1),nl},
			paragraphs_4_a(Answers2),
			{append(Answers1,Answers2,Answers)}.
paragraphs_4_a(Answers) -->	
			para_mark_410, 
			paragraphs_4(_,Answers).
paragraphs_4_a([]) --> 	[].
% statements get added to the database; questions get asked, and are not added
% questions are considered interpolation in text: in text: [sentence1, 
% ? question, CONNECTIVE sentence2], sentence1 and sentence2 are considered
% joined by CONNECTIVE
paragraph_A_10(X,Sentence,Para,Answers) -->
			paragraph_B_11(P),
			paragraph_A_10_a(X,Sentence,P,Para,Answers).
paragraph_A_10_a(statement,c(CONN,PrevSent,NextSent),PrevSent,Para,Answers) -->
                   	i_819(xu), 
			paragraph_A_10(question(CONN),NextSent,Para,Answers).
paragraph_A_10_a(statement,c(CONN,PrevSent,NextSent),PrevSent,Para,Answers) -->
                   	i_819(CONN), 
			{CONN \= xu},
			paragraph_A_10(statement,NextSent,Para,Answers).
paragraph_A_10_a(question(CONN),NextSent,PrevSent,Para,[Answer|Answers]) -->
                   	i_819(xu), 
			{ask(PrevSent,Para,Answer)},
			paragraph_A_10(question(CONN),NextSent,Para,Answers).
% connective functor: fragments P and T are connected by binary connective CONN
paragraph_A_10_a(question(CONN),NextSent,PrevSent,Para,[Answer|Answers]) -->
			i_819(CONN), 
			{CONN  \= xu,
			ask(PrevSent,Para,Answer)},
			paragraph_A_10(statement,NextSent,Para,Answers).
paragraph_A_10_a(statement,P,P,Para,[]) --> 	
			[].
paragraph_A_10_a(question(seq),[],PrevSent,Para,[Answer]) --> 	
			[],
			{ask(PrevSent,Para,Answer)}.
paragraph_B_11(P) --> 	sentence_40(P).
sentence_40(Predication) --> 	
			bridi_tail_50([],2,Predication); 
			sentence_A_41(Predication).
sentence_A_41(c(CONN,P1,P2)) -->	
			gek_807(CONN),sentence_A_41(P1),
			gik_816,sentence_40(P2).
sentence_A_41(P) -->	statement_42(_,P).

% prolog counts arguments as we go; start at 1. Having read all arguments
% before the bridi_tail, we will be up to the Nth argument --- where 
% bridi_tail picks up the count after the predicate itself
% Predication1 is the tail of Predication supplied by bridi_tail
statement_42(Foreargs,Predication) -->	
			terms_80(Foreargs,1,_,N,Predication,Predication1), 
			{nonvar(N)},
			statement_42_a(Foreargs,N,Predication1).

statement_42_a(Foreargs,N,Predication1) -->
			{nonvar(N)},
			bridi_tail_50(Foreargs,N,Predication1);
			front_gap_451, 
			{nonvar(N)},
			bridi_tail_50(Foreargs,N,Predication1).
			
bridi_tail_50(Foreargs,N,P) -->
			bridi_tail_A_51(Foreargs,N,P).
bridi_tail_A_51(Foreargs,N,P) -->
			{append(Foreargs,Tailargs,ArgsIn)},
			bridi_tail_C_53(ArgsIn,N,P1),
			gihek_818(Conn),
			bridi_tail_C_53(ArgsIn,N,P2),
			tail_terms_71(Tailargs,N,P,c(Conn,P1,P2)).
bridi_tail_A_51(Foreargs,N,P) -->
			bridi_tail_C_53(Foreargs,N,P).

bridi_tail_C_53(Foreargs,N,Predication,SIn,SOut):-
			append(Foreargs,Tailargs,ArgsIn),
			selbri_130(ArgsIn,Bridi,SIn,SOut1), 
			tail_terms_71(Tailargs,N,Predication,Bridi,SOut1,SOut2),
			SOut2 = SOut.

tail_terms_71([],_,P,P) -->	
			vau_gap_456.
tail_terms_71(Tailargs,N,P,P1) -->	
			{nonvar(N)},
			terms_80(Tailargs,N,_,_,P,P1), vau_gap_456.

% we anticipate the head of the list to be the Mth argument. We find its
% successor will be the Nth. The successor of the last argument in the list
% (return argument) will be the Xth.
terms_80([Arg|Tail],M,N,X,P,P1) -->  
			term_81(Arg,M,N,P,P2), terms_80_a(Tail,N,X,P2,P1).
terms_80_a(Tail,N,X,P2,P1) -->
			terms_80(Tail,N,_,X,P2,P1).
terms_80_a([],N,N,P1,P1) --> [].

% default: arguments are in seq. x1 x2 x3, so successor of x(n) is x(n+1)
term_81(term(M,Arg),M,N,P,P1) -->	
			{nonvar(M),N is M + 1},
			sumti_90(Arg,P,P1).

% the function of mod_head_490 is to alter this default; thus M is ignored
% in determining the ordinal of the argument
term_81(term(X,Arg),M,N,P,P1) -->	 
			modifier_82(Arg,M,X,N,P,P1).
% for time being, ignore these in building up predication
modifier_82([],M,X,N,P,P) -->	
			mod_head_490(M,X,N), gap_450.
modifier_82(Arg,M,X,N,P,P1) -->	
			mod_head_490(M,X,N), sumti_90(Arg,P,P1).
sumti_90(Arg,P,P1) -->	sumti_A_91(Arg,P,P1).
sumti_A_91(Arg,P,P1) -->
			sumti_D_94(Arg1,P2,P3),
			sumti_A_91_a(Arg,P,P1,Arg1,P2,P3).
sumti_A_91_a(c(Conn,X1,X2),q(c(Conn,N1,N2),c(Conn,X1,X2),c(Conn,R1,R2),
c(Conn,M1,M2),P1),P1,X1,q(N1,X1,R1,M1,[]),[]) -->	
			joik_ek_421(Conn),
			sumti_A_91(X2,q(N2,X2,R2,M2,[]),[]).
sumti_A_91_a(Arg,P,P1,Arg,P,P1) -->	
			[].
sumti_D_94(Arg,P,P1) -->
			sumti_E_95(Arg,Clause,P,P1),
			sumti_D_94_a(Arg,Clause).
sumti_D_94_a(Arg,Clause) -->
			relative_clauses_121(Arg,Clause).
sumti_D_94_a(Arg,_) --> [].
sumti_E_95(Arg,Clause,P,P1) --> 
			sumti_F_96(Arg,suho(1),Clause,P,P1).
sumti_E_95(Arg,Clause,P,P1) -->
			quantifier_300(N),
			sumti_F_96(Arg,N,Clause,P,P1).

% for now, ignore numbering of or relative clauses on anaphors and names; 
% iota quantification with quantifier "la"
sumti_F_96(Arg,_,_,q(la,Arg,[],[],P),P) -->	
			anaphora_400(Arg);
			la_558, cmene_405(Arg).
% term q is quantifier: number, variable, initial restricting predication,
% further restricting predications, remainder of predication
sumti_F_96(Arg,N,Clause,q(N,Arg,Restrict,Clause,P1),P1) -->
			description_110(Arg,Restrict).
description_110(X,Restrict) -->	
			le_562, 
			sumti_tail_A_112(X,Restrict), 
			gap_450.
sumti_tail_A_112(X,Restrict) -->
			selbri_130([term(1,X)],Restrict).
relative_clauses_121(X,Clause) -->
			relative_clause_122(X,Clause).
relative_clause_122(X,Clause) -->
			noi_585, sentence_40(Clause1), kuho_gap_469,
			{anaphorise(keha,X,Clause1,Clause)}.
% The instantiation of X will eventually force execution of order_transform
% to delay until ArgsIn is fully known

selbri_130(ArgsIn,Predication) -->	
			tanru_unit_A_151(ArgsIn,Predication,P1).
/*tanru_unit_A_151(ArgsIn,Predication,Predication) -->
			tanru_unit_B_152(ArgsIn,ArgsOut,Selbri),
			{functor(Predication,Selbri,5),
			fill_in_args(Predication,ArgsOut)}.
tanru_unit_A_151(ArgsIn,P,Predication) -->
			tanru_unit_B_152(ArgsIn1,ArgsOut,Selbri),
			linkargs_160(ArgsIn2,P,Predication),
			{append(ArgsIn2,ArgsIn,ArgsIn1),
			functor(Predication,Selbri,5),
			fill_in_args(Predication,ArgsOut)}.*/
tanru_unit_A_151(ArgsIn,P,Predication) -->
			tanru_unit_B_152(ArgsIn1,ArgsOut,Selbri),
			tanru_unit_A_151_a(ArgsIn,P,Predication,ArgsIn1,ArgsOut,Selbri).
tanru_unit_A_151_a(ArgsIn,Predication,Predication,ArgsIn,ArgsOut,Selbri) -->
			{functor(Predication,Selbri,5),
			fill_in_args(Predication,ArgsOut)}.
tanru_unit_A_151_a(ArgsIn,P,Predication,ArgsIn1,ArgsOut,Selbri) -->
			linkargs_160(ArgsIn2,P,Predication),
			{append(ArgsIn2,ArgsIn,ArgsIn1),
			functor(Predication,Selbri,5),
			fill_in_args(Predication,ArgsOut)}.
tanru_unit_B_152(ArgsIn,ArgsIn,Selbri) -->
			bridi_valsi_407(ArgsIn,Selbri).
tanru_unit_B_152(ArgsIn,ArgsOut,Selbri) -->
			se_480(N), tanru_unit_B_152(ArgsIn1,Args1,Selbri),
			{order_transform(Args1,ArgsOut,N),
			ArgsIn = ArgsIn1}. 
tanru_unit_B_152(ArgsIn,ArgsOut,Nu) -->
			nu_425(Nu),sentence_40(P),
			{append(ArgsIn,[term(2,P)],ArgsOut)},
			kei_gap_453.
linkargs_160([Arg|Tail],P,P1) -->
			be_504, term_81(Arg,2,N,P,P2),
			linkargs_160_a(Tail,N,P1,P2).
linkargs_160_a([],_,P,P) -->
			beho_gap_467.
linkargs_160_a(Tail,N,P1,P2) -->
			links_161(Tail,N,_,P2,P1), beho_gap_467.
links_161([Arg|Tail],N,_,P,P1) -->
			bei_505, term_80(Arg,N,N1,P,P2),
			links_161_a(Tail,N1,P1,P2).
links_161_a([],_,P1,P1) -->
			[].
links_161_a(Tail,N1,P1,P2) -->
			links_161(Tail,N1,_,P2,P1).

quantifier_300(N) -->	number_812(N), boi_gap_461.
anaphora_400(Arg) -->  	koha_555(Arg).
anaphora_400([]) -->	by_string_817, boi_gap_461.
cmene_405([Arg|Tail]) -->    cmene_517(Arg),cmene_405_a(Tail).
cmene_405_a(Tail) --> cmene_405(Tail);
		       [],
		      {Tail=[]}.
bridi_valsi_407(Args,Selbri) --> 
			brivla_509(Args,Selbri).
para_mark_410 --> ['niho'].
joik_ek_421(Conn) -->
		  ek_802(Conn).
nu_425(nu) -->    ['nu'].
nu_425(ka) -->    ['ka'].
lex_nu_425(X):- var(X),!,fail.
lex_nu_425(nu).
lex_nu_425(ka).
gap_450 -->       ['ku'].
front_gap_451 --> ['cu'].
kei_gap_453 -->   ['kei'].
vau_gap_456 -->   ['vau'].
boi_gap_461 -->   ['boi'].
beho_gap_467 -->  ['beho'].
kuho_gap_469 -->  ['kuho'].
se_480(N) -->	  se_596(N).

% M is the anticipated ordinal of the current arg; X is the ordinal of the 
% current arg; N, that of its successor. for example, at the start of a
% phrase, M is 1. if 'fe' is encountered, then X is 2 and N is 3.
mod_head_490(_,X,N) -->  fa_527(X),{N is X + 1}.
mod_head_490(M,0,M) --> modal_C_975.

a_501(a) -->	  ['a'].
a_501(e) -->	  ['e'].
bai_502 -->	  ['bai'].
be_504 -->	  ['be'].
bei_505 -->	  ['bei'].
by_513(b) -->     ['by'].
by_513(c) -->     ['cy'].
fa_527(1) -->	  ['fa'].
fa_527(2) -->     ['fe'].
fa_527(3) -->     ['fi'].
fa_527(4) -->     ['fo'].
fa_527(5) -->     ['fu'].
ga_537(a) -->	  ['ga'].
ga_537(e) -->	  ['ge'].
giha_541(a) -->	  ['giha'].
giha_541(e) -->	  ['gihe'].
ja_546(a) -->	  ['ja'].
ja_546(e) -->     ['je'].
koha_555(koha) -->	['koha'].
koha_555(kohe) -->	['kohe'].
koha_555(kohi) -->	['kohi'].
koha_555(koho) -->	['koho'].
koha_555(kohu) -->	['kohu'].
koha_555(foha) -->	['foha'].
koha_555(fohe) -->	['fohe'].
koha_555(fohi) -->	['fohi'].
koha_555(foho) -->	['foho'].
koha_555(fohu) -->	['fohu'].
koha_555(mi) -->	['mi'].
koha_555(keha) -->	['keha'].
koha_555(da) -->	['da'].
koha_555(de) -->	['de'].
koha_555(di) -->	['di'].
% and lexical classification of these:
lex_da_555a(X):- var(X),!,fail.
lex_da_555a(da).
lex_da_555a(de).
lex_da_555a(di).
la_558 --> 	  ['la'].
le_562 -->	  ['lo'];
		  ['le'].
noi_585 -->	  ['poi'].
se_596(2) -->     ['se'].
se_596(3) -->     ['te'].
se_596(4) -->     ['ve'].
se_596(5) -->     ['xe'].
pa_672(0) -->     ['no'].
pa_672(1) -->	  ['pa'].
pa_672(2) -->     ['re'].
pa_672(3) -->     ['ci'].
pa_672(4) -->     ['vo'].
pa_672(5) -->     ['mu'].
pa_672(6) -->     ['xa'].
pa_672(7) -->     ['ze'].
pa_672(8) -->     ['bi'].
pa_672(9) -->     ['so'].
pa_672(suho(_)) -->
		  ['suho'].
pa_672(ro) -->	  ['ro'].
ek_802(Conn) -->  a_501(Conn).
jek_805(Conn) --> ja_546(Conn).
gek_807(Conn) --> ga_537(Conn).
number_812(N) --> number_root_961(N).
gik_816 --> ['gi'].
by_string_817 --> by_987;
		  by_987, by_string_817a.
by_string_817a --> by_987, by_string_817a;
		   pa_672(_), by_string_817a.
gihek_818(Conn) --> giha_541(Conn).
% just plain sequencing
i_819(Conn) --> ['i'],simple_joik_jek_957(Conn).
i_819(xu) --> ['i','xu'].
i_819(seq) --> ['i'].
simple_joik_jek_957(Conn) --> jek_805(Conn).
number_root_961(N) -->
		  pa_672(N),{isNumber(N)}.
% "at least" defaults to "at least 1".
number_root_961(suho(1)) -->
		  pa_672(suho(1)).
number_root_961(ro) -->
		  pa_672(ro).
number_root_961(N) -->
		  pa_672(N1),
		  number_root_961(N2),
		  {isNumber(N1), N3 is N1 * 10 , N is N3 + N2}.
number_root_961(suho(N)) -->
		  pa_672(suho(N)),
		  number_root_961(N).
% "all n": ignore n
number_root_961(ro) -->
		  pa_672(ro),
		  number_root_961(_).
modal_C_975 -->   bai_502;
		  se_596(_), bai_502.
by_987 -->	  by_513(_).

% transform list of lists to list of heads of these lists; designed for use
% with getTokenList
select_first([[A|_]|B],[A|C]):-select_first(B,C).
select_first([[_|end_of_file]],[]).

% get declared lexemes out of file (preceding text) and declare them to
% be part of program vocab. Returns text proper, as remainder of list

lexeme_declarations([end_of_lex_list|Text],Text).
lexeme_declarations([cmene|[X|Tail]],Text):-
%	assert(cmene_517(_,X) --> [X]),
	assert(cmene_517(X,X.SOut,SOut)), 
	lexeme_declarations(Tail,Text).
lexeme_declarations([brivla|[X|Tail]],Text):-
%	assert(brivla_509(X) --> [X]),
	assert(brivla_509(_,X,X.SOut,SOut)),
	lexeme_declarations(Tail,Text).

% change the order of arguments in a list, exchanging the 1st and Nth
?- order_transform(A,_,_) when (A).
order_transform([term(1,Arg)|X],[term(N,Arg)|Y],N):-
	order_transform(X,Y,N).
order_transform([term(N,Arg)|X],[term(1,Arg)|Y],N):-
	order_transform(X,Y,N).
order_transform([term(N,Arg)|X],[term(N,Arg)|Y],M):-
	N =\= M, order_transform(X,Y,N).
order_transform([],[],_).

% fill in predication with arguments out of Arglist
?- fill_in_args(_,A) when (A).
fill_in_args(Predication,[]).
fill_in_args(Predication,[term(N,Arg)|Tail]) :-
		N > 0,arg(N,Predication,Arg),fill_in_args(Predication,Tail).
fill_in_args(Predication,[term(0,Arg)|Tail]) :-
		fill_in_args(Predication,Tail).

% substitute all instances of (atom: anaphor) D with (variable: referent) X
% in predication P, giving predication P1.
%?- anaphorise(_,_,A,_) when A.
anaphorise(D,X,A,A) :- D \== A, var(A).
anaphorise(D,X,A,A) :- D \== A, atomic(A).
anaphorise(D,X,D1,X) :- D == D1.
anaphorise(D,X,P,P1) :-
		compound(P),
		functor(P,F,N),
		ilist(1,N,NList),
		functor(P1,F,N),
		anaph_recurse(NList,D,X,P,P1).
anaph_recurse([],D,X,_,_).
anaph_recurse([N|T],D,X,P,P1) :-
		arg(N,P,Q),
		anaphorise(D,X,Q,Q1),
		arg(N,P1,Q1),
		anaph_recurse(T,D,X,P,P1).

postproc(A,B) :- postproc0(A,C),postproc2(C,D,[],[]),postproc1(D,B),!.

% postprocessing: resolve conjunctions
postproc0(A,A) :- (var(A);atomic(A)),!.
postproc0(q(c(C,N1,N2),c(C,X1,X2),c(C,R1,R2),c(C,M1,M2),P),
	 c(C,q(N1,X1,R1,M1,Q1),q(N2,X2,R2,M2,Q2))) :-
		anaphorise(c(C,X1,X2),X1,P,Q1),
		anaphorise(c(C,X1,X2),X2,P,Q2),!.
postproc0(P,P1) :-
		functor(P,F,N),
		ilist(1,N,NList),
		functor(P1,F,N),
		postproc0_recurse(NList,P,P1).
postproc0_recurse([],_,_).
postproc0_recurse([N|T],P,P1) :-
		arg(N,P,Q),
		postproc0(Q,Q1),
		arg(N,P1,Q1),
		postproc0_recurse(T,P,P1).

% further postprocessing:
% kill "la" quantification (anaphors and names; iota q'ntfn is different)
postproc1(A,A) :- (var(A);atomic(A)),!.
postproc1(q(la,X,R,M,Q),Q1) :-
		postproc1(Q,Q1),!.
postproc1(P,P1) :-
		functor(P,F,N),
		ilist(1,N,NList),
		functor(P1,F,N),
		postproc1_recurse(NList,P,P1).
postproc1_recurse([],_,_).
postproc1_recurse([N|T],P,P1) :-
		arg(N,P,Q),
		postproc1(Q,Q1),
		arg(N,P1,Q1),
		postproc1_recurse(T,P,P1).

postproc2(A,A,B,B):-
		(var(A);atomic(A)),!.
postproc2(q(la,Da,R,M,P),q(suho(1),X,R1,M1,P1),RestOfPara,RestOfPara1):-
		lex_da_555a(Da),
		anaphorise(Da,X,P,P2),
		anaphorise(Da,X,R,R2),
		anaphorise(Da,X,M,M2),
% if da occurs in one phrase, anaphorise it throughout the paragraph!
		anaphorise(Da,X,RestOfPara,RestOfPara2),
		postproc2(R2,R1,RestOfPara2,RestOfPara3),
		postproc2(M2,M1,RestOfPara3,RestOfPara4),
		postproc2(P2,P1,RestOfPara4,RestOfPara1),!.
postproc2(q(Q,X,R,M,P),q(Q,X,R1,M1,P1),RestOfPara,RestOfPara1):-
		(Q \= la ; not lex_da_555a(X)),
		postproc2(R,R1,RestOfPara,RestOfPara2),
		postproc2(M,M1,RestOfPara2,RestOfPara3),
		postproc2(P,P1,RestOfPara3,RestOfPara1),!.
postproc2(c(C,A,B),c(C,A1,B1),RestOfPara,RestOfPara1):-
		postproc2(A,A1,[B|RestOfPara],[B2|RestOfPara2]),
		postproc2(B2,B1,RestOfPara2,RestOfPara1),!.
postproc2(A,A1,RestOfPara,RestOfPara1):-
		nonvar(A),functor(A,F,N),ilist(1,N,NL),
		F \= q, F \= c,
		postproc2_recurse(NL,A,A1,RestOfPara,RestOfPara1).
postproc2_recurse([],A,A,B,B).
postproc2_recurse([H|T],A,A1,RestOfPara,RestOfPara1):-
		arg(H,A,B),arg(H,A1,B1),
		postproc2(B,B1,RestOfPara,RestOfPara2),
		postproc2_recurse(T,A,A1,RestOfPara2,RestOfPara1).

% generate list of numbers between A and B
ilist(A,A,[A]).
ilist(A,B,[A|T]):- A < B, C is A + 1,ilist(C,B,T).

transform_semantic(P,Q):-
	permuteQ1(_,P,Q);
	generalise(P,P1),permuteQ1(_,P1,Q).

% generalise quantifiers: if A then E, etc.

generalise(P,P):- (var(P);atomic(P)),!.
generalise(q(Q,X,R,M,P),q(suho(1),X,R1,M1,P1)):-
	(Q = la ; Q = ro ),
	generalise(R,R1),
	generalise(M,M1),
	generalise(P,P1).
generalise(q(Q,X,R,M,P),q(Q,X,R1,M1,P1)):-
	generalise(R,R1),
	generalise(M,M1),
	generalise(P,P1).
generalise(P,Q):-
	functor(P,Functor,2),lex_nu_425(Functor),
	functor(Q,Functor,2),arg(1,P,X),arg(1,Q,X),
	arg(2,P,Sent),generalise(Sent,Sent1),arg(2,Q,Sent1).
generalise(P,P):-
	functor(P,F,_),F \= q.

% delete a quantification q(Q,X,R,M,_) from a clause
%?- deleteQ(_,_,_,_,q(_,_,_,_,A),B) when A or B.
deleteQ(Q,X,R,M,q(Q,X,R,M,P),P).
deleteQ(Q,X,R,M,q(Q1,X1,R1,M1,q(Q2,X2,R2,M2,P)),
q(Q1,X1,R1,M1,P1)):-
	deleteQ(Q,X,R,M,q(Q2,X2,R2,M2,P),P1).
deleteQ(_,_,_,_,P,P):-
	functor(P,Functor,_),Functor \= q.

% return: the subsection of the current clause starting at the first
% quantifier other than the current one; also return the whole clause
% such that this subsection may be permuted in permuteQ1
getnextQ1(Q,q(Q,X,R,M,P),q(Q,X,R,M,P1),P2,P3):-
	nonvar(P),getnextQ(Q,P,P1,P2,P3).
getnextQ1(Q,q(Q1,X,R,M,P),P1,q(Q1,X,R,M,P),P1):- nonvar(Q1),Q \= Q1.
getnextQ(Q,P0,P1,P2,P3):- getnextQ1(Q,P0,P1,P2,P3).
getnextQ(Q,P0,P1,P3,P4):-
	getnextQ1(Q,P0,P1,q(Q1,X,R,M,P),P2),
	getnextQ(Q1,q(Q1,X,R,M,P),P2,P3,P4).

% form all permutations of contiguous quantifications with same quantifier
permuteQ1(_,P,P):- (var(P);atomic(P)),!.
permuteQ1(Q,q(Q,X1,R1,M1,P1),q(Q,X2,R,M,P2)):-
	deleteQ(Q,X2,R2,M2,q(Q,X1,R1,M1,P1),P3),
	permuteQ1(_,R2,R),permuteQ1(_,M2,M),
	permuteQ1(Q,P3,P2).
permuteQ1(Q,q(Q1,X1,R1,M1,P1),q(Q1,X1,R1,M1,P1)):- Q \= Q1.
permuteQ1(_,q(Q,X,R,M,P),WholeSentence):-
	getnextQ(Q,q(Q,X,R,M,P),WholeSentence,NewSubSect,NewSubSectPerm),
	permuteQ1(_,NewSubSect,NewSubSectPerm).
permuteQ1(_,P,Q):-
	functor(P,Functor,2),lex_nu_425(Functor),
	functor(Q,Functor,2),arg(1,P,X),arg(1,Q,X),
	arg(2,P,Sent),permuteQ1(_,Sent,Sent1),arg(2,Q,Sent1).
permuteQ1(_,P,P):-
	functor(P,Functor,_),Functor \= q.

% expand embedded conjunctions in a sentence to the sentence level. e.g.
% ExEy&(EzA,Ew+(EvB,EuC)) expands to &(ExEyEzA,+(ExEyEwEvB,ExEyEzEuC))
% Applying this transformation to a text allows a question concerning a
% possible alternative such as ?ExEyEzA to be answered in the affirmative.

conjexpand(P,P,Pred,Pred):-
	var(P),!;
	nonvar(P),functor(P,F,_),F \= q, F \= c.
conjexpand(q(Q,X,R,M,P),q(Q,X,R,M,P1),PrenexToDate,Expansion):-
	conjexpand(P,P1,PrenexToDate,Expansion).
conjexpand(c(C,A,B),P,PrenexToDate,c(C,Expansion1,Expansion2)):-
	nonvar(PrenexToDate),
	anaphorise(P,P1,PrenexToDate,PrenexToDate1),
	anaphorise(P,P2,PrenexToDate,PrenexToDate2),
	conjexpand(A,P1,PrenexToDate1,Expansion1),
	conjexpand(B,P2,PrenexToDate2,Expansion2).
conjexpand(c(C,A,B),P,PrenexToDate,c(C,Expansion1,Expansion2)):-
	var(PrenexToDate),
	conjexpand(A,P1,P1,Expansion1),
	conjexpand(B,P2,P2,Expansion2).

% when a para is still being parsed, the next sentence after a question is a
% variable; this disrupts the program by futile delays, so replace it with []

paracutoff(c(C,A,B),c(C,A,[])):- var(B).
paracutoff(c(C,A,B),c(C,A,B)):- nonvar(B).

% unidirectional unification. Thus, is database has car(red) and question
% seeks car(_), unify; but if database has car(_), and question has car(red),
% do not unify for lack of sufficient info
unify(A,B):-
	(var(A);nonvar(B),atomic(B)),A = B, !.
unify(A,B):-
	nonvar(A),functor(A,F,N),nonvar(B),functor(B,F,N),
	ilist(1,N,NL),unify_recurse(NL,A,B).
unify_recurse([],_,_).
unify_recurse([H|T],A,B):-
	arg(H,A,A1),arg(H,B,B1),
	unify(A1,B1),unify_recurse(T,A,B).

?- ask3(_,c(A,_,_),_) when A.
ask3(P,X,_):- var(X),!,fail.
ask3(P,c(seq,P1,_),Ans):- ask3(P,P1,Ans). 
ask3(P,c(seq,_,P2),Ans):- ask3(P,P2,Ans). 
ask3(P,c(e,P1,_),Ans):- ask3(P,P1,Ans). 
ask3(P,c(e,_,P2),Ans):- ask3(P,P2,Ans). 
ask3(X,Y,Y):- 
	nonvar(X),nonvar(Y),
	duplicate(X,X1),duplicate(Y,Y1),
	unify(X1,Y1).
?- ask2(_,c(A,_,_)) when A.
ask2(P,X):- conjexpand(P,Q,Q,P1),ask2a(P1,X).
ask2a(P,X):- functor(P,F,_),F \= c,ask3(P,X,_).
ask2a(c(a,A,B),X):- ask2a(A,X);ask2a(B,X).
ask2a(c(a,A,B),X):- ask3(c(a,A,B),X,_);ask3(c(a,B,A),X,_).
ask2a(c(e,A,B),X):- 
	ask3(A,X,Ans1),ask3(B,X,Ans2),
	duplicate(c(e,A,B),c(e,Ans1,Ans2)).
	% to guarantee we get any common prenexes right
ask1(P,X):-
	conjexpand(X,Q,Q,X1),ask2(P,X1).
ask1(P,_):-
	uttered(X),conjexpand(X,Prenex,Prenex,X1),ask2(P,X1).
ask(P,X,Ans):- 
	transform_semantic(P,P1),
	postproc(P1,P2),
	paracutoff(X,X1),
	postproc(X1,X2),
	ask1(P2,X2) -> Ans=yes;
	Ans=no_answer.
	
parse(File) :-
	cd('/stude/mundil/n/nsn/603'),
	open(File,read,Stream),
	getTokenList(Stream,TokList),
	select_first(TokList,WordList),
	lexeme_declarations(WordList,Text),
	phrase(text_0,Text).