diff --git a/compiler/erg_parser/ast.rs b/compiler/erg_parser/ast.rs index 6f74c7e2..e044d068 100644 --- a/compiler/erg_parser/ast.rs +++ b/compiler/erg_parser/ast.rs @@ -109,7 +109,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 { @@ -367,6 +367,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)) } @@ -503,6 +507,38 @@ impl_nested_display_for_enum!(Array; Normal, WithLength, Comprehension); impl_display_for_enum!(Array; Normal, WithLength, Comprehension); impl_locational_for_enum!(Array; Normal, WithLength, Comprehension); +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct NormalTuple { + pub elems: Args, +} + +impl NestedDisplay for NormalTuple { + 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)) + } +} + +impl_display_from_nested!(NormalTuple); +impl_locational!(NormalTuple, elems, elems); + +impl NormalTuple { + pub fn new(elems: Args) -> Self { + Self { elems } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Tuple { + Normal(NormalTuple), + // Comprehension(TupleComprehension), +} + +impl_nested_display_for_enum!(Tuple; Normal); +impl_display_for_enum!(Tuple; Normal); +impl_locational_for_enum!(Tuple; Normal); + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct NormalDict { l_brace: Token, @@ -832,6 +868,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, @@ -861,12 +921,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 { @@ -2426,6 +2487,7 @@ pub enum Expr { Lit(Literal), Accessor(Accessor), Array(Array), + Tuple(Tuple), Dict(Dict), Set(Set), Record(Record), @@ -2437,9 +2499,9 @@ pub enum Expr { Def(Def), } -impl_nested_display_for_chunk_enum!(Expr; Lit, Accessor, Array, Dict, Set, Record, BinOp, UnaryOp, Call, Lambda, Decl, Def); +impl_nested_display_for_chunk_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, Lambda, Decl, Def); impl_display_from_nested!(Expr); -impl_locational_for_enum!(Expr; Lit, Accessor, Array, Dict, Set, Record, BinOp, UnaryOp, Call, Lambda, Decl, Def); +impl_locational_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, Lambda, Decl, Def); impl Expr { pub fn is_match_call(&self) -> bool { diff --git a/compiler/erg_parser/parse.rs b/compiler/erg_parser/parse.rs index 96949107..2dc68874 100644 --- a/compiler/erg_parser/parse.rs +++ b/compiler/erg_parser/parse.rs @@ -686,10 +686,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(); @@ -1778,6 +1792,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; @@ -1852,18 +1874,24 @@ impl Parser { Ok(Expr::Lambda(lambda)) } Some(t) if t.is(LParen) => { - self.skip(); - let expr = self.try_reduce_expr().map_err(|_| self.stack_dec())?; + let lparen = self.lpop(); if self.cur_is(RParen) { - self.skip(); + let rparen = self.lpop(); + let args = Args::new(vec![], vec![], Some((lparen, rparen))); + let unit = Tuple::Normal(NormalTuple::new(args)); self.level -= 1; - Ok(expr) - } else { - self.level -= 1; - let err = self.skip_and_throw_syntax_err(caused_by!()); - self.errs.push(err); - return Err(()); + 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) } Some(t) if t.is(LSqBr) => { let array = self.try_reduce_array().map_err(|_| self.stack_dec())?; @@ -2033,6 +2061,47 @@ impl Parser { todo!() } + fn try_reduce_tuple(&mut self, first_elem: Expr) -> ParseResult { + debug_call_info!(self); + 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(tup) + } + #[inline] fn try_reduce_ident(&mut self) -> ParseResult { debug_call_info!(self);