use ahash::RandomState; use std::collections::HashSet; use crate::ast; use crate::error::{LexicalError, LexicalErrorType}; pub struct ArgumentList { pub args: Vec, pub keywords: Vec, } type ParameterDefs = (Vec, Vec, Vec); type ParameterDef = (ast::Arg, Option); pub fn parse_params( params: (Vec, Vec), ) -> Result { let mut posonly = 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() { // Once we have started with defaults, all remaining arguments must // have defaults return Err(LexicalError { error: LexicalErrorType::DefaultArgumentError, location: name.location, }); } Ok(()) }; for (name, default) in params.0 { try_default(&name, default)?; posonly.push(name); } for (name, default) in params.1 { try_default(&name, default)?; names.push(name); } Ok((posonly, names, defaults)) } type FunctionArgument = (Option<(ast::Location, Option)>, ast::Expr); pub fn parse_args(func_args: Vec) -> Result { let mut args = vec![]; let mut keywords = vec![]; let mut keyword_names = HashSet::with_capacity_and_hasher(func_args.len(), RandomState::new()); for (name, value) in func_args { if let Some((location, name)) = name { if let Some(keyword_name) = &name { if keyword_names.contains(keyword_name) { return Err(LexicalError { error: LexicalErrorType::DuplicateKeywordArgumentError, location, }); } keyword_names.insert(keyword_name.clone()); } keywords.push(ast::Keyword::new( location, ast::KeywordData { arg: name.map(String::into), value: Box::new(value) }, )); } else { // Allow starred args after keyword arguments. if !keywords.is_empty() && !is_starred(&value) { return Err(LexicalError { error: LexicalErrorType::PositionalArgumentError, location: value.location, }); } args.push(value); } } Ok(ArgumentList { args, keywords }) } fn is_starred(exp: &ast::Expr) -> bool { matches!(exp.node, ast::ExprKind::Starred { .. }) }