附件中包含了到目前为止的所有文件,除了COOL目录之外。
语法分析的功能基本上成熟了,调试也完成了,现在剩下将这些东西整合起来成为一个没有调试输出(特别是内存分配调试)的整体了。首先要做的事情是,把COOL中的Queue/Stack这些东西包含进来,就放在datastruct.h中。
#ifndef _DATA_STRUCTURE_H #define _DATA_STRUCTURE_H #include"const.h" #include"COOL/Stack/Stack.h" #include"COOL/Queue/Queue.h" #include"COOL/MemoryAllocationDebugger.h" // blablabla #endif /* _DATA_STRUCTURE_H */
这样就不用在其它每个文件中都重复地引用这些头文件了。接着,我们可以抽取原来词法分析主文件中的一些函数出来,让语法分析也可以使用,如这些函数。
/* jerry-ui.h */ #ifndef _JERRY_UI_H #define _JERRY_UI_H #include<stdio.h> #include"datastruct.h" // 原来的 handleFile 用于处理传入编译器的命令行参数 void handleParam(int argc, char* argv[], FILE** out); int nextChar(void); unsigned int getLineNumber(void); // 获取行号 void lexicalError(struct Token* token); // 以前词法分析中的error函数,参数列表有改动 void syntaxError(ErrMsg e, struct Token* t); int isFailed(void); // 是否出错 #ifdef _DEBUG_MODE #include"jerry-ui.c" #endif /* _DEBUG_MODE */ #endif /* _JERRY_UI_H */
需要注意的是,以前行号lineNumber和failed是直接可以引用的int值,这样并不规范,所以现在弄成了一个接口函数,在其它文件中只能通过getLineNumber和isFailed进行只读操作,它们现在放在jerry-ui.c中维护了
#include<stdio.h> #include<stdlib.h> #include<string.h> #include"jerry-ui.h" #define HELP_INFO \ "Jerry Compiler.\n" \ "Option:\n" \ " -o <file>: output into <file>" static unsigned int lineNumber = 1; static int currentChar; static int failed = 0; static FILE* in; int isFailed(void) { return failed; } unsigned int getLineNumber(void) { return lineNumber; } void lexicalError(struct Token* token) { fprintf(stderr, "Lexical error @ line %u: `%s'\n" " %s\n", token->line, token->image, token->err); failed = 1; } void syntaxError(ErrMsg e, struct Token* t) { failed = 1; if (END == t->type) { fprintf(stderr, "Syntax error at the end of the file.\n" " %s\n", e); } else { fprintf(stderr, "Syntax error @ line: %u token: `%s'\n" " %s\n", t->line, t->image, e); } } int nextChar(void) { currentChar = fgetc(in); lineNumber += ('\n' == currentChar); if (EOF == currentChar) { fclose(in); } return currentChar; } void handleParam(int argc, char* argv[], FILE** out) { if (0 == argc) { puts(HELP_INFO); exit(0); } in = fopen(argv[0], "r"); if (NULL == in) { fprintf(stderr, "Usage: %s access error.\n", argv[0]); exit(0); } *out = stdout; int i = 1; while (i < argc) { if (0 == strcmp("-o", argv[i])) { // 检查option. 现在只提供了-o这一个功能,所以只要这样就可以了 ++i; if (i < argc) { *out = fopen(argv[i], "w"); if (NULL == *out) { fprintf(stderr, "Usage: %s access error.\n", argv[i]); exit(0); } ++i; continue; } else { fprintf(stderr, "No output file specified.\n"); exit(0); } } fprintf(stderr, "Unknown parameter: %s\n", argv[i]); exit(0); } }
因为lineNumber现在换成unsigned int了,所以对应地,AbstractSyntaxNode和Token中的域也要改成unsigned int类型。
当然,firstToken, nextToken, eofToken这些函数并没有抽取,因为词法和语法分析中这些函数是有显著不同的。如果想编译生成语法分析器或仅编译生成词法分析器,那么可以将这两者分开,并用一个makefile来分开编译
CC=gcc -Wall # change it if COOL is somewhere else. COOL=COOL/ UI_SET=jerry-ui.o LEXICAL_SET=dfa.o SYNTAX_SET=syntax-node.o lr-analyser.o operation-analyser.o variable-analyser.o Stack.o Queue.o syntax:Syntax.out lexical:Lexical.out jerry-ui.o:jerry-ui.c $(CC) -c $< Lexical.out:jerry-lexical.o $(UI_SET) $(LEXICAL_SET) $(CC) -o $@ $^ jerry-lexical.o:jerry-lexical.c $(CC) -c $< dfa.o:dfa.c $(CC) -c $< Syntax.out:jerry-syntax.o $(UI_SET) $(LEXICAL_SET) $(SYNTAX_SET) $(CC) -o $@ $^ jerry-syntax.o:jerry-syntax.c $(CC) -c $< syntax-node.o:syntax-node.c $(CC) -c $< lr-analyser.o:lr-analyser.c $(CC) -c $< operation-analyser.o:operation-analyser.c $(CC) -c $< variable-analyser.o:variable-analyser.c $(CC) -c $< Stack.o:$(COOL)Stack/Stack.c $(CC) -c $< -o $@ Queue.o:$(COOL)Queue/Queue.c $(CC) -c $< -o $@ clean: rm *.o
默认的make将产生语法分析器,如果使用make lexical命令,则会产生词法分析器。
语法分析就到此为止了,以后将进入语义分析部分