diff --git a/parser/python.lalrpop b/parser/python.lalrpop index a73d835..70ad18f 100644 --- a/parser/python.lalrpop +++ b/parser/python.lalrpop @@ -6,7 +6,7 @@ use crate::{ ast, error::{LexicalError, LexicalErrorType}, - function::{ArgumentList, parse_args, parse_params}, + function::{ArgumentList, parse_args, parse_params, validate_arguments}, lexer, context::set_context, string::parse_strings, @@ -552,16 +552,20 @@ FuncDef: ast::Stmt = { }; Parameters: ast::Arguments = { - "(" )?> ")" => { - a.unwrap_or_else(|| ast::Arguments { - posonlyargs: vec![], - args: vec![], - vararg: None, - kwonlyargs: vec![], - kw_defaults: vec![], - kwarg: None, - defaults: vec![] - }) + "(" )?> ")" =>? { + let args = validate_arguments( + a.unwrap_or_else(|| ast::Arguments { + posonlyargs: vec![], + args: vec![], + vararg: None, + kwonlyargs: vec![], + kw_defaults: vec![], + kwarg: None, + defaults: vec![] + }) + )?; + + Ok(args) } }; @@ -774,19 +778,22 @@ NamedExpression: ast::Expr = { }; LambdaDef: ast::Expr = { - "lambda" ?> ":" > => { - let p = p.unwrap_or_else(|| { - ast::Arguments { - posonlyargs: vec![], - args: vec![], - vararg: None, - kwonlyargs: vec![], - kw_defaults: vec![], - kwarg: None, - defaults: vec![] + "lambda" ?> ":" > =>? { + let p = validate_arguments( + p.unwrap_or_else(|| { + ast::Arguments { + posonlyargs: vec![], + args: vec![], + vararg: None, + kwonlyargs: vec![], + kw_defaults: vec![], + kwarg: None, + defaults: vec![] + } } - }); - ast::Expr { + ))?; + + Ok(ast::Expr { location, end_location: Some(end_location), custom: (), @@ -794,7 +801,7 @@ LambdaDef: ast::Expr = { args: Box::new(p), body: Box::new(body) } - } + }) } } diff --git a/parser/src/error.rs b/parser/src/error.rs index 47096a5..b9d7cbf 100644 --- a/parser/src/error.rs +++ b/parser/src/error.rs @@ -21,6 +21,7 @@ pub enum LexicalErrorType { TabError, TabsAfterSpaces, DefaultArgumentError, + DuplicateArgumentError, PositionalArgumentError, UnpackedArgumentError, DuplicateKeywordArgumentError, @@ -50,6 +51,9 @@ impl fmt::Display for LexicalErrorType { LexicalErrorType::DefaultArgumentError => { write!(f, "non-default argument follows default argument") } + LexicalErrorType::DuplicateArgumentError => { + write!(f, "duplicate argument in function definition") + } LexicalErrorType::DuplicateKeywordArgumentError => { write!(f, "keyword argument repeated") } diff --git a/parser/src/function.rs b/parser/src/function.rs index 4662113..adbb269 100644 --- a/parser/src/function.rs +++ b/parser/src/function.rs @@ -10,6 +10,39 @@ pub struct ArgumentList { type ParameterDefs = (Vec, Vec, Vec); type ParameterDef = (ast::Arg, Option); +pub fn validate_arguments( + arguments: ast::Arguments +) -> Result { + let mut all_args: Vec<&ast::Located> = 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.clone(); + if !all_arg_names.insert(arg_name) { + return Err(LexicalError { + error: LexicalErrorType::DuplicateArgumentError, + location: arg.location, + }); + } + } + + return Ok(arguments); +} + pub fn parse_params( params: (Vec, Vec), ) -> Result {