2018-01-08 05:34:04 +08:00
|
|
|
// Programmatic representation of fish grammar
|
|
|
|
#ifndef FISH_PARSE_GRAMMAR_H
|
|
|
|
#define FISH_PARSE_GRAMMAR_H
|
|
|
|
|
|
|
|
#include "parse_constants.h"
|
|
|
|
#include "tokenizer.h"
|
2018-01-08 07:53:36 +08:00
|
|
|
#include <array>
|
2018-01-08 05:34:04 +08:00
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
struct parse_token_t;
|
|
|
|
typedef uint8_t parse_node_tag_t;
|
|
|
|
|
|
|
|
using parse_node_tag_t = uint8_t;
|
|
|
|
struct parse_token_t;
|
2018-01-08 05:34:04 +08:00
|
|
|
namespace grammar {
|
2018-01-08 07:53:36 +08:00
|
|
|
|
2018-01-08 05:34:04 +08:00
|
|
|
using production_element_t = uint8_t;
|
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
// Define primitive types.
|
|
|
|
template <enum parse_token_type_t Token>
|
|
|
|
struct primitive {
|
2018-01-08 11:50:34 +08:00
|
|
|
using type_tuple = std::tuple<>;
|
|
|
|
static constexpr parse_token_type_t token = Token;
|
2018-01-08 07:53:36 +08:00
|
|
|
static constexpr production_element_t element() { return Token; }
|
|
|
|
};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
using tok_end = primitive<parse_token_type_end>;
|
|
|
|
using tok_string = primitive<parse_token_type_string>;
|
|
|
|
using tok_pipe = primitive<parse_token_type_pipe>;
|
|
|
|
using tok_background = primitive<parse_token_type_background>;
|
|
|
|
using tok_redirection = primitive<parse_token_type_redirection>;
|
|
|
|
|
|
|
|
// Define keyword types.
|
|
|
|
template <parse_keyword_t Keyword>
|
|
|
|
struct keyword {
|
2018-01-08 11:50:34 +08:00
|
|
|
using type_tuple = std::tuple<>;
|
2018-01-08 07:53:36 +08:00
|
|
|
static constexpr production_element_t element() {
|
|
|
|
// Convert a parse_keyword_t enum to a production_element_t enum.
|
|
|
|
return Keyword + LAST_TOKEN_OR_SYMBOL + 1;
|
|
|
|
}
|
2018-01-08 05:34:04 +08:00
|
|
|
};
|
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
// Forward declare all the symbol types.
|
|
|
|
#define ELEM(T) struct T;
|
|
|
|
#include "parse_grammar_elements.inc"
|
2018-01-08 05:34:04 +08:00
|
|
|
|
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
// A production is a sequence of production elements.
|
|
|
|
// +1 to hold the terminating token_type_invalid
|
|
|
|
template <size_t Count>
|
|
|
|
using production_t = std::array<const production_element_t, Count + 1>;
|
2018-01-08 05:34:04 +08:00
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
// This is an ugly hack to avoid ODR violations
|
|
|
|
// Given some type, return a pointer to its production.
|
2018-01-08 05:34:04 +08:00
|
|
|
template <typename T>
|
2018-01-08 07:53:36 +08:00
|
|
|
const production_element_t *production_for() {
|
|
|
|
static constexpr auto prod = T::production;
|
|
|
|
return prod.data();
|
|
|
|
}
|
2018-01-08 05:34:04 +08:00
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
// Get some production element.
|
|
|
|
template <typename T>
|
|
|
|
constexpr production_element_t element() {
|
|
|
|
return T::element();
|
|
|
|
}
|
2018-01-08 05:34:04 +08:00
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
// Partial specialization hack.
|
|
|
|
#define ELEM(T) \
|
|
|
|
template <> \
|
|
|
|
constexpr production_element_t element<T>() { \
|
|
|
|
return symbol_##T; \
|
|
|
|
}
|
|
|
|
#include "parse_grammar_elements.inc"
|
2018-01-08 05:34:04 +08:00
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
// Empty produces nothing.
|
|
|
|
struct empty {
|
|
|
|
static constexpr production_t<0> production = {{token_type_invalid}};
|
|
|
|
static const production_element_t *resolve(const parse_token_t &, const parse_token_t &,
|
|
|
|
parse_node_tag_t *) {
|
|
|
|
return production_for<empty>();
|
|
|
|
}
|
|
|
|
};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
// Sequence represents a list of (at least two) productions.
|
|
|
|
template <class T0, class... Ts>
|
|
|
|
struct seq {
|
|
|
|
static constexpr production_t<1 + sizeof...(Ts)> production = {
|
|
|
|
{element<T0>(), element<Ts>()..., token_type_invalid}};
|
2018-01-08 11:07:49 +08:00
|
|
|
|
|
|
|
using type_tuple = std::tuple<T0, Ts...>;
|
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
static const production_element_t *resolve(const parse_token_t &, const parse_token_t &,
|
|
|
|
parse_node_tag_t *) {
|
|
|
|
return production_for<seq>();
|
|
|
|
}
|
|
|
|
};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
|
|
|
template <class... Args>
|
2018-01-08 07:53:36 +08:00
|
|
|
using produces_sequence = seq<Args...>;
|
2018-01-08 05:34:04 +08:00
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
// Ergonomic way to create a production for a single element.
|
|
|
|
template <class T>
|
|
|
|
using single = seq<T>;
|
2018-01-08 05:34:04 +08:00
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
template <class T>
|
|
|
|
using produces_single = single<T>;
|
|
|
|
|
|
|
|
// Alternative represents a choice.
|
|
|
|
struct alternative {
|
|
|
|
};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
|
|
|
// Following are the grammar productions.
|
2018-01-08 11:50:34 +08:00
|
|
|
#define BODY(T) static constexpr parse_token_type_t token = symbol_##T;
|
|
|
|
|
2018-01-08 05:34:04 +08:00
|
|
|
#define DEF(T) struct T : public
|
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
#define DEF_ALT(T) struct T : public alternative
|
|
|
|
#define ALT_BODY(T) \
|
|
|
|
BODY(T) \
|
2018-01-12 01:41:49 +08:00
|
|
|
using type_tuple = std::tuple<>; \
|
2018-01-08 07:53:36 +08:00
|
|
|
static const production_element_t *resolve(const parse_token_t &, const parse_token_t &, \
|
|
|
|
parse_node_tag_t *);
|
|
|
|
|
2018-01-08 05:34:04 +08:00
|
|
|
// A job_list is a list of jobs, separated by semicolons or newlines
|
2018-01-08 07:53:36 +08:00
|
|
|
DEF_ALT(job_list) {
|
|
|
|
using normal = seq<job, job_list>;
|
|
|
|
using empty_line = seq<tok_end, job_list>;
|
|
|
|
using empty = grammar::empty;
|
|
|
|
ALT_BODY(job_list);
|
|
|
|
};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
|
|
|
// 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 '&'
|
2018-01-08 07:53:36 +08:00
|
|
|
DEF(job) produces_sequence<statement, job_continuation, optional_background>{BODY(job)};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
DEF_ALT(job_continuation) {
|
|
|
|
using piped = seq<tok_pipe, statement, job_continuation>;
|
|
|
|
ALT_BODY(job_continuation);
|
|
|
|
};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
|
|
|
// A statement is a normal command, or an if / while / and etc
|
2018-01-08 07:53:36 +08:00
|
|
|
DEF_ALT(statement) {
|
|
|
|
using boolean = single<boolean_statement>;
|
|
|
|
using block = single<block_statement>;
|
|
|
|
using ifs = single<if_statement>;
|
|
|
|
using switchs = single<switch_statement>;
|
|
|
|
using decorated = single<decorated_statement>;
|
|
|
|
ALT_BODY(statement);
|
|
|
|
};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
|
|
|
// A block is a conditional, loop, or begin/end
|
|
|
|
DEF(if_statement)
|
2018-01-08 07:53:36 +08:00
|
|
|
produces_sequence<if_clause, else_clause, end_command, arguments_or_redirections_list>{
|
|
|
|
BODY(if_statement)};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
|
|
|
DEF(if_clause)
|
2018-01-08 07:53:36 +08:00
|
|
|
produces_sequence<keyword<parse_keyword_if>, job, tok_end, andor_job_list, job_list>{
|
|
|
|
BODY(if_clause)};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
DEF_ALT(else_clause) {
|
|
|
|
using empty = grammar::empty;
|
|
|
|
using else_cont = seq<keyword<parse_keyword_else>, else_continuation>;
|
|
|
|
ALT_BODY(else_clause);
|
|
|
|
};
|
|
|
|
|
|
|
|
DEF_ALT(else_continuation) {
|
|
|
|
using else_if = seq<if_clause, else_clause>;
|
|
|
|
using else_only = seq<tok_end, job_list>;
|
|
|
|
ALT_BODY(else_continuation);
|
|
|
|
};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
|
|
|
DEF(switch_statement)
|
|
|
|
produces_sequence<keyword<parse_keyword_switch>, argument, tok_end, case_item_list, end_command,
|
2018-01-08 07:53:36 +08:00
|
|
|
arguments_or_redirections_list>{BODY(switch_statement)};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
DEF_ALT(case_item_list) {
|
|
|
|
using empty = grammar::empty;
|
|
|
|
using case_items = seq<case_item, case_item_list>;
|
|
|
|
using blank_line = seq<tok_end, case_item_list>;
|
|
|
|
ALT_BODY(case_item_list);
|
|
|
|
};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
DEF(case_item) produces_sequence<keyword<parse_keyword_case>, argument_list, tok_end, job_list> {
|
|
|
|
BODY(case_item);
|
|
|
|
};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
|
|
|
DEF(block_statement)
|
|
|
|
produces_sequence<block_header, job_list, end_command, arguments_or_redirections_list>{};
|
2018-01-08 07:53:36 +08:00
|
|
|
|
|
|
|
DEF_ALT(block_header) {
|
|
|
|
using forh = single<for_header>;
|
|
|
|
using whileh = single<while_header>;
|
|
|
|
using funch = single<function_header>;
|
|
|
|
using beginh = single<begin_header>;
|
|
|
|
ALT_BODY(block_header);
|
|
|
|
};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
|
|
|
DEF(for_header)
|
|
|
|
produces_sequence<keyword<parse_keyword_for>, tok_string, keyword<parse_keyword_in>, argument_list,
|
|
|
|
tok_end>{};
|
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
DEF(while_header)
|
|
|
|
produces_sequence<keyword<parse_keyword_while>, job, tok_end, andor_job_list>{BODY(while_header)};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
DEF(begin_header) produces_single<keyword<parse_keyword_begin>>{BODY(begin_header)};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
|
|
|
// Functions take arguments, and require at least one (the name). No redirections allowed.
|
|
|
|
DEF(function_header)
|
|
|
|
produces_sequence<keyword<parse_keyword_function>, argument, argument_list, tok_end>{};
|
|
|
|
|
|
|
|
// A boolean statement is AND or OR or NOT
|
2018-01-08 07:53:36 +08:00
|
|
|
DEF_ALT(boolean_statement) {
|
|
|
|
using ands = seq<keyword<parse_keyword_and>, statement>;
|
|
|
|
using ors = seq<keyword<parse_keyword_or>, statement>;
|
|
|
|
using nots = seq<keyword<parse_keyword_not>, statement>;
|
|
|
|
ALT_BODY(boolean_statement);
|
|
|
|
};
|
|
|
|
|
2018-01-08 05:34:04 +08:00
|
|
|
// An andor_job_list is zero or more job lists, where each starts with an `and` or `or` boolean
|
|
|
|
// statement.
|
2018-01-08 07:53:36 +08:00
|
|
|
DEF_ALT(andor_job_list) {
|
|
|
|
using empty = grammar::empty;
|
|
|
|
using andor_job = seq<job, andor_job_list>;
|
|
|
|
using empty_line = seq<tok_end, andor_job_list>;
|
|
|
|
ALT_BODY(andor_job_list);
|
|
|
|
};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
|
|
|
// A decorated_statement is a command with a list of arguments_or_redirections, possibly with
|
|
|
|
// "builtin" or "command" or "exec"
|
2018-01-08 07:53:36 +08:00
|
|
|
DEF_ALT(decorated_statement) {
|
|
|
|
using plains = single<plain_statement>;
|
|
|
|
using cmds = seq<keyword<parse_keyword_command>, plain_statement>;
|
|
|
|
using builtins = seq<keyword<parse_keyword_builtin>, plain_statement>;
|
|
|
|
using execs = seq<keyword<parse_keyword_exec>, plain_statement>;
|
|
|
|
ALT_BODY(decorated_statement);
|
|
|
|
};
|
|
|
|
|
|
|
|
DEF(plain_statement)
|
|
|
|
produces_sequence<tok_string, arguments_or_redirections_list>{BODY(plain_statement)};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
DEF_ALT(argument_list) {
|
|
|
|
using empty = grammar::empty;
|
|
|
|
using arg = seq<argument, argument_list>;
|
|
|
|
ALT_BODY(argument_list);
|
|
|
|
};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
DEF_ALT(arguments_or_redirections_list) {
|
|
|
|
using empty = grammar::empty;
|
|
|
|
using value = seq<argument_or_redirection, arguments_or_redirections_list>;
|
|
|
|
ALT_BODY(arguments_or_redirections_list);
|
|
|
|
};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
2018-01-08 07:53:36 +08:00
|
|
|
DEF_ALT(argument_or_redirection) {
|
|
|
|
using arg = single<argument>;
|
|
|
|
using redir = single<redirection>;
|
|
|
|
ALT_BODY(argument_or_redirection);
|
|
|
|
};
|
|
|
|
|
|
|
|
DEF(argument) produces_single<tok_string>{BODY(argument)};
|
|
|
|
DEF(redirection) produces_sequence<tok_redirection, tok_string>{BODY(redirection)};
|
|
|
|
|
|
|
|
DEF_ALT(optional_background) {
|
|
|
|
using empty = grammar::empty;
|
|
|
|
using background = single<tok_background>;
|
|
|
|
ALT_BODY(optional_background);
|
|
|
|
};
|
|
|
|
|
|
|
|
DEF(end_command) produces_single<keyword<parse_keyword_end>>{BODY(end_command)};
|
2018-01-08 05:34:04 +08:00
|
|
|
|
|
|
|
// A freestanding_argument_list is equivalent to a normal argument list, except it may contain
|
|
|
|
// TOK_END (newlines, and even semicolons, for historical reasons)
|
2018-01-08 07:53:36 +08:00
|
|
|
DEF_ALT(freestanding_argument_list) {
|
|
|
|
using empty = grammar::empty;
|
|
|
|
using arg = seq<argument, freestanding_argument_list>;
|
|
|
|
using semicolon = seq<tok_end, freestanding_argument_list>;
|
|
|
|
ALT_BODY(freestanding_argument_list);
|
|
|
|
};
|
2018-01-08 05:34:04 +08:00
|
|
|
}
|
|
|
|
#endif
|