use crate::diagnostics::{Diagnostic, Level, Span};
use crate::id::{Id, IdRef};
use crate::types::{ConstValue, Type};
use rustre_parser::ast::{
AstNode, AstToken, CallByPosExpressionNode, NodeNode, StaticArgNode, StaticArgsNode,
};
use yeter::Database;
#[yeter::query]
pub fn static_params_of_node(_db: &Database, node: NodeNode) -> Vec<Box<Id>> {
if let Some(static_params) = node.static_params_node() {
static_params
.all_static_param_node()
.map(|param| param.id_node().map(Box::from).unwrap_or_default())
.collect()
} else {
Vec::new()
}
}
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
pub struct StaticArgs {
inner: Vec<(Box<Id>, StaticArg)>,
}
impl StaticArgs {
pub fn get(&self, id: &Id) -> Option<&StaticArg> {
self.inner
.iter()
.find(|(par_id, _)| par_id.as_ref() == id)
.map(|(_, arg)| arg)
}
pub fn resolve_type(&self, id: &Id) -> Option<&Type> {
self.get(id).and_then(StaticArg::as_type)
}
pub fn resolve_const(&self, id: &Id) -> Option<&ConstValue> {
self.get(id).and_then(StaticArg::as_const)
}
pub fn resolve_node(&self, id: &Id) -> Option<NodeNode> {
self.get(id).and_then(StaticArg::as_node)
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum StaticArg {
Type(Type),
Const(ConstValue),
Node(NodeNode),
}
macro_rules! as_impl {
($self:ident as $variant:path) => {
if let $variant(x) = $self {
Some(x)
} else {
None
}
};
}
impl StaticArg {
pub fn as_type(&self) -> Option<&Type> {
as_impl!(self as Self::Type)
}
pub fn as_const(&self) -> Option<&ConstValue> {
as_impl!(self as Self::Const)
}
pub fn as_node(&self) -> Option<NodeNode> {
as_impl!(self as Self::Node).cloned()
}
}
impl From<Type> for StaticArg {
fn from(value: Type) -> Self {
Self::Type(value)
}
}
impl From<ConstValue> for StaticArg {
fn from(value: ConstValue) -> Self {
Self::Const(value)
}
}
impl From<NodeNode> for StaticArg {
fn from(value: NodeNode) -> Self {
Self::Node(value)
}
}
fn static_arg_of_static_arg_node(
db: &Database,
caller_static_args: &Option<StaticArgs>,
caller_node: &Option<NodeNode>,
arg: StaticArgNode,
) -> Option<StaticArg> {
if let Some(n) = arg.type_node() {
let ty =
crate::types::type_of_ast_type(db, caller_static_args.clone(), caller_node.clone(), n);
Some(ty.as_ref().clone().into())
} else if let Some(n) = arg.expression_node() {
crate::eval::eval_const_node(db, n, caller_static_args.as_ref(), caller_node.clone())
.as_ref()
.clone()
.map(StaticArg::from)
} else if let Some(n) = arg.effective_node_node() {
n.id_ref_node()
.map(IdRef::from)
.and_then(|id_ref| {
Option::clone(&crate::name_resolution::find_node(
db,
caller_static_args,
id_ref,
))
})
.map(StaticArg::from)
} else {
unimplemented!()
}
}
#[yeter::query]
pub fn static_args_of_effective_node(
db: &Database,
caller_static_args: Option<StaticArgs>,
caller_node: Option<NodeNode>,
callee: NodeNode,
static_args_node: Option<StaticArgsNode>,
) -> Option<StaticArgs> {
let static_params = static_params_of_node(db, callee);
match static_args_node {
None => Some(StaticArgs::default()),
Some(args) => {
let values = args
.all_static_arg_node()
.map(|arg| {
Option::clone(&static_arg_of_static_arg_node(
db,
&caller_static_args,
&caller_node,
arg,
))
})
.collect::<Option<Vec<StaticArg>>>()?;
if values.is_empty() {
let span = Span::of_node(db, args.syntax());
Diagnostic::new(Level::Error, "no static arguments inside << >>")
.with_attachment(span, "hint: remove this")
.emit(db);
}
if values.len() != static_params.len() {
let span = Span::of_node(db, args.syntax());
let expected = static_params.len();
Diagnostic::new(Level::Error, "invalid static argument count")
.with_attachment(span, format!("expected {expected} static arguments"))
.emit(db);
}
Some(StaticArgs {
inner: std::iter::zip(static_params.iter().cloned(), values).collect(),
})
}
}
}
#[yeter::query]
pub fn static_args_of_call_expression(
db: &Database,
caller_static_args: Option<StaticArgs>,
caller_node: Option<NodeNode>,
expr: CallByPosExpressionNode,
) -> Option<StaticArgs> {
let callee_name = expr.node_ref().unwrap().id_node().unwrap().ident().unwrap();
let callee_name = callee_name.text();
let id_ref = IdRef::new_implicit(Id::from_str(callee_name));
let callee = crate::name_resolution::find_node(db, &caller_static_args, id_ref);
let callee = callee.as_ref().as_ref()?;
Option::clone(&static_args_of_effective_node(
db,
caller_static_args,
caller_node,
callee.clone(),
expr.static_args_node(),
))
}