Ogre源代码浅析——脚本及其解析(六)

Ogre对脚本中import语句的内容将生成对应的ImportAbstractNode对象(参见:http://www.cnblogs.com/yzwalkman/archive/2013/01/08/2848831.html“脚本及其解析四”)。在创建数据对象前,对ImportAbstractNode对象的处理则将在ScriptCompiler::processImports()函数中进行,函数展开如下:

复制代码
 1     void ScriptCompiler::processImports(Ogre::AbstractNodeListPtr &nodes)
 2     {
 3         // We only need to iterate over the top-level of nodes
 4         AbstractNodeList::iterator i = nodes->begin();
 5         while(i != nodes->end())
 6         {
 7             // We move to the next node here and save the current one.
 8             // If any replacement happens, then we are still assured that
 9             // i points to the node *after* the replaced nodes, no matter
10             // how many insertions and deletions may happen
11             AbstractNodeList::iterator cur = i++;
12             if((*cur)->type == ANT_IMPORT)
13             {
14                 ImportAbstractNode *import = (ImportAbstractNode*)(*cur).get();
15                 // Only process if the file's contents haven't been loaded
16                 if(mImports.find(import->source) == mImports.end())
17                 {
18                     // Load the script
19                     AbstractNodeListPtr importedNodes = loadImportPath(import->source);
20                     if(!importedNodes.isNull() && !importedNodes->empty())
21                     {
22                         processImports(importedNodes);
23                         processObjects(importedNodes.get(), importedNodes);
24                     }
25                     if(!importedNodes.isNull() && !importedNodes->empty())
26                         mImports.insert(std::make_pair(import->source, importedNodes));
27                 }
28 
29                 // Handle the target request now
30                 // If it is a '*' import we remove all previous requests and just use the '*'
31                 // Otherwise, ensure '*' isn't already registered and register our request
32                 if(import->target == "*")
33                 {
34                     mImportRequests.erase(mImportRequests.lower_bound(import->source),
35                         mImportRequests.upper_bound(import->source));
36                     mImportRequests.insert(std::make_pair(import->source, "*"));
37                 }
38                 else
39                 {
40                     ImportRequestMap::iterator iter = mImportRequests.lower_bound(import->source),
41                         end = mImportRequests.upper_bound(import->source);
42                     if(iter == end || iter->second != "*")
43                     {
44                         mImportRequests.insert(std::make_pair(import->source, import->target));
45                     }
46                 }
47 
48                 nodes->erase(cur);
49             }
50         }
51 
52         // All import nodes are removed
53         // We have cached the code blocks from all the imported scripts
54         // We can process all import requests now
55         for(ImportCacheMap::iterator it = mImports.begin(); it != mImports.end(); ++it)
56         {
57             ImportRequestMap::iterator j = mImportRequests.lower_bound(it->first),
58                 end = mImportRequests.upper_bound(it->first);
59             if(j != end)
60             {
61                 if(j->second == "*")
62                 {
63                     // Insert the entire AST into the import table
64                     mImportTable.insert(mImportTable.begin(), it->second->begin(), it->second->end());
65                     continue; // Skip ahead to the next file
66                 }
67                 else
68                 {
69                     for(; j != end; ++j)
70                     {
71                         // Locate this target and insert it into the import table
72                         AbstractNodeListPtr newNodes = locateTarget(it->second.get(), j->second);
73                         if(!newNodes.isNull() && !newNodes->empty())
74                             mImportTable.insert(mImportTable.begin(), newNodes->begin(), newNodes->end());
75                     }
76                 }
77             }
78         }
79     }
复制代码

其中的loadImportPath()函数(19行)主要负责对脚本中由import语句所导入的材质对象进行解析,其中的import->source用来指向需导入的材质对象所在的脚本文件名,函数展开如下:

复制代码
 1     AbstractNodeListPtr ScriptCompiler::loadImportPath(const Ogre::String &name)
 2     {
 3         AbstractNodeListPtr retval;
 4         ConcreteNodeListPtr nodes;
 5 
 6         if(mListener)
 7             nodes = mListener->importFile(this, name);
 8 
 9         if(nodes.isNull() && ResourceGroupManager::getSingletonPtr())
10         {
11             DataStreamPtr stream = ResourceGroupManager::getSingleton().openResource(name, mGroup);
12             if(!stream.isNull())
13             {
14                 ScriptLexer lexer;
15                 ScriptTokenListPtr tokens = lexer.tokenize(stream->getAsString(), name);
16                 ScriptParser parser;
17                 nodes = parser.parse(tokens);
18             }
19         }
20 
21         if(!nodes.isNull())
22             retval = convertToAST(nodes);
23 
24         return retval;
25     }
复制代码

loadImportPath()函数首先打开材质所在的脚本文件(11行);然后对它进行“词法分析”(14、15行);接下来进行“语义分析”(16、17行);再在此基础上创建与脚本文件对应的AbstractNodeList以描述整个导入脚本文件的结构(21、22行)。整个过程与ScriptCompiler::compile(const String &str, const String &source, const String &group)的前半部分(参见:http://www.cnblogs.com/yzwalkman/archive/2013/01/02/2841607.html “脚本及其解析二”的ScriptCompiler::compile()函数3-5行)和ScriptCompiler::compile(const ConcreteNodeListPtr &nodes, const String &group)函数的前半部分是一致的。

回到ScriptCompiler::processImports()函数,在创建了导入脚本文件的AbstractNodeList链表结构后,接下来需要对导入脚本文件中的import和object进行预处理,这是一个递归的过程(22、23行)。当整个导入过程完成后,还要根据导入的情况填写ScriptCompiler的mImportTable链表,以备父调用中的processObject()函数(如果在父调用中存在的话)使用(参见:http://www.cnblogs.com/yzwalkman/archive/2013/01/13/2856398.html“脚本及其解析五”中的processObjects()函数第16行)。对导入脚本中,'$'字符所定义和引用的脚本变量,并不立即进行替换。它们将在ScriptCompiler::compile(const ConcreteNodeListPtr &nodes, const String &group)函数中统一处理。

对变量进行替换的过程将在ScriptCompiler::processVariables()函数中进行,函数展开如下:

复制代码
 1     void ScriptCompiler::processVariables(Ogre::AbstractNodeList *nodes)
 2     {
 3         AbstractNodeList::iterator i = nodes->begin();
 4         while(i != nodes->end())
 5         {
 6             AbstractNodeList::iterator cur = i;
 7             ++i;
 8 
 9             if((*cur)->type == ANT_OBJECT)
10             {
11                 // Only process if this object is not abstract
12                 ObjectAbstractNode *obj = (ObjectAbstractNode*)(*cur).get();
13                 if(!obj->abstract)
14                 {
15                     processVariables(&obj->children);
16                     processVariables(&obj->values);
17                 }
18             }
19             else if((*cur)->type == ANT_PROPERTY)
20             {
21                 PropertyAbstractNode *prop = (PropertyAbstractNode*)(*cur).get();
22                 processVariables(&prop->values);
23             }
24             else if((*cur)->type == ANT_VARIABLE_ACCESS)
25             {
26                 VariableAccessAbstractNode *var = (VariableAccessAbstractNode*)(*cur).get();
27 
28                 // Look up the enclosing scope
29                 ObjectAbstractNode *scope = 0;
30                 AbstractNode *temp = var->parent;
31                 while(temp)
32                 {
33                     if(temp->type == ANT_OBJECT)
34                     {
35                         scope = (ObjectAbstractNode*)temp;
36                         break;
37                     }
38                     temp = temp->parent;
39                 }
40 
41                 // Look up the variable in the environment
42                 std::pair<bool,String> varAccess;
43                 if(scope)
44                     varAccess = scope->getVariable(var->name);
45                 if(!scope || !varAccess.first)
46                 {
47                     map<String,String>::type::iterator k = mEnv.find(var->name);
48                     varAccess.first = k != mEnv.end();
49                     if(varAccess.first)
50                         varAccess.second = k->second;
51                 }
52 
53                 if(varAccess.first)
54                 {
55                     // Found the variable, so process it and insert it into the tree
56                     ScriptLexer lexer;
57                     ScriptTokenListPtr tokens = lexer.tokenize(varAccess.second, var->file);
58                     ScriptParser parser;
59                     ConcreteNodeListPtr cst = parser.parseChunk(tokens);
60                     AbstractNodeListPtr ast = convertToAST(cst);
61 
62                     // Set up ownership for these nodes
63                     for(AbstractNodeList::iterator j = ast->begin(); j != ast->end(); ++j)
64                         (*j)->parent = var->parent;
65 
66                     // Recursively handle variable accesses within the variable expansion
67                     processVariables(ast.get());
68 
69                     // Insert the nodes in place of the variable
70                     nodes->insert(cur, ast->begin(), ast->end());
71                 }
72                 else
73                 {
74                     // Error
75                     addError(CE_UNDEFINEDVARIABLE, var->file, var->line);
76                 }
77 
78                 // Remove the variable node
79                 nodes->erase(cur);
80             }
81         }
82     }
复制代码

函数会对AbstractNodeList链表中的各AbstractNode逐一进行访问,如果结点是VariableAccessAbstractNode类型对象(24-26行),那么说明结点表示的是对脚本中定义的变量的引用(参见:http://www.cnblogs.com/yzwalkman/archive/2013/01/08/2848831.html “脚本及其解析四”),函数就会根据结点的变量名称找到它对应的实际值(43-50行,44行的getVariable()函数会到相应的ObjectAbstractNode的mEnv容器中去寻找,如果找不到就会到ScriptCompiler的mEnv中去寻找,47-50行。这两者的mEnv中都保存了所有的变量和它们对应的实际值。(参见:http://www.cnblogs.com/yzwalkman/archive/2013/01/08/2848831.html “脚本及其解析四”))。变量的实际值一般也对应一个脚本对象,因此函数会解析这个脚本对象所在的文件,经过同样的三个阶段后,创建其所对应的AbstractNodeList(56-60行),并将实际值的解析结果插入到函数传入的nodes链表的当前访问的结点之前(70行),然后将当前结点从nodes中删除(79行)。这样一来,所有对变量对象的引用就被实际的变量值对象替换了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值