From 70ef6534d6cb3153de5e7a28f42938394ac8b566 Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Sat, 16 Mar 2024 13:19:15 +0900 Subject: [PATCH] feat: array packing --- crates/erg_compiler/error/lower.rs | 30 +++++++++++++++++++++++ crates/erg_compiler/lower.rs | 20 ++-------------- crates/erg_parser/ast.rs | 28 +++++++++++++++++----- crates/erg_parser/convert.rs | 37 +++++++++++++++++++++++++++-- crates/erg_parser/desugar.rs | 38 ++++++++++++++++++++++++++++++ crates/erg_parser/parse.rs | 18 ++++++++++++++ tests/should_ok/star_expr.er | 3 +++ tests/test.rs | 5 ++++ 8 files changed, 153 insertions(+), 26 deletions(-) create mode 100644 tests/should_ok/star_expr.er diff --git a/crates/erg_compiler/error/lower.rs b/crates/erg_compiler/error/lower.rs index f4b03e804..60217f568 100644 --- a/crates/erg_compiler/error/lower.rs +++ b/crates/erg_compiler/error/lower.rs @@ -1089,6 +1089,36 @@ impl LowerError { caused_by, ) } + + pub fn set_homogeneity_error( + input: Input, + errno: usize, + loc: Location, + caused_by: String, + ) -> Self { + LowerError::syntax_error( + input, + errno, + loc, + caused_by, + switch_lang!( + "japanese" => "集合の要素は全て同じ型である必要があります", + "simplified_chinese" => "集合元素必须全部是相同类型", + "traditional_chinese" => "集合元素必須全部是相同類型", + "english" => "all elements of a set must be of the same type", + ) + .to_owned(), + Some( + switch_lang!( + "japanese" => "Int or Strなど明示的に型を指定してください", + "simplified_chinese" => "明确指定类型,例如: Int or Str", + "traditional_chinese" => "明確指定類型,例如: Int or Str", + "english" => "please specify the type explicitly, e.g. Int or Str", + ) + .to_owned(), + ), + ) + } } impl LowerWarning { diff --git a/crates/erg_compiler/lower.rs b/crates/erg_compiler/lower.rs index 96acb56b6..a532f11f9 100644 --- a/crates/erg_compiler/lower.rs +++ b/crates/erg_compiler/lower.rs @@ -633,27 +633,11 @@ impl GenericASTLowerer { let elem = self.lower_expr(elem.expr, expect_elem.as_ref())?; union = self.module.context.union(&union, elem.ref_t()); if ERG_MODE && union.is_union_type() { - return Err(LowerErrors::from(LowerError::syntax_error( + return Err(LowerErrors::from(LowerError::set_homogeneity_error( self.cfg.input.clone(), line!() as usize, elem.loc(), - String::from(&self.module.context.name[..]), - switch_lang!( - "japanese" => "集合の要素は全て同じ型である必要があります", - "simplified_chinese" => "集合元素必须全部是相同类型", - "traditional_chinese" => "集合元素必須全部是相同類型", - "english" => "all elements of a set must be of the same type", - ) - .to_owned(), - Some( - switch_lang!( - "japanese" => "Int or Strなど明示的に型を指定してください", - "simplified_chinese" => "明确指定类型,例如: Int or Str", - "traditional_chinese" => "明確指定類型,例如: Int or Str", - "english" => "please specify the type explicitly, e.g. Int or Str", - ) - .to_owned(), - ), + self.module.context.caused_by(), ))); } new_set.push(elem); diff --git a/crates/erg_parser/ast.rs b/crates/erg_parser/ast.rs index 9743cbfab..0d83eabbc 100644 --- a/crates/erg_parser/ast.rs +++ b/crates/erg_parser/ast.rs @@ -335,7 +335,15 @@ impl NestedDisplay for Args { fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result { fmt_lines(self.pos_args.iter(), f, level)?; writeln!(f)?; - fmt_lines(self.kw_args.iter(), f, level) + if let Some(var) = &self.var_args { + writeln!(f, "*{var}")?; + } + fmt_lines(self.kw_args.iter(), f, level)?; + if let Some(var) = &self.kw_var_args { + writeln!(f)?; + write!(f, "**{var}")?; + } + Ok(()) } } @@ -4566,11 +4574,16 @@ impl VarSignature { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Vars { pub(crate) elems: Vec, + pub(crate) starred: Option>, } impl NestedDisplay for Vars { fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { - write!(f, "{}", fmt_vec(&self.elems)) + write!(f, "{}", fmt_vec(&self.elems))?; + if let Some(starred) = &self.starred { + write!(f, ", *{starred}")?; + } + Ok(()) } } @@ -4588,12 +4601,15 @@ impl Locational for Vars { } impl Vars { - pub const fn new(elems: Vec) -> Self { - Self { elems } + pub fn new(elems: Vec, starred: Option) -> Self { + Self { + elems, + starred: starred.map(Box::new), + } } - pub const fn empty() -> Self { - Self::new(vec![]) + pub fn empty() -> Self { + Self::new(vec![], None) } } diff --git a/crates/erg_parser/convert.rs b/crates/erg_parser/convert.rs index 976247ffa..78c9d3a13 100644 --- a/crates/erg_parser/convert.rs +++ b/crates/erg_parser/convert.rs @@ -101,7 +101,7 @@ impl Parser { match array { Array::Normal(arr) => { let mut vars = Vars::empty(); - for elem in arr.elems.into_iters().0 { + for elem in arr.elems.pos_args { let pat = self .convert_rhs_to_sig(elem.expr) .map_err(|_| self.stack_dec(fn_name!()))?; @@ -117,6 +117,22 @@ impl Parser { } } } + if let Some(var) = arr.elems.var_args { + let pat = self + .convert_rhs_to_sig(var.expr) + .map_err(|_| self.stack_dec(fn_name!()))?; + match pat { + Signature::Var(v) => { + vars.starred = Some(Box::new(v)); + } + Signature::Subr(subr) => { + let err = ParseError::simple_syntax_error(line!() as usize, subr.loc()); + self.errs.push(err); + debug_exit_info!(self); + return Err(()); + } + } + } let pat = VarArrayPattern::new(arr.l_sqbr, vars, arr.r_sqbr); debug_exit_info!(self); Ok(pat) @@ -215,7 +231,7 @@ impl Parser { let mut vars = Vars::empty(); match tuple { Tuple::Normal(tup) => { - let (pos_args, _var_args, _kw_args, _kw_var, 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) @@ -233,6 +249,23 @@ impl Parser { } } } + if let Some(var_args) = var_args { + let sig = self + .convert_rhs_to_sig(var_args.expr) + .map_err(|_| self.stack_dec(fn_name!()))?; + match sig { + Signature::Var(var) => { + vars.starred = Some(Box::new(var)); + } + other => { + let err = + ParseError::simple_syntax_error(line!() as usize, other.loc()); + self.errs.push(err); + debug_exit_info!(self); + return Err(()); + } + } + } let tuple = VarTuplePattern::new(paren, vars); debug_exit_info!(self); Ok(tuple) diff --git a/crates/erg_parser/desugar.rs b/crates/erg_parser/desugar.rs index b19ce0bb7..a0b089d10 100644 --- a/crates/erg_parser/desugar.rs +++ b/crates/erg_parser/desugar.rs @@ -713,6 +713,10 @@ impl Desugarer { for (n, elem) in tup.elems.iter().enumerate() { self.desugar_nested_var_pattern(new, elem, &buf_name, BufIndex::Tuple(n)); } + let elems_len = tup.elems.len(); + if let Some(var) = tup.elems.starred.as_ref() { + self.desugar_rest_values(new, var, &buf_name, elems_len); + } } VarPattern::Array(arr) => { let (buf_name, buf_sig) = self.gen_buf_name_and_sig(v.loc(), v.t_spec); @@ -722,6 +726,10 @@ impl Desugarer { for (n, elem) in arr.elems.iter().enumerate() { self.desugar_nested_var_pattern(new, elem, &buf_name, BufIndex::Array(n)); } + let elems_len = arr.elems.len(); + if let Some(var) = arr.elems.starred.as_ref() { + self.desugar_rest_values(new, var, &buf_name, elems_len); + } } VarPattern::Record(rec) => { let (buf_name, buf_sig) = self.gen_buf_name_and_sig(v.loc(), v.t_spec); @@ -961,6 +969,36 @@ impl Desugarer { } } + /// `a, *b = aaa` -> `a = aaa[0]; b = aaa[1..MAX]` + fn desugar_rest_values( + &mut self, + new_module: &mut Vec, + sig: &VarSignature, + buf_name: &str, + elems_len: usize, + ) { + let obj = Expr::local( + buf_name, + sig.ln_begin().unwrap_or(1), + sig.col_begin().unwrap_or(0), + sig.col_end().unwrap_or(0), + ); + let op = Token::from_str(TokenKind::Assign, "="); + let id = DefId(get_hash(&(&obj, buf_name))); + let start = Expr::Literal(Literal::nat(elems_len, sig.ln_begin().unwrap_or(1))); + // FIXME: infinity + let max = 109521666047; // 102*1024*1024*1024-1 but why is this the limit? + let end = Expr::Literal(Literal::nat(max, sig.ln_begin().unwrap_or(1))); + let range = Token::new_with_loc(TokenKind::Closed, "..", sig.loc()); + let acc = obj.subscr( + start.bin_op(range, end).into(), + Token::new_fake(TokenKind::RBrace, "]", 0, 0, 0), + ); + let body = DefBody::new(op, Block::new(vec![Expr::Accessor(acc)]), id); + let starred = Def::new(Signature::Var(sig.clone()), body); + new_module.push(Expr::Def(starred)); + } + /// `{x; y}` -> `{x = x; y = y}` fn desugar_shortened_record(module: Module) -> Module { Self::desugar_all_chunks(module, Self::rec_desugar_shortened_record) diff --git a/crates/erg_parser/parse.rs b/crates/erg_parser/parse.rs index 2c81bb5e1..a1345ac1b 100644 --- a/crates/erg_parser/parse.rs +++ b/crates/erg_parser/parse.rs @@ -893,6 +893,15 @@ impl Parser { debug_exit_info!(self); return Ok(ArrayInner::WithLength(elems.remove_pos(0), len)); } + Some(PreStar) => { + self.lpop(); + let rest = self + .try_reduce_expr(false, false, false, false) + .map_err(|_| self.stack_dec(fn_name!()))?; + elems.set_var_args(PosArg::new(rest)); + debug_exit_info!(self); + return Ok(ArrayInner::Normal(elems)); + } Some(Inclusion) => { self.lpop(); let Expr::Accessor(Accessor::Ident(sym)) = elems.remove_pos(0).expr else { @@ -981,6 +990,14 @@ impl Parser { Some(RParen | RSqBr | RBrace | Dedent) => { break; } + Some(PreStar) => { + self.lpop(); + let rest = self + .try_reduce_expr(false, false, false, false) + .map_err(|_| self.stack_dec(fn_name!()))?; + elems.set_var_args(PosArg::new(rest)); + break; + } _ => {} } elems.push_pos( @@ -2485,6 +2502,7 @@ impl Parser { debug_exit_info!(self); Ok(call_or_acc) } + // REVIEW: correct? Some(t) if t.is(PreStar) || t.is(PreDblStar) => { let kind = t.kind; let _ = self.lpop(); diff --git a/tests/should_ok/star_expr.er b/tests/should_ok/star_expr.er new file mode 100644 index 000000000..088bc9ef1 --- /dev/null +++ b/tests/should_ok/star_expr.er @@ -0,0 +1,3 @@ +[a, *b] = [1, 2, 3] +assert a == 1 +assert b == [2, 3] diff --git a/tests/test.rs b/tests/test.rs index bccbb4349..e55adf2a7 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -374,6 +374,11 @@ fn exec_slice() -> Result<(), ()> { expect_success("tests/should_ok/slice.er", 0) } +#[test] +fn exec_star_expr() -> Result<(), ()> { + expect_success("tests/should_ok/star_expr.er", 0) +} + #[test] fn exec_structural_example() -> Result<(), ()> { expect_success("examples/structural.er", 0)