####################################################################
#
#    This file was generated using Parse::Yapp version 1.05.
#
#        Don't edit this file, use source file instead.
#
#             ANY CHANGE MADE HERE WILL BE LOST !
#
####################################################################
package UR::BoolExpr::BxParser;
use vars qw ( @ISA );
use strict;

@ISA= qw ( UR::BoolExpr::BxParser::Yapp::Driver );
#Included Parse/Yapp/Driver.pm file----------------------------------------
{
#
# Module UR::BoolExpr::BxParser::Yapp::Driver
#
# This module is part of the Parse::Yapp package available on your
# nearest CPAN
#
# Any use of this module in a standalone parser make the included
# text under the same copyright as the Parse::Yapp module itself.
#
# This notice should remain unchanged.
#
# (c) Copyright 1998-2001 Francois Desarmenien, all rights reserved.
# (see the pod text in Parse::Yapp module for use and distribution rights)
#

package UR::BoolExpr::BxParser::Yapp::Driver;

require 5.004;

use strict;

use vars qw ( $VERSION $COMPATIBLE $FILENAME );

$VERSION = '1.05'; # No BumpVersion
$COMPATIBLE = '0.07';
$FILENAME=__FILE__;

use Carp;

#Known parameters, all starting with YY (leading YY will be discarded)
my(%params)=(YYLEX => 'CODE', 'YYERROR' => 'CODE', YYVERSION => '',
			 YYRULES => 'ARRAY', YYSTATES => 'ARRAY', YYDEBUG => '');
#Mandatory parameters
my(@params)=('LEX','RULES','STATES');

sub new {
    my($class)=shift;
	my($errst,$nberr,$token,$value,$check,$dotpos);
    my($self)={ ERROR => \&_Error,
				ERRST => \$errst,
                NBERR => \$nberr,
				TOKEN => \$token,
				VALUE => \$value,
				DOTPOS => \$dotpos,
				STACK => [],
				DEBUG => 0,
				CHECK => \$check };

	_CheckParams( [], \%params, \@_, $self );

		exists($$self{VERSION})
	and	$$self{VERSION} < $COMPATIBLE
	and	croak "Yapp driver version $VERSION ".
			  "incompatible with version $$self{VERSION}:\n".
			  "Please recompile parser module.";

        ref($class)
    and $class=ref($class);

    bless($self,$class);
}

sub YYParse {
    my($self)=shift;
    my($retval);

	_CheckParams( \@params, \%params, \@_, $self );

	if($$self{DEBUG}) {
		_DBLoad();
		$retval = eval '$self->_DBParse()';#Do not create stab entry on compile
        $@ and die $@;
	}
	else {
		$retval = $self->_Parse();
	}
    $retval
}

sub YYData {
	my($self)=shift;

		exists($$self{USER})
	or	$$self{USER}={};

	$$self{USER};
	
}

sub YYErrok {
	my($self)=shift;

	${$$self{ERRST}}=0;
    undef;
}

sub YYNberr {
	my($self)=shift;

	${$$self{NBERR}};
}

sub YYRecovering {
	my($self)=shift;

	${$$self{ERRST}} != 0;
}

sub YYAbort {
	my($self)=shift;

	${$$self{CHECK}}='ABORT';
    undef;
}

sub YYAccept {
	my($self)=shift;

	${$$self{CHECK}}='ACCEPT';
    undef;
}

sub YYError {
	my($self)=shift;

	${$$self{CHECK}}='ERROR';
    undef;
}

sub YYSemval {
	my($self)=shift;
	my($index)= $_[0] - ${$$self{DOTPOS}} - 1;

		$index < 0
	and	-$index <= @{$$self{STACK}}
	and	return $$self{STACK}[$index][1];

	undef;	#Invalid index
}

sub YYCurtok {
	my($self)=shift;

        @_
    and ${$$self{TOKEN}}=$_[0];
    ${$$self{TOKEN}};
}

sub YYCurval {
	my($self)=shift;

        @_
    and ${$$self{VALUE}}=$_[0];
    ${$$self{VALUE}};
}

sub YYExpect {
    my($self)=shift;

    keys %{$self->{STATES}[$self->{STACK}[-1][0]]{ACTIONS}}
}

sub YYLexer {
    my($self)=shift;

	$$self{LEX};
}


#################
# Private stuff #
#################


sub _CheckParams {
	my($mandatory,$checklist,$inarray,$outhash)=@_;
	my($prm,$value);
	my($prmlst)={};

	while(($prm,$value)=splice(@$inarray,0,2)) {
        $prm=uc($prm);
			exists($$checklist{$prm})
		or	croak("Unknow parameter '$prm'");
			ref($value) eq $$checklist{$prm}
		or	croak("Invalid value for parameter '$prm'");
        $prm=unpack('@2A*',$prm);
		$$outhash{$prm}=$value;
	}
	for (@$mandatory) {
			exists($$outhash{$_})
		or	croak("Missing mandatory parameter '".lc($_)."'");
	}
}

sub _Error {
	print "Parse error.\n";
}

sub _DBLoad {
	{
		no strict 'refs';

			exists(${__PACKAGE__.'::'}{_DBParse})#Already loaded ?
		and	return;
	}
	my($fname)=__FILE__;
	my(@drv);
	open(DRV,"<$fname") or die "Report this as a BUG: Cannot open $fname";
	while(<DRV>) {
                	/^\s*sub\s+_Parse\s*{\s*$/ .. /^\s*}\s*#\s*_Parse\s*$/
        	and     do {
                	s/^#DBG>//;
                	push(@drv,$_);
        	}
	}
	close(DRV);

	$drv[0]=~s/_P/_DBP/;
	eval join('',@drv);
}

#Note that for loading debugging version of the driver,
#this file will be parsed from 'sub _Parse' up to '}#_Parse' inclusive.
#So, DO NOT remove comment at end of sub !!!
sub _Parse {
    my($self)=shift;

	my($rules,$states,$lex,$error)
     = @$self{ 'RULES', 'STATES', 'LEX', 'ERROR' };
	my($errstatus,$nberror,$token,$value,$stack,$check,$dotpos)
     = @$self{ 'ERRST', 'NBERR', 'TOKEN', 'VALUE', 'STACK', 'CHECK', 'DOTPOS' };

#DBG>	my($debug)=$$self{DEBUG};
#DBG>	my($dbgerror)=0;

#DBG>	my($ShowCurToken) = sub {
#DBG>		my($tok)='>';
#DBG>		for (split('',$$token)) {
#DBG>			$tok.=		(ord($_) < 32 or ord($_) > 126)
#DBG>					?	sprintf('<%02X>',ord($_))
#DBG>					:	$_;
#DBG>		}
#DBG>		$tok.='<';
#DBG>	};

	$$errstatus=0;
	$$nberror=0;
	($$token,$$value)=(undef,undef);
	@$stack=( [ 0, undef ] );
	$$check='';

    while(1) {
        my($actions,$act,$stateno);

        $stateno=$$stack[-1][0];
        $actions=$$states[$stateno];

#DBG>	print STDERR ('-' x 40),"\n";
#DBG>		$debug & 0x2
#DBG>	and	print STDERR "In state $stateno:\n";
#DBG>		$debug & 0x08
#DBG>	and	print STDERR "Stack:[".
#DBG>					 join(',',map { $$_[0] } @$stack).
#DBG>					 "]\n";


        if  (exists($$actions{ACTIONS})) {

				defined($$token)
            or	do {
				($$token,$$value)=&$lex($self);
#DBG>				$debug & 0x01
#DBG>			and	print STDERR "Need token. Got ".&$ShowCurToken."\n";
			};

            $act=   exists($$actions{ACTIONS}{$$token})
                    ?   $$actions{ACTIONS}{$$token}
                    :   exists($$actions{DEFAULT})
                        ?   $$actions{DEFAULT}
                        :   undef;
        }
        else {
            $act=$$actions{DEFAULT};
#DBG>			$debug & 0x01
#DBG>		and	print STDERR "Don't need token.\n";
        }

            defined($act)
        and do {

                $act > 0
            and do {        #shift

#DBG>				$debug & 0x04
#DBG>			and	print STDERR "Shift and go to state $act.\n";

					$$errstatus
				and	do {
					--$$errstatus;

#DBG>					$debug & 0x10
#DBG>				and	$dbgerror
#DBG>				and	$$errstatus == 0
#DBG>				and	do {
#DBG>					print STDERR "**End of Error recovery.\n";
#DBG>					$dbgerror=0;
#DBG>				};
				};


                push(@$stack,[ $act, $$value ]);

					$$token ne ''	#Don't eat the eof
				and	$$token=$$value=undef;
                next;
            };

            #reduce
            my($lhs,$len,$code,@sempar,$semval);
            ($lhs,$len,$code)=@{$$rules[-$act]};

#DBG>			$debug & 0x04
#DBG>		and	$act
#DBG>		and	print STDERR "Reduce using rule ".-$act." ($lhs,$len): ";

                $act
            or  $self->YYAccept();

            $$dotpos=$len;

                unpack('A1',$lhs) eq '@'    #In line rule
            and do {
                    $lhs =~ /^\@[0-9]+\-([0-9]+)$/
                or  die "In line rule name '$lhs' ill formed: ".
                        "report it as a BUG.\n";
                $$dotpos = $1;
            };

            @sempar =       $$dotpos
                        ?   map { $$_[1] } @$stack[ -$$dotpos .. -1 ]
                        :   ();

            $semval = $code ? &$code( $self, @sempar )
                            : @sempar ? $sempar[0] : undef;

            splice(@$stack,-$len,$len);

                $$check eq 'ACCEPT'
            and do {

#DBG>			$debug & 0x04
#DBG>		and	print STDERR "Accept.\n";

				return($semval);
			};

                $$check eq 'ABORT'
            and	do {

#DBG>			$debug & 0x04
#DBG>		and	print STDERR "Abort.\n";

				return(undef);

			};

#DBG>			$debug & 0x04
#DBG>		and	print STDERR "Back to state $$stack[-1][0], then ";

                $$check eq 'ERROR'
            or  do {
#DBG>				$debug & 0x04
#DBG>			and	print STDERR 
#DBG>				    "go to state $$states[$$stack[-1][0]]{GOTOS}{$lhs}.\n";

#DBG>				$debug & 0x10
#DBG>			and	$dbgerror
#DBG>			and	$$errstatus == 0
#DBG>			and	do {
#DBG>				print STDERR "**End of Error recovery.\n";
#DBG>				$dbgerror=0;
#DBG>			};

			    push(@$stack,
                     [ $$states[$$stack[-1][0]]{GOTOS}{$lhs}, $semval ]);
                $$check='';
                next;
            };

#DBG>			$debug & 0x04
#DBG>		and	print STDERR "Forced Error recovery.\n";

            $$check='';

        };

        #Error
            $$errstatus
        or   do {

            $$errstatus = 1;
            &$error($self);
                $$errstatus # if 0, then YYErrok has been called
            or  next;       # so continue parsing

#DBG>			$debug & 0x10
#DBG>		and	do {
#DBG>			print STDERR "**Entering Error recovery.\n";
#DBG>			++$dbgerror;
#DBG>		};

            ++$$nberror;

        };

			$$errstatus == 3	#The next token is not valid: discard it
		and	do {
				$$token eq ''	# End of input: no hope
			and	do {
#DBG>				$debug & 0x10
#DBG>			and	print STDERR "**At eof: aborting.\n";
				return(undef);
			};

#DBG>			$debug & 0x10
#DBG>		and	print STDERR "**Dicard invalid token ".&$ShowCurToken.".\n";

			$$token=$$value=undef;
		};

        $$errstatus=3;

		while(	  @$stack
			  and (		not exists($$states[$$stack[-1][0]]{ACTIONS})
			        or  not exists($$states[$$stack[-1][0]]{ACTIONS}{error})
					or	$$states[$$stack[-1][0]]{ACTIONS}{error} <= 0)) {

#DBG>			$debug & 0x10
#DBG>		and	print STDERR "**Pop state $$stack[-1][0].\n";

			pop(@$stack);
		}

			@$stack
		or	do {

#DBG>			$debug & 0x10
#DBG>		and	print STDERR "**No state left on stack: aborting.\n";

			return(undef);
		};

		#shift the error token

#DBG>			$debug & 0x10
#DBG>		and	print STDERR "**Shift \$error token and go to state ".
#DBG>						 $$states[$$stack[-1][0]]{ACTIONS}{error}.
#DBG>						 ".\n";

		push(@$stack, [ $$states[$$stack[-1][0]]{ACTIONS}{error}, undef ]);

    }

    #never reached
	croak("Error in driver logic. Please, report it as a BUG");

}#_Parse
#DO NOT remove comment

1;

}
#End of include--------------------------------------------------




sub new {
        my($class)=shift;
        ref($class)
    and $class=ref($class);

    my($self)=$class->SUPER::new( yyversion => '1.05',
                                  yystates =>
[
	{#State 0
		ACTIONS => {
			'LEFT_PAREN' => 4,
			'BETWEEN_WORD' => 11,
			'ASC_WORD' => 1,
			'DESC_WORD' => 2,
			'IDENTIFIER' => 3,
			'NOT_WORD' => 6,
			'LIKE_WORD' => 8,
			'FALSE_WORD' => 9,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13
		},
		DEFAULT => -1,
		GOTOS => {
			'boolexpr' => 5,
			'expr' => 10,
			'keyword_as_value' => 7,
			'property' => 15,
			'condition' => 14
		}
	},
	{#State 1
		DEFAULT => -66
	},
	{#State 2
		DEFAULT => -65
	},
	{#State 3
		DEFAULT => -29
	},
	{#State 4
		ACTIONS => {
			'ASC_WORD' => 1,
			'LEFT_PAREN' => 4,
			'IDENTIFIER' => 3,
			'DESC_WORD' => 2,
			'NOT_WORD' => 6,
			'LIKE_WORD' => 8,
			'FALSE_WORD' => 9,
			'BETWEEN_WORD' => 11,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13
		},
		GOTOS => {
			'expr' => 16,
			'keyword_as_value' => 7,
			'condition' => 14,
			'property' => 15
		}
	},
	{#State 5
		ACTIONS => {
			'' => 17,
			'OFFSET' => 18,
			'ORDER_BY' => 20,
			'GROUP_BY' => 19,
			'LIMIT' => 21
		}
	},
	{#State 6
		DEFAULT => -64
	},
	{#State 7
		DEFAULT => -30
	},
	{#State 8
		DEFAULT => -62
	},
	{#State 9
		DEFAULT => -68
	},
	{#State 10
		ACTIONS => {
			'AND' => 22,
			'OR' => 23
		},
		DEFAULT => -2
	},
	{#State 11
		DEFAULT => -63
	},
	{#State 12
		DEFAULT => -67
	},
	{#State 13
		DEFAULT => -61
	},
	{#State 14
		DEFAULT => -7
	},
	{#State 15
		ACTIONS => {
			'DOUBLEEQUAL_SIGN' => 24,
			'NOT_BANG' => 33,
			'NOT_WORD' => 34,
			'COLON' => 25,
			'LIKE_WORD' => 35,
			'OPERATORS' => 26,
			'EQUAL_SIGN' => 36,
			'FALSE_WORD' => 37,
			'BETWEEN_WORD' => 29,
			'TILDE' => 38,
			'TRUE_WORD' => 40,
			'IN_WORD' => 41,
			'IS_NOT_NULL' => 44,
			'IS_NULL' => 32
		},
		GOTOS => {
			'operator' => 28,
			'like_operator' => 30,
			'an_operator' => 31,
			'in_operator' => 39,
			'null_op_word' => 27,
			'boolean_op_word' => 43,
			'between_operator' => 42,
			'negation' => 45
		}
	},
	{#State 16
		ACTIONS => {
			'AND' => 22,
			'OR' => 23,
			'RIGHT_PAREN' => 46
		}
	},
	{#State 17
		DEFAULT => 0
	},
	{#State 18
		ACTIONS => {
			'INTEGER' => 47
		}
	},
	{#State 19
		ACTIONS => {
			'ASC_WORD' => 1,
			'IDENTIFIER' => 3,
			'DESC_WORD' => 2,
			'NOT_WORD' => 6,
			'LIKE_WORD' => 8,
			'FALSE_WORD' => 9,
			'BETWEEN_WORD' => 11,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13
		},
		GOTOS => {
			'keyword_as_value' => 7,
			'group_by_list' => 48,
			'property' => 49
		}
	},
	{#State 20
		ACTIONS => {
			'ASC_WORD' => 1,
			'IDENTIFIER' => 3,
			'DESC_WORD' => 2,
			'NOT_WORD' => 6,
			'MINUS' => 50,
			'LIKE_WORD' => 8,
			'FALSE_WORD' => 9,
			'BETWEEN_WORD' => 11,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13
		},
		GOTOS => {
			'order_by_list' => 53,
			'order_by_property' => 51,
			'keyword_as_value' => 7,
			'property' => 52
		}
	},
	{#State 21
		ACTIONS => {
			'INTEGER' => 54
		}
	},
	{#State 22
		ACTIONS => {
			'ASC_WORD' => 1,
			'IDENTIFIER' => 3,
			'DESC_WORD' => 2,
			'LEFT_PAREN' => 4,
			'NOT_WORD' => 6,
			'LIKE_WORD' => 8,
			'FALSE_WORD' => 9,
			'BETWEEN_WORD' => 11,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13
		},
		GOTOS => {
			'expr' => 55,
			'keyword_as_value' => 7,
			'condition' => 14,
			'property' => 15
		}
	},
	{#State 23
		ACTIONS => {
			'ASC_WORD' => 1,
			'IDENTIFIER' => 3,
			'DESC_WORD' => 2,
			'LEFT_PAREN' => 4,
			'NOT_WORD' => 6,
			'LIKE_WORD' => 8,
			'FALSE_WORD' => 9,
			'BETWEEN_WORD' => 11,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13
		},
		GOTOS => {
			'expr' => 56,
			'keyword_as_value' => 7,
			'condition' => 14,
			'property' => 15
		}
	},
	{#State 24
		DEFAULT => -45
	},
	{#State 25
		ACTIONS => {
			'WHITESPACE' => 58
		},
		DEFAULT => -27,
		GOTOS => {
			'optional_spaces' => 59,
			'spaces' => 57
		}
	},
	{#State 26
		DEFAULT => -43
	},
	{#State 27
		DEFAULT => -20
	},
	{#State 28
		ACTIONS => {
			'WHITESPACE' => 58
		},
		DEFAULT => -27,
		GOTOS => {
			'optional_spaces' => 60,
			'spaces' => 57
		}
	},
	{#State 29
		DEFAULT => -58
	},
	{#State 30
		ACTIONS => {
			'WORD' => 61,
			'DOUBLEQUOTE_STRING' => 62,
			'MINUS' => 63,
			'BETWEEN_WORD' => 11,
			'REAL' => 67,
			'INTEGER' => 68,
			'ASC_WORD' => 1,
			'IDENTIFIER' => 69,
			'DESC_WORD' => 2,
			'NOT_WORD' => 6,
			'LIKE_WORD' => 8,
			'AND' => 73,
			'FALSE_WORD' => 9,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13,
			'OR' => 74,
			'SINGLEQUOTE_STRING' => 75
		},
		GOTOS => {
			'number' => 64,
			'value' => 71,
			'keyword_as_value' => 70,
			'single_value' => 72,
			'like_value' => 66,
			'subsequent_value_part' => 65
		}
	},
	{#State 31
		DEFAULT => -39
	},
	{#State 32
		DEFAULT => -23
	},
	{#State 33
		DEFAULT => -42
	},
	{#State 34
		DEFAULT => -41
	},
	{#State 35
		DEFAULT => -46
	},
	{#State 36
		DEFAULT => -44
	},
	{#State 37
		DEFAULT => -22
	},
	{#State 38
		DEFAULT => -48
	},
	{#State 39
		ACTIONS => {
			'LEFT_BRACKET' => 76
		},
		GOTOS => {
			'set' => 77
		}
	},
	{#State 40
		DEFAULT => -21
	},
	{#State 41
		DEFAULT => -51
	},
	{#State 42
		ACTIONS => {
			'WORD' => 61,
			'DOUBLEQUOTE_STRING' => 62,
			'MINUS' => 63,
			'BETWEEN_WORD' => 11,
			'REAL' => 67,
			'INTEGER' => 68,
			'ASC_WORD' => 1,
			'IDENTIFIER' => 69,
			'DESC_WORD' => 2,
			'NOT_WORD' => 6,
			'LIKE_WORD' => 8,
			'AND' => 73,
			'FALSE_WORD' => 9,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13,
			'OR' => 74,
			'SINGLEQUOTE_STRING' => 75
		},
		GOTOS => {
			'between_value' => 78,
			'number' => 64,
			'keyword_as_value' => 70,
			'single_value' => 79,
			'subsequent_value_part' => 65
		}
	},
	{#State 43
		DEFAULT => -19
	},
	{#State 44
		DEFAULT => -25
	},
	{#State 45
		ACTIONS => {
			'DOUBLEEQUAL_SIGN' => 24,
			'COLON' => 80,
			'LIKE_WORD' => 84,
			'OPERATORS' => 26,
			'EQUAL_SIGN' => 36,
			'BETWEEN_WORD' => 81,
			'TILDE' => 85,
			'IN_WORD' => 86,
			'IS_NULL' => 83
		},
		GOTOS => {
			'an_operator' => 82
		}
	},
	{#State 46
		DEFAULT => -10
	},
	{#State 47
		DEFAULT => -6
	},
	{#State 48
		DEFAULT => -4
	},
	{#State 49
		ACTIONS => {
			'AND' => 87
		},
		DEFAULT => -37
	},
	{#State 50
		ACTIONS => {
			'ASC_WORD' => 1,
			'IDENTIFIER' => 3,
			'DESC_WORD' => 2,
			'NOT_WORD' => 6,
			'LIKE_WORD' => 8,
			'FALSE_WORD' => 9,
			'BETWEEN_WORD' => 11,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13
		},
		GOTOS => {
			'keyword_as_value' => 7,
			'property' => 88
		}
	},
	{#State 51
		ACTIONS => {
			'AND' => 89
		},
		DEFAULT => -35
	},
	{#State 52
		ACTIONS => {
			'ASC_WORD' => 90,
			'DESC_WORD' => 91
		},
		DEFAULT => -31
	},
	{#State 53
		DEFAULT => -3
	},
	{#State 54
		DEFAULT => -5
	},
	{#State 55
		ACTIONS => {
			'AND' => 22
		},
		DEFAULT => -8
	},
	{#State 56
		ACTIONS => {
			'AND' => 22,
			'OR' => 23
		},
		DEFAULT => -9
	},
	{#State 57
		DEFAULT => -28
	},
	{#State 58
		DEFAULT => -26
	},
	{#State 59
		ACTIONS => {
			'WORD' => 61,
			'DOUBLEQUOTE_STRING' => 62,
			'MINUS' => 63,
			'BETWEEN_WORD' => 11,
			'REAL' => 67,
			'INTEGER' => 68,
			'ASC_WORD' => 1,
			'IDENTIFIER' => 69,
			'DESC_WORD' => 2,
			'NOT_WORD' => 6,
			'LIKE_WORD' => 8,
			'AND' => 73,
			'FALSE_WORD' => 9,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13,
			'OR' => 74,
			'SINGLEQUOTE_STRING' => 75
		},
		GOTOS => {
			'between_value' => 93,
			'number' => 64,
			'keyword_as_value' => 70,
			'old_syntax_in_value' => 92,
			'single_value' => 94,
			'subsequent_value_part' => 65
		}
	},
	{#State 60
		ACTIONS => {
			'WORD' => 61,
			'DOUBLEQUOTE_STRING' => 62,
			'MINUS' => 63,
			'BETWEEN_WORD' => 11,
			'REAL' => 67,
			'INTEGER' => 68,
			'ASC_WORD' => 1,
			'IDENTIFIER' => 69,
			'DESC_WORD' => 2,
			'NOT_WORD' => 6,
			'LIKE_WORD' => 8,
			'AND' => 73,
			'FALSE_WORD' => 9,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13,
			'OR' => 74,
			'SINGLEQUOTE_STRING' => 75
		},
		GOTOS => {
			'number' => 64,
			'value' => 95,
			'keyword_as_value' => 70,
			'single_value' => 72,
			'subsequent_value_part' => 65
		}
	},
	{#State 61
		DEFAULT => -73
	},
	{#State 62
		DEFAULT => -74
	},
	{#State 63
		ACTIONS => {
			'INTEGER' => 97,
			'REAL' => 96
		}
	},
	{#State 64
		DEFAULT => -72
	},
	{#State 65
		DEFAULT => -81
	},
	{#State 66
		DEFAULT => -12
	},
	{#State 67
		DEFAULT => -85
	},
	{#State 68
		DEFAULT => -84
	},
	{#State 69
		DEFAULT => -71
	},
	{#State 70
		DEFAULT => -76
	},
	{#State 71
		DEFAULT => -50
	},
	{#State 72
		ACTIONS => {
			'WORD' => 61,
			'DOUBLEQUOTE_STRING' => 62,
			'MINUS' => 63,
			'BETWEEN_WORD' => 11,
			'REAL' => 67,
			'INTEGER' => 68,
			'ASC_WORD' => 1,
			'IDENTIFIER' => 69,
			'DESC_WORD' => 2,
			'NOT_WORD' => 6,
			'LIKE_WORD' => 8,
			'WHITESPACE' => 58,
			'FALSE_WORD' => 9,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13,
			'SINGLEQUOTE_STRING' => 75
		},
		DEFAULT => -70,
		GOTOS => {
			'number' => 64,
			'keyword_as_value' => 70,
			'subsequent_values_list' => 100,
			'spaces' => 99,
			'subsequent_value_part' => 98
		}
	},
	{#State 73
		DEFAULT => -82
	},
	{#State 74
		DEFAULT => -83
	},
	{#State 75
		DEFAULT => -75
	},
	{#State 76
		ACTIONS => {
			'WORD' => 61,
			'DOUBLEQUOTE_STRING' => 62,
			'MINUS' => 63,
			'BETWEEN_WORD' => 11,
			'REAL' => 67,
			'INTEGER' => 68,
			'ASC_WORD' => 1,
			'IDENTIFIER' => 69,
			'DESC_WORD' => 2,
			'NOT_WORD' => 6,
			'LIKE_WORD' => 8,
			'AND' => 73,
			'FALSE_WORD' => 9,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13,
			'OR' => 74,
			'SINGLEQUOTE_STRING' => 75
		},
		GOTOS => {
			'set_body' => 101,
			'number' => 64,
			'value' => 102,
			'keyword_as_value' => 70,
			'single_value' => 72,
			'subsequent_value_part' => 65
		}
	},
	{#State 77
		DEFAULT => -13
	},
	{#State 78
		DEFAULT => -16
	},
	{#State 79
		ACTIONS => {
			'MINUS' => 103
		}
	},
	{#State 80
		ACTIONS => {
			'WHITESPACE' => 58
		},
		DEFAULT => -27,
		GOTOS => {
			'optional_spaces' => 104,
			'spaces' => 57
		}
	},
	{#State 81
		DEFAULT => -59
	},
	{#State 82
		DEFAULT => -40
	},
	{#State 83
		DEFAULT => -24
	},
	{#State 84
		DEFAULT => -47
	},
	{#State 85
		DEFAULT => -49
	},
	{#State 86
		DEFAULT => -52
	},
	{#State 87
		ACTIONS => {
			'ASC_WORD' => 1,
			'IDENTIFIER' => 3,
			'DESC_WORD' => 2,
			'NOT_WORD' => 6,
			'LIKE_WORD' => 8,
			'FALSE_WORD' => 9,
			'BETWEEN_WORD' => 11,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13
		},
		GOTOS => {
			'keyword_as_value' => 7,
			'group_by_list' => 105,
			'property' => 49
		}
	},
	{#State 88
		DEFAULT => -32
	},
	{#State 89
		ACTIONS => {
			'ASC_WORD' => 1,
			'IDENTIFIER' => 3,
			'DESC_WORD' => 2,
			'NOT_WORD' => 6,
			'MINUS' => 50,
			'LIKE_WORD' => 8,
			'FALSE_WORD' => 9,
			'BETWEEN_WORD' => 11,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13
		},
		GOTOS => {
			'order_by_list' => 106,
			'order_by_property' => 51,
			'keyword_as_value' => 7,
			'property' => 52
		}
	},
	{#State 90
		DEFAULT => -34
	},
	{#State 91
		DEFAULT => -33
	},
	{#State 92
		DEFAULT => -14
	},
	{#State 93
		DEFAULT => -17
	},
	{#State 94
		ACTIONS => {
			'IN_DIVIDER' => 107,
			'MINUS' => 103
		}
	},
	{#State 95
		DEFAULT => -11
	},
	{#State 96
		DEFAULT => -87
	},
	{#State 97
		DEFAULT => -86
	},
	{#State 98
		ACTIONS => {
			'WORD' => 61,
			'DOUBLEQUOTE_STRING' => 62,
			'MINUS' => 63,
			'BETWEEN_WORD' => 11,
			'REAL' => 67,
			'INTEGER' => 68,
			'ASC_WORD' => 1,
			'IDENTIFIER' => 69,
			'DESC_WORD' => 2,
			'NOT_WORD' => 6,
			'LIKE_WORD' => 8,
			'WHITESPACE' => 58,
			'FALSE_WORD' => 9,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13,
			'SINGLEQUOTE_STRING' => 75
		},
		DEFAULT => -77,
		GOTOS => {
			'number' => 64,
			'keyword_as_value' => 70,
			'subsequent_values_list' => 108,
			'spaces' => 99,
			'subsequent_value_part' => 98
		}
	},
	{#State 99
		ACTIONS => {
			'WORD' => 61,
			'DOUBLEQUOTE_STRING' => 62,
			'MINUS' => 63,
			'BETWEEN_WORD' => 11,
			'REAL' => 67,
			'INTEGER' => 68,
			'ASC_WORD' => 1,
			'IDENTIFIER' => 69,
			'DESC_WORD' => 2,
			'NOT_WORD' => 6,
			'LIKE_WORD' => 8,
			'WHITESPACE' => 58,
			'FALSE_WORD' => 9,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13,
			'SINGLEQUOTE_STRING' => 75
		},
		DEFAULT => -80,
		GOTOS => {
			'number' => 64,
			'keyword_as_value' => 70,
			'subsequent_values_list' => 109,
			'spaces' => 99,
			'subsequent_value_part' => 98
		}
	},
	{#State 100
		DEFAULT => -69
	},
	{#State 101
		ACTIONS => {
			'RIGHT_BRACKET' => 110
		}
	},
	{#State 102
		ACTIONS => {
			'SET_SEPARATOR' => 111
		},
		DEFAULT => -57
	},
	{#State 103
		ACTIONS => {
			'WORD' => 61,
			'DOUBLEQUOTE_STRING' => 62,
			'MINUS' => 63,
			'BETWEEN_WORD' => 11,
			'REAL' => 67,
			'INTEGER' => 68,
			'ASC_WORD' => 1,
			'IDENTIFIER' => 69,
			'DESC_WORD' => 2,
			'NOT_WORD' => 6,
			'LIKE_WORD' => 8,
			'AND' => 73,
			'FALSE_WORD' => 9,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13,
			'OR' => 74,
			'SINGLEQUOTE_STRING' => 75
		},
		GOTOS => {
			'number' => 64,
			'keyword_as_value' => 70,
			'single_value' => 112,
			'subsequent_value_part' => 65
		}
	},
	{#State 104
		ACTIONS => {
			'WORD' => 61,
			'DOUBLEQUOTE_STRING' => 62,
			'MINUS' => 63,
			'BETWEEN_WORD' => 11,
			'REAL' => 67,
			'INTEGER' => 68,
			'ASC_WORD' => 1,
			'IDENTIFIER' => 69,
			'DESC_WORD' => 2,
			'NOT_WORD' => 6,
			'LIKE_WORD' => 8,
			'AND' => 73,
			'FALSE_WORD' => 9,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13,
			'OR' => 74,
			'SINGLEQUOTE_STRING' => 75
		},
		GOTOS => {
			'between_value' => 114,
			'number' => 64,
			'keyword_as_value' => 70,
			'old_syntax_in_value' => 113,
			'single_value' => 94,
			'subsequent_value_part' => 65
		}
	},
	{#State 105
		DEFAULT => -38
	},
	{#State 106
		DEFAULT => -36
	},
	{#State 107
		ACTIONS => {
			'WORD' => 61,
			'DOUBLEQUOTE_STRING' => 62,
			'MINUS' => 63,
			'BETWEEN_WORD' => 11,
			'REAL' => 67,
			'INTEGER' => 68,
			'ASC_WORD' => 1,
			'IDENTIFIER' => 69,
			'DESC_WORD' => 2,
			'NOT_WORD' => 6,
			'LIKE_WORD' => 8,
			'AND' => 73,
			'FALSE_WORD' => 9,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13,
			'OR' => 74,
			'SINGLEQUOTE_STRING' => 75
		},
		GOTOS => {
			'number' => 64,
			'keyword_as_value' => 70,
			'old_syntax_in_value' => 115,
			'single_value' => 116,
			'subsequent_value_part' => 65
		}
	},
	{#State 108
		DEFAULT => -78
	},
	{#State 109
		DEFAULT => -79
	},
	{#State 110
		DEFAULT => -55
	},
	{#State 111
		ACTIONS => {
			'WORD' => 61,
			'DOUBLEQUOTE_STRING' => 62,
			'MINUS' => 63,
			'BETWEEN_WORD' => 11,
			'REAL' => 67,
			'INTEGER' => 68,
			'ASC_WORD' => 1,
			'IDENTIFIER' => 69,
			'DESC_WORD' => 2,
			'NOT_WORD' => 6,
			'LIKE_WORD' => 8,
			'AND' => 73,
			'FALSE_WORD' => 9,
			'TRUE_WORD' => 12,
			'IN_WORD' => 13,
			'OR' => 74,
			'SINGLEQUOTE_STRING' => 75
		},
		GOTOS => {
			'set_body' => 117,
			'number' => 64,
			'value' => 102,
			'keyword_as_value' => 70,
			'single_value' => 72,
			'subsequent_value_part' => 65
		}
	},
	{#State 112
		DEFAULT => -60
	},
	{#State 113
		DEFAULT => -15
	},
	{#State 114
		DEFAULT => -18
	},
	{#State 115
		DEFAULT => -53
	},
	{#State 116
		ACTIONS => {
			'IN_DIVIDER' => 107
		},
		DEFAULT => -54
	},
	{#State 117
		DEFAULT => -56
	}
],
                                  yyrules  =>
[
	[#Rule 0
		 '$start', 2, undef
	],
	[#Rule 1
		 'boolexpr', 0,
sub
#line 10 "BxParser.yp"
{ [] }
	],
	[#Rule 2
		 'boolexpr', 1,
sub
#line 11 "BxParser.yp"
{ UR::BoolExpr::BxParser->_simplify($_[1]) }
	],
	[#Rule 3
		 'boolexpr', 3,
sub
#line 12 "BxParser.yp"
{ [@{$_[1]}, '-order', $_[3]] }
	],
	[#Rule 4
		 'boolexpr', 3,
sub
#line 13 "BxParser.yp"
{ [@{$_[1]}, '-group', $_[3]] }
	],
	[#Rule 5
		 'boolexpr', 3,
sub
#line 14 "BxParser.yp"
{ [@{$_[1]}, '-limit', $_[3]] }
	],
	[#Rule 6
		 'boolexpr', 3,
sub
#line 15 "BxParser.yp"
{ [@{$_[1]}, '-offset', $_[3]] }
	],
	[#Rule 7
		 'expr', 1,
sub
#line 18 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 8
		 'expr', 3,
sub
#line 19 "BxParser.yp"
{ UR::BoolExpr::BxParser->_and($_[1], $_[3]) }
	],
	[#Rule 9
		 'expr', 3,
sub
#line 20 "BxParser.yp"
{ UR::BoolExpr::BxParser->_or($_[1], $_[3]) }
	],
	[#Rule 10
		 'expr', 3,
sub
#line 21 "BxParser.yp"
{ $_[2] }
	],
	[#Rule 11
		 'condition', 4,
sub
#line 24 "BxParser.yp"
{ [ "$_[1] $_[2]" => $_[4] ] }
	],
	[#Rule 12
		 'condition', 3,
sub
#line 25 "BxParser.yp"
{ [ "$_[1] $_[2]" => $_[3] ] }
	],
	[#Rule 13
		 'condition', 3,
sub
#line 26 "BxParser.yp"
{ [ "$_[1] $_[2]" => $_[3] ] }
	],
	[#Rule 14
		 'condition', 4,
sub
#line 27 "BxParser.yp"
{ [ "$_[1] in" => $_[4] ] }
	],
	[#Rule 15
		 'condition', 5,
sub
#line 28 "BxParser.yp"
{ [ "$_[1] $_[2] in" => $_[5] ] }
	],
	[#Rule 16
		 'condition', 3,
sub
#line 29 "BxParser.yp"
{ [ "$_[1] $_[2]" => $_[3] ] }
	],
	[#Rule 17
		 'condition', 4,
sub
#line 30 "BxParser.yp"
{ [ "$_[1] between" => $_[4] ] }
	],
	[#Rule 18
		 'condition', 5,
sub
#line 31 "BxParser.yp"
{ [ "$_[1] $_[2] between" => $_[5] ] }
	],
	[#Rule 19
		 'condition', 2,
sub
#line 32 "BxParser.yp"
{ [ "$_[1] $_[2]" => 1 ] }
	],
	[#Rule 20
		 'condition', 2,
sub
#line 33 "BxParser.yp"
{ [ "$_[1] $_[2]" => undef ] }
	],
	[#Rule 21
		 'boolean_op_word', 1,
sub
#line 36 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 22
		 'boolean_op_word', 1,
sub
#line 37 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 23
		 'null_op_word', 1,
sub
#line 40 "BxParser.yp"
{ '=' }
	],
	[#Rule 24
		 'null_op_word', 2,
sub
#line 41 "BxParser.yp"
{ "!=" }
	],
	[#Rule 25
		 'null_op_word', 1,
sub
#line 42 "BxParser.yp"
{ '!=' }
	],
	[#Rule 26
		 'spaces', 1,
sub
#line 45 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 27
		 'optional_spaces', 0,
sub
#line 48 "BxParser.yp"
{ undef }
	],
	[#Rule 28
		 'optional_spaces', 1,
sub
#line 49 "BxParser.yp"
{ undef }
	],
	[#Rule 29
		 'property', 1,
sub
#line 52 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 30
		 'property', 1,
sub
#line 53 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 31
		 'order_by_property', 1,
sub
#line 56 "BxParser.yp"
{ $_[1 ] }
	],
	[#Rule 32
		 'order_by_property', 2,
sub
#line 57 "BxParser.yp"
{ '-'.$_[2] }
	],
	[#Rule 33
		 'order_by_property', 2,
sub
#line 58 "BxParser.yp"
{ '-'.$_[1] }
	],
	[#Rule 34
		 'order_by_property', 2,
sub
#line 59 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 35
		 'order_by_list', 1,
sub
#line 62 "BxParser.yp"
{ [ $_[1]] }
	],
	[#Rule 36
		 'order_by_list', 3,
sub
#line 63 "BxParser.yp"
{ [$_[1], @{$_[3]}] }
	],
	[#Rule 37
		 'group_by_list', 1,
sub
#line 66 "BxParser.yp"
{ [ $_[1] ] }
	],
	[#Rule 38
		 'group_by_list', 3,
sub
#line 67 "BxParser.yp"
{ [$_[1], @{$_[3]}] }
	],
	[#Rule 39
		 'operator', 1,
sub
#line 70 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 40
		 'operator', 2,
sub
#line 71 "BxParser.yp"
{ "$_[1] $_[2]" }
	],
	[#Rule 41
		 'negation', 1,
sub
#line 74 "BxParser.yp"
{ 'not' }
	],
	[#Rule 42
		 'negation', 1,
sub
#line 75 "BxParser.yp"
{ 'not' }
	],
	[#Rule 43
		 'an_operator', 1,
sub
#line 78 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 44
		 'an_operator', 1,
sub
#line 79 "BxParser.yp"
{ '=' }
	],
	[#Rule 45
		 'an_operator', 1,
sub
#line 80 "BxParser.yp"
{ '=' }
	],
	[#Rule 46
		 'like_operator', 1,
sub
#line 83 "BxParser.yp"
{ 'like' }
	],
	[#Rule 47
		 'like_operator', 2,
sub
#line 84 "BxParser.yp"
{ "$_[1] like" }
	],
	[#Rule 48
		 'like_operator', 1,
sub
#line 85 "BxParser.yp"
{ 'like' }
	],
	[#Rule 49
		 'like_operator', 2,
sub
#line 86 "BxParser.yp"
{ "$_[1] like" }
	],
	[#Rule 50
		 'like_value', 1,
sub
#line 89 "BxParser.yp"
{  $_[1] =~ m/\%/ ? $_[1] : '%' . $_[1] . '%' }
	],
	[#Rule 51
		 'in_operator', 1,
sub
#line 92 "BxParser.yp"
{ 'in' }
	],
	[#Rule 52
		 'in_operator', 2,
sub
#line 93 "BxParser.yp"
{ "$_[1] in" }
	],
	[#Rule 53
		 'old_syntax_in_value', 3,
sub
#line 96 "BxParser.yp"
{ [ $_[1], @{$_[3]} ] }
	],
	[#Rule 54
		 'old_syntax_in_value', 3,
sub
#line 97 "BxParser.yp"
{ [ $_[1], $_[3] ] }
	],
	[#Rule 55
		 'set', 3,
sub
#line 100 "BxParser.yp"
{ $_[2] }
	],
	[#Rule 56
		 'set_body', 3,
sub
#line 103 "BxParser.yp"
{ [ $_[1], @{$_[3]} ] }
	],
	[#Rule 57
		 'set_body', 1,
sub
#line 104 "BxParser.yp"
{ [ $_[1] ] }
	],
	[#Rule 58
		 'between_operator', 1,
sub
#line 107 "BxParser.yp"
{ 'between' }
	],
	[#Rule 59
		 'between_operator', 2,
sub
#line 108 "BxParser.yp"
{ "$_[1] between" }
	],
	[#Rule 60
		 'between_value', 3,
sub
#line 111 "BxParser.yp"
{ [ $_[1], $_[3] ] }
	],
	[#Rule 61
		 'keyword_as_value', 1,
sub
#line 114 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 62
		 'keyword_as_value', 1,
sub
#line 115 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 63
		 'keyword_as_value', 1,
sub
#line 116 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 64
		 'keyword_as_value', 1,
sub
#line 117 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 65
		 'keyword_as_value', 1,
sub
#line 118 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 66
		 'keyword_as_value', 1,
sub
#line 119 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 67
		 'keyword_as_value', 1,
sub
#line 120 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 68
		 'keyword_as_value', 1,
sub
#line 121 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 69
		 'value', 2,
sub
#line 124 "BxParser.yp"
{ $_[1].$_[2] }
	],
	[#Rule 70
		 'value', 1,
sub
#line 125 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 71
		 'subsequent_value_part', 1,
sub
#line 128 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 72
		 'subsequent_value_part', 1,
sub
#line 129 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 73
		 'subsequent_value_part', 1,
sub
#line 130 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 74
		 'subsequent_value_part', 1,
sub
#line 131 "BxParser.yp"
{ ($_[1] =~ m/^"(.*?)"$/)[0]; }
	],
	[#Rule 75
		 'subsequent_value_part', 1,
sub
#line 132 "BxParser.yp"
{ ($_[1] =~ m/^'(.*?)'$/)[0]; }
	],
	[#Rule 76
		 'subsequent_value_part', 1,
sub
#line 133 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 77
		 'subsequent_values_list', 1,
sub
#line 136 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 78
		 'subsequent_values_list', 2,
sub
#line 137 "BxParser.yp"
{ $_[1].$_[2] }
	],
	[#Rule 79
		 'subsequent_values_list', 2,
sub
#line 138 "BxParser.yp"
{ $_[1].$_[2] }
	],
	[#Rule 80
		 'subsequent_values_list', 1,
sub
#line 139 "BxParser.yp"
{ '' }
	],
	[#Rule 81
		 'single_value', 1,
sub
#line 142 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 82
		 'single_value', 1,
sub
#line 143 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 83
		 'single_value', 1,
sub
#line 144 "BxParser.yp"
{ $_[1] }
	],
	[#Rule 84
		 'number', 1,
sub
#line 148 "BxParser.yp"
{ $_[1] + 0 }
	],
	[#Rule 85
		 'number', 1,
sub
#line 149 "BxParser.yp"
{ $_[1] + 0 }
	],
	[#Rule 86
		 'number', 2,
sub
#line 150 "BxParser.yp"
{ 0 - $_[2] }
	],
	[#Rule 87
		 'number', 2,
sub
#line 151 "BxParser.yp"
{ 0 - $_[2] }
	]
],
                                  @_);
    bless($self,$class);
}

#line 154 "BxParser.yp"


package UR::BoolExpr::BxParser;
use strict;
use warnings;

sub _error {
    my @expect = $_[0]->YYExpect;
    my $tok = $_[0]->YYData->{INPUT};
    my $match = $_[0]->YYData->{MATCH};
    my $string = $_[0]->YYData->{STRING};
    my $err = qq(Can't parse expression "$string"\n  Syntax error near token $tok '$match');
    my $rem = $_[0]->YYData->{REMAINING};
    $err .= ", remaining text: '$rem'" if $rem;
    $err .= "\nExpected one of: " . join(", ", @expect) . "\n";
    Carp::croak($err);
}

my %token_states = (
    'DEFAULT' => [
        WHITESPACE => qr{\s+},
        AND => [ qr{and}i, 'DEFAULT'],
        OR => [ qr{or}i, 'DEFAULT' ],
        BETWEEN_WORD => qr{between},
        LIKE_WORD => qr{like},
        IN_WORD => qr{in},
        NOT_WORD => qr{not},
        DESC_WORD => qr{desc},
        ASC_WORD => qr{asc},
        TRUE_WORD => qr{true},
        FALSE_WORD => qr{false},
        LIMIT => qr{limit},
        OFFSET => qr{offset},
        IDENTIFIER => qr{[a-zA-Z_][a-zA-Z0-9_.]*},
        MINUS => qr{-},
        INTEGER => qr{\d+},
        REAL => qr{\d*\.\d+|\d+\.\d*},
        WORD => [ qr{[%\+\.\/\w][\+\-\.%\w\/]*},   # also allow / for pathnames, - for hyphenated names, % for like wildcards
                    'dont_gobble_spaces' ],
        DOUBLEQUOTE_STRING => qr{"(?:\\.|[^"])*"},
        SINGLEQUOTE_STRING => qr{'(?:\\.|[^'])*'},
        LEFT_PAREN => [ qr{\(}, 'DEFAULT' ],
        RIGHT_PAREN => [ qr{\)}, 'DEFAULT' ],
        LEFT_BRACKET => [ qr{\[}, 'set_contents'],
        RIGHT_BRACKET => [qr{\]}, 'DEFAULT' ],
        NOT_BANG => qr{!},
        EQUAL_SIGN => [ qr{=}, 'dont_gobble_spaces' ],
        DOUBLEEQUAL_SIGN => [ qr{=>}, 'dont_gobble_spaces' ],
        OPERATORS => [ qr{<=|>=|<|>}, 'dont_gobble_spaces' ],
        AND => [ qr{,}, 'DEFAULT' ],
        COLON => [ qr{:}, 'after_colon_value' ],
        TILDE => qr{~},
        ORDER_BY => qr{order by},
        GROUP_BY => qr{group by},
        IS_NULL => qr{is null|is undef},
        IS_NOT_NULL => qr{is not null|is not undef},
    ],
    'set_contents' => [
        SET_SEPARATOR => qr{,},  # Depending on state, can be either AND or SET_SEPARATOR
        WORD => qr{[%\+\.\w\:][\+\.\:%\w]*},   # also allow / for pathnames, - for hyphenated names, % for like wildcards
        RIGHT_BRACKET => [qr{\]}, 'DEFAULT' ],
    ],
    'after_colon_value' => [
        INTEGER => qr{\d+},
        REAL => qr{\d*\.\d+|\d+\.\d*},
        IN_DIVIDER => qr{\/},
        #WORD => qr{\w+},    # Override WORD in DEFAULT to disallow /
        WORD => qr{[%\+\.\w\:][\+\.\:%\w]*},   # Override WORD in DEFAULT to disallow /
        DOUBLEQUOTE_STRING => qr{"(?:\\.|[^"])*"},
        SINGLEQUOTE_STRING => qr{'(?:\\.|[^'])*'},
        WHITESPACE => [qr{\s+}, 'DEFAULT'],
    ],
    'dont_gobble_spaces' => [
        AND => [ qr{and}, 'DEFAULT'],
        OR => [ qr{or}, 'DEFAULT' ],
        LIMIT => [qr{limit}, 'DEFAULT'],
        OFFSET => [qr{offset}, 'DEFAULT'],
        INTEGER => qr{\d+},
        REAL => qr{\d*\.\d+|\d+\.\d*},
        WORD => qr{[%\+\.\/\w][\+\-\.\:%\w\/]*},   # also allow / for pathnames, - for hyphenated names, % for like wildcards
        ORDER_BY => [qr{order by}, 'DEFAULT'],
        GROUP_BY => [qr{group by}, 'DEFAULT'],
    ],
);

sub parse {
    my $string = shift;
    my %params = @_;

    my $debug = $params{'tokdebug'};
    my $yydebug = $params{'yydebug'} || 0;

    print "\nStarting parse for string $string\n" if $debug;
    my $parser = UR::BoolExpr::BxParser->new();
    $parser->YYData->{STRING} = $string;

    my $parser_state = 'DEFAULT';

    my $get_next_token = sub {
        if (length($string) == 0) {
            print "String is empty, we're done!\n" if $debug;
            return (undef, '');  
       }

        GET_NEXT_TOKEN:
        foreach (1) {
            my $longest = 0;
            my $longest_token = '';
            my $longest_match = '';

            for my $token_list ( $parser_state, 'DEFAULT' ) {
                print "\nTrying tokens for state $token_list...\n" if $debug;
                my $tokens = $token_states{$token_list};
                for(my $i = 0; $i < @$tokens; $i += 2) {
                    my($tok, $re) = @$tokens[$i, $i+1];
                    print "Trying token $tok... " if $debug;

                    my($regex,$next_parser_state);
                    if (ref($re) eq 'ARRAY') {
                        ($regex,$next_parser_state) = @$re;
                    } else {
                        $regex = $re;
                    }

                    if ($string =~ m/^($regex)/) {
                        print "Matched >>$1<<" if $debug;
                        my $match_len = length($1);
                        if ($match_len > $longest) {
                            print "\n  ** It's now the longest" if $debug;
                            $longest = $match_len;
                            $longest_token = $tok;
                            $longest_match = $1;
                            if ($next_parser_state) {
                                $parser_state = $next_parser_state;
                            }
                        }
                    }
                    print "\n" if $debug;
                }

                $string = substr($string, $longest);
                print "Consuming up to char pos $longest chars, string is now >>$string<<\n" if $debug;

                if ($longest_token eq 'WHITESPACE' and $parser_state ne 'dont_gobble_spaces') {
                    print "Redoing token extraction after whitespace\n" if $debug;
                    redo GET_NEXT_TOKEN;
                }

                $parser->YYData->{REMAINING} = $string;
                if ($longest) {
                    print "Returning token $longest_token, match $longest_match\n  next state is named $parser_state\n" if $debug;
                    $parser->YYData->{INPUT} = $longest_token;
                    $parser->YYData->{MATCH} = $longest_match;
                    return ($longest_token, $longest_match);
                }
                last if $token_list eq 'DEFAULT';  # avoid going over it twice if $parser_state is DEFAULT
            }
        }
        print "Didn't match anything, done!\n" if $debug;
        return (undef, '');  # Didn't match anything
    };

    return ( $parser->YYParse(
                yylex => $get_next_token,
                yyerror => \&_error,
                yydebug => $yydebug),
             \$string,
            );
}

# Used by the top-level expr production to turn an or-type parse tree with
# only a single AND condition into a simple AND-type tree (1-level arrayref).
# Or to add the '-or' to the front of a real OR-type tree so it can be passed
# directly to UR::BoolExpr::resolve()
sub _simplify {
    my($class, $expr) = @_;

    if (ref($expr->[0])) {
        if (@$expr == 1) {
            # An or-type parse tree, but with only one AND subrule - use as a simple and-type rule
            $expr = $expr->[0];
        } else {
            $expr = ['-or', $expr]; # an or-type parse tree with multiple subrules
        }
    }
    return $expr;
}

# Handles the case for "expr AND expr" where one or both exprs can be an
# OR-type expr.  In that case, it distributes the AND exprs among all the
# OR conditions.  For example:
# (a=1 or b=2) and (c=3 or d=4)
# is the same as
# (a=1 and c=3) or (a=1 and d=4) or (b=2 and c=3) or (b=2 and d=4)
# This is necessary because the BoolExpr resolver can only handle 1-level deep
# AND-type rules, or a 1-level deep OR-type rule composed of any number of
# 1-level deep AND-type rules
sub _and {
    my($class,$left, $right) = @_;

    # force them to be [[ "property operator" => value]] instead of just [ "property operator" => value ]
    $left  = [ $left ]  unless (ref($left->[0]));
    $right = [ $right ] unless (ref($right->[0]));

    my @and;
    foreach my $left_subexpr ( @$left ) {
        foreach my $right_subexpr (@$right) {
            push @and, [@$left_subexpr, @$right_subexpr];
        }
    }
    \@and;
}

sub _or {
    my($class,$left, $right) = @_;

    # force them to be [[ "property operator" => value]] instead of just [ "property operator" => value ]
    $left  = [ $left ]  unless (ref($left->[0]));
    $right = [ $right ] unless (ref($right->[0]));

    [ @$left, @$right ];
}

1;


1;