/*

    Parse a 'focus' string and produce a list of stuff we now know...
    (an "options" list)


focus(X, "some-x -search +words", "").
X = [text('some-x'), negtext(search), text(words), multiple|_G408]

focus(X, "http://foo.bar#baz", "").
X = [uri_with_hash('http://foo.bar', baz, [uri_without_hash|_G332])|_G390]

focus(X, "http://foo.bar", "").
X = [uri_without_hash|_G306]


*/


focus_flags(Focus, FocusFlags) :-
	atom_codes(Focus, FocusCodes),
	(  focus(FocusFlags, FocusCodes, "")
	-> true
	;  memberchk(focus_syntax_error, FocusFlags)
	),
	length(FocusFlags, _),   % close the list
	!.

focus(X) -->
	optwhitespace, ("" ; identifier(X) ; pattern(X)), optwhitespace.


/* This grammar is ambiguous about qname vs uri_without_hash; that's
just how the specs are written.  ("foo:bar" could be either.)  We'll
put qname first because ... I think that's usually right.
Backtracking will pick up the other one... */

identifier(X) -->
	qname(X)
	; uri_without_hash(X)
	; uri_with_hash(X)
	; prefix_alone(X)
/*	; ground_function_term(X)  */
	.

pattern(X) -->
	(
	  search_term_list(X)
/*	; n3_property_list(X)   */
/*	; non_ground_function_term(X)  */
	),
	{ memberchk(multiple, X) }
	.

% could use
% http://gbiv.com/protocols/uri/rev-2002/rfc2396bis.html
% but we don't really care about the details much.
uri_without_hash(X) -->
	save_text(uri_without_hash2(X), URI),
	{  memberchk(uri_without_hash, X),
	   memberchk(load(URI), X)  }
	.

uri_without_hash2(_) -->
	scheme, ":", ( "//", authority ; "" ), path, ( "?", query ; "").

scheme -->
	[Alpha], { code_type(Alpha, alpha) }, schemeRest.
schemeRest -->
	""
	; schemeChar, schemeRest.
schemeChar -->
	  [D], { code_type(D, alnum) }
	; "."
	; "-"
	; "+"
	.

% hack for now?
authority --> general.
path --> general.
query --> general.

general -->
	  generalChar
	; generalChar, general.

generalChar -->
	pchar ; "/" ; "?".
pchar -->
	unreserved ; pct_encoded ; sub_delims ; ":" ; "@".
unreserved -->
	[D], { code_type(D, alnum) }
	; "."
	; "-"
	; "_"
	; "~"
	.
sub_delims --> "!" ; "$" ; "&" ; "'" ; "(" ; ")"
               ; "*" ; "+" ; "," ; ";" ; "=".
pct_encoded -->
	"%", hexdig, hexdig.
hexdig -->
	[D], { code_type(D, hex) }.

% From http://www.w3.org/TR/REC-xml-names/
qname(X) -->
	save_text(prefix,Prefix), ":", save_text(localpart,Localpart),
	{ memberchk(qname(Prefix, Localpart), X) }
	.

uri_with_hash(X) -->	
	save_text(uri_without_hash(Sub), Stem),
	"#",
	save_text(general, Fragment),
	{ memberchk(uri_with_hash(Stem, Fragment, Sub), X),
	  memberchk(load(Stem), X) }.

uri(X) -->
	save_text(uri_without_hash2(_),X).
uri(X) -->
	save_text(uri_with_hash2,X).
uri_with_hash2 -->
	uri_without_hash2(_), "#", general.

	
/*
  Wrapper for a DCG non-terminal letting you grab
  the actual text which matched.
*/
save_text(Callable, SaveTo, Li, Lo) :-
	call(Callable, Li, Lo),
	append(Codes, Lo, Li),
	atom_codes(SaveTo, Codes).

	
	
prefix_alone(X) -->
	save_text(prefix, Prefix), ":",
	{ memberchk(prefix_alone(Prefix), X) }
	.

prefix -->
	ncname.

localpart -->
	ncname.

ncname -->
	  letter, ncrest
	; "_", ncrest
	.

ncrest -->
	""
	; ncNameChar, ncrest
	.

ncNameChar -->
	  [D], { code_type(D, alnum) }
	; "."
	; "-"
	; "_"
	.
	% plus CombiningChar and Extender... ?!?!?

letter([C|Cs], Cs) :- char_type(C, alpha).
	
optwhitespace -->
	  ""
	; " ", optwhitespace.

whitespace -->
	  " "
	; " ", optwhitespace.

search_term_list(X) -->
	  search_term(X)
	; search_term(X), whitespace, search_term_list(X).

search_term(X) -->
	save_text(word,Word),
	{ memberchk(word(Word), X) }.

search_term(X) -->
	"-", save_text(word,Word),
	{ memberchk(not(word(Word)), X) }.

search_term(X) -->
	% allow "+foo" but dont do anything special with the "+" yet
	"+", save_text(word,Word),
	{ memberchk(word(Word), X) }.

search_term(X) -->
	"""", save_text(noquote,Word), """",
	{ memberchk(string(Word), X) }.
search_term(X) -->
	"+""", save_text(noquote,Word), """",
	{ memberchk(not(string(Word)), X) }.
search_term(X) -->
	"-""", save_text(noquote,Word), """",
	{ memberchk(not(string(Word)), X) }.

noquote -->
	""
	; noquotechar
	; noquotechar, noquote
	.

noquotechar -->
	[D], { [D] \= """" }.

word --> ncname.

	
% save_text(word, X, "A", "").
