From a3c267da37631a9063af3dfa7799268888208dca Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Sun, 12 Nov 2023 09:52:59 +0900 Subject: [PATCH 1/9] feat: add kw-var-args --- crates/erg_compiler/codegen.rs | 2 +- crates/erg_compiler/declare.rs | 33 +++++++++++++++++++++---- crates/erg_compiler/hir.rs | 41 ++++++++++++++++++++++--------- crates/erg_compiler/lower.rs | 24 +++++++++++++++++- crates/erg_compiler/ownercheck.rs | 8 +++++- crates/erg_parser/ast.rs | 38 ++++++++++++++++++++++------ crates/erg_parser/convert.rs | 34 ++++++++++++++++--------- crates/erg_parser/desugar.rs | 9 ++++--- crates/erg_parser/lex.rs | 11 +++++++-- crates/erg_parser/parse.rs | 38 +++++++++++++++++++--------- crates/erg_parser/typespec.rs | 14 ++++++++--- 11 files changed, 194 insertions(+), 58 deletions(-) diff --git a/crates/erg_compiler/codegen.rs b/crates/erg_compiler/codegen.rs index 32bb8f890..2e899357c 100644 --- a/crates/erg_compiler/codegen.rs +++ b/crates/erg_compiler/codegen.rs @@ -3432,7 +3432,7 @@ impl PyCodeGenerator { "?".into(), ); let param = NonDefaultParamSignature::new(raw, vi, None); - let params = Params::new(vec![self_param, param], None, vec![], vec![], None); + let params = Params::new(vec![self_param, param], None, vec![], None, vec![], None); (param_name, params) } else { ("_".into(), Params::single(self_param)) diff --git a/crates/erg_compiler/declare.rs b/crates/erg_compiler/declare.rs index 8ef78aa90..800c18952 100644 --- a/crates/erg_compiler/declare.rs +++ b/crates/erg_compiler/declare.rs @@ -215,7 +215,7 @@ impl GenericASTLowerer { } fn fake_lower_args(&self, args: ast::Args) -> LowerResult { - let (pos_args_, var_args_, kw_args_, paren) = args.deconstruct(); + let (pos_args_, var_args_, kw_args_, kw_var_, paren) = args.deconstruct(); let mut pos_args = vec![]; for arg in pos_args_.into_iter() { let arg = self.fake_lower_expr(arg.expr)?; @@ -233,7 +233,14 @@ impl GenericASTLowerer { let expr = self.fake_lower_expr(kw_arg.expr)?; kw_args.push(hir::KwArg::new(kw_arg.keyword, expr)); } - let args = hir::Args::new(pos_args, var_args, kw_args, paren); + let kw_var = match kw_var_ { + Some(kw_var) => { + let kw_var = self.fake_lower_expr(kw_var.expr)?; + Some(hir::PosArg::new(kw_var)) + } + None => None, + }; + let args = hir::Args::new(pos_args, var_args, kw_args, kw_var, paren); Ok(args) } @@ -288,7 +295,7 @@ impl GenericASTLowerer { let elem = self.fake_lower_expr(elem.expr)?; elems.push(hir::PosArg::new(elem)); } - let elems = hir::Args::new(elems, None, vec![], None); + let elems = hir::Args::new(elems, None, vec![], None, None); let t = array_t(Type::Failure, TyParam::value(elems.len())); Ok(hir::Array::Normal(hir::NormalArray::new( arr.l_sqbr, arr.r_sqbr, t, elems, @@ -307,7 +314,7 @@ impl GenericASTLowerer { match tup { ast::Tuple::Normal(tup) => { let mut elems = Vec::new(); - let (elems_, _, _, paren) = tup.elems.deconstruct(); + let (elems_, _, _, _, paren) = tup.elems.deconstruct(); for elem in elems_.into_iter() { let elem = self.fake_lower_expr(elem.expr)?; elems.push(hir::PosArg::new(elem)); @@ -446,7 +453,8 @@ impl GenericASTLowerer { } fn fake_lower_params(&self, params: ast::Params) -> LowerResult { - let (non_defaults_, var_params_, defaults_, guards_, parens) = params.deconstruct(); + let (non_defaults_, var_params_, defaults_, kw_var_, guards_, parens) = + params.deconstruct(); let mut non_defaults = vec![]; for non_default_ in non_defaults_.into_iter() { let t_spec_as_expr = non_default_ @@ -492,6 +500,20 @@ impl GenericASTLowerer { let default = hir::DefaultParamSignature::new(sig, default_val); defaults.push(default); } + let kw_var = if let Some(kw_var) = kw_var_ { + let t_spec_as_expr = kw_var + .t_spec + .as_ref() + .map(|t_spec| self.fake_lower_expr(*t_spec.t_spec_as_expr.clone())) + .transpose()?; + Some(Box::new(hir::NonDefaultParamSignature::new( + *kw_var, + VarInfo::default(), + t_spec_as_expr, + ))) + } else { + None + }; let mut guards = vec![]; for guard in guards_.into_iter() { let guard = match guard { @@ -506,6 +528,7 @@ impl GenericASTLowerer { non_defaults, var_params, defaults, + kw_var, guards, parens, )) diff --git a/crates/erg_compiler/hir.rs b/crates/erg_compiler/hir.rs index 3adc8ab42..a5494a50b 100644 --- a/crates/erg_compiler/hir.rs +++ b/crates/erg_compiler/hir.rs @@ -170,6 +170,7 @@ pub struct Args { pub pos_args: Vec, pub var_args: Option>, pub kw_args: Vec, + pub kw_var: Option>, pub paren: Option<(Token, Token)>, } @@ -179,12 +180,16 @@ impl NestedDisplay for Args { fmt_lines(self.pos_args.iter(), f, level)?; } if let Some(var_args) = &self.var_args { - writeln!(f, "*")?; + write!(f, "*")?; var_args.fmt_nest(f, level)?; } if !self.kw_args.is_empty() { fmt_lines(self.kw_args.iter(), f, level)?; } + if let Some(kw_var) = &self.kw_var { + write!(f, "**")?; + kw_var.fmt_nest(f, level)?; + } Ok(()) } } @@ -200,7 +205,7 @@ impl NoTypeDisplay for Args { .fold("".to_string(), |acc, s| acc + &s + ", "); } if let Some(var_args) = &self.var_args { - s += &format!(", ...{}", var_args.to_string_notype()); + s += &format!(", *{}", var_args.to_string_notype()); } if !self.kw_args.is_empty() { s += &self @@ -209,6 +214,9 @@ impl NoTypeDisplay for Args { .map(|x| x.to_string_notype()) .fold("".to_string(), |acc, s| acc + &s + ", "); } + if let Some(kw_var) = &self.kw_var { + s += &format!(", **{}", kw_var.to_string_notype()); + } s } } @@ -245,12 +253,14 @@ impl Args { pos_args: Vec, var_args: Option, kw_args: Vec, + kw_var: Option, paren: Option<(Token, Token)>, ) -> Self { Self { pos_args, var_args: var_args.map(Box::new), kw_args, + kw_var: kw_var.map(Box::new), paren, } } @@ -264,11 +274,11 @@ impl Args { } pub fn pos_only(pos_args: Vec, paren: Option<(Token, Token)>) -> Self { - Self::new(pos_args, None, vec![], paren) + Self::new(pos_args, None, vec![], None, paren) } pub fn empty() -> Self { - Self::new(vec![], None, vec![], None) + Self::new(vec![], None, vec![], None, None) } #[inline] @@ -1812,6 +1822,7 @@ pub struct Params { pub non_defaults: Vec, pub var_params: Option>, pub defaults: Vec, + pub kw_var_params: Option>, pub guards: Vec, pub parens: Option<(Token, Token)>, } @@ -1820,10 +1831,11 @@ impl fmt::Display for Params { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "({}, {}, {})", + "({}, {}{}{})", fmt_vec(&self.non_defaults), - fmt_option!(pre "*", &self.var_params), + fmt_option!("*", &self.var_params, ", "), fmt_vec(&self.defaults), + fmt_option!(pre ", **", &self.kw_var_params), )?; if !self.guards.is_empty() { write!(f, " if {}", fmt_vec_split_with(&self.guards, " and ")) @@ -1836,13 +1848,14 @@ impl fmt::Display for Params { impl NoTypeDisplay for Params { fn to_string_notype(&self) -> String { format!( - "({}, {}, {})", + "({}, {}{}{})", fmt_vec(&self.non_defaults), - fmt_option!(pre "*", &self.var_params), + fmt_option!("*", &self.var_params, ", "), self.defaults .iter() .map(|p| p.to_string_notype()) - .fold("".to_string(), |acc, e| acc + &e + ", ") + .fold("".to_string(), |acc, e| acc + &e + ", "), + fmt_option!(pre ", **", &self.kw_var_params), ) } } @@ -1875,6 +1888,7 @@ type RawParams = ( Vec, Option>, Vec, + Option>, Option<(Token, Token)>, ); @@ -1882,6 +1896,7 @@ type RefRawParams<'a> = ( &'a Vec, &'a Option>, &'a Vec, + &'a Option>, &'a Option<(Token, Token)>, ); @@ -1890,6 +1905,7 @@ impl Params { non_defaults: Vec, var_params: Option>, defaults: Vec, + kw_var_params: Option>, guards: Vec, parens: Option<(Token, Token)>, ) -> Self { @@ -1897,17 +1913,18 @@ impl Params { non_defaults, var_params, defaults, + kw_var_params, guards, parens, } } pub fn empty() -> Self { - Self::new(vec![], None, vec![], vec![], None) + Self::new(vec![], None, vec![], None, vec![], None) } pub fn single(sig: NonDefaultParamSignature) -> Self { - Self::new(vec![sig], None, vec![], vec![], None) + Self::new(vec![sig], None, vec![], None, vec![], None) } pub const fn ref_deconstruct(&self) -> RefRawParams { @@ -1915,6 +1932,7 @@ impl Params { &self.non_defaults, &self.var_params, &self.defaults, + &self.kw_var_params, &self.parens, ) } @@ -1931,6 +1949,7 @@ impl Params { self.non_defaults, self.var_params, self.defaults, + self.kw_var_params, self.parens, ) } diff --git a/crates/erg_compiler/lower.rs b/crates/erg_compiler/lower.rs index 6e77ce2d8..951d9eaf1 100644 --- a/crates/erg_compiler/lower.rs +++ b/crates/erg_compiler/lower.rs @@ -1131,11 +1131,12 @@ impl GenericASTLowerer { expect: Option<&SubrType>, errs: &mut LowerErrors, ) -> hir::Args { - let (pos_args, var_args, kw_args, paren) = args.deconstruct(); + let (pos_args, var_args, kw_args, kw_var, paren) = args.deconstruct(); let mut hir_args = hir::Args::new( Vec::with_capacity(pos_args.len()), None, Vec::with_capacity(kw_args.len()), + None, paren, ); let pos_params = expect @@ -1204,6 +1205,16 @@ impl GenericASTLowerer { } } } + if let Some(kw_var) = kw_var { + match self.lower_expr(kw_var.expr, None) { + Ok(expr) => hir_args.kw_var = Some(Box::new(hir::PosArg::new(expr))), + Err(es) => { + errs.extend(es); + let dummy = hir::Expr::Dummy(hir::Dummy::empty()); + hir_args.kw_var = Some(Box::new(hir::PosArg::new(dummy))); + } + } + } hir_args } @@ -1523,10 +1534,21 @@ impl GenericASTLowerer { Err(es) => errs.extend(es), } } + let hir_kw_var_params = match params.kw_var_params { + Some(kw_var_params) => match self.lower_non_default_param(*kw_var_params) { + Ok(sig) => Some(Box::new(sig)), + Err(es) => { + errs.extend(es); + None + } + }, + None => None, + }; let mut hir_params = hir::Params::new( hir_non_defaults, hir_var_params, hir_defaults, + hir_kw_var_params, vec![], params.parens, ); diff --git a/crates/erg_compiler/ownercheck.rs b/crates/erg_compiler/ownercheck.rs index 6e7019775..8c596e04a 100644 --- a/crates/erg_compiler/ownercheck.rs +++ b/crates/erg_compiler/ownercheck.rs @@ -107,7 +107,8 @@ impl OwnershipChecker { self.dict .insert(Str::from(self.full_path()), LocalVars::default()); if let Signature::Subr(subr) = &def.sig { - let (nd_params, var_params, d_params, _) = subr.params.ref_deconstruct(); + let (nd_params, var_params, d_params, kw_var, _) = + subr.params.ref_deconstruct(); for param in nd_params { if let ParamPattern::VarName(name) = ¶m.raw.pat { self.define_param(name); @@ -123,6 +124,11 @@ impl OwnershipChecker { self.define_param(name); } } + if let Some(kw_var) = kw_var { + if let ParamPattern::VarName(name) = &kw_var.raw.pat { + self.define_param(name); + } + } } self.check_block(&def.body.block); self.path_stack.pop(); diff --git a/crates/erg_parser/ast.rs b/crates/erg_parser/ast.rs index 1a3ca3744..7fad8f3b2 100644 --- a/crates/erg_parser/ast.rs +++ b/crates/erg_parser/ast.rs @@ -175,6 +175,7 @@ pub struct Args { pos_args: Vec, pub(crate) var_args: Option>, kw_args: Vec, + pub(crate) kw_var_args: Option>, // these are for ELS pub paren: Option<(Token, Token)>, } @@ -213,18 +214,20 @@ impl Args { pos_args: Vec, var_args: Option, kw_args: Vec, + kw_var_args: Option, paren: Option<(Token, Token)>, ) -> Self { Self { pos_args, var_args: var_args.map(Box::new), kw_args, + kw_var_args: kw_var_args.map(Box::new), paren, } } pub fn pos_only(pos_arg: Vec, paren: Option<(Token, Token)>) -> Self { - Self::new(pos_arg, None, vec![], paren) + Self::new(pos_arg, None, vec![], None, paren) } pub fn single(pos_args: PosArg) -> Self { @@ -232,7 +235,7 @@ impl Args { } pub fn empty() -> Self { - Self::new(vec![], None, vec![], None) + Self::new(vec![], None, vec![], None, None) } // for replacing to hir::Args @@ -243,12 +246,14 @@ impl Args { Vec, Option, Vec, + Option, Option<(Token, Token)>, ) { ( self.pos_args, self.var_args.map(|x| *x), self.kw_args, + self.kw_var_args.map(|x| *x), self.paren, ) } @@ -313,6 +318,10 @@ impl Args { self.kw_args.push(arg); } + pub fn set_kw_var(&mut self, arg: PosArg) { + self.kw_var_args = Some(Box::new(arg)); + } + pub fn set_parens(&mut self, paren: (Token, Token)) { self.paren = Some(paren); } @@ -2332,6 +2341,7 @@ pub struct ConstArgs { pos_args: Vec, pub var_args: Option>, kw_args: Vec, + pub kw_var: Option>, paren: Option<(Token, Token)>, } @@ -2366,18 +2376,20 @@ impl ConstArgs { pos_args: Vec, var_args: Option, kw_args: Vec, + kw_var: Option, paren: Option<(Token, Token)>, ) -> Self { Self { pos_args, var_args: var_args.map(Box::new), kw_args, + kw_var: kw_var.map(Box::new), paren, } } pub fn pos_only(pos_args: Vec, paren: Option<(Token, Token)>) -> Self { - Self::new(pos_args, None, vec![], paren) + Self::new(pos_args, None, vec![], None, paren) } pub fn single(expr: ConstExpr) -> Self { @@ -2391,18 +2403,20 @@ impl ConstArgs { Vec, Option, Vec, + Option, Option<(Token, Token)>, ) { ( self.pos_args, self.var_args.map(|x| *x), self.kw_args, + self.kw_var.map(|x| *x), self.paren, ) } pub fn empty() -> Self { - Self::new(vec![], None, vec![], None) + Self::new(vec![], None, vec![], None, None) } pub fn is_empty(&self) -> bool { @@ -2435,7 +2449,7 @@ impl ConstArgs { } pub fn downgrade(self) -> Args { - let (pos_args, var_args, kw_args, paren) = self.deconstruct(); + let (pos_args, var_args, kw_args, kw_var, paren) = self.deconstruct(); Args::new( pos_args .into_iter() @@ -2447,6 +2461,7 @@ impl ConstArgs { // TODO t_spec .map(|arg| KwArg::new(arg.keyword, None, arg.expr.downgrade())) .collect(), + kw_var.map(|arg| PosArg::new(arg.expr.downgrade())), paren, ) } @@ -2994,6 +3009,7 @@ impl TypeSpec { None, vec![], None, + None, )) } @@ -4225,6 +4241,7 @@ pub struct Params { pub non_defaults: Vec, pub var_params: Option>, pub defaults: Vec, + pub kw_var_params: Option>, /// match conditions pub guards: Vec, pub parens: Option<(Token, Token)>, @@ -4239,6 +4256,9 @@ impl fmt::Display for Params { if !self.defaults.is_empty() { write!(f, ", {}", fmt_vec(&self.defaults))?; } + if let Some(kw_var_params) = &self.kw_var_params { + write!(f, ", **{kw_var_params}")?; + } if !self.guards.is_empty() { write!(f, " if ")?; } @@ -4280,6 +4300,7 @@ type RawParams = ( Vec, Option>, Vec, + Option>, Vec, Option<(Token, Token)>, ); @@ -4289,19 +4310,21 @@ impl Params { non_defaults: Vec, var_params: Option, defaults: Vec, + kw_var_params: Option, parens: Option<(Token, Token)>, ) -> Self { Self { non_defaults, var_params: var_params.map(Box::new), defaults, + kw_var_params: kw_var_params.map(Box::new), guards: Vec::new(), parens, } } pub fn single(non_default: NonDefaultParamSignature) -> Self { - Self::new(vec![non_default], None, vec![], None) + Self::new(vec![non_default], None, vec![], None, None) } pub fn deconstruct(self) -> RawParams { @@ -4309,6 +4332,7 @@ impl Params { self.non_defaults, self.var_params, self.defaults, + self.kw_var_params, self.guards, self.parens, ) @@ -4471,7 +4495,7 @@ impl LambdaSignature { pub fn do_sig(do_symbol: &Token) -> Self { let parens = Some((do_symbol.clone(), do_symbol.clone())); Self::new( - Params::new(vec![], None, vec![], parens), + Params::new(vec![], None, vec![], None, parens), None, TypeBoundSpecs::empty(), ) diff --git a/crates/erg_parser/convert.rs b/crates/erg_parser/convert.rs index 6126d27e7..976247ffa 100644 --- a/crates/erg_parser/convert.rs +++ b/crates/erg_parser/convert.rs @@ -215,7 +215,7 @@ impl Parser { let mut vars = Vars::empty(); match tuple { Tuple::Normal(tup) => { - let (pos_args, _var_args, _kw_args, paren) = tup.elems.deconstruct(); + let (pos_args, _var_args, _kw_args, _kw_var, paren) = tup.elems.deconstruct(); for arg in pos_args { let sig = self .convert_rhs_to_sig(arg.expr) @@ -333,7 +333,7 @@ impl Parser { return Ok(TypeBoundSpecs::empty()); }; let mut bounds = vec![]; - let (pos_args, _var_args, _kw_args, _paren) = args.deconstruct(); + let (pos_args, _var_args, _kw_args, _kw_var, _paren) = args.deconstruct(); for arg in pos_args.into_iter() { let bound = self .convert_type_arg_to_bound(arg) @@ -377,8 +377,8 @@ impl Parser { pub(crate) fn convert_args_to_params(&mut self, args: Args) -> ParseResult { debug_call_info!(self); - let (pos_args, var_args, kw_args, parens) = args.deconstruct(); - let mut params = Params::new(vec![], None, vec![], parens); + let (pos_args, var_args, kw_args, kw_var, parens) = args.deconstruct(); + let mut params = Params::new(vec![], None, vec![], None, parens); for (i, arg) in pos_args.into_iter().enumerate() { let nd_param = self .convert_pos_arg_to_non_default_param(arg, i == 0) @@ -386,10 +386,10 @@ impl Parser { params.non_defaults.push(nd_param); } if let Some(var_args) = var_args { - let var_args = self + let var_params = self .convert_pos_arg_to_non_default_param(var_args, false) .map_err(|_| self.stack_dec(fn_name!()))?; - params.var_params = Some(Box::new(var_args)); + params.var_params = Some(Box::new(var_params)); } // TODO: varargs for arg in kw_args.into_iter() { @@ -398,6 +398,12 @@ impl Parser { .map_err(|_| self.stack_dec(fn_name!()))?; params.defaults.push(d_param); } + if let Some(kw_var) = kw_var { + let kw_var_params = self + .convert_pos_arg_to_non_default_param(kw_var, false) + .map_err(|_| self.stack_dec(fn_name!()))?; + params.kw_var_params = Some(Box::new(kw_var_params)); + } debug_exit_info!(self); Ok(params) } @@ -539,7 +545,7 @@ impl Parser { for arg in arr.elems.into_iters().0 { params.push(self.convert_pos_arg_to_non_default_param(arg, false)?); } - let params = Params::new(params, None, vec![], None); + let params = Params::new(params, None, vec![], None, None); debug_exit_info!(self); Ok(ParamArrayPattern::new(arr.l_sqbr, params, arr.r_sqbr)) } @@ -616,7 +622,7 @@ impl Parser { match tuple { Tuple::Normal(tup) => { let mut params = vec![]; - let (elems, var_args, _, parens) = tup.elems.deconstruct(); + let (elems, var_args, _, _, parens) = tup.elems.deconstruct(); for arg in elems.into_iter() { params.push(self.convert_pos_arg_to_non_default_param(arg, false)?); } @@ -626,7 +632,7 @@ impl Parser { } else { None }; - let params = Params::new(params, var_params, vec![], parens); + let params = Params::new(params, var_params, vec![], None, parens); debug_exit_info!(self); Ok(ParamTuplePattern::new(params)) } @@ -768,8 +774,8 @@ impl Parser { debug_call_info!(self); match tuple { Tuple::Normal(tup) => { - let (pos_args, var_args, kw_args, paren) = tup.elems.deconstruct(); - let mut params = Params::new(vec![], None, vec![], paren); + let (pos_args, var_args, kw_args, kw_var, paren) = tup.elems.deconstruct(); + let mut params = Params::new(vec![], None, vec![], None, paren); for (i, arg) in pos_args.into_iter().enumerate() { let param = self .convert_pos_arg_to_non_default_param(arg, i == 0) @@ -788,6 +794,12 @@ impl Parser { .map_err(|_| self.stack_dec(fn_name!()))?; params.defaults.push(param); } + if let Some(kw_var) = kw_var { + let param = self + .convert_pos_arg_to_non_default_param(kw_var, false) + .map_err(|_| self.stack_dec(fn_name!()))?; + params.kw_var_params = Some(Box::new(param)); + } debug_exit_info!(self); Ok(params) } diff --git a/crates/erg_parser/desugar.rs b/crates/erg_parser/desugar.rs index ee3d7ea68..879aabfb4 100644 --- a/crates/erg_parser/desugar.rs +++ b/crates/erg_parser/desugar.rs @@ -100,7 +100,7 @@ impl Desugarer { } fn desugar_args(mut desugar: impl FnMut(Expr) -> Expr, args: Args) -> Args { - let (pos_args, var_args, kw_args, paren) = args.deconstruct(); + let (pos_args, var_args, kw_args, ke_var, paren) = args.deconstruct(); let pos_args = pos_args .into_iter() .map(|arg| PosArg::new(desugar(arg.expr))) @@ -112,7 +112,8 @@ impl Desugarer { KwArg::new(arg.keyword, arg.t_spec, desugar(arg.expr)) // TODO: t_spec }) .collect(); - Args::new(pos_args, var_args, kw_args, paren) + let kw_var_args = ke_var.map(|arg| PosArg::new(desugar(arg.expr))); + Args::new(pos_args, var_args, kw_args, kw_var_args, paren) } fn perform_desugar_acc(mut desugar: impl FnMut(Expr) -> Expr, acc: Accessor) -> Accessor { @@ -220,7 +221,7 @@ impl Desugarer { }, Expr::Tuple(tuple) => match tuple { Tuple::Normal(tup) => { - let (elems, _, _, paren) = tup.elems.deconstruct(); + let (elems, _, _, _, paren) = tup.elems.deconstruct(); let elems = elems .into_iter() .map(|elem| PosArg::new(desugar(elem.expr))) @@ -512,7 +513,7 @@ impl Desugarer { _ => unreachable!(), } } - Params::new(params, None, vec![], None) + Params::new(params, None, vec![], None, None) } Expr::Accessor(Accessor::Ident(ident)) => { let param_name = ident.inspect(); diff --git a/crates/erg_parser/lex.rs b/crates/erg_parser/lex.rs index 5481ed9a6..9824fddba 100644 --- a/crates/erg_parser/lex.rs +++ b/crates/erg_parser/lex.rs @@ -1416,10 +1416,17 @@ impl Iterator for Lexer /*<'a>*/ { } }, Some('*') => match self.peek_cur_ch() { - // TODO: infix/prefix Some('*') => { self.consume(); - self.accept(Pow, "**") + let kind = match self.op_fix() { + Some(OpFix::Infix) => Pow, + Some(OpFix::Prefix) => PreDblStar, + _ => { + let token = self.emit_token(Illegal, "*"); + return Some(Err(LexError::simple_syntax_error(0, token.loc()))); + } + }; + self.accept(kind, "**") } _ => { let kind = match self.op_fix() { diff --git a/crates/erg_parser/parse.rs b/crates/erg_parser/parse.rs index 3bcabc7b7..53018c3bf 100644 --- a/crates/erg_parser/parse.rs +++ b/crates/erg_parser/parse.rs @@ -131,6 +131,7 @@ enum ArgKind { Pos(PosArg), Var(PosArg), Kw(KwArg), + KwVar(PosArg), } pub enum ArrayInner { @@ -1093,8 +1094,9 @@ impl Parser { .map_err(|_| self.stack_dec(fn_name!()))? { ArgKind::Pos(arg) => Args::single(arg), - ArgKind::Var(arg) => Args::new(vec![], Some(arg), vec![], None), - ArgKind::Kw(arg) => Args::new(vec![], None, vec![arg], None), + ArgKind::Var(arg) => Args::new(vec![], Some(arg), vec![], None, None), + ArgKind::Kw(arg) => Args::new(vec![], None, vec![arg], None, None), + ArgKind::KwVar(arg) => Args::new(vec![], None, vec![], Some(arg), None), }; loop { match self.peek_kind() { @@ -1157,6 +1159,9 @@ impl Parser { ArgKind::Kw(arg) => { args.push_kw(arg); } + ArgKind::KwVar(arg) => { + args.set_kw_var(arg); + } } } } @@ -1166,8 +1171,8 @@ impl Parser { args.set_parens((lp, rp)); } else { // e.g. f(g 1) - let (pos_args, var_args, kw_args, _) = args.deconstruct(); - args = Args::new(pos_args, var_args, kw_args, None); + let (pos_args, var_args, kw_args, kw_var, _) = args.deconstruct(); + args = Args::new(pos_args, var_args, kw_args, kw_var, None); } break; } @@ -1225,6 +1230,9 @@ impl Parser { ArgKind::Kw(arg) => { args.push_kw(arg); } + ArgKind::KwVar(arg) => { + args.set_kw_var(arg); + } } } } @@ -1277,7 +1285,7 @@ impl Parser { debug_exit_info!(self); Ok(ArgKind::Kw(KwArg::new(kw, None, expr))) } else { - let expr = self + let expr: Expr = self .try_reduce_expr(false, in_type_args, false, false) .map_err(|_| { if let Some(err) = self.errs.last_mut() { @@ -1340,7 +1348,7 @@ impl Parser { } } } - Some(PreStar) => { + Some(star @ (PreStar | PreDblStar)) => { self.skip(); let expr = self .try_reduce_expr(false, in_type_args, false, false) @@ -1356,7 +1364,11 @@ impl Parser { self.stack_dec(fn_name!()) })?; debug_exit_info!(self); - Ok(ArgKind::Var(PosArg::new(expr))) + if star == PreStar { + Ok(ArgKind::Var(PosArg::new(expr))) + } else { + Ok(ArgKind::KwVar(PosArg::new(expr))) + } } Some(_) => { let expr = self @@ -2993,7 +3005,7 @@ impl Parser { return Ok(BraceContainer::Set(Set::Comprehension(comp))); } Some(RBrace) => { - let arg = Args::new(vec![PosArg::new(other)], None, vec![], None); + let arg = Args::new(vec![PosArg::new(other)], None, vec![], None, None); let r_brace = self.lpop(); debug_exit_info!(self); return Ok(BraceContainer::Set(Set::Normal(NormalSet::new( @@ -3325,7 +3337,7 @@ impl Parser { } } }, - ArgKind::Var(var) => { + ArgKind::Var(var) | ArgKind::KwVar(var) => { let err = ParseError::simple_syntax_error(line!() as usize, var.loc()); self.errs.push(err); debug_exit_info!(self); @@ -3375,8 +3387,9 @@ impl Parser { debug_call_info!(self); let mut args = match first_elem { ArgKind::Pos(pos) => Args::single(pos), - ArgKind::Var(var) => Args::new(vec![], Some(var), vec![], None), - ArgKind::Kw(kw) => Args::new(vec![], None, vec![kw], None), + ArgKind::Var(var) => Args::new(vec![], Some(var), vec![], None, None), + ArgKind::Kw(kw) => Args::new(vec![], None, vec![kw], None, None), + ArgKind::KwVar(kw_var) => Args::new(vec![], None, vec![], Some(kw_var), None), }; #[allow(clippy::while_let_loop)] loop { @@ -3438,6 +3451,9 @@ impl Parser { ArgKind::Kw(arg) => { args.push_kw(arg); } + ArgKind::KwVar(arg) => { + args.set_kw_var(arg); + } } } Some(_other) => { diff --git a/crates/erg_parser/typespec.rs b/crates/erg_parser/typespec.rs index 43fbed7cf..8fc5b8eae 100644 --- a/crates/erg_parser/typespec.rs +++ b/crates/erg_parser/typespec.rs @@ -110,7 +110,7 @@ impl Parser { }, Expr::Tuple(tuple) => match tuple { Tuple::Normal(tup) => { - let (elems, _, _, paren) = tup.elems.deconstruct(); + let (elems, _, _, _, paren) = tup.elems.deconstruct(); let mut const_elems = vec![]; for elem in elems.into_iter() { let const_expr = Self::validate_const_expr(elem.expr)?; @@ -142,7 +142,7 @@ impl Parser { )); };*/ let attr_name = call.attr_name; - let (pos_args, _, _, paren) = call.args.deconstruct(); + let (pos_args, _, _, _, paren) = call.args.deconstruct(); let mut const_pos_args = vec![]; for elem in pos_args.into_iter() { let const_expr = Self::validate_const_expr(elem.expr)?; @@ -228,7 +228,7 @@ impl Parser { pub(crate) fn call_to_predecl_type_spec(call: Call) -> Result { match *call.obj { Expr::Accessor(Accessor::Ident(ident)) => { - let (_pos_args, _var_args, _kw_args, paren) = call.args.deconstruct(); + let (_pos_args, _var_args, _kw_args, _kw_var, paren) = call.args.deconstruct(); let mut pos_args = vec![]; for arg in _pos_args.into_iter() { let const_expr = Self::validate_const_expr(arg.expr)?; @@ -245,6 +245,12 @@ impl Parser { let const_expr = Self::validate_const_expr(arg.expr)?; kw_args.push(ConstKwArg::new(arg.keyword, const_expr)); } + let kw_var = if let Some(kw_var) = _kw_var { + let const_kw_var = Self::validate_const_expr(kw_var.expr)?; + Some(ConstPosArg::new(const_kw_var)) + } else { + None + }; let acc = if let Some(attr) = call.attr_name { ConstAccessor::attr(ConstExpr::Accessor(ConstAccessor::Local(ident)), attr) } else { @@ -252,7 +258,7 @@ impl Parser { }; Ok(PreDeclTypeSpec::poly( acc, - ConstArgs::new(pos_args, var_args, kw_args, paren), + ConstArgs::new(pos_args, var_args, kw_args, kw_var, paren), )) } other => { From 4407999c194109e15f9366daf7a71d6ecb8ee837 Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Sun, 12 Nov 2023 12:22:48 +0900 Subject: [PATCH 2/9] feat: add SubrType::kw_var_params --- crates/erg_compiler/context/eval.rs | 28 ++++++ crates/erg_compiler/context/generalize.rs | 10 +++ .../context/initialize/classes.rs | 86 ++++++++++--------- .../erg_compiler/context/initialize/funcs.rs | 75 ++++++---------- crates/erg_compiler/context/initialize/mod.rs | 3 +- .../erg_compiler/context/initialize/procs.rs | 16 ++-- .../erg_compiler/context/initialize/traits.rs | 5 +- crates/erg_compiler/context/inquire.rs | 18 ++-- crates/erg_compiler/context/instantiate.rs | 6 ++ .../erg_compiler/context/instantiate_spec.rs | 64 ++++++++++++-- crates/erg_compiler/context/register.rs | 28 +++++- crates/erg_compiler/lower.rs | 9 ++ crates/erg_compiler/ownercheck.rs | 42 ++++++--- crates/erg_compiler/ty/const_subr.rs | 2 + crates/erg_compiler/ty/constructors.rs | 75 +++++++++++++--- crates/erg_compiler/ty/mod.rs | 43 +++++++++- crates/erg_compiler/ty/typaram.rs | 3 + crates/erg_parser/ast.rs | 9 +- crates/erg_parser/typespec.rs | 15 ++++ 19 files changed, 388 insertions(+), 149 deletions(-) diff --git a/crates/erg_compiler/context/eval.rs b/crates/erg_compiler/context/eval.rs index d27a288b2..45cee6e90 100644 --- a/crates/erg_compiler/context/eval.rs +++ b/crates/erg_compiler/context/eval.rs @@ -941,6 +941,24 @@ impl Context { } } } + let kw_var_params = if let Some(p) = lambda.sig.params.kw_var_params.as_ref() { + match self.instantiate_param_ty( + p, + None, + &mut tmp_tv_cache, + RegistrationMode::Normal, + ParamKind::KwVarParams, + false, + ) { + Ok(pt) => Some(pt), + Err((pt, err)) => { + errs.extend(err); + Some(pt) + } + } + } else { + None + }; // HACK: should avoid cloning let mut lambda_ctx = Context::instant( Str::ever(""), @@ -955,6 +973,7 @@ impl Context { non_default_params.clone(), var_params, default_params.clone(), + kw_var_params, return_t, ); let block = @@ -1600,12 +1619,20 @@ impl Context { Err((_, errs)) => return Err((Subr(subr), errs)), }; } + if let Some(kw_var_args) = subr.kw_var_params.as_mut() { + *kw_var_args.typ_mut() = + match self.eval_t_params(mem::take(kw_var_args.typ_mut()), level, t_loc) { + Ok(t) => t, + Err((_, errs)) => return Err((Subr(subr), errs)), + }; + } match self.eval_t_params(*subr.return_t, level, t_loc) { Ok(return_t) => Ok(subr_t( subr.kind, subr.non_default_params, subr.var_params.map(|v| *v), subr.default_params, + subr.kw_var_params.map(|v| *v), return_t, )), Err((_, errs)) => { @@ -1614,6 +1641,7 @@ impl Context { subr.non_default_params, subr.var_params.map(|v| *v), subr.default_params, + subr.kw_var_params.map(|v| *v), Failure, ); Err((subr, errs)) diff --git a/crates/erg_compiler/context/generalize.rs b/crates/erg_compiler/context/generalize.rs index 94414de1f..cd521d94b 100644 --- a/crates/erg_compiler/context/generalize.rs +++ b/crates/erg_compiler/context/generalize.rs @@ -99,6 +99,9 @@ impl Generalizer { .into_iter() .map(|pt| pt.map_type(|t| self.generalize_t(t, uninit))) .collect::>(); + let kw_var_params = lambda + .kw_var_params + .map(|pt| pt.map_type(|t| self.generalize_t(t, uninit))); let body = lambda .body .into_iter() @@ -109,6 +112,7 @@ impl Generalizer { nd_params, var_params, d_params, + kw_var_params, body, )) } @@ -213,6 +217,7 @@ impl Generalizer { subr.non_default_params, subr.var_params.map(|x| *x), subr.default_params, + subr.kw_var_params.map(|x| *x), return_t, ) } @@ -496,6 +501,10 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> { .into_iter() .map(|pt| pt.try_map_type(|t| self.deref_tyvar(t))) .collect::>()?; + let kw_var_params = lambda + .kw_var_params + .map(|pt| pt.try_map_type(|t| self.deref_tyvar(t))) + .transpose()?; let body = lambda .body .into_iter() @@ -506,6 +515,7 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> { nd_params, var_params, d_params, + kw_var_params, body, ))) } diff --git a/crates/erg_compiler/context/initialize/classes.rs b/crates/erg_compiler/context/initialize/classes.rs index 915d053d6..796de6a7a 100644 --- a/crates/erg_compiler/context/initialize/classes.rs +++ b/crates/erg_compiler/context/initialize/classes.rs @@ -90,12 +90,7 @@ impl Context { Visibility::BUILTIN_PUBLIC, Some(FUNC_CONJUGATE), ); - let t = func( - vec![], - None, - vec![kw(REAL, Float), kw(IMAG, Float)], - Complex, - ); + let t = no_var_func(vec![], vec![kw(REAL, Float), kw(IMAG, Float)], Complex); complex.register_builtin_py_impl( FUNDAMENTAL_CALL, t, @@ -153,6 +148,7 @@ impl Context { vec![kw(KW_OTHER, Float)], None, vec![kw(KW_EPSILON, Float)], + None, Bool, ); float.register_builtin_erg_impl( @@ -442,7 +438,7 @@ impl Context { 38, ); int.register_py_builtin(FUNC_BIT_COUNT, fn0_met(Int, Nat), Some(FUNC_BIT_COUNT), 27); - let t_from_bytes = func( + let t_from_bytes = no_var_func( vec![kw( BYTES, or( @@ -450,7 +446,6 @@ impl Context { array_t(Type::from(value(0)..=value(255)), TyParam::erased(Nat)), ), )], - None, vec![kw( FUNC_BYTEORDER, v_enum( @@ -460,9 +455,8 @@ impl Context { Int, ); int.register_py_builtin(FUNC_FROM_BYTES, t_from_bytes, Some(FUNC_FROM_BYTES), 40); - let t_to_bytes = func( + let t_to_bytes = no_var_func( vec![kw(KW_SELF, Int)], - None, vec![ kw(KW_LENGTH, Nat), kw( @@ -475,7 +469,7 @@ impl Context { mono(BYTES), ); int.register_py_builtin(FUNC_TO_BYTES, t_to_bytes, Some(FUNC_TO_BYTES), 55); - let t_call = func(vec![pos(Obj)], None, vec![kw("base", Nat)], Int); + let t_call = no_var_func(vec![pos(Obj)], vec![kw("base", Nat)], Int); int.register_builtin_erg_impl( FUNDAMENTAL_CALL, t_call, @@ -614,7 +608,7 @@ impl Context { Some(FUNC_TIMES), 13, ); - let t_call = func(vec![pos(Obj)], None, vec![kw("base", Nat)], Nat); + let t_call = no_var_func(vec![pos(Obj)], vec![kw("base", Nat)], Nat); nat.register_builtin_erg_impl( FUNDAMENTAL_CALL, t_call, @@ -763,6 +757,7 @@ impl Context { vec![kw(KW_PAT, Str), kw(KW_INTO, Str)], None, vec![], + None, Str, ), Immutable, @@ -775,6 +770,7 @@ impl Context { vec![], None, vec![kw(KW_ENCODING, Str), kw(KW_ERRORS, Str)], + None, mono(BYTES), ), Some(FUNC_ENCODE), @@ -782,25 +778,25 @@ impl Context { ); str_.register_builtin_erg_impl( FUNC_FORMAT, - fn_met(Str, vec![], Some(kw(KW_ARGS, Obj)), vec![], Str), + fn_met(Str, vec![], Some(kw(KW_ARGS, Obj)), vec![], None, Str), Immutable, Visibility::BUILTIN_PUBLIC, ); str_.register_builtin_erg_impl( FUNC_LOWER, - fn_met(Str, vec![], None, vec![], Str), + fn_met(Str, vec![], None, vec![], None, Str), Immutable, Visibility::BUILTIN_PUBLIC, ); str_.register_builtin_erg_impl( FUNC_UPPER, - fn_met(Str, vec![], None, vec![], Str), + fn_met(Str, vec![], None, vec![], None, Str), Immutable, Visibility::BUILTIN_PUBLIC, ); str_.register_builtin_erg_impl( FUNC_TO_INT, - fn_met(Str, vec![], None, vec![], or(Int, NoneType)), + fn_met(Str, vec![], None, vec![], None, or(Int, NoneType)), Immutable, Visibility::BUILTIN_PUBLIC, ); @@ -824,6 +820,7 @@ impl Context { vec![kw(KW_SEP, Str)], None, vec![kw(KW_MAXSPLIT, Nat)], + None, unknown_len_array_t(Str), ), Immutable, @@ -837,6 +834,7 @@ impl Context { vec![], None, vec![kw(KW_KEEPENDS, Bool)], + None, unknown_len_array_t(Str), ), Immutable, @@ -857,6 +855,7 @@ impl Context { vec![kw(KW_SUB, Str)], None, vec![kw(KW_START, Nat), kw(KW_END, Nat)], + None, or(Nat, Never), ), Some(FUNC_INDEX), @@ -869,6 +868,7 @@ impl Context { vec![kw(KW_SUB, Str)], None, vec![kw(KW_START, Nat), kw(KW_END, Nat)], + None, or(Nat, Never), ), Immutable, @@ -882,6 +882,7 @@ impl Context { vec![kw(KW_SUB, Str)], None, vec![kw(KW_START, Nat), kw(KW_END, Nat)], + None, or(Nat, v_enum(set! {(-1).into()})), ), Some(FUNC_FIND), @@ -894,6 +895,7 @@ impl Context { vec![kw(KW_SUB, Str)], None, vec![kw(KW_START, Nat), kw(KW_END, Nat)], + None, or(Nat, v_enum(set! {(-1).into()})), ), Immutable, @@ -907,6 +909,7 @@ impl Context { vec![kw(KW_SUB, Str)], None, vec![kw(KW_START, Nat), kw(KW_END, Nat)], + None, Nat, ), Some(FUNC_COUNT), @@ -926,7 +929,14 @@ impl Context { ); str_.register_builtin_py_impl( FUNC_STRIP, - fn_met(Str, vec![], None, vec![kw(KW_CHARS, Str | NoneType)], Str), + fn_met( + Str, + vec![], + None, + vec![kw(KW_CHARS, Str | NoneType)], + None, + Str, + ), Immutable, Visibility::BUILTIN_PUBLIC, Some(FUNC_STRIP), @@ -1038,7 +1048,7 @@ impl Context { ); str_.register_marker_trait(self, poly(INDEXABLE, vec![ty_tp(Nat), ty_tp(Str)])) .unwrap(); - let t_call = func(vec![], None, vec![kw("object", Obj)], Str); + let t_call = func(vec![], None, vec![kw("object", Obj)], None, Str); str_.register_builtin_erg_impl( FUNDAMENTAL_CALL, t_call, @@ -1415,22 +1425,20 @@ impl Context { .register_marker_trait(self, poly(OUTPUT, vec![ty_tp(T.clone())])) .unwrap(); let arr_t = array_t(T.clone(), N.clone()); - let t = fn_met( + let t = no_var_fn_met( arr_t.clone(), vec![kw(KW_RHS, array_t(T.clone(), M.clone()))], - None, vec![], array_t(T.clone(), N.clone() + M.clone()), ) .quantify(); array_.register_py_builtin(FUNC_CONCAT, t.clone(), Some(OP_ADD), 9); let t_count = - fn_met(arr_t.clone(), vec![kw(KW_X, T.clone())], None, vec![], Nat).quantify(); + no_var_fn_met(arr_t.clone(), vec![kw(KW_X, T.clone())], vec![], Nat).quantify(); array_.register_py_builtin(FUNC_COUNT, t_count, Some(FUNC_COUNT), 17); - let t_get = fn_met( + let t_get = no_var_fn_met( arr_t.clone(), vec![pos(Nat)], - None, vec![ParamTy::kw_default(KW_DEFAULT.into(), U.clone(), NoneType)], or(T.clone(), U.clone()), ) @@ -1451,10 +1459,9 @@ impl Context { ValueObj::builtin_class(out_t), ); array_.register_trait(arr_t.clone(), array_add); - let t = fn_met( + let t = no_var_fn_met( arr_t.clone(), vec![kw(KW_ELEM, T.clone())], - None, vec![], array_t(T.clone(), N.clone() + value(1usize)), ) @@ -1587,10 +1594,9 @@ impl Context { ]), ); array_.register_py_builtin(FUNC_PARTITION, t.quantify(), Some(FUNC_PARTITION), 37); - let t = fn_met( + let t = no_var_fn_met( array_t(T.clone(), TyParam::erased(Nat)), vec![], - None, vec![kw( "same_bucket", or(func2(T.clone(), T.clone(), Bool), NoneType), @@ -1633,10 +1639,9 @@ impl Context { set_.register_superclass(mono(GENERIC_SET), &generic_set); set_.register_marker_trait(self, poly(OUTPUT, vec![ty_tp(T.clone())])) .unwrap(); - let t = fn_met( + let t = no_var_fn_met( set_t.clone(), vec![kw(KW_RHS, set_t.clone())], - None, vec![], set_t.clone(), ) @@ -1833,10 +1838,9 @@ impl Context { ))); dict_.register_builtin_const(FUNC_AS_RECORD, Visibility::BUILTIN_PUBLIC, as_record); let Def = type_q("Default"); - let get_t = fn_met( + let get_t = no_var_fn_met( dict_t.clone(), vec![kw("key", T.clone())], - None, vec![kw_default("default", Def.clone(), NoneType)], or( proj_call(D.clone(), FUNDAMENTAL_GETITEM, vec![ty_tp(T.clone())]), @@ -2261,7 +2265,7 @@ impl Context { Visibility::BUILTIN_PUBLIC, ValueObj::builtin_class(Obj), ); - let f_t = kw(KW_FUNC, func(vec![kw(KW_OLD, Int)], None, vec![], Int)); + let f_t = kw(KW_FUNC, no_var_func(vec![kw(KW_OLD, Int)], vec![], Int)); let t = pr_met( ref_mut(mono(MUTABLE_OBJ), None), vec![f_t], @@ -2285,7 +2289,7 @@ impl Context { Visibility::BUILTIN_PUBLIC, ValueObj::builtin_class(Float), ); - let f_t = kw(KW_FUNC, func(vec![kw(KW_OLD, Float)], None, vec![], Float)); + let f_t = kw(KW_FUNC, no_var_func(vec![kw(KW_OLD, Float)], vec![], Float)); let t = pr_met( ref_mut(mono(MUT_FLOAT), None), vec![f_t], @@ -2310,7 +2314,7 @@ impl Context { Visibility::BUILTIN_PUBLIC, ValueObj::builtin_class(Ratio), ); - let f_t = kw(KW_FUNC, func(vec![kw(KW_OLD, Ratio)], None, vec![], Ratio)); + let f_t = kw(KW_FUNC, no_var_func(vec![kw(KW_OLD, Ratio)], vec![], Ratio)); let t = pr_met( ref_mut(mono(MUT_RATIO), None), vec![f_t], @@ -2351,7 +2355,7 @@ impl Context { Visibility::BUILTIN_PUBLIC, ValueObj::builtin_class(Int), ); - let f_t = kw(KW_FUNC, func(vec![kw(KW_OLD, Int)], None, vec![], Int)); + let f_t = kw(KW_FUNC, no_var_func(vec![kw(KW_OLD, Int)], vec![], Int)); let t = pr_met( ref_mut(mono(MUT_INT), None), vec![f_t], @@ -2377,7 +2381,7 @@ impl Context { Visibility::BUILTIN_PUBLIC, ValueObj::builtin_class(Nat), ); - let f_t = kw(KW_FUNC, func(vec![kw(KW_OLD, Nat)], None, vec![], Nat)); + let f_t = kw(KW_FUNC, no_var_func(vec![kw(KW_OLD, Nat)], vec![], Nat)); let t = pr_met( ref_mut(mono(MUT_NAT), None), vec![f_t], @@ -2403,7 +2407,7 @@ impl Context { Visibility::BUILTIN_PUBLIC, ValueObj::builtin_class(Bool), ); - let f_t = kw(KW_FUNC, func(vec![kw(KW_OLD, Bool)], None, vec![], Bool)); + let f_t = kw(KW_FUNC, no_var_func(vec![kw(KW_OLD, Bool)], vec![], Bool)); let t = pr_met( ref_mut(mono(MUT_BOOL), None), vec![f_t], @@ -2436,7 +2440,7 @@ impl Context { Visibility::BUILTIN_PUBLIC, ValueObj::builtin_class(Str), ); - let f_t = kw(KW_FUNC, func(vec![kw(KW_OLD, Str)], None, vec![], Str)); + let f_t = kw(KW_FUNC, no_var_func(vec![kw(KW_OLD, Str)], vec![], Str)); let t = pr_met( ref_mut(mono(MUT_STR), None), vec![f_t], @@ -2642,7 +2646,7 @@ impl Context { None, vec![kw( KW_KEY, - func(vec![kw(KW_X, T.clone())], None, vec![], mono(ORD)), + no_var_func(vec![kw(KW_X, T.clone())], vec![], mono(ORD)), )], NoneType, ) @@ -2670,7 +2674,7 @@ impl Context { array_mut_.register_py_builtin(PROC_UPDATE_NTH, t_update_nth, Some(FUNC_UPDATE_NTH), 105); let f_t = kw( KW_FUNC, - func(vec![kw(KW_OLD, arr_t.clone())], None, vec![], arr_t.clone()), + no_var_func(vec![kw(KW_OLD, arr_t.clone())], vec![], arr_t.clone()), ); let t = pr_met( ref_mut(array_mut_t.clone(), None), @@ -2885,7 +2889,7 @@ impl Context { ); let f_t = kw( KW_FUNC, - func(vec![kw(KW_OLD, set_t.clone())], None, vec![], set_t.clone()), + no_var_func(vec![kw(KW_OLD, set_t.clone())], vec![], set_t.clone()), ); let t = pr_met( ref_mut(set_mut_t.clone(), None), diff --git a/crates/erg_compiler/context/initialize/funcs.rs b/crates/erg_compiler/context/initialize/funcs.rs index a6d868645..aa00356c2 100644 --- a/crates/erg_compiler/context/initialize/funcs.rs +++ b/crates/erg_compiler/context/initialize/funcs.rs @@ -24,37 +24,28 @@ impl Context { let U = mono_q(TY_U, instanceof(Type)); let Path = mono_q_tp(PATH, instanceof(Str)); let t_abs = nd_func(vec![kw(KW_N, mono(NUM))], None, Nat); - let t_all = func( + let t_all = no_var_func( vec![kw(KW_ITERABLE, poly(ITERABLE, vec![ty_tp(Bool)]))], - None, vec![], Bool, ); - let t_any = func( + let t_any = no_var_func( vec![kw(KW_ITERABLE, poly(ITERABLE, vec![ty_tp(Bool)]))], - None, vec![], Bool, ); let t_ascii = nd_func(vec![kw(KW_OBJECT, Obj)], None, Str); - let t_array = func( + let t_array = no_var_func( vec![], - None, vec![kw(KW_ITERABLE, poly(ITERABLE, vec![ty_tp(T.clone())]))], array_t(T.clone(), TyParam::erased(Nat)), ) .quantify(); - let t_assert = func( - vec![kw(KW_TEST, Bool)], - None, - vec![kw(KW_MSG, Str)], - NoneType, - ); + let t_assert = no_var_func(vec![kw(KW_TEST, Bool)], vec![kw(KW_MSG, Str)], NoneType); let t_bin = nd_func(vec![kw(KW_N, Int)], None, Str); let t_bytes = func0(mono(BYTES)) - & func( + & no_var_func( vec![kw(KW_STR, Str), kw(KW_ENCODING, Str)], - None, vec![kw(KW_ERRORS, Str)], mono(BYTES), ) @@ -64,9 +55,8 @@ impl Context { None, mono(BYTES), ); - let t_bytes_array = func( + let t_bytes_array = no_var_func( vec![], - None, vec![kw(KW_ITERABLE, poly(ITERABLE, vec![ty_tp(Int)]))], mono(BYTEARRAY), ); @@ -89,9 +79,8 @@ impl Context { T.clone(), ) .quantify(); - let t_dict = func( + let t_dict = no_var_func( vec![], - None, vec![kw( KW_ITERABLE, poly(ITERABLE, vec![ty_tp(tuple_t(vec![T.clone(), U.clone()]))]), @@ -100,9 +89,8 @@ impl Context { ) .quantify(); let t_discard = nd_func(vec![kw(KW_OBJ, Obj)], None, NoneType); - let t_enumerate = func( + let t_enumerate = no_var_func( vec![kw(KW_ITERABLE, poly(ITERABLE, vec![ty_tp(T.clone())]))], - None, vec![kw(KW_START, Int)], poly(ENUMERATE, vec![ty_tp(T.clone())]), ) @@ -122,21 +110,19 @@ impl Context { poly(FROZENSET, vec![ty_tp(T.clone())]), ) .quantify(); - let getattr_t = func( + let getattr_t = no_var_func( vec![kw(KW_OBJ, Obj), kw(KW_NAME, Str)], - None, vec![kw_default(KW_DEFAULT, T.clone(), Obj)], T.clone(), ) .quantify(); - let hasattr_t = func(vec![kw(KW_OBJ, Obj), kw(KW_NAME, Str)], None, vec![], Bool); + let hasattr_t = no_var_func(vec![kw(KW_OBJ, Obj), kw(KW_NAME, Str)], vec![], Bool); let t_hash = func1(mono(HASH), Int); - let t_if = func( + let t_if = no_var_func( vec![ kw(KW_COND, Bool), kw(KW_THEN, nd_func(vec![], None, T.clone())), ], - None, vec![kw_default( KW_ELSE, nd_func(vec![], None, U.clone()), @@ -145,7 +131,7 @@ impl Context { or(T.clone(), U.clone()), ) .quantify(); - let t_int = func(vec![kw(KW_OBJ, Obj)], None, vec![kw(KW_BASE, Nat)], Int); + let t_int = no_var_func(vec![kw(KW_OBJ, Obj)], vec![kw(KW_BASE, Nat)], Int); let t_import = nd_func( vec![anon(tp_enum(Str, set! {Path.clone()}))], None, @@ -191,6 +177,7 @@ impl Context { kw(KW_FILE, mono(WRITE)), kw(KW_FLUSH, Bool), ], + None, NoneType, ); let t_map = nd_func( @@ -251,7 +238,7 @@ impl Context { None, Code, ); - let t_quit = func(vec![], None, vec![kw(KW_CODE, Int)], Never); + let t_quit = no_var_func(vec![], vec![kw(KW_CODE, Int)], Never); let t_exit = t_quit.clone(); let t_repr = nd_func(vec![kw(KW_OBJECT, Obj)], None, Str); let t_reversed = nd_func( @@ -261,16 +248,14 @@ impl Context { ) .quantify(); let t_round = nd_func(vec![kw(KW_NUMBER, Float)], None, Int); - let t_set = func( + let t_set = no_var_func( vec![], - None, vec![kw(KW_ITERABLE, poly(ITERABLE, vec![ty_tp(T.clone())]))], set_t(T.clone(), TyParam::erased(Nat)), ) .quantify(); - let t_slice = func( + let t_slice = no_var_func( vec![kw(KW_START, Int)], - None, vec![kw(KW_STOP, Int), kw(KW_STEP, Int)], mono(SLICE), ); @@ -284,9 +269,8 @@ impl Context { let t_str = nd_func(vec![kw(KW_OBJECT, Obj)], None, Str); let A = mono_q(TY_A, Constraint::Uninited); let A = mono_q(TY_A, subtypeof(poly(ADD, vec![ty_tp(A)]))); - let t_sum = func( + let t_sum = no_var_func( vec![kw(KW_ITERABLE, poly(ITERABLE, vec![ty_tp(A.clone())]))], - None, vec![kw_default(KW_START, or(A.clone(), Int), Int)], A, ) @@ -600,17 +584,15 @@ impl Context { } else { Visibility::BUILTIN_PRIVATE }; - let class_t = func( + let class_t = no_var_func( vec![], - None, vec![kw(KW_REQUIREMENT, or(Type, Ellipsis)), kw(KW_IMPL, Type)], ClassType, ); let class = ConstSubr::Builtin(BuiltinConstSubr::new(CLASS, class_func, class_t, None)); self.register_builtin_const(CLASS, vis.clone(), ValueObj::Subr(class)); - let inherit_t = func( + let inherit_t = no_var_func( vec![kw(KW_SUPER, ClassType)], - None, vec![kw(KW_IMPL, Type), kw(KW_ADDITIONAL, Type)], ClassType, ); @@ -621,17 +603,15 @@ impl Context { None, )); self.register_builtin_const(INHERIT, vis.clone(), ValueObj::Subr(inherit)); - let trait_t = func( + let trait_t = no_var_func( vec![kw(KW_REQUIREMENT, Type)], - None, vec![kw(KW_IMPL, Type)], TraitType, ); let trait_ = ConstSubr::Builtin(BuiltinConstSubr::new(TRAIT, trait_func, trait_t, None)); self.register_builtin_const(TRAIT, vis.clone(), ValueObj::Subr(trait_)); - let subsume_t = func( + let subsume_t = no_var_func( vec![kw(KW_SUPER, TraitType)], - None, vec![kw(KW_IMPL, Type), kw(KW_ADDITIONAL, Type)], TraitType, ); @@ -670,9 +650,8 @@ impl Context { // TODO: register Del function object let t_del = nd_func(vec![kw(KW_OBJ, Obj)], None, NoneType); self.register_builtin_erg_impl(DEL, t_del, Immutable, vis.clone()); - let patch_t = func( + let patch_t = no_var_func( vec![kw(KW_REQUIREMENT, Type)], - None, vec![kw(KW_IMPL, Type)], TraitType, ); @@ -681,9 +660,8 @@ impl Context { } pub(super) fn init_builtin_py_specific_funcs(&mut self) { - let setattr_t = func( + let setattr_t = no_var_func( vec![kw(KW_OBJ, Obj), kw(KW_NAME, Str), kw(KW_VALUE, Obj)], - None, vec![], NoneType, ); @@ -694,12 +672,7 @@ impl Context { Visibility::BUILTIN_PUBLIC, None, ); - let delattr_t = func( - vec![kw(KW_OBJ, Obj), kw(KW_NAME, Str)], - None, - vec![], - NoneType, - ); + let delattr_t = no_var_func(vec![kw(KW_OBJ, Obj), kw(KW_NAME, Str)], vec![], NoneType); self.register_builtin_py_impl( FUNC_DELATTR, delattr_t, diff --git a/crates/erg_compiler/context/initialize/mod.rs b/crates/erg_compiler/context/initialize/mod.rs index 4e4b0f1ae..8f65cc4f5 100644 --- a/crates/erg_compiler/context/initialize/mod.rs +++ b/crates/erg_compiler/context/initialize/mod.rs @@ -902,9 +902,8 @@ impl Context { .iter() .filter_map(|ps| ps.has_default().then_some(ParamTy::from(ps))) .collect::>(); - let meta_t = func( + let meta_t = no_var_func( nd_params.clone(), - None, d_params.clone(), v_enum(set! { ret_val }), ) diff --git a/crates/erg_compiler/context/initialize/procs.rs b/crates/erg_compiler/context/initialize/procs.rs index 69b183ff3..4c38d2ff4 100644 --- a/crates/erg_compiler/context/initialize/procs.rs +++ b/crates/erg_compiler/context/initialize/procs.rs @@ -20,9 +20,8 @@ impl Context { }; let T = mono_q("T", instanceof(Type)); let U = mono_q("U", instanceof(Type)); - let t_dir = proc( + let t_dir = no_var_proc( vec![kw("obj", ref_(Obj))], - None, vec![], array_t(Str, TyParam::erased(Nat)), ); @@ -35,16 +34,16 @@ impl Context { kw("file", mono("Writable!")), kw("flush", Bool), ], + None, NoneType, ); let t_id = nd_func(vec![kw("old", Obj)], None, Nat); - let t_input = proc(vec![], None, vec![kw("msg", Str)], Str); - let t_if = proc( + let t_input = no_var_proc(vec![], vec![kw("msg", Str)], Str); + let t_if = no_var_proc( vec![ kw("cond", Bool), kw("then", nd_proc(vec![], None, T.clone())), ], - None, vec![kw_default( "else", nd_proc(vec![], None, U.clone()), @@ -62,8 +61,8 @@ impl Context { NoneType, ) .quantify(); - let t_globals = proc(vec![], None, vec![], dict! { Str => Obj }.into()); - let t_locals = proc(vec![], None, vec![], dict! { Str => Obj }.into()); + let t_globals = no_var_proc(vec![], vec![], dict! { Str => Obj }.into()); + let t_locals = no_var_proc(vec![], vec![], dict! { Str => Obj }.into()); let t_next = nd_proc( vec![kw( "iterable", @@ -88,9 +87,8 @@ impl Context { NoneType, ); let P = mono_q("P", subtypeof(mono("PathLike"))); - let t_open = proc( + let t_open = no_var_proc( vec![kw("file", P)], - None, vec![ kw("mode", Str), kw("buffering", Int), diff --git a/crates/erg_compiler/context/initialize/traits.rs b/crates/erg_compiler/context/initialize/traits.rs index 40f685e2d..edc8857d6 100644 --- a/crates/erg_compiler/context/initialize/traits.rs +++ b/crates/erg_compiler/context/initialize/traits.rs @@ -35,7 +35,7 @@ impl Context { let mut mutable = Self::builtin_mono_trait(MUTABLE, 2); let Slf = mono_q(SELF, subtypeof(mono(IMMUTIZABLE))); let immut_t = proj(Slf.clone(), IMMUT_TYPE); - let f_t = func(vec![kw(KW_OLD, immut_t.clone())], None, vec![], immut_t); + let f_t = no_var_func(vec![kw(KW_OLD, immut_t.clone())], vec![], immut_t); let t = pr1_met(ref_mut(Slf, None), f_t, NoneType).quantify(); mutable.register_builtin_erg_decl(PROC_UPDATE, t, Visibility::BUILTIN_PUBLIC); // REVIEW: Immutatable? @@ -339,14 +339,13 @@ impl Context { Visibility::BUILTIN_PUBLIC, Some(FUNDAMENTAL_ENTER), ); - let t = fn_met( + let t = no_var_fn_met( Slf, vec![ kw(EXC_TYPE, ClassType), kw(EXC_VALUE, Obj), kw(TRACEBACK, Obj), // TODO: ], - None, vec![], NoneType, ) diff --git a/crates/erg_compiler/context/inquire.rs b/crates/erg_compiler/context/inquire.rs index 85fca9bff..e94a38956 100644 --- a/crates/erg_compiler/context/inquire.rs +++ b/crates/erg_compiler/context/inquire.rs @@ -519,9 +519,9 @@ impl Context { let param_ty = ParamTy::Pos(match_target_expr_t.clone()); let param_ts = [vec![param_ty], branch_ts.to_vec()].concat(); let t = if kind.is_func() { - func(param_ts, None, vec![], return_t) + func(param_ts, None, vec![], None, return_t) } else { - proc(param_ts, None, vec![], return_t) + proc(param_ts, None, vec![], None, return_t) }; let vi = VarInfo { t, @@ -985,6 +985,7 @@ impl Context { vec![], Some(ParamTy::Pos(ref_(Obj))), vec![], + Some(ParamTy::Pos(ref_(Obj))), Failure, )), ..VarInfo::default() @@ -1019,6 +1020,7 @@ impl Context { vec![], Some(ParamTy::Pos(ref_(Obj))), vec![], + Some(ParamTy::Pos(ref_(Obj))), Failure, )), ..VarInfo::default() @@ -1057,6 +1059,7 @@ impl Context { .iter() .map(|kw| ParamTy::kw(kw.keyword.content.clone(), kw.expr.t())) .collect(), + None, Obj, ); for ty in intersecs.iter() { @@ -1130,7 +1133,7 @@ impl Context { }) .collect::>(); let return_t = free_var(self.level, Constraint::new_type_of(Type)); - let subr_t = fn_met(obj.t(), nd_params, None, d_params, return_t); + let subr_t = fn_met(obj.t(), nd_params, None, d_params, None, return_t); if let Some(fv) = obj.ref_t().as_free() { if fv.get_sub().is_some() { let vis = self.instantiate_vis_modifier(&attr_name.vis).unwrap(); @@ -1669,7 +1672,7 @@ impl Context { }; let ret_t = free_var(self.level, Constraint::new_type_of(Type)); let non_default_params = pos_args.iter().map(|a| anon(a.expr.t())).collect(); - let subr_t = subr_t(kind, non_default_params, None, vec![], ret_t); + let subr_t = subr_t(kind, non_default_params, None, vec![], None, ret_t); self.occur(&subr_t, instance, obj)?; instance.destructive_link(&subr_t); Ok(SubstituteResult::Ok) @@ -1704,7 +1707,7 @@ impl Context { subr.non_default_params.len() + subr.default_params.len() }; if (params_len < pos_args.len() || params_len < pos_args.len() + kw_args.len()) - && subr.var_params.is_none() + && subr.is_no_var() { return Err( self.gen_too_many_args_error(&callee, subr, is_method, pos_args, kw_args) @@ -2183,6 +2186,8 @@ impl Context { .collect(), ) })?; + } else if let Some(kw_var) = subr_ty.kw_var_params.as_deref() { + self.sub_unify(arg_t, kw_var.typ(), arg, Some(kw_name))?; } else { let similar = levenshtein::get_similar_name(subr_ty.param_names(), arg.keyword.inspect()); @@ -3360,8 +3365,9 @@ impl Context { let subr = SubrType::new( subr.kind, subr.non_default_params.clone(), - subr.var_params.as_ref().map(|p| *p.clone()), + subr.var_params.as_deref().cloned(), subr.default_params.clone(), + subr.kw_var_params.as_deref().cloned(), ret_t, ); Type::Subr(subr) diff --git a/crates/erg_compiler/context/instantiate.rs b/crates/erg_compiler/context/instantiate.rs index 2ca1313a5..6b2d2f7e3 100644 --- a/crates/erg_compiler/context/instantiate.rs +++ b/crates/erg_compiler/context/instantiate.rs @@ -455,6 +455,10 @@ impl Context { .into_iter() .map(|pt| pt.try_map_type(|t| self.instantiate_t_inner(t, tmp_tv_cache, loc))) .collect::>()?; + let kw_var_params = lambda + .kw_var_params + .map(|pt| pt.try_map_type(|t| self.instantiate_t_inner(t, tmp_tv_cache, loc))) + .transpose()?; let body = lambda .body .into_iter() @@ -465,6 +469,7 @@ impl Context { nd_params, var_params, d_params, + kw_var_params, body, ))) } @@ -728,6 +733,7 @@ impl Context { subr.non_default_params, subr.var_params.map(|p| *p), subr.default_params, + subr.kw_var_params.map(|p| *p), return_t, ); Ok(res) diff --git a/crates/erg_compiler/context/instantiate_spec.rs b/crates/erg_compiler/context/instantiate_spec.rs index 3df7bf412..e159b8a3d 100644 --- a/crates/erg_compiler/context/instantiate_spec.rs +++ b/crates/erg_compiler/context/instantiate_spec.rs @@ -65,15 +65,15 @@ pub enum ParamKind { NonDefault, Default(Type), VarParams, - KwParams, + KwVarParams, } impl ParamKind { pub const fn is_var_params(&self) -> bool { matches!(self, ParamKind::VarParams) } - pub const fn is_kw_params(&self) -> bool { - matches!(self, ParamKind::KwParams) + pub const fn is_kw_var_params(&self) -> bool { + matches!(self, ParamKind::KwVarParams) } pub const fn is_default(&self) -> bool { matches!(self, ParamKind::Default(_)) @@ -302,6 +302,28 @@ impl Context { } } } + let kw_var_args = if let Some(kw_var_args) = sig.params.kw_var_params.as_ref() { + let opt_decl_t = opt_decl_sig_t + .as_ref() + .and_then(|subr| subr.kw_var_params.as_ref().map(|v| v.as_ref())); + let pt = match self.instantiate_param_ty( + kw_var_args, + opt_decl_t, + &mut tmp_tv_cache, + mode, + ParamKind::KwVarParams, + false, + ) { + Ok(pt) => pt, + Err((pt, es)) => { + errs.extend(es); + pt + } + }; + Some(pt) + } else { + None + }; let spec_return_t = if let Some(t_spec) = sig.return_t_spec.as_ref() { let opt_decl_t = opt_decl_sig_t .as_ref() @@ -330,9 +352,9 @@ impl Context { }; // tmp_tv_cache.warn_isolated_vars(self); let typ = if sig.ident.is_procedural() { - proc(non_defaults, var_args, defaults, spec_return_t) + proc(non_defaults, var_args, defaults, kw_var_args, spec_return_t) } else { - func(non_defaults, var_args, defaults, spec_return_t) + func(non_defaults, var_args, defaults, kw_var_args, spec_return_t) }; if errs.is_empty() { Ok(typ) @@ -1117,6 +1139,21 @@ impl Context { .map_err(|(_, errs)| errs)?; d_params.push(pt); } + let kw_var_params = if let Some(p) = lambda.sig.params.kw_var_params.as_ref() { + let pt = self + .instantiate_param_ty( + p, + None, + tmp_tv_cache, + RegistrationMode::Normal, + ParamKind::KwVarParams, + not_found_is_qvar, + ) + .map_err(|(_, errs)| errs)?; + Some(pt) + } else { + None + }; let mut body = vec![]; for expr in lambda.body.iter() { let param = @@ -1129,6 +1166,7 @@ impl Context { nd_params, var_params, d_params, + kw_var_params, body, ))) } @@ -1274,6 +1312,7 @@ impl Context { lambda.nd_params, lambda.var_params, lambda.d_params, + lambda.kw_var_params, return_t, ); Ok(Type::Subr(subr)) @@ -1639,6 +1678,20 @@ impl Context { })? .into_iter() .collect(); + let kw_var_params = subr + .kw_var_params + .as_ref() + .map(|p| { + self.instantiate_func_param_spec( + p, + opt_decl_t, + None, + tmp_tv_ctx, + mode, + not_found_is_qvar, + ) + }) + .transpose()?; let return_t = self.instantiate_typespec_full( &subr.return_t, opt_decl_t, @@ -1652,6 +1705,7 @@ impl Context { non_defaults, var_params, defaults, + kw_var_params, return_t, )) } diff --git a/crates/erg_compiler/context/register.rs b/crates/erg_compiler/context/register.rs index 651a5dd40..672dd7698 100644 --- a/crates/erg_compiler/context/register.rs +++ b/crates/erg_compiler/context/register.rs @@ -20,7 +20,8 @@ use ast::{ use erg_parser::ast::{self, ClassAttr, TypeSpecWithOp}; use crate::ty::constructors::{ - free_var, func, func0, func1, proc, ref_, ref_mut, tp_enum, unknown_len_array_t, v_enum, + free_var, func, func0, func1, proc, ref_, ref_mut, str_dict_t, tp_enum, unknown_len_array_t, + v_enum, }; use crate::ty::free::{Constraint, HasLevel}; use crate::ty::typaram::TyParam; @@ -332,7 +333,7 @@ impl Context { Visibility::private(self.name.clone()) }; let default = kind.default_info(); - let is_var_params = kind.is_var_params(); + let is_var_params = kind.is_var_params() || kind.is_kw_var_params(); match &sig.raw.pat { // Literal patterns will be desugared to discard patterns ast::ParamPattern::Lit(_) => unreachable!(), @@ -606,6 +607,20 @@ impl Context { errs.extend(es); } } + if let Some(kw_var_params) = &mut params.kw_var_params { + if let Some(pt) = &subr_t.var_params { + let pt = pt.clone().map_type(str_dict_t); + if let Err(es) = + self.assign_param(kw_var_params, Some(&pt), ParamKind::KwVarParams) + { + errs.extend(es); + } + } else if let Err(es) = + self.assign_param(kw_var_params, None, ParamKind::KwVarParams) + { + errs.extend(es); + } + } } else { for non_default in params.non_defaults.iter_mut() { if let Err(es) = self.assign_param(non_default, None, ParamKind::NonDefault) { @@ -626,6 +641,11 @@ impl Context { errs.extend(es); } } + if let Some(kw_var_params) = &mut params.kw_var_params { + if let Err(es) = self.assign_param(kw_var_params, None, ParamKind::KwVarParams) { + errs.extend(es); + } + } } if errs.is_empty() { Ok(()) @@ -765,6 +785,7 @@ impl Context { subr_t.non_default_params.clone(), subr_t.var_params.as_deref().cloned(), subr_t.default_params.clone(), + subr_t.kw_var_params.as_deref().cloned(), return_t, ) } else { @@ -772,6 +793,7 @@ impl Context { subr_t.non_default_params.clone(), subr_t.var_params.as_deref().cloned(), subr_t.default_params.clone(), + subr_t.kw_var_params.as_deref().cloned(), return_t, ) }; @@ -1953,7 +1975,7 @@ impl Context { )) }) .collect(); - let meta_t = func(params, None, vec![], v_enum(set! { val.clone() })).quantify(); + let meta_t = func(params, None, vec![], None, v_enum(set! { val.clone() })).quantify(); let name = &ident.name; let id = DefId(get_hash(&(&self.name, &name))); let vi = VarInfo::new( diff --git a/crates/erg_compiler/lower.rs b/crates/erg_compiler/lower.rs index 951d9eaf1..4f9813dbd 100644 --- a/crates/erg_compiler/lower.rs +++ b/crates/erg_compiler/lower.rs @@ -1676,6 +1676,13 @@ impl GenericASTLowerer { &default.sig, ); } + if let Some(kw_var_param) = params.kw_var_params.as_deref() { + self.inc_ref( + kw_var_param.inspect().unwrap_or(&"_".into()), + &kw_var_param.vi, + kw_var_param, + ); + } } let (non_default_params, default_params): (Vec<_>, Vec<_>) = self .module @@ -1757,6 +1764,7 @@ impl GenericASTLowerer { non_default_param_tys, var_params, default_param_tys, + None, body.t(), ) } else { @@ -1764,6 +1772,7 @@ impl GenericASTLowerer { non_default_param_tys, var_params, default_param_tys, + None, body.t(), ) }; diff --git a/crates/erg_compiler/ownercheck.rs b/crates/erg_compiler/ownercheck.rs index 8c596e04a..806c20fd5 100644 --- a/crates/erg_compiler/ownercheck.rs +++ b/crates/erg_compiler/ownercheck.rs @@ -161,6 +161,7 @@ impl OwnershipChecker { } else { args_owns.non_defaults.len() }; + let defaults_len = args_owns.defaults.len(); if call.args.pos_args.len() > non_defaults_len { let (non_default_args, var_args) = call.args.pos_args.split_at(non_defaults_len); @@ -180,21 +181,34 @@ impl OwnershipChecker { } } } - for kw_arg in call.args.kw_args.iter() { - if let Some((_, ownership)) = args_owns - .defaults - .iter() - .find(|(k, _)| k == kw_arg.keyword.inspect()) - { - self.check_expr(&kw_arg.expr, *ownership, false); - } else if let Some((_, ownership)) = args_owns - .non_defaults - .iter() - .find(|(k, _)| k.as_ref() == Some(kw_arg.keyword.inspect())) - { - self.check_expr(&kw_arg.expr, *ownership, false); + if call.args.kw_args.len() > defaults_len { + let (default_args, kw_var_args) = call.args.kw_args.split_at(defaults_len); + for kw_arg in default_args.iter() { + if let Some((_, ownership)) = args_owns + .defaults + .iter() + .find(|(k, _)| k == kw_arg.keyword.inspect()) + { + self.check_expr(&kw_arg.expr, *ownership, false); + } else if let Some((_, ownership)) = args_owns + .non_defaults + .iter() + .find(|(k, _)| k.as_ref() == Some(kw_arg.keyword.inspect())) + { + self.check_expr(&kw_arg.expr, *ownership, false); + } else { + todo!() + } + } + if let Some((_, ownership)) = args_owns.kw_var_params.as_ref() { + for var_arg in kw_var_args.iter() { + self.check_expr(&var_arg.expr, *ownership, false); + } } else { - todo!() + let kw_args = kw_var_args; + for (arg, (_, ownership)) in kw_args.iter().zip(args_owns.defaults.iter()) { + self.check_expr(&arg.expr, *ownership, false); + } } } } diff --git a/crates/erg_compiler/ty/const_subr.rs b/crates/erg_compiler/ty/const_subr.rs index 08973b249..73f34aa6f 100644 --- a/crates/erg_compiler/ty/const_subr.rs +++ b/crates/erg_compiler/ty/const_subr.rs @@ -262,11 +262,13 @@ impl ConstSubr { if let Predicate::Equal { rhs, .. } = refine.pred.as_ref() { let return_t = ctx.convert_tp_into_type(rhs.clone()).ok()?; let var_params = subr.var_params.as_ref().map(|t| t.as_ref()); + let kw_var_params = subr.kw_var_params.as_ref().map(|t| t.as_ref()); let subr_t = subr_t( subr.kind, subr.non_default_params.clone(), var_params.cloned(), subr.default_params.clone(), + kw_var_params.cloned(), return_t, ); let subr_t = if subr_t.has_qvar() { diff --git a/crates/erg_compiler/ty/constructors.rs b/crates/erg_compiler/ty/constructors.rs index 4c80b28f8..fbfef7320 100644 --- a/crates/erg_compiler/ty/constructors.rs +++ b/crates/erg_compiler/ty/constructors.rs @@ -1,5 +1,6 @@ use std::convert::TryInto; +use erg_common::dict; use erg_common::fresh::FRESH_GEN; use crate::ty::*; @@ -55,6 +56,10 @@ pub fn unknown_len_array_mut(elem_t: Type) -> Type { array_mut(elem_t, TyParam::erased(Type::Nat)) } +pub fn str_dict_t(value: Type) -> Type { + dict! { Type::Str => value }.into() +} + /// `UnsizedArray` is a type of `[x; _]` (unsized array literal). /// `UnsizedArray(T) != Array(T, _)` pub fn unsized_array_t(elem_t: Type) -> Type { @@ -226,6 +231,7 @@ pub fn subr_t( non_default_params: Vec, var_params: Option, default_params: Vec, + kw_var_params: Option, return_t: Type, ) -> Type { Type::Subr(SubrType::new( @@ -233,6 +239,7 @@ pub fn subr_t( non_default_params, var_params, default_params, + kw_var_params, return_t, )) } @@ -241,6 +248,7 @@ pub fn func( non_default_params: Vec, var_params: Option, default_params: Vec, + kw_var_params: Option, return_t: Type, ) -> Type { Type::Subr(SubrType::new( @@ -248,16 +256,25 @@ pub fn func( non_default_params, var_params, default_params, + kw_var_params, return_t, )) } +pub fn no_var_func( + non_default_params: Vec, + default_params: Vec, + return_t: Type, +) -> Type { + func(non_default_params, None, default_params, None, return_t) +} + pub fn func0(return_t: Type) -> Type { - func(vec![], None, vec![], return_t) + func(vec![], None, vec![], None, return_t) } pub fn func1(param_t: Type, return_t: Type) -> Type { - func(vec![ParamTy::Pos(param_t)], None, vec![], return_t) + func(vec![ParamTy::Pos(param_t)], None, vec![], None, return_t) } pub fn kind1(param: Type) -> Type { @@ -269,6 +286,7 @@ pub fn func2(l: Type, r: Type, return_t: Type) -> Type { vec![ParamTy::Pos(l), ParamTy::Pos(r)], None, vec![], + None, return_t, ) } @@ -288,6 +306,7 @@ pub fn proc( non_default_params: Vec, var_params: Option, default_params: Vec, + kw_var_params: Option, return_t: Type, ) -> Type { Type::Subr(SubrType::new( @@ -295,16 +314,25 @@ pub fn proc( non_default_params, var_params, default_params, + kw_var_params, return_t, )) } +pub fn no_var_proc( + non_default_params: Vec, + default_params: Vec, + return_t: Type, +) -> Type { + proc(non_default_params, None, default_params, None, return_t) +} + pub fn proc0(return_t: Type) -> Type { - proc(vec![], None, vec![], return_t) + proc(vec![], None, vec![], None, return_t) } pub fn proc1(param_t: Type, return_t: Type) -> Type { - proc(vec![ParamTy::Pos(param_t)], None, vec![], return_t) + proc(vec![ParamTy::Pos(param_t)], None, vec![], None, return_t) } pub fn proc2(l: Type, r: Type, return_t: Type) -> Type { @@ -312,6 +340,7 @@ pub fn proc2(l: Type, r: Type, return_t: Type) -> Type { vec![ParamTy::Pos(l), ParamTy::Pos(r)], None, vec![], + None, return_t, ) } @@ -321,6 +350,7 @@ pub fn fn_met( mut non_default_params: Vec, var_params: Option, default_params: Vec, + kw_var_params: Option, return_t: Type, ) -> Type { non_default_params.insert(0, ParamTy::kw(Str::ever("self"), self_t)); @@ -329,20 +359,44 @@ pub fn fn_met( non_default_params, var_params, default_params, + kw_var_params, return_t, )) } +pub fn no_var_fn_met( + self_t: Type, + non_default_params: Vec, + default_params: Vec, + return_t: Type, +) -> Type { + fn_met( + self_t, + non_default_params, + None, + default_params, + None, + return_t, + ) +} + pub fn fn0_met(self_t: Type, return_t: Type) -> Type { - fn_met(self_t, vec![], None, vec![], return_t) + fn_met(self_t, vec![], None, vec![], None, return_t) } pub fn fn1_met(self_t: Type, input_t: Type, return_t: Type) -> Type { - fn_met(self_t, vec![ParamTy::Pos(input_t)], None, vec![], return_t) + fn_met( + self_t, + vec![ParamTy::Pos(input_t)], + None, + vec![], + None, + return_t, + ) } pub fn fn1_kw_met(self_t: Type, input: ParamTy, return_t: Type) -> Type { - fn_met(self_t, vec![input], None, vec![], return_t) + fn_met(self_t, vec![input], None, vec![], None, return_t) } pub fn pr_met( @@ -358,6 +412,7 @@ pub fn pr_met( non_default_params, var_params, default_params, + None, return_t, )) } @@ -377,17 +432,17 @@ pub fn pr1_kw_met(self_t: Type, input: ParamTy, return_t: Type) -> Type { /// function type with non-default parameters #[inline] pub fn nd_func(params: Vec, var_params: Option, ret: Type) -> Type { - func(params, var_params, vec![], ret) + func(params, var_params, vec![], None, ret) } #[inline] pub fn nd_proc(params: Vec, var_params: Option, ret: Type) -> Type { - proc(params, var_params, vec![], ret) + proc(params, var_params, vec![], None, ret) } #[inline] pub fn d_func(default_params: Vec, return_t: Type) -> Type { - func(vec![], None, default_params, return_t) + func(vec![], None, default_params, None, return_t) } #[inline] diff --git a/crates/erg_compiler/ty/mod.rs b/crates/erg_compiler/ty/mod.rs index a66d733b9..89a58d3be 100644 --- a/crates/erg_compiler/ty/mod.rs +++ b/crates/erg_compiler/ty/mod.rs @@ -259,7 +259,7 @@ pub struct SubrType { pub non_default_params: Vec, pub var_params: Option>, // TODO: need to have a position (var_params can be specified after default_params) pub default_params: Vec, - // var_kw_params: Option<(Str, Box)>, + pub kw_var_params: Option>, pub return_t: Box, } @@ -329,6 +329,16 @@ impl LimitedDisplay for SubrType { write!(f, "{} := ", pt.name().unwrap())?; pt.typ().limited_fmt(f, limit - 1)?; } + if let Some(kw_var_params) = &self.kw_var_params { + if !self.non_default_params.is_empty() || !self.default_params.is_empty() { + write!(f, ", ")?; + } + write!(f, "**")?; + if let Some(name) = kw_var_params.name() { + write!(f, "{}: ", name)?; + } + kw_var_params.typ().limited_fmt(f, limit - 1)?; + } write!(f, ") {} ", self.kind.arrow())?; self.return_t.limited_fmt(f, limit - 1) } @@ -363,7 +373,16 @@ impl StructuralEq for SubrType { .zip(other.var_params.iter()) .all(|(l, r)| l.typ().structural_eq(r.typ())); let return_t_judge = self.return_t.structural_eq(&other.return_t); - non_defaults_judge && var_params_judge && return_t_judge && kw_check() + let kw_var_params_judge = self + .kw_var_params + .iter() + .zip(other.kw_var_params.iter()) + .all(|(l, r)| l.typ().structural_eq(r.typ())); + non_defaults_judge + && var_params_judge + && kw_var_params_judge + && return_t_judge + && kw_check() } } @@ -373,6 +392,7 @@ impl SubrType { non_default_params: Vec, var_params: Option, default_params: Vec, + kw_var_params: Option, return_t: Type, ) -> Self { Self { @@ -380,6 +400,7 @@ impl SubrType { non_default_params, var_params: var_params.map(Box::new), default_params, + kw_var_params: kw_var_params.map(Box::new), return_t: Box::new(return_t), } } @@ -591,6 +612,10 @@ impl SubrType { .chain(self.default_params.iter()) .map(|pt| pt.name().map_or("_", |s| &s[..])) } + + pub fn is_no_var(&self) -> bool { + self.var_params.is_none() && self.kw_var_params.is_none() + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -746,6 +771,7 @@ pub struct ArgsOwnership { pub non_defaults: Vec<(Option, Ownership)>, pub var_params: Option<(Option, Ownership)>, pub defaults: Vec<(Str, Ownership)>, + pub kw_var_params: Option<(Option, Ownership)>, } impl fmt::Display for ArgsOwnership { @@ -782,11 +808,13 @@ impl ArgsOwnership { non_defaults: Vec<(Option, Ownership)>, var_params: Option<(Option, Ownership)>, defaults: Vec<(Str, Ownership)>, + kw_var_params: Option<(Option, Ownership)>, ) -> Self { Self { non_defaults, var_params, defaults, + kw_var_params, } } } @@ -2357,7 +2385,11 @@ impl Type { }; d_args.push((d_param.name().unwrap().clone(), ownership)); } - ArgsOwnership::new(nd_args, var_args, d_args) + let kw_var_args = subr + .kw_var_params + .as_ref() + .map(|t| (t.name().cloned(), t.typ().ownership())); + ArgsOwnership::new(nd_args, var_args, d_args, kw_var_args) } Self::Quantified(quant) => quant.args_ownership(), other => todo!("{other}"), @@ -3364,12 +3396,17 @@ impl Type { .map_type(|t| t.replace(&Self::Failure, &Self::Obj)) }) .collect(); + let kw_var_params = subr.kw_var_params.as_ref().map(|pt| { + pt.clone() + .map_type(|t| t.replace(&Self::Failure, &Self::Obj)) + }); let return_t = subr.return_t.clone().replace(&Self::Failure, &Self::Never); subr_t( subr.kind, non_default_params, var_params, default_params, + kw_var_params, return_t, ) } diff --git a/crates/erg_compiler/ty/typaram.rs b/crates/erg_compiler/ty/typaram.rs index 681ce593e..03bfa6243 100644 --- a/crates/erg_compiler/ty/typaram.rs +++ b/crates/erg_compiler/ty/typaram.rs @@ -179,6 +179,7 @@ pub struct TyParamLambda { pub nd_params: Vec, pub var_params: Option, pub d_params: Vec, + pub kw_var_params: Option, pub body: Vec, } @@ -216,6 +217,7 @@ impl TyParamLambda { nd_params: Vec, var_params: Option, d_params: Vec, + kw_var_params: Option, body: Vec, ) -> Self { Self { @@ -223,6 +225,7 @@ impl TyParamLambda { nd_params, var_params, d_params, + kw_var_params, body, } } diff --git a/crates/erg_parser/ast.rs b/crates/erg_parser/ast.rs index 7fad8f3b2..ee547473b 100644 --- a/crates/erg_parser/ast.rs +++ b/crates/erg_parser/ast.rs @@ -2637,6 +2637,7 @@ pub struct SubrTypeSpec { pub non_defaults: Vec, pub var_params: Option>, pub defaults: Vec, + pub kw_var_params: Option>, pub arrow: Token, pub return_t: Box, } @@ -2648,10 +2649,11 @@ impl fmt::Display for SubrTypeSpec { } write!( f, - "({}, {}, {}) {} {}", + "({}, {}{}{}) {} {}", fmt_vec(&self.non_defaults), - fmt_option!(pre "*", &self.var_params), + fmt_option!("*", &self.var_params, ", "), fmt_vec(&self.defaults), + fmt_option!(pre ", **", &self.kw_var_params), self.arrow.content, self.return_t ) @@ -2677,12 +2679,14 @@ impl Locational for SubrTypeSpec { } impl SubrTypeSpec { + #[allow(clippy::too_many_arguments)] pub fn new( bounds: TypeBoundSpecs, lparen: Option, non_defaults: Vec, var_params: Option, defaults: Vec, + kw_var_params: Option, arrow: Token, return_t: TypeSpec, ) -> Self { @@ -2692,6 +2696,7 @@ impl SubrTypeSpec { non_defaults, var_params: var_params.map(Box::new), defaults, + kw_var_params: kw_var_params.map(Box::new), arrow, return_t: Box::new(return_t), } diff --git a/crates/erg_parser/typespec.rs b/crates/erg_parser/typespec.rs index 8fc5b8eae..f4375283d 100644 --- a/crates/erg_parser/typespec.rs +++ b/crates/erg_parser/typespec.rs @@ -331,6 +331,20 @@ impl Parser { }; defaults.push(param); } + let kw_var_params = lambda.sig.params.kw_var_params.map(|kw_var_args| { + match (kw_var_args.pat, kw_var_args.t_spec) { + (ParamPattern::VarName(name), Some(t_spec_with_op)) => { + ParamTySpec::new(Some(name.into_token()), t_spec_with_op.t_spec) + } + (ParamPattern::VarName(name), None) => ParamTySpec::anonymous(TypeSpec::mono( + Identifier::new(VisModifierSpec::Private, name), + )), + (ParamPattern::Discard(_), Some(t_spec_with_op)) => { + ParamTySpec::anonymous(t_spec_with_op.t_spec) + } + (param, t_spec) => todo!("{param}: {t_spec:?}"), + } + }); let return_t = Self::expr_to_type_spec(lambda.body.remove(0))?; Ok(SubrTypeSpec::new( bounds, @@ -338,6 +352,7 @@ impl Parser { non_defaults, var_params, defaults, + kw_var_params, lambda.op, return_t, )) From cfd24ea6fcc3b3044149713a718fd1d03b5f636b Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Sun, 12 Nov 2023 12:23:32 +0900 Subject: [PATCH 3/9] feat: add `jinja2` type declaration --- .../lib/external/jinja2.d/__init__.d.er | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 crates/erg_compiler/lib/external/jinja2.d/__init__.d.er diff --git a/crates/erg_compiler/lib/external/jinja2.d/__init__.d.er b/crates/erg_compiler/lib/external/jinja2.d/__init__.d.er new file mode 100644 index 000000000..a2f8509c1 --- /dev/null +++ b/crates/erg_compiler/lib/external/jinja2.d/__init__.d.er @@ -0,0 +1,26 @@ +.BaseLoader: ClassType + +.PackageLoader: ClassType +.PackageLoader <: .BaseLoader +.PackageLoader. + __call__: (package_name: Str, package_path: Str) -> .PackageLoader + +.FileSystemLoader: ClassType +.FileSystemLoader <: .BaseLoader +.FileSystemLoader. + __call__: (path: Str) -> .FileSystemLoader + +.DictLoader: ClassType +.DictLoader <: .BaseLoader +.DictLoader. + __call__: (mapping: {Str: Str}) -> .DictLoader + +.Template: ClassType +.Template. + __call__: (source: Str, autoescape := Bool, enable_async := Bool) -> .Template + render: (self: Ref(.Template), **kwargs: Obj) -> Str + +.Enviroment: ClassType +.Enviroment. + __call__: (loader := .BaseLoader, trim_blocks := Bool) -> .Enviroment + get_template: (self: Ref(.Enviroment), name := Str) -> .Template From 6d12e09b2938836328a6fbcb908622defff62996 Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Sun, 12 Nov 2023 12:28:57 +0900 Subject: [PATCH 4/9] test: add `external` test --- examples/external.er | 4 ++++ tests/should_ok/external.er | 4 ++++ tests/test.rs | 5 ++++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/examples/external.er b/examples/external.er index 73869eb55..6625feeb4 100644 --- a/examples/external.er +++ b/examples/external.er @@ -1,5 +1,6 @@ time = pyimport "time" tqdm = pyimport "tqdm" +j2 = pyimport "jinja2" for! tqdm.Tqdm!(0..<100), _ => time.sleep! 0.01 @@ -10,3 +11,6 @@ discard plt.plot! 0..<10, [2, 3, 2, 3, 2, 3, 2, 3, 2, 3] discard plt.title! "My Plot" discard plt.xlabel! "X" plt.show!() + +res = j2.Template("Hello {{ name }}!").render(name:="World") +assert res == "Hello World!" diff --git a/tests/should_ok/external.er b/tests/should_ok/external.er index e023750e6..2bd95d7f4 100644 --- a/tests/should_ok/external.er +++ b/tests/should_ok/external.er @@ -1,5 +1,6 @@ time = pyimport "time" tqdm = pyimport "tqdm" +j2 = pyimport "jinja2" for! tqdm.Tqdm!(0..<10), _ => time.sleep! 0.00001 @@ -10,3 +11,6 @@ discard plt.plot! 0..<10, [2, 3, 2, 3, 2, 3, 2, 3, 2, 3] discard plt.title! "My Plot" discard plt.xlabel! "X" # plt.show!() + +res = j2.Template("Hello {{ name }}!").render(name:="World") +assert res == "Hello World!" diff --git a/tests/test.rs b/tests/test.rs index 9aeb80eda..d88b2cbf1 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -124,7 +124,10 @@ fn exec_empty_check() -> Result<(), ()> { #[test] fn exec_external() -> Result<(), ()> { let py_command = opt_which_python().unwrap(); - if module_exists(&py_command, "matplotlib") && module_exists(&py_command, "tqdm") { + if module_exists(&py_command, "matplotlib") + && module_exists(&py_command, "tqdm") + && module_exists(&py_command, "jinja2") + { expect_success("tests/should_ok/external.er", 0) } else { expect_compile_success("tests/should_ok/external.er", 0) From eae025ee204d226bb7a3e4028f72965dd4d16ebc Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Sun, 12 Nov 2023 13:10:28 +0900 Subject: [PATCH 5/9] Update parse.rs --- crates/erg_parser/parse.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/erg_parser/parse.rs b/crates/erg_parser/parse.rs index 53018c3bf..48f7803b5 100644 --- a/crates/erg_parser/parse.rs +++ b/crates/erg_parser/parse.rs @@ -8,6 +8,7 @@ use erg_common::config::ErgConfig; use erg_common::error::Location; use erg_common::io::{Input, InputKind}; use erg_common::set::Set as HashSet; +use erg_common::spawn::exec_new_thread; use erg_common::str::Str; use erg_common::traits::{DequeStream, ExitStatus, Locational, New, Runnable, Stream}; use erg_common::{ @@ -109,9 +110,8 @@ impl Parsable for SimpleParser { let ts = Lexer::from_str(code).lex()?; let mut parser = Parser::new(ts); let mut desugarer = Desugarer::new(); - let artifact = parser - .parse() - .map_err(|iart| iart.map_mod(|module| desugarer.desugar(module)))?; + let res = exec_new_thread(move || parser.parse(), fn_name!()); + let artifact = res.map_err(|iart| iart.map_mod(|module| desugarer.desugar(module)))?; Ok(artifact.map(|module| desugarer.desugar(module))) } } From 540027282a9eaface02824cb046cec417be0b092 Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Sun, 12 Nov 2023 13:18:26 +0900 Subject: [PATCH 6/9] Revert "Update parse.rs" This reverts commit eae025ee204d226bb7a3e4028f72965dd4d16ebc. --- crates/erg_parser/parse.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/erg_parser/parse.rs b/crates/erg_parser/parse.rs index 48f7803b5..53018c3bf 100644 --- a/crates/erg_parser/parse.rs +++ b/crates/erg_parser/parse.rs @@ -8,7 +8,6 @@ use erg_common::config::ErgConfig; use erg_common::error::Location; use erg_common::io::{Input, InputKind}; use erg_common::set::Set as HashSet; -use erg_common::spawn::exec_new_thread; use erg_common::str::Str; use erg_common::traits::{DequeStream, ExitStatus, Locational, New, Runnable, Stream}; use erg_common::{ @@ -110,8 +109,9 @@ impl Parsable for SimpleParser { let ts = Lexer::from_str(code).lex()?; let mut parser = Parser::new(ts); let mut desugarer = Desugarer::new(); - let res = exec_new_thread(move || parser.parse(), fn_name!()); - let artifact = res.map_err(|iart| iart.map_mod(|module| desugarer.desugar(module)))?; + let artifact = parser + .parse() + .map_err(|iart| iart.map_mod(|module| desugarer.desugar(module)))?; Ok(artifact.map(|module| desugarer.desugar(module))) } } From 1b19d8098a892ffb37c7650d4811b74f2ef1f658 Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Sun, 12 Nov 2023 13:20:07 +0900 Subject: [PATCH 7/9] Update test.rs --- .github/workflows/rust.yml | 2 +- crates/els/tests/test.rs | 3 +++ crates/erg_parser/parse.rs | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 79af65785..75df8fca2 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -46,7 +46,7 @@ jobs: python-version: ${{ matrix.python-version }} - uses: Swatinem/rust-cache@v2 - run: rustup update stable - - run: cargo test --all --verbose -- --include-ignored --nocapture + - run: cargo test --features backtrace --all --verbose -- --include-ignored --nocapture build-check: strategy: diff --git a/crates/els/tests/test.rs b/crates/els/tests/test.rs index db757298e..ae015bed7 100644 --- a/crates/els/tests/test.rs +++ b/crates/els/tests/test.rs @@ -130,6 +130,7 @@ fn test_completion_retrigger() -> Result<(), Box> { } #[test] +#[exec_new_thread] fn test_rename() -> Result<(), Box> { let mut client = Server::bind_fake_client(); client.request_initialize()?; @@ -202,6 +203,7 @@ fn test_hover() -> Result<(), Box> { } #[test] +#[exec_new_thread] fn test_references() -> Result<(), Box> { let mut client = Server::bind_fake_client(); client.request_initialize()?; @@ -298,6 +300,7 @@ fn test_inlay_hint() -> Result<(), Box> { } #[test] +#[exec_new_thread] fn test_dependents_check() -> Result<(), Box> { let mut client = Server::bind_fake_client(); client.request_initialize()?; diff --git a/crates/erg_parser/parse.rs b/crates/erg_parser/parse.rs index 53018c3bf..1221757bc 100644 --- a/crates/erg_parser/parse.rs +++ b/crates/erg_parser/parse.rs @@ -1285,7 +1285,7 @@ impl Parser { debug_exit_info!(self); Ok(ArgKind::Kw(KwArg::new(kw, None, expr))) } else { - let expr: Expr = self + let expr = self .try_reduce_expr(false, in_type_args, false, false) .map_err(|_| { if let Some(err) = self.errs.last_mut() { From 88940aa878ffcd9aba6ef6f0dedcc5531359f7ac Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Tue, 14 Nov 2023 01:19:49 +0900 Subject: [PATCH 8/9] Update free.rs --- crates/erg_compiler/ty/free.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/erg_compiler/ty/free.rs b/crates/erg_compiler/ty/free.rs index 5257a4c0a..e7aed6ec7 100644 --- a/crates/erg_compiler/ty/free.rs +++ b/crates/erg_compiler/ty/free.rs @@ -4,7 +4,7 @@ use std::hash::{Hash, Hasher}; use std::mem; use std::sync::atomic::AtomicUsize; -use erg_common::consts::{BACKTRACE_MODE, DEBUG_MODE}; +use erg_common::consts::DEBUG_MODE; use erg_common::shared::Forkable; use erg_common::traits::{LimitedDisplay, StructuralEq}; use erg_common::{addr, Str}; @@ -382,7 +382,7 @@ impl LimitedDisplay for FreeKind { } match self { Self::Linked(t) | Self::UndoableLinked { t, .. } => { - if DEBUG_MODE || BACKTRACE_MODE { + if DEBUG_MODE { write!(f, "(")?; t.limited_fmt(f, limit)?; write!(f, ")") @@ -397,14 +397,14 @@ impl LimitedDisplay for FreeKind { } => { if *lev == GENERIC_LEVEL { write!(f, "{name}")?; - if DEBUG_MODE || BACKTRACE_MODE { + if DEBUG_MODE { write!(f, "(")?; constraint.limited_fmt(f, limit - 1)?; write!(f, ")")?; } } else { write!(f, "?{name}")?; - if DEBUG_MODE || BACKTRACE_MODE { + if DEBUG_MODE { write!(f, "(")?; constraint.limited_fmt(f, limit - 1)?; write!(f, ")")?; @@ -420,14 +420,14 @@ impl LimitedDisplay for FreeKind { } => { if *lev == GENERIC_LEVEL { write!(f, "%{id}")?; - if DEBUG_MODE || BACKTRACE_MODE { + if DEBUG_MODE { write!(f, "(")?; constraint.limited_fmt(f, limit - 1)?; write!(f, ")")?; } } else { write!(f, "?{id}")?; - if DEBUG_MODE || BACKTRACE_MODE { + if DEBUG_MODE { write!(f, "(")?; constraint.limited_fmt(f, limit - 1)?; write!(f, ")")?; From 663a0824ad4e0cbadd6efbe3c3eb0993f46da01d Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Tue, 14 Nov 2023 02:17:47 +0900 Subject: [PATCH 9/9] feat: var-kwargs codegen --- crates/erg_compiler/codegen.rs | 20 +++++++++++++++----- crates/erg_compiler/context/register.rs | 10 +++++----- crates/erg_compiler/ty/codeobj.rs | 3 ++- crates/erg_parser/parse.rs | 10 ++++++++-- tests/should_err/var_kwargs.er | 4 ++++ tests/should_ok/var_kwargs.er | 3 +++ tests/test.rs | 10 ++++++++++ 7 files changed, 47 insertions(+), 13 deletions(-) create mode 100644 tests/should_err/var_kwargs.er create mode 100644 tests/should_ok/var_kwargs.er diff --git a/crates/erg_compiler/codegen.rs b/crates/erg_compiler/codegen.rs index 2e899357c..5a3390495 100644 --- a/crates/erg_compiler/codegen.rs +++ b/crates/erg_compiler/codegen.rs @@ -1026,6 +1026,14 @@ impl PyCodeGenerator { .iter() .map(|p| (p.inspect().map(|s| &s[..]).unwrap_or("_"), &p.sig.vi)), ) + .chain(if let Some(kw_var_args) = ¶ms.kw_var_params { + vec![( + kw_var_args.inspect().map(|s| &s[..]).unwrap_or("_"), + &kw_var_args.vi, + )] + } else { + vec![] + }) .enumerate() .map(|(i, (s, vi))| { if s == "_" { @@ -1405,11 +1413,13 @@ impl PyCodeGenerator { self.stack_dec_n(defaults_len - 1); make_function_flag += MakeFunctionFlags::Defaults as usize; } - let flags = if sig.params.var_params.is_some() { - CodeObjFlags::VarArgs as u32 - } else { - 0 - }; + let mut flags = 0; + if sig.params.var_params.is_some() { + flags += CodeObjFlags::VarArgs as u32; + } + if sig.params.kw_var_params.is_some() { + flags += CodeObjFlags::VarKeywords as u32; + } let code = self.emit_block( body.block, sig.params.guards, diff --git a/crates/erg_compiler/context/register.rs b/crates/erg_compiler/context/register.rs index 672dd7698..2b9518a18 100644 --- a/crates/erg_compiler/context/register.rs +++ b/crates/erg_compiler/context/register.rs @@ -390,16 +390,16 @@ impl Context { opt_decl_t, &mut dummy_tv_cache, Normal, - kind, + kind.clone(), false, ) { Ok(ty) => (ty, TyCheckErrors::empty()), Err((ty, errs)) => (ty, errs), }; - let spec_t = if is_var_params { - unknown_len_array_t(spec_t) - } else { - spec_t + let spec_t = match kind { + ParamKind::VarParams => unknown_len_array_t(spec_t), + ParamKind::KwVarParams => str_dict_t(spec_t), + _ => spec_t, }; if &name.inspect()[..] == "self" { self.type_self_param(&sig.raw.pat, name, &spec_t, &mut errs); diff --git a/crates/erg_compiler/ty/codeobj.rs b/crates/erg_compiler/ty/codeobj.rs index e297ecd03..b9b2241cc 100644 --- a/crates/erg_compiler/ty/codeobj.rs +++ b/crates/erg_compiler/ty/codeobj.rs @@ -280,8 +280,9 @@ impl CodeObj { ) -> Self { let name = name.into(); let var_args_defined = (flags & CodeObjFlags::VarArgs as u32 != 0) as u32; + let kw_var_args_defined = (flags & CodeObjFlags::VarKeywords as u32 != 0) as u32; Self { - argcount: params.len() as u32 - var_args_defined, + argcount: params.len() as u32 - var_args_defined - kw_var_args_defined, posonlyargcount: 0, kwonlyargcount: 0, nlocals: params.len() as u32, diff --git a/crates/erg_parser/parse.rs b/crates/erg_parser/parse.rs index 1221757bc..445996cdf 100644 --- a/crates/erg_parser/parse.rs +++ b/crates/erg_parser/parse.rs @@ -2415,7 +2415,8 @@ impl Parser { debug_exit_info!(self); Ok(call_or_acc) } - Some(t) if t.is(PreStar) => { + Some(t) if t.is(PreStar) || t.is(PreDblStar) => { + let kind = t.kind; let _ = self.lpop(); let expr = self .try_reduce_expr(false, in_type_args, in_brace, false) @@ -2430,8 +2431,13 @@ impl Parser { } self.stack_dec(fn_name!()) })?; + let arg = match kind { + PreStar => ArgKind::Var(PosArg::new(expr)), + PreDblStar => ArgKind::KwVar(PosArg::new(expr)), + _ => switch_unreachable!(), + }; let tuple = self - .try_reduce_nonempty_tuple(ArgKind::Var(PosArg::new(expr)), false) + .try_reduce_nonempty_tuple(arg, false) .map_err(|_| self.stack_dec(fn_name!()))?; debug_exit_info!(self); Ok(Expr::Tuple(tuple)) diff --git a/tests/should_err/var_kwargs.er b/tests/should_err/var_kwargs.er new file mode 100644 index 000000000..8619a3fd2 --- /dev/null +++ b/tests/should_err/var_kwargs.er @@ -0,0 +1,4 @@ +kw_var(**x: Int) = x["::a"] + x["::b"] + +_ = kw_var(a:="1", b:=2) # ERR +_ = kw_var(a:=1, b:="2") # ERR diff --git a/tests/should_ok/var_kwargs.er b/tests/should_ok/var_kwargs.er new file mode 100644 index 000000000..fe93c3e83 --- /dev/null +++ b/tests/should_ok/var_kwargs.er @@ -0,0 +1,3 @@ +kw_var(**x: Int) = x["::a"] + x["::b"] + +assert kw_var(a:=1, b:=2) == 3 diff --git a/tests/test.rs b/tests/test.rs index d88b2cbf1..01e0d73c2 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -410,6 +410,11 @@ fn exec_var_args() -> Result<(), ()> { expect_success("tests/should_ok/var_args.er", 0) } +#[test] +fn exec_var_kwargs() -> Result<(), ()> { + expect_success("tests/should_ok/var_kwargs.er", 0) +} + #[test] fn exec_with() -> Result<(), ()> { expect_success("examples/with.er", 0) @@ -621,6 +626,11 @@ fn exec_var_args_err() -> Result<(), ()> { expect_failure("tests/should_err/var_args.er", 0, 3) } +#[test] +fn exec_var_kwargs_err() -> Result<(), ()> { + expect_failure("tests/should_err/var_kwargs.er", 0, 2) +} + #[test] fn exec_visibility() -> Result<(), ()> { expect_failure("tests/should_err/visibility.er", 2, 7)