use smallvec::SmallVec;
use crate::{
ast::{
annotation::{Annotation, Argument},
builder::Builder,
diagnostics::Diagnostic as D,
resolve::literals::{ExpectedType, ValueEvalError},
Value,
},
cst::{ChildrenExt as _, Kind},
parse::{Span, TokenKind},
Node,
};
#[derive(Debug, Clone, Copy, thiserror::Error)]
#[non_exhaustive]
pub enum AnnotationResolveError {
#[error("missing annotation name")]
MissingName { span: Span },
#[error("named argument is missing a value")]
MissingArgValue { span: Span },
#[error("duplicate argument")]
DuplicateArgument { span: Span },
#[error("invalid argument value: {0}")]
ResolveValue(#[from] ValueEvalError),
}
impl AnnotationResolveError {
#[must_use]
pub fn span(&self) -> Span {
match self {
Self::MissingName { span }
| Self::MissingArgValue { span }
| Self::DuplicateArgument { span } => *span,
Self::ResolveValue(err) => err.span(),
}
}
}
type Errors = SmallVec<[AnnotationResolveError; 1]>;
impl<'a> Builder<'a> {
pub(crate) fn attach_annotations(&mut self, parent: &Node) {
let cst = self.cst;
let mut pending: SmallVec<[Annotation; 2]> = SmallVec::new();
for node in parent.children.get(cst).iter().filter_map(|c| c.tree(cst)) {
match node.kind {
Kind::Comment => {}
Kind::Annotation => pending.extend(self.resolve_annotation(node)),
_ => {
let target = node.trimmed_span(cst);
for annotation in pending.drain(..) {
self.annotations.attach(annotation, target);
}
self.attach_annotations(node);
}
}
}
if let (Some(first), Some(last)) = (pending.first(), pending.last()) {
self.push(D::TrailingAnnotations(first.span().cover(last.span())).unwrap());
}
}
fn resolve_annotation(&mut self, node: &Node) -> Option<Annotation> {
match self.try_resolve_annotation(node) {
Ok(annotation) => Some(annotation),
Err(errors) => {
for error in errors {
self.push(D::AnnotationResolve(error).unwrap());
}
None
}
}
}
fn try_resolve_annotation(&self, node: &Node) -> Result<Annotation, Errors> {
let cst = self.cst;
let children = node.children.get(cst);
let name = children
.find_token(cst, TokenKind::Name)
.map(|t| t.span)
.ok_or_else(|| {
Errors::from_elem(AnnotationResolveError::MissingName { span: node.span }, 1)
})?;
let mut arguments = Vec::new();
let mut errors = Errors::new();
if let Some(list) = children.find_tree(cst, Kind::AnnotationArgList) {
for arg in list
.children
.get(cst)
.iter()
.filter_map(|c| c.tree(cst))
.filter(|n| n.kind == Kind::AnnotationArg)
{
match self.try_resolve_arg(arg) {
Ok(Some(argument)) => {
let name = &self.text[argument.name()];
if arguments
.iter()
.any(|a: &Argument| &self.text[a.name()] == name)
{
errors
.push(AnnotationResolveError::DuplicateArgument { span: arg.span });
} else {
arguments.push(argument);
}
}
Ok(None) => {}
Err(error) => errors.push(error),
}
}
}
if !errors.is_empty() {
return Err(errors);
}
Ok(Annotation::new(node.span, name, arguments))
}
fn try_resolve_arg(&self, arg: &Node) -> Result<Option<Argument>, AnnotationResolveError> {
let cst = self.cst;
let children = arg.children.get(cst);
let Some(name) = children
.first()
.and_then(|c| c.token(cst))
.filter(|t| t.kind == TokenKind::Name)
else {
return Ok(None);
};
let has_eq = children
.get(1)
.and_then(|c| c.token(cst))
.is_some_and(|t| t.kind == TokenKind::Eq);
if !has_eq {
return Ok(Some(Argument::Flag(name.span)));
}
let token = children
.get(2)
.and_then(|c| c.token(cst))
.ok_or(AnnotationResolveError::MissingArgValue { span: arg.span })?;
let value = Value::eval(self.text, token, Some(ExpectedType::Any), None)?;
Ok(Some(Argument::Named {
name: name.span,
value,
}))
}
}