語法分析 - 入口點
--- main()
我們打開shell.c的main函數,大概300來行,其主題都是圍繞這xxx_init,做各種初始化操作。
我們可以略過不看,等遇到問題的時候再說。把目光放到最后一句 reader_loop()。這是一個循環讀
入并執行命令的函數。
--- reader_loop()
位于eval.c的reader_loop()函數,其中仿佛只有調用read_command()是重點。
--- read_command()
同樣位于eval.c的read_command()函數。一開始那一段ALARM信號的處理讓人覺得很費解,難道
在bash輸入命令還要有時間限制嗎?無論如何,這種看似偏門的、非關鍵性的東西,在代碼分析的初期
是不能理會的,如果太深究這些東西,沒有把握代碼的主線,則會走入死胡同,而且會失去源碼分析
的樂趣。
代碼主線走入parse_command()函數。
--- parse_command()
同樣位于eval.c的parse_command()函數。它調用的yyparse()函數是語法分析的開始。
用過yacc的人很明白這一點了。一開始我們看到文件列表中有y.tab.c這樣的文件,就能意識到bash也是
利用yacc生成的代碼來完成語法分析的。
--- Yacc的作用
你只要告訴yacc三樣東西:語法、每一條語法的處理函數、負責詞法分析的函數
yacc就會為你生成y.tab.c文件,只要調用這個文件中的yyparse()函數,就可以完成編譯器的
詞法分析和語法分析的部分了。在分析的過程中,你剛剛指定的每一條語法對應的處理函數也會
被調用。關于yacc的具體介紹,可以在網上搜搜,很多的。
例子:
告訴yacc:語法和對應的處理函數。
expr : expr '+' expr { $$ = add($1, $3) }
| expr '*' expr { $$ = mul($1, $3) }
| expr '-' expr { $$ = sub($1, $3) }
| NUMBER
;
調用yyparse(),輸入 1 + 2
add(1, 2) 就會被回調了
在處理函數中 $$ 代表著處理函數的返回值
$1 代表著該條語法中的第一個元素(expr)
$2 代表著該條語法中的第二個元素('+')
$3 代表著該條語法中的第三個元素(expr)
至于說這些元素的類型,則會在前面定義。比如 %type<char *> expr 之類。
具體的還是找篇文章看看吧。
--- parse.y
觀察Makefile可以發現:
y.tab.c y.tab.h: parse.y
$(YACC) -d $(srcdir)/parse.y
y.tab.c是由parse.y生成的。而parse.y中包含了語法和對應的處理函數,它是語法分析的核心文件。
首先是一個%union定義
%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;
}
然后是一系列的token定義:
/* 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
%token COND_START COND_END COND_ERROR
%token IN BANG TIME TIMEOPT
/* More general tokens. yylex () knows how to make these. */
%token <word> WORD ASSIGNMENT_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 LESS_LESS_MINUS AND_GREATER LESS_GREATER
%token GREATER_BAR
讀入字符串流,返回token是詞法分析函數的責任。
以%token定義,表明返回值是int類型
以%token <word>定義,表明返回值是%union中對應的類型
詞法分析函數是lex生成的,但這個工程好像把原始的
.lex文件刪除了。我們只能看到生成后的yylex()函數。
但有一個表,可以看出token對應的字串內容:
/* Reserved words. These are only recognized as the first word of a
command. */
STRING_INT_ALIST word_token_alist[] = {
{ "if", IF },
{ "then", THEN },
{ "else", ELSE },
{ "elif", ELIF },
{ "fi", FI },
{ "case", CASE },
{ "esac", ESAC },
{ "for", FOR },
#if defined (SELECT_COMMAND)
{ "select", SELECT },
#endif
{ "while", WHILE },
{ "until", UNTIL },
{ "do", DO },
{ "done", DONE },
{ "in", IN },
{ "function", FUNCTION },
#if defined (COMMAND_TIMING)
{ "time", TIME },
#endif
{ "{", '{' },
{ "}", '}' },
{ "!", BANG },
#if defined (COND_COMMAND)
{ "[[", COND_START },
{ "]]", COND_END },
#endif
{ (char *)NULL, 0}
};
/* other tokens that can be returned by read_token() */
STRING_INT_ALIST other_token_alist[] = {
/* Multiple-character tokens with special values */
{ "-p", TIMEOPT },
{ "&&", AND_AND },
{ "||", OR_OR },
{ ">>", GREATER_GREATER },
{ "<<", LESS_LESS },
{ "<&", LESS_AND },
{ ">&", GREATER_AND },
{ ";;", SEMI_SEMI },
{ "<<-", LESS_LESS_MINUS },
{ "<<<", LESS_LESS_LESS },
{ "&>", AND_GREATER },
{ "<>", LESS_GREATER },
{ ">|", GREATER_BAR },
{ "EOF", yacc_EOF },
/* Tokens whose value is the character itself */
{ ">", '>' },
{ "<", '<' },
{ "-", '-' },
{ "{", '{' },
{ "}", '}' },
{ ";", ';' },
{ "(", '(' },
{ ")", ')' },
{ "|", '|' },
{ "&", '&' },
{ "newline", '\n' },
{ (char *)NULL, 0}
};
/* others not listed here:
WORD look at yylval.word
ASSIGNMENT_WORD look at yylval.word
NUMBER look at yylval.number
ARITH_CMD look at yylval.word_list
ARITH_FOR_EXPRS look at yylval.word_list
COND_CMD look at yylval.command
*/
這些token在語法中會遇到的。
接下來是對語法中每一項內容(編譯原理沒學好,不知道這個術語叫什么。。)的定義:
/* 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> 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
從名字上來看,大概能知道是作什么的。
%start 表示整個語法分析的入口是 inputunit 那一項。
接著就是語法了,內容就比較多,不直接貼了。
語法是我比較感興趣的地方,無論看哪本關于bash的書,都不如看代碼來的直接,呵呵。
我們以后慢慢看。