#ifndef ACTIONSANDRULES_H #define ACTIONSANDRULES_H ///////////////////////////////////////////////////////////////////// // ActionsAndRules.h - declares new parsing rules and actions // // ver 3.5 // // Language: Visual C++ 2008, SP1 // // Platform: Dell Precision T7400, Vista Ultimate SP1 // // Application: Prototype for CSE687 Pr1, Sp09 // // Author: Jim Fawcett, CST 4-187, Syracuse University // // (315) 443-3948, jfawcett@twcny.rr.com // ///////////////////////////////////////////////////////////////////// /* Module Operations: ================== This module defines several action classes. Its classes provide specialized services needed for specific applications. The modules Parser, Semiression, and Tokenizer, are intended to be reusable without change. This module provides a place to put extensions of these facilities and is not expected to be reusable. Required files ============== - Parser.h, Parser.cpp, ScopeStack.h, ScopeStack.cpp, ActionsAndRules.h, ActionsAndRules.cpp, ConfigureParser.cpp, ITokenCollection.h, Semiression.h, Semiression.cpp, tokenizer.h, tokenizer.cpp Maintenance History: ==================== ver 3.5 : 11 Feb 2019 - small changes spread over many of the Rule and Action classes, caused by changing the lexical scanner, which forced some changes to the scanner's interface (sigh!) ver 3.4 : 28 May 2017 - Added several tests for scopestack size in actions to fix exceptions thrown when processing code that won't compile. ver 3.3 : 26 Feb 2017 - Fixed bug in public data analysis with changes to rule CppDeclaration and its action HandleCppDeclaration. - Fixed a minor bug in name collection for operators. Won't be important for dependency analysis. ver 3.2 : 28 Aug 16 - fixed errors in many rules and actions based on lots of testing - cleaned up text, removing comments and improving prologues ver 3.1 : 23 Aug 16 - qualified input pointers in rules and actions as const - cleaned up code by removing unreachables and commented code, and by simplifying ver 3.0 : 06 Aug 16 - Added use of AbstrSynTree - Added new rules and actions - Renamed and modified most of the other actions and rules ver 2.1 : 15 Feb 16 - small functional change to a few of the actions changes display strategy - preface the (new) Toker and Semi with Lexer namespace ver 2.0 : 01 Jun 11 - added processing on way to building strong code analyzer ver 1.1 : 17 Jan 09 - changed to accept a pointer to interfaced ITokenCollection instead of a Semiression ver 1.0 : 12 Jan 06 - first release Planned Changes: ================ C++ lambda detection needs strengthening */ // #include #include #include #include #include "Parser.h" #include "../GrammarHelpers/GrammarHelpers.h" #include "../SemiExpression/ITokenCollection.h" #include "../ScopeStack/ScopeStack.h" #include "../Tokenizer/Toker.h" #include "../SemiExpression/Semi.h" #include "../AbstractSyntaxTree/AbstrSynTree.h" #include "../Logger/Logger.h" #include "../FileSystem/FileSystem.h" namespace CodeAnalysis { /////////////////////////////////////////////////////////////////// // Repository instance is used to share resources // among all actions. enum Language { C /* not implemented */, Cpp, CSharp }; class Repository // application specific { public: using Rslt = Logging::StaticLogger<0>; // use for application results using Demo = Logging::StaticLogger<1>; // use for demonstrations of processing using Dbug = Logging::StaticLogger<2>; // use for debug output using Package = std::string; using Path = std::string; private: Language language_ = Language::Cpp; Path path_; ScopeStack stack; AbstrSynTree ast; ASTNode* pGlobalScope; Package package_; Lexer::Toker* p_Toker; Access currentAccess_ = Access::publ; static Repository* instance; public: Repository(Lexer::Toker* pToker) : ast(stack) { p_Toker = pToker; pGlobalScope = stack.top(); // installed in AST constructor instance = this; } ~Repository() { Dbug::write("\n deleting repository"); } Language& language() { return language_; } Package& package() { return package_; } Path& currentPath() { return path_; } Access& currentAccess() { return currentAccess_; } static Repository* getInstance() { return instance; } ScopeStack& scopeStack() { return stack; } AbstrSynTree& AST() { return ast; } ASTNode* getGlobalScope() { return pGlobalScope; } Lexer::Toker* Toker() { return p_Toker; } size_t lineCount() { return (size_t)(p_Toker->lineCount()); } }; /////////////////////////////////////////////////////////////// // rule to detect beginning of scope class BeginScope : public IRule { public: bool doTest(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Test begin scope", *pTc); // don't parse Semi with single semicolon token if (pTc->size() == 1 && (*pTc)[0] == ";") return IRule::Stop; if (pTc->contains("{")) { doActions(pTc); } return IRule::Continue; } }; /////////////////////////////////////////////////////////////// // action to handle scope stack at beginning of scope class HandleBeginScope : public IAction { Repository* p_Repos; public: HandleBeginScope(Repository* pRepos) { p_Repos = pRepos; } void doAction(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("handle begin scope", *pTc); ASTNode* pElem = new ASTNode; pElem->type_ = "anonymous"; pElem->name_ = "none"; pElem->package_ = p_Repos->package(); pElem->startLineCount_ = p_Repos->lineCount(); pElem->endLineCount_ = 1; pElem->path_ = p_Repos->currentPath(); /* * make this ASTNode child of ASTNode on stack top * then push onto stack */ p_Repos->AST().add(pElem); } }; /////////////////////////////////////////////////////////////// // rule to detect end of scope class EndScope : public IRule { public: bool doTest(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Test end scope", *pTc); size_t posCloseBrace; pTc->find("}", posCloseBrace); if (posCloseBrace < pTc->size()) { doActions(pTc); return IRule::Stop; } return IRule::Continue; } }; /////////////////////////////////////////////////////////////// // action to handle scope stack at end of scope class HandleEndScope : public IAction { Repository* p_Repos; public: using Dbug = Logging::StaticLogger<2>; HandleEndScope(Repository* pRepos) { p_Repos = pRepos; } void doAction(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Handle end scope", *pTc); if (p_Repos->scopeStack().size() == 0) return; ASTNode* pElem = p_Repos->AST().pop(); if (pElem == nullptr) return; pElem->endLineCount_ = p_Repos->lineCount(); if (pElem->type_ == "class" || pElem->type_ == "struct") (pElem->endLineCount_)++; p_Repos->currentAccess() = Access::priv; } }; /////////////////////////////////////////////////////////////// // rule to detect access for C++ class DetectAccessSpecifier : public IRule { public: bool doTest(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Test access spec", *pTc); Repository* pRepo = Repository::getInstance(); if (pRepo->language() != Language::Cpp) return IRule::Continue; size_t pos; pTc->find(":", pos); if (0 < pos && pos < pTc->size()) { const std::string tok = (*pTc)[pos - 1]; if (tok == "public" || tok == "protected" || tok == "private") { doActions(pTc); return IRule::Stop; } } return IRule::Continue; } }; /////////////////////////////////////////////////////////////// // action to handle access specifier class HandleAccessSpecifier : public IAction { Repository* p_Repos; public: HandleAccessSpecifier(Repository* pRepos) { p_Repos = pRepos; } void doAction(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Handle access spec: ", *pTc); std::string tok = (*pTc)[pTc->size() - 2]; Access& access = p_Repos->currentAccess(); if (tok == "public") access = Access::publ; else if (tok == "protected") access = Access::prot; else access = Access::priv; } }; /////////////////////////////////////////////////////////////// // rule to detect preprocessor statements class PreprocStatement : public IRule { public: bool doTest(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Test preproc statement: ", *pTc); size_t pPos; pTc->find("#", pPos); if (pPos < pTc->size()) { doActions(pTc); return IRule::Stop; } return IRule::Continue; } }; /////////////////////////////////////////////////////////////// // action to add Semi to scope stack top statements_ class HandlePreprocStatement : public IAction { Repository* p_Repos; public: HandlePreprocStatement(Repository* pRepos) { p_Repos = pRepos; } void doAction(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Handle preproc statement: ", *pTc); Lexer::ITokenCollection* pClone = pTc->clone(); if (p_Repos->scopeStack().size() == 0) return; ASTNode* pElem = p_Repos->scopeStack().top(); pElem->statements_.push_back(pClone); GrammarHelper::showParse("Preproc Stmt", *pTc); } }; /////////////////////////////////////////////////////////////// // rule to detect namespace statements class NamespaceDefinition : public IRule { public: bool doTest(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Test namespace definition: ", *pTc); const Lexer::ITokenCollection& tc = *pTc; if (tc[tc.size() - 1] == "{") { size_t len; tc.find("namespace", len); if (len < tc.size()) { doActions(pTc); return IRule::Stop; } } return IRule::Continue; } }; /////////////////////////////////////////////////////////////// // action to add namespace info to scope stack top class HandleNamespaceDefinition : public IAction { Repository* p_Repos; public: HandleNamespaceDefinition(Repository* pRepos) { p_Repos = pRepos; } void doAction(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Handle namespace definition: ", *pTc); if (p_Repos->scopeStack().size() == 0) return; ASTNode* top = p_Repos->scopeStack().top(); size_t pos; pTc->find("namespace", pos); std::string name = (*pTc)[pos + 1]; top->type_ = "namespace"; top->name_ = name; top->package_ = p_Repos->package(); GrammarHelper::showParse("namespace def", *pTc); } }; /////////////////////////////////////////////////////////////// // rule to detect class statements class ClassDefinition : public IRule { public: bool doTest(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Test class definition: ", *pTc); const Lexer::ITokenCollection& tc = *pTc; if (tc[tc.size() - 1] == "{") { size_t len; tc.find("class", len); if (len < tc.size()) { doActions(pTc); return IRule::Stop; } if (tc.contains("interface")) { doActions(pTc); return IRule::Stop; } } return IRule::Continue; } }; /////////////////////////////////////////////////////////////// // action to add class info to scope stack top class HandleClassDefinition : public IAction { Repository* p_Repos; public: HandleClassDefinition(Repository* pRepos) { p_Repos = pRepos; } void doAction(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Handle class definition: ", *pTc); p_Repos->currentAccess() = Access::priv; if (p_Repos->scopeStack().size() == 0) return; ASTNode* top = p_Repos->scopeStack().top(); size_t typeIndex; pTc->find("class", typeIndex); if (typeIndex < pTc->size()) { size_t nameIndex = typeIndex + 1; std::string name = (*pTc)[nameIndex]; top->type_ = "class"; top->name_ = name; top->package_ = p_Repos->package(); p_Repos->AST().typeMap()[name] = top; GrammarHelper::showParse("class def", *pTc); } else // C# { pTc->find("interface", typeIndex); size_t nameIndex = typeIndex + 1; std::string name = (*pTc)[nameIndex]; top->type_ = "interface"; top->name_ = name; top->package_ = p_Repos->package(); p_Repos->AST().typeMap()[name] = top; GrammarHelper::showParse("interface def", *pTc); } } }; /////////////////////////////////////////////////////////////// // rule to detect struct statements class StructDefinition : public IRule { public: bool doTest(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Test struct definition: ", *pTc); const Lexer::ITokenCollection& tc = *pTc; if (tc[tc.size() - 1] == "{") { size_t len; tc.find("struct", len); if (len < tc.size()) { doActions(pTc); return IRule::Stop; } } return IRule::Continue; } }; /////////////////////////////////////////////////////////////// // action to add struct info to scope stack top class HandleStructDefinition : public IAction { Repository* p_Repos; public: HandleStructDefinition(Repository* pRepos) { p_Repos = pRepos; } void doAction(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Handle struct definition: ", *pTc); p_Repos->currentAccess() = Access::publ; if (p_Repos->scopeStack().size() == 0) return; ASTNode* top = p_Repos->scopeStack().top(); size_t pos; pTc->find("struct", pos); std::string name = (*pTc)[pos + 1]; top->type_ = "struct"; top->name_ = name; top->package_ = p_Repos->package(); p_Repos->AST().typeMap()[name] = top; GrammarHelper::showParse("struct def", *pTc); } }; /////////////////////////////////////////////////////////////// // rule to detect C++ function definitions class CppFunctionDefinition : public IRule { public: bool doTest(const Lexer::ITokenCollection* pTc) override { Repository* pRepo = Repository::getInstance(); if (pRepo->language() != Language::Cpp) return IRule::Continue; GrammarHelper::showParseDemo("Test C++ function definition: ", *pTc); ScopeStack& stack = pRepo->scopeStack(); if (stack.size() < 2) return IRule::Continue; std::string parentType = stack.predOfTop()->type_; if (parentType != "namespace" && parentType != "class" && parentType != "struct") return IRule::Continue; const Lexer::ITokenCollection& tc = *pTc; if (tc[tc.size() - 1] == "{") { if (GrammarHelper::isFunction(*pTc)) { doActions(pTc); return IRule::Stop; } } return IRule::Continue; } }; /////////////////////////////////////////////////////////////// // action to add function info to scope stack top class HandleCppFunctionDefinition : public IAction { Repository* p_Repos; public: HandleCppFunctionDefinition(Repository* pRepos) { p_Repos = pRepos; } void doAction(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Handle C++ function definition: ", *pTc); ScopeStack& stack = p_Repos->scopeStack(); if (stack.size() == 0) return; ASTNode* top = stack.top(); size_t nameIndex; if(pTc->find("(", nameIndex)) --nameIndex; else return; std::string name = (*pTc)[nameIndex]; // is function a destructor? if (nameIndex > 0 && (*pTc)[nameIndex - 1] == "~") { --nameIndex; name = "~" + name; } top->type_ = "function"; top->name_ = name; top->package_ = p_Repos->package(); GrammarHelper::showParse("function def", *pTc); // is function an operator? size_t operIndex; pTc->find("operator", operIndex); if (operIndex < pTc->size()) { name = "operator" + (*pTc)[operIndex + 1];// +(*pTc)[operIndex + 2]; if ((*pTc)[operIndex + 2] != "(") name += (*pTc)[operIndex + 2]; top->name_ = name; nameIndex = operIndex; } // is function a member of a class or struct? if (nameIndex > 1 && (*pTc)[nameIndex - 1] == "::") { //----< start find class name >-------------------- std::string className = (*pTc)[nameIndex - 2]; // is class a template? if (className == ">") { size_t startParam = GrammarHelper::findLast(*pTc, "<"); if (0 < startParam && startParam < pTc->size()) className = (*pTc)[startParam - 1]; } //----< end find class name >---------------------- /* * - this function's ASTNode is at stack top * - find ASTNode of function's class * - unlink function ASTNode from stack top predecessor * - relink function ASTNode to it's class ASTNode * - leave function ASTNode on stack top as it may have child nodes */ ASTNode* pClassNode = p_Repos->AST().find(className); if (pClassNode == nullptr) return; if (p_Repos->scopeStack().size() < 2) return; ASTNode* pFunctNode = p_Repos->scopeStack().top(); ASTNode* pParentNode = p_Repos->scopeStack().predOfTop(); if(pParentNode->children_.size() > 0) pParentNode->children_.pop_back(); // unlink function pFunctNode->parentType_ = pClassNode->type_; pClassNode->children_.push_back(pFunctNode); // relink function return; } // is this a lambda? std::string packageName = p_Repos->package(); std::string ext = FileSystem::Path::getExt(packageName); size_t posOpenBracket; pTc->find("[", posOpenBracket); size_t posCloseBracket; pTc->find("]", posCloseBracket); size_t posBrace; pTc->find("{", posBrace); size_t posOpenParen; pTc->find("(", posOpenParen); if (posCloseBracket != posBrace + 1 && posCloseBracket != posOpenParen + 1) // []{ or []( return; if (posOpenBracket < posCloseBracket && posBrace == (posCloseBracket + 1) && posBrace < pTc->size()) { std::string name; for (size_t i = posOpenBracket; i < posBrace; ++i) name += (*pTc)[i]; top->name_ = name; top->type_ = "lambda"; } } }; /////////////////////////////////////////////////////////////// // rule to detect C# function definitions class CSharpFunctionDefinition : public IRule { public: bool doTest(const Lexer::ITokenCollection* pTc) override { Repository* pRepo = Repository::getInstance(); if (pRepo->language() != Language::CSharp) return IRule::Continue; GrammarHelper::showParseDemo("Test C# function definition: ", *pTc); const Lexer::ITokenCollection& tc = *pTc; if (tc[tc.size() - 1] == "{") { Lexer::Semi se; for (size_t i = 0; i < tc.size(); ++i) se.add(tc[i]); if (GrammarHelper::isFunction(se)) { doActions(&se); return IRule::Stop; } } return IRule::Continue; } }; /////////////////////////////////////////////////////////////// // action to add function info to scope stack top class HandleCSharpFunctionDefinition : public IAction { Repository* p_Repos; public: HandleCSharpFunctionDefinition(Repository* pRepos) { p_Repos = pRepos; } void doAction(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Handle C# function definition: ", *pTc); if (p_Repos->scopeStack().size() == 0) return; ASTNode* top = p_Repos->scopeStack().top(); size_t nameIndex; if (pTc->find("(", nameIndex)) --nameIndex; else return; std::string name = (*pTc)[nameIndex]; // is function a destructor? if (nameIndex > 0 && (*pTc)[nameIndex - 1] == "~") { --nameIndex; name = "~" + name; } top->type_ = "function"; top->name_ = name; top->package_ = p_Repos->package(); GrammarHelper::showParse("function def", *pTc); // is function an operator? size_t operIndex; pTc->find("operator", operIndex); if (operIndex < pTc->size()) { name = "operator" + (*pTc)[operIndex + 1] + (*pTc)[operIndex + 2]; top->name_ = name; nameIndex = operIndex; } // is lambda? size_t posParen; pTc->find("(", posParen); size_t posBrace; pTc->find("{", posBrace); size_t posEqual; pTc->find("=", posEqual); if (posParen < posBrace && posBrace < pTc->size()) { if (posEqual < pTc->size() - 1 && (*pTc)[posEqual + 1] == ">") { std::string name; for (size_t i = posParen; i <= posBrace; ++i) name += (*pTc)[i]; top->name_ = name; top->type_ = "lambda"; } } } }; /////////////////////////////////////////////////////////////// // rule to detect control definitions class ControlDefinition : public IRule { public: bool doTest(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Test control definition: ", *pTc); const Lexer::ITokenCollection& tc = *pTc; if (tc[tc.size() - 1] == "{") { size_t len; tc.find("(", len); if (len < tc.size() && GrammarHelper::isControlKeyWord(tc[len - 1])) { doActions(pTc); return IRule::Stop; } else if (tc.size() > 1 && GrammarHelper::isControlKeyWord(tc[tc.size() - 2])) { doActions(pTc); return IRule::Stop; } } return IRule::Continue; } }; /////////////////////////////////////////////////////////////// // action to add control info to scope stack top class HandleControlDefinition : public IAction { Repository* p_Repos; public: HandleControlDefinition(Repository* pRepos) { p_Repos = pRepos; } void doAction(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Handle control definition: ", *pTc); if (p_Repos->scopeStack().size() == 0) return; ASTNode* top; if ((p_Repos->scopeStack()).size() > 0) top = p_Repos->scopeStack().top(); else return; size_t nameIndex; if(pTc->find("(", nameIndex)) // if, for, while, switch, catch --nameIndex; //if (nameIndex == pTc->size() - 1) // do, try - they don't have parens else nameIndex = pTc->size() - 2; std::string name = (*pTc)[nameIndex]; top->type_ = "control"; top->name_ = name; GrammarHelper::showParse("control def", *pTc); } }; /////////////////////////////////////////////////////////////// // action to send semi-expression that starts a function def // to console class PrintFunction : public IAction { Repository* p_Repos; public: using Rslt = Logging::StaticLogger<0>; PrintFunction(Repository* pRepos) { p_Repos = pRepos; } void doAction(const Lexer::ITokenCollection* pTc) override { std::ostringstream out; //out << "\n FuncDef: " << pTc->show().c_str(); Rslt::write(out.str()); } }; /////////////////////////////////////////////////////////////// // action to send signature of a function def to console class PrettyPrintFunction : public IAction { Repository* p_Repos; public: using Rslt = Logging::StaticLogger<0>; PrettyPrintFunction(Repository* pRepos) : p_Repos(pRepos) {} void doAction(const Lexer::ITokenCollection* pTc) override { size_t len; pTc->find(")", len); std::ostringstream out; out << "\n\n Pretty Stmt: "; for (size_t i = 0; i < len + 1; ++i) out << (*pTc)[i] << " "; out << "\n"; Rslt::write(out.str()); } }; /////////////////////////////////////////////////////////////// // rule to detect C++ Declaration /* * - Declaration ends in semicolon * - has type, name, modifiers & initializers * So: * - strip off modifiers and initializers * - if you have two things left it's a declar, else executable */ class CppDeclaration : public IRule { public: bool doTest(const Lexer::ITokenCollection* pTc) override { Repository* pRepo = Repository::getInstance(); if (pRepo->language() != Language::Cpp) return IRule::Continue; GrammarHelper::showParseDemo("Test C++ declaration: ", *pTc); Lexer::Semi tc; tc.clone(*pTc); // begin added 2/26/2017 Access access = pRepo->currentAccess(); bool isPublic = false; if (pRepo->scopeStack().size() == 0) return false; std::string parentType = pRepo->scopeStack().top()->type_; size_t posPrivate; pTc->find("private", posPrivate); if (posPrivate < pTc->size()) { isPublic = false; pRepo->currentAccess() = Access::priv; } size_t posProtected; pTc->find("protected", posProtected); if (posProtected < pTc->size()) { isPublic = false; pRepo->currentAccess() = Access::prot; } size_t posPublic; pTc->find("public", posPublic); if (posPublic < pTc->size() && parentType != "function") { isPublic = true; pRepo->currentAccess() = Access::publ; } // end added 2/26/2017 if (tc.size() > 0 && tc[0] == "using") { doActions(pTc); return IRule::Stop; } if (tc[tc.size() - 1] == ";" && tc.size() > 2) { std::string nextToLast = tc[tc.size() - 2]; if (nextToLast == "delete" || nextToLast == "default" || nextToLast == "const") { { // function declaration doActions(pTc); return IRule::Stop; } } std::string parentType = pRepo->scopeStack().top()->type_; if (GrammarHelper::isDataDeclaration(tc) || GrammarHelper::isFunctionDeclaration(tc, parentType)) { doActions(pTc); return IRule::Stop; } if (parentType != "function") { // can't be executable so must be declaration doActions(pTc); return IRule::Stop; } } return IRule::Continue; } }; /////////////////////////////////////////////////////////////// // action to add declaration info to scope stack top class HandleCppDeclaration : public IAction { Repository* p_Repos; public: HandleCppDeclaration(Repository* pRepos) : p_Repos(pRepos) {} void doAction(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Handle C++ declaration: ", *pTc); // save declaration info in ASTNode if (p_Repos->scopeStack().size() == 0) return; ASTNode* pCurrNode = p_Repos->scopeStack().top(); DeclarationNode declNode; declNode.access_ = p_Repos->currentAccess(); declNode.pTc = pTc->clone(); declNode.package_ = p_Repos->package(); declNode.line_ = p_Repos->lineCount(); Lexer::Semi se; se.clone(*pTc); GrammarHelper::removeComments(se); if (se[0] == "using") { declNode.declType_ = DeclType::usingDecl; pCurrNode->decl_.push_back(declNode); GrammarHelper::showParse("using declar", *pTc); return; } if (GrammarHelper::isFunctionDeclaration(se,"parentNotFunction")) { return; } if (GrammarHelper::isDataDeclaration(se)) { declNode.declType_ = DeclType::dataDecl; pCurrNode->decl_.push_back(declNode); GrammarHelper::showParse("data declar", *pTc); } } }; /////////////////////////////////////////////////////////////// // rule to detect C# Declaration /* * - Declaration ends in semicolon * - has type, name, modifiers & initializers * So: * - strip of modifiers and initializers * - if you have two things left it's a declar, else executable */ class CSharpDeclaration : public IRule { public: bool doTest(const Lexer::ITokenCollection* pTc) override { Repository* pRepo = Repository::getInstance(); if (pRepo->language() != Language::CSharp) return IRule::Continue; GrammarHelper::showParseDemo("Test C# declaration: ", *pTc); Access access = pRepo->currentAccess(); bool isPublic = false; if (pRepo->scopeStack().size() == 0) return false; std::string parentType = pRepo->scopeStack().top()->type_; size_t posPublic; pTc->find("public", posPublic); if (posPublic < pTc->size() && parentType != "function") { isPublic = true; pRepo->currentAccess() = Access::publ; } const Lexer::ITokenCollection& tc = *pTc; if (tc.size() > 0 && tc[0] == "using") { doActions(pTc); pRepo->currentAccess() = access; return IRule::Stop; } Lexer::Semi se; se.clone(*pTc); if (GrammarHelper::isDataDeclaration(se)) { doActions(pTc); pRepo->currentAccess() = access; return IRule::Stop; } if (GrammarHelper::isFunctionDeclaration(se, parentType)) { doActions(pTc); pRepo->currentAccess() = access; return IRule::Stop; } return IRule::Continue; } }; /////////////////////////////////////////////////////////////// // action to add declaration info to scope stack top class HandleCSharpDeclaration : public IAction { Repository* p_Repos; public: HandleCSharpDeclaration(Repository* pRepos) : p_Repos(pRepos) {} void doAction(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Handle C# declaration: ", *pTc); // store declaration info in ASTNode if (p_Repos->scopeStack().size() == 0) return; ASTNode* pCurrNode = p_Repos->scopeStack().top(); DeclarationNode declNode; declNode.access_ = p_Repos->currentAccess(); declNode.pTc = pTc->clone(); declNode.package_ = p_Repos->package(); declNode.line_ = p_Repos->lineCount(); Lexer::Semi se; se.clone(*pTc); GrammarHelper::removeComments(se); if (se[0] == "using") { declNode.declType_ = DeclType::usingDecl; pCurrNode->decl_.push_back(declNode); GrammarHelper::showParse("using declar", *pTc); return; } std::string parentType = p_Repos->scopeStack().top()->type_; if (GrammarHelper::isFunctionDeclaration(se, parentType)) { Access adebug = declNode.access_; declNode.declType_ = DeclType::functionDecl; pCurrNode->decl_.push_back(declNode); GrammarHelper::showParse("function declar", *pTc); } else { Access adebug = declNode.access_; declNode.declType_ = DeclType::dataDecl; pCurrNode->decl_.push_back(declNode); GrammarHelper::showParse("data declar", *pTc); } } }; /////////////////////////////////////////////////////////////// // rule to detect C++ Executable class CppExecutable : public IRule { public: bool doTest(const Lexer::ITokenCollection* pTc) override { Repository* pRepo = Repository::getInstance(); if (pRepo->language() != Language::Cpp) return IRule::Continue; GrammarHelper::showParseDemo("Test C++ executable: ", *pTc); Lexer::Semi tc; tc.clone(*pTc); if (tc[tc.size() - 1] == ";" && tc.size() > 2) { GrammarHelper::removeFunctionArgs(tc); GrammarHelper::condenseTemplateTypes(tc); // remove modifiers, comments, newlines, returns, and initializers Lexer::Semi se; for (size_t i = 0; i < tc.size(); ++i) { if (GrammarHelper::isQualifierKeyWord(tc[i])) continue; if (se.isComment(tc[i]) || tc[i] == "\n" || tc[i] == "return") continue; if (tc[i] == "=" || tc[i] == ";") { se.add(";"); break; } else se.add(tc[i]); } if (se.size() != 3) // not a declaration { doActions(pTc); return IRule::Stop; } } return IRule::Continue; } }; /////////////////////////////////////////////////////////////// // action to display C++ executable info class HandleCppExecutable : public IAction { Repository* p_Repo; public: HandleCppExecutable(Repository* pRepo) : p_Repo(pRepo) {} void doAction(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Handle C++ executable: ", *pTc); GrammarHelper::showParse("executable", *pTc); } }; /////////////////////////////////////////////////////////////// // rule to detect C# Executable class CSharpExecutable : public IRule { public: bool doTest(const Lexer::ITokenCollection* pTc) override { Repository* pRepo = Repository::getInstance(); if (pRepo->language() != Language::CSharp) return IRule::Continue; GrammarHelper::showParseDemo("Test C# executable: ", *pTc); const Lexer::ITokenCollection& in = *pTc; Lexer::Semi tc; for (size_t i = 0; i < in.size(); ++i) tc.add(in[i]); if (tc[tc.size() - 1] == ";" && tc.size() > 2) { GrammarHelper::removeFunctionArgs(tc); GrammarHelper::condenseTemplateTypes(tc); // remove modifiers, comments, newlines, returns, and initializers Lexer::Semi se; for (size_t i = 0; i < tc.size(); ++i) { if (GrammarHelper::isQualifierKeyWord(tc[i])) continue; if (se.isComment(tc[i]) || tc[i] == "\n" || tc[i] == "return") continue; if (tc[i] == "=" || tc[i] == ";") { se.add(";"); break; } else se.add(tc[i]); } if (se.size() != 3) // not a declaration { doActions(pTc); return IRule::Stop; } } return IRule::Continue; } }; /////////////////////////////////////////////////////////////// // action to display C# executable info class HandleCSharpExecutable : public IAction { Repository* p_Repo; public: HandleCSharpExecutable(Repository* pRepo) : p_Repo(pRepo) {} void doAction(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Handle C# executable: ", *pTc); Lexer::Semi se; se.clone(*pTc); GrammarHelper::removeComments(se); GrammarHelper::showParse("executable", se); } }; /////////////////////////////////////////////////////////////// // default rule // - this is here to catch any Semi that didn't parse // - We don't have rule for enums, so they are caugth here class Default : public IRule { public: bool doTest(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Test default: ", *pTc); doActions(pTc); // catches everything return IRule::Stop; } }; /////////////////////////////////////////////////////////////// // action to display default info class HandleDefault : public IAction { Repository* p_Repo; public: HandleDefault(Repository* pRepo) : p_Repo(pRepo) {} void doAction(const Lexer::ITokenCollection* pTc) override { GrammarHelper::showParseDemo("Handle default: ", *pTc); GrammarHelper::showParse("default: ", *pTc); } }; } #endif