diff --git a/compiler/erg_parser/ast.rs b/compiler/erg_parser/ast.rs index 7d847f89..2cd705ae 100644 --- a/compiler/erg_parser/ast.rs +++ b/compiler/erg_parser/ast.rs @@ -130,7 +130,7 @@ impl KwArg { pub struct Args { pos_args: Vec, kw_args: Vec, - paren: Option<(Token, Token)>, + pub paren: Option<(Token, Token)>, } impl NestedDisplay for Args { @@ -358,6 +358,10 @@ impl Accessor { Self::Attr(Attribute::new(obj, name)) } + pub fn tuple_attr(obj: Expr, index: Literal) -> Self { + Self::TupleAttr(TupleAttribute::new(obj, index)) + } + pub fn subscr(obj: Expr, index: Expr) -> Self { Self::Subscr(Subscript::new(obj, index)) } @@ -497,8 +501,10 @@ pub struct NormalTuple { } impl NestedDisplay for NormalTuple { - fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { - write!(f, "({})", self.elems) + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { + writeln!(f, "(")?; + self.elems.fmt_nest(f, level + 1)?; + write!(f, "\n{})", " ".repeat(level)) } } @@ -507,9 +513,7 @@ impl_locational!(NormalTuple, elems, elems); impl NormalTuple { pub fn new(elems: Args) -> Self { - Self { - elems, - } + Self { elems } } } @@ -765,6 +769,30 @@ impl ConstAttribute { } } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ConstTupleAttribute { + tup: Box, + index: Literal, +} + +impl NestedDisplay for ConstTupleAttribute { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "{}.{}", self.tup, self.index) + } +} + +impl_display_from_nested!(ConstTupleAttribute); +impl_locational!(ConstTupleAttribute, tup, index); + +impl ConstTupleAttribute { + pub fn new(tup: ConstExpr, index: Literal) -> Self { + Self { + tup: Box::new(tup), + index, + } + } +} + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct ConstSubscript { obj: Box, @@ -794,12 +822,13 @@ pub enum ConstAccessor { Local(ConstLocal), SelfDot(ConstLocal), Attr(ConstAttribute), + TupleAttr(ConstTupleAttribute), Subscr(ConstSubscript), } -impl_nested_display_for_enum!(ConstAccessor; Local, SelfDot, Attr, Subscr); +impl_nested_display_for_enum!(ConstAccessor; Local, SelfDot, Attr, TupleAttr, Subscr); impl_display_from_nested!(ConstAccessor); -impl_locational_for_enum!(ConstAccessor; Local, SelfDot, Attr, Subscr); +impl_locational_for_enum!(ConstAccessor; Local, SelfDot, Attr, TupleAttr, Subscr); impl ConstAccessor { pub const fn local(symbol: Token) -> Self { diff --git a/compiler/erg_parser/parse.rs b/compiler/erg_parser/parse.rs index 3c0ba1cb..452cece8 100644 --- a/compiler/erg_parser/parse.rs +++ b/compiler/erg_parser/parse.rs @@ -659,10 +659,24 @@ impl Parser { match self.peek() { Some(t) if t.is(Dot) => { self.skip(); - let symbol = self.lpop(); - debug_power_assert!(symbol.is(Symbol)); - let attr = Local::new(symbol); - acc = Accessor::attr(Expr::Accessor(acc), attr); + let token = self.lpop(); + match token.kind { + Symbol => { + let attr = Local::new(token); + acc = Accessor::attr(Expr::Accessor(acc), attr); + } + NatLit => { + let attr = Literal::from(token); + acc = Accessor::tuple_attr(Expr::Accessor(acc), attr); + } + _ => { + self.restore(token); + self.level -= 1; + let err = self.skip_and_throw_syntax_err(caused_by!()); + self.errs.push(err); + return Err(()); + } + } } Some(t) if t.is(LSqBr) => { self.skip(); @@ -1681,6 +1695,14 @@ impl Parser { } } } + Some(t) if t.is(Comma) => { + let first_elem = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let tup = self + .try_reduce_tuple(first_elem) + .map_err(|_| self.stack_dec())?; + self.level -= 1; + return Ok(Expr::Tuple(tup)); + } _ => { if stack.len() <= 1 { break; @@ -1755,9 +1777,24 @@ impl Parser { Ok(Expr::Lambda(lambda)) } Some(t) if t.is(LParen) => { - let tuple = self.try_reduce_tuple().map_err(|_| self.stack_dec())?; + let lparen = self.lpop(); + if self.cur_is(RParen) { + let rparen = self.lpop(); + let args = Args::new(vec![], vec![], Some((lparen, rparen))); + let unit = Tuple::Normal(NormalTuple::new(args)); + self.level -= 1; + return Ok(Expr::Tuple(unit)); + } + let mut expr = self.try_reduce_expr().map_err(|_| self.stack_dec())?; + let rparen = self.lpop(); + match &mut expr { + Expr::Tuple(Tuple::Normal(tup)) => { + tup.elems.paren = Some((lparen, rparen)); + } + _ => {} + } self.level -= 1; - Ok(Expr::Tuple(tuple)) + Ok(expr) } Some(t) if t.is(LSqBr) => { let array = self.try_reduce_array().map_err(|_| self.stack_dec())?; @@ -1852,24 +1889,45 @@ impl Parser { } #[inline] - fn try_reduce_tuple(&mut self) -> ParseResult { + fn try_reduce_tuple(&mut self, first_elem: Expr) -> ParseResult { debug_call_info!(self); - self.skip(); - let inner = self.try_reduce_elems().map_err(|_| self.stack_dec())?; - self.skip(); - let tpl = match inner { - ArrayInner::Normal(elems) => { - Tuple::Normal(NormalTuple::new(elems)) - }, - ArrayInner::Comprehension { - elem: _, - generators: _, - guards: _, - } => todo!(), - _ => unreachable!(), - }; + let mut args = Args::new(vec![PosArg::new(first_elem)], vec![], None); + loop { + match self.peek() { + Some(t) if t.is(Comma) => { + self.skip(); + if self.cur_is(Comma) { + self.level -= 1; + let err = self.skip_and_throw_syntax_err(caused_by!()); + self.errs.push(err); + return Err(()); + } + match self.try_reduce_arg().map_err(|_| self.stack_dec())? { + PosOrKwArg::Pos(arg) => { + args.push_pos(arg); + } + PosOrKwArg::Kw(_arg) => todo!(), + } + } + Some(t) if t.is(Newline) => { + while self.cur_is(Newline) { + self.skip(); + } + match self.try_reduce_arg().map_err(|_| self.stack_dec())? { + PosOrKwArg::Pos(arg) => { + args.push_pos(arg); + } + PosOrKwArg::Kw(_arg) => todo!(), + } + } + _ => { + break; + } + } + } + let tup = Tuple::Normal(NormalTuple::new(args)); self.level -= 1; - Ok(tpl) + Ok(tup) } #[inline]