1:- module(rsasak_pddl_parser,[parseProblem/3,parseDomain/3]).    2
    3:- style_check(-singleton).    4:- use_module(library(prolog_pack)).    5:- if( \+ prolog_pack:current_pack(logicmoo_planners)).    6:- dynamic   user:file_search_path/2.    7:- multifile user:file_search_path/2.    8:- prolog_load_context(directory,Dir),
    9   DirFor = planner,
   10   (( \+ user:file_search_path(DirFor,Dir)) ->asserta(user:file_search_path(DirFor,Dir));true),
   11   absolute_file_name('.',Y,[relative_to(Dir),file_type(directory)]),
   12   (( \+ user:file_search_path(pack,Y)) ->asserta(user:file_search_path(pack,Y));true).   13:- attach_packs.   14:- initialization(attach_packs).   15:- endif.   16
   17
   18:- prolog_load_context(directory,Dir),
   19   must((absolute_file_name('../pddl',Y,[relative_to(Dir),file_type(directory)]),
   20   (( \+ user:file_search_path(pddl,Y)) ->asserta(user:file_search_path(pddl,Y));true))).   21
   22
   23:- meta_predicate emptyOr(//,?,?).   24
   25%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   26%%%  read_file
   27%%%  This is a modified version for parsing pddl files.
   28%%%  Read the input file character by character and parse it
   29%%%  into a list. Brackets, comma, period and question marks
   30%%%  are treated as separate words. White spaces separed 
   31%%%  words. 
   32%%%
   33%%%  Similar to read_sent in Pereira and Shieber, Prolog and
   34%%%        Natural Language Analysis, CSLI, 1987.
   35%%%
   36%%%  Examples:
   37%%%           :- read_file('input.txt', L).
   38%%%           input.txt> The sky was blue, after the rain.
   39%%%           L = [the, sky, was, blue, (','), after, the, rain, '.']
   40%%%
   41%%%           :- read_file('domain.pddl', L).
   42%%%           domain.pddl>
   43%%%           (define (domain BLOCKS)
   44%%%             (:requirements :strips :typing :action-costs)
   45%%%             (:types block)
   46%%%             (:predicates (on ?x - block ?y - block)
   47%%%           ...
   48%%%           L = ['(', define, '(', domain, blocks, ')', '(', :, requirements|...].
   49%
   50%read_file(+File, -List).
   51read_file(File, Words) :- seeing(Old), see(File), get_code(C), read_rest(C, Words), seen, see(Old).
   52
   53/* Ends the input. */
   54read_rest(-1,[]) :- !.
   55
   56/* Spaces, tabs and newlines between words are ignored. */
   57read_rest(C,Words) :- ( C=32 ; C=10 ; C=9 ; C=13 ; C=92 ) , !,
   58                     get_code(C1),
   59                     read_rest(C1,Words).
   60
   61/* Brackets, comma, period or question marks are treated as separed words */
   62read_rest(C, [Char|Words]) :- ( C=40 ; C=41 ; C=44 ; C=45 ; C=46 ; C=63 ; C=58 ) , name(Char, [C]), !,
   63			get_code(C1),
   64			read_rest(C1, Words).
   65
   66/* Read comments to the end of line */
   67read_rest(59, Words) :- get_code(Next), !, 
   68			      read_comment(Next, Last),
   69			      read_rest(Last, Words).
   70
   71/* Otherwise get all of the next word. */
   72read_rest(C,[Word|Words]) :- read_word(C,Chars,Next),
   73                             name(Word,Chars),
   74                             read_rest(Next,Words).
   75
   76/* Space, comma, newline, period, end-of-file or question mark separate words. */
   77read_word(C,[],C) :- ( C=32 ; C=44 ; C=10 ; C=9 ; C=13 ;
   78                         C=46 ; C=63 ; C=40 ; C=41 ; C=58 ; C= -1 ) , !.
   79
   80/* Otherwise, get characters and convert to lower case. */
   81read_word(C,[LC|Chars],Last) :- lower_case(C, LC),
   82				get_code(Next),
   83                                read_word(Next,Chars,Last).
   84
   85/* Convert to lower case if necessary. */
   86lower_case(C,C) :- ( C <  65 ; C > 90 ) , !.
   87lower_case(C,LC) :- LC is C + 32.
   88
   89
   90/* Keep reading as long you dont find end-of-line or end-of-file */
   91read_comment(10, 10) :- !.
   92read_comment(-1, -1) :- !.
   93read_comment(_, Last) :- get_code(Next),
   94			 read_comment(Next, Last).
   95
   96%get0(C):-get_code(C), !.
   97
   98/* for reference ... 
   99newline(10).
  100comma(44).
  101space(32).
  102period(46).
  103question_mark(63).
  104*/
  105
  106
  107
  108%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  109%% parseDomain.pl
  110%%   Simple parser of PDDL domain file into prolog syntax.
  111%% Author: Robert Sasak, Charles University in Prague
  112%%
  113%% Example: 
  114%% ?-parseDomain('blocks_world.pddl', O).
  115%%   O = domain(blocks,
  116%%        [strips, typing, 'action-costs'],
  117%%        [block],
  118%%        _G4108,
  119%%        [ on(block(?x), block(?y)),
  120%%	         ontable(block(?x)),
  121%%	         clear(block(?x)),
  122%%	         handempty,
  123%%	         holding(block(?x)) ],
  124%%        [number(f('total-cost', []))],
  125%%        _G4108,
  126%%        [ action('pick-up', [block(?x)],       %parameters
  127%%		      [clear(?x), ontable(?x), handempty], %preconditions
  128%%		      [holding(?x)],                       %positiv effects
  129%%          [ontable(?x), clear(?x), handempty], %negativ effects
  130%%          [increase('total-cost', 2)]),        %numeric effects
  131%%         ...],
  132%%       ...)
  133%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  134
  135
  136% parseDomain(+File, -Output).
  137% Parse PDDL domain File and return it rewritten prolog syntax.   
  138parseDomain(F, O):- parseDomain(F, O, _).
  139% parseDomain(+File, -Output, -RestOfFile)
  140% The same as above and also return rest of file. Can be useful when domain and problem are in one file.
  141parseDomain(File, Output, R) :-
  142		read_file(File, List),
  143		domainBNF(Output, List, R).
  144
  145% Support for reading file as a list.
  146% :-[readFile].
  147
  148% Defining operator ?. It is a syntax sugar for marking variables: ?x
  149:-op(300, fy, ?).  150
  151% List of DCG rules describing structure of domain file in language PDDL.
  152% BNF description was obtain from http://www.cs.yale.edu/homes/dvm/papers/pddl-bnf.pdf
  153% This parser do not fully NOT support PDDL 3.0
  154% However you will find comment out lines ready for futher development.
  155domainBNF(domain(N, R, T, C, P, F, C, S))
  156			--> ['(','define', '(','domain'], name(N), [')'],
  157                             (require_def(R)	; []),
  158                             (types_def(T)    	; []), %:typing
  159                             (constants_def(C) 	; []),
  160                             (predicates_def(P)	; []),
  161                             (functions_def(F)	; []), %:fluents
  162%                             (constraints(C)	; []),    %:constraints
  163                             zeroOrMore(structure_def, S),
  164			     [')'].
  165
  166require_def(R)		--> ['(',':','requirements'], oneOrMore(require_key, R), [')'].
  167require_key(strips)								--> [':strips'].
  168require_key(typing)								--> [':typing'].
  169%require_key('negative-preconditions')		--> [':negative-preconditions'].
  170%require_key('disjunctive-preconditions')	--> [':disjunctive-preconditions'].
  171require_key(equality)							--> [':equality'].
  172require_key('existential-preconditions')	--> [':existential-preconditions'].
  173require_key('universal-preconditions')		--> [':universal-preconditions'].
  174require_key('quantified-preconditions')	--> [':quantified-preconditions'].
  175require_key('conditional-effects')			--> [':conditional-effects'].
  176require_key(fluents)								--> [':fluents'].
  177require_key(adl)									--> [':adl'].
  178require_key('durative-actions')				--> [':durative-actions'].
  179require_key('derived-predicates')			--> [':derived-predicates'].
  180require_key('timed-initial-literals')		--> [':timed-initial-literals'].
  181require_key(preferences)						--> [':preferences'].
  182require_key(constraints)						--> [':constraints'].
  183% Universal requirements
  184require_key(R)		--> [':', R].
  185
  186types_def(L)			--> ['(',':',types],      typed_list(name, L), [')'].
  187constants_def(L)		--> ['(',':',constants],  typed_list(name, L), [')'].
  188predicates_def(P)		--> ['(',':',predicates], oneOrMore(atomic_formula_skeleton, P), [')'].
  189
  190atomic_formula_skeleton(F)
  191				--> ['('], predicate(P), typed_list(variable, L), [')'], {F =.. [P|L]}.
  192predicate(P)			--> name(P).
  193
  194variable(V)			--> ['?'], name(N), {V =.. [?, N]}.
  195atomic_function_skeleton(f(S, L))
  196				--> ['('], function_symbol(S), typed_list(variable, L), [')'].
  197function_symbol(S)		--> name(S).
  198functions_def(F)		--> ['(',':',functions], function_typed_list(atomic_function_skeleton, F), [')'].	%:fluents
  199%constraints(C)		--> ['(',':',constraints], con_GD(C), [')'].	%:constraints
  200structure_def(A)		--> action_def(A).
  201%structure_def(D)		--> durative_action_def(D).	%:durativeactions
  202%structure_def(D)		--> derived_def(D).		%:derivedpredicates
  203%typed_list(W, G)		--> oneOrMore(W, N), ['-'], type(T), {G =.. [T, N]}.
  204typed_list(W, [G|Ns])		--> oneOrMore(W, N), ['-'], type(T), !, typed_list(W, Ns), {G =.. [T,N]}.
  205typed_list(W, N)		--> zeroOrMore(W, N).
  206
  207primitive_type(N)		--> name(N).
  208type(either(PT))		--> ['(',either], !, oneOrMore(primitive_type, PT), [')'].
  209type(PT)			--> primitive_type(PT).
  210function_typed_list(W, [F|Ls])
  211				--> oneOrMore(W, L), ['-'], !, function_type(T), function_typed_list(W, Ls), {F =.. [T|L]}.	%:typing
  212function_typed_list(W, L)	--> zeroOrMore(W, L).
  213
  214function_type(number)		--> [number].
  215
  216emptyOr(_)			--> ['(',')'].
  217emptyOr(W)			--> W.
  218
  219% Actions definitons
  220action_def(action(S, L, Precon, Pos, Neg, Assign))
  221				--> ['(',':',action], action_symbol(S),
  222						[':',parameters,'('], typed_list(variable, L), [')'],
  223						action_def_body(Precon, Pos, Neg, Assign),
  224					[')'].
  225action_symbol(N)		--> name(N).
  226action_def_body(P, Pos, Neg, Assign)
  227				--> (([':',precondition], emptyOr(pre_GD(P)))	; []),
  228				    (([':',effect],       emptyOr(effect(Pos, Neg, Assign)))	; []).
  229pre_GD([F])			--> atomic_formula(term, F), !.
  230pre_GD(P)			--> pref_GD(P).
  231pre_GD(P)			--> ['(',and], pre_GD(P), [')'].
  232%pre_GD(forall(L, P))		--> ['(',forall,'('], typed_list(variable, L), [')'], pre_GD(P), [')'].		%:universal-preconditions
  233%pref_GD(preference(N, P))	--> ['(',preference], (pref_name(N); []), gd(P), [')'].				%:preferences
  234pref_GD(P)			--> gd(P).
  235pref_name(N)			--> name(N).
  236gd(F)				--> atomic_formula(term, F).	%: this option is covered by gd(L)
  237%gd(L)				--> literal(term, L).								%:negative-preconditions
  238gd(P)				--> ['(',and],  zeroOrMore(gd, P), [')'].
  239%gd(or(P))			--> ['(',or],   zeroOrMore(gd ,P), [')'].					%:disjuctive-preconditions
  240%gd(not(P))			--> ['(',not],  gd(P), [')'].							%:disjuctive-preconditions
  241%gd(imply(P1, P2))		--> ['(',imply], gd(P1), gd(P2), [')'].						%:disjuctive-preconditions
  242%gd(exists(L, P))		--> ['(',exists,'('], typed_list(variable, L), [')'], gd(P), [')'].		%:existential-preconditions
  243%gd(forall(L, P))		--> ['(',forall,'('], typed_list(variable, L), [')'], gd(P), [')'].		%:universal-preconditions
  244gd(F)				--> f_comp(F).	%:fluents
  245f_comp(compare(C, E1, E2))	--> ['('], binary_comp(C), f_exp(E1), f_exp(E2), [')'].
  246literal(T, F)			--> atomic_formula(T, F).
  247literal(T, not(F))		--> ['(',not], atomic_formula(T, F), [')'].
  248atomic_formula(_, F)		--> ['('], predicate(P), zeroOrMore(term, T), [')'], {F =.. [P|T]}.		% cheating, maybe wrong
  249
  250
  251term(N)				--> name(N).
  252term(V)				--> variable(V).
  253f_exp(N)			--> number(N).
  254f_exp(op(O, E1, E2))		--> ['('],binary_op(O), f_exp(E1), f_exp(E2), [')'].
  255f_exp('-'(E))			--> ['(','-'], f_exp(E), [')'].
  256f_exp(H)			--> f_head(H).
  257f_head(F)			--> ['('], function_symbol(S), zeroOrMore(term, T), [')'], { F =.. [S|T] }.
  258f_head(S)				--> function_symbol(S).
  259binary_op(O)			--> multi_op(O).
  260binary_op(45)			--> [45]. % 45 = minus = '-' 
  261binary_op('/')			--> ['/'].
  262multi_op('*')			--> ['*'].
  263multi_op('+')			--> ['+'].
  264binary_comp('>')		--> ['>'].
  265binary_comp('<')		--> ['<'].
  266binary_comp('=')		--> ['='].
  267binary_comp('>=')		--> ['>='].
  268binary_comp('<=')		--> ['<='].
  269number(N)			--> [N], {integer(N)}.
  270number(N)			--> [N], {float(N)}.
  271effect(P, N, A)			--> ['(',and], c_effect(P, N, A), [')'].
  272effect(P, N, A)			--> c_effect(P, N, A).
  273%c_effect(forall(E))		--> ['(',forall,'('], typed-list(variable)∗) effect(E), ')'.	%:conditional-effects
  274%c_effect(when(P, E))		--> ['(',when], gd(P), cond_effect(E), [')'].			%:conditional-effects
  275c_effect(P, N, A)		--> p_effect(P, N, A).
  276p_effect([], [], [])		--> [].
  277p_effect(Ps, Ns, [F|As])
  278				--> ['('], assign_op(O), f_head(H), f_exp(E), [')'], p_effect(Ps, Ns, As), {F =.. [O, H, E]}.
  279p_effect(Ps, [F|Ns], As)	--> ['(',not], atomic_formula(term,F), [')'], p_effect(Ps, Ns, As).
  280p_effect([F|Ps], Ns, As)	--> atomic_formula(term, F), p_effect(Ps, Ns, As).
  281%p_effect(op(O, H, E))		--> ['('], assign_op(O), f_head(H), f_exp(E), [')'].	%:fluents , What is difference between rule 3 lines above???
  282%cond_effect(E)		--> ['(',and], zeroOrMore(p_effect, E), [')'].				%:conditional-effects
  283%cond_effect(E)			--> p_effect(E).						%:conditional-effects
  284assign_op(assign)		--> [assign].
  285assign_op(scale_up)		--> [scale_up].
  286assign_op(scale_down)		--> [scale_down].
  287assign_op(increase)		--> [increase].
  288assign_op(decrease)		--> [decrease].
  289
  290
  291% BNF description include operator <term>+ to mark zero or more replacements.
  292% This DCG extension to overcome this. 
  293oneOrMore(W, [R|Rs], A, C) :- F =.. [W, R, A, B], F, (
  294					oneOrMore(W, Rs, B, C) ;
  295					(Rs = [] , C = B) 
  296				).
  297% BNF operator <term>*
  298zeroOrMore(W, R)		--> oneOrMore(W, R).
  299zeroOrMore(_, [])		--> [].
  300
  301% Name is everything that is not number, bracket or question mark.
  302% Those rules are not necessary, but rapidly speed up parsing process.
  303name(N)				--> [N], {integer(N), !, fail}.
  304name(N)				--> [N], {float(N), !, fail}.
  305name(N)				--> [N], {N=')', !, fail}.
  306name(N)				--> [N], {N='(', !, fail}.
  307name(N)				--> [N], {N='?', !, fail}.
  308name(N)				--> [N], {N='-', !, fail}.
  309name(N)				--> [N].
  310
  311
  312
  313%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  314%% parseDomain.pl
  315%%   Simple parser of PDDL domain file into prolog syntax.
  316%% Author: Robert Sasak, Charles University in Prague
  317%%
  318%% Example: 
  319%% ?-parseProblem('problem.pddl', O).
  320%%   O = problem('blocks-4-0',							%name
  321%%              blocks,										%domain name
  322%%              _G1443,                            %require definition
  323%%              [block(d, b, a, c)],					%object declaration
  324%%              [ clear(c), clear(a), clear(b), clear(d), ontable(c), %initial state
  325%%                ontable(a), ontable(b), ontable(d), handempty,
  326%%                set('total-cost', 0)	],
  327%%              [on(d, c), on(c, b), on(b, a)],		%goal
  328%%              _G1447,										%constraints-not implemented
  329%%              metric(minimize, 'total-cost'),		%metric
  330%%              _G1449										%length_specification-not implemented
  331%%              )
  332%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  333:-catch(guitracer,E,writeq(E)),nl.  334
  335% parseProblem(+File, -Output).
  336% Parse PDDL problem File and return rewritten prolog syntax. 
  337parseProblem(F, O):-parseProblem(F, O, _).
  338
  339% parseProblem(+File, -Output, -RestOfFile).
  340% The same as above and also return rest of file. Can be useful when domain and problem are in one file.
  341parseProblem(F, O, R) :-
  342	read_file(F, L),
  343	problem(O, L, R).
  344
  345% Support for reading file as a list.
  346% :-[readFile].
  347
  348
  349
  350% List of DCG rules describing structure of problem file in language PDDL.
  351% BNF description was obtain from http://www.cs.yale.edu/homes/dvm/papers/pddl-bnf.pdf
  352% This parser do not fully NOT support PDDL 3.0
  353% However you will find comment out lines ready for futher development.
  354% Some of the rules are already implemented in parseDomain.pl
  355% :-[parseDomain]. %make sure that it is loaded.
  356problem(problem(Name, Domain, R, OD, I, G, _, MS, LS))   
  357				--> ['(',define,'(',problem,Name,')',
  358							'(',':',domain, Domain,')'],
  359                     (require_def(R)		; []),
  360							(object_declaration(OD)	; []),
  361							init(I),
  362							goal(G),
  363%                     (constraints(C)		; []), %:constraints
  364							(metric_spec(MS)	; []),
  365                     (length_spec(LS)	; []),
  366				[')'].
  367
  368object_declaration(L)		--> ['(',':',objects], typed_list_as_list(name, L),[')'].
  369
  370typed_list_as_list(W, [G|Ns])           --> oneOrMore(W, N), ['-'], type(T), !, typed_list_as_list(W, Ns), {G =.. [T,N]}.
  371typed_list_as_list(W, N)                --> zeroOrMore(W, N).
  372
  373
  374
  375init(I)                      	--> ['(',':',init], zeroOrMore(init_el, I), [')'].
  376
  377init_el(I)			--> literal(name, I).
  378init_el(set(H,N))		--> ['(','='], f_head(H), number(N), [')'].					%fluents
  379init_el(at(N, L))		--> ['(',at], number(N), literal(name, L), [')'].				% timed-initial literal
  380goal(G)				--> ['(',':',goal], pre_GD(G),[')'].
  381%constraints(C)			--> ['(',':',constraints], pref_con_GD(C), [')'].				% constraints
  382pref_con_GD(and(P))		--> ['(',and], zeroOrMore(pref_con_GD, P), [')'].
  383%pref_con_GD(foral(L, P))	--> ['(',forall,'('], typed_list(variable, L), [')'], pref_con_GD(P), [')'].	%universal-preconditions
  384%pref_con_GD(prefernce(N, P))	--> ['(',preference], (pref_name(N) ; []), con_GD(P), [')'].			%prefernces
  385pref_con_GD(P)			--> con_GD(P).
  386
  387con_GD(and(L))			--> ['(',and], zeroOrMore(con_GD, L), [')'].
  388con_GD(forall(L, P))		--> ['(',forall,'('], typed_list(variable, L),[')'], con_GD(P), [')'].
  389con_GD(at_end(P))		--> ['(',at,end],	gd(P), [')'].
  390con_GD(always(P))		--> ['(',always],	gd(P), [')'].
  391con_GD(sometime(P))		--> ['(',sometime],	gd(P), [')'].
  392con_GD(within(N, P))		--> ['(',within], number(N), gd(P), [')'].
  393
  394con_GD(at_most_once(P))		--> ['(','at-most-once'], gd(P),[')'].
  395con_GD(some_time_after(P1, P2))	--> ['(','sometime-after'], gd(P1), gd(P2), [')'].
  396con_GD(some_time_before(P1, P2))--> ['(','sometime-before'], gd(P1), gd(P2), [')'].
  397con_GD(always_within(N, P1, P2))--> ['(','always-within'], number(N), gd(P1), gd(P2), [')'].
  398con_GD(hold_during(N1, N2, P))	--> ['(','hold-during'], number(N1), number(N2), gd(P), [')'].
  399con_GD(hold_after(N, P))	--> ['(','hold-after'], number(N), gd(P),[')'].
  400
  401metric_spec(metric(O, E))	--> ['(',':',metric], optimization(O), metric_f_exp(E), [')'].
  402
  403optimization(minimize)		--> [minimize].
  404optimization(maximize)		--> [maximize].
  405
  406metric_f_exp(E)			--> ['('], binary_op(O), metric_f_exp(E1), metric_f_exp(E2), [')'], {E =..[O, E1, E2]}.
  407metric_f_exp(multi_op(O,[E1|E]))--> ['('], multi_op(O), metric_f_exp(E1), oneOrMore(metric_f_exp, E), [')']. % I dont see meanful of this rule, in additional is missing in f-exp
  408metric_f_exp(E)			--> ['(','-'], metric_f_exp(E1), [')'], {E=..[-, E1]}.
  409metric_f_exp(N)			--> number(N).
  410metric_f_exp(F)			--> ['('], function_symbol(S), zeroOrMore(name, Ns), [')'], { F=..[S|Ns]}.%concat_atom([S|Ns], '-', F) }.
  411metric_f_exp(function(S))	--> function_symbol(S).
  412metric_f_exp(total_time)	--> ['total-time'].
  413metric_f_exp(is_violated(N))	--> ['(','is-violated'], pref_name(N), [')'].
  414
  415% Work arround
  416length_spec([])			--> [not_defined].	% there is no definition???