use crate::ast;
use crate::error::{LexicalError, LexicalErrorType};
use rustc_hash::FxHashSet;
pub struct ArgumentList {
pub args: Vec<ast::Expr>,
pub keywords: Vec<ast::Keyword>,
}
type ParameterDefs = (Vec<ast::Arg>, Vec<ast::Arg>, Vec<ast::Expr>);
type ParameterDef = (ast::Arg, Option<ast::Expr>);
pub fn validate_arguments(arguments: ast::Arguments) -> Result<ast::Arguments, LexicalError> {
let mut all_args: Vec<&ast::Located<ast::ArgData>> = vec![];
all_args.extend(arguments.posonlyargs.iter());
all_args.extend(arguments.args.iter());
if let Some(a) = &arguments.vararg {
all_args.push(a);
}
all_args.extend(arguments.kwonlyargs.iter());
if let Some(a) = &arguments.kwarg {
all_args.push(a);
}
let mut all_arg_names = FxHashSet::with_hasher(Default::default());
for arg in all_args {
let arg_name = &arg.node.arg;
if !all_arg_names.insert(arg_name) {
return Err(LexicalError {
error: LexicalErrorType::DuplicateArgumentError(arg_name.to_string()),
location: arg.location,
});
}
}
Ok(arguments)
}
pub fn parse_params(
params: (Vec<ParameterDef>, Vec<ParameterDef>),
) -> Result<ParameterDefs, LexicalError> {
let mut pos_only = Vec::with_capacity(params.0.len());
let mut names = Vec::with_capacity(params.1.len());
let mut defaults = vec![];
let mut try_default = |name: &ast::Arg, default| {
if let Some(default) = default {
defaults.push(default);
} else if !defaults.is_empty() {
return Err(LexicalError {
error: LexicalErrorType::DefaultArgumentError,
location: name.location,
});
}
Ok(())
};
for (name, default) in params.0 {
try_default(&name, default)?;
pos_only.push(name);
}
for (name, default) in params.1 {
try_default(&name, default)?;
names.push(name);
}
Ok((pos_only, names, defaults))
}
type FunctionArgument = (
Option<(ast::Location, ast::Location, Option<String>)>,
ast::Expr,
);
pub fn parse_args(func_args: Vec<FunctionArgument>) -> Result<ArgumentList, LexicalError> {
let mut args = vec![];
let mut keywords = vec![];
let mut keyword_names =
FxHashSet::with_capacity_and_hasher(func_args.len(), Default::default());
let mut double_starred = false;
for (name, value) in func_args {
match name {
Some((start, end, name)) => {
if let Some(keyword_name) = &name {
if keyword_names.contains(keyword_name) {
return Err(LexicalError {
error: LexicalErrorType::DuplicateKeywordArgumentError(
keyword_name.to_string(),
),
location: start,
});
}
keyword_names.insert(keyword_name.clone());
} else {
double_starred = true;
}
keywords.push(ast::Keyword::new(
start,
end,
ast::KeywordData { arg: name, value },
));
}
None => {
if !keywords.is_empty() && !is_starred(&value) {
return Err(LexicalError {
error: LexicalErrorType::PositionalArgumentError,
location: value.location,
});
} else if double_starred {
return Err(LexicalError {
error: LexicalErrorType::UnpackedArgumentError,
location: value.location,
});
}
args.push(value);
}
}
}
Ok(ArgumentList { args, keywords })
}
fn is_starred(exp: &ast::Expr) -> bool {
matches!(exp.node, ast::ExprKind::Starred { .. })
}