/* XML Utilities
 *
 * Copyright (C) 2001-2005 Binding Time Limited
 * Copyright (C) 2005-2011 John Fletcher
 *
 * Current Release: $Revision: 3.4 $
 * 
 * TERMS AND CONDITIONS:
 *
 * This program is offered free of charge, as unsupported source code. You may
 * use it, copy it, distribute it, modify it or sell it without restriction,
 * but entirely at your own risk.
 */


:- use_module(library(lists)).

% Entity and Namespace map operations: these maps are usually quite small, so
% a linear list lookup is okay. They could be substituted by a logarithmic
% data structure - in extremis.

/** empty_map( ?Map )
 *
 * is true if Map is a null map.
 */
empty_map( [] ).

/* map_member( +Key, +Map, ?Data )
 *
 * is true if Map is a ordered map structure
 * which records the pair Key-Data. Key must be ground.
 */
map_member( Key0, [Key1-Data1|Rest], Data0 ) :-
	( Key0 == Key1 ->
		Data0 = Data1
	; Key0 @> Key1 ->
		map_member( Key0, Rest, Data0 )
	).

/* map_store( +Map0, +Key, +Data, ?Map1 )
 *
 *
 * is true if Map0 is an ordered map
 * structure, Key must be ground, and Map1 is identical to Map0 except that
 * the pair Key-Data is recorded by Map1.
 */
map_store( [], Key, Data, [Key-Data] ).
map_store( [Key0-Data0|Map0], Key, Data, Map ) :-
	( Key == Key0 ->
		Map = [Key-Data|Map0]
	; Key @< Key0 ->
		Map = [Key-Data,Key0-Data0|Map0]
	; otherwise -> % >
		Map = [Key0-Data0|Map1],
		map_store( Map0, Key, Data, Map1 )
	).

/** context(?Element, ?PreserveSpace, ?CurrentNS, ?DefaultNS, ?Entities, ?Namespaces )
 *
 * is an ADT hiding the "state" arguments for XML Acquisition
 */
initial_context(
		Controls,
		context(void,PreserveSpace,'','',Entities,Empty,
			RemoveAttributePrefixes,AllowAmpersand)
		) :-
	empty_map( Empty ),
	( member( extended_characters(false), Controls ) ->
		Entities = Empty
	; otherwise ->
		extended_character_entities(Entities)
	),
	( member( format(false), Controls ) ->
		PreserveSpace = true
	; otherwise ->
		PreserveSpace = false
	),
	( member( remove_attribute_prefixes(true), Controls ) ->
		RemoveAttributePrefixes = true
	; otherwise ->
		RemoveAttributePrefixes = false
	),
	( member( allow_ampersand(true), Controls ) ->
		AllowAmpersand = true
	; otherwise ->
		AllowAmpersand = false
	).

context_update( current_namespace, Context0, URI, Context1 ) :-
	Context0 = context(Element,Preserve,_Current,Default,Entities,
		Namespaces,RemoveAttributePrefixes,Amp),
	Context1 = context(Element,Preserve,URI,Default,Entities,
		Namespaces,RemoveAttributePrefixes,Amp).
context_update( element, Context0, Tag, Context1 ) :-
	Context0 = context(_Element,Preserve,Current,Default,Entities,
		Namespaces,RemoveAttributePrefixes,Amp),
	Context1 = context(tag(Tag),Preserve,Current,Default,Entities,
		Namespaces,RemoveAttributePrefixes,Amp).
context_update( default_namespace, Context0, URI, Context1 ):-
	Context0 = context(Element,Preserve,Current,_Default,Entities,
		Namespaces,RemoveAttributePrefixes,Amp),
	Context1 = context(Element,Preserve,Current,URI,Entities,
		Namespaces,RemoveAttributePrefixes,Amp).
context_update( space_preserve, Context0, Boolean, Context1 ):-
	Context0 = context(Element,_Preserve,Current,Default,Entities,
		Namespaces,RemoveAttributePrefixes,Amp),
	Context1 = context(Element,Boolean,Current,Default,Entities,
		Namespaces,RemoveAttributePrefixes,Amp).
context_update( ns_prefix(Prefix), Context0, URI, Context1 ) :-
	Context0 = context(Element,Preserve,Current,Default,Entities,
		Namespaces0,RemoveAttributePrefixes,Amp),
	Context1 = context(Element,Preserve,Current,Default,Entities,
		Namespaces1,RemoveAttributePrefixes,Amp),
	map_store( Namespaces0, Prefix, URI, Namespaces1 ).
context_update( entity(Name), Context0, String, Context1 ) :-
	Context0 = context(Element,Preserve,Current,Default,Entities0,
		Namespaces,RemoveAttributePrefixes,Amp),
	Context1 = context(Element,Preserve,Current,Default,Entities1,
		Namespaces,RemoveAttributePrefixes,Amp),
	map_store( Entities0, Name, String, Entities1 ).

remove_attribute_prefixes( Context ) :-
	Context = context(_Element,_Preserve,_Current,_Default,_Entities,
		_Namespaces,true,_Amp).

current_tag( Context, Tag ) :-
	Context = context(tag(Tag),_Preserve,_Current,_Default,_Entities,
		_Namespaces,_RPFA,_Amp).

current_namespace( Context, Current ) :-
	Context = context(_Element,_Preserve,Current,_Default,_Entities,
		_Namespaces,_RPFA,_Amp).

default_namespace( Context, Default ) :-
	Context = context(_Element,_Preserve,_Current,Default,_Entities,
		_Namespaces,_RPFA,_Amp).

space_preserve( Context ) :-
	Context = context(tag(_Tag),true,_Current,_Default,_Entities,
		_Namespaces,_RPFA,_Amp).

specific_namespace( Prefix, Context, URI ) :-
	Context = context(_Element,_Preserve,_Current,_Default,_Entities,
		Namespaces,_RPFA,_Amp),
	map_member( Prefix, Namespaces, URI ).

defined_entity( Reference, Context, String ) :-
	Context = context(_Element,_Preserve,_Current,_Default,Entities,
		_Namespaces,_RPFA,_Amp),
	map_member( Reference, Entities, String ).
	
close_context( Context, Terms, WellFormed ) :-
	Context = context(Element,_Preserve,_Current,_Default,_Entities,
		_Namespaces,_RPFA,_Amp),
	close_context1( Element, Terms, WellFormed ).

close_context1( void, [], true ).
close_context1( tag(TagChars), [out_of_context(Tag)], false ) :-
	atom_chars( Tag, TagChars ).

void_context(
	context(void,_Preserve,_Current,_Default,_Entities,_Names,_RPFA,_Amp)
	).

allow_ampersand(
	context(_Void,_Preserve,_Current,_Default,_Entities,_Names,_RPFA,true)
	).

/** pp_string( +String )
 *
 * prints String onto the current output stream.
 * If String contains only 7-bit chars it is printed in shorthand quoted
 * format, otherwise it is written as a list.
 * If your Prolog uses " to delimit a special string type, just use write/1.
 */
pp_string( Chars ) :-
	( member( Char, Chars ),
	  not_shorthand( Char ) ->
		write( Chars )
	; otherwise ->
		put_quote,
		pp_string1( Chars ),
		put_quote
	).

not_shorthand( Char ) :-
	Char > 255.
not_shorthand( Char ) :-
	Char < 9.
not_shorthand( 126 ).	% ~ gives syntax errors in LPA Prolog

put_quote :-
	put( 0'" ). % '

pp_string1( [] ).
pp_string1( [Char|Chars] ) :-
	( Char =:= """"  -> % Meta-quote
		put( Char ),
		put( Char ),
		pp_string1( Chars )
	; Char =:= 13,	% Handle Windows border-settings
	  Chars = [10|Chars1] ->
		put( 10 ),
		pp_string1( Chars1 )
	; otherwise ->
		put( Char ),
		pp_string1( Chars )
	).

xml_declaration_attributes_valid( [] ).
xml_declaration_attributes_valid( [Name=Value|Attributes] ) :-
	xml_declaration_attribute_valid( Name, Value ),
	xml_declaration_attributes_valid( Attributes ).

xml_declaration_attribute_valid( Name, Value ) :-
	lowercase( Value, Lowercase ),
	canonical_xml_declaration_attribute( Name, Lowercase ).

canonical_xml_declaration_attribute( version, "1.0" ).
canonical_xml_declaration_attribute( standalone, "yes" ).
canonical_xml_declaration_attribute( standalone, "no" ).
% The encodings here are all valid for the output produced.
canonical_xml_declaration_attribute( encoding, "utf-8" ).
% canonical_xml_declaration_attribute( encoding, "utf-16" ).
% This is erroneous for the output of this library
canonical_xml_declaration_attribute( encoding, "us-ascii" ).
canonical_xml_declaration_attribute( encoding, "ascii" ).
canonical_xml_declaration_attribute( encoding, "iso-8859-1" ).
canonical_xml_declaration_attribute( encoding, "iso-8859-2" ).
canonical_xml_declaration_attribute( encoding, "iso-8859-15" ).
canonical_xml_declaration_attribute( encoding, "windows-1252" ).
% In general, it's better not to specify an encoding.

/* lowercase( +MixedCase, ?Lowercase ) holds when Lowercase and MixedCase are
 * lists of character codes, and Lowercase is identical to MixedCase with
 * every uppercase character replaced by its lowercase equivalent.
 */
lowercase( [], [] ).
lowercase( [Char|Chars], [Lower|LowerCase] ) :-
	( Char >= "A", Char =< "Z" ->
		Lower is Char + "a" - "A"
	; otherwise ->
		Lower = Char
	),
	lowercase( Chars, LowerCase ).

extended_character_entities( [
	"AElig"-[198],		% latin capital letter AE
	"Aacute"-[193],		% latin capital letter A with acute,
	"Acirc"-[194],		% latin capital letter A with circumflex,
	"Agrave"-[192],		% latin capital letter A with grave
	"Alpha"-[913],		% greek capital letter alpha, U+0391
	"Aring"-[197],		% latin capital letter A with ring above
	"Atilde"-[195],		% latin capital letter A with tilde,
	"Auml"-[196],		% latin capital letter A with diaeresis,
	"Beta"-[914],		% greek capital letter beta, U+0392
	"Ccedil"-[199],		% latin capital letter C with cedilla,
	"Chi"-[935],		% greek capital letter chi, U+03A7
	"Dagger"-[8225],	% double dagger, U+2021 ISOpub
	"Delta"-[916],		% greek capital letter delta,
	"ETH"-[208],		% latin capital letter ETH, U+00D0 ISOlat1>
	"Eacute"-[201],		% latin capital letter E with acute,
	"Ecirc"-[202],		% latin capital letter E with circumflex,
	"Egrave"-[200],		% latin capital letter E with grave,
	"Epsilon"-[917],	% greek capital letter epsilon, U+0395
	"Eta"-[919],		% greek capital letter eta, U+0397
	"Euml"-[203],		% latin capital letter E with diaeresis,
	"Gamma"-[915],		% greek capital letter gamma,
	"Iacute"-[205],		% latin capital letter I with acute,
	"Icirc"-[206],		% latin capital letter I with circumflex,
	"Igrave"-[204],		% latin capital letter I with grave,
	"Iota"-[921],		% greek capital letter iota, U+0399
	"Iuml"-[207],		% latin capital letter I with diaeresis,
	"Kappa"-[922],		% greek capital letter kappa, U+039A
	"Lambda"-[923],		% greek capital letter lambda,
	"Mu"-[924],			% greek capital letter mu, U+039C
	"Ntilde"-[209],		% latin capital letter N with tilde,
	"Nu"-[925],			% greek capital letter nu, U+039D
	"OElig"-[338],		% latin capital ligature OE,
	"Oacute"-[211],		% latin capital letter O with acute,
	"Ocirc"-[212],		% latin capital letter O with circumflex,
	"Ograve"-[210],		% latin capital letter O with grave,
	"Omega"-[937],		% greek capital letter omega,
	"Omicron"-[927],	% greek capital letter omicron, U+039F
	"Oslash"-[216],		% latin capital letter O with stroke
	"Otilde"-[213],		% latin capital letter O with tilde,
	"Ouml"-[214],		% latin capital letter O with diaeresis,
	"Phi"-[934],		% greek capital letter phi,
	"Pi"-[928],			% greek capital letter pi, U+03A0 ISOgrk3
	"Prime"-[8243],		% double prime = seconds = inches,
	"Psi"-[936],		% greek capital letter psi,
	"Rho"-[929],		% greek capital letter rho, U+03A1
	"Scaron"-[352],		% latin capital letter S with caron,
	"Sigma"-[931],		% greek capital letter sigma,
	"THORN"-[222],		% latin capital letter THORN,
	"Tau"-[932],		% greek capital letter tau, U+03A4
	"Theta"-[920],		% greek capital letter theta,
	"Uacute"-[218],		% latin capital letter U with acute,
	"Ucirc"-[219],		% latin capital letter U with circumflex,
	"Ugrave"-[217],		% latin capital letter U with grave,
	"Upsilon"-[933],	% greek capital letter upsilon,
	"Uuml"-[220],		% latin capital letter U with diaeresis,
	"Xi"-[926],			% greek capital letter xi, U+039E ISOgrk3
	"Yacute"-[221],		% latin capital letter Y with acute,
	"Yuml"-[376],		% latin capital letter Y with diaeresis,
	"Zeta"-[918],		% greek capital letter zeta, U+0396
	"aacute"-[225],		% latin small letter a with acute,
	"acirc"-[226],		% latin small letter a with circumflex,
	"acute"-[180],		% acute accent = spacing acute,
	"aelig"-[230],		% latin small letter ae
	"agrave"-[224],		% latin small letter a with grave
	"alefsym"-[8501],	% alef symbol = first transfinite cardinal,
	"alpha"-[945],		% greek small letter alpha,
	"and"-[8743],		% logical and = wedge, U+2227 ISOtech
	"ang"-[8736],		% angle, U+2220 ISOamso
	"aring"-[229],		% latin small letter a with ring above
	"asymp"-[8776],		% almost equal to = asymptotic to,
	"atilde"-[227],		% latin small letter a with tilde,
	"auml"-[228],		% latin small letter a with diaeresis,
	"bdquo"-[8222],		% double low-9 quotation mark, U+201E NEW
	"beta"-[946],		% greek small letter beta, U+03B2 ISOgrk3
	"brvbar"-[166],		% broken bar = broken vertical bar,
	"bull"-[8226],		% bullet = black small circle,
	"cap"-[8745],		% intersection = cap, U+2229 ISOtech
	"ccedil"-[231],		% latin small letter c with cedilla,
	"cedil"-[184],		% cedilla = spacing cedilla, U+00B8 ISOdia>
	"cent"-[162],		% cent sign, U+00A2 ISOnum>
	"chi"-[967],		% greek small letter chi, U+03C7 ISOgrk3
	"circ"-[710],		% modifier letter circumflex accent,
	"clubs"-[9827],		% black club suit = shamrock,
	"cong"-[8773],		% approximately equal to, U+2245 ISOtech
	"copy"-[169],		% copyright sign, U+00A9 ISOnum>
	"crarr"-[8629],		% downwards arrow with corner leftwards
	"cup"-[8746],		% union = cup, U+222A ISOtech
	"curren"-[164],		% currency sign, U+00A4 ISOnum>
	"dArr"-[8659],		% downwards double arrow, U+21D3 ISOamsa
	"dagger"-[8224],	% dagger, U+2020 ISOpub
	"darr"-[8595],		% downwards arrow, U+2193 ISOnum
	"deg"-[176],		% degree sign, U+00B0 ISOnum>
	"delta"-[948],		% greek small letter delta,
	"diams"-[9830],		% black diamond suit, U+2666 ISOpub
	"divide"-[247],		% division sign, U+00F7 ISOnum>
	"eacute"-[233],		% latin small letter e with acute,
	"ecirc"-[234],		% latin small letter e with circumflex,
	"egrave"-[232],		% latin small letter e with grave,
	"empty"-[8709],		% empty set = null set = diameter,
	"emsp"-[8195],		% em space, U+2003 ISOpub
	"ensp"-[8194],		% en space, U+2002 ISOpub
	"epsilon"-[949],	% greek small letter epsilon,
	"equiv"-[8801],		% identical to, U+2261 ISOtech
	"eta"-[951],		% greek small letter eta, U+03B7 ISOgrk3
	"eth"-[240],		% latin small letter eth, U+00F0 ISOlat1>
	"euml"-[235],		% latin small letter e with diaeresis,
	"euro"-[8364],		% euro sign, U+20AC NEW
	"exist"-[8707],		% there exists, U+2203 ISOtech
	"fnof"-[402],		% latin small f with hook = function
	"forall"-[8704],	% for all, U+2200 ISOtech
	"frac12"-[189],		% vulgar fraction one half
	"frac14"-[188],		% vulgar fraction one quarter
	"frac34"-[190],		% vulgar fraction three quarters
	"frasl"-[8260],		% fraction slash, U+2044 NEW
	"gamma"-[947],		% greek small letter gamma,
	"ge"-[8805],		% greater-than or equal to,
	"hArr"-[8660],		% left right double arrow,
	"harr"-[8596],		% left right arrow, U+2194 ISOamsa
	"hearts"-[9829],	% black heart suit = valentine,
	"hellip"-[8230],	% horizontal ellipsis = three dot leader,
	"iacute"-[237],		% latin small letter i with acute,
	"icirc"-[238],		% latin small letter i with circumflex,
	"iexcl"-[161],		% inverted exclamation mark, U+00A1 ISOnum>
	"igrave"-[236],		% latin small letter i with grave,
	"image"-[8465],		% blackletter capital I = imaginary part,
	"infin"-[8734],		% infinity, U+221E ISOtech
	"int"-[8747],		% integral, U+222B ISOtech
	"iota"-[953],		% greek small letter iota, U+03B9 ISOgrk3
	"iquest"-[191],		% inverted question mark
	"isin"-[8712],		% element of, U+2208 ISOtech
	"iuml"-[239],		% latin small letter i with diaeresis,
	"kappa"-[954],		% greek small letter kappa,
	"lArr"-[8656],		% leftwards double arrow, U+21D0 ISOtech
	"lambda"-[955],		% greek small letter lambda,
	"lang"-[9001],		% left-pointing angle bracket = bra,
	"laquo"-[171],		% left-pointing double angle quotation mark
	"larr"-[8592],		% leftwards arrow, U+2190 ISOnum
	"lceil"-[8968],		% left ceiling = apl upstile,
	"ldquo"-[8220],		% left double quotation mark,
	"le"-[8804],		% less-than or equal to, U+2264 ISOtech
	"lfloor"-[8970],	% left floor = apl downstile,
	"lowast"-[8727],	% asterisk operator, U+2217 ISOtech
	"loz"-[9674],		% lozenge, U+25CA ISOpub
	"lrm"-[8206],		% left-to-right mark, U+200E NEW RFC 2070
	"lsaquo"-[8249],	% single left-pointing angle quotation mark,
	"lsquo"-[8216],		% left single quotation mark,
	"macr"-[175],		% macron = spacing macron = overline
	"mdash"-[8212],		% em dash, U+2014 ISOpub
	"micro"-[181],		% micro sign, U+00B5 ISOnum>
	"middot"-[183],		% middle dot = Georgian comma
	"minus"-[8722],		% minus sign, U+2212 ISOtech
	"mu"-[956],			% greek small letter mu, U+03BC ISOgrk3
	"nabla"-[8711],		% nabla = backward difference,
	"nbsp"-[160],		% no-break space = non-breaking space,
	"ndash"-[8211],		% en dash, U+2013 ISOpub
	"ne"-[8800],		% not equal to, U+2260 ISOtech
	"ni"-[8715],		% contains as member, U+220B ISOtech
	"not"-[172],		% not sign, U+00AC ISOnum>
	"notin"-[8713],		% not an element of, U+2209 ISOtech
	"nsub"-[8836],		% not a subset of, U+2284 ISOamsn
	"ntilde"-[241],		% latin small letter n with tilde,
	"nu"-[957],			% greek small letter nu, U+03BD ISOgrk3
	"oacute"-[243],		% latin small letter o with acute,
	"ocirc"-[244],		% latin small letter o with circumflex,
	"oelig"-[339],		% latin small ligature oe, U+0153 ISOlat2
	"ograve"-[242],		% latin small letter o with grave,
	"oline"-[8254],		% overline = spacing overscore,
	"omega"-[969],		% greek small letter omega,
	"omicron"-[959],	% greek small letter omicron, U+03BF NEW
	"oplus"-[8853],		% circled plus = direct sum,
	"or"-[8744],		% logical or = vee, U+2228 ISOtech
	"ordf"-[170],		% feminine ordinal indicator, U+00AA ISOnum>
	"ordm"-[186],		% masculine ordinal indicator,
	"oslash"-[248],		% latin small letter o with stroke,
	"otilde"-[245],		% latin small letter o with tilde,
	"otimes"-[8855],	% circled times = vector product,
	"ouml"-[246],		% latin small letter o with diaeresis,
	"para"-[182],		% pilcrow sign = paragraph sign,
	"part"-[8706],		% partial differential, U+2202 ISOtech
	"permil"-[8240],	% per mille sign, U+2030 ISOtech
	"perp"-[8869],		% up tack = orthogonal to = perpendicular,
	"phi"-[966],		% greek small letter phi, U+03C6 ISOgrk3
	"pi"-[960],			% greek small letter pi, U+03C0 ISOgrk3
	"piv"-[982],		% greek pi symbol, U+03D6 ISOgrk3
	"plusmn"-[177],		% plus-minus sign = plus-or-minus sign,
	"pound"-[163],		% pound sign, U+00A3 ISOnum>
	"prime"-[8242],		% prime = minutes = feet, U+2032 ISOtech
	"prod"-[8719],		% n-ary product = product sign,
	"prop"-[8733],		% proportional to, U+221D ISOtech
	"psi"-[968],		% greek small letter psi, U+03C8 ISOgrk3
	"rArr"-[8658],		% rightwards double arrow,
	"radic"-[8730],		% square root = radical sign,
	"rang"-[9002],		% right-pointing angle bracket = ket,
	"raquo"-[187],		% right-pointing double angle quotation mark
	"rarr"-[8594],		% rightwards arrow, U+2192 ISOnum
	"rceil"-[8969],		% right ceiling, U+2309 ISOamsc
	"rdquo"-[8221],		% right double quotation mark,
	"real"-[8476],		% blackletter capital R = real part symbol,
	"reg"-[174],		% registered sign = registered trade mark sign,
	"rfloor"-[8971],	% right floor, U+230B ISOamsc
	"rho"-[961],		% greek small letter rho, U+03C1 ISOgrk3
	"rlm"-[8207],		% right-to-left mark, U+200F NEW RFC 2070
	"rsaquo"-[8250],	% single right-pointing angle quotation mark,
	"rsquo"-[8217],		% right single quotation mark,
	"sbquo"-[8218],		% single low-9 quotation mark, U+201A NEW
	"scaron"-[353],		% latin small letter s with caron,
	"sdot"-[8901],		% dot operator, U+22C5 ISOamsb
	"sect"-[167],		% section sign, U+00A7 ISOnum>
	"shy"-[173],		% soft hyphen = discretionary hyphen,
	"sigma"-[963],		% greek small letter sigma,
	"sigmaf"-[962],		% greek small letter final sigma,
	"sim"-[8764],		% tilde operator = varies with = similar to,
	"spades"-[9824],	% black spade suit, U+2660 ISOpub
	"sub"-[8834],		% subset of, U+2282 ISOtech
	"sube"-[8838],		% subset of or equal to, U+2286 ISOtech
	"sum"-[8721],		% n-ary sumation, U+2211 ISOamsb
	"sup"-[8835],		% superset of, U+2283 ISOtech
	"sup1"-[185],		% superscript one = superscript digit one,
	"sup2"-[178],		% superscript two = superscript digit two
	"sup3"-[179],		% superscript three = superscript digit three
	"supe"-[8839],		% superset of or equal to,
	"szlig"-[223],		% latin small letter sharp s = ess-zed,
	"tau"-[964],		% greek small letter tau, U+03C4 ISOgrk3
	"there4"-[8756],	% therefore, U+2234 ISOtech
	"theta"-[952],		% greek small letter theta,
	"thetasym"-[977],	% greek small letter theta symbol,
	"thinsp"-[8201],	% thin space, U+2009 ISOpub
	"thorn"-[254],		% latin small letter thorn with,
	"tilde"-[732],		% small tilde, U+02DC ISOdia
	"times"-[215],		% multiplication sign, U+00D7 ISOnum>
	"trade"-[8482],		% trade mark sign, U+2122 ISOnum
	"uArr"-[8657],		% upwards double arrow, U+21D1 ISOamsa
	"uacute"-[250],		% latin small letter u with acute,
	"uarr"-[8593],		% upwards arrow, U+2191 ISOnum
	"ucirc"-[251],		% latin small letter u with circumflex,
	"ugrave"-[249],		% latin small letter u with grave,
	"uml"-[168],		% diaeresis = spacing diaeresis,
	"upsih"-[978],		% greek upsilon with hook symbol,
	"upsilon"-[965],	% greek small letter upsilon,
	"uuml"-[252],		% latin small letter u with diaeresis,
	"weierp"-[8472],	% script capital P = power set
	"xi"-[958],			% greek small letter xi, U+03BE ISOgrk3
	"yacute"-[253],		% latin small letter y with acute,
	"yen"-[165],		% yen sign = yuan sign, U+00A5 ISOnum>
	"yuml"-[255],		% latin small letter y with diaeresis,
	"zeta"-[950],		% greek small letter zeta, U+03B6 ISOgrk3
	"zwj"-[8205],		% zero width joiner, U+200D NEW RFC 2070
	"zwnj"-[8204]		% zero width non-joiner,
	] ).

/* @pred chars( ?Chars, ?Plus, ?Minus )
 *
 * used as chars( ?Chars ) in a DCG to
 * copy the list Chars inline.
 *
 * This is best expressed in terms of append/3 where append/3 is built-in.
 * For other Prologs, a straightforward specification can be used:
 *
 *	chars( [] ) --> "".
 *	chars( [Char|Chars] ) -->
 *		[Char],
 *		chars( Chars ).
 */

chars( Chars, Plus, Minus ) :-
	append( Chars, Minus, Plus ).