use reifydb_type::error::{AstErrorKind, Error, TypeError};
use super::{Parser, Precedence};
use crate::{
Result,
ast::ast::{Ast, AstAppend, AstAppendSource, AstFrom, AstStatement, AstVariable},
token::{keyword::Keyword, operator::Operator, separator::Separator, token::TokenKind},
};
impl<'bump> Parser<'bump> {
pub(crate) fn parse_append(&mut self) -> Result<AstAppend<'bump>> {
let token = self.current()?;
self.advance()?;
if !self.is_eof() && self.current()?.is_operator(Operator::OpenCurly) {
let with = self.parse_sub_query()?;
return Ok(AstAppend::Query {
token,
with,
});
}
let variable_token = self.current()?;
if !matches!(variable_token.kind, TokenKind::Variable) {
let fragment = variable_token.fragment.to_owned();
return Err(Error::from(TypeError::Ast {
kind: AstErrorKind::UnexpectedToken {
expected: "expected variable name starting with '$'".to_string(),
},
message: format!(
"Unexpected token: expected {}, got {}",
"expected variable name starting with '$'",
fragment.text()
),
fragment,
}));
}
let var_token = self.advance()?;
let target = AstVariable {
token: var_token,
};
self.consume_keyword(Keyword::From)?;
let source = if !self.is_eof() && self.current()?.is_operator(Operator::OpenBracket) {
AstAppendSource::Inline(self.parse_list()?)
} else if !self.is_eof() && matches!(self.current()?.kind, TokenKind::Variable) {
let src_token = self.advance()?;
let variable = AstVariable {
token: src_token,
};
let first_node = Ast::From(AstFrom::Variable {
token: src_token,
variable,
});
let mut nodes = vec![first_node];
let mut has_pipes = false;
while !self.is_eof() {
if let Ok(current) = self.current()
&& current.is_separator(Separator::Semicolon)
{
break;
}
if self.current()?.is_operator(Operator::Pipe) {
self.advance()?; has_pipes = true;
nodes.push(self.parse_node(Precedence::None)?);
} else {
break;
}
}
AstAppendSource::Statement(AstStatement {
nodes,
has_pipes,
is_output: false,
rql: "", })
} else {
let statement = self.parse_statement_content()?;
AstAppendSource::Statement(statement)
};
Ok(AstAppend::IntoVariable {
token,
target,
source,
})
}
}
#[cfg(test)]
pub mod tests {
use crate::{
ast::{
ast::{Ast, AstAppend, AstFrom},
parse::Parser,
},
bump::Bump,
token::tokenize,
};
#[test]
fn test_append_query_basic() {
let bump = Bump::new();
let source = "append { from test::orders }";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let mut parser = Parser::new(&bump, source, tokens);
let mut result = parser.parse().unwrap();
assert_eq!(result.len(), 1);
let result = result.pop().unwrap();
let node = result.first_unchecked();
if let Ast::Append(AstAppend::Query {
with,
..
}) = node
{
let first_node = with.statement.nodes.first().expect("Expected node in subquery");
if let Ast::From(AstFrom::Source {
source,
..
}) = first_node
{
assert_eq!(source.namespace[0].text(), "test");
assert_eq!(source.name.text(), "orders");
} else {
panic!("Expected From node in subquery");
}
} else {
panic!("Expected Append::Query");
}
}
#[test]
fn test_append_query_with_from() {
let bump = Bump::new();
let source = "from test::source1 append { from test::source2 }";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let mut parser = Parser::new(&bump, source, tokens);
let result = parser.parse().unwrap();
assert_eq!(result.len(), 1);
let statement = &result[0];
assert_eq!(statement.nodes.len(), 2);
assert!(statement.nodes[0].is_from());
if let Ast::Append(AstAppend::Query {
with,
..
}) = &statement.nodes[1]
{
let first_node = with.statement.nodes.first().expect("Expected node in subquery");
if let Ast::From(AstFrom::Source {
source,
..
}) = first_node
{
assert_eq!(source.namespace[0].text(), "test");
assert_eq!(source.name.text(), "source2");
} else {
panic!("Expected From node in subquery");
}
} else {
panic!("Expected Append::Query");
}
}
#[test]
fn test_append_query_chained() {
let bump = Bump::new();
let source = "from test::source1 append { from test::source2 } append { from test::source3 }";
let tokens = tokenize(&bump, source).unwrap().into_iter().collect();
let mut parser = Parser::new(&bump, source, tokens);
let result = parser.parse().unwrap();
assert_eq!(result.len(), 1);
let statement = &result[0];
assert_eq!(statement.nodes.len(), 3);
assert!(statement.nodes[0].is_from());
assert!(matches!(statement.nodes[1], Ast::Append(AstAppend::Query { .. })));
assert!(matches!(statement.nodes[2], Ast::Append(AstAppend::Query { .. })));
}
}