lib/parse.js
function parse(str) {
	str = '(' + str.trim() + ')';

	/**
	 * expr
	 */

	return expr();

	/**
	 * Assert `expr`.
	 */

	function error(expr, msg) {
		if (expr) return;
		var ctx = str.slice(0, 10);
		assert(0, msg + ' near `' + ctx + '`');
	}

	/**
	 * '(' binop ')'
	 */

	function expr() {
		error('(' == str[0], "missing opening '('");
		str = str.slice(1);

		var node = binop();

		error(')' == str[0], "missing closing ')'");
		str = str.slice(1);

		return node;
	}

	/**
	 * field | expr
	 */

	function primary() {
		return field()
			|| expr();
	}

	/**
	 * primary (OR|AND) binop
	 */

	function binop() {
		var left = primary();

		var m = str.match(/^ *(OR|AND) */i);
		if (!m) return left;

		str = str.slice(m[0].length);

		var right = binop();

		return {
			type: 'op',
			op: m[1].toLowerCase(),
			left: left,
			right: right
		}
	}

	/**
	 * FIELD[:VALUE]
	 */

	function field() {
		var val = true;
		var m = str.match(/^([-.\w]+)/);
		if (!m) return;

		var name = m[0];
		str = str.slice(name.length);

		var m = str.match(/^:([-*\w.]+|".*?"|'.*?'|\/(.*?)\/) */);
		if (m) {
			str = str.slice(m[0].length);
			val = m[1];
			if (numeric(val)) val = parseFloat(val);
		}

		return {
			type: 'field',
			name: name,
			value: coerce(val)
		}
	}
}
function parse(str) {
	str = '(' + str.trim() + ')';

	/**
	 * expr
	 */

	return expr();

	/**
	 * Assert `expr`.
	 */

	function error(expr, msg) {
		if (expr) return;
		var ctx = str.slice(0, 10);
		assert(0, msg + ' near `' + ctx + '`');
	}

	/**
	 * '(' binop ')'
	 */

	function expr() {
		error('(' == str[0], "missing opening '('");
		str = str.slice(1);

		var node = binop();

		error(')' == str[0], "missing closing ')'");
		str = str.slice(1);

		return node;
	}

	/**
	 * field | expr
	 */

	function primary() {
		return field()
			|| expr();
	}

	/**
	 * primary (OR|AND) binop
	 */

	function binop() {
		var left = primary();

		var m = str.match(/^ *(OR|AND) */i);
		if (!m) return left;

		str = str.slice(m[0].length);

		var right = binop();

		return {
			type: 'op',
			op: m[1].toLowerCase(),
			left: left,
			right: right
		}
	}

	/**
	 * FIELD[:VALUE]
	 */

	function field() {
		var val = true;
		var m = str.match(/^([-.\w]+)/);
		if (!m) return;

		var name = m[0];
		str = str.slice(name.length);

		var m = str.match(/^:([-*\w.]+|".*?"|'.*?'|\/(.*?)\/) */);
		if (m) {
			str = str.slice(m[0].length);
			val = m[1];
			if (numeric(val)) val = parseFloat(val);
		}

		return {
			type: 'field',
			name: name,
			value: coerce(val)
		}
	}
}
function parse ( str ) { str = '(' + str . trim ( ) + ')' ; /**
	 * expr
	 */ return expr ( ) ; /**
	 * Assert `expr`.
	 */ function error ( expr , msg ) { if ( expr ) return ; var ctx = str . slice ( 0 , 10 ) ; assert ( 0 , msg + ' near `' + ctx + '`' ) ; } /**
	 * '(' binop ')'
	 */ function expr ( ) { error ( '(' == str [ 0 ] , "missing opening '('" ) ; str = str . slice ( 1 ) ; var node = binop ( ) ; error ( ')' == str [ 0 ] , "missing closing ')'" ) ; str = str . slice ( 1 ) ; return node ; } /**
	 * field | expr
	 */ function primary ( ) { return field ( ) || expr ( ) ; } /**
	 * primary (OR|AND) binop
	 */ function binop ( ) { var left = primary ( ) ; var m = str . match ( / ^ *(OR|AND) * / i ) ; if ( ! m ) return left ; str = str . slice ( m [ 0 ] . length ) ; var right = binop ( ) ; return { type : 'op' , op : m [ 1 ] . toLowerCase ( ) , left : left , right : right } } /**
	 * FIELD[:VALUE]
	 */ function field ( ) { var val = true ; var m = str . match ( / ^([-.\w]+) / ) ; if ( ! m ) return ; var name = m [ 0 ] ; str = str . slice ( name . length ) ; var m = str . match ( / ^:([-*\w.]+|".*?"|'.*?'|\/(.*?)\/) * / ) ; if ( m ) { str = str . slice ( m [ 0 ] . length ) ; val = m [ 1 ] ; if ( numeric ( val ) ) val = parseFloat ( val ) ; } return { type : 'field' , name : name , value : coerce ( val ) } } }
Parse `str` to produce an AST.

@param {String} str
@return {Object}
@api public
Parse str to produce an AST .