下面来详细地分析语法分析相关的类,以便了解整个语法分析的过程和细节,这样也方便地复用第二人生里的脚本编译器,达到源码复用的目标。先来分析类
LLScriptFilePosition
,它的声明代码如下:
#001
class LLScriptFilePosition
#002
{
#003
public:
构造函数,保存脚本所在的行号和列号。
#004
LLScriptFilePosition(S32 line, S32 col)
#005
: mLineNumber(line), mColumnNumber(col), mByteOffset(0), mByteSize(0)
#006
{
#007
}
#008
析构函数。
#009
virtual ~LLScriptFilePosition() {}
#010
下面函数接口实现递归处理。
#011
virtual void recurse(FILE *fp, S32 tabs, S32 tabsize,
#012
LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg,
#013
LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count,
#014
LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize,
#015
LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata) = 0;
获取代码的大小。
#016
virtual S32 getSize() = 0;
#017
输出对齐字符。
#018
void fdotabs(FILE *fp, S32 tabs, S32 tabsize);
#019
保存当前的行号。
#020
S32 mLineNumber;
保存当前的列号。
#021
S32 mColumnNumber;
#022
字节偏移位置。
#023
S32 mByteOffset;
字节的大小。
#024
S32 mByteSize;
#025
};
通过上面这个类来实现代码行号和列号的保存,并且可以递归调用生成代码。这个类是基类,后面会有很多类都继承它,用它来保存单词是出现在那里的,下面就来看看单词出现的类型,它的声明如下:
#001
class LLScriptType : public LLScriptFilePosition
从这里可以看到它是继承上面介绍的类
LLScriptFilePosition
。
#002
{
#003
public:
构造函数,可以初始化保存行号,列号和单词的类型。
#004
LLScriptType(S32 line, S32 col, LSCRIPTType type)
#005
: LLScriptFilePosition(line, col), mType(type)
#006
{
#007
}
#008
析构函数。
#009
~LLScriptType() {}
#010
递归函数接口的实现。
#011
void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type,
#012
LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
获取大小的实现。
#013
S32 getSize();
#014
只存单词的类型。
#015
LSCRIPTType
mType;
#016
};
#017
在第
15
行里定义一个类型
LSCRIPTType
,这是脚本文件里出现的类型,它的定义如下:
#001
typedef enum e_lscript_types
#002
{
空类型。
#003
LST_NULL,
整数类型。
#004
LST_INTEGER,
浮点数类型。
#005
LST_FLOATINGPOINT,
字符串类型。
#006
LST_STRING,
关键字类型。
#007
LST_KEY,
数组类型。
#008
LST_VECTOR,
四元数类型。
#009
LST_QUATERNION,
列表类型。
#010
LST_LIST,
未有定义类型。
#011
LST_UNDEFINED,
结束类型。
#012
LST_EOF
#013
} LSCRIPTType;
因此,上面的类里只能保存这几种类型,下面再来看类
LLScriptType
的实现代码:
#001
void LLScriptType::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type,
#002
LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
#003
{
如果脚本代码有错,就不用处理。
#004
if (gErrorToText.getErrors())
#005
{
#006
return;
#007
}
根据编译那一遍进行不同的处理。
#008
switch(pass)
#009
{
这两遍是输出类型名称。
#010
case LSCP_PRETTY_PRINT:
#011
case LSCP_EMIT_ASSEMBLY:
根据类型
mType
从数组
LSCRIPTTypeNames
里获取相应的类字符串输出。
#012
fprintf(fp,"%s",LSCRIPTTypeNames[mType]);
#013
break;
获取类型。
#014
case LSCP_TYPE:
#015
type = mType;
#016
break;
输出
CIL
的汇编。
#017
case LSCP_EMIT_CIL_ASSEMBLY:
#018
print_cil_type(fp, mType);
#019
break;
#020
default:
#021
break;
#022
}
#023
}
#024
获取类型的大小,比如整数是
4
字节。
#025
S32 LLScriptType::getSize()
#026
{
#027
return LSCRIPTDataSize[mType];
#028
}
数组
LSCRIPTDataSize
具体值如下:
#001
const S32 LSCRIPTDataSize[LST_EOF] =
#002
{
#003
0,
// VOID
#004
4,
// integer
#005
4,
// float
#006
4,
// string
#007
4,
// key
#008
12,
// vector
#009
16,
// quaternion
#010
4,
// list
#011
0
// invalid
#012
};
#013
这个数组就是定义每一个类型占用内存的大小。到这里就把类型分析完成了,下一次再来分析其它的代码。