mirror of
https://github.com/erg-lang/erg.git
synced 2025-08-04 10:49:54 +00:00
feat: array unpacking
This commit is contained in:
parent
74e89f6d5b
commit
95e675cccd
8 changed files with 153 additions and 26 deletions
|
@ -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 {
|
||||
|
|
|
@ -633,27 +633,11 @@ impl<A: ASTBuildable> GenericASTLowerer<A> {
|
|||
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);
|
||||
|
|
|
@ -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<VarSignature>,
|
||||
pub(crate) starred: Option<Box<VarSignature>>,
|
||||
}
|
||||
|
||||
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<VarSignature>) -> Self {
|
||||
Self { elems }
|
||||
pub fn new(elems: Vec<VarSignature>, starred: Option<VarSignature>) -> Self {
|
||||
Self {
|
||||
elems,
|
||||
starred: starred.map(Box::new),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn empty() -> Self {
|
||||
Self::new(vec![])
|
||||
pub fn empty() -> Self {
|
||||
Self::new(vec![], None)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<Expr>,
|
||||
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)
|
||||
|
|
|
@ -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();
|
||||
|
|
3
tests/should_ok/star_expr.er
Normal file
3
tests/should_ok/star_expr.er
Normal file
|
@ -0,0 +1,3 @@
|
|||
[a, *b] = [1, 2, 3]
|
||||
assert a == 1
|
||||
assert b == [2, 3]
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue