use super::next_node;
use crate::ast_node::{ASTContext, Node, TokenSlice};
use crate::operations::Operation;
use crate::syntax_error::SyntaxError;
use crate::tokens::{find_matching_parenthesis_on_tokens, Token};
use crate::types::IJType;
use anyhow::Result;
use std::rc::Rc;
pub fn gather_operands(
types: Vec<Vec<IJType>>,
slice: TokenSlice,
context: &mut ASTContext,
) -> Result<(Vec<Rc<Node>>, TokenSlice)> {
let mut operands = Vec::new();
let longest_variant_length = types.iter().map(|t| t.len()).max().unwrap();
let shortest_variant_length = types.iter().map(|t| t.len()).min().unwrap();
let mut rest = slice;
let mut longest_match_length = 0;
for _ in 0..longest_variant_length {
if rest.is_empty() {
break;
}
let result = next_node(rest, context);
if let Err(e) = result {
if longest_match_length < shortest_variant_length {
return Err(e);
}
break;
}
let (node, new_rest) = result.unwrap();
rest = new_rest;
match node.op {
Operation::Group => {
operands.extend(node.operands.clone().into_iter());
}
_ => operands.push(node),
}
let operands_types = operands
.iter()
.map(|n| n.output_type.clone())
.collect::<Vec<IJType>>();
if types.iter().any(|t| t.iter().zip(operands_types.iter()).all(|(a, b)| a.type_match(b))) {
longest_match_length = operands.len();
}
}
if longest_match_length < shortest_variant_length {
let input_types_str = types
.iter()
.map(|t| {
format!(
"[{}]",
t.iter()
.map(|inner_t| inner_t.to_string())
.collect::<Vec<String>>()
.join(", ")
)
})
.collect::<Vec<String>>()
.join(", ");
let found_types_str = operands
.iter()
.map(|n| n.output_type.to_string())
.collect::<String>();
let syntax_error = SyntaxError::GatherMismatch(input_types_str, found_types_str);
return Err(context.add_context_to_syntax_error(syntax_error.into(), slice));
}
let operands = operands[..longest_match_length].to_vec();
Ok((operands, rest))
}
pub fn gather_all(slice: TokenSlice, context: &mut ASTContext) -> Result<Vec<Rc<Node>>> {
let mut operands = Vec::new();
let slices = comma_separate(slice, context)?;
for sub_slice in slices {
let mut rest = sub_slice;
while !rest.is_empty() {
let (node, new_rest) = next_node(rest, context)
.map_err(|e| context.add_context_to_syntax_error(e, rest))?;
operands.push(node);
rest = new_rest;
}
}
Ok(operands)
}
pub fn find_matching_parenthesis(
context: &mut ASTContext,
slice: TokenSlice,
lparen: &Token,
rparen: &Token,
) -> Result<usize> {
let tokens = &context.get_tokens()[slice.start..slice.end];
let maybe_index = find_matching_parenthesis_on_tokens(tokens, lparen, rparen);
match maybe_index {
Some(index) => Ok(index),
None => Err(SyntaxError::UnmatchedParenthesis(context.token_slice_to_string(slice)).into()),
}
}
pub fn check_ok_needed_outputs(needed_outputs: Option<&[IJType]>, actual_output: &IJType) -> bool {
match needed_outputs {
Some(outputs) => outputs.iter().any(|output| actual_output.type_match(output)),
None => true,
}
}
pub fn comma_separate(slice: TokenSlice, context: &mut ASTContext) -> Result<Vec<TokenSlice>> {
let mut slices: Vec<TokenSlice> = vec![];
let mut last_endpoint = 0;
let tokens = &context.get_tokens()[slice.start..slice.end];
let mut index = 0;
while index < tokens.len() {
let token = &tokens[index];
match token {
Token::Comma => {
let new_slice = slice.move_end(index)?.move_start(last_endpoint)?;
slices.push(new_slice);
last_endpoint = index + 1;
}
Token::LParen => {
let group_len = find_matching_parenthesis(
context,
slice.move_start(index + 1)?,
&Token::LParen,
&Token::RParen,
)?;
index += group_len;
}
Token::LSqBracket => {
let group_len = find_matching_parenthesis(
context,
slice.move_start(index + 1)?,
&Token::LSqBracket,
&Token::RSqBracket,
)?;
index += group_len;
}
_ => (),
}
index += 1;
}
let last_slice = slice.move_end(tokens.len())?.move_start(last_endpoint)?;
slices.push(last_slice);
if slices.iter().any(|slice| slice.is_empty()) {
return Err(SyntaxError::EmptySlice.into());
}
Ok(slices)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::next_node_functional;
use crate::parser::test_utils::*;
#[test]
fn test_comma_separate() {
let maybe_context = create_context_with_tokens_from_str("+,/-,f");
if let Err(e) = maybe_context {
panic!("Failed to create context: {:?}", e);
}
let mut context = maybe_context.unwrap();
let maybe_slices = comma_separate(context.full_slice(), &mut context);
if let Err(e) = maybe_slices {
panic!("Failed to comma separate: {:?}", e);
}
let slices = maybe_slices.unwrap();
for slice in &slices {
let tokens = &context.get_tokens()[slice.start..slice.end];
println!("{:?}: '{:?}'", slice, tokens);
}
let correct_slices = vec![
TokenSlice::new(0, 1, 6),
TokenSlice::new(2, 4, 6),
TokenSlice::new(5, 6, 6),
];
assert_eq!(slices, correct_slices);
}
#[test]
fn test_comma_separate_empty() {
let maybe_context = create_context_with_tokens_from_str("");
if let Err(e) = maybe_context {
panic!("Failed to create context: {:?}", e);
}
let mut context = maybe_context.unwrap();
let maybe_slices = comma_separate(context.full_slice(), &mut context);
assert!(maybe_slices.is_err());
let error = maybe_slices.unwrap_err();
let actual_error = error.downcast_ref::<SyntaxError>().unwrap();
assert_eq!(actual_error, &SyntaxError::EmptySlice);
}
#[test]
fn test_comma_separate_with_nesting() {
let maybe_context = create_context_with_tokens_from_str("+,(x,y),f");
if let Err(e) = maybe_context {
panic!("Failed to create context: {:?}", e);
}
let mut context = maybe_context.unwrap();
let maybe_slices = comma_separate(context.full_slice(), &mut context);
let slices = maybe_slices.unwrap();
for slice in &slices {
let tokens = &context.get_tokens()[slice.start..slice.end];
println!("{:?}: '{:?}'", slice, tokens);
}
let correct_slices = vec![
TokenSlice::new(0, 1, 9),
TokenSlice::new(2, 7, 9),
TokenSlice::new(8, 9, 9),
];
assert_eq!(slices, correct_slices);
}
#[test]
fn test_comma_separate_with_array() {
let maybe_context = create_context_with_tokens_from_str("+,[0,1],f");
if let Err(e) = maybe_context {
panic!("Failed to create context: {:?}", e);
}
let mut context = maybe_context.unwrap();
let maybe_slices = comma_separate(context.full_slice(), &mut context);
let slices = maybe_slices.unwrap();
for slice in &slices {
let tokens = &context.get_tokens()[slice.start..slice.end];
println!("{:?}: '{:?}'", slice, tokens);
}
let correct_slices = vec![
TokenSlice::new(0, 1, 9),
TokenSlice::new(2, 7, 9),
TokenSlice::new(8, 9, 9),
];
assert_eq!(slices, correct_slices);
}
#[test]
fn test_comma_separate_next_node_functional() {
let maybe_context = create_context_with_tokens_from_str("/+,+");
if let Err(e) = maybe_context {
panic!("Failed to create context: {:?}", e);
}
let mut context = maybe_context.unwrap();
let maybe_slices = comma_separate(context.full_slice(), &mut context);
let slices = maybe_slices.unwrap();
for slice in &slices {
let tokens = &context.get_tokens()[slice.start..slice.end];
println!("{:?}: '{:?}'", slice, tokens);
let (node, rest) = next_node_functional(*slice, &mut context, None).unwrap();
println!("{:?}", node);
for n in &node {
let num_operands = n.operands.len();
println!("Output type: {:}", n.output_type);
println!("Input types: {:?}", n.input_types);
println!("Num operands: {:?}", num_operands);
}
println!("{:?}", rest.is_empty());
println!("---");
}
}
}