use super::{check_ok_needed_outputs, gather_operands, ParseNode, ParseNodeFunctional};
use crate::ast_node::{ASTContext, Node, TokenSlice};
use crate::operations::Operation;
use crate::syntax_error::SyntaxError;
use crate::tokens::{SymbolToken, Token};
use crate::types::IJType;
use anyhow::Result;
use std::rc::Rc;
pub struct Symbol;
impl ParseNode for Symbol {
fn next_node(
op: Token,
slice: TokenSlice,
context: &mut ASTContext,
) -> Result<(Rc<Node>, TokenSlice)> {
if let Token::Symbol(SymbolToken { name }) = op {
let variable = context
.symbols
.get(&name)
.ok_or(SyntaxError::UnknownSymbol(name.clone()))?;
let (node, rest) = match variable.typ.clone() {
IJType::Tensor(n) => (
Node::new(
Operation::Symbol(name.clone()),
vec![],
IJType::Tensor(n),
vec![],
context,
)?,
slice,
),
IJType::Function(signature) => {
let (operands, rest) =
gather_operands(vec![signature.input.clone()], slice, context)?;
let node = Node::new(
Operation::Function(name.clone()),
signature.input.clone(),
*signature.output.clone(),
operands,
context,
)?;
(node, rest)
}
IJType::Number(n) => (
Node::new(
Operation::Symbol(name.clone()),
vec![],
IJType::Number(n),
vec![],
context,
)?,
slice,
),
_ => unreachable!(),
};
Ok((Rc::new(node), rest))
} else {
unreachable!()
}
}
}
impl ParseNodeFunctional for Symbol {
fn next_node_functional_impl(
op: Token,
slice: TokenSlice,
context: &mut ASTContext,
needed_outputs: Option<&[IJType]>,
) -> Result<(Vec<Rc<Node>>, TokenSlice)> {
let name = match op {
Token::Symbol(SymbolToken { name }) => name,
_ => unreachable!(),
};
let variable = context
.symbols
.get(name.as_str())
.ok_or(SyntaxError::UnknownSymbol(name.clone()))?;
if let IJType::Function(ref func_signature) = variable.typ {
if !check_ok_needed_outputs(needed_outputs, &func_signature.output) {
let err_string = format!(
"{:?}, which has outputs: {:?}, but we expected output types to be one of {:?}",
context.token_slice_to_string(slice),
func_signature.output,
needed_outputs.unwrap()
);
return Err(SyntaxError::ExpectedFunction(err_string).into());
}
Ok((
vec![Rc::new(Node::new(
Operation::Function(name.clone()),
vec![],
variable.typ.clone(),
vec![],
context,
)?)],
slice.move_start(1)?,
))
} else {
Err(SyntaxError::ExpectedFunction(context.token_slice_to_string(slice)).into())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parser::parse_str;
use crate::types::FunctionSignature;
#[test]
fn test_symbol_number_type() -> Result<()> {
let mut context = ASTContext::new();
parse_str("var x: N<a>", &mut context)?;
let node = parse_str("x", &mut context)?;
assert_eq!(node.output_type, IJType::Number(Some("a".to_string())));
let mut context = ASTContext::new();
parse_str("var x: T<a>", &mut context)?;
let node = parse_str("x", &mut context)?;
assert_eq!(node.output_type, IJType::Tensor(Some("a".to_string())));
let mut context = ASTContext::new();
parse_str("var x: N<a>", &mut context)?;
let node = parse_str("x", &mut context)?;
assert_eq!(node.output_type, IJType::Number(Some("a".to_string())));
let mut context = ASTContext::new();
parse_str("var x: Fn(N<a>->T<a>)", &mut context)?;
let node = parse_str("~x", &mut context)?;
assert_eq!(
node.output_type,
IJType::Function(FunctionSignature::new(
vec![IJType::Number(Some("a".to_string()))],
IJType::Tensor(Some("a".to_string())),
))
);
Ok(())
}
}