fish-shell/parse_tree.h

308 lines
9 KiB
C
Raw Normal View History

/**\file parse_tree.h
Programmatic representation of fish code.
*/
2013-07-25 22:24:22 +00:00
#ifndef FISH_PARSE_PRODUCTIONS_H
#define FISH_PARSE_PRODUCTIONS_H
#include <wchar.h>
#include "config.h"
#include "util.h"
#include "common.h"
2013-06-02 05:14:47 +00:00
#include "tokenizer.h"
2013-06-11 16:37:51 +00:00
#include <vector>
#define PARSE_ASSERT(a) assert(a)
2013-08-08 22:06:46 +00:00
#define PARSER_DIE() do { fprintf(stderr, "Parser dying!\n"); exit_without_destructors(-1); } while (0)
class parse_node_t;
class parse_node_tree_t;
typedef size_t node_offset_t;
2013-06-23 09:09:46 +00:00
#define NODE_OFFSET_INVALID (static_cast<node_offset_t>(-1))
struct parse_error_t
{
/** Text of the error */
wcstring text;
/** Offset and length of the token in the source code that triggered this error */
size_t source_start;
size_t source_length;
/** Return a string describing the error, suitable for presentation to the user */
wcstring describe(const wcstring &src) const;
};
typedef std::vector<parse_error_t> parse_error_list_t;
2013-06-11 16:37:51 +00:00
enum parse_token_type_t
{
token_type_invalid,
2013-06-11 16:37:51 +00:00
// Non-terminal tokens
2013-06-23 09:09:46 +00:00
symbol_job_list,
symbol_job,
symbol_job_continuation,
2013-06-11 16:37:51 +00:00
symbol_statement,
symbol_block_statement,
symbol_block_header,
symbol_for_header,
symbol_while_header,
symbol_begin_header,
symbol_function_header,
2013-06-27 22:12:27 +00:00
symbol_if_statement,
symbol_if_clause,
symbol_else_clause,
symbol_else_continuation,
2013-06-30 22:38:31 +00:00
symbol_switch_statement,
symbol_case_item_list,
symbol_case_item,
2013-06-11 16:37:51 +00:00
symbol_boolean_statement,
symbol_decorated_statement,
symbol_plain_statement,
symbol_arguments_or_redirections_list,
symbol_argument_or_redirection,
2013-06-30 22:38:31 +00:00
symbol_argument_list,
2013-08-11 07:35:00 +00:00
2013-08-08 22:06:46 +00:00
symbol_argument,
symbol_redirection,
2013-08-11 07:35:00 +00:00
symbol_optional_background,
2013-06-11 16:37:51 +00:00
// Terminal types
parse_token_type_string,
parse_token_type_pipe,
parse_token_type_redirection,
parse_token_type_background,
2013-06-11 16:37:51 +00:00
parse_token_type_end,
parse_token_type_terminate,
2013-08-11 07:35:00 +00:00
2013-08-08 22:06:46 +00:00
// Very special terminal types that don't appear in the production list
parse_special_type_parse_error,
parse_special_type_tokenizer_error,
parse_special_type_comment,
2013-08-11 07:35:00 +00:00
FIRST_TERMINAL_TYPE = parse_token_type_string,
LAST_TERMINAL_TYPE = parse_token_type_terminate,
2013-07-27 06:59:12 +00:00
LAST_TOKEN_OR_SYMBOL = parse_token_type_terminate,
2013-06-11 16:37:51 +00:00
FIRST_PARSE_TOKEN_TYPE = parse_token_type_string
};
enum parse_keyword_t
{
parse_keyword_none,
parse_keyword_if,
parse_keyword_else,
parse_keyword_for,
parse_keyword_in,
parse_keyword_while,
parse_keyword_begin,
parse_keyword_function,
parse_keyword_switch,
2013-06-30 22:38:31 +00:00
parse_keyword_case,
2013-06-11 16:37:51 +00:00
parse_keyword_end,
parse_keyword_and,
parse_keyword_or,
parse_keyword_not,
parse_keyword_command,
2013-08-11 07:35:00 +00:00
parse_keyword_builtin,
LAST_KEYWORD = parse_keyword_builtin
};
enum
{
parse_flag_none = 0,
/* Attempt to build a "parse tree" no matter what. This may result in a 'forest' of disconnected trees. This is intended to be used by syntax highlighting. */
parse_flag_continue_after_error = 1 << 0,
2013-10-06 23:23:45 +00:00
2013-08-11 07:35:00 +00:00
/* Include comment tokens */
parse_flag_include_comments = 1 << 1
2013-06-11 16:37:51 +00:00
};
2013-08-11 07:35:00 +00:00
typedef unsigned int parse_tree_flags_t;
class parse_ll_t;
class parse_t
{
parse_ll_t * const parser;
public:
parse_t();
~parse_t();
/* Parse a string */
bool parse(const wcstring &str, parse_tree_flags_t flags, parse_node_tree_t *output, parse_error_list_t *errors, bool log_it = false);
/* Parse a single token */
bool parse_1_token(parse_token_type_t token, parse_keyword_t keyword, parse_node_tree_t *output, parse_error_list_t *errors);
/* Reset, ready to parse something else */
void clear();
};
wcstring parse_dump_tree(const parse_node_tree_t &tree, const wcstring &src);
2013-06-11 16:37:51 +00:00
2013-06-23 09:09:46 +00:00
wcstring token_type_description(parse_token_type_t type);
wcstring keyword_description(parse_keyword_t type);
2013-10-06 23:23:45 +00:00
/** Class for nodes of a parse tree */
2013-06-11 16:37:51 +00:00
class parse_node_t
{
public:
2013-06-11 16:37:51 +00:00
/* Type of the node */
enum parse_token_type_t type;
2013-06-11 16:37:51 +00:00
/* Start in the source code */
size_t source_start;
2013-06-11 16:37:51 +00:00
/* Length of our range in the source code */
size_t source_length;
2013-10-07 08:04:37 +00:00
/* Parent */
node_offset_t parent;
2013-06-11 16:37:51 +00:00
/* Children */
node_offset_t child_start;
node_offset_t child_count;
2013-06-11 16:37:51 +00:00
/* Type-dependent data */
uint32_t tag;
2013-08-11 07:35:00 +00:00
2013-07-28 22:19:38 +00:00
/* Which production was used */
uint8_t production_idx;
2013-06-11 16:37:51 +00:00
/* Description */
wcstring describe(void) const;
2013-06-11 16:37:51 +00:00
/* Constructor */
2013-10-07 08:04:37 +00:00
explicit parse_node_t(parse_token_type_t ty) : type(ty), source_start(-1), source_length(0), parent(NODE_OFFSET_INVALID), child_start(0), child_count(0), tag(0)
2013-06-11 16:37:51 +00:00
{
}
2013-06-23 09:09:46 +00:00
node_offset_t child_offset(node_offset_t which) const
{
PARSE_ASSERT(which < child_count);
return child_start + which;
}
2013-08-11 07:35:00 +00:00
2013-10-06 23:23:45 +00:00
/* Indicate if this node has a range of source code associated with it */
2013-08-08 22:06:46 +00:00
bool has_source() const
{
return source_start != (size_t)(-1);
}
2013-06-11 16:37:51 +00:00
};
2013-10-06 23:23:45 +00:00
/* The parse tree itself */
class parse_node_tree_t : public std::vector<parse_node_t>
{
2013-08-11 07:35:00 +00:00
public:
2013-08-08 22:06:46 +00:00
/* Get the node corresponding to a child of the given node, or NULL if there is no such child. If expected_type is provided, assert that the node has that type. */
2013-10-07 08:04:37 +00:00
const parse_node_t *get_child(const parse_node_t &parent, node_offset_t which, parse_token_type_t expected_type = token_type_invalid) const;
/* Get the node corresponding to the parent of the given node, or NULL if there is no such child. If expected_type is provided, only returns the parent if it is of that type. Note the asymmetry: get_child asserts since the children are known, but get_parent does not, since the parent may not be known. */
const parse_node_t *get_parent(const parse_node_t &node, parse_token_type_t expected_type = token_type_invalid) const;
2013-08-11 07:35:00 +00:00
2013-08-08 22:06:46 +00:00
/* Find all the nodes of a given type underneath a given node */
typedef std::vector<const parse_node_t *> parse_node_list_t;
parse_node_list_t find_nodes(const parse_node_t &parent, parse_token_type_t type) const;
};
2013-06-09 02:20:26 +00:00
2013-10-07 08:04:37 +00:00
/* Node type specific data, stored in the tag field */
2013-10-06 23:23:45 +00:00
/* Statement decorations, stored in the tag of plain_statement. This matches the order of productions in decorated_statement */
enum parse_statement_decoration_t
{
parse_statement_decoration_none,
parse_statement_decoration_command,
parse_statement_decoration_builtin
};
2013-06-09 02:20:26 +00:00
2013-10-07 08:04:37 +00:00
/* Argument flags as a bitmask, stored in the tag of argument */
enum parse_argument_flags_t
{
/* Indicates that this or a prior argument was --, so this should not be treated as an option */
parse_argument_no_options = 1 << 0,
/* Indicates that the argument is for a cd command */
parse_argument_is_for_cd = 1 << 1
};
/* Fish grammar:
2013-06-23 09:09:46 +00:00
# A job_list is a list of jobs, separated by semicolons or newlines
2013-06-23 09:09:46 +00:00
job_list = <empty> |
job job_list
2013-08-11 07:35:00 +00:00
<TOK_END> job_list
2013-06-23 09:09:46 +00: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
2013-06-23 09:09:46 +00:00
job = statement job_continuation
job_continuation = <empty> |
2013-06-23 09:09:46 +00:00
<TOK_PIPE> statement job_continuation
# A statement is a normal command, or an if / while / and etc
2013-06-30 22:38:31 +00:00
statement = boolean_statement | block_statement | if_statement | switch_statement | decorated_statement
# A block is a conditional, loop, or begin/end
if_statement = if_clause else_clause <END> arguments_or_redirections_list
2013-06-27 22:12:27 +00:00
if_clause = <IF> job STATEMENT_TERMINATOR job_list
else_clause = <empty> |
<ELSE> else_continuation
else_continuation = if_clause else_clause |
STATEMENT_TERMINATOR job_list
2013-06-30 22:38:31 +00:00
switch_statement = SWITCH <TOK_STRING> STATEMENT_TERMINATOR case_item_list <END>
case_item_list = <empty> |
case_item case_item_list
case_item = CASE argument_list STATEMENT_TERMINATOR job_list
block_statement = block_header <TOK_END> job_list <END> arguments_or_redirections_list
2013-06-27 22:12:27 +00:00
block_header = for_header | while_header | function_header | begin_header
for_header = FOR var_name IN arguments_or_redirections_list
while_header = WHILE statement
2013-06-23 09:09:46 +00:00
begin_header = BEGIN
2013-06-30 22:38:31 +00:00
function_header = FUNCTION function_name argument_list
# A boolean statement is AND or OR or NOT
boolean_statement = AND statement | OR statement | NOT statement
# A decorated_statement is a command with a list of arguments_or_redirections, possibly with "builtin" or "command"
2013-10-06 23:23:45 +00:00
# The tag of a plain statement indicates which mode to use
2013-10-06 23:23:45 +00:00
decorated_statement = plain_statement | COMMAND plain_statement | BUILTIN plain_statement
plain_statement = <TOK_STRING> arguments_or_redirections_list optional_background
2013-10-07 08:04:37 +00:00
argument_list = <empty> | argument argument_list
2013-06-02 05:14:47 +00:00
arguments_or_redirections_list = <empty> |
argument_or_redirection arguments_or_redirections_list
2013-08-08 22:06:46 +00:00
argument_or_redirection = argument | redirection
argument = <TOK_STRING>
2013-06-02 05:14:47 +00:00
redirection = <TOK_REDIRECTION>
2013-08-11 07:35:00 +00:00
2013-06-02 05:14:47 +00:00
terminator = <TOK_END> | <TOK_BACKGROUND>
2013-08-11 07:35:00 +00:00
optional_background = <empty> | <TOK_BACKGROUND>
2013-06-02 05:14:47 +00:00
*/
#endif