//// TextTemplate.cpp// tools/shaderScribe/src//// Created by Sam Gateau on 12/15/2014.// Copyright 2013 High Fidelity, Inc.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html#include "TextTemplate.h" #include <stdarg.h>#include <fstream>#include <sstream>#include <algorithm> typedef TextTemplate::Block::Pointer BlockPointer;typedef TextTemplate::Config::Pointer ConfigPointer;typedef TextTemplate::Pointer TextTemplatePointer; const std::string TextTemplate::Tag::NULL_VAR = "_SCRIBE_NULL"; //-----------------------------------------------------------------------------TextTemplate::Config::Config() : _includes(), _funcs(), _logStream(&std::cout), _numErrors(0), _includerCallback(TextTemplate::loadFile) { _paths.push_back("");} const TextTemplatePointer TextTemplate::Config::addInclude(const ConfigPointer& config, const char* include) { if (!config) { return TextTemplatePointer(); } TextTemplatePointer included = config->findInclude(include); if (included) { return included; } // INcluded doest exist yet so let's try to create it String includeStream; if (config->_includerCallback(config, include, includeStream)) { // ok, then create a new Template on the include file with this as lib included = std::make_shared<TextTemplate>(include, config); std::stringstream src(includeStream); int nbErrors = included->parse(src); if (nbErrors > 0) { included->logError(included->_root, "File failed to parse, not included"); return TextTemplatePointer(); } config->_includes.insert(Includes::value_type(include, included)); return included; } return TextTemplatePointer();} const TextTemplatePointer TextTemplate::Config::findInclude(const char* include) { Includes::iterator includeIt = _includes.find(String(include)); if (includeIt != _includes.end()) { return (*includeIt).second; } else { return TextTemplatePointer(); }} void TextTemplate::Config::addIncludePath(const char* path) { _paths.push_back(String(path));} bool TextTemplate::loadFile(const ConfigPointer& config, const char* filename, String& source) { String sourceFile(filename); String fullfilename; for (unsigned int i = 0; i < config->_paths.size(); i++) { fullfilename = config->_paths[i] + sourceFile; std::ifstream ifs; ifs.open(fullfilename.c_str()); if (ifs.is_open()) { std::string str((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>()); source = str; ifs.close(); return (source.length() > 0); } } return false;} TextTemplate::Funcs::Funcs() : _funcs() {} TextTemplate::Funcs::~Funcs() {} const BlockPointer TextTemplate::Funcs::findFunc(const char* func) { map::iterator it = _funcs.find(String(func)); if (it != _funcs.end()) { return (*it).second; } else { return BlockPointer(); }} const BlockPointer TextTemplate::Funcs::addFunc(const char* func, const BlockPointer& funcBlock) { BlockPointer included = findFunc(func); if (! included) { _funcs.insert(map::value_type(func, funcBlock)); } return included;} void TextTemplate::Block::addNewBlock(const Pointer& parent, const Pointer& block) { if (parent) { parent->blocks.push_back(block); block->parent = parent; }} const BlockPointer& TextTemplate::Block::getCurrentBlock(const Pointer& block) { if (block && block->command.isBlockEnd()) { return block->parent; } else { return block; }} void TextTemplate::logError(const Block::Pointer& block, const char* fmt, ...) { va_list argp; va_start(argp, fmt); char buff[256]; sprintf(buff, fmt, argp); _numErrors++; log() << block->sourceName << " Error >>" << buff << std::endl; int level = 1; displayTree(std::cerr, level); } bool TextTemplate::grabUntilBeginTag(std::istream* str, std::string& grabbed, Tag::Type& tagType) { std::stringstream dst; while (!str->eof()) { std::string datatoken; getline((*str), datatoken, Tag::BEGIN); dst << datatoken; char next = str->peek(); if (next == Tag::VAR) { tagType = Tag::VARIABLE; grabbed = dst.str(); str->get(); // skip tag char return true; } else if (next == Tag::COM) { tagType = Tag::COMMAND; grabbed = dst.str(); str->get(); // skip tag char return true; } else if (next == Tag::REM) { tagType = Tag::REMARK; grabbed = dst.str(); str->get(); // skip tag char return true; } else { if (!str->eof()) { // false positive, just found the Tag::BEGIN without consequence dst << Tag::BEGIN; // keep searching } else { // end of the file finishing with no tag tagType = Tag::INVALID; grabbed = dst.str(); return true; } } } return false;} bool TextTemplate::grabUntilEndTag(std::istream* str, std::string& grabbed, Tag::Type& tagType) { std::stringstream dst; // preEnd char depends on tag type char preEnd = Tag::COM; if (tagType == Tag::VARIABLE) { preEnd = Tag::VAR; } else if (tagType == Tag::REMARK) { preEnd = Tag::REM; } while (!str->eof() && !str->fail()) { // looking for the end of the tag means find the next preEnd std::string dataToken; getline((*str), dataToken, preEnd); char end = str->peek(); dst << dataToken; // and if the next char is Tag::END then that's it if (end == Tag::END) { grabbed = dst.str(); str->get(); // eat the Tag::END return true; } else { // false positive, keep on searching dst << preEnd; } } grabbed = dst.str(); return false;} bool TextTemplate::stepForward(std::istream* str, std::string& grabbed, std::string& tag, Tag::Type& tagType, Tag::Type& nextTagType) { if (str->eof()) { return false; } if (!_steppingStarted) { _steppingStarted = true; return grabUntilBeginTag(str, grabbed, nextTagType); } // Read from the last opening Tag::BEGIN captured to the next Tag::END if (grabUntilEndTag(str, tag, tagType)) { // skip trailing space and new lines only after Command or Remark tag block if ((tagType == Tag::COMMAND) || (tagType == Tag::REMARK)) { while (!str->eof() && !str->fail()) { char c = str->peek(); if ((c == ' ') || (c == '\t') || (c == '\n')) { str->get(); } else { break; } } } grabUntilBeginTag(str, grabbed, nextTagType); } return true; //hasElement;} const BlockPointer TextTemplate::processStep(const BlockPointer& block, std::string& grabbed, std::string& tag, Tag::Type& tagType) { switch (tagType) { case Tag::INVALID: block->ostr << grabbed; return block; break; case Tag::VARIABLE: return processStepVar(block, grabbed, tag); break; case Tag::COMMAND: return processStepCommand(block, grabbed, tag); break; case Tag::REMARK: return processStepRemark(block, grabbed, tag); break; } logError(block, "Invalid tag"); return block;} bool TextTemplate::grabFirstToken(String& src, String& token, String& reminder) { bool goOn = true; std::string::size_type i = 0; while (goOn && (i < src.length())) { char c = src[i]; if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_') || (c == '.') || (c == '[') || (c == ']')) { token += c; } else { if (!token.empty()) { reminder = src.substr(i); return true; } } i++; } return (!token.empty());} bool TextTemplate::convertExpressionToArguments(String& src, std::vector< String >& arguments) { std::stringstream str(src); String token; while (!str.eof()) { str >> token; if (!str.fail()) { arguments.push_back(token); } } return true;} bool TextTemplate::convertExpressionToDefArguments(String& src, std::vector< String >& arguments) { if (src.empty()) { return false; } std::stringstream argstr(src); std::stringstream dest; bool inVar = false; while (!argstr.eof()) { // parse the value of a var, try to find a VAR, so look for the pattern BEGIN,VAR ... VAR,END String token; char tag; if (!inVar) { getline(argstr, token, Tag::BEGIN); dest << token; tag = argstr.peek(); } else { getline(argstr, token, Tag::END); dest << token; tag = token.back(); } if (tag == Tag::VAR) { if (!inVar) { // real var coming: arguments.push_back(dest.str()); inVar = true; } else { // real var over arguments.push_back(dest.str()); inVar = false; } } else { if (argstr.eof()) { arguments.push_back(dest.str()); } else { // put back the tag char stolen dest << (!inVar ? Tag::BEGIN : Tag::END); } } } return true;} bool TextTemplate::convertExpressionToFuncArguments(String& src, std::vector< String >& arguments) { if (src.empty()) { return false; } std::stringstream streamSrc(src); String params; getline(streamSrc, params, '('); getline(streamSrc, params, ')'); if (params.empty()) { return false; } std::stringstream str(params); String token; int nbTokens = 0; while (!str.eof()) { char c = str.peek(); if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_') || (c == '.') || (c == Tag::VAR) || (c == '[') || (c == ']')) { token += c; } else if (c == ',') { if (!token.empty()) { if (token == Tag::NULL_VAR) { arguments.push_back(Tag::NULL_VAR); } else { arguments.push_back(token); } nbTokens++; } token.clear(); } str.get(); } if (token.size()) { arguments.push_back(token); nbTokens++; } return (nbTokens > 0);} const BlockPointer TextTemplate::processStepVar(const BlockPointer& block, String& grabbed, String& tag) { // then look at the define String var = tag; String varName; String val; if (grabFirstToken(var, varName, val)) { if (!varName.empty()) { BlockPointer parent = Block::getCurrentBlock(block); // Add a new BLock BlockPointer newBlock = std::make_shared<Block>(_root->sourceName); (newBlock->ostr) << grabbed; newBlock->command.type = Command::VAR; newBlock->command.arguments.push_back(varName); if (!val.empty()) { convertExpressionToFuncArguments(val, newBlock->command.arguments); } Block::addNewBlock(parent, newBlock); // dive in the new block return newBlock; } } return block;} const BlockPointer TextTemplate::processStepCommand(const BlockPointer& block, String& grabbed, String& tag) { // Grab the command name String command = tag; String commandName; if (grabFirstToken(command, commandName, command)) { if (commandName.compare("def") == 0) { return processStepDef(block, grabbed, command); } else if (commandName.compare("if") == 0) { return processStepCommandIf(block, grabbed, command); } else if (commandName.compare("endif") == 0) { return processStepCommandEndIf(block, grabbed, command); } else if (commandName.compare("else") == 0) { return processStepCommandElse(block, grabbed, command); } else if (commandName.compare("elif") == 0) { return processStepCommandElif(block, grabbed, command); } else if (commandName.compare("include") == 0) { return processStepInclude(block, grabbed, command); } else if (commandName.compare("func") == 0) { return processStepFunc(block, grabbed, command); } else if (commandName.compare("endfunc") == 0) { return processStepEndFunc(block, grabbed, command); } } return block;} const BlockPointer TextTemplate::processStepDef(const BlockPointer& block, String& grabbed, String& def) { // then look at the define String varName; String val; if (!grabFirstToken(def, varName, val)) { logError(block, "Invalid Var name in a <def> tag"); return block; } BlockPointer parent = Block::getCurrentBlock(block); // Add a new BLock BlockPointer newBlock = std::make_shared<Block>(_root->sourceName); (newBlock->ostr) << grabbed; newBlock->command.type = Command::DEF; newBlock->command.arguments.push_back(varName); if (!val.empty()) { // loose first character which should be a white space val = val.substr(val.find_first_not_of(' ')); convertExpressionToDefArguments(val, newBlock->command.arguments); } Block::addNewBlock(parent, newBlock); // dive in the new block return newBlock;} const BlockPointer TextTemplate::processStepCommandIf(const BlockPointer& block, String& grabbed, String& expression) { BlockPointer parent = Block::getCurrentBlock(block); // Add a new BLock depth BlockPointer newIfBlock = std::make_shared<Block>(_root->sourceName); newIfBlock->command.type = Command::IFBLOCK; Block::addNewBlock(parent, newIfBlock); BlockPointer newBlock = std::make_shared<Block>(_root->sourceName); (newBlock->ostr) << grabbed; newBlock->command.type = Command::IF; convertExpressionToArguments(expression, newBlock->command.arguments);Lines 1–500 of 998. Load more
// // TextTemplate.cpp // tools/shaderScribe/src // // Created by Sam Gateau on 12/15/2014. // Copyright 2013 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html #include "TextTemplate.h" #include <stdarg.h> #include <fstream> #include <sstream> #include <algorithm> typedef TextTemplate::Block::Pointer BlockPointer; typedef TextTemplate::Config::Pointer ConfigPointer; typedef TextTemplate::Pointer TextTemplatePointer; const std::string TextTemplate::Tag::NULL_VAR = "_SCRIBE_NULL"; //----------------------------------------------------------------------------- TextTemplate::Config::Config() : _includes(), _funcs(), _logStream(&std::cout), _numErrors(0), _includerCallback(TextTemplate::loadFile) { _paths.push_back(""); } const TextTemplatePointer TextTemplate::Config::addInclude(const ConfigPointer& config, const char* include) { if (!config) { return TextTemplatePointer(); } TextTemplatePointer included = config->findInclude(include); if (included) { return included; } // INcluded doest exist yet so let's try to create it String includeStream; if (config->_includerCallback(config, include, includeStream)) { // ok, then create a new Template on the include file with this as lib included = std::make_shared<TextTemplate>(include, config); std::stringstream src(includeStream); int nbErrors = included->parse(src); if (nbErrors > 0) { included->logError(included->_root, "File failed to parse, not included"); return TextTemplatePointer(); } config->_includes.insert(Includes::value_type(include, included)); return included; } return TextTemplatePointer(); } const TextTemplatePointer TextTemplate::Config::findInclude(const char* include) { Includes::iterator includeIt = _includes.find(String(include)); if (includeIt != _includes.end()) { return (*includeIt).second; } else { return TextTemplatePointer(); } } void TextTemplate::Config::addIncludePath(const char* path) { _paths.push_back(String(path)); } bool TextTemplate::loadFile(const ConfigPointer& config, const char* filename, String& source) { String sourceFile(filename); String fullfilename; for (unsigned int i = 0; i < config->_paths.size(); i++) { fullfilename = config->_paths[i] + sourceFile; std::ifstream ifs; ifs.open(fullfilename.c_str()); if (ifs.is_open()) { std::string str((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>()); source = str; ifs.close(); return (source.length() > 0); } } return false; } TextTemplate::Funcs::Funcs() : _funcs() { } TextTemplate::Funcs::~Funcs() { } const BlockPointer TextTemplate::Funcs::findFunc(const char* func) { map::iterator it = _funcs.find(String(func)); if (it != _funcs.end()) { return (*it).second; } else { return BlockPointer(); } } const BlockPointer TextTemplate::Funcs::addFunc(const char* func, const BlockPointer& funcBlock) { BlockPointer included = findFunc(func); if (! included) { _funcs.insert(map::value_type(func, funcBlock)); } return included; } void TextTemplate::Block::addNewBlock(const Pointer& parent, const Pointer& block) { if (parent) { parent->blocks.push_back(block); block->parent = parent; } } const BlockPointer& TextTemplate::Block::getCurrentBlock(const Pointer& block) { if (block && block->command.isBlockEnd()) { return block->parent; } else { return block; } } void TextTemplate::logError(const Block::Pointer& block, const char* fmt, ...) { va_list argp; va_start(argp, fmt); char buff[256]; sprintf(buff, fmt, argp); _numErrors++; log() << block->sourceName << " Error >>" << buff << std::endl; int level = 1; displayTree(std::cerr, level); } bool TextTemplate::grabUntilBeginTag(std::istream* str, std::string& grabbed, Tag::Type& tagType) { std::stringstream dst; while (!str->eof()) { std::string datatoken; getline((*str), datatoken, Tag::BEGIN); dst << datatoken; char next = str->peek(); if (next == Tag::VAR) { tagType = Tag::VARIABLE; grabbed = dst.str(); str->get(); // skip tag char return true; } else if (next == Tag::COM) { tagType = Tag::COMMAND; grabbed = dst.str(); str->get(); // skip tag char return true; } else if (next == Tag::REM) { tagType = Tag::REMARK; grabbed = dst.str(); str->get(); // skip tag char return true; } else { if (!str->eof()) { // false positive, just found the Tag::BEGIN without consequence dst << Tag::BEGIN; // keep searching } else { // end of the file finishing with no tag tagType = Tag::INVALID; grabbed = dst.str(); return true; } } } return false; } bool TextTemplate::grabUntilEndTag(std::istream* str, std::string& grabbed, Tag::Type& tagType) { std::stringstream dst; // preEnd char depends on tag type char preEnd = Tag::COM; if (tagType == Tag::VARIABLE) { preEnd = Tag::VAR; } else if (tagType == Tag::REMARK) { preEnd = Tag::REM; } while (!str->eof() && !str->fail()) { // looking for the end of the tag means find the next preEnd std::string dataToken; getline((*str), dataToken, preEnd); char end = str->peek(); dst << dataToken; // and if the next char is Tag::END then that's it if (end == Tag::END) { grabbed = dst.str(); str->get(); // eat the Tag::END return true; } else { // false positive, keep on searching dst << preEnd; } } grabbed = dst.str(); return false; } bool TextTemplate::stepForward(std::istream* str, std::string& grabbed, std::string& tag, Tag::Type& tagType, Tag::Type& nextTagType) { if (str->eof()) { return false; } if (!_steppingStarted) { _steppingStarted = true; return grabUntilBeginTag(str, grabbed, nextTagType); } // Read from the last opening Tag::BEGIN captured to the next Tag::END if (grabUntilEndTag(str, tag, tagType)) { // skip trailing space and new lines only after Command or Remark tag block if ((tagType == Tag::COMMAND) || (tagType == Tag::REMARK)) { while (!str->eof() && !str->fail()) { char c = str->peek(); if ((c == ' ') || (c == '\t') || (c == '\n')) { str->get(); } else { break; } } } grabUntilBeginTag(str, grabbed, nextTagType); } return true; //hasElement; } const BlockPointer TextTemplate::processStep(const BlockPointer& block, std::string& grabbed, std::string& tag, Tag::Type& tagType) { switch (tagType) { case Tag::INVALID: block->ostr << grabbed; return block; break; case Tag::VARIABLE: return processStepVar(block, grabbed, tag); break; case Tag::COMMAND: return processStepCommand(block, grabbed, tag); break; case Tag::REMARK: return processStepRemark(block, grabbed, tag); break; } logError(block, "Invalid tag"); return block; } bool TextTemplate::grabFirstToken(String& src, String& token, String& reminder) { bool goOn = true; std::string::size_type i = 0; while (goOn && (i < src.length())) { char c = src[i]; if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_') || (c == '.') || (c == '[') || (c == ']')) { token += c; } else { if (!token.empty()) { reminder = src.substr(i); return true; } } i++; } return (!token.empty()); } bool TextTemplate::convertExpressionToArguments(String& src, std::vector< String >& arguments) { std::stringstream str(src); String token; while (!str.eof()) { str >> token; if (!str.fail()) { arguments.push_back(token); } } return true; } bool TextTemplate::convertExpressionToDefArguments(String& src, std::vector< String >& arguments) { if (src.empty()) { return false; } std::stringstream argstr(src); std::stringstream dest; bool inVar = false; while (!argstr.eof()) { // parse the value of a var, try to find a VAR, so look for the pattern BEGIN,VAR ... VAR,END String token; char tag; if (!inVar) { getline(argstr, token, Tag::BEGIN); dest << token; tag = argstr.peek(); } else { getline(argstr, token, Tag::END); dest << token; tag = token.back(); } if (tag == Tag::VAR) { if (!inVar) { // real var coming: arguments.push_back(dest.str()); inVar = true; } else { // real var over arguments.push_back(dest.str()); inVar = false; } } else { if (argstr.eof()) { arguments.push_back(dest.str()); } else { // put back the tag char stolen dest << (!inVar ? Tag::BEGIN : Tag::END); } } } return true; } bool TextTemplate::convertExpressionToFuncArguments(String& src, std::vector< String >& arguments) { if (src.empty()) { return false; } std::stringstream streamSrc(src); String params; getline(streamSrc, params, '('); getline(streamSrc, params, ')'); if (params.empty()) { return false; } std::stringstream str(params); String token; int nbTokens = 0; while (!str.eof()) { char c = str.peek(); if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_') || (c == '.') || (c == Tag::VAR) || (c == '[') || (c == ']')) { token += c; } else if (c == ',') { if (!token.empty()) { if (token == Tag::NULL_VAR) { arguments.push_back(Tag::NULL_VAR); } else { arguments.push_back(token); } nbTokens++; } token.clear(); } str.get(); } if (token.size()) { arguments.push_back(token); nbTokens++; } return (nbTokens > 0); } const BlockPointer TextTemplate::processStepVar(const BlockPointer& block, String& grabbed, String& tag) { // then look at the define String var = tag; String varName; String val; if (grabFirstToken(var, varName, val)) { if (!varName.empty()) { BlockPointer parent = Block::getCurrentBlock(block); // Add a new BLock BlockPointer newBlock = std::make_shared<Block>(_root->sourceName); (newBlock->ostr) << grabbed; newBlock->command.type = Command::VAR; newBlock->command.arguments.push_back(varName); if (!val.empty()) { convertExpressionToFuncArguments(val, newBlock->command.arguments); } Block::addNewBlock(parent, newBlock); // dive in the new block return newBlock; } } return block; } const BlockPointer TextTemplate::processStepCommand(const BlockPointer& block, String& grabbed, String& tag) { // Grab the command name String command = tag; String commandName; if (grabFirstToken(command, commandName, command)) { if (commandName.compare("def") == 0) { return processStepDef(block, grabbed, command); } else if (commandName.compare("if") == 0) { return processStepCommandIf(block, grabbed, command); } else if (commandName.compare("endif") == 0) { return processStepCommandEndIf(block, grabbed, command); } else if (commandName.compare("else") == 0) { return processStepCommandElse(block, grabbed, command); } else if (commandName.compare("elif") == 0) { return processStepCommandElif(block, grabbed, command); } else if (commandName.compare("include") == 0) { return processStepInclude(block, grabbed, command); } else if (commandName.compare("func") == 0) { return processStepFunc(block, grabbed, command); } else if (commandName.compare("endfunc") == 0) { return processStepEndFunc(block, grabbed, command); } } return block; } const BlockPointer TextTemplate::processStepDef(const BlockPointer& block, String& grabbed, String& def) { // then look at the define String varName; String val; if (!grabFirstToken(def, varName, val)) { logError(block, "Invalid Var name in a <def> tag"); return block; } BlockPointer parent = Block::getCurrentBlock(block); // Add a new BLock BlockPointer newBlock = std::make_shared<Block>(_root->sourceName); (newBlock->ostr) << grabbed; newBlock->command.type = Command::DEF; newBlock->command.arguments.push_back(varName); if (!val.empty()) { // loose first character which should be a white space val = val.substr(val.find_first_not_of(' ')); convertExpressionToDefArguments(val, newBlock->command.arguments); } Block::addNewBlock(parent, newBlock); // dive in the new block return newBlock; } const BlockPointer TextTemplate::processStepCommandIf(const BlockPointer& block, String& grabbed, String& expression) { BlockPointer parent = Block::getCurrentBlock(block); // Add a new BLock depth BlockPointer newIfBlock = std::make_shared<Block>(_root->sourceName); newIfBlock->command.type = Command::IFBLOCK; Block::addNewBlock(parent, newIfBlock); BlockPointer newBlock = std::make_shared<Block>(_root->sourceName); (newBlock->ostr) << grabbed; newBlock->command.type = Command::IF; convertExpressionToArguments(expression, newBlock->command.arguments);