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


        Ogre脚本解析的前两个阶段——“语义分析”的准备工作和“词法分析”是在ScriptCompiler::compile(const String &str, const String &source, const String &group)函数中进行的(参见“http://www.cnblogs.com/yzwalkman/archive/2013/01/02/2841607.html 脚本及其解析(二)”的3-5行);而生成AbstractNodeList并进行相关后期处理的过程,则会在ScriptCompiler::compile(const ConcreteNodeListPtr &nodes, const String &group)函数中完成。函数展开如下:

     1     bool ScriptCompiler::compile(const ConcreteNodeListPtr &nodes, const String &group)
     2     {
     3         // Set up the compilation context
     4         mGroup = group;
     5 
     6         // Clear the past errors
     7         mErrors.clear();
     8 
     9         // Clear the environment
    10         mEnv.clear();
    11 
    12         if(mListener)
    13             mListener->preConversion(this, nodes);
    14 
    15         // Convert our nodes to an AST
    16         AbstractNodeListPtr ast = convertToAST(nodes);
    17         // Processes the imports for this script
    18         processImports(ast);
    19         // Process object inheritance
    20         processObjects(ast.get(), ast);
    21         // Process variable expansion
    22         processVariables(ast.get());
    23 
    24         // Allows early bail-out through the listener
    25         if(mListener && !mListener->postConversion(this, ast))
    26             return mErrors.empty();
    27         
    28         // Translate the nodes
    29         for(AbstractNodeList::iterator i = ast->begin(); i != ast->end(); ++i)
    30         {
    31             //logAST(0, *i);
    32             if((*i)->type == ANT_OBJECT && reinterpret_cast<ObjectAbstractNode*>((*i).get())->abstract)
    33                 continue;
    34             //LogManager::getSingleton().logMessage(reinterpret_cast<ObjectAbstractNode*>((*i).get())->name);
    35             ScriptTranslator *translator = ScriptCompilerManager::getSingleton().getTranslator(*i);
    36             if(translator)
    37                 translator->translate(this, *i);
    38         }
    39 
    40         mImports.clear();
    41         mImportRequests.clear();
    42         mImportTable.clear();
    43 
    44         return mErrors.empty();
    45     }

        16-22行代码的作用已在前面的“脚本及其解析”系列中进行了阐述。对脚本对象数据的解读工作则是在35-37行中完成的。compile()函数首先根据AbstractNode的类型来获取相应的ScriptTranslator(35行),获取ScriptTranslator对象的函数调用展开如下:

     1     ScriptTranslator *BuiltinScriptTranslatorManager::getTranslator(const AbstractNodePtr &node)
     2     {
     3         ScriptTranslator *translator = 0;
     4 
     5         if(node->type == ANT_OBJECT)
     6         {
     7             ObjectAbstractNode *obj = reinterpret_cast<ObjectAbstractNode*>(node.get());
     8             ObjectAbstractNode *parent = obj->parent ? reinterpret_cast<ObjectAbstractNode*>(obj->parent) : 0;
     9             if(obj->id == ID_MATERIAL)
    10                 translator = &mMaterialTranslator;
    11             else if(obj->id == ID_TECHNIQUE && parent && parent->id == ID_MATERIAL)
    12                 translator = &mTechniqueTranslator;
    13             else if(obj->id == ID_PASS && parent && parent->id == ID_TECHNIQUE)
    14                 translator = &mPassTranslator;
    15             else if(obj->id == ID_TEXTURE_UNIT && parent && parent->id == ID_PASS)
    16                 translator = &mTextureUnitTranslator;
    17             else if(obj->id == ID_TEXTURE_SOURCE && parent && parent->id == ID_TEXTURE_UNIT)
    18                 translator = &mTextureSourceTranslator;
    19             else if(obj->id == ID_FRAGMENT_PROGRAM || obj->id == ID_VERTEX_PROGRAM || obj->id == ID_GEOMETRY_PROGRAM)
    20                 translator = &mGpuProgramTranslator;
    21             else if(obj->id == ID_SHARED_PARAMS)
    22                 translator = &mSharedParamsTranslator;
    23             else if(obj->id == ID_PARTICLE_SYSTEM)
    24                 translator = &mParticleSystemTranslator;
    25             else if(obj->id == ID_EMITTER)
    26                 translator = &mParticleEmitterTranslator;
    27             else if(obj->id == ID_AFFECTOR)
    28                 translator = &mParticleAffectorTranslator;
    29             else if(obj->id == ID_COMPOSITOR)
    30                 translator = &mCompositorTranslator;
    31             else if(obj->id == ID_TECHNIQUE && parent && parent->id == ID_COMPOSITOR)
    32                 translator = &mCompositionTechniqueTranslator;
    33             else if((obj->id == ID_TARGET || obj->id == ID_TARGET_OUTPUT) && parent && parent->id == ID_TECHNIQUE)
    34                 translator = &mCompositionTargetPassTranslator;
    35             else if(obj->id == ID_PASS && parent && (parent->id == ID_TARGET || parent->id == ID_TARGET_OUTPUT))
    36                 translator = &mCompositionPassTranslator;
    37         }
    38 
    39         return translator;
    40     }

         可以看到,函数只对ANT_OBJECT类型的AbstractNode(对应着脚本对象)进行下一步的数据处理(5行)。每一个ObjectAbstractNode有一个自己的id值,此id与之前定义的枚举变量(参见“脚本及其解析(七)”)所对应的脚本对象相关联。BuiltinScriptTranslatorManager类对象将根据传入结点的此id值来查找相应的ScriptTranslator类型对象,并返回对象指针。

        然后,ScriptCompiler::compile()函数正式开始对脚本对象数据的解析(第一段代码36、37行)。以材质脚本的解析为例,translate()函数将展开如下:

      1     void MaterialTranslator::translate(ScriptCompiler *compiler, const AbstractNodePtr &node)
      2     {
      3         ObjectAbstractNode *obj = reinterpret_cast<ObjectAbstractNode*>(node.get());
      4         if(obj->name.empty())
      5             compiler->addError(ScriptCompiler::CE_OBJECTNAMEEXPECTED, obj->file, obj->line);
      6 
      7         // Create a material with the given name
      8         CreateMaterialScriptCompilerEvent evt(node->file, obj->name, compiler->getResourceGroup());
      9         bool processed = compiler->_fireEvent(&evt, (void*)&mMaterial);
     10 
     11         if(!processed)
     12         {
     13             mMaterial = reinterpret_cast<Ogre::Material*>(MaterialManager::getSingleton().create(obj->name, compiler->getResourceGroup()).get());
     14         }
     15         else
     16         {
     17             if(!mMaterial)
     18                 compiler->addError(ScriptCompiler::CE_OBJECTALLOCATIONERROR, obj->file, obj->line, 
     19                     "failed to find or create material \"" + obj->name + "\"");
     20         }
     21 
     22         mMaterial->removeAllTechniques();
     23         obj->context = Any(mMaterial);
     24         mMaterial->_notifyOrigin(obj->file);
     25 
     26         for(AbstractNodeList::iterator i = obj->children.begin(); i != obj->children.end(); ++i)
     27         {
     28             if((*i)->type == ANT_PROPERTY)
     29             {
     30                 PropertyAbstractNode *prop = reinterpret_cast<PropertyAbstractNode*>((*i).get());
     31                 switch(prop->id)
     32                 {
     33                 case ID_LOD_VALUES:
     34                     {
     35                         Material::LodValueList lods;
     36                         for(AbstractNodeList::iterator j = prop->values.begin(); j != prop->values.end(); ++j)
     37                         {
     38                             Real v = 0;
     39                             if(getReal(*j, &v))
     40                                 lods.push_back(v);
     41                             else
     42                                 compiler->addError(ScriptCompiler::CE_NUMBEREXPECTED, prop->file, prop->line,
     43                                     "lod_values expects only numbers as arguments");
     44                         }
     45                         mMaterial->setLodLevels(lods);
     46                     }
     47                     break;
     48                 case ID_LOD_DISTANCES:
     49                     {
     50                         // Set strategy to distance strategy
     51                         LodStrategy *strategy = DistanceLodStrategy::getSingletonPtr();
     52                         mMaterial->setLodStrategy(strategy);
     53 
     54                         // Read in lod distances
     55                         Material::LodValueList lods;
     56                         for(AbstractNodeList::iterator j = prop->values.begin(); j != prop->values.end(); ++j)
     57                         {
     58                             Real v = 0;
     59                             if(getReal(*j, &v))
     60                                 lods.push_back(v);
     61                             else
     62                                 compiler->addError(ScriptCompiler::CE_NUMBEREXPECTED, prop->file, prop->line,
     63                                     "lod_values expects only numbers as arguments");
     64                         }
     65                         mMaterial->setLodLevels(lods);
     66                     }
     67                     break;
     68                 case ID_LOD_STRATEGY:
     69                     if (prop->values.empty())
     70                     {
     71                         compiler->addError(ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line);
     72                     }
     73                     else if (prop->values.size() > 1)
     74                     {
     75                         compiler->addError(ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line,
     76                             "lod_strategy only supports 1 argument");
     77                     }
     78                     else
     79                     {
     80                         String strategyName;
     81                         bool result = getString(prop->values.front(), &strategyName);
     82                         if (result)
     83                         {
     84                             LodStrategy *strategy = LodStrategyManager::getSingleton().getStrategy(strategyName);
     85 
     86                             result = (strategy != 0);
     87 
     88                             if (result)
     89                                 mMaterial->setLodStrategy(strategy);
     90                         }
     91                         
     92                         if (!result)
     93                         {
     94                             compiler->addError(ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line,
     95                                 "lod_strategy argument must be a valid lod strategy");
     96                         }
     97                     }
     98                     break;
     99                 case ID_RECEIVE_SHADOWS:
    100                     if(prop->values.empty())
    101                     {
    102                         compiler->addError(ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line);
    103                     }
    104                     else if(prop->values.size() > 1)
    105                     {
    106                         compiler->addError(ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line,
    107                             "receive_shadows only supports 1 argument");
    108                     }
    109                     else
    110                     {
    111                         bool val = true;
    112                         if(getBoolean(prop->values.front(), &val))
    113                             mMaterial->setReceiveShadows(val);
    114                         else
    115                             compiler->addError(ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line,
    116                                 "receive_shadows argument must be \"true\", \"false\", \"yes\", \"no\", \"on\", or \"off\"");
    117                     }
    118                     break;
    119                 case ID_TRANSPARENCY_CASTS_SHADOWS:
    120                     if(prop->values.empty())
    121                     {
    122                         compiler->addError(ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line);
    123                     }
    124                     else if(prop->values.size() > 1)
    125                     {
    126                         compiler->addError(ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line,
    127                             "transparency_casts_shadows only supports 1 argument");
    128                     }
    129                     else
    130                     {
    131                         bool val = true;
    132                         if(getBoolean(prop->values.front(), &val))
    133                             mMaterial->setTransparencyCastsShadows(val);
    134                         else
    135                             compiler->addError(ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line,
    136                                 "transparency_casts_shadows argument must be \"true\", \"false\", \"yes\", \"no\", \"on\", or \"off\"");
    137                     }
    138                     break;
    139                 case ID_SET_TEXTURE_ALIAS:
    140                     if(prop->values.empty())
    141                     {
    142                         compiler->addError(ScriptCompiler::CE_STRINGEXPECTED, prop->file, prop->line);
    143                     }
    144                     else if(prop->values.size() > 3)
    145                     {
    146                         compiler->addError(ScriptCompiler::CE_FEWERPARAMETERSEXPECTED, prop->file, prop->line,
    147                             "set_texture_alias only supports 2 arguments");
    148                     }
    149                     else
    150                     {
    151                         AbstractNodeList::const_iterator i0 = getNodeAt(prop->values, 0), i1 = getNodeAt(prop->values, 1);
    152                         String name, value;
    153                         if(getString(*i0, &name) && getString(*i1, &value))
    154                             mTextureAliases.insert(std::make_pair(name, value));
    155                         else
    156                             compiler->addError(ScriptCompiler::CE_INVALIDPARAMETERS, prop->file, prop->line,
    157                                 "set_texture_alias must have 2 string argument");
    158                     }
    159                     break;
    160                 default:
    161                     compiler->addError(ScriptCompiler::CE_UNEXPECTEDTOKEN, prop->file, prop->line, 
    162                         "token \"" + prop->name + "\" is not recognized");
    163                 }
    164             }
    165             else if((*i)->type == ANT_OBJECT)
    166             {
    167                 processNode(compiler, *i);
    168             }
    169         }
    170 
    171         // Apply the texture aliases
    172         if(compiler->getListener())
    173         {
    174             PreApplyTextureAliasesScriptCompilerEvent locEvt(mMaterial, &mTextureAliases);
    175             compiler->_fireEvent(&locEvt, 0);
    176         }
    177         mMaterial->applyTextureAliases(mTextureAliases);
    178         mTextureAliases.clear();
    179     }

         在translate函数中,会对传入的AbstractNode的子链表中的结点进行逐一解析,如果子链表中的当前AbstractNode是一个属性类型的结点的话,函数就会根据它的id值,进行相应的数据处理(28-164行);如果此AbstractNode自身也是一个脚本对象的话就会调用processNode()函数进行处理(165-168行)。processNode函数的核心部分也是一个translate调用,也就是说,167行实际上实现了一个translate函数的递归调用。processNode函数展开如下:

     1     void ScriptTranslator::processNode(ScriptCompiler *compiler, const AbstractNodePtr &node)
     2     {
     3         if(node->type != ANT_OBJECT)
     4             return;
     5 
     6         // Abstract objects are completely skipped
     7         if((reinterpret_cast<ObjectAbstractNode*>(node.get()))->abstract)
     8             return;
     9 
    10         // Retrieve the translator to use
    11         ScriptTranslator *translator = 
    12             ScriptCompilerManager::getSingleton().getTranslator(node);
    13         
    14         if(translator)
    15             translator->translate(compiler, node);
    16         else
    17             compiler->addError(ScriptCompiler::CE_UNEXPECTEDTOKEN, node->file, node->line,
    18                 "token \"" + reinterpret_cast<ObjectAbstractNode*>(node.get())->cls + "\" is not recognized");
    19     }

        至此,Ogre脚本的解析工作才算基本完成。

        分析概念清晰、设计巧妙的代码是一种乐趣;隐藏在代码背后的思想有时会引发读者不由自主的感慨。好的代码是通过它的正确性、逻辑的严谨和设计的巧妙来展示其中的美感的。分析优质的代码,还会带来许多“福利”——为我们处理类似或相关问题提供最直观的参考。但最好仅限于参考,因为,依照自已的想法来创造,这个过程同样甚至会有更多的乐趣;另外,解决同一问题的优秀设计永远不会是唯一的。

    作者:yzwalkman
    转载请注明出处。
  • 相关阅读:
    hp一體機cartridge error及carriage jam4/22
    指纹仪zkonline.ocx:access violation...4/13
    IIS6:Service Unaviable 9/27
    寶寶的成長腳印3/15
    vs2003不能调试4/8
    C++ 的复制构造函数
    导入与导出数据 大容量复制程序(bcp)
    关于SQlserver数据库的加密应用
    DataGridView使用技巧
    使用C# 向记事本窗口发送消息
  • 原文地址:https://www.cnblogs.com/yzwalkman/p/2874832.html
Copyright © 2020-2023  润新知