📄 parse.y
字号:
/* parse.y - Yacc grammar for bash. *//* Copyright (C) 1989-2010 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. Bash is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Bash is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Bash. If not, see <http://www.gnu.org/licenses/>.*/%{#include "config.h"#include "bashtypes.h"#include "bashansi.h"#include "filecntl.h"#if defined (HAVE_UNISTD_H)# include <unistd.h>#endif#if defined (HAVE_LOCALE_H)# include <locale.h>#endif#include <stdio.h>#include "chartypes.h"#include <signal.h>#include "memalloc.h"#include "bashintl.h"#define NEED_STRFTIME_DECL /* used in externs.h */#include "shell.h"#include "trap.h"#include "flags.h"#include "parser.h"#include "mailcheck.h"#include "test.h"#include "builtins.h"#include "builtins/common.h"#include "builtins/builtext.h"#include "shmbutil.h"#if defined (READLINE)# include "bashline.h"# include <readline/readline.h>#endif /* READLINE */#if defined (HISTORY)# include "bashhist.h"# include <readline/history.h>#endif /* HISTORY */#if defined (JOB_CONTROL)# include "jobs.h"#endif /* JOB_CONTROL */#if defined (ALIAS)# include "alias.h"#elsetypedef void *alias_t;#endif /* ALIAS */#if defined (PROMPT_STRING_DECODE)# ifndef _MINIX# include <sys/param.h># endif# include <time.h># if defined (TM_IN_SYS_TIME)# include <sys/types.h># include <sys/time.h># endif /* TM_IN_SYS_TIME */# include "maxpath.h"#endif /* PROMPT_STRING_DECODE */#define RE_READ_TOKEN -99#define NO_EXPANSION -100#ifdef DEBUG# define YYDEBUG 1#else# define YYDEBUG 0#endif#if defined (HANDLE_MULTIBYTE)# define last_shell_getc_is_singlebyte \ ((shell_input_line_index > 1) \ ? shell_input_line_property[shell_input_line_index - 1] \ : 1)# define MBTEST(x) ((x) && last_shell_getc_is_singlebyte)#else# define last_shell_getc_is_singlebyte 1# define MBTEST(x) ((x))#endif#if defined (EXTENDED_GLOB)extern int extended_glob;#endifextern int eof_encountered;extern int no_line_editing, running_under_emacs;extern int current_command_number;extern int sourcelevel, parse_and_execute_level;extern int posixly_correct;extern int last_command_exit_value;extern pid_t last_command_subst_pid;extern char *shell_name, *current_host_name;extern char *dist_version;extern int patch_level;extern int dump_translatable_strings, dump_po_strings;extern sh_builtin_func_t *last_shell_builtin, *this_shell_builtin;#if defined (BUFFERED_INPUT)extern int bash_input_fd_changed;#endifextern int errno;/* **************************************************************** *//* *//* "Forward" declarations *//* *//* **************************************************************** */#ifdef DEBUGstatic void debug_parser __P((int));#endifstatic int yy_getc __P((void));static int yy_ungetc __P((int));#if defined (READLINE)static int yy_readline_get __P((void));static int yy_readline_unget __P((int));#endifstatic int yy_string_get __P((void));static int yy_string_unget __P((int));static void rewind_input_string __P((void));static int yy_stream_get __P((void));static int yy_stream_unget __P((int));static int shell_getc __P((int));static void shell_ungetc __P((int));static void discard_until __P((int));#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)static void push_string __P((char *, int, alias_t *));static void pop_string __P((void));static void free_string_list __P((void));#endifstatic char *read_a_line __P((int));static int reserved_word_acceptable __P((int));static int yylex __P((void));static int alias_expand_token __P((char *));static int time_command_acceptable __P((void));static int special_case_tokens __P((char *));static int read_token __P((int));static char *parse_matched_pair __P((int, int, int, int *, int));static char *parse_comsub __P((int, int, int, int *, int));#if defined (ARRAY_VARS)static char *parse_compound_assignment __P((int *));#endif#if defined (DPAREN_ARITHMETIC) || defined (ARITH_FOR_COMMAND)static int parse_dparen __P((int));static int parse_arith_cmd __P((char **, int));#endif#if defined (COND_COMMAND)static void cond_error __P((void));static COND_COM *cond_expr __P((void));static COND_COM *cond_or __P((void));static COND_COM *cond_and __P((void));static COND_COM *cond_term __P((void));static int cond_skip_newlines __P((void));static COMMAND *parse_cond_command __P((void));#endif#if defined (ARRAY_VARS)static int token_is_assignment __P((char *, int));static int token_is_ident __P((char *, int));#endifstatic int read_token_word __P((int));static void discard_parser_constructs __P((int));static char *error_token_from_token __P((int));static char *error_token_from_text __P((void));static void print_offending_line __P((void));static void report_syntax_error __P((char *));static void handle_eof_input_unit __P((void));static void prompt_again __P((void));#if 0static void reset_readline_prompt __P((void));#endifstatic void print_prompt __P((void));#if defined (HANDLE_MULTIBYTE)static void set_line_mbstate __P((void));static char *shell_input_line_property = NULL;#else# define set_line_mbstate()#endifextern int yyerror __P((const char *));#ifdef DEBUGextern int yydebug;#endif/* Default prompt strings */char *primary_prompt = PPROMPT;char *secondary_prompt = SPROMPT;/* PROMPT_STRING_POINTER points to one of these, never to an actual string. */char *ps1_prompt, *ps2_prompt;/* Handle on the current prompt string. Indirectly points through ps1_ or ps2_prompt. */char **prompt_string_pointer = (char **)NULL;char *current_prompt_string;/* Non-zero means we expand aliases in commands. */int expand_aliases = 0;/* If non-zero, the decoded prompt string undergoes parameter and variable substitution, command substitution, arithmetic substitution, string expansion, process substitution, and quote removal in decode_prompt_string. */int promptvars = 1;/* If non-zero, $'...' and $"..." are expanded when they appear within a ${...} expansion, even when the expansion appears within double quotes. */int extended_quote = 1;/* The number of lines read from input while creating the current command. */int current_command_line_count;/* The number of lines in a command saved while we run parse_and_execute */int saved_command_line_count;/* The token that currently denotes the end of parse. */int shell_eof_token;/* The token currently being read. */int current_token;/* The current parser state. */int parser_state;/* Variables to manage the task of reading here documents, because we need to defer the reading until after a complete command has been collected. */static REDIRECT *redir_stack[10];int need_here_doc;/* Where shell input comes from. History expansion is performed on each line when the shell is interactive. */static char *shell_input_line = (char *)NULL;static int shell_input_line_index;static int shell_input_line_size; /* Amount allocated for shell_input_line. */static int shell_input_line_len; /* strlen (shell_input_line) *//* Either zero or EOF. */static int shell_input_line_terminator;/* The line number in a script on which a function definition starts. */static int function_dstart;/* The line number in a script on which a function body starts. */static int function_bstart;/* The line number in a script at which an arithmetic for command starts. */static int arith_for_lineno;/* The decoded prompt string. Used if READLINE is not defined or if editing is turned off. Analogous to current_readline_prompt. */static char *current_decoded_prompt;/* The last read token, or NULL. read_token () uses this for context checking. */static int last_read_token;/* The token read prior to last_read_token. */static int token_before_that;/* The token read prior to token_before_that. */static int two_tokens_ago;static int global_extglob;/* The line number in a script where the word in a `case WORD', `select WORD' or `for WORD' begins. This is a nested command maximum, since the array index is decremented after a case, select, or for command is parsed. */#define MAX_CASE_NEST 128static int word_lineno[MAX_CASE_NEST];static int word_top = -1;/* If non-zero, it is the token that we want read_token to return regardless of what text is (or isn't) present to be read. This is reset by read_token. If token_to_read == WORD or ASSIGNMENT_WORD, yylval.word should be set to word_desc_to_read. */static int token_to_read;static WORD_DESC *word_desc_to_read;static REDIRECTEE source;static REDIRECTEE redir;%}%union { WORD_DESC *word; /* the word that we read. */ int number; /* the number that we read. */ WORD_LIST *word_list; COMMAND *command; REDIRECT *redirect; ELEMENT element; PATTERN_LIST *pattern;}/* Reserved words. Members of the first group are only recognized in the case that they are preceded by a list_terminator. Members of the second group are for [[...]] commands. Members of the third group are recognized only under special circumstances. */%token IF THEN ELSE ELIF FI CASE ESAC FOR SELECT WHILE UNTIL DO DONE FUNCTION COPROC%token COND_START COND_END COND_ERROR%token IN BANG TIME TIMEOPT TIMEIGN/* More general tokens. yylex () knows how to make these. */%token <word> WORD ASSIGNMENT_WORD REDIR_WORD%token <number> NUMBER%token <word_list> ARITH_CMD ARITH_FOR_EXPRS%token <command> COND_CMD%token AND_AND OR_OR GREATER_GREATER LESS_LESS LESS_AND LESS_LESS_LESS%token GREATER_AND SEMI_SEMI SEMI_AND SEMI_SEMI_AND%token LESS_LESS_MINUS AND_GREATER AND_GREATER_GREATER LESS_GREATER%token GREATER_BAR BAR_AND/* The types that the various syntactical units return. */%type <command> inputunit command pipeline pipeline_command%type <command> list list0 list1 compound_list simple_list simple_list1%type <command> simple_command shell_command%type <command> for_command select_command case_command group_command%type <command> arith_command%type <command> cond_command%type <command> arith_for_command%type <command> coproc%type <command> function_def function_body if_command elif_clause subshell%type <redirect> redirection redirection_list%type <element> simple_command_element%type <word_list> word_list pattern%type <pattern> pattern_list case_clause_sequence case_clause%type <number> timespec%type <number> list_terminator%start inputunit%left '&' ';' '\n' yacc_EOF%left AND_AND OR_OR%right '|' BAR_AND%%inputunit: simple_list simple_list_terminator { /* Case of regular command. Discard the error safety net,and return the command just parsed. */ global_command = $1; eof_encountered = 0; /* discard_parser_constructs (0); */ if (parser_state & PST_CMDSUBST) parser_state |= PST_EOFTOKEN; YYACCEPT; } | '\n' { /* Case of regular command, but not a very interesting one. Return a NULL command. */ global_command = (COMMAND *)NULL; if (parser_state & PST_CMDSUBST) parser_state |= PST_EOFTOKEN; YYACCEPT; } | error '\n' { /* Error during parsing. Return NULL command. */ global_command = (COMMAND *)NULL; eof_encountered = 0; /* discard_parser_constructs (1); */ if (interactive && parse_and_execute_level == 0) { YYACCEPT; } else { YYABORT; } } | yacc_EOF { /* Case of EOF seen by itself. Do ignoreeof or not. */ global_command = (COMMAND *)NULL; handle_eof_input_unit (); YYACCEPT; } ;word_list: WORD { $$ = make_word_list ($1, (WORD_LIST *)NULL); } | word_list WORD { $$ = make_word_list ($2, $1); } ;redirection: '>' WORD { source.dest = 1; redir.filename = $2; $$ = make_redirection (source, r_output_direction, redir, 0); } | '<' WORD { source.dest = 0; redir.filename = $2; $$ = make_redirection (source, r_input_direction, redir, 0); } | NUMBER '>' WORD { source.dest = $1; redir.filename = $3; $$ = make_redirection (source, r_output_direction, redir, 0); } | NUMBER '<' WORD { source.dest = $1; redir.filename = $3; $$ = make_redirection (source, r_input_direction, redir, 0); } | REDIR_WORD '>' WORD { source.filename = $1; redir.filename = $3; $$ = make_redirection (source, r_output_direction, redir, REDIR_VARASSIGN); } | REDIR_WORD '<' WORD { source.filename = $1; redir.filename = $3; $$ = make_redirection (source, r_input_direction, redir, REDIR_VARASSIGN); } | GREATER_GREATER WORD { source.dest = 1; redir.filename = $2; $$ = make_redirection (source, r_appending_to, redir, 0); } | NUMBER GREATER_GREATER WORD { source.dest = $1; redir.filename = $3; $$ = make_redirection (source, r_appending_to, redir, 0); } | REDIR_WORD GREATER_GREATER WORD { source.filename = $1; redir.filename = $3; $$ = make_redirection (source, r_appending_to, redir, REDIR_VARASSIGN); } | GREATER_BAR WORD { source.dest = 1; redir.filename = $2;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -