use std::collections::HashSet; use ahash::RandomState; use crate::{ ast, error::{LexicalError, LexicalErrorType}, }; 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 parse_params( params: (Vec<ParameterDef>, Vec<ParameterDef>), ) -> Result<ParameterDefs, LexicalError> { 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<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 = 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 { .. }) }