reifydb-rql 0.4.12

ReifyDB Query Language (RQL) parser and AST
Documentation
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2025 ReifyDB

use crate::{
	Result,
	ast::{
		ast::AstTuple,
		parse::{Parser, Precedence},
	},
	token::{
		operator::{Operator, Operator::CloseParen},
		separator::Separator::Comma,
		token::{Token, TokenKind},
	},
};

impl<'bump> Parser<'bump> {
	pub(crate) fn parse_tuple(&mut self) -> Result<AstTuple<'bump>> {
		let token = self.consume_operator(Operator::OpenParen)?;
		self.parse_tuple_call(token)
	}

	pub(crate) fn parse_tuple_call(&mut self, operator: Token<'bump>) -> Result<AstTuple<'bump>> {
		let mut nodes = Vec::with_capacity(4);
		loop {
			self.skip_new_line()?;

			if self.current()?.is_operator(CloseParen) {
				break;
			}
			nodes.push(self.parse_node(Precedence::None)?);
			if self.consume_if(TokenKind::Separator(Comma))?.is_none() {
				break;
			};
		}

		self.consume_operator(CloseParen)?;
		Ok(AstTuple {
			token: operator,
			nodes,
		})
	}
}

#[cfg(test)]
pub mod tests {
	use crate::{
		ast::{
			ast::{
				Ast::{Identifier, Infix, Literal},
				AstInfix,
				AstLiteral::Number,
				InfixOperator,
			},
			parse::parse,
		},
		bump::Bump,
		token::tokenize,
	};

	#[test]
	fn test_empty_tuple() {
		let bump = Bump::new();
		let source = "()";
		let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
		let result = parse(&bump, source, tokens).unwrap();
		assert_eq!(result.len(), 1);

		let node = result[0].first_unchecked().as_tuple();
		assert!(node.nodes.is_empty());
	}

	#[test]
	fn test_tuple_with_number() {
		let bump = Bump::new();
		let source = "(9924)";
		let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
		let result = parse(&bump, source, tokens).unwrap();
		assert_eq!(result.len(), 1);

		let node = result[0].first_unchecked().as_tuple();
		let Some(node) = node.nodes.first() else {
			panic!()
		};
		let Literal(Number(number)) = &node else {
			panic!()
		};
		assert_eq!(number.value(), "9924");
	}

	#[test]
	fn test_nested_tuple() {
		let bump = Bump::new();
		let source = "(1 * ( 2 + 3 ))";
		let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
		let result = parse(&bump, source, tokens).unwrap();
		assert_eq!(result.len(), 1);

		let node = result[0].first_unchecked().as_tuple();
		let Some(node) = node.nodes.first() else {
			panic!()
		};
		let Infix(AstInfix {
			left,
			right,
			..
		}) = &node
		else {
			panic!()
		};

		let Literal(Number(left)) = &left.as_ref() else {
			panic!()
		};
		assert_eq!(left.value(), "1");

		let node = right.as_tuple();
		let Some(node) = node.nodes.first() else {
			panic!()
		};
		let AstInfix {
			left,
			right,
			..
		} = &node.as_infix();

		let Literal(Number(left)) = &left.as_ref() else {
			panic!()
		};
		assert_eq!(left.value(), "2");

		let Literal(Number(right)) = &right.as_ref() else {
			panic!()
		};
		assert_eq!(right.value(), "3");
	}

	#[test]
	fn test_tuple_with_identifier() {
		let bump = Bump::new();
		let source = "(u)";
		let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
		let result = parse(&bump, source, tokens).unwrap();
		assert_eq!(result.len(), 1);

		let node = &result[0].first_unchecked().as_tuple();
		let Some(node) = node.nodes.first() else {
			panic!()
		};
		let Identifier(node) = node else {
			panic!()
		};
		assert_eq!(node.text(), "u");
	}

	#[test]
	fn test_tuple_with_identifier_and_type() {
		let bump = Bump::new();
		let source = "(u: Bool)";
		let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
		let result = parse(&bump, source, tokens).unwrap();
		assert_eq!(result.len(), 1);

		let node = result[0].first_unchecked().as_tuple();
		let Some(node) = node.nodes.first() else {
			panic!()
		};
		let Infix(AstInfix {
			left,
			right,
			..
		}) = &node
		else {
			panic!()
		};

		let identifier = &left.as_identifier();
		assert_eq!(identifier.text(), "u");

		let dat_type = &right.as_identifier();
		assert_eq!(dat_type.text(), "Bool");
	}

	#[test]
	fn test_tuple_with_multiple_identifiers() {
		let bump = Bump::new();
		let source = "(u,v)";
		let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
		let result = parse(&bump, source, tokens).unwrap();
		assert_eq!(result.len(), 1);

		let node = result[0].first_unchecked().as_tuple();

		let Some(Identifier(u_node)) = &node.nodes.first() else {
			panic!()
		};
		assert_eq!(u_node.text(), "u");

		let Some(Identifier(v_node)) = &node.nodes.last() else {
			panic!()
		};
		assert_eq!(v_node.text(), "v");
	}

	#[test]
	fn test_tuple_with_identifiers_and_types() {
		let bump = Bump::new();
		let source = "(u: Bool, v: Text)";
		let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
		let result = parse(&bump, source, tokens).unwrap();
		assert_eq!(result.len(), 1);

		let node = result[0].first_unchecked().as_tuple();

		let Some(u_node) = node.nodes.first() else {
			panic!()
		};
		let Infix(AstInfix {
			left,
			right,
			..
		}) = &u_node
		else {
			panic!()
		};
		let Identifier(identifier) = &left.as_ref() else {
			panic!()
		};
		assert_eq!(identifier.text(), "u");
		let Identifier(ty) = &right.as_ref() else {
			panic!()
		};
		assert_eq!(ty.text(), "Bool");

		let Some(v_node) = node.nodes.last() else {
			panic!()
		};
		let Infix(AstInfix {
			left,
			right,
			..
		}) = &v_node
		else {
			panic!()
		};
		let Identifier(identifier) = &left.as_ref() else {
			panic!()
		};
		assert_eq!(identifier.text(), "v");
		let Identifier(identifier) = &right.as_ref() else {
			panic!()
		};
		assert_eq!(identifier.text(), "Text");
	}

	#[test]
	fn test_tuple_with_identifiers_and_declaration() {
		let bump = Bump::new();
		let source = "(u = 1, v = 2)";
		let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
		let result = parse(&bump, source, tokens).unwrap();
		assert_eq!(result.len(), 1);

		let node = result[0].first_unchecked().as_tuple();

		let Some(u_node) = node.nodes.first() else {
			panic!()
		};
		let Infix(AstInfix {
			left,
			operator,
			right,
			..
		}) = &u_node
		else {
			panic!()
		};
		let Identifier(identifier) = &left.as_ref() else {
			panic!()
		};
		assert_eq!(identifier.text(), "u");
		assert!(matches!(operator, InfixOperator::Assign(_)));
		let Literal(Number(number)) = right.as_ref() else {
			panic!()
		};
		assert_eq!(number.value(), "1");

		let Some(v_node) = node.nodes.last() else {
			panic!()
		};
		let Infix(AstInfix {
			left,
			operator,
			right,
			..
		}) = &v_node
		else {
			panic!()
		};
		let Identifier(identifier) = &left.as_ref() else {
			panic!()
		};
		assert_eq!(identifier.text(), "v");
		assert!(matches!(operator, InfixOperator::Assign(_)));
		let Literal(Number(number)) = right.as_ref() else {
			panic!()
		};
		assert_eq!(number.value(), "2");
	}

	#[test]
	fn test_multiline_tuple() {
		let bump = Bump::new();
		let source = r#"(
        u: Bool,
        v: Text
        )"#;
		let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
		let result = parse(&bump, source, tokens).unwrap();
		assert_eq!(result.len(), 1);

		let node = result[0].first_unchecked().as_tuple();

		let Some(u_node) = node.nodes.first() else {
			panic!()
		};
		let Infix(AstInfix {
			left,
			right,
			..
		}) = &u_node
		else {
			panic!()
		};
		let Identifier(identifier) = &left.as_ref() else {
			panic!()
		};
		assert_eq!(identifier.text(), "u");
		let Identifier(identifier) = &right.as_ref() else {
			panic!()
		};
		assert_eq!(identifier.text(), "Bool");

		let Some(v_node) = node.nodes.last() else {
			panic!()
		};
		let Infix(AstInfix {
			left,
			right,
			..
		}) = &v_node
		else {
			panic!()
		};
		let Identifier(identifier) = &left.as_ref() else {
			panic!()
		};
		assert_eq!(identifier.text(), "v");
		let Identifier(identifier) = &right.as_ref() else {
			panic!()
		};
		assert_eq!(identifier.text(), "Text");
	}

	#[test]
	fn test_regression() {
		let bump = Bump::new();
		let source = "(-1 -2)";
		let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
		let result = parse(&bump, source, tokens).unwrap();
		assert_eq!(result.len(), 1);

		let node = result[0].first_unchecked().as_tuple();
		assert_eq!(node.nodes.len(), 1);

		let infix = node.nodes[0].as_infix();

		let left_number = infix.left.as_literal_number();
		assert_eq!(left_number.value(), "-1");

		assert!(matches!(infix.operator, InfixOperator::Subtract(_)));

		let right_number = infix.right.as_literal_number();
		assert_eq!(right_number.value(), "2");
	}
}