diff --git a/src/ast.rs b/src/ast.rs index c8c95e2c2..8691d5a88 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -591,11 +591,15 @@ fn accept_mut(&mut self, visitor: &mut V) { } /// Implement the acceptor trait for the given branch node. -macro_rules! implement_acceptor_for_branch { +macro_rules! Acceptor { ( - $name:ident - $(, $field_name:ident )* - $(,)? + $(#[$_m:meta])* + $_v:vis struct $name:ident { + $( + $(#[$_fm:meta])* + $_fv:vis $field_name:ident : $_ft:ty + ),* $(,)? + } ) => { impl Acceptor for $name { #[allow(unused_variables)] @@ -621,17 +625,16 @@ fn accept_mut(&mut self, visitor: &mut V) { visitor.did_visit_fields_of(self, flow); } } - } + }; } /// A redirection has an operator like > or 2>, and a target like /dev/null or &1. /// Note that pipes are not redirections. -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct Redirection { pub oper: TokenRedirection, pub target: String_, } -implement_acceptor_for_branch!(Redirection, oper, target); impl CheckParse for Redirection { fn can_be_parsed(pop: &mut Populator<'_>) -> bool { @@ -761,7 +764,7 @@ fn accept_mut(&mut self, visitor: &mut V) { /// A job is a non-empty list of statements, separated by pipes. (Non-empty is useful for cases /// like if statements, where we require a command). -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct JobPipeline { /// Maybe the time keyword. pub time: Option, @@ -774,10 +777,9 @@ pub struct JobPipeline { /// Maybe backgrounded. pub bg: Option, } -implement_acceptor_for_branch!(JobPipeline, time, variables, statement, continuation, bg); /// A job_conjunction is a job followed by a && or || continuations. -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct JobConjunction { /// The job conjunction decorator. pub decorator: Option, @@ -790,7 +792,6 @@ pub struct JobConjunction { /// only fail to be present if we ran out of tokens. pub semi_nl: Option, } -implement_acceptor_for_branch!(JobConjunction, decorator, job, continuations, semi_nl); impl CheckParse for JobConjunction { fn can_be_parsed(pop: &mut Populator<'_>) -> bool { @@ -805,7 +806,7 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool { } } -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct ForHeader { /// 'for' pub kw_for: KeywordFor, @@ -818,18 +819,16 @@ pub struct ForHeader { /// newline or semicolon pub semi_nl: SemiNl, } -implement_acceptor_for_branch!(ForHeader, kw_for, var_name, kw_in, args, semi_nl); -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct WhileHeader { /// 'while' pub kw_while: KeywordWhile, pub condition: JobConjunction, pub andor_tail: AndorJobList, } -implement_acceptor_for_branch!(WhileHeader, kw_while, condition, andor_tail); -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct FunctionHeader { pub kw_function: KeywordFunction, /// functions require at least one argument. @@ -837,18 +836,16 @@ pub struct FunctionHeader { pub args: ArgumentList, pub semi_nl: SemiNl, } -implement_acceptor_for_branch!(FunctionHeader, kw_function, first_arg, args, semi_nl); -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct BeginHeader { pub kw_begin: KeywordBegin, /// Note that 'begin' does NOT require a semi or nl afterwards. /// This is valid: begin echo hi; end pub semi_nl: Option, } -implement_acceptor_for_branch!(BeginHeader, kw_begin, semi_nl); -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct BlockStatement { /// A header like for, while, etc. pub header: BlockStatementHeader, @@ -859,9 +856,8 @@ pub struct BlockStatement { /// Arguments and redirections associated with the block. pub args_or_redirs: ArgumentOrRedirectionList, } -implement_acceptor_for_branch!(BlockStatement, header, jobs, end, args_or_redirs); -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct BraceStatement { /// The opening brace, in command position. pub left_brace: TokenLeftBrace, @@ -872,15 +868,8 @@ pub struct BraceStatement { /// Arguments and redirections associated with the block. pub args_or_redirs: ArgumentOrRedirectionList, } -implement_acceptor_for_branch!( - BraceStatement, - left_brace, - jobs, - right_brace, - args_or_redirs -); -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct IfClause { /// The 'if' keyword. pub kw_if: KeywordIf, @@ -891,16 +880,14 @@ pub struct IfClause { /// The body to execute if the condition is true. pub body: JobList, } -implement_acceptor_for_branch!(IfClause, kw_if, condition, andor_tail, body); -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct ElseifClause { /// The 'else' keyword. pub kw_else: KeywordElse, /// The 'if' clause following it. pub if_clause: IfClause, } -implement_acceptor_for_branch!(ElseifClause, kw_else, if_clause); impl CheckParse for ElseifClause { fn can_be_parsed(pop: &mut Populator<'_>) -> bool { pop.peek_token(0).keyword == ParseKeyword::Else @@ -910,21 +897,20 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool { define_list_node!(ElseifClauseList, ElseifClause); -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct ElseClause { /// else ; body pub kw_else: KeywordElse, pub semi_nl: Option, pub body: JobList, } -implement_acceptor_for_branch!(ElseClause, kw_else, semi_nl, body); impl CheckParse for ElseClause { fn can_be_parsed(pop: &mut Populator<'_>) -> bool { pop.peek_token(0).keyword == ParseKeyword::Else } } -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct IfStatement { /// if part pub if_clause: IfClause, @@ -937,16 +923,8 @@ pub struct IfStatement { /// block args / redirs pub args_or_redirs: ArgumentOrRedirectionList, } -implement_acceptor_for_branch!( - IfStatement, - if_clause, - elseif_clauses, - else_clause, - end, - args_or_redirs -); -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct CaseItem { /// case \ ; body pub kw_case: KeywordCase, @@ -954,14 +932,13 @@ pub struct CaseItem { pub semi_nl: SemiNl, pub body: JobList, } -implement_acceptor_for_branch!(CaseItem, kw_case, arguments, semi_nl, body); impl CheckParse for CaseItem { fn can_be_parsed(pop: &mut Populator<'_>) -> bool { pop.peek_token(0).keyword == ParseKeyword::Case } } -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct SwitchStatement { /// switch \ ; body ; end args_redirs pub kw_switch: KeywordSwitch, @@ -971,19 +948,10 @@ pub struct SwitchStatement { pub end: KeywordEnd, pub args_or_redirs: ArgumentOrRedirectionList, } -implement_acceptor_for_branch!( - SwitchStatement, - kw_switch, - argument, - semi_nl, - cases, - end, - args_or_redirs -); /// A decorated_statement is a command with a list of arguments_or_redirections, possibly with /// "builtin" or "command" or "exec" -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct DecoratedStatement { /// An optional decoration (command, builtin, exec, etc). pub opt_decoration: Option, @@ -992,10 +960,9 @@ pub struct DecoratedStatement { /// Args and redirs pub args_or_redirs: ArgumentOrRedirectionList, } -implement_acceptor_for_branch!(DecoratedStatement, opt_decoration, command, args_or_redirs); /// A not statement like `not true` or `! true` -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct NotStatement { /// Keyword, either not or exclam. pub kw: KeywordNot, @@ -1003,16 +970,14 @@ pub struct NotStatement { pub variables: VariableAssignmentList, pub contents: Statement, } -implement_acceptor_for_branch!(NotStatement, kw, time, variables, contents); -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct JobContinuation { pub pipe: TokenPipe, pub newlines: MaybeNewlines, pub variables: VariableAssignmentList, pub statement: Statement, } -implement_acceptor_for_branch!(JobContinuation, pipe, newlines, variables, statement); impl CheckParse for JobContinuation { fn can_be_parsed(pop: &mut Populator<'_>) -> bool { pop.peek_type(0) == ParseTokenType::pipe @@ -1021,7 +986,7 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool { define_list_node!(JobContinuationList, JobContinuation); -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct JobConjunctionContinuation { /// The && or || token. pub conjunction: TokenConjunction, @@ -1029,7 +994,6 @@ pub struct JobConjunctionContinuation { /// The job itself. pub job: JobPipeline, } -implement_acceptor_for_branch!(JobConjunctionContinuation, conjunction, newlines, job); impl CheckParse for JobConjunctionContinuation { fn can_be_parsed(pop: &mut Populator<'_>) -> bool { let typ = pop.peek_type(0); @@ -1040,11 +1004,10 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool { /// An andor_job just wraps a job, but requires that the job have an 'and' or 'or' job_decorator. /// Note this is only used for andor_job_list; jobs that are not part of an andor_job_list are not /// instances of this. -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct AndorJob { pub job: JobConjunction, } -implement_acceptor_for_branch!(AndorJob, job); impl CheckParse for AndorJob { fn can_be_parsed(pop: &mut Populator<'_>) -> bool { let keyword = pop.peek_token(0).keyword; @@ -1066,11 +1029,10 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool { /// A freestanding_argument_list is equivalent to a normal argument list, except it may contain /// TOK_END (newlines, and even semicolons, for historical reasons). /// In practice the tok_ends are ignored by fish code so we do not bother to store them. -#[derive(Default, Debug, Node!)] +#[derive(Default, Debug, Node!, Acceptor!)] pub struct FreestandingArgumentList { pub arguments: ArgumentList, } -implement_acceptor_for_branch!(FreestandingArgumentList, arguments); define_list_node!(JobConjunctionContinuationList, JobConjunctionContinuation);