use std::{iter, mem};
use crate::{
parser::command_spec::{unchecked::DeriveValue, Attribute},
types::BASE_TYPES,
};
use convert_case::{Case, Casing};
use crate::parser::{
command_spec::{
checked::{
self, CommandSpec, DocIdentifier, DocNamedType, Enumeration, Function, Identifier,
NamedType, Namespace, Structure, Type,
},
unchecked::{
CommandSpec as UncheckedCommandSpec, DocNamedType as UncheckedDocNamedType,
Enumeration as UncheckedEnumeration, Function as UncheckedFunction,
NamedType as UncheckedNamedType, Namespace as UncheckedNamespace,
Structure as UncheckedStructure, Type as UncheckedType,
},
Variant,
},
error::ErrorContext,
lexing::{Token, TokenKind, TokenSpan, TokenStream},
};
use self::error::{ParsingError, SpannedParsingError};
pub mod error;
macro_rules! take_attrs {
($name:expr, $($types:ident),*) => {
$name
.attributes
.into_iter()
.map(|val| {
take_attrs!{@process_val val, $($types),*}
})
.collect::<Result<Vec<_>, _>>()?
};
(@process_val_last $iden:ident) => {
{
let span = $iden.span();
return Err(ParsingError::WrongAttributeInPosition {
specified: $iden,
span,
});
}
};
(@process_val $iden:ident, $val:ident) => {
if let $crate::parser::command_spec::unchecked::Attribute::$val{..} = $iden {
return Ok($iden.into());
};
take_attrs!{@process_val_last $iden}
};
(@process_val $iden:ident, $val:ident, $($other:tt),+ $(,)*) => {
if let $crate::parser::command_spec::unchecked::Attribute::$val{..} = $iden {
return Ok($iden.into());
};
take_attrs!{@process_val $iden, $($other),*}
};
}
struct Parser {
command_spec: UncheckedCommandSpec,
structures: Vec<UncheckedStructure>,
enumerations: Vec<UncheckedEnumeration>,
original_file: String,
}
impl UncheckedCommandSpec {
pub fn process(self, original_file: String) -> Result<CommandSpec, SpannedParsingError> {
let original_file = TokenStream::replace(&original_file).to_string();
let checked = Parser {
command_spec: self,
structures: vec![],
enumerations: vec![],
original_file,
}
.parse()?;
Ok(checked)
}
}
impl Parser {
fn parse(mut self) -> Result<CommandSpec, SpannedParsingError> {
let namespace: UncheckedNamespace =
UncheckedNamespace::from(mem::take(&mut self.command_spec));
let namespace = self.process_namespace(namespace, vec![]).map_err(|err| {
let span = *err.span();
SpannedParsingError {
source: Box::new(err),
context: ErrorContext::from_span(span, &self.original_file),
}
})?;
Ok(namespace.into())
}
fn process_namespace(
&mut self,
namespace: UncheckedNamespace,
previous_namespaces: Vec<Identifier>,
) -> Result<Namespace, ParsingError> {
let namespace_span = namespace.name.span;
let name = match namespace.name.kind {
TokenKind::Identifier(ident) => Identifier {
name: ident,
variant: Variant::Namespace,
},
TokenKind::DefaultTokenKind => Identifier {
name: "<root>".to_owned(),
variant: Variant::RootNamespace,
},
_ => unreachable!("This should never be more than these two enum veriants"),
};
let previous_namespaces = previous_namespaces
.into_iter()
.chain(iter::once(name.clone()))
.collect();
let mut enumerations = vec![];
let mut enumerations_counter = 0;
for enumeration in namespace.enumerations {
enumerations.push(self.process_enumeration(
enumeration,
&name,
namespace_span,
&previous_namespaces,
)?);
enumerations_counter += 1;
}
let mut structures = vec![];
let mut structures_counter = 0;
for structure in namespace.structures {
structures.push(self.process_structure(structure, &previous_namespaces)?);
structures_counter += 1;
}
let mut functions = vec![];
for function in namespace.functions {
functions.push(self.process_function(function, &previous_namespaces)?);
}
let mut namespaces = vec![];
for namespace in namespace.namespaces {
namespaces.push(self.process_namespace(namespace, previous_namespaces.clone())?);
}
(0..structures_counter).for_each(|_| {
self.structures.pop();
});
(0..enumerations_counter).for_each(|_| {
self.enumerations.pop();
});
Ok(Namespace {
name,
functions,
structures,
enumerations,
namespaces,
attributes: take_attrs! {namespace, doc},
})
}
fn process_function(
&mut self,
mut function: UncheckedFunction,
parent_namespaces: &Vec<Identifier>,
) -> Result<Function, ParsingError> {
let identifier = mem::take(&mut function.identifier.kind).to_identifier(Variant::Function);
let mut inputs = vec![];
for input in function.inputs {
inputs.push(self.process_named_type(input, parent_namespaces)?);
}
let output = if let Some(r#type) = function.output {
Some(self.process_type(r#type, parent_namespaces)?)
} else {
None
};
Ok(Function {
identifier,
inputs,
output,
attributes: take_attrs! {function, doc},
})
}
fn process_enumeration(
&mut self,
mut enumeration: UncheckedEnumeration,
namespace_name: &Identifier,
namespace_span: TokenSpan,
parent_namespaces: &Vec<Identifier>,
) -> Result<Enumeration, ParsingError> {
self.enumerations.push(enumeration.clone());
let enum_span = enumeration.identifier.span;
let identifier: Identifier =
mem::take(&mut enumeration.identifier.kind).to_identifier(Variant::Enumeration {
namespace: parent_namespaces.clone(),
});
if &identifier == namespace_name {
return Err(ParsingError::EnumWithNamespaceName {
name: identifier.clone(),
enum_span,
namespace_span,
});
}
if identifier.name == namespace_name.name.to_case(Case::Pascal) {
return Err(ParsingError::EnumWithNamespaceNamePascal {
name: identifier.clone(),
enum_span,
namespace_span,
});
}
let attributes = take_attrs! {enumeration, doc, derive};
let is_error_enum = attributes.iter().any(|attr| match attr {
Attribute::derive(derive_value) => match derive_value {
DeriveValue::Error => true,
},
_ => false,
});
let mut states = vec![];
for mut state in enumeration.states {
states.push({
let ident: Identifier =
mem::take(&mut state.token.kind).to_identifier(Variant::DocNamedType);
let state_attributes: Vec<Attribute> = take_attrs! {state, doc, error};
if is_error_enum {
if !state_attributes.iter().any(|attr| match attr {
Attribute::error(_) => true,
_ => false,
}) {
return Err(ParsingError::EnumWithoutMarkedState {
name: identifier,
variant: ident,
enum_span,
state_span: *state.token.span(),
});
}
}
DocIdentifier {
name: ident.name,
attributes: state_attributes,
variant: Variant::DocNamedType,
}
})
}
if states.is_empty() {
return Err(ParsingError::EnumWithoutVariants {
name: identifier,
enum_span,
});
}
Ok(Enumeration {
identifier,
states,
attributes,
})
}
fn process_structure(
&mut self,
mut structure: UncheckedStructure,
parent_namespaces: &Vec<Identifier>,
) -> Result<Structure, ParsingError> {
self.structures.push(structure.clone());
let identifier: Identifier =
mem::take(&mut structure.identifier.kind).to_identifier(Variant::Structure {
namespace: parent_namespaces.clone(),
});
let mut contents = vec![];
for named_type in structure.contents {
contents.push(self.process_doc_named_type(named_type, parent_namespaces)?);
}
Ok(Structure {
identifier,
contents,
attributes: take_attrs! {structure, doc, derive},
})
}
fn process_named_type(
&mut self,
mut named_type: UncheckedNamedType,
parent_namespaces: &Vec<Identifier>,
) -> Result<NamedType, ParsingError> {
let name: Identifier =
mem::take(&mut named_type.name.kind).to_identifier(Variant::NamedType);
let r#type: Type = self.process_type(named_type.r#type, parent_namespaces)?;
Ok(NamedType { name, r#type })
}
fn process_doc_named_type(
&mut self,
mut doc_named_type: UncheckedDocNamedType,
parent_namespaces: &Vec<Identifier>,
) -> Result<DocNamedType, ParsingError> {
let name: Identifier =
mem::take(&mut doc_named_type.name.kind).to_identifier(Variant::DocNamedType);
let r#type: Type = self.process_type(doc_named_type.r#type, parent_namespaces)?;
Ok(DocNamedType {
name,
r#type,
attributes: take_attrs! {doc_named_type, doc},
})
}
fn process_type(
&mut self,
r#type: UncheckedType,
parent_namespaces: &Vec<Identifier>,
) -> Result<Type, ParsingError> {
match r#type {
UncheckedType::Typical {
identifier,
generic_args,
} => self.process_typical_type(identifier, generic_args, parent_namespaces),
UncheckedType::Function { inputs, output } => {
self.process_function_type(inputs, output, parent_namespaces)
}
}
}
fn process_typical_type(
&mut self,
mut type_identifier: Token,
generic_args: Vec<UncheckedType>,
parent_namespaces: &Vec<Identifier>,
) -> Result<Type, ParsingError> {
fn match_to_vector_struct(matches: Vec<&UncheckedStructure>) -> Vec<Identifier> {
matches
.first()
.expect("This exists")
.namespaces
.iter()
.map(|token| match token.kind() {
TokenKind::Identifier(ident) => Identifier {
name: ident.to_owned(),
variant: Variant::Namespace,
},
_ => unreachable!("This should not be here"),
})
.collect()
}
fn match_to_vector_enum(matches: Vec<&UncheckedEnumeration>) -> Vec<Identifier> {
matches
.first()
.expect("This exists")
.namespaces
.iter()
.map(|token| match token.kind() {
TokenKind::Identifier(ident) => Identifier {
name: ident.to_owned(),
variant: Variant::Namespace,
},
_ => unreachable!("This should not be here"),
})
.collect()
}
let identifier: Identifier =
mem::take(&mut type_identifier.kind).to_identifier(Variant::Void);
let variant: Variant;
let matched_structures: Vec<_> = self
.structures
.iter()
.filter(|r#struct| {
(r#struct.identifier.kind.clone()).to_identifier(Variant::Void) == identifier
})
.collect();
let matched_enumerations: Vec<_> = self
.enumerations
.iter()
.filter(|r#enum| {
(r#enum.identifier.kind.clone()).to_identifier(Variant::Void) == identifier
})
.collect();
if !matched_structures.is_empty() {
if matched_structures.len() != 1 {
return Err(error::ParsingError::StructureCopied {
structure: identifier,
span: *matched_structures
.first()
.expect("Exists")
.identifier
.span(),
});
}
variant = Variant::Structure {
namespace: match_to_vector_struct(matched_structures),
};
} else if !matched_enumerations.is_empty() {
if matched_enumerations.len() != 1 {
return Err(error::ParsingError::EnumerationCopied {
enumeration: identifier,
span: *matched_enumerations
.first()
.expect("Exists")
.identifier
.span(),
});
}
variant = Variant::Enumeration {
namespace: match_to_vector_enum(matched_enumerations),
};
} else if BASE_TYPES
.iter()
.any(|(ident, _)| ident == &identifier.name)
{
variant = match identifier.name.as_str() {
"Result" => Variant::Result {
namespace: parent_namespaces.clone(),
},
"Option" => Variant::Option {
namespace: parent_namespaces.clone(),
},
"Vec" => Variant::Vec {
namespace: parent_namespaces.clone(),
},
_ => Variant::Primitive,
}
} else {
return Err(ParsingError::TypeNotDeclared {
r#type: identifier,
span: type_identifier.span,
});
}
let identifier: Identifier = Identifier {
name: identifier.name,
variant,
};
{
let fitting_types: Vec<&usize> = BASE_TYPES
.iter()
.filter_map(|(ident, generic_number)| {
if ident == &identifier.name {
Some(generic_number)
} else {
None
}
})
.collect();
if !fitting_types.is_empty() {
let min_fitting_type = fitting_types.iter().min().expect("We checked for none");
let max_fitting_type = fitting_types.iter().max().expect("We checked for none");
if !fitting_types
.iter()
.any(|generic_number| *generic_number == &generic_args.len())
{
if generic_args.len() < **min_fitting_type {
return Err(ParsingError::NotEnoughGenericArgs {
expected_min: **min_fitting_type,
got: generic_args.len(),
r#type: identifier,
span: type_identifier.span,
});
} else if generic_args.len() > **max_fitting_type {
return Err(ParsingError::TooManyGenericArgs {
expected_max: **max_fitting_type,
got: generic_args.len(),
r#type: identifier,
span: type_identifier.span,
});
}
}
} else if fitting_types.is_empty() && !generic_args.is_empty() {
return Err(ParsingError::TooManyGenericArgs {
expected_max: 0,
got: generic_args.len(),
r#type: identifier,
span: type_identifier.span,
});
}
}
let mut new_generic_args: Vec<checked::Type> = vec![];
for generic_arg in generic_args {
new_generic_args.push(self.process_type(generic_arg, parent_namespaces)?);
}
Ok(Type::Typical {
identifier,
generic_args: new_generic_args,
})
}
fn process_function_type(
&mut self,
inputs: Vec<UncheckedNamedType>,
output: Option<Box<UncheckedType>>,
parent_namespaces: &Vec<Identifier>,
) -> Result<Type, ParsingError> {
let inputs = inputs
.into_iter()
.map(|input| self.process_named_type(input, parent_namespaces))
.collect::<Result<Vec<NamedType>, ParsingError>>()?;
let mut new_output = None;
if let Some(output) = output {
new_output = Some(Box::new(self.process_type(*output, parent_namespaces)?));
}
Ok(Type::Function {
inputs,
output: new_output,
})
}
}