第二人生的源码分析(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 
 
辅助代码部份主要调用语法分析生成函数来编译脚本,当然语法分析的函数里又调用词法分析的函数来获取所需要的单词,最后生成脚本的树中间代码。
posted @ 2008-07-13 21:20  ajuanabc  阅读(142)  评论(0编辑  收藏  举报