zoukankan      html  css  js  c++  java
  • 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行)。这样一来,所有对变量对象的引用就被实际的变量值对象替换了。

    作者:yzwalkman
    转载请注明出处。
  • 相关阅读:
    SqlServer报错:指定的网络名不再可用
    Flutter Build apk 错误(一)
    修改项目语言为C#8.0
    Foxmail6.5 ERR LOGIN FAIL 重新输入口令
    VSCode调试Flutter的问题解决
    解决localdb中文智能的问题
    EF Oracle:错误 175
    清除SqlServer日志
    EF:根据实体类生成表结构SQL
    修改TNSLSNR的端口
  • 原文地址:https://www.cnblogs.com/yzwalkman/p/2861320.html
Copyright © 2011-2022 走看看