第二人生的源码分析(105)脚本的词法分析(3)
下面来分析flex文件的最后一部份,就是辅助代码部份,这部份代码是原封不动地拷贝到生成的文件里。它的代码如下:
#001 %%
这个双百分号开始,就表示flex文件的第三部分开始了。
#002
#003 LLScriptAllocationManager *gAllocationManager;
定义分配管理器指针。
#004 LLScriptScript *gScriptp;
定义脚本的指针。
#005
#006 // Prototype for the yacc parser entry point
#007 int yyparse(void);
前置声明yacc的语法分析的函数。
#008
#009 int yyerror(const char *fmt, ...)
#010 {
#011 gErrorToText.writeError(yyout, gLine, gColumn, LSERROR_SYNTAX_ERROR);
#012 return 0;
#013 }
分析出错时输出错误位置。
#014
#015 //#define EMERGENCY_DEBUG_PRINTOUTS
#016 //#define EMIT_CIL_ASSEMBLER
#017
下面开始进行脚本编译处理。
#018 BOOL lscript_compile(const char* src_filename, const char* dst_filename,
#019 const char* err_filename, BOOL is_god_like)
#020 {
#021 BOOL b_parse_ok = FALSE;
#022 BOOL b_dummy = FALSE;
#023 U64 b_dummy_count = FALSE;
#024 LSCRIPTType type = LST_NULL;
#025
#026 gInternalColumn = 0;
#027 gInternalLine = 0;
#028 gScriptp = NULL;
#029
#030 gErrorToText.init();
#031 init_supported_expressions();
#032 init_temp_jumps();
#033 gAllocationManager = new LLScriptAllocationManager();
#034
下面打开脚本文件。
#035 yyin = LLFile::fopen(src_filename, "r");
#036 if (yyin)
#037 {
打开错误输出文件。
#038 yyout = LLFile::fopen(err_filename, "w");
#039
#040 // Reset the lexer's internal buffering.
#041
读入脚本文件。
#042 yyrestart(yyin);
#043
进行语法分析。
#044 b_parse_ok = !yyparse();
#045
#046 if (b_parse_ok)
#047 {
语法分析成功。
#048 #ifdef EMERGENCY_DEBUG_PRINTOUTS
#049 char compiled[256];
#050 sprintf(compiled, "%s.o", src_filename);
#051 FILE* compfile;
#052 compfile = LLFile::fopen(compiled, "w");
#053 #endif
#054
#055 if(dst_filename)
#056 {
#057 gScriptp->setBytecodeDest(dst_filename);
#058 }
#059
#060 gScriptp->mGodLike = is_god_like;
#061
#062 gScopeStringTable = new LLStringTable(16384);
#063 #ifdef EMERGENCY_DEBUG_PRINTOUTS
下面的代码遍历语法树,生成脚本的中间代码。
#064 gScriptp->recurse(compfile, 0, 4, LSCP_PRETTY_PRINT, LSPRUNE_INVALID, b_dummy, NULL, type, type, b_dummy_count, NULL, NULL,
#065 0, NULL, 0, NULL);
#066 #endif
#067 gScriptp->recurse(yyout, 0, 0, LSCP_PRUNE, LSPRUNE_INVALID, b_dummy, NULL, type, type, b_dummy_count,
#068 NULL, NULL, 0, NULL, 0, NULL);
#069 gScriptp->recurse(yyout, 0, 0, LSCP_SCOPE_PASS1, LSPRUNE_INVALID, b_dummy, NULL, type, type, b_dummy_count, NULL, NULL, 0,
#070 NULL, 0, NULL);
#071 gScriptp->recurse(yyout, 0, 0, LSCP_SCOPE_PASS2, LSPRUNE_INVALID, b_dummy, NULL, type, type, b_dummy_count, NULL, NULL, 0,
#072 NULL, 0, NULL);
#073 gScriptp->recurse(yyout, 0, 0, LSCP_TYPE, LSPRUNE_INVALID, b_dummy, NULL, type, type, b_dummy_count,
#074 NULL, NULL, 0, NULL, 0, NULL);
#075 if (!gErrorToText.getErrors())
#076 {
#077 gScriptp->recurse(yyout, 0, 0, LSCP_RESOURCE, LSPRUNE_INVALID, b_dummy,
#078 NULL, type, type, b_dummy_count, NULL, NULL, 0, NULL, 0, NULL);
#079 #ifdef EMERGENCY_DEBUG_PRINTOUTS
#080 gScriptp->recurse(yyout, 0, 0, LSCP_EMIT_ASSEMBLY, LSPRUNE_INVALID, b_dummy, NULL, type, type,
#081 b_dummy_count, NULL, NULL, 0, NULL, 0, NULL);
#082 #endif
#083 #ifdef EMIT_CIL_ASSEMBLER
#084 const char* cil_output_file_name = dst_filename? dst_filename : "lscript.cil";
#085 FILE* cilout = LLFile::fopen(cil_output_file_name, "w");
#086 if(NULL == cilout)
#087 {
#088 fprintf(yyout, "Error opening cil output file %s/n", cil_output_file_name);
#089 }
#090 else
#091 {
#092 gScriptp->recurse(cilout, 0, 0, LSCP_EMIT_CIL_ASSEMBLY, LSPRUNE_INVALID, b_dummy, NULL,
#093 type, type, b_dummy_count, NULL, NULL, 0, NULL, 0, NULL);
#094 if(fclose(cilout) == EOF)
#095 {
#096 fprintf(yyout, "Error closing cil output file %s/n", cil_output_file_name);
#097 }
#098 }
#099 #endif
#100 gScriptp->recurse(yyout, 0, 0, LSCP_EMIT_BYTE_CODE, LSPRUNE_INVALID, b_dummy, NULL, type, type,
#101 b_dummy_count, NULL, NULL, 0, NULL, 0, NULL);
#102 }
#103 delete gScopeStringTable;
#104 gScopeStringTable = NULL;
#105 #ifdef EMERGENCY_DEBUG_PRINTOUTS
#106 fclose(compfile);
#107 #endif
#108 }
#109 fclose(yyout);
#110 }
#111
#112 fclose(yyin);
#113 delete gAllocationManager;
#114 delete gScopeStringTable;
#115
#116 return b_parse_ok && !gErrorToText.getErrors();
#117 }
#118
#119
输入文件名称的脚本编译函数。
#120 BOOL lscript_compile(char *filename, BOOL is_god_like = FALSE)
#121 {
生成输入和输出文件名称。
#122 char src_filename[MAX_STRING];
#123 sprintf(src_filename, "%s.lsl", filename);
#124 char err_filename[MAX_STRING];
#125 sprintf(err_filename, "%s.out", filename);
调用前面脚本编译函数。
#126 return lscript_compile(src_filename, NULL, err_filename, is_god_like);
#127 }
#128
#129
下面处理GCC编译问题。
#130 S32 yywrap()
#131 {
#132 #if defined(FLEX_SCANNER) && !defined(LL_WINDOWS)
#133 // get gcc to stop complaining about lack of use of yyunput
#134 (void) yyunput;
#135 #endif
#136 return(1);
#137 }
#138
跳过所有脚本注释部份。
#139 void comment()
#140 {
#141 char c;
#142
#143 while ((c = yyinput()) != '/n' && c != 0 && c != EOF)
#144 ;
#145 }
#146
统计编译所在的行号和列号。
#147 void count()
#148 {
#149 S32 i;
#150
#151 gColumn = gInternalColumn;
#152 gLine = gInternalLine;
#153
#154 for (i = 0; yytext[i] != '/0'; i++)
#155 if (yytext[i] == '/n')
#156 {
#157 gInternalLine++;
#158 gInternalColumn = 0;
#159 }
#160 else if (yytext[i] == '/t')
#161 gInternalColumn += 4 - (gInternalColumn % 8);
#162 else
#163 gInternalColumn++;
#164 }
#165
分析脚本的字符串。
#166 void parse_string()
#167 {
#168 S32 length = (S32)strlen(yytext);
#169 length = length - 2;
#170 char *temp = yytext + 1;
#171
#172 S32 i;
#173 S32 escapes = 0;
#174 S32 tabs = 0;
#175 for (i = 0; i < length; i++)
#176 {
#177 if (temp[i] == '//')
#178 {
#179 escapes++;
#180 i++;
#181 if (temp[i] == 't')
#182 tabs++;
#183 }
#184 }
#185
#186 S32 newlength = length - escapes + tabs*3;
#187 yylval.sval = new char[newlength + 1];
#188
#189 char *dest = yylval.sval;
#190
#191 for (i = 0; i < length; i++)
#192 {
#193 if (temp[i] == '//')
#194 {
#195 i++;
#196 // linefeed
#197 if (temp[i] == 'n')
#198 {
#199 *dest++ = 10;
#200 }
#201 else if (temp[i] == 't')
#202 {
#203 *dest++ = ' ';
#204 *dest++ = ' ';
#205 *dest++ = ' ';
#206 *dest++ = ' ';
#207 }
#208 else
#209 {
#210 *dest++ = temp[i];
#211 }
#212 }
#213 else
#214 {
#215 *dest++ = temp[i];
#216 }
#217 }
#218 yylval.sval[newlength] = 0;
#219 }
#220
辅助代码部份主要调用语法分析生成函数来编译脚本,当然语法分析的函数里又调用词法分析的函数来获取所需要的单词,最后生成脚本的树中间代码。