use crate::{
    data_type::parse_data_type,
    keywords::Keyword,
    lexer::Token,
    parser::{ParseError, Parser},
    select::{parse_select, OrderFlag},
    span::OptSpanned,
    statement::parse_compound_query,
    DataType, Identifier, SString, Span, Spanned, Statement,
};
use alloc::string::ToString;
use alloc::vec;
use alloc::{boxed::Box, vec::Vec};
#[derive(Debug, Clone)]
pub enum Function<'a> {
    Abs,
    Acos,
    AddDate,
    AddMonths,
    AddTime,
    Ascii,
    Asin,
    Atan,
    Atan2,
    Bin,
    BitLength,
    Ceil,
    CharacterLength,
    Chr,
    Concat,
    ConcatWs,
    Conv,
    ConvertTs,
    Cos,
    Cot,
    Crc32,
    Crc32c,
    CurDate,
    CurrentTimestamp,
    CurTime,
    Date,
    DateAdd,
    DateDiff,
    DateFormat,
    DateSub,
    DayName,
    DayOfMonth,
    DayOfWeek,
    DayOfYear,
    Degrees,
    Elt,
    Exists,
    Exp,
    ExportSet,
    ExtractValue,
    Field,
    FindInSet,
    Floor,
    Format,
    FromBase64,
    FromDays,
    FromUnixTime,
    Greatest,
    Hex,
    If,
    IfNull,
    Insert,
    InStr,
    JsonArray,
    JsonArrayAgg,
    JsonArrayAppend,
    JsonArrayInsert,
    JsonArrayIntersect,
    JsonCompact,
    JsonContains,
    JsonContainsPath,
    JsonDepth,
    JsonDetailed,
    JsonEquals,
    JsonExists,
    JsonExtract,
    JsonInsert,
    JsonKeys,
    JsonLength,
    JsonLoose,
    JsonMerge,
    JsonMergePath,
    JsonMergePerserve,
    JsonNormalize,
    JsonObject,
    JsonObjectFilterKeys,
    JsonObjectToArray,
    JsonObjectAgg,
    JsonOverlaps,
    JsonPretty,
    JsonQuote,
    JsonQuery,
    JsonRemove,
    JsonReplace,
    JsonSchemaValid,
    JsonSearch,
    JsonSet,
    JsonTable,
    JsonType,
    JsonUnquote,
    JsonValid,
    JsonValue,
    LCase,
    Least,
    Left,
    Length,
    LengthB,
    Ln,
    LoadFile,
    Locate,
    Log,
    Log10,
    Log2,
    Lower,
    LPad,
    LTrim,
    MakeDate,
    MakeSet,
    MakeTime,
    Max,
    MicroSecond,
    Mid,
    Min,
    Minute,
    MonthName,
    NaturalSortkey,
    Now,
    NullIf,
    NVL2,
    Oct,
    OctetLength,
    Ord,
    PeriodAdd,
    PeriodDiff,
    Pi,
    Position,
    Pow,
    Quarter,
    Quote,
    Radians,
    Rand,
    Repeat,
    Replace,
    Reverse,
    Right,
    Round,
    RPad,
    RTrim,
    Second,
    SecToTime,
    SFormat,
    Sign,
    Sin,
    SoundEx,
    Space,
    Sqrt,
    StrCmp,
    StrToDate,
    SubDate,
    SubStr,
    SubStringIndex,
    SubTime,
    Sum,
    Tan,
    Time,
    TimeDiff,
    TimeFormat,
    Timestamp,
    TimestampAdd,
    TimestampDiff,
    TimeToSec,
    ToBase64,
    ToChar,
    ToDays,
    ToSeconds,
    Truncate,
    UCase,
    UncompressedLength,
    UnHex,
    UnixTimestamp,
    Unknown,
    UpdateXml,
    Upper,
    UtcDate,
    UtcTime,
    UtcTimeStamp,
    Value,
    Week,
    Weekday,
    WeekOfYear,
    Lead,
    Lag,
    Other(&'a str),
}
#[derive(Debug, Clone)]
pub enum Variable<'a> {
    TimeZone,
    Other(&'a str),
}
#[derive(Debug, Clone, Copy)]
pub enum BinaryOperator {
    Or,
    Xor,
    And,
    Eq,
    NullSafeEq,
    GtEq,
    Gt,
    LtEq,
    Lt,
    Neq,
    ShiftLeft,
    ShiftRight,
    BitAnd,
    BitOr,
    BitXor,
    Add,
    Subtract,
    Divide,
    Div,
    Mod,
    Mult,
    Like,
    NotLike,
}
#[derive(Debug, Clone, Copy)]
pub enum Is {
    Null,
    NotNull,
    True,
    NotTrue,
    False,
    NotFalse,
    Unknown,
    NotUnknown,
}
#[derive(Debug, Clone, Copy)]
pub enum UnaryOperator {
    Binary,
    Collate,
    LogicalNot,
    Minus,
    Not,
}
#[derive(Debug, Clone)]
pub enum IdentifierPart<'a> {
    Name(Identifier<'a>),
    Star(Span),
}
impl<'a> Spanned for IdentifierPart<'a> {
    fn span(&self) -> Span {
        match &self {
            IdentifierPart::Name(v) => v.span(),
            IdentifierPart::Star(v) => v.span(),
        }
    }
}
#[derive(Debug, Clone)]
pub struct When<'a> {
    pub when_span: Span,
    pub when: Expression<'a>,
    pub then_span: Span,
    pub then: Expression<'a>,
}
impl<'a> Spanned for When<'a> {
    fn span(&self) -> Span {
        self.when_span
            .join_span(&self.when)
            .join_span(&self.then_span)
            .join_span(&self.then)
    }
}
#[derive(Debug, Clone)]
pub struct WindowSpec<'a> {
    pub order_by: (Span, Vec<(Expression<'a>, OrderFlag)>),
}
impl<'a> Spanned for WindowSpec<'a> {
    fn span(&self) -> Span {
        self.order_by.span()
    }
}
#[derive(Debug, Clone)]
pub enum Expression<'a> {
    Binary {
        op: BinaryOperator,
        op_span: Span,
        lhs: Box<Expression<'a>>,
        rhs: Box<Expression<'a>>,
    },
    Unary {
        op: UnaryOperator,
        op_span: Span,
        operand: Box<Expression<'a>>,
    },
    Subquery(Box<Statement<'a>>),
    Null(Span),
    Bool(bool, Span),
    String(SString<'a>),
    Integer((u64, Span)),
    ListHack((usize, Span)),
    Float((f64, Span)),
    Function(Function<'a>, Vec<Expression<'a>>, Span),
    WindowFunction {
        function: Function<'a>,
        args: Vec<Expression<'a>>,
        function_span: Span,
        over_span: Span,
        window_spec: WindowSpec<'a>,
    },
    Identifier(Vec<IdentifierPart<'a>>),
    Arg((usize, Span)),
    Exists(Box<Statement<'a>>),
    In {
        lhs: Box<Expression<'a>>,
        rhs: Vec<Expression<'a>>,
        in_span: Span,
        not_in: bool,
    },
    Is(Box<Expression<'a>>, Is, Span),
    Invalid(Span),
    Case {
        case_span: Span,
        value: Option<Box<Expression<'a>>>,
        whens: Vec<When<'a>>,
        else_: Option<(Span, Box<Expression<'a>>)>,
        end_span: Span,
    },
    Cast {
        cast_span: Span,
        expr: Box<Expression<'a>>,
        as_span: Span,
        type_: DataType<'a>,
    },
    Count {
        count_span: Span,
        distinct_span: Option<Span>,
        expr: Box<Expression<'a>>,
    },
    GroupConcat {
        group_concat_span: Span,
        distinct_span: Option<Span>,
        expr: Box<Expression<'a>>,
    },
    Variable {
        global: Option<Span>,
        session: Option<Span>,
        dot: Option<Span>,
        variable: Variable<'a>,
        variable_span: Span,
    },
}
impl<'a> Spanned for Expression<'a> {
    fn span(&self) -> Span {
        match &self {
            Expression::Binary {
                op_span, lhs, rhs, ..
            } => op_span.join_span(lhs).join_span(rhs),
            Expression::Unary {
                op_span, operand, ..
            } => op_span.join_span(operand),
            Expression::Subquery(v) => v.span(),
            Expression::Null(v) => v.span(),
            Expression::Bool(_, v) => v.span(),
            Expression::String(v) => v.span(),
            Expression::Integer(v) => v.span(),
            Expression::Float(v) => v.span(),
            Expression::ListHack((_, s)) => s.span(),
            Expression::Function(_, b, c) => c.join_span(b),
            Expression::Identifier(v) => v.opt_span().expect("Span of identifier parts"),
            Expression::Arg(v) => v.span(),
            Expression::Exists(v) => v.span(),
            Expression::In {
                lhs, rhs, in_span, ..
            } => in_span.join_span(lhs).join_span(rhs),
            Expression::Is(a, _, b) => b.join_span(a),
            Expression::Invalid(s) => s.span(),
            Expression::Case {
                case_span,
                value,
                whens,
                else_,
                end_span,
            } => case_span
                .join_span(value)
                .join_span(whens)
                .join_span(else_)
                .join_span(end_span),
            Expression::Cast {
                cast_span,
                expr,
                as_span,
                type_,
            } => cast_span
                .join_span(expr)
                .join_span(as_span)
                .join_span(type_),
            Expression::Count {
                count_span,
                distinct_span,
                expr,
            } => count_span.join_span(distinct_span).join_span(expr),
            Expression::GroupConcat {
                group_concat_span,
                distinct_span,
                expr,
            } => group_concat_span.join_span(distinct_span).join_span(expr),
            Expression::Variable {
                global,
                session,
                dot,
                variable_span,
                variable: _,
            } => variable_span
                .join_span(global)
                .join_span(session)
                .join_span(dot),
            Expression::WindowFunction {
                function: _,
                args,
                function_span,
                over_span,
                window_spec,
            } => function_span
                .join_span(args)
                .join_span(over_span)
                .join_span(window_spec),
        }
    }
}
fn parse_function<'a>(
    parser: &mut Parser<'a, '_>,
    t: Token<'a>,
    span: Span,
) -> Result<Expression<'a>, ParseError> {
    parser.consume_token(Token::LParen)?;
    let func = match &t {
        Token::Ident(_, Keyword::ASCII) => Function::Ascii,
        Token::Ident(_, Keyword::BIN) => Function::Bin,
        Token::Ident(_, Keyword::BIT_LENGTH) => Function::BitLength,
        Token::Ident(_, Keyword::CHAR_LENGTH) => Function::CharacterLength,
        Token::Ident(_, Keyword::CHARACTER_LENGTH) => Function::CharacterLength,
        Token::Ident(_, Keyword::CHR) => Function::Chr,
        Token::Ident(_, Keyword::CONCAT) => Function::Concat,
        Token::Ident(_, Keyword::CONCAT_WS) => Function::ConcatWs,
        Token::Ident(_, Keyword::ELT) => Function::Elt,
        Token::Ident(_, Keyword::EXPORT_SET) => Function::ExportSet,
        Token::Ident(_, Keyword::EXTRACTVALUE) => Function::ExtractValue,
        Token::Ident(_, Keyword::FIELD) => Function::Field,
        Token::Ident(_, Keyword::FIND_IN_SET) => Function::FindInSet,
        Token::Ident(_, Keyword::FORMAT) => Function::Format,
        Token::Ident(_, Keyword::FROM_BASE64) => Function::FromBase64,
        Token::Ident(_, Keyword::HEX) => Function::Hex,
        Token::Ident(_, Keyword::INSERT) => Function::Insert,
        Token::Ident(_, Keyword::INSTR) => Function::InStr,
        Token::Ident(_, Keyword::LCASE) => Function::LCase,
        Token::Ident(_, Keyword::LEFT) => Function::Left,
        Token::Ident(_, Keyword::LENGTH) => Function::Length,
        Token::Ident(_, Keyword::LENGTHB) => Function::LengthB,
        Token::Ident(_, Keyword::LOAD_FILE) => Function::LoadFile,
        Token::Ident(_, Keyword::LOCATE) => Function::Locate,
        Token::Ident(_, Keyword::LOWER) => Function::Lower,
        Token::Ident(_, Keyword::LPAD) => Function::LPad,
        Token::Ident(_, Keyword::LTRIM) => Function::LTrim,
        Token::Ident(_, Keyword::MAKE_SET) => Function::MakeSet,
        Token::Ident(_, Keyword::MID) => Function::Mid,
        Token::Ident(_, Keyword::NATURAL_SORT_KEY) => Function::NaturalSortkey,
        Token::Ident(_, Keyword::OCTET_LENGTH) => Function::OctetLength,
        Token::Ident(_, Keyword::ORD) => Function::Ord,
        Token::Ident(_, Keyword::POSITION) => Function::Position,
        Token::Ident(_, Keyword::QUOTE) => Function::Quote,
        Token::Ident(_, Keyword::REPEAT) => Function::Repeat,
        Token::Ident(_, Keyword::REPLACE) => Function::Replace,
        Token::Ident(_, Keyword::REVERSE) => Function::Reverse,
        Token::Ident(_, Keyword::RIGHT) => Function::Right,
        Token::Ident(_, Keyword::RPAD) => Function::RPad,
        Token::Ident(_, Keyword::RTRIM) => Function::RTrim,
        Token::Ident(_, Keyword::SOUNDEX) => Function::SoundEx,
        Token::Ident(_, Keyword::SPACE) => Function::Space,
        Token::Ident(_, Keyword::STRCMP) => Function::StrCmp,
        Token::Ident(_, Keyword::SUBSTR) => Function::SubStr,
        Token::Ident(_, Keyword::SUBSTRING) => Function::SubStr,
        Token::Ident(_, Keyword::SUBSTRING_INDEX) => Function::SubStringIndex,
        Token::Ident(_, Keyword::TO_BASE64) => Function::ToBase64,
        Token::Ident(_, Keyword::TO_CHAR) => Function::ToChar,
        Token::Ident(_, Keyword::UCASE) => Function::UCase,
        Token::Ident(_, Keyword::UNCOMPRESSED_LENGTH) => Function::UncompressedLength,
        Token::Ident(_, Keyword::UNHEX) => Function::UnHex,
        Token::Ident(_, Keyword::UPDATEXML) => Function::UpdateXml,
        Token::Ident(_, Keyword::UPPER) => Function::Upper,
        Token::Ident(_, Keyword::SFORMAT) => Function::SFormat,
        Token::Ident(_, Keyword::EXISTS) => Function::Exists,
        Token::Ident(_, Keyword::MIN) => Function::Min,
        Token::Ident(_, Keyword::MAX) => Function::Max,
        Token::Ident(_, Keyword::SUM) => Function::Sum,
        Token::Ident(_, Keyword::VALUE) => Function::Value,
        Token::Ident(_, Keyword::VALUES) => Function::Value,
        Token::Ident(_, Keyword::LEAD) => Function::Lead,
        Token::Ident(_, Keyword::LAG) => Function::Lag,
        Token::Ident(_, Keyword::IFNULL) => Function::IfNull,
        Token::Ident(_, Keyword::NULLIF) => Function::NullIf,
        Token::Ident(_, Keyword::NVL) => Function::IfNull,
        Token::Ident(_, Keyword::NVL2) => Function::NVL2,
        Token::Ident(_, Keyword::IF) => Function::If,
        Token::Ident(_, Keyword::ABS) => Function::Abs,
        Token::Ident(_, Keyword::ACOS) => Function::Acos,
        Token::Ident(_, Keyword::ASIN) => Function::Asin,
        Token::Ident(_, Keyword::ATAN) => Function::Atan,
        Token::Ident(_, Keyword::ATAN2) => Function::Atan2,
        Token::Ident(_, Keyword::CEIL | Keyword::CEILING) => Function::Ceil,
        Token::Ident(_, Keyword::CONV) => Function::Conv,
        Token::Ident(_, Keyword::COS) => Function::Cos,
        Token::Ident(_, Keyword::COT) => Function::Cot,
        Token::Ident(_, Keyword::CRC32) => Function::Crc32,
        Token::Ident(_, Keyword::DEGREES) => Function::Degrees,
        Token::Ident(_, Keyword::EXP) => Function::Exp,
        Token::Ident(_, Keyword::FLOOR) => Function::Floor,
        Token::Ident(_, Keyword::GREATEST) => Function::Greatest,
        Token::Ident(_, Keyword::LN) => Function::Ln,
        Token::Ident(_, Keyword::LOG) => Function::Log,
        Token::Ident(_, Keyword::LOG10) => Function::Log10,
        Token::Ident(_, Keyword::LOG2) => Function::Log2,
        Token::Ident(_, Keyword::OCT) => Function::Oct,
        Token::Ident(_, Keyword::PI) => Function::Pi,
        Token::Ident(_, Keyword::POW | Keyword::POWER) => Function::Pow,
        Token::Ident(_, Keyword::RADIANS) => Function::Radians,
        Token::Ident(_, Keyword::RAND) => Function::Rand,
        Token::Ident(_, Keyword::ROUND) => Function::Round,
        Token::Ident(_, Keyword::SIGN) => Function::Sign,
        Token::Ident(_, Keyword::SIN) => Function::Sin,
        Token::Ident(_, Keyword::SQRT) => Function::Sqrt,
        Token::Ident(_, Keyword::TAN) => Function::Tan,
        Token::Ident(_, Keyword::TRUNCATE) => Function::Truncate,
        Token::Ident(_, Keyword::CRC32C) => Function::Crc32c,
        Token::Ident(_, Keyword::LEAST) => Function::Least,
        Token::Ident(_, Keyword::ADDDATE) => Function::AddDate,
        Token::Ident(_, Keyword::ADDTIME) => Function::AddTime,
        Token::Ident(_, Keyword::CONVERT_TS) => Function::ConvertTs,
        Token::Ident(_, Keyword::CURDATE) => Function::CurDate,
        Token::Ident(_, Keyword::CURRENT_DATE) => Function::CurDate,
        Token::Ident(_, Keyword::CURRENT_TIME) => Function::CurTime,
        Token::Ident(_, Keyword::CURTIME) => Function::CurTime,
        Token::Ident(_, Keyword::DATE) => Function::Date,
        Token::Ident(_, Keyword::DATEDIFF) => Function::DateDiff,
        Token::Ident(_, Keyword::DATE_ADD) => Function::DateAdd,
        Token::Ident(_, Keyword::DATE_FORMAT) => Function::DateFormat,
        Token::Ident(_, Keyword::DATE_SUB) => Function::DateSub,
        Token::Ident(_, Keyword::DAY | Keyword::DAYOFMONTH) => Function::DayOfMonth,
        Token::Ident(_, Keyword::DAYNAME) => Function::DayName,
        Token::Ident(_, Keyword::DAYOFWEEK) => Function::DayOfWeek,
        Token::Ident(_, Keyword::DAYOFYEAR) => Function::DayOfYear,
        Token::Ident(_, Keyword::FROM_DAYS) => Function::FromDays,
        Token::Ident(
            _,
            Keyword::LOCALTIME | Keyword::LOCALTIMESTAMP | Keyword::CURRENT_TIMESTAMP,
        ) => Function::Now,
        Token::Ident(_, Keyword::MAKEDATE) => Function::MakeDate,
        Token::Ident(_, Keyword::MAKETIME) => Function::MakeTime,
        Token::Ident(_, Keyword::MICROSECOND) => Function::MicroSecond,
        Token::Ident(_, Keyword::MINUTE) => Function::Minute,
        Token::Ident(_, Keyword::MONTHNAME) => Function::MonthName,
        Token::Ident(_, Keyword::NOW) => Function::Now,
        Token::Ident(_, Keyword::PERIOD_ADD) => Function::PeriodAdd,
        Token::Ident(_, Keyword::PERIOD_DIFF) => Function::PeriodDiff,
        Token::Ident(_, Keyword::QUARTER) => Function::Quarter,
        Token::Ident(_, Keyword::SECOND) => Function::Second,
        Token::Ident(_, Keyword::SEC_TO_TIME) => Function::SecToTime,
        Token::Ident(_, Keyword::STR_TO_DATE) => Function::StrToDate,
        Token::Ident(_, Keyword::SUBDATE) => Function::SubDate,
        Token::Ident(_, Keyword::SUBTIME) => Function::SubTime,
        Token::Ident(_, Keyword::TIME) => Function::Time,
        Token::Ident(_, Keyword::TIMEDIFF) => Function::TimeDiff,
        Token::Ident(_, Keyword::TIMESTAMP) => Function::Timestamp,
        Token::Ident(_, Keyword::TIMESTAMPADD) => Function::TimestampAdd,
        Token::Ident(_, Keyword::TIMESTAMPDIFF) => Function::TimestampDiff,
        Token::Ident(_, Keyword::TIME_FORMAT) => Function::TimeFormat,
        Token::Ident(_, Keyword::TIME_TO_SEC) => Function::TimeToSec,
        Token::Ident(_, Keyword::TO_DAYS) => Function::ToDays,
        Token::Ident(_, Keyword::TO_SECONDS) => Function::ToSeconds,
        Token::Ident(_, Keyword::UNIX_TIMESTAMP) => Function::UnixTimestamp,
        Token::Ident(_, Keyword::UTC_DATE) => Function::UtcDate,
        Token::Ident(_, Keyword::UTC_TIME) => Function::UtcTime,
        Token::Ident(_, Keyword::UTC_TIMESTAMP) => Function::UtcTimeStamp,
        Token::Ident(_, Keyword::WEEK) => Function::Week,
        Token::Ident(_, Keyword::WEEKDAY) => Function::Weekday,
        Token::Ident(_, Keyword::WEEKOFYEAR) => Function::WeekOfYear,
        Token::Ident(_, Keyword::ADD_MONTHS) => Function::AddMonths,
        Token::Ident(_, Keyword::FROM_UNIXTIME) => Function::FromUnixTime,
        Token::Ident(_, Keyword::JSON_ARRAY) => Function::JsonArray,
        Token::Ident(_, Keyword::JSON_ARRAYAGG) => Function::JsonArrayAgg,
        Token::Ident(_, Keyword::JSON_ARRAY_APPEND) => Function::JsonArrayAppend,
        Token::Ident(_, Keyword::JSON_ARRAY_INSERT) => Function::JsonArrayInsert,
        Token::Ident(_, Keyword::JSON_ARRAY_INTERSECT) => Function::JsonArrayIntersect,
        Token::Ident(_, Keyword::JSON_COMPACT) => Function::JsonCompact,
        Token::Ident(_, Keyword::JSON_CONTAINS) => Function::JsonContains,
        Token::Ident(_, Keyword::JSON_CONTAINS_PATH) => Function::JsonContainsPath,
        Token::Ident(_, Keyword::JSON_DEPTH) => Function::JsonDepth,
        Token::Ident(_, Keyword::JSON_DETAILED) => Function::JsonDetailed,
        Token::Ident(_, Keyword::JSON_EQUALS) => Function::JsonEquals,
        Token::Ident(_, Keyword::JSON_EXISTS) => Function::JsonExists,
        Token::Ident(_, Keyword::JSON_EXTRACT) => Function::JsonExtract,
        Token::Ident(_, Keyword::JSON_INSERT) => Function::JsonInsert,
        Token::Ident(_, Keyword::JSON_KEYS) => Function::JsonKeys,
        Token::Ident(_, Keyword::JSON_LENGTH) => Function::JsonLength,
        Token::Ident(_, Keyword::JSON_LOOSE) => Function::JsonLoose,
        Token::Ident(_, Keyword::JSON_MERGE) => Function::JsonMerge,
        Token::Ident(_, Keyword::JSON_MERGE_PATCH) => Function::JsonMergePath,
        Token::Ident(_, Keyword::JSON_MERGE_PRESERVE) => Function::JsonMergePerserve,
        Token::Ident(_, Keyword::JSON_NORMALIZE) => Function::JsonNormalize,
        Token::Ident(_, Keyword::JSON_OBJECT) => Function::JsonObject,
        Token::Ident(_, Keyword::JSON_OBJECT_FILTER_KEYS) => Function::JsonObjectFilterKeys,
        Token::Ident(_, Keyword::JSON_OBJECT_TO_ARRAY) => Function::JsonObjectToArray,
        Token::Ident(_, Keyword::JSON_OBJECTAGG) => Function::JsonObjectAgg,
        Token::Ident(_, Keyword::JSON_OVERLAPS) => Function::JsonOverlaps,
        Token::Ident(_, Keyword::JSON_PRETTY) => Function::JsonPretty,
        Token::Ident(_, Keyword::JSON_QUERY) => Function::JsonQuery,
        Token::Ident(_, Keyword::JSON_QUOTE) => Function::JsonQuote,
        Token::Ident(_, Keyword::JSON_REMOVE) => Function::JsonRemove,
        Token::Ident(_, Keyword::JSON_REPLACE) => Function::JsonReplace,
        Token::Ident(_, Keyword::JSON_SCHEMA_VALID) => Function::JsonSchemaValid,
        Token::Ident(_, Keyword::JSON_SEARCH) => Function::JsonSearch,
        Token::Ident(_, Keyword::JSON_SET) => Function::JsonSet,
        Token::Ident(_, Keyword::JSON_TABLE) => Function::JsonTable,
        Token::Ident(_, Keyword::JSON_TYPE) => Function::JsonType,
        Token::Ident(_, Keyword::JSON_UNQUOTE) => Function::JsonUnquote,
        Token::Ident(_, Keyword::JSON_VALID) => Function::JsonValid,
        Token::Ident(_, Keyword::JSON_VALUE) => Function::JsonValue,
        Token::Ident(v, k) if !k.reserved() => Function::Other(v),
        _ => {
            parser
                .issues
                .push(crate::Issue::err("Unknown function", &span));
            Function::Unknown
        }
    };
    let mut args = Vec::new();
    if !matches!(parser.token, Token::RParen) {
        loop {
            parser.recovered(
                "')' or ','",
                &|t| matches!(t, Token::RParen | Token::Comma),
                |parser| {
                    args.push(parse_expression_outer(parser)?);
                    Ok(())
                },
            )?;
            if parser.skip_token(Token::Comma).is_none() {
                break;
            }
        }
    }
    parser.consume_token(Token::RParen)?;
    if let Some(over_span) = parser.skip_keyword(Keyword::OVER) {
        parser.consume_token(Token::LParen)?;
        let order_span = parser.consume_keywords(&[Keyword::ORDER, Keyword::BY])?;
        let mut order = Vec::new();
        loop {
            let e = parse_expression(parser, false)?;
            let f = match &parser.token {
                Token::Ident(_, Keyword::ASC) => OrderFlag::Asc(parser.consume()),
                Token::Ident(_, Keyword::DESC) => OrderFlag::Desc(parser.consume()),
                _ => OrderFlag::None,
            };
            order.push((e, f));
            if parser.skip_token(Token::Comma).is_none() {
                break;
            }
        }
        parser.consume_token(Token::RParen)?;
        Ok(Expression::WindowFunction {
            function: func,
            args,
            function_span: span,
            over_span,
            window_spec: WindowSpec {
                order_by: (order_span, order),
            },
        })
    } else {
        Ok(Expression::Function(func, args, span))
    }
}
const IN_PRIORITY: usize = 110;
trait Priority {
    fn priority(&self) -> usize;
}
impl Priority for BinaryOperator {
    fn priority(&self) -> usize {
        match self {
            BinaryOperator::Or => 140,
            BinaryOperator::Xor => 150,
            BinaryOperator::And => 160,
            BinaryOperator::Eq => 110,
            BinaryOperator::NullSafeEq => 110,
            BinaryOperator::GtEq => 110,
            BinaryOperator::Gt => 110,
            BinaryOperator::LtEq => 110,
            BinaryOperator::Lt => 110,
            BinaryOperator::Neq => 110,
            BinaryOperator::Like => 110,
            BinaryOperator::NotLike => 110,
            BinaryOperator::ShiftLeft => 80,
            BinaryOperator::ShiftRight => 80,
            BinaryOperator::BitAnd => 90,
            BinaryOperator::BitOr => 100,
            BinaryOperator::BitXor => 50,
            BinaryOperator::Add => 70,
            BinaryOperator::Subtract => 70,
            BinaryOperator::Divide => 60,
            BinaryOperator::Div => 60,
            BinaryOperator::Mod => 60,
            BinaryOperator::Mult => 60,
        }
    }
}
impl Priority for UnaryOperator {
    fn priority(&self) -> usize {
        match self {
            UnaryOperator::Binary => 20,
            UnaryOperator::Collate => 20,
            UnaryOperator::LogicalNot => 30,
            UnaryOperator::Minus => 40,
            UnaryOperator::Not => 130,
        }
    }
}
#[derive(Debug)]
enum ReduceMember<'a> {
    Expression(Expression<'a>),
    Binary(BinaryOperator, Span),
    Unary(UnaryOperator, Span),
}
struct Reducer<'a> {
    stack: Vec<ReduceMember<'a>>,
}
impl<'a> Reducer<'a> {
    fn reduce(&mut self, priority: usize) -> Result<(), &'static str> {
        let mut e = match self.stack.pop() {
            Some(ReduceMember::Expression(e)) => e,
            _ => {
                return Err("Expected expression before here");
            }
        };
        loop {
            let v = self.stack.pop();
            match v {
                None => break,
                Some(ReduceMember::Expression(_)) => return Err("ICE Reduce stack error 1"),
                Some(ReduceMember::Unary(op, span)) if op.priority() > priority => {
                    self.stack.push(ReduceMember::Unary(op, span));
                    break;
                }
                Some(ReduceMember::Binary(op, span)) if op.priority() > priority => {
                    self.stack.push(ReduceMember::Binary(op, span));
                    break;
                }
                Some(ReduceMember::Unary(op, op_span)) => {
                    e = Expression::Unary {
                        op,
                        op_span,
                        operand: Box::new(e),
                    };
                }
                Some(ReduceMember::Binary(op, op_span)) => {
                    let lhs = match self.stack.pop() {
                        Some(ReduceMember::Expression(e)) => e,
                        _ => return Err("ICE Reduce stack error 2"),
                    };
                    e = Expression::Binary {
                        op,
                        op_span,
                        lhs: Box::new(lhs),
                        rhs: Box::new(e),
                    };
                }
            }
        }
        self.stack.push(ReduceMember::Expression(e));
        Ok(())
    }
    fn shift_binop(&mut self, span: Span, op: BinaryOperator) -> Result<(), &'static str> {
        self.reduce(op.priority())?;
        self.stack.push(ReduceMember::Binary(op, span));
        Ok(())
    }
    fn shift_unary(&mut self, span: Span, op: UnaryOperator) -> Result<(), &'static str> {
        if matches!(self.stack.last(), Some(ReduceMember::Expression(_))) {
            return Err("Unary operator cannot come before expression");
        }
        self.stack.push(ReduceMember::Unary(op, span));
        Ok(())
    }
    fn shift_expr(&mut self, e: Expression<'a>) -> Result<(), &'static str> {
        if matches!(self.stack.last(), Some(ReduceMember::Expression(_))) {
            return Err("Expression should not follow expression");
        }
        self.stack.push(ReduceMember::Expression(e));
        Ok(())
    }
}
pub(crate) fn parse_expression<'a>(
    parser: &mut Parser<'a, '_>,
    inner: bool,
) -> Result<Expression<'a>, ParseError> {
    let mut r = Reducer { stack: Vec::new() };
    loop {
        let e = match &parser.token {
            Token::Ident(_, Keyword::OR) | Token::DoublePipe if !inner => {
                r.shift_binop(parser.consume(), BinaryOperator::Or)
            }
            Token::Ident(_, Keyword::XOR) if !inner => {
                r.shift_binop(parser.consume(), BinaryOperator::Xor)
            }
            Token::Ident(_, Keyword::AND) | Token::DoubleAmpersand if !inner => {
                r.shift_binop(parser.consume(), BinaryOperator::And)
            }
            Token::Eq if !inner => r.shift_binop(parser.consume(), BinaryOperator::Eq),
            Token::Spaceship if !inner => {
                r.shift_binop(parser.consume(), BinaryOperator::NullSafeEq)
            }
            Token::GtEq if !inner => r.shift_binop(parser.consume(), BinaryOperator::GtEq),
            Token::Gt if !inner => r.shift_binop(parser.consume(), BinaryOperator::Gt),
            Token::LtEq if !inner => r.shift_binop(parser.consume(), BinaryOperator::LtEq),
            Token::Lt if !inner => r.shift_binop(parser.consume(), BinaryOperator::Lt),
            Token::Neq if !inner => r.shift_binop(parser.consume(), BinaryOperator::Neq),
            Token::ShiftLeft if !inner => {
                r.shift_binop(parser.consume(), BinaryOperator::ShiftLeft)
            }
            Token::ShiftRight if !inner => {
                r.shift_binop(parser.consume(), BinaryOperator::ShiftRight)
            }
            Token::Ampersand => r.shift_binop(parser.consume(), BinaryOperator::BitAnd),
            Token::Pipe if !inner => r.shift_binop(parser.consume(), BinaryOperator::BitOr),
            Token::Ident(_, Keyword::BINARY) if !inner => {
                r.shift_unary(parser.consume(), UnaryOperator::Binary)
            }
            Token::Ident(_, Keyword::COLLATE) if !inner => {
                r.shift_unary(parser.consume(), UnaryOperator::Collate)
            }
            Token::ExclamationMark if !inner => {
                r.shift_unary(parser.consume(), UnaryOperator::LogicalNot)
            }
            Token::Minus if !matches!(r.stack.last(), Some(ReduceMember::Expression(_))) => {
                r.shift_unary(parser.consume(), UnaryOperator::Minus)
            }
            Token::Minus
                if !inner && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
            {
                r.shift_binop(parser.consume(), BinaryOperator::Subtract)
            }
            Token::Ident(_, Keyword::IN) if !inner => {
                if let Err(e) = r.reduce(IN_PRIORITY) {
                    parser.error(e)?;
                }
                let lhs = match r.stack.pop() {
                    Some(ReduceMember::Expression(e)) => e,
                    _ => parser.error("Expected expression before here 3")?,
                };
                let op = parser.consume_keyword(Keyword::IN)?;
                parser.consume_token(Token::LParen)?;
                let mut rhs = Vec::new();
                loop {
                    parser.recovered(
                        "')' or ','",
                        &|t| matches!(t, Token::RParen | Token::Comma),
                        |parser| {
                            rhs.push(parse_expression_paren(parser)?);
                            Ok(())
                        },
                    )?;
                    if parser.skip_token(Token::Comma).is_none() {
                        break;
                    }
                }
                parser.consume_token(Token::RParen)?;
                r.shift_expr(Expression::In {
                    lhs: Box::new(lhs),
                    rhs,
                    in_span: op,
                    not_in: false,
                })
            }
            Token::Ident(_, Keyword::IS) if !inner => {
                if let Err(e) = r.reduce(IN_PRIORITY) {
                    parser.error(e)?;
                }
                let lhs = match r.stack.pop() {
                    Some(ReduceMember::Expression(e)) => e,
                    _ => parser.error("Expected expression before here 4")?,
                };
                let op = parser.consume_keyword(Keyword::IS)?;
                let (is, op) = match &parser.token {
                    Token::Ident(_, Keyword::NOT) => {
                        parser.consume();
                        match &parser.token {
                            Token::Ident(_, Keyword::TRUE) => {
                                (Is::NotTrue, parser.consume().join_span(&op))
                            }
                            Token::Ident(_, Keyword::FALSE) => {
                                (Is::NotFalse, parser.consume().join_span(&op))
                            }
                            Token::Ident(_, Keyword::NULL) => {
                                (Is::NotNull, parser.consume().join_span(&op))
                            }
                            Token::Ident(_, Keyword::UNKNOWN) => {
                                (Is::NotUnknown, parser.consume().join_span(&op))
                            }
                            _ => parser.expected_failure("'TRUE', 'FALSE', 'UNKNOWN' or 'NULL'")?,
                        }
                    }
                    Token::Ident(_, Keyword::TRUE) => (Is::True, parser.consume().join_span(&op)),
                    Token::Ident(_, Keyword::FALSE) => (Is::False, parser.consume().join_span(&op)),
                    Token::Ident(_, Keyword::NULL) => (Is::Null, parser.consume().join_span(&op)),
                    Token::Ident(_, Keyword::UNKNOWN) => {
                        (Is::Unknown, parser.consume().join_span(&op))
                    }
                    _ => parser.expected_failure("'NOT', 'TRUE', 'FALSE', 'UNKNOWN' or 'NULL'")?,
                };
                r.shift_expr(Expression::Is(Box::new(lhs), is, op))
            }
            Token::Ident(_, Keyword::NOT)
                if !matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
            {
                r.shift_unary(parser.consume(), UnaryOperator::Not)
            }
            Token::Ident(_, Keyword::NOT)
                if !inner && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) =>
            {
                if let Err(e) = r.reduce(IN_PRIORITY) {
                    parser.error(e)?;
                }
                let lhs = match r.stack.pop() {
                    Some(ReduceMember::Expression(e)) => e,
                    _ => parser.error("Expected expression before here 2")?,
                };
                let op = parser.consume_keyword(Keyword::NOT)?;
                match &parser.token {
                    Token::Ident(_, Keyword::IN) => {
                        let op = parser.consume_keyword(Keyword::IN)?.join_span(&op);
                        parser.consume_token(Token::LParen)?;
                        let mut rhs = Vec::new();
                        loop {
                            parser.recovered(
                                "')' or ','",
                                &|t| matches!(t, Token::RParen | Token::Comma),
                                |parser| {
                                    rhs.push(parse_expression_paren(parser)?);
                                    Ok(())
                                },
                            )?;
                            if parser.skip_token(Token::Comma).is_none() {
                                break;
                            }
                        }
                        parser.consume_token(Token::RParen)?;
                        r.shift_expr(Expression::In {
                            lhs: Box::new(lhs),
                            rhs,
                            in_span: op,
                            not_in: true,
                        })
                    }
                    Token::Ident(_, Keyword::LIKE) => {
                        r.stack.push(ReduceMember::Expression(lhs));
                        r.shift_binop(parser.consume().join_span(&op), BinaryOperator::NotLike)
                    }
                    _ => parser.expected_failure("'IN' or 'LIKE'")?,
                }
            }
            Token::Ident(_, Keyword::LIKE) if !inner => {
                r.shift_binop(parser.consume(), BinaryOperator::Like)
            }
            Token::Plus if !inner => r.shift_binop(parser.consume(), BinaryOperator::Add),
            Token::Div if !inner => r.shift_binop(parser.consume(), BinaryOperator::Divide),
            Token::Minus if !inner => r.shift_binop(parser.consume(), BinaryOperator::Subtract),
            Token::Ident(_, Keyword::LIKE) if !inner => {
                r.shift_binop(parser.consume(), BinaryOperator::Like)
            }
            Token::Mul if !matches!(r.stack.last(), Some(ReduceMember::Expression(_))) => r
                .shift_expr(Expression::Identifier(vec![IdentifierPart::Star(
                    parser.consume_token(Token::Mul)?,
                )])),
            Token::Mul if !inner && matches!(r.stack.last(), Some(ReduceMember::Expression(_))) => {
                r.shift_binop(parser.consume(), BinaryOperator::Mult)
            }
            Token::Ident(_, Keyword::TRUE) => r.shift_expr(Expression::Bool(
                true,
                parser.consume_keyword(Keyword::TRUE)?,
            )),
            Token::Ident(_, Keyword::FALSE) => r.shift_expr(Expression::Bool(
                false,
                parser.consume_keyword(Keyword::FALSE)?,
            )),
            Token::Ident(_, Keyword::NULL) => {
                r.shift_expr(Expression::Null(parser.consume_keyword(Keyword::NULL)?))
            }
            Token::Ident(_, Keyword::_LIST_) if parser.options.list_hack => {
                let arg = parser.arg;
                parser.arg += 1;
                r.shift_expr(Expression::ListHack((
                    arg,
                    parser.consume_keyword(Keyword::_LIST_)?,
                )))
            }
            Token::SingleQuotedString(_) | Token::DoubleQuotedString(_) => {
                r.shift_expr(Expression::String(parser.consume_string()?))
            }
            Token::Integer(_) => r.shift_expr(Expression::Integer(parser.consume_int()?)),
            Token::Float(_) => r.shift_expr(Expression::Float(parser.consume_float()?)),
            Token::Ident(_, Keyword::CAST) => {
                let cast_span = parser.consume_keyword(Keyword::CAST)?;
                parser.consume_token(Token::LParen)?;
                let cast = parser.recovered("')'", &|t| matches!(t, Token::RParen), |parser| {
                    let expr = parse_expression_outer(parser)?;
                    let as_span = parser.consume_keyword(Keyword::AS)?;
                    let type_ = parse_data_type(parser, false)?;
                    Ok(Some((expr, as_span, type_)))
                })?;
                parser.consume_token(Token::RParen)?;
                if let Some((expr, as_span, type_)) = cast {
                    r.shift_expr(Expression::Cast {
                        cast_span,
                        expr: Box::new(expr),
                        as_span,
                        type_,
                    })
                } else {
                    r.shift_expr(Expression::Invalid(cast_span))
                }
            }
            Token::Ident(_, Keyword::COUNT) => {
                let count_span = parser.consume_keyword(Keyword::COUNT)?;
                parser.consume_token(Token::LParen)?;
                let distinct_span = parser.skip_keyword(Keyword::DISTINCT);
                let expr = parser.recovered("')'", &|t| matches!(t, Token::RParen), |parser| {
                    let expr = parse_expression_outer(parser)?;
                    Ok(Some(expr))
                })?;
                parser.consume_token(Token::RParen)?;
                if let Some(expr) = expr {
                    r.shift_expr(Expression::Count {
                        count_span,
                        distinct_span,
                        expr: Box::new(expr),
                    })
                } else {
                    r.shift_expr(Expression::Invalid(count_span))
                }
            }
            Token::Ident(_, Keyword::GROUP_CONCAT) => {
                let group_concat_span = parser.consume_keyword(Keyword::GROUP_CONCAT)?;
                parser.consume_token(Token::LParen)?;
                let distinct_span = parser.skip_keyword(Keyword::DISTINCT);
                let expr = parser.recovered("')'", &|t| matches!(t, Token::RParen), |parser| {
                    let expr = parse_expression_outer(parser)?;
                    Ok(Some(expr))
                })?;
                parser.consume_token(Token::RParen)?;
                if let Some(expr) = expr {
                    r.shift_expr(Expression::GroupConcat {
                        group_concat_span,
                        distinct_span,
                        expr: Box::new(expr),
                    })
                } else {
                    r.shift_expr(Expression::Invalid(group_concat_span))
                }
            }
            Token::Ident(_, k) if k.expr_ident() => {
                let i = parser.token.clone();
                let s = parser.span.clone();
                parser.consume();
                if matches!(parser.token, Token::LParen) {
                    r.shift_expr(parse_function(parser, i, s)?)
                } else {
                    let f = match i {
                        Token::Ident(_, Keyword::CURRENT_TIMESTAMP) => {
                            Some(Function::CurrentTimestamp)
                        }
                        _ => None,
                    };
                    if let Some(f) = f {
                        r.shift_expr(Expression::Function(f, Vec::new(), s))
                    } else {
                        let mut parts = vec![IdentifierPart::Name(
                            parser.token_to_plain_identifier(&i, s)?,
                        )];
                        loop {
                            if parser.skip_token(Token::Period).is_none() {
                                break;
                            }
                            match &parser.token {
                                Token::Mul => parts
                                    .push(IdentifierPart::Star(parser.consume_token(Token::Mul)?)),
                                Token::Ident(_, _) => parts
                                    .push(IdentifierPart::Name(parser.consume_plain_identifier()?)),
                                _ => parser.expected_failure("Identifier or '*'")?,
                            }
                        }
                        r.shift_expr(Expression::Identifier(parts))
                    }
                }
            }
            Token::QuestionMark
                if matches!(parser.options.arguments, crate::SQLArguments::QuestionMark) =>
            {
                let arg = parser.arg;
                parser.arg += 1;
                r.shift_expr(Expression::Arg((
                    arg,
                    parser.consume_token(Token::QuestionMark)?,
                )))
            }
            Token::PercentS if matches!(parser.options.arguments, crate::SQLArguments::Percent) => {
                let arg = parser.arg;
                parser.arg += 1;
                r.shift_expr(Expression::Arg((
                    arg,
                    parser.consume_token(Token::PercentS)?,
                )))
            }
            Token::DollarArg(arg)
                if matches!(parser.options.arguments, crate::SQLArguments::Dollar) =>
            {
                r.shift_expr(Expression::Arg((arg - 1, parser.consume())))
            }
            Token::LParen => {
                parser.consume_token(Token::LParen)?;
                let ans = parse_expression_paren(parser)?;
                parser.consume_token(Token::RParen)?;
                r.shift_expr(ans)
            }
            Token::Ident(_, Keyword::EXISTS) => {
                parser.consume_keyword(Keyword::EXISTS)?;
                parser.consume_token(Token::LParen)?;
                let ans = Expression::Exists(Box::new(parse_compound_query(parser)?));
                parser.consume_token(Token::RParen)?;
                r.shift_expr(ans)
            }
            Token::Ident(_, Keyword::CASE) => {
                let case_span = parser.consume_keyword(Keyword::CASE)?;
                let value = if !matches!(parser.token, Token::Ident(_, Keyword::WHEN)) {
                    Some(Box::new(parse_expression(parser, false)?))
                } else {
                    None
                };
                let mut whens = Vec::new();
                let mut else_ = None;
                parser.recovered(
                    "'END'",
                    &|t| matches!(t, Token::Ident(_, Keyword::END)),
                    |parser| {
                        loop {
                            let when_span = parser.consume_keyword(Keyword::WHEN)?;
                            let when = parse_expression(parser, false)?;
                            let then_span = parser.consume_keyword(Keyword::THEN)?;
                            let then = parse_expression(parser, false)?;
                            whens.push(When {
                                when_span,
                                when,
                                then_span,
                                then,
                            });
                            if !matches!(parser.token, Token::Ident(_, Keyword::WHEN)) {
                                break;
                            }
                        }
                        if let Some(span) = parser.skip_keyword(Keyword::ELSE) {
                            else_ = Some((span, Box::new(parse_expression(parser, false)?)))
                        };
                        Ok(())
                    },
                )?;
                let end_span = parser.consume_keyword(Keyword::END)?;
                r.shift_expr(Expression::Case {
                    case_span,
                    value,
                    whens,
                    else_,
                    end_span,
                })
            }
            Token::AtAtGlobal | Token::AtAtSession => {
                let global = parser.skip_token(Token::AtAtGlobal);
                let session = if global.is_none() {
                    Some(parser.consume_token(Token::AtAtSession)?)
                } else {
                    None
                };
                let dot = Some(parser.consume_token(Token::Period)?);
                let variable = match &parser.token {
                    Token::Ident(_, Keyword::TIME_ZONE) => Variable::TimeZone,
                    Token::Ident(t, _) => Variable::Other(t),
                    _ => parser.expected_failure("Identifier")?,
                };
                let variable_span = parser.consume();
                r.shift_expr(Expression::Variable {
                    global,
                    session,
                    dot,
                    variable,
                    variable_span,
                })
            }
            _ => break,
        };
        if let Err(e) = e {
            parser.error(e.to_string())?;
        }
    }
    if r.reduce(99999).is_err() {
        parser.error("Expected expression")
    } else if r.stack.len() != 1 {
        parser.ice(file!(), line!())
    } else if let Some(ReduceMember::Expression(e)) = r.stack.pop() {
        Ok(e)
    } else {
        parser.ice(file!(), line!())
    }
}
pub(crate) fn parse_expression_outer<'a>(
    parser: &mut Parser<'a, '_>,
) -> Result<Expression<'a>, ParseError> {
    if matches!(parser.token, Token::Ident(_, Keyword::SELECT)) {
        Ok(Expression::Subquery(Box::new(Statement::Select(
            parse_select(parser)?,
        ))))
    } else {
        parse_expression(parser, false)
    }
}
pub(crate) fn parse_expression_paren<'a>(
    parser: &mut Parser<'a, '_>,
) -> Result<Expression<'a>, ParseError> {
    if matches!(parser.token, Token::Ident(_, Keyword::SELECT)) {
        Ok(Expression::Subquery(Box::new(parse_compound_query(
            parser,
        )?)))
    } else {
        parse_expression(parser, false)
    }
}
#[cfg(test)]
mod tests {
    use core::ops::Deref;
    use alloc::{
        format,
        string::{String, ToString},
        vec::Vec,
    };
    use crate::{
        expression::{BinaryOperator, Expression},
        parser::Parser,
        ParseOptions, SQLDialect,
    };
    use super::{parse_expression, IdentifierPart};
    fn test_ident<'a>(e: impl AsRef<Expression<'a>>, v: &str) -> Result<(), String> {
        let v = match e.as_ref() {
            Expression::Identifier(a) => match a.as_slice() {
                [IdentifierPart::Name(vv)] => vv.deref() == v,
                _ => false,
            },
            _ => false,
        };
        if !v {
            Err(format!("Expected identifier {} found {:?}", v, e.as_ref()))
        } else {
            Ok(())
        }
    }
    fn test_expr(src: &'static str, f: impl FnOnce(&Expression<'_>) -> Result<(), String>) {
        let mut issues = Vec::new();
        let options = ParseOptions::new().dialect(SQLDialect::MariaDB);
        let mut parser = Parser::new(src, &mut issues, &options);
        let res = parse_expression(&mut parser, false).expect("Expression in test expr");
        if let Err(e) = f(&res) {
            panic!("Error parsing {}: {}\nGot {:#?}", src, e, res);
        }
    }
    #[test]
    fn expressions() {
        test_expr("`a` + `b` * `c` + `d`", |e| {
            match e {
                Expression::Binary {
                    op: BinaryOperator::Add,
                    lhs,
                    rhs,
                    ..
                } => {
                    match lhs.as_ref() {
                        Expression::Binary {
                            op: BinaryOperator::Add,
                            lhs,
                            rhs,
                            ..
                        } => {
                            test_ident(lhs, "a")?;
                            match rhs.as_ref() {
                                Expression::Binary {
                                    op: BinaryOperator::Mult,
                                    lhs,
                                    rhs,
                                    ..
                                } => {
                                    test_ident(lhs, "b")?;
                                    test_ident(rhs, "c")?;
                                }
                                _ => return Err("Lhs.Rhs".to_string()),
                            }
                        }
                        _ => return Err("Lhs".to_string()),
                    }
                    test_ident(rhs, "d")?;
                }
                _ => return Err("Outer".to_string()),
            }
            Ok(())
        });
    }
}