#include "parse.h"

/*
	atom:         «string» | '*'
	scope:        «atom» ('.' «atom»)*
	path:         '^'? «scope» ('>'? «scope»)* '$'?
	group:        '(' «selector» ')'
	filter:       ("L:"|"R:"|"B:") («group» | «path»)
	expression:   '-'? («filter» | «group» | «path»)
	composite:    «expression» ([|&-] «expression»)*
	selector:     «composite» (',' «composite»)*
*/

// #define ENTER fprintf(stderr, "%s\n", __FUNCTION__)
#define ENTER

namespace scope
{
	namespace parse
	{
		using namespace types;

		struct context_t
		{
			char const* it;
			char const* last;

			bool ws ();

			bool parse_char (char const* ch, char* dst = NULL);

			bool parse_atom (atom_t& res);
			bool parse_scope (scope::types::scope_t& res);
			bool parse_path (path_t& res);
			bool parse_path (any_ptr& res);
			bool parse_group (any_ptr& res);
			bool parse_filter (any_ptr& res);
			bool parse_expression (expression_t& res);
			bool parse_composite (composite_t& res);
			bool parse_selector (selector_t& res);
		};

		bool context_t::parse_atom (atom_t& res)
		{
			ENTER;
			if(parse_char("*"))
				return res = scope::types::atom_any, true;

			if(!isalnum(*it) && *it < 0x80)
				return false;

			char const* from = it;
			while(isalnum(*it) || *it == '_' || *it == '-' || *it == '+' || *it > 0x7F)
				++it;
			res.insert(res.end(), from, it);
			return true;
		}

		bool context_t::parse_scope (scope::types::scope_t& res)
		{
			ENTER;
			bool rc = false;
			res.anchor_to_previous = parse_char(">") && ws();
			do {
				res.atoms.push_back(atom_t());
				if(!parse_atom(res.atoms.back()))
				{
					res.atoms.pop_back();
					break;
				}
				rc = true;
			} while(parse_char("."));

			return rc;
		}

		bool context_t::parse_path (path_t& res)
		{
			ENTER;
			res.anchor_to_bol = parse_char("^") && ws();

			do {
				res.scopes.push_back(scope::types::scope_t());
				if(!parse_scope(res.scopes.back()))
					break;
			} while(ws());

			if(res.scopes.size() > 1)
				res.scopes.pop_back();

			res.anchor_to_eol = parse_char("$");
			return true;
		}

		bool context_t::parse_path (any_ptr& res)
		{
			path_t tmp;
			if(parse_path(tmp))
				return res.reset(new path_t(tmp)), true;
			return false;
		}

		bool context_t::parse_group (any_ptr& res)
		{
			ENTER;
			char const* bt = it;
			group_t group;
			if(parse_char("(") && parse_selector(group.selector) && ws() && parse_char(")"))
				return res.reset(new group_t(group)), true;
			return it = bt, false;
		}

		bool context_t::parse_filter (any_ptr& res)
		{
			ENTER;
			char const* bt = it;
			filter_t filter;
			char side;
			if(parse_char("LRB", &side) && parse_char(":") && ws() && (parse_group(filter.selector) || parse_path(filter.selector)))
				return filter.filter = filter_t::side_t(side), res.reset(new filter_t(filter)), true;
			return it = bt, false;
		}

		bool context_t::parse_expression (expression_t& res)
		{
			ENTER;
			if(parse_char("-") && ws())
				res.negate = true;
			return parse_filter(res.selector) || parse_group(res.selector) || parse_path(res.selector);
		}

		bool context_t::parse_composite (composite_t& res)
		{
			ENTER;
			bool rc = false;
			char op = expression_t::op_none;
			do {
				expression_t tmp(op);
				if(!parse_expression(tmp))
					break;
				res.expressions.push_back(tmp);
				rc = true;
			} while(ws() && parse_char("&|-", &op) && ws());
			return rc;
		}

		bool context_t::parse_selector (selector_t& res)
		{
			ENTER;
			bool rc = false;
			ws();
			do {
				composite_t tmp;
				if(!parse_composite(tmp))
					break;
				res.composites.push_back(tmp);
				rc = true;
			} while(ws() && parse_char(",") && ws());
			return rc;
		}

		// =============================
		// = General Utility Functions =
		// =============================

		bool context_t::ws ()
		{
			while(it != last && strchr(" \t", *it))
				++it;
			return true;
		}

		bool context_t::parse_char (char const* ch, char* dst)
		{
			if(it == last || !strchr(ch, *it))
				return false;
			if(dst)
				*dst = *it;
			return ++it, true;
		}

		// =======
		// = API =
		// =======

		char const* scope (char const* first, char const* last, scope::types::scope_t& scope)
		{
			context_t context = { first, last };
			context.ws();
			context.parse_scope(scope);
			return context.it;
		}

		char const* path (char const* first, char const* last, scope::types::path_t& path)
		{
			context_t context = { first, last };
			context.ws();
			context.parse_path(path);
			return context.it;
		}

		char const* selector (char const* first, char const* last, scope::types::selector_t& selector)
		{
			context_t context = { first, last };
			context.ws();
			context.parse_selector(selector);
			return context.it;
		}

	} /* parse */ 

} /* scope */ 
