From 2949c64be89cb9ae8d51a1510ac3d279f8f5cec2 Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Sat, 11 Feb 2023 08:04:54 +0900 Subject: [PATCH] feat(parser): add postfix operator syntax --- crates/els/semantic.rs | 6 +-- crates/erg_compiler/context/eval.rs | 6 +-- crates/erg_compiler/lower.rs | 4 +- crates/erg_parser/ast.rs | 66 +++++++++++++++++++++++------ crates/erg_parser/convert.rs | 14 +++--- crates/erg_parser/desugar.rs | 17 +++++--- crates/erg_parser/lex.rs | 43 ++++++++++--------- crates/erg_parser/parse.rs | 66 +++++++++++++++++------------ crates/erg_parser/token.rs | 48 +++++++++++++++++++-- crates/erg_parser/typespec.rs | 2 +- 10 files changed, 186 insertions(+), 86 deletions(-) diff --git a/crates/els/semantic.rs b/crates/els/semantic.rs index 7203b872e..9094d682f 100644 --- a/crates/els/semantic.rs +++ b/crates/els/semantic.rs @@ -10,7 +10,7 @@ use erg_common::traits::{Locational, Runnable}; use erg_compiler::artifact::BuildRunnable; use erg_compiler::erg_parser::ast::{ Accessor, Args, BinOp, Block, Call, ClassAttr, Def, DefKind, Expr, Methods, Params, - PreDeclTypeSpec, SimpleTypeSpec, TypeSpec, UnaryOp, AST, + PreDeclTypeSpec, PrefixOp, SimpleTypeSpec, TypeSpec, AST, }; use erg_compiler::erg_parser::token::TokenKind; use erg_compiler::ASTBuilder; @@ -156,7 +156,7 @@ impl ASTSemanticState { Expr::Accessor(acc) => self.gen_from_acc(acc), Expr::Call(call) => self.gen_from_call(call), Expr::BinOp(bin) => self.gen_from_bin(bin), - Expr::UnaryOp(unary) => self.gen_from_unary(unary), + Expr::PrefixOp(unary) => self.gen_from_unary(unary), _ => vec![], } } @@ -249,7 +249,7 @@ impl ASTSemanticState { tokens } - fn gen_from_unary(&mut self, unary: UnaryOp) -> Vec { + fn gen_from_unary(&mut self, unary: PrefixOp) -> Vec { let mut tokens = vec![self.gen_token(unary.op.loc(), SemanticTokenType::OPERATOR)]; let mut args = unary.args.into_iter(); tokens.extend(self.gen_from_expr(*args.next().unwrap())); diff --git a/crates/erg_compiler/context/eval.rs b/crates/erg_compiler/context/eval.rs index eb55cf1bd..f03ed9a2f 100644 --- a/crates/erg_compiler/context/eval.rs +++ b/crates/erg_compiler/context/eval.rs @@ -200,7 +200,7 @@ impl Context { self.eval_bin(op, lhs, rhs) } - fn eval_const_unary(&self, unary: &UnaryOp) -> EvalResult { + fn eval_const_unary(&self, unary: &PrefixOp) -> EvalResult { let val = self.eval_const_expr(&unary.args[0])?; let op = self.try_get_op_kind_from_token(&unary.op)?; self.eval_unary_val(op, val) @@ -526,7 +526,7 @@ impl Context { Expr::Literal(lit) => self.eval_lit(lit), Expr::Accessor(acc) => self.eval_const_acc(acc), Expr::BinOp(bin) => self.eval_const_bin(bin), - Expr::UnaryOp(unary) => self.eval_const_unary(unary), + Expr::PrefixOp(unary) => self.eval_const_unary(unary), Expr::Call(call) => self.eval_const_call(call), Expr::Array(arr) => self.eval_const_array(arr), Expr::Set(set) => self.eval_const_set(set), @@ -556,7 +556,7 @@ impl Context { Expr::Literal(lit) => self.eval_lit(lit), Expr::Accessor(acc) => self.eval_const_acc(acc), Expr::BinOp(bin) => self.eval_const_bin(bin), - Expr::UnaryOp(unary) => self.eval_const_unary(unary), + Expr::PrefixOp(unary) => self.eval_const_unary(unary), Expr::Call(call) => self.eval_const_call(call), Expr::Array(arr) => self.eval_const_array(arr), Expr::Set(set) => self.eval_const_set(set), diff --git a/crates/erg_compiler/lower.rs b/crates/erg_compiler/lower.rs index c232181e2..3767d6479 100644 --- a/crates/erg_compiler/lower.rs +++ b/crates/erg_compiler/lower.rs @@ -685,7 +685,7 @@ impl ASTLowerer { Ok(hir::BinOp::new(bin.op, lhs, rhs, t)) } - fn lower_unary(&mut self, unary: ast::UnaryOp) -> LowerResult { + fn lower_unary(&mut self, unary: ast::PrefixOp) -> LowerResult { log!(info "entered {}({unary})", fn_name!()); let mut args = unary.args.into_iter(); let arg = hir::PosArg::new(self.lower_expr(*args.next().unwrap())?); @@ -2021,7 +2021,7 @@ impl ASTLowerer { ast::Expr::Dict(dict) => Ok(hir::Expr::Dict(self.lower_dict(dict)?)), ast::Expr::Accessor(acc) => Ok(hir::Expr::Accessor(self.lower_acc(acc)?)), ast::Expr::BinOp(bin) => Ok(hir::Expr::BinOp(self.lower_bin(bin)?)), - ast::Expr::UnaryOp(unary) => Ok(hir::Expr::UnaryOp(self.lower_unary(unary)?)), + ast::Expr::PrefixOp(unary) => Ok(hir::Expr::UnaryOp(self.lower_unary(unary)?)), ast::Expr::Call(call) => Ok(hir::Expr::Call(self.lower_call(call)?)), ast::Expr::DataPack(pack) => Ok(hir::Expr::Call(self.lower_pack(pack)?)), ast::Expr::Lambda(lambda) => Ok(hir::Expr::Lambda(self.lower_lambda(lambda)?)), diff --git a/crates/erg_parser/ast.rs b/crates/erg_parser/ast.rs index d10ec1f07..f2f22ab44 100644 --- a/crates/erg_parser/ast.rs +++ b/crates/erg_parser/ast.rs @@ -1037,27 +1037,27 @@ impl BinOp { } #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct UnaryOp { +pub struct PrefixOp { pub op: Token, pub args: [Box; 1], } -impl NestedDisplay for UnaryOp { +impl NestedDisplay for PrefixOp { fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { writeln!(f, "`{}`:", self.op.content)?; self.args[0].fmt_nest(f, level + 1) } } -impl_display_from_nested!(UnaryOp); +impl_display_from_nested!(PrefixOp); -impl Locational for UnaryOp { +impl Locational for PrefixOp { fn loc(&self) -> Location { Location::concat(&self.op, self.args[0].as_ref()) } } -impl UnaryOp { +impl PrefixOp { pub fn new(op: Token, expr: Expr) -> Self { Self { op, @@ -1071,6 +1071,41 @@ impl UnaryOp { } } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct PostfixOp { + pub op: Token, + pub args: [Box; 1], +} + +impl NestedDisplay for PostfixOp { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { + self.args[0].fmt_nest(f, level + 1)?; + writeln!(f, "{}", self.op.content) + } +} + +impl_display_from_nested!(PostfixOp); + +impl Locational for PostfixOp { + fn loc(&self) -> Location { + Location::concat(self.args[0].as_ref(), &self.op) + } +} + +impl PostfixOp { + pub fn new(expr: Expr, op: Token) -> Self { + Self { + op, + args: [Box::new(expr)], + } + } + + pub fn deconstruct(self) -> (Expr, Token) { + let mut exprs = self.args.into_iter(); + (*exprs.next().unwrap(), self.op) + } +} + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Call { pub obj: Box, @@ -1562,8 +1597,8 @@ impl ConstUnaryOp { } } - pub fn downcast(self) -> UnaryOp { - UnaryOp::new(self.op, self.expr.downcast()) + pub fn downcast(self) -> PrefixOp { + PrefixOp::new(self.op, self.expr.downcast()) } } @@ -1636,7 +1671,7 @@ impl ConstExpr { // Self::Set(set) => Expr::Set(set.downcast()), // Self::Dict(dict) => Expr::Dict(dict.downcast()), Self::BinOp(binop) => Expr::BinOp(binop.downcast()), - Self::UnaryOp(unop) => Expr::UnaryOp(unop.downcast()), + Self::UnaryOp(unop) => Expr::PrefixOp(unop.downcast()), _ => todo!(), } } @@ -3717,7 +3752,8 @@ pub enum Expr { Set(Set), Record(Record), BinOp(BinOp), - UnaryOp(UnaryOp), + PrefixOp(PrefixOp), + PostfixOp(PostfixOp), Call(Call), DataPack(DataPack), Lambda(Lambda), @@ -3731,10 +3767,10 @@ pub enum Expr { Dummy(Dummy), } -impl_nested_display_for_chunk_enum!(Expr; Literal, Accessor, Array, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, DataPack, Lambda, TypeAscription, Def, Methods, ClassDef, PatchDef, ReDef, Dummy); -impl_from_trait_for_enum!(Expr; Literal, Accessor, Array, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, DataPack, Lambda, TypeAscription, Def, Methods, ClassDef, PatchDef, ReDef, Dummy); +impl_nested_display_for_chunk_enum!(Expr; Literal, Accessor, Array, Tuple, Dict, Set, Record, BinOp, PrefixOp, PostfixOp, Call, DataPack, Lambda, TypeAscription, Def, Methods, ClassDef, PatchDef, ReDef, Dummy); +impl_from_trait_for_enum!(Expr; Literal, Accessor, Array, Tuple, Dict, Set, Record, BinOp, PrefixOp, PostfixOp, Call, DataPack, Lambda, TypeAscription, Def, Methods, ClassDef, PatchDef, ReDef, Dummy); impl_display_from_nested!(Expr); -impl_locational_for_enum!(Expr; Literal, Accessor, Array, Tuple, Dict, Set, Record, BinOp, UnaryOp, Call, DataPack, Lambda, TypeAscription, Def, Methods, ClassDef, PatchDef, ReDef, Dummy); +impl_locational_for_enum!(Expr; Literal, Accessor, Array, Tuple, Dict, Set, Record, BinOp, PrefixOp, PostfixOp, Call, DataPack, Lambda, TypeAscription, Def, Methods, ClassDef, PatchDef, ReDef, Dummy); impl Expr { pub fn is_match_call(&self) -> bool { @@ -3759,7 +3795,7 @@ impl Expr { pub fn need_to_be_closed(&self) -> bool { matches!( self, - Expr::BinOp(_) | Expr::UnaryOp(_) | Expr::Lambda(_) | Expr::TypeAscription(_) + Expr::BinOp(_) | Expr::PrefixOp(_) | Expr::Lambda(_) | Expr::TypeAscription(_) ) } @@ -3826,6 +3862,10 @@ impl Expr { Self::Call(self.call(args)) } + pub fn postfix_expr(self, op: Token) -> Self { + Self::PostfixOp(PostfixOp::new(self, op)) + } + pub fn type_asc(self, op: Token, t_spec: TypeSpec) -> TypeAscription { TypeAscription::new(self, op, t_spec) } diff --git a/crates/erg_parser/convert.rs b/crates/erg_parser/convert.rs index d2852fd95..160e2bbd8 100644 --- a/crates/erg_parser/convert.rs +++ b/crates/erg_parser/convert.rs @@ -446,9 +446,9 @@ impl Parser { debug_exit_info!(self); Ok(param) } - Expr::UnaryOp(unary) => match unary.op.kind { + Expr::PrefixOp(prefix) => match prefix.op.kind { TokenKind::RefOp => { - let var = unary.args.into_iter().next().unwrap(); + let var = prefix.args.into_iter().next().unwrap(); let var = option_enum_unwrap!(*var, Expr::Accessor:(Accessor::Ident:(_))) .unwrap_or_else(|| todo!()); let pat = ParamPattern::Ref(var.name); @@ -457,7 +457,7 @@ impl Parser { Ok(param) } TokenKind::RefMutOp => { - let var = unary.args.into_iter().next().unwrap(); + let var = prefix.args.into_iter().next().unwrap(); let var = option_enum_unwrap!(*var, Expr::Accessor:(Accessor::Ident:(_))) .unwrap_or_else(|| todo!()); let pat = ParamPattern::RefMut(var.name); @@ -467,7 +467,7 @@ impl Parser { } // TODO: Spread _other => { - let err = ParseError::simple_syntax_error(line!() as usize, unary.loc()); + let err = ParseError::simple_syntax_error(line!() as usize, prefix.loc()); self.errs.push(err); debug_exit_info!(self); Err(()) @@ -652,9 +652,9 @@ impl Parser { debug_exit_info!(self); Ok(sig) } - Expr::UnaryOp(unary) => match unary.op.kind { + Expr::PrefixOp(prefix) => match prefix.op.kind { TokenKind::PreStar => { - let mut exprs = unary.args.into_iter(); + let mut exprs = prefix.args.into_iter(); let param = self .convert_rhs_to_param(*exprs.next().unwrap(), false) .map_err(|_| self.stack_dec(fn_name!()))?; @@ -663,7 +663,7 @@ impl Parser { Ok(LambdaSignature::new(params, None, TypeBoundSpecs::empty())) } _ => { - let err = ParseError::simple_syntax_error(line!() as usize, unary.op.loc()); + let err = ParseError::simple_syntax_error(line!() as usize, prefix.op.loc()); self.errs.push(err); debug_exit_info!(self); Err(()) diff --git a/crates/erg_parser/desugar.rs b/crates/erg_parser/desugar.rs index 66ef87c5c..8b489493a 100644 --- a/crates/erg_parser/desugar.rs +++ b/crates/erg_parser/desugar.rs @@ -13,9 +13,9 @@ use crate::ast::{ ClassAttr, ClassAttrs, ClassDef, ConstExpr, DataPack, Def, DefBody, DefId, Dict, Dummy, Expr, Identifier, KeyValue, KwArg, Lambda, LambdaSignature, Literal, Methods, MixedRecord, Module, NonDefaultParamSignature, NormalArray, NormalDict, NormalRecord, NormalSet, NormalTuple, - ParamPattern, ParamRecordAttr, Params, PatchDef, PosArg, ReDef, Record, RecordAttrOrIdent, - RecordAttrs, Set as astSet, SetWithLength, Signature, SubrSignature, Tuple, TupleTypeSpec, - TypeAppArgs, TypeBoundSpecs, TypeSpec, TypeSpecWithOp, UnaryOp, VarName, VarPattern, + ParamPattern, ParamRecordAttr, Params, PatchDef, PosArg, PostfixOp, PrefixOp, ReDef, Record, + RecordAttrOrIdent, RecordAttrs, Set as astSet, SetWithLength, Signature, SubrSignature, Tuple, + TupleTypeSpec, TypeAppArgs, TypeBoundSpecs, TypeSpec, TypeSpecWithOp, VarName, VarPattern, VarRecordAttr, VarSignature, }; use crate::token::{Token, TokenKind, COLON, DOT}; @@ -219,10 +219,15 @@ impl Desugarer { let rhs = desugar(*args.next().unwrap()); Expr::BinOp(BinOp::new(binop.op, lhs, rhs)) } - Expr::UnaryOp(unaryop) => { - let mut args = unaryop.args.into_iter(); + Expr::PrefixOp(prefixop) => { + let mut args = prefixop.args.into_iter(); let expr = desugar(*args.next().unwrap()); - Expr::UnaryOp(UnaryOp::new(unaryop.op, expr)) + Expr::PrefixOp(PrefixOp::new(prefixop.op, expr)) + } + Expr::PostfixOp(postfixop) => { + let mut args = postfixop.args.into_iter(); + let expr = desugar(*args.next().unwrap()); + Expr::PostfixOp(PostfixOp::new(expr, postfixop.op)) } Expr::Call(call) => { let obj = desugar(*call.obj); diff --git a/crates/erg_parser/lex.rs b/crates/erg_parser/lex.rs index 95cb8c7f5..7e98be80c 100644 --- a/crates/erg_parser/lex.rs +++ b/crates/erg_parser/lex.rs @@ -11,7 +11,7 @@ use erg_common::traits::{Locational, Runnable, Stream}; use erg_common::{debug_power_assert, fn_name_full, normalize_newline, switch_lang}; use crate::error::{LexError, LexErrors, LexResult, LexerRunnerError, LexerRunnerErrors}; -use crate::token::{Token, TokenCategory, TokenKind, TokenStream}; +use crate::token::{Token, TokenKind, TokenStream}; use TokenKind::*; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -292,32 +292,29 @@ impl Lexer /*<'a>*/ { ) } + fn is_right_disconnective(s: char) -> bool { + matches!(s, ')' | ']' | '}' | ',' | ':' | ';' | '\n' | '>' | '=') + } + // +, -, * etc. may be pre/bin // and, or, is!, isnot!, in, notin, as, dot, cross may be bin/function + // None means invalid syntax fn op_fix(&self) -> Option { - match self.prev_token.category() { - // unary: `[ +`, `= +`, `+ +`, `, +`, `:: +` - TokenCategory::LEnclosure - | TokenCategory::BinOp - | TokenCategory::UnaryOp - | TokenCategory::Separator - | TokenCategory::SpecialBinOp - | TokenCategory::DefOp - | TokenCategory::LambdaOp - | TokenCategory::StrInterpLeft - | TokenCategory::StrInterpMid - | TokenCategory::BOF => Some(OpFix::Prefix), - TokenCategory::REnclosure - | TokenCategory::Literal - | TokenCategory::StrInterpRight - | TokenCategory::Symbol => match (self.peek_prev_prev_ch(), self.peek_cur_ch()) { + let left_disconnective = self.prev_token.category().is_left_disconnective(); + let right_disconnective = self + .peek_cur_ch() + .map_or(false, Self::is_right_disconnective); + match (left_disconnective, right_disconnective) { + (true, true) => None, + (true, false) => Some(OpFix::Prefix), + (false, true) => Some(OpFix::Postfix), + (false, false) => match (self.peek_prev_prev_ch(), self.peek_cur_ch()) { (Some(' '), Some(' ')) => Some(OpFix::Infix), // x + 1: bin (Some(' '), Some(_)) => Some(OpFix::Prefix), // x +1: unary (Some(_), Some(' ')) => Some(OpFix::Infix), // x+ 1 : bin (Some(_), Some(_)) => Some(OpFix::Infix), // x+1: bin _ => None, }, - _ => None, } } @@ -1207,7 +1204,15 @@ impl Iterator for Lexer /*<'a>*/ { self.consume(); self.accept(EllipsisLit, "...") } - _ => self.accept(Closed, ".."), + _ => match self.op_fix() { + Some(OpFix::Prefix) => self.accept(PreRange, ".."), + Some(OpFix::Infix) => self.accept(Closed, ".."), + Some(OpFix::Postfix) => self.accept(PostRange, ".."), + _ => { + let token = self.emit_token(Illegal, ".."); + Some(Err(LexError::simple_syntax_error(0, token.loc()))) + } + }, } } // prev_token is Symbol => TupleAttribute diff --git a/crates/erg_parser/parse.rs b/crates/erg_parser/parse.rs index a35b17297..06a7d5f65 100644 --- a/crates/erg_parser/parse.rs +++ b/crates/erg_parser/parse.rs @@ -710,16 +710,16 @@ impl Parser { .map_err(|_| self.stack_dec(fn_name!()))? { PosOrKwArg::Pos(PosArg { - expr: Expr::UnaryOp(unary), - }) if unary.op.is(PreStar) => { - let pos_args = PosArg::new(unary.deconstruct().1); + expr: Expr::PrefixOp(prefix), + }) if prefix.op.is(PreStar) => { + let pos_args = PosArg::new(prefix.deconstruct().1); Args::new(vec![], Some(pos_args), vec![], None) } PosOrKwArg::Pos(PosArg { expr: Expr::TypeAscription(TypeAscription { expr, op, t_spec }), - }) if matches!(expr.as_ref(), Expr::UnaryOp(unary) if unary.op.is(PreStar)) => { - let Expr::UnaryOp(unary) = *expr else { unreachable!() }; - let var_args = PosArg::new(unary.deconstruct().1.type_asc_expr(op, t_spec)); + }) if matches!(expr.as_ref(), Expr::PrefixOp(prefix) if prefix.op.is(PreStar)) => { + let Expr::PrefixOp(prefix) = *expr else { unreachable!() }; + let var_args = PosArg::new(prefix.deconstruct().1.type_asc_expr(op, t_spec)); Args::new(vec![], Some(var_args), vec![], None) } PosOrKwArg::Pos(arg) => Args::pos_only(vec![arg], None), @@ -781,17 +781,17 @@ impl Parser { .map_err(|_| self.stack_dec(fn_name!()))? { PosOrKwArg::Pos(PosArg { - expr: Expr::UnaryOp(unary), - }) if unary.op.is(PreStar) => { - args.set_var_args(PosArg::new(unary.deconstruct().1)); + expr: Expr::PrefixOp(prefix), + }) if prefix.op.is(PreStar) => { + args.set_var_args(PosArg::new(prefix.deconstruct().1)); } PosOrKwArg::Pos(PosArg { expr: Expr::TypeAscription(TypeAscription { expr, op, t_spec }), - }) if matches!(expr.as_ref(), Expr::UnaryOp(unary) if unary.op.is(PreStar)) => + }) if matches!(expr.as_ref(), Expr::PrefixOp(prefix) if prefix.op.is(PreStar)) => { - let Expr::UnaryOp(unary) = *expr else { unreachable!() }; + let Expr::PrefixOp(prefix) = *expr else { unreachable!() }; args.set_var_args(PosArg::new( - unary.deconstruct().1.type_asc_expr(op, t_spec), + prefix.deconstruct().1.type_asc_expr(op, t_spec), )); } PosOrKwArg::Pos(arg) => { @@ -1104,6 +1104,11 @@ impl Parser { let obj = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); stack.push(ExprOrOp::Expr(obj.call_expr(args))); } + Some(post_op) if post_op.category_is(TokenCategory::PostfixOp) => { + let post_op = self.lpop(); + let obj = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + stack.push(ExprOrOp::Expr(obj.postfix_expr(post_op))); + } Some(op) if op.category_is(TC::DefOp) => { let op = self.lpop(); let is_multiline_block = self.cur_is(Newline); @@ -1399,6 +1404,11 @@ impl Parser { )); loop { match self.peek() { + Some(post_op) if post_op.category_is(TokenCategory::PostfixOp) => { + let post_op = self.lpop(); + let obj = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + stack.push(ExprOrOp::Expr(obj.postfix_expr(post_op))); + } Some(op) if op.category_is(TC::LambdaOp) => { let op = self.lpop(); let is_multiline_block = self.cur_is(Newline); @@ -1703,11 +1713,11 @@ impl Parser { Ok(call_or_acc) } Some(t) if t.category_is(TC::UnaryOp) => { - let unaryop = self - .try_reduce_unary() + let prefixop = self + .try_reduce_prefix() .map_err(|_| self.stack_dec(fn_name!()))?; debug_exit_info!(self); - Ok(Expr::UnaryOp(unaryop)) + Ok(Expr::PrefixOp(prefixop)) } Some(t) if t.is(LParen) => { let lparen = self.lpop(); @@ -1964,14 +1974,14 @@ impl Parser { } #[inline] - fn try_reduce_unary(&mut self) -> ParseResult { + fn try_reduce_prefix(&mut self) -> ParseResult { debug_call_info!(self); let op = self.lpop(); let expr = self .try_reduce_expr(false, false, false, false) .map_err(|_| self.stack_dec(fn_name!()))?; debug_exit_info!(self); - Ok(UnaryOp::new(op, expr)) + Ok(PrefixOp::new(op, expr)) } #[inline] @@ -2361,16 +2371,16 @@ impl Parser { debug_call_info!(self); let mut args = match first_elem { PosOrKwArg::Pos(PosArg { - expr: Expr::UnaryOp(unary), - }) if unary.op.is(PreStar) => { - let var_args = Some(PosArg::new(unary.deconstruct().1)); + expr: Expr::PrefixOp(prefix), + }) if prefix.op.is(PreStar) => { + let var_args = Some(PosArg::new(prefix.deconstruct().1)); Args::new(vec![], var_args, vec![], None) } PosOrKwArg::Pos(PosArg { expr: Expr::TypeAscription(TypeAscription { expr, op, t_spec }), - }) if matches!(expr.as_ref(), Expr::UnaryOp(unary) if unary.op.is(PreStar)) => { - let Expr::UnaryOp(unary) = *expr else { unreachable!() }; - let expr = unary.deconstruct().1; + }) if matches!(expr.as_ref(), Expr::PrefixOp(prefix) if prefix.op.is(PreStar)) => { + let Expr::PrefixOp(prefix) = *expr else { unreachable!() }; + let expr = prefix.deconstruct().1; let var_args = Some(PosArg::new(expr.type_asc_expr(op, t_spec))); Args::new(vec![], var_args, vec![], None) } @@ -2399,13 +2409,13 @@ impl Parser { { PosOrKwArg::Pos(arg) if args.kw_is_empty() && args.var_args.is_none() => { match arg.expr { - Expr::UnaryOp(unary) if unary.op.is(PreStar) => { - args.set_var_args(PosArg::new(unary.deconstruct().1)); + Expr::PrefixOp(prefix) if prefix.op.is(PreStar) => { + args.set_var_args(PosArg::new(prefix.deconstruct().1)); } - Expr::TypeAscription(TypeAscription { expr, op, t_spec }) if matches!(expr.as_ref(), Expr::UnaryOp(unary) if unary.op.is(PreStar)) => + Expr::TypeAscription(TypeAscription { expr, op, t_spec }) if matches!(expr.as_ref(), Expr::PrefixOp(prefix) if prefix.op.is(PreStar)) => { - let Expr::UnaryOp(unary) = *expr else { unreachable!() }; - let expr = unary.deconstruct().1; + let Expr::PrefixOp(prefix) = *expr else { unreachable!() }; + let expr = prefix.deconstruct().1; args.set_var_args(PosArg::new(expr.type_asc_expr(op, t_spec))); } Expr::Tuple(Tuple::Normal(tup)) if tup.elems.paren.is_none() => { diff --git a/crates/erg_parser/token.rs b/crates/erg_parser/token.rs index ff43bb822..62521bbf7 100644 --- a/crates/erg_parser/token.rs +++ b/crates/erg_parser/token.rs @@ -44,6 +44,10 @@ pub enum TokenKind { PreMinus, /// ~ (unary) PreBitNot, + /// .. (unary prefix) + PreRange, + /// <.. (unary prefix) + PreOpenRange, // PreAmp, // & (unary) // PreAt, // @ (unary) /// ! (unary) @@ -52,6 +56,10 @@ pub enum TokenKind { PreDblStar, // ** (unary) /// ? (postfix) Try, + /// .. (postfix) + PostRange, + /// <.. (postfix) + PostOpenRange, /// `+` Plus, /// `-` @@ -218,6 +226,39 @@ impl TokenCategory { pub const fn is_block_op(&self) -> bool { matches!(self, Self::DefOp | Self::LambdaOp) } + + // prefix?: `[ +`, `= +`, `+ +`, `, +`, `:: +` + pub fn is_left_disconnective(&self) -> bool { + matches!( + self, + Self::LEnclosure + | Self::BinOp + | Self::UnaryOp + | Self::Separator + | Self::SpecialBinOp + | Self::DefOp + | Self::LambdaOp + | Self::StrInterpLeft + | Self::StrInterpMid + | Self::BOF + ) + } + + /// postfix?: `.. ]`, `.. ;` + pub fn is_right_disconnective(&self) -> bool { + matches!( + self, + Self::REnclosure + | Self::PostfixOp + | Self::Separator + | Self::SpecialBinOp + | Self::DefOp + | Self::LambdaOp + | Self::StrInterpMid + | Self::StrInterpRight + | Self::EOF + ) + } } impl TokenKind { @@ -229,10 +270,9 @@ impl TokenKind { StrInterpLeft => TokenCategory::StrInterpLeft, StrInterpMid => TokenCategory::StrInterpMid, StrInterpRight => TokenCategory::StrInterpRight, - PrePlus | PreMinus | PreBitNot | Mutate | PreStar | PreDblStar | RefOp | RefMutOp => { - TokenCategory::UnaryOp - } - Try => TokenCategory::PostfixOp, + PrePlus | PreMinus | PreBitNot | Mutate | PreStar | PreDblStar | RefOp | RefMutOp + | PreRange | PreOpenRange => TokenCategory::UnaryOp, + Try | PostOpenRange | PostRange => TokenCategory::PostfixOp, Comma | Colon | DblColon | SupertypeOf | SubtypeOf | Dot | Pipe | Walrus | Inclusion => TokenCategory::SpecialBinOp, Equal => TokenCategory::DefOp, diff --git a/crates/erg_parser/typespec.rs b/crates/erg_parser/typespec.rs index 496e324e6..d1f5624e9 100644 --- a/crates/erg_parser/typespec.rs +++ b/crates/erg_parser/typespec.rs @@ -86,7 +86,7 @@ impl Parser { let rhs = Self::validate_const_expr(*args.next().unwrap())?; Ok(ConstExpr::BinOp(ConstBinOp::new(bin.op, lhs, rhs))) } - Expr::UnaryOp(unary) => { + Expr::PrefixOp(unary) => { let mut args = unary.args.into_iter(); let arg = Self::validate_const_expr(*args.next().unwrap())?; Ok(ConstExpr::UnaryOp(ConstUnaryOp::new(unary.op, arg)))