// Copyright (c) 2022 Kaushik Kulkarni
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
use crate::primitives::{Expression, BinaryOpType, UnaryOpType, LiteralT,
SmallVecExprT};
use std::rc::Rc;
use std::str::FromStr;
use smallvec::{smallvec};
grammar;
pub Expr: Rc<Expression> = {
IfExpr,
};
IfExpr: Rc<Expression> = {
<then_expr: OrExpr> "if" <cond_expr: OrExpr> "else" <else_expr: IfExpr>
=> Rc::new(Expression::If(cond_expr.clone(), then_expr.clone(), else_expr.clone())),
OrExpr,
}
OrExpr: Rc<Expression> = {
<left_op: OrExpr> "or" <right_op: AndExpr>
=> Rc::new(Expression::BinaryOp(left_op.clone(), BinaryOpType::LogicalOr, right_op.clone())),
AndExpr,
};
AndExpr: Rc<Expression> = {
<left_op: AndExpr> "and" <right_op: NotExpr>
=> Rc::new(Expression::BinaryOp(left_op.clone(), BinaryOpType::LogicalAnd, right_op.clone())),
NotExpr,
};
NotExpr: Rc<Expression> = {
"not" <op: NotExpr>
=> Rc::new(Expression::UnaryOp(UnaryOpType::LogicalNot, op.clone())),
CmpExpr,
};
CmpExpr: Rc<Expression> = {
<left_op: CmpExpr> <operator: CmpOperator> <right_op: BitwiseOrExpr>
=> Rc::new(Expression::BinaryOp(left_op.clone(), operator, right_op.clone())),
BitwiseOrExpr,
};
BitwiseOrExpr: Rc<Expression> = {
<left_op: BitwiseOrExpr> "|" <right_op: BitwiseXorExpr>
=> Rc::new(Expression::BinaryOp(left_op.clone(), BinaryOpType::BitwiseOr, right_op.clone())),
BitwiseXorExpr,
};
BitwiseXorExpr: Rc<Expression> = {
<left_op: BitwiseXorExpr> "^" <right_op: BitwiseAndExpr>
=> Rc::new(Expression::BinaryOp(left_op.clone(), BinaryOpType::BitwiseXor, right_op.clone())),
BitwiseAndExpr,
};
BitwiseAndExpr: Rc<Expression> = {
<left_op: BitwiseAndExpr> "&" <right_op: ShiftExpr>
=> Rc::new(Expression::BinaryOp(left_op.clone(), BinaryOpType::BitwiseAnd, right_op.clone())),
ShiftExpr,
};
ShiftExpr: Rc<Expression> = {
<left_op: ShiftExpr> <operator: ShiftOperator> <right_op: SumExpr>
=> Rc::new(Expression::BinaryOp(left_op.clone(), operator, right_op.clone())),
SumExpr,
};
SumExpr: Rc<Expression> = {
<left_op: SumExpr> "+" <right_op: ProdExpr>
=> Rc::new(Expression::BinaryOp(left_op.clone(), BinaryOpType::Sum, right_op.clone())),
<left_op: SumExpr> "-" <right_op: ProdExpr>
=> Rc::new(Expression::BinaryOp(left_op.clone(), BinaryOpType::Subtract, right_op.clone())),
ProdExpr,
};
ProdExpr: Rc<Expression> = {
<left_op: ProdExpr> "*" <right_op: AtomExpr>
=> Rc::new(Expression::BinaryOp(left_op.clone(), BinaryOpType::Product, right_op.clone())),
<left_op: ProdExpr> "/" <right_op: AtomExpr>
=> Rc::new(Expression::BinaryOp(left_op.clone(), BinaryOpType::Divide, right_op.clone())),
<left_op: ProdExpr> "//" <right_op: AtomExpr>
=> Rc::new(Expression::BinaryOp(left_op.clone(), BinaryOpType::FloorDiv, right_op.clone())),
<left_op: ProdExpr> "%" <right_op: AtomExpr>
=> Rc::new(Expression::BinaryOp(left_op.clone(), BinaryOpType::Modulo, right_op.clone())),
UnaryExpr,
};
UnaryExpr: Rc<Expression> = {
"-" <op: UnaryExpr> => Rc::new(Expression::UnaryOp(UnaryOpType::Minus, op.clone())),
"~" <op: UnaryExpr> => Rc::new(Expression::UnaryOp(UnaryOpType::BitwiseNot, op.clone())),
"+" <op: UnaryExpr> => op,
ExponentExpr,
};
ExponentExpr: Rc<Expression> = {
<left_op: AtomExpr> "**" <right_op: ExponentExpr>
=> Rc::new(Expression::BinaryOp(left_op.clone(), BinaryOpType::Exponent, right_op.clone())),
AtomExpr,
};
AtomExpr: Rc<Expression> = {
<identifier: Identifier> => Rc::new(Expression::Variable(identifier)),
<call: AtomExpr> "(" <commaed_exprs: CommaedExprsFinal> ")" => Rc::new(Expression::Call(call.clone(), commaed_exprs)),
<call: AtomExpr> "(" ")" => Rc::new(Expression::Call(call.clone(), smallvec![])),
<agg: AtomExpr> "[" <commaed_exprs: CommaedExprsFinal> "]" => Rc::new(Expression::Subscript(agg.clone(), commaed_exprs)),
<agg: AtomExpr> "[" "]" => Rc::new(Expression::Subscript(agg.clone(), smallvec![])),
"(" <expr: Expr> ")" => expr,
<int_literal: IntLiteral> => Rc::new(Expression::Scalar(LiteralT::I32(int_literal))),
<float_literal: FloatLiteral> => Rc::new(Expression::Scalar(LiteralT::F64(float_literal))),
};
CommaedExprs: Vec<Rc<Expression>> = {
<commaed_exprs: CommaedExprs> "," <expr: Expr> => [commaed_exprs.as_slice(), vec![expr].as_slice()].concat(),
<expr: Expr> => vec![expr],
}
CommaedExprsFinal: SmallVecExprT = {
<commaed_exprs: CommaedExprs> => SmallVecExprT::from_vec(commaed_exprs),
<commaed_exprs: CommaedExprs> "," => SmallVecExprT::from_vec(commaed_exprs),
};
CmpOperator: BinaryOpType = {
"==" => BinaryOpType::Equal,
"!=" => BinaryOpType::NotEqual,
">" => BinaryOpType::Greater,
">=" => BinaryOpType::GreaterEqual,
"<" => BinaryOpType::Less,
"<=" => BinaryOpType::LessEqual,
};
ShiftOperator: BinaryOpType = {
">>" => BinaryOpType::RightShift,
"<<" => BinaryOpType::LeftShift,
};
Identifier: String = {
<s:r"[a-zA-Z_][a-zA-Z0-9_]*"> => s.to_string(),
};
IntLiteral: i32 = {
<s:r"(-?)([1-9][0-9]*)|0"> => i32::from_str(s).unwrap(),
}
FloatLiteral: f64 = {
// f64 with the decimal point
<s:r"((-?)([1-9][0-9]*)|0)\.([0-9]*)(([eE]((-?)([1-9][0-9]*)|0))?)"> => f64::from_str(s).unwrap(),
// f64 without the decimal point
<s:r"((-?)([1-9][0-9]*)|0)([eE]((-?)([1-9][0-9]*)|0))"> => f64::from_str(s).unwrap(),
}
// vim:syntax=rust