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