// Programmatic representation of fish grammar #ifndef FISH_PARSE_GRAMMAR_H #define FISH_PARSE_GRAMMAR_H #include "parse_constants.h" #include "tokenizer.h" namespace grammar { using production_element_t = uint8_t; // Forward declarations. #define ELEM(T) struct T; #include "parse_grammar_elements.inc" // A production is a sequence of production elements. template struct production_t { production_element_t elems[count]; }; template struct prim {}; using tok_end = prim; using tok_string = prim; template struct keyword {}; // A producer holds various productions. template struct producer {}; // Empty produces nothing. struct empty : public producer> {}; // Not sure if we need this. template struct single {}; template using produces_single = producer>; // Alternative represents a choice. template struct alternative {}; template using produces_alternative = producer>; // Sequence represents a list of productions. template struct seq {}; template using produces_sequence = producer>; // Following are the grammar productions. #define DEF(T) struct T : public // A job_list is a list of jobs, separated by semicolons or newlines DEF(job_list) produces_alternative, // seq>{}; // 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). To represent "non-empty", we require a // statement, followed by a possibly empty job_continuation, and then optionally a background // specifier '&' DEF(job) produces_sequence{}; DEF(job_continuation) produces_alternative, statement, job_continuation>>{}; // A statement is a normal command, or an if / while / and etc DEF(statement) produces_alternative{}; // A block is a conditional, loop, or begin/end DEF(if_statement) produces_sequence{}; DEF(if_clause) produces_sequence, job, tok_end, andor_job_list, job_list>{}; DEF(else_clause) produces_alternative, else_continuation>>{}; DEF(else_continuation) produces_alternative, // seq>{}; DEF(switch_statement) produces_sequence, argument, tok_end, case_item_list, end_command, arguments_or_redirections_list>{}; DEF(case_item_list) produces_alternative, // seq>{}; DEF(case_item) produces_sequence, argument_list, tok_end, job_list>{}; DEF(block_statement) produces_sequence{}; DEF(block_header) produces_alternative{}; DEF(for_header) produces_sequence, tok_string, keyword, argument_list, tok_end>{}; DEF(while_header) produces_sequence, job, tok_end, andor_job_list>{}; struct begin_header : produces_single> {}; // Functions take arguments, and require at least one (the name). No redirections allowed. DEF(function_header) produces_sequence, argument, argument_list, tok_end>{}; // A boolean statement is AND or OR or NOT DEF(boolean_statement) produces_alternative, statement>, // seq, statement>, // seq, statement>>{}; // An andor_job_list is zero or more job lists, where each starts with an `and` or `or` boolean // statement. DEF(andor_job_list) produces_alternative, // seq>{}; // A decorated_statement is a command with a list of arguments_or_redirections, possibly with // "builtin" or "command" or "exec" DEF(decorated_statement) produces_alternative, plain_statement>, // seq, plain_statement>, // seq, plain_statement>>{}; DEF(plain_statement) produces_sequence{}; DEF(argument_list) produces_alternative>{}; DEF(arguments_or_redirections_list) produces_alternative>{}; DEF(argument_or_redirection) produces_alternative{}; DEF(argument) produces_single{}; DEF(optional_background) produces_alternative>{}; DEF(end_command) produces_single>{}; // A freestanding_argument_list is equivalent to a normal argument list, except it may contain // TOK_END (newlines, and even semicolons, for historical reasons) DEF(freestanding_argument_list) produces_alternative, // seq>{}; } #endif