第二人生的源码分析(105)脚本的词法分析(3)

系统 1572 0
下面来分析 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 
 
辅助代码部份主要调用语法分析生成函数来编译脚本,当然语法分析的函数里又调用词法分析的函数来获取所需要的单词,最后生成脚本的树中间代码。

第二人生的源码分析(105)脚本的词法分析(3)


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论