Skip to content

Commit

Permalink
feat: array packing
Browse files Browse the repository at this point in the history
  • Loading branch information
mtshiba committed Mar 16, 2024
1 parent 74e89f6 commit 70ef653
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 26 deletions.
30 changes: 30 additions & 0 deletions crates/erg_compiler/error/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
20 changes: 2 additions & 18 deletions crates/erg_compiler/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
28 changes: 22 additions & 6 deletions crates/erg_parser/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
}

Expand Down Expand Up @@ -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(())
}
}

Expand All @@ -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)
}
}

Expand Down
37 changes: 35 additions & 2 deletions crates/erg_parser/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!()))?;
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down
38 changes: 38 additions & 0 deletions crates/erg_parser/desugar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand Down
18 changes: 18 additions & 0 deletions crates/erg_parser/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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();
Expand Down
3 changes: 3 additions & 0 deletions tests/should_ok/star_expr.er
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[a, *b] = [1, 2, 3]
assert a == 1
assert b == [2, 3]
5 changes: 5 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 70ef653

Please sign in to comment.