%
% xml.pro % a Prolog to XML translator % a main entry point just for testing % purposes. :- module(amzi_xml). :- export xml_term/2. test :- test(TERM), writeq(TERM), nl, xml_term(XML, TERM), writeq(XML), nl, xml_term(XML, TERM2), writeq(TERM2), nl, nl, fail. test. % various test cases test(hello). test('Daffy Duck isn''t'). test($this is a $$5 string$). test([23, 2.3, 2.3e4, 2.3e23]). test([a,b, [c,d, [e,f]]]). test(X). test([X,Y,X]). test(duck(leona)). test(same(X,X)). test(different(X,Y)). test(X = [a,b,foo(2.3, 4)]). test(complex(first, X = [a,b,foo(2.3, 4)], nest(X, [Y,$a string$,X]))). test(complex(first, X = [a,b,foo(2.3, 4)], nest(X, [Y,$a string$,X]))). % Convert between a Prolog term and a string % of XML describing that term. This uses well-formed % XML, but not 'valid' because it doesn't include % the DTD specification. See next predicates for that. xml_term(XML_STRING, TERM) :- var(XML_STRING), !, term(TERM, VARS, XML_CHARS, ""), string_list(XML_STRING, XML_CHARS). xml_term(XML_STRING, TERM) :- string_list(XML_STRING, XML_CHARS), term(TERM, VARS, XML_CHARS, ""), !. % A valid XML document has a document specification DTD % included. This predicate provides well-formed and % valid XML. valid_xml_term(XML_STRING, TERM) :- var(XML_STRING), !, xmldoc(TERM, XML_CHARS, ""), string_list(XML_STRING, XML_CHARS). valid_xml_term(XML_STRING, TERM) :- string_list(XML_STRING, XML_CHARS), xmldoc(TERM, XML_CHARS, ""), !. xmlhead --> "". doctype --> "", "", "", "", "", "", "", "", "", "", "]>". xmldoc(TERM) --> xmlhead, doctype, "", term(TERM, VARS), " ". % Check which way we're going. Need to check % the XML string, because T might be a variable % either way. term(T, VARS, XML, REST) :- var(XML), !, term_to_xml(T, VARS, XML, REST). term(T, VARS) --> xml_to_term(T, VARS). % These clauses cover the case where the XML % is provided and we're creating the term. xml_to_term(T, VARS) --> tag(string), !, term_codes(CODES), endtag(string), { string_list(T, CODES) }. xml_to_term(T, VARS) --> tag(atom), !, term_codes(CODES), endtag(atom), { atom_codes(T, CODES) }. xml_to_term(T, VARS) --> tag(number), !, term_codes(CODES), endtag(number), { string_list(S, CODES), string_term(S, T) }. xml_to_term(T, VARS) --> tag(list), !, items(T, VARS), endtag(list). xml_to_term(T, VARS) --> tag(structure), !, tag(name), term_codes(CODES), { atom_codes(NAME, CODES) }, endtag(name), args(ARGS, VARS), { T =.. [NAME|ARGS] }, endtag(structure). xml_to_term(T, VARS) --> tag(variable), !, term_codes(CODES), { atom_codes(VNAME, CODES), open_member(VNAME=T, VARS) }, endtag(variable). % These clauses are for the cases when the % term is provided and we're generating XML. term_to_xml(T, VARS) --> { number(T), ! }, { string_term(S, T), string_list(S, CODES) }, tag(number), term_codes(CODES), endtag(number). term_to_xml(T, VARS) --> { string(T), ! }, { string_list(T, CODES) }, tag(string), term_codes(CODES), endtag(string). term_to_xml(T, VARS) --> { atom(T), ! }, { atom_codes(T, CODES) }, tag(atom), term_codes(CODES), endtag(atom). term_to_xml(T, VARS) --> { islist(T), ! }, tag(list), items(T, VARS), endtag(list). term_to_xml(T, VARS) --> { structure(T), ! }, { T =.. [NAME|ARGS], atom_codes(NAME, CODES) }, tag(structure), tag(name), term_codes(CODES), endtag(name), args(ARGS, VARS), endtag(structure). term_to_xml(T, VARS) --> { var(T), ! }, tag(variable), { string_term(S, T), string_list(S, CODES) }, term_codes(CODES), endtag(variable). % Pick up the value of the list of codes for that % stuff that resides between two tags. So the argument % in these clauses is the list of codes. The parsed % input is from the XML string. term_codes([X|Y]) --> term_code(X), !, term_codes(Y). term_codes([]) --> []. % In the code list, that is between the tags, a % < or > is represented specially. A real < read % from the XML string signifies the end of the % stuff between the tags. Note this works both % ways, when the code list is bound or the XML % text is bound. The last clause is only in play % when it's the XML string that is bound. term_code(0'<) --> "<". term_code(0'>) --> ">". term_code(0'&) --> "&". term_code(X) --> [X], { X \= 0'< }. % Pick up a tag. tag(TAG) --> white_space, "<", word(TAG), ">". endtag(TAG) --> "", word(TAG), ">". solotag(TAG) --> "<", word(TAG), "/>". word(WORD) --> { var(WORD), ! }, chars(CHARS), { atom_codes(WORD, CHARS) }. word(WORD) --> { nonvar(WORD) }, { atom_codes(WORD, CHARS) }, chars(CHARS). white_space --> white, white_space. white_space --> []. white --> [X], { nonvar(X), X =< 32 }. chars([X|Y]) --> char(X), !, chars(Y). chars([]) --> []. char(X) --> [X], { is_char(X) }. is_char(X) :- X >= 0'a, X =< 0'z, !. is_char(X) :- X >= 0'A, X =< 0'Z, !. is_char(X) :- X >= 0'0, X =< 0'9, !. is_char(0'_). % Items in a list items([X|Y], VARS) --> item(X, VARS), !, items(Y, VARS). items([], _) --> []. item(X, VARS) --> tag(item), term(X, VARS), endtag(item). % A list of structure arguments args([X|Y], VARS) --> sarg(X, VARS), !, args(Y, VARS). args([], _) --> []. sarg(X, VARS) --> tag(arg), term(X, VARS), endtag(arg). % open_member is just like member, but named it this % just to indicate its working on an open list, and % adds values that aren't already on the list. open_member(X, [X|_]). open_member(X, [_|Y]) :- open_member(X,Y). :- end_module(amzi_xml).