use super::{gather_operands, ParseNode, ParseNodeFunctional};
use crate::ast_node::{ASTContext, Node, TokenSlice};
use crate::operations::Operation;
use crate::tokens::Token;
use crate::types::{FunctionSignature, IJType};
use anyhow::Result;
use std::rc::Rc;
pub struct IdentityNode;
impl ParseNode for IdentityNode {
fn next_node(
_: Token,
tokens: TokenSlice,
context: &mut ASTContext,
) -> Result<(Rc<Node>, TokenSlice)> {
let (operands, rest) = gather_operands(
vec![vec![IJType::Number(None)], vec![IJType::Tensor(None)]],
tokens,
context,
)?;
if operands.len() != 1 {
return Err(anyhow::anyhow!("Identity operation expects one operand"));
}
Ok((operands[0].clone(), rest))
}
}
impl ParseNodeFunctional for IdentityNode {
fn next_node_functional_impl(
_op: Token,
slice: TokenSlice,
context: &mut ASTContext,
needed_outputs: Option<&[IJType]>,
) -> Result<(Vec<Rc<Node>>, TokenSlice)> {
let slice = slice.move_start(1)?;
let output_types = match needed_outputs {
Some(outputs) => outputs,
None => &[IJType::Number(None), IJType::Tensor(None)],
};
let nodes = output_types
.iter()
.map(|output_type| {
Ok(Rc::new(Node::new(
Operation::Identity,
vec![],
IJType::Function(FunctionSignature::new(
vec![output_type.clone()],
output_type.clone(),
)),
vec![],
context,
)?))
})
.collect::<Result<Vec<Rc<Node>>>>()?;
Ok((nodes, slice))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::parse_str_no_context;
#[test]
fn test_identity() -> Result<()> {
let (node, _) = parse_str_no_context("I 1")?;
assert!(matches!(node.op, Operation::Number(..)));
let (node, _) = parse_str_no_context("I [1,2,3]")?;
assert!(matches!(node.op, Operation::Array));
let (node, _) = parse_str_no_context("I I I I I 1")?;
assert!(matches!(node.op, Operation::Number(..)));
Ok(())
}
#[test]
fn test_identity_functional() -> Result<()> {
let (node, _) = parse_str_no_context("~I: Fn(N->N)")?;
assert_eq!(node.op, Operation::Identity);
assert_eq!(node.input_types.len(), 0);
assert_eq!(node.output_type, IJType::number_function(1));
let (node, _) = parse_str_no_context("~I: Fn(T->T)")?;
assert_eq!(node.op, Operation::Identity);
assert_eq!(node.input_types.len(), 0);
assert_eq!(node.output_type, IJType::tensor_function(1));
let (node, _) = parse_str_no_context("@(~-:Fn(T->T), I) [1]")?;
assert_eq!(node.op, Operation::FunctionComposition(2));
assert_eq!(node.output_type, IJType::Tensor(None));
Ok(())
}
#[test]
fn test_identity_number_type() -> Result<()> {
let (node, _) = parse_str_no_context("I 1<a>")?;
assert_eq!(node.output_type, IJType::Number(Some("a".to_string())));
let (node, _) = parse_str_no_context("I [1]<a>")?;
assert_eq!(node.output_type, IJType::Tensor(Some("a".to_string())));
let (node, _) = parse_str_no_context("I I I I I 1<a>")?;
assert_eq!(node.output_type, IJType::Number(Some("a".to_string())));
Ok(())
}
}