use pest::pratt_parser::{Assoc, Op, PrattParser};
use super::{IntoChildNodes, Node, Rule, RuleMatcher, literal::visit_value_literal, visit_identifier, visit_var};
use crate::{
common::{Spanned, error::TypeQLError, token},
expression::{
BuiltinFunctionName, Expression, FunctionCall, FunctionName, List, ListIndex, ListIndexRange, Operation, Paren,
},
value::{Literal, StructLiteral, ValueLiteral},
};
pub(super) fn visit_expression_function(node: Node<'_>) -> FunctionCall {
debug_assert_eq!(node.as_rule(), Rule::expression_function);
let span = node.span();
let mut children = node.into_children();
let name = visit_expression_function_name(children.consume_expected(Rule::expression_function_name));
let args =
children.try_consume_expected(Rule::expression_arguments).map(visit_expression_arguments).unwrap_or_default();
debug_assert_eq!(children.try_consume_any(), None);
FunctionCall::new(span, name, args)
}
fn visit_expression_arguments(node: Node<'_>) -> Vec<Expression> {
debug_assert_eq!(node.as_rule(), Rule::expression_arguments);
node.into_children().map(visit_expression).collect()
}
pub(super) fn visit_expression(node: Node<'_>) -> Expression {
debug_assert_eq!(node.as_rule(), Rule::expression);
let child = node.into_child();
match child.as_rule() {
Rule::expression_value => visit_expression_value(child),
Rule::expression_list => visit_expression_list(child),
_ => unreachable!("{}", TypeQLError::IllegalGrammar { input: child.as_str().to_owned() }),
}
}
pub(super) fn visit_expression_value(node: Node<'_>) -> Expression {
debug_assert_eq!(node.as_rule(), Rule::expression_value);
let pratt_parser: PrattParser<Rule> = PrattParser::new()
.op(Op::infix(Rule::PLUS, Assoc::Left) | Op::infix(Rule::MINUS, Assoc::Left))
.op(Op::infix(Rule::TIMES, Assoc::Left)
| Op::infix(Rule::DIVIDE, Assoc::Left)
| Op::infix(Rule::MODULO, Assoc::Left))
.op(Op::infix(Rule::POWER, Assoc::Right));
pratt_parser
.map_primary(visit_expression_base)
.map_infix(|left, op, right| {
let op = match op.as_rule() {
Rule::PLUS => token::ArithmeticOperator::Add,
Rule::MINUS => token::ArithmeticOperator::Subtract,
Rule::TIMES => token::ArithmeticOperator::Multiply,
Rule::DIVIDE => token::ArithmeticOperator::Divide,
Rule::MODULO => token::ArithmeticOperator::Modulo,
Rule::POWER => token::ArithmeticOperator::Power,
_ => unreachable!("{}", TypeQLError::IllegalGrammar { input: op.as_str().to_owned() }),
};
Expression::Operation(Box::new(Operation::new(op, left, right)))
})
.parse(node.into_children())
}
fn visit_expression_base(node: Node<'_>) -> Expression {
debug_assert_eq!(node.as_rule(), Rule::expression_base);
let child = node.into_child();
match child.as_rule() {
Rule::var => Expression::Variable(visit_var(child)),
Rule::value_literal => Expression::Value(visit_value_literal(child)),
Rule::expression_function => Expression::Function(visit_expression_function(child)),
Rule::expression_parenthesis => Expression::Paren(Box::new(visit_expression_parenthesis(child))),
Rule::expression_list_index => Expression::ListIndex(Box::new(visit_expression_list_index(child))),
_ => unreachable!("{}", TypeQLError::IllegalGrammar { input: child.as_str().to_owned() }),
}
}
fn visit_expression_list_index(node: Node<'_>) -> ListIndex {
debug_assert_eq!(node.as_rule(), Rule::expression_list_index);
let span = node.span();
let mut children = node.into_children();
let variable = visit_var(children.consume_expected(Rule::var));
let index = visit_list_index(children.consume_expected(Rule::list_index));
ListIndex::new(span, variable, index)
}
fn visit_list_index(node: Node<'_>) -> Expression {
debug_assert_eq!(node.as_rule(), Rule::list_index);
visit_expression_value(node.into_child())
}
pub(super) fn visit_expression_struct(node: Node<'_>) -> Literal {
debug_assert_eq!(node.as_rule(), Rule::expression_struct);
Literal::new(node.span(), ValueLiteral::Struct(StructLiteral { inner: node.as_str().to_owned() }))
}
fn visit_expression_parenthesis(node: Node<'_>) -> Paren {
debug_assert_eq!(node.as_rule(), Rule::expression_parenthesis);
Paren::new(node.span(), visit_expression_value(node.into_child()))
}
pub(super) fn visit_expression_list(node: Node<'_>) -> Expression {
debug_assert_eq!(node.as_rule(), Rule::expression_list);
let child = node.into_child();
match child.as_rule() {
Rule::expression_list_new => visit_expression_list_new(child),
Rule::expression_list_subrange => visit_expression_list_subrange(child),
_ => unreachable!("{}", TypeQLError::IllegalGrammar { input: child.as_str().to_owned() }),
}
}
fn visit_expression_list_subrange(node: Node<'_>) -> Expression {
debug_assert_eq!(node.as_rule(), Rule::expression_list_subrange);
let span = node.span();
let mut children = node.into_children();
let var = visit_var(children.consume_expected(Rule::var));
let (from, to) = visit_list_range(children.consume_expected(Rule::list_range));
debug_assert_eq!(children.try_consume_any(), None);
Expression::ListIndexRange(Box::new(ListIndexRange::new(span, var, from, to)))
}
fn visit_list_range(node: Node<'_>) -> (Expression, Expression) {
debug_assert_eq!(node.as_rule(), Rule::list_range);
let mut children = node.into_children();
let from = visit_expression_value(children.consume_expected(Rule::expression_value));
let to = visit_expression_value(children.consume_expected(Rule::expression_value));
debug_assert_eq!(children.try_consume_any(), None);
(from, to)
}
fn visit_expression_list_new(node: Node<'_>) -> Expression {
debug_assert_eq!(node.as_rule(), Rule::expression_list_new);
let span = node.span();
let items = node.into_children().map(visit_expression_value).collect();
Expression::List(List::new(span, items))
}
fn visit_expression_function_name(node: Node<'_>) -> FunctionName {
debug_assert_eq!(node.as_rule(), Rule::expression_function_name);
let child = node.into_child();
match child.as_rule() {
Rule::identifier => FunctionName::Identifier(visit_identifier(child)),
Rule::builtin_func_name => FunctionName::Builtin(visit_builtin_func_name(child)),
_ => unreachable!("{}", TypeQLError::IllegalGrammar { input: child.as_str().to_owned() }),
}
}
fn visit_builtin_func_name(node: Node<'_>) -> BuiltinFunctionName {
debug_assert_eq!(node.as_rule(), Rule::builtin_func_name);
let span = node.span();
let child = node.into_child();
let token = match child.as_rule() {
Rule::ABS => token::Function::Abs,
Rule::CEIL => token::Function::Ceil,
Rule::FLOOR => token::Function::Floor,
Rule::IID => token::Function::Iid,
Rule::LABEL => token::Function::Label,
Rule::LEN => token::Function::Len,
Rule::MAX => token::Function::Max,
Rule::MIN => token::Function::Min,
Rule::ROUND => token::Function::Round,
_ => unreachable!("{}", TypeQLError::IllegalGrammar { input: child.as_str().to_owned() }),
};
BuiltinFunctionName::new(span, token)
}