1:- module(ics_parser,
    2	 [translate_ics/2,
    3	  translate_ics_files/2,
    4      download_ics/1]).    5
    6:- use_module(library(lists)).    7%	     [append/3,
    8%	      member/2]).
    9%:- use_module(library(system),
   10%	     [system/1]).
   11
   12:- use_module(parser_utils).   13:- use_module(debug).   14
   15%:- use_module(ruleml_parser).
   16
   17/*
   18translate_ics_files(FileList,OutFile):-
   19	write_debug('translate_ics_files: init...'),
   20	write_debug('Parsing ICS: init...'),
   21	merge_files(FileList,'_temp_ics_file_.txt'),
   22	translate_ics('_temp_ics_file_.txt',OutFile),!,
   23	% This cut is important, as in order to signal translation errors, 
   24    % choice points are left open 
   25	write_debug('ICS successfully translated and written to:'),
   26	write_debug(OutFile),
   27	write_debug('Parsing ICS: end.'). 
   28*/
   29
   30download_ics(URLstring):-
   31    atom_codes(URL,URLstring),
   32    translate_ics_files([URL],'./temp.pl'),
   33    open('./temp.pl',read,Stream),
   34    call_terms(Stream),
   35    close(Stream).
   36
   37call_terms(Stream):-
   38    read(Stream,Term),
   39    (Term=end_of_file -> true
   40        ; (Term = ics(Body,Head)-> call(user:ic(Body,Head)) % invokes all atoms ic/2 in the file
   41            ; true),
   42          call_terms(Stream)
   43    ).
   44
   45translate_ics_files(FileList,OutFile):-
   46	open(OutFile,write,Stream),
   47	write(Stream,':-module(ics,[ics/2]).'),nl(Stream),nl(Stream),
   48	translate_ics_list(FileList,Stream),
   49	close(Stream).
   50
   51translate_ics_list([],_).
   52translate_ics_list([InFile|FileList],Stream):-
   53    write_debug('Parsing file '), write_debug(InFile),
   54    translate_ics_opened(InFile,Stream),!,
   55    write_debug(' --> OK'), nl,
   56    translate_ics_list(FileList,Stream).
   57
   58merge_files(FileList,OutFile):-
   59%	write('About to open outfile'),nl,
   60	open(OutFile,write,Stream),
   61%	write('outfile opened'),nl, write(FileList), nl,
   62	FileList=[H|T],write(H),nl,write(T),nl,
   63	merge_files_to_stream(FileList,Stream),
   64	close(Stream).
   65
   66merge_files_to_stream([],_):-write('empty list'),nl.
   67merge_files_to_stream([File|MoreFiles],OutStream):-	
   68	read_file_to_string(File,String),
   69	write_string_to_stream(String,OutStream),
   70	merge_files_to_stream(MoreFiles,OutStream).
   71
   72write_string_to_stream([],_).
   73write_string_to_stream([Code|MoreCodes],Stream):-
   74	put_code(Stream,Code),
   75	write_string_to_stream(MoreCodes,Stream).
   76
   77translate_ics(InFile,OutFile):-
   78	open(OutFile,write,Stream),
   79	write(Stream,':-module(ics,[ics/2]).'),nl(Stream),nl(Stream),
   80    translate_ics_opened(InFile,Stream),
   81	close(Stream).
   82
   83% Assumes the outfile is already open
   84translate_ics_opened(InFile,Stream):-
   85	% If the XML succeeds, OK, otherwise try to parse as normal
   86	% Sept 2008: if the ruleml_parse_file/3 predicate exists, try to
   87	% invoke it, and check if the InFile is in RuleML syntax.
   88	% Otherwise, if the ruleml_parser library is not loaded, use the normal parser.
   89	% In this way, the same code can also work in other Prolog systems that
   90	% do not have an XML library.
   91	% Moreover, it uses less memory (does not load ruleml_parser when it is not
   92	% needed)
   93	(current_predicate(ruleml_parser:ruleml_parse_file/3),
   94	 ruleml_parser:ruleml_parse_file(InFile,ICSR,Error),
   95	 Error \= no_ruleml
   96	   ->  write_ics_to_stream(ICSR,Stream)
   97	   ;   parse_ics(InFile,ICS),
   98	       write_ics_to_stream(ICS,Stream)
   99    ).
  100
  101parse_ics(FileName,ICList):-
  102	read_file_to_string(FileName,FileString),
  103	phrase(elementList(FileString2),FileString),
  104	drop_whites(FileString2, NoWhitesString),
  105	phrase(ic_list(ICList,1), NoWhitesString).
  106
  107
  108
  109
  110%----------------------------------------------------------
  111% ICS DCG
  112%----------------------------------------------------------
  113
  114ic_list([],_) -->
  115	[].
  116ic_list([IC|MoreICs],N) -->
  117	ic(IC),
  118	!,
  119	{N1 is N+1},
  120	ic_list(MoreICs,N1).
  121ic_list([_|_],N) -->
  122    {write_error('Error in IC number '), 
  123    write_error(N), 
  124    write_error(' ***'),
  125    nl, fail}.
  126
  127ic(ic(Body,Head)) -->
  128	body(Body),
  129	impl_symbol,!,
  130	head2(Head).
  131ic(_) -->
  132    {nl, write_error('*** Error in Body or could not find implication symbol: '), nl, fail}.
  133
  134/* Old syntax: body should start with event or abducible 
  135body([BodyAtom|MoreAtoms]) -->
  136	abducible(BodyAtom),!,
  137	body_tail(MoreAtoms).
  138body([BodyAtom|MoreAtoms]) -->
  139	event(BodyAtom),!,
  140	body_tail(MoreAtoms).
  141body(_) -->
  142    {nl, write_error('*** Body must begin with event or abducible.'), nl, fail}.
  143*/
  144
  145body([BodyAtom|MoreAtoms]) -->
  146	body_atom(BodyAtom),!,
  147	body_tail(MoreAtoms).
  148
  149body_tail([BodyAtom|MoreBodyAtoms]) -->
  150	and_symbol,
  151	body_atom(BodyAtom),
  152	!,
  153	body_tail(MoreBodyAtoms).
  154body_tail([]) -->
  155	[].
  156body_tail(_) -->
  157    comma,
  158    {nl, write_error('*** Error in body conjunct: comma instead of /\\ symbol?'), fail}.
  159
  160body_atom(BodyAtom) -->
  161	abducible(BodyAtom).
  162body_atom(BodyAtom) -->
  163	event(BodyAtom).
  164body_atom(BodyAtom) -->
  165	atom(BodyAtom).
  166body_atom(BodyAtom) -->
  167	relat(BodyAtom).
  168
  169relat(Relation) -->
  170	clp_relation(Relation),
  171	!.
  172relat(Relation) -->
  173	unify_relation(Relation).
  174
  175unify_relation(Relation) -->
  176	term(Term1),
  177	unify_operator(Operator),
  178	term(Term2),
  179	{Relation=..[Operator,Term1,Term2]}.
  180
  181clp_relation(Relation) -->
  182	expression(Expression1),
  183	clp_relop(Relop),
  184	expression(Expression2),
  185	{Relation=..[Relop,Expression1,Expression2]}.
  186
  187
  188
  189expression(Expression) -->
  190	operand(Operand1),
  191	clp_operator(CLPOperator),
  192	operand(Operand2),
  193	{Expression=..[CLPOperator,Operand1,Operand2]}.
  194expression(Expression) -->
  195	operand(Expression).
  196expression(Expression) -->
  197	term(Expression).
  198
  199is_constraint(C):-
  200	%C=..[R|_],
  201	functor(C,R,_),
  202	member(R,[=,<>,>=,>,=<,<,::]).
  203
  204clp_relop(=) -->
  205	"==",
  206	!.
  207clp_relop(<>) -->
  208	"<>",
  209	!.
  210clp_relop(>=) -->
  211	">=",
  212	!.
  213clp_relop(>) -->
  214	">",
  215	!.
  216clp_relop(=<) -->
  217	"<=",
  218	!.
  219clp_relop(<) -->
  220	"<".
  221clp_relop(::) -->
  222	"::".
  223
  224clp_operator(+) -->
  225	"+".
  226clp_operator(-) -->
  227	"-".
  228clp_operator(*) -->
  229	"*".
  230clp_operator(/) -->
  231	"/".
  232
  233
  234unify_operator(unif) -->
  235	"=".
  236unify_operator(not_unif) -->
  237	"!=".
  238
  239operand(Number) -->
  240	number(Number).
  241operand(Variable) -->
  242	variable(Variable).
  243
  244head2(Head) -->
  245    head1(Head),
  246	full_stop,!.
  247head2(_) -->
  248    {nl, write_error('*** Error in Head or could not find full stop: '), fail}.
  249
  250%head1([[false]])-->"false",!.
  251head1([])-->"false",!.
  252head1(Head)-->head(Head).
  253
  254head([Disjunct|MoreDisjuncts]) -->
  255	disjunct_1(Disjunct),
  256	head_tail(MoreDisjuncts).
  257
  258disjunct_1(Disjunct1) -->
  259	disjunct(Disjunct),
  260	{constraints_before(Disjunct,Disjunct1)}.
  261
  262constraints_before(L1,L2):-
  263	divide_constraints_from_abducibles(L1,Constraints,Abducibles),
  264	append(Constraints,Abducibles,L2).
  265
  266divide_constraints_from_abducibles([],[],[]).
  267divide_constraints_from_abducibles([H|T],[H|T1],L2):-
  268	is_constraint(H),
  269	!,
  270	divide_constraints_from_abducibles(T,T1,L2).
  271divide_constraints_from_abducibles([H|T],L1,[H|T2]):-
  272	divide_constraints_from_abducibles(T,L1,T2).
  273
  274
  275
  276head_tail([Disjunct|MoreDisjuncts]) -->
  277	or_symbol,
  278	disjunct(Disjunct),
  279	!,
  280	head_tail(MoreDisjuncts).
  281head_tail([]) -->
  282	[].
  283
  284disjunct([Conjunct|MoreConjuncts]) -->
  285	abducible(Conjunct),
  286	disjunct_tail(MoreConjuncts).
  287disjunct([Conjunct|MoreConjuncts]) -->
  288	atom(Conjunct),
  289	{writeln_debug(''), writeln_debug('*** Warning: atom in head ***')},
  290	disjunct_tail(MoreConjuncts)
  290.
  291disjunct([Conjunct|MoreConjuncts]) -->
  292	event(Conjunct),
  293	{writeln_debug(''), writeln_debug('*** Warning: H in head ***'), nl},
  294	disjunct_tail(MoreConjuncts)
  294.
  295disjunct([Conjunct|MoreConjuncts]) -->
  296	relat(Conjunct),
  297	disjunct_tail(MoreConjuncts)
  297.
  298
  299disjunct_tail([Conjunct|MoreConjuncts]) -->
  300	and_symbol,
  301	head_conjunct(Conjunct),
  302	!,
  303	disjunct_tail(MoreConjuncts).
  304disjunct_tail([]) -->
  305	[].
  306disjunct_tail(_) -->
  307    comma,
  308    {nl, write_error('*** Error in conjunct: comma instead of /\\ symbol?'), fail}.
  309
  310head_conjunct(Conjunct) -->
  311	abducible(Conjunct).
  312head_conjunct(Conjunct) -->
  313	atom(Conjunct).
  314head_conjunct(Conjunct) -->
  315	relat(Conjunct).
  316head_conjunct(Conjunct) -->
  317	event(Conjunct), {writeln_debug('*** Warning: H in head ***'), nl}.
  318
  319
  320atom(Atom) -->
  321	funct(Functor),
  322	opening_parenthesis,
  323	!,
  324	term_list(Arguments),
  325	closing_parenthesis,
  326	{Atom=..[Functor|Arguments]}.
  327atom(Atom) -->
  328    atomic_constant(Atom).
  329
  330abducible(Abducible) -->
  331	abducible_functor(Functor),
  332	opening_parenthesis,
  333	content(Content),
  334	comma,
  335	time(Time),
  336	closing_parenthesis,!,
  337	{Abducible=..[Functor,Content,Time]}.
  338abducible(_) -->
  339	abducible_functor(Functor),
  340	opening_parenthesis,
  341	content(Content),
  342	comma,!,
  343	{nl, write_error('*** Error in Abducible "'), write_error2(Functor), write_error2('('), write_error2(Content),
  344	write_error(' -HERE- ":'), nl,
  345    write_error('error in Time, wrong number of arguments or missing ")"  '), nl, fail}.
  346abducible(_) -->
  347	abducible_functor(Functor),
  348	opening_parenthesis,!,
  349	{nl, write_error('*** Error in Abducible "'), write_error2(Functor), 
  350    write_error2('('),
  351    write_error(' -HERE- ": error in Content or missing \',\' '), nl, fail}.
  352abducible(_) -->
  353	abducible_functor(Functor),!,
  354	{nl, write_error('*** Error in Abducible "'), write_error2(Functor), 
  355    write_error(' -HERE- ": missing \'(\' '), nl, fail}.
  356
  357
  358event(Event) -->
  359	event_functor(Functor),
  360	opening_parenthesis,
  361	content(Content),
  362	comma,
  363	time(Time),
  364	closing_parenthesis,!,
  365	{Event=..[Functor,Content,Time]}.
  366event(_) -->
  367	event_functor(Functor),
  368	opening_parenthesis,
  369	content(Content),
  370	comma,!,
  371	{nl, write_error('*** Error in event "'), write_error2(Functor), write_error2('('), write_error2(Content),
  372	write_error(' -HERE- ":'), nl,
  373    write_error('error in Time, wrong number of arguments or missing ")"  '), nl, fail}.
  374event(_) -->
  375	event_functor(Functor),
  376	opening_parenthesis,!,
  377	{nl, write_error('*** Error in event "'), write_error2(Functor), 
  378    write_error2('('),
  379    write_error(' -HERE- ": error in Content or missing \',\' '), nl, fail}.
  380event(_) -->
  381	event_functor(Functor),!,
  382	{nl, write_error('*** Error in event "'), write_error2(Functor), 
  383    write_error(' -HERE- ": missing \'(\' '), nl, fail}.
  384
  385abducible_functor(e) -->
  386	"E".
  387abducible_functor(e) -->
  388	atomic_constant(Functor),
  389	{Functor = e}.
  390abducible_functor(en) -->
  391	"EN".
  392abducible_functor(en) -->
  393	atomic_constant(Functor),
  394	{Functor = en}.
  395abducible_functor(note) -->
  396	"!E".
  397abducible_functor(note) -->
  398	"!e".
  399abducible_functor(noten) -->
  400	"!EN".
  401abducible_functor(noten) -->
  402	"!en".
  403abducible_functor(abd) -->
  404	"ABD".
  405abducible_functor(abd) -->
  406	atomic_constant(Functor),
  407	{Functor = abd}.
  408		  
  409event_functor(h) -->
  410	"H".
  411event_functor(h) -->
  412	atomic_constant(Functor),
  413	{Functor = h}.
  414event_functor(noth) -->
  415	"!H".
  416event_functor(noth) -->
  417	"!h".
  418
  419content(Content) -->
  420	term(Content).
  421
  422
  423
  424
  425
  426			 
  427
  428	
  429
  430
  431
  432
  433or_symbol -->
  434	"\\/".
  435and_symbol -->
  436	"/\\".	
  437
  438impl_symbol -->
  439	"--->".
  440
  441
  442
  443
  444
  445
  446write_ics_to_file(FileName,ICList):-
  447	open(FileName,write,Stream),
  448	write_ics_to_stream(ICList,Stream),
  449	close(Stream).
  450
  451write_ics_to_stream([],_).
  452write_ics_to_stream([IC|MoreICs],Stream):-
  453	write_ic_to_stream(IC,Stream),
  454	write_ics_to_stream(MoreICs,Stream).
  455
  456write_ic_to_stream(ic(Body,Head),Stream):-
  457	write(Stream,'ics('),
  458	write(Stream,Body),write(Stream,','),
  459	nl(Stream),
  460	spaces(Stream),
  461	write(Stream,'['),
  462	write_head_to_stream(Head,Stream),
  463	write(Stream,']).'),
  464	nl(Stream),
  465	nl(Stream).
  466
  467write_head_to_stream([],_Stream).
  468write_head_to_stream([Disjunct],Stream):-
  469	write(Stream,Disjunct).
  470write_head_to_stream([Disjunct1,Disjunct2|MoreDisjuncts],Stream):-
  471	write(Stream,Disjunct1),
  472	write(Stream,','),
  473	nl(Stream),
  474	spaces(Stream),
  475	write_head_to_stream([Disjunct2|MoreDisjuncts],Stream).
  476	
  477		     
  478spaces(Stream):-
  479	write(Stream,'        ')