有时候希望在lua中加入自定义风格的代码,例如将某个经常用的函数简化为一个符号,在开发某个gal引擎时,因为需要大量的用到回显文字的命令,所以希望将该命令简化,可以极大地提升写脚本的效率.例如:
//原有脚本实现 echo("这真是一个糟糕的事情","人物1"); echo("这真是一个糟糕的事情","人物2"); echo("这真是一个糟糕的事情","人物3"); echo("这真是一个糟糕的事情","人物4"); ... //修改后的实现 @人物1:"这真是一个不错的事情" @人物2:"这真是一个不错的事情" @人物3:"这真是一个不错的事情" @人物4:"这真是一个不错的事情" ...
这看起来是一件不错的事,^_^想象一下一个文字游戏,基本上都是文字回显的命令,简化后的效率提升不言而喻.
当然,这就要拿lua(遵循GPL协议)的源码开刀,lua的脚本解析主要在llex.c文件中,通过分析每个字符,提取出关键字,符号,变量等传递给lparser.c检查语法.
而在llex.c中最主要的函数是static int llex (LexState *ls,SemInfo *seminfo),该函数负责分割并解析每个WORD,然后返回给语法检查器,我们需要在这里动手将lua中没有定义的符号@解析为echo命令并将两个参数返回.
//翻译并传递echo命令 char *str_echo=0,*str_rolename=0; int len_echo=0,len_rolename=0; int trans_echo_command_step = -1; static int llex (LexState *ls,SemInfo *seminfo) { luaZ_resetbuffer(ls->buff); if(trans_echo_command_step!=-1) { switch(trans_echo_command_step) { case 1: //返回左括号 trans_echo_command_step++; return 40; case 2: //返回echo字符串 trans_echo_command_step++; seminfo->ts = luaX_newstring(ls,str_echo,len_echo); return TK_STRING; case 3: //返回逗号 trans_echo_command_step++; return 44; case 4: //返回rolename字符串 trans_echo_command_step++; seminfo->ts = luaX_newstring(ls,str_rolename,len_rolename); return TK_STRING; case 5: //返回右括号 trans_echo_command_step=-1; return 41; } } for (;;) { switch (ls->current) { case '\n': case '\r': { /* line breaks */ inclinenumber(ls); break; } case ' ': case '\f': case '\t': case '\v': { /* spaces */ next(ls); break; } /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! fscript_echo_process */ case '@': { TString *ts; str_echo=0,str_rolename=0; len_echo=0,len_rolename=0; next(ls); if(ls->current != 34) // 如果不是 " 则读取rolename { // 读取人名 do { save_and_next(ls); } while (ls->current != ':'); // 复制人名 str_rolename = luaZ_buffer(ls->buff); len_rolename = ls->buff->n; next(ls); // 跳过 : } fs_get_string(ls,ls->current); str_echo = luaZ_buffer(ls->buff) + len_rolename + 1; len_echo = ls->buff->n - len_rolename - 2; //第一步返回命令 trans_echo_command_step = 1; ts = luaX_newstring(ls,"doecho",6); seminfo->ts = ts; if (isreserved(ts)) /* reserved word? */ return ts->tsv.extra - 1 + FirsT_RESERVED; else return TK_NAME; } case '-': { /* '-' or '--' (comment) */ next(ls); if (ls->current != '-') return '-'; /* else is a comment */ next(ls); if (ls->current == '[') { /* long comment? */ int sep = skip_sep(ls); luaZ_resetbuffer(ls->buff); /* `skip_sep' may dirty the buffer */ if (sep >= 0) { read_long_string(ls,NULL,sep); /* skip long comment */ luaZ_resetbuffer(ls->buff); /* prevIoUs call may dirty the buff. */ break; } } /* else short comment */ while (!currIsNewline(ls) && ls->current != EOZ) next(ls); /* skip until end of line (or end of file) */ break; } case '[': { /* long string or simply '[' */ int sep = skip_sep(ls); if (sep >= 0) { read_long_string(ls,seminfo,sep); return TK_STRING; } else if (sep == -1) return '['; else lexerror(ls,"invalid long string delimiter",TK_STRING); } case '=': { next(ls); if (ls->current != '=') return '='; else { next(ls); return TK_EQ; } } case '<': { next(ls); if (ls->current != '=') return '<'; else { next(ls); return TK_LE; } } case '>': { next(ls); if (ls->current != '=') return '>'; else { next(ls); return TK_GE; } } case '~': { next(ls); if (ls->current != '=') return '~'; else { next(ls); return TK_NE; } } case ':': { next(ls); if (ls->current != ':') return ':'; else { next(ls); return TK_DBCOLON; } } case '"': case '\'': { /* short literal strings */ read_string(ls,ls->current,seminfo); return TK_STRING; } case '.': { /* '.','..','...',or number */ save_and_next(ls); if (check_next(ls,".")) { if (check_next(ls,".")) return TK_DOTS; /* '...' */ else return TK_CONCAT; /* '..' */ } else if (!lisdigit(ls->current)) return '.'; /* else go through */ } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { read_numeral(ls,seminfo); return TK_NUMBER; } case EOZ: { return TK_EOS; } default: { if (lislalpha(ls->current)|| ls->current > 0x80) { /* identifier or reserved word? */ TString *ts; do { if(ls->current > 0x80) { save_and_next(ls); save_and_next(ls); } else save_and_next(ls); } while (lislalnum(ls->current) || ls->current > 0x80); ts = luaX_newstring(ls,luaZ_buffer(ls->buff),luaZ_bufflen(ls->buff)); seminfo->ts = ts; if (isreserved(ts)) /* reserved word? */ return ts->tsv.extra - 1 + FirsT_RESERVED; else { return TK_NAME; } } else { /* single-char tokens (+ - / ...) */ int c = ls->current; next(ls); return c; } } } } }
加感叹号的地方为解析@符号的地方,要注意的是这里要完全按照lua的流程依次将命令,左右括号,逗号和两个参数都按照顺序传递回去,不然的话语法检查器会认为语法错误!