From 483dcace45e6120fc84ed6c74376a4adf197f30f Mon Sep 17 00:00:00 2001 From: vl241552 <vincent.lorrain@cea.fr> Date: Tue, 12 Sep 2023 15:27:18 +0000 Subject: [PATCH] [graphRegex] init commit --- .../aidge/graphRegex/GraphFsmInterpreter.hpp | 73 ++++ include/aidge/graphRegex/GraphLexer.hpp | 68 ++++ include/aidge/graphRegex/GraphParser.hpp | 95 +++++ include/aidge/graphRegex/GraphRegexTypes.hpp | 29 ++ include/aidge/graphRegex/matchFsm/FsmEdge.hpp | 218 +++++++++++ .../aidge/graphRegex/matchFsm/FsmGraph.hpp | 95 +++++ include/aidge/graphRegex/matchFsm/FsmNode.hpp | 99 +++++ .../graphRegex/matchFsm/FsmRunTimeContext.hpp | 174 +++++++++ .../aidge/graphRegex/matchFsm/MatchResult.hpp | 34 ++ include/aidge/nodeTester/ConditionalData.hpp | 98 +++++ .../nodeTester/ConditionalInterpreter.hpp | 326 ++++++++++++++++ include/aidge/nodeTester/ConditionalLexer.hpp | 86 +++++ .../aidge/nodeTester/ConditionalParser.hpp | 106 ++++++ include/aidge/nodeTester/ConditionalTypes.hpp | 34 ++ include/aidge/utilsParsing/AstNode.hpp | 4 +- include/aidge/utilsParsing/ParsingToken.hpp | 12 +- src/graphRegex/GraphFsmInterpreter.cpp | 182 +++++++++ src/graphRegex/GraphLexer.cpp | 157 ++++++++ src/graphRegex/GraphParser.cpp | 174 +++++++++ src/graphRegex/matchFsm/FsmEdge.cpp | 269 +++++++++++++ src/graphRegex/matchFsm/FsmGraph.cpp | 170 +++++++++ src/graphRegex/matchFsm/FsmNode.cpp | 117 ++++++ src/graphRegex/matchFsm/FsmRunTimeContext.cpp | 204 ++++++++++ src/graphRegex/matchFsm/MatchResult.cpp | 4 + src/nodeTester/ConditionalInterpreter.cpp | 359 ++++++++++++++++++ src/nodeTester/ConditionalLexer.cpp | 242 ++++++++++++ src/nodeTester/ConditionalParser.cpp | 188 +++++++++ unit_tests/graphRegex/Test_Fsm.cpp | 194 ++++++++++ .../graphRegex/Test_GraphFsmInterpreter.cpp | 27 ++ unit_tests/graphRegex/Test_GraphLexer.cpp | 118 ++++++ unit_tests/graphRegex/Test_GraphParser.cpp | 80 ++++ .../Test_ConditionalInterpreter.cpp | 30 ++ .../nodeTester/Test_ConditionalLexer.cpp | 144 +++++++ .../nodeTester/Test_ConditionalParser.cpp | 75 ++++ 34 files changed, 4280 insertions(+), 5 deletions(-) create mode 100644 include/aidge/graphRegex/GraphFsmInterpreter.hpp create mode 100644 include/aidge/graphRegex/GraphLexer.hpp create mode 100644 include/aidge/graphRegex/GraphParser.hpp create mode 100644 include/aidge/graphRegex/GraphRegexTypes.hpp create mode 100644 include/aidge/graphRegex/matchFsm/FsmEdge.hpp create mode 100644 include/aidge/graphRegex/matchFsm/FsmGraph.hpp create mode 100644 include/aidge/graphRegex/matchFsm/FsmNode.hpp create mode 100644 include/aidge/graphRegex/matchFsm/FsmRunTimeContext.hpp create mode 100644 include/aidge/graphRegex/matchFsm/MatchResult.hpp create mode 100644 include/aidge/nodeTester/ConditionalData.hpp create mode 100644 include/aidge/nodeTester/ConditionalInterpreter.hpp create mode 100644 include/aidge/nodeTester/ConditionalLexer.hpp create mode 100644 include/aidge/nodeTester/ConditionalParser.hpp create mode 100644 include/aidge/nodeTester/ConditionalTypes.hpp create mode 100644 src/graphRegex/GraphFsmInterpreter.cpp create mode 100644 src/graphRegex/GraphLexer.cpp create mode 100644 src/graphRegex/GraphParser.cpp create mode 100644 src/graphRegex/matchFsm/FsmEdge.cpp create mode 100644 src/graphRegex/matchFsm/FsmGraph.cpp create mode 100644 src/graphRegex/matchFsm/FsmNode.cpp create mode 100644 src/graphRegex/matchFsm/FsmRunTimeContext.cpp create mode 100644 src/graphRegex/matchFsm/MatchResult.cpp create mode 100644 src/nodeTester/ConditionalInterpreter.cpp create mode 100644 src/nodeTester/ConditionalLexer.cpp create mode 100644 src/nodeTester/ConditionalParser.cpp create mode 100644 unit_tests/graphRegex/Test_Fsm.cpp create mode 100644 unit_tests/graphRegex/Test_GraphFsmInterpreter.cpp create mode 100644 unit_tests/graphRegex/Test_GraphLexer.cpp create mode 100644 unit_tests/graphRegex/Test_GraphParser.cpp create mode 100644 unit_tests/nodeTester/Test_ConditionalInterpreter.cpp create mode 100644 unit_tests/nodeTester/Test_ConditionalLexer.cpp create mode 100644 unit_tests/nodeTester/Test_ConditionalParser.cpp diff --git a/include/aidge/graphRegex/GraphFsmInterpreter.hpp b/include/aidge/graphRegex/GraphFsmInterpreter.hpp new file mode 100644 index 000000000..545b34e72 --- /dev/null +++ b/include/aidge/graphRegex/GraphFsmInterpreter.hpp @@ -0,0 +1,73 @@ +#ifndef __AIDGE_GRAPH_FSM_INTERPRETER_H__ +#define __AIDGE_GRAPH_FSM_INTERPRETER_H__ + +#include <string> +#include <memory> + +#include "aidge/utilsParsing/AstNode.hpp" +#include "aidge/graphRegex/GraphRegexTypes.hpp" +#include "aidge/graphRegex/GraphParser.hpp" +#include "aidge/graphRegex/matchFsm/FsmGraph.hpp" + +namespace Aidge { + + class GraphFsmInterpreter + { + private: + /* data */ + GraphParser mParser; + std::size_t mActGroupe; + std::map<std::string,std::shared_ptr<ConditionalInterpreter>> mNodesCondition; + + public: + GraphFsmInterpreter(const std::string graphMatchExpr,std::map<std::string,std::shared_ptr<ConditionalInterpreter>> nodesCondition); + virtual ~GraphFsmInterpreter() =default; + + + std::shared_ptr<FsmGraph> interpret(void); + + private: + + + std::shared_ptr<FsmGraph> visit(std::shared_ptr<AstNode<gRegexTokenTypes>> AstTree); + + /** + * @defgroup graphFsmInterpreterF Functions for interpreting AST nodes + * @brief For each node type in the AST, define how build the FsmGraph + */ + + + /** + * @ingroup graphFsmInterpreterF + * @brief leaf of fsm make the fsm for test one transition + */ + std::shared_ptr<FsmGraph> keyF(std::shared_ptr<AstNode<gRegexTokenTypes>> AstNode); + /** + * @ingroup graphFsmInterpreterF + * @brief combine two fsm of two expression. + */ + std::shared_ptr<FsmGraph> sepF(std::shared_ptr<FsmGraph> leftFsm,std::shared_ptr<FsmGraph> rigthFsm); + /** + * @ingroup graphFsmInterpreterF + * @brief combine two to make a new that match leftFsm next rigthFsm + */ + std::shared_ptr<FsmGraph> nextF(std::shared_ptr<FsmGraph> leftFsm,std::shared_ptr<FsmGraph> rigthFsm); + /** + * @ingroup graphFsmInterpreterF + * @brief make the fsm match + + */ + std::shared_ptr<FsmGraph> qomF(std::shared_ptr<FsmGraph> fsm); + /** + * @ingroup graphFsmInterpreterF + * @brief make the fsm match * + */ + std::shared_ptr<FsmGraph> qzmF(std::shared_ptr<FsmGraph> fsm); + + }; + + + +} + + +#endif //__AIDGE_GRAPH_STM_INTERPRETER_H__ \ No newline at end of file diff --git a/include/aidge/graphRegex/GraphLexer.hpp b/include/aidge/graphRegex/GraphLexer.hpp new file mode 100644 index 000000000..88698ef34 --- /dev/null +++ b/include/aidge/graphRegex/GraphLexer.hpp @@ -0,0 +1,68 @@ +#ifndef __AIDGE_GRAPH_LEXER_H__ +#define __AIDGE_GRAPH_LEXER_H__ + +#include <string> +#include <memory> +#include <regex> +#include <stdexcept> //error +#include <sstream> + +#include "aidge/utilsParsing/ParsingToken.hpp" +#include "aidge/graphRegex/GraphRegexTypes.hpp" + +namespace Aidge { + + class GraphLexer + { + + public: + GraphLexer( const std::string gRegexExpressions ); + + /** + * @brief Get the next token on the gRegexExpressions + * @return ConditionalToken + */ + std::shared_ptr<ParsingToken<gRegexTokenTypes>> getNextToken(void); + /** + * @brief Restart at the start of the gRegexExpressions + * + */ + void rstPosition(void); + + /** + * @brief Test if the string is completely read + * @return bool + */ + bool isEnd(void); + + + /** + * @brief Get the representation of the class + * @return string + */ + const std::string rep(); + + private: + + /** + * @brief Constructs an error message to display the character not understood by the lexer + * @return error mesage + */ + std::runtime_error badTokenError(const std::string& currentChars,std::size_t position); + + /** + * @brief The expression of the test to be performed on the nodes + */ + const std::string mRegularExpressions; + /** + * @brief The lexer's current position in mConditionalExpressions + */ + std::size_t mPosition; + + }; +} + + + + +#endif //__AIDGE_GRAPH_LEXER_H__ \ No newline at end of file diff --git a/include/aidge/graphRegex/GraphParser.hpp b/include/aidge/graphRegex/GraphParser.hpp new file mode 100644 index 000000000..d0234afbf --- /dev/null +++ b/include/aidge/graphRegex/GraphParser.hpp @@ -0,0 +1,95 @@ +#ifndef _AIDGE_GRAPH_PARSER_H_ +#define _AIDGE_GRAPH_PARSER_H_ + + +#include <memory> // for shared_ptr +#include "aidge/graphRegex/GraphLexer.hpp" +#include "aidge/utilsParsing/AstNode.hpp" +#include "aidge/graphRegex/GraphRegexTypes.hpp" + +namespace Aidge{ + +/** + * @brief this class uses the lexer to create an AST according to a set of gramer rules + */ +class GraphParser{ + + public: + /** + * @brief AST graph creation function + * @param gRegexExpressions String representing the logical fuction to be performed + */ + GraphParser(const std::string gRegexExpressions); + + /** + * @brief AST graph creation function + * @return The AST tree + */ + std::shared_ptr<AstNode<gRegexTokenTypes>> parse(void); + + + private: + /** + * @brief restart at the start of the ConditionalExpressions for LEXER and restart mCurrentToken + */ + void rstParser(void); + + ////////////////// + + /** + * @defgroup ParsingFunctions Function for creating AST + * @brief Functions for recursive construction of the AST representing grammar rules + */ + + /** + * @ingroup ParsingFunctions + * @brief Token reading and verification function + * + */ + void ackToken(gRegexTokenTypes tokenType); + + //TODO TODO + /** + * @ingroup ParsingFunctions + * @brief Function of grammar rules for key : KEY(QOM | QZM)? | CKEY + * @return AST node + */ + std::shared_ptr<AstNode<gRegexTokenTypes>> constructAstExp(void); + + /** + * @ingroup ParsingFunctions + * @brief Function of grammar rules for sequence : seq :exp (NEXT seq)* + * @return AST node + */ + std::shared_ptr<AstNode<gRegexTokenTypes>> constructAstSeq(void); + + /** + * @ingroup ParsingFunctions + * @brief Function of grammar rules for domain : (seq NEXT domain)? | LPAREN domain RPAREN (QOM | QZM) (NEXT domain)? + * @return AST node + */ + std::shared_ptr<AstNode<gRegexTokenTypes>> constructAstDomain(void); + + /** + * @ingroup ParsingFunctions + * @brief Function of grammar rules for multiple exepresion : allExpr: domain (SEP allExpr)* + * @return AST node + */ + std::shared_ptr<AstNode<gRegexTokenTypes>> constructAstAllExpr(void); + + + /** + * @brief The actual token in the parce + */ + std::shared_ptr<ParsingToken<gRegexTokenTypes>> mCurrentToken; + /** + * @brief The lexem use + */ + GraphLexer mLexer; + +}; + + +} + +#endif //_AIDGE_GRAPH_PARSER_H_ diff --git a/include/aidge/graphRegex/GraphRegexTypes.hpp b/include/aidge/graphRegex/GraphRegexTypes.hpp new file mode 100644 index 000000000..ddb9d581a --- /dev/null +++ b/include/aidge/graphRegex/GraphRegexTypes.hpp @@ -0,0 +1,29 @@ + +#ifndef __AIDGE_GREGEX_TOKEN_TYPES_H__ +#define __AIDGE_GREGEX_TOKEN_TYPES_H__ + + +namespace Aidge { + /** + * @brief enum for all types of token use in the of the regex + * 7-5 type + * 4-0 id + */ + enum class gRegexTokenTypes + { + NEXT = (1 << 4)+1, /**< -> */ + + QOM = (1 << 5)+1, /**< + */ + QZM = (1 << 5)+2, /**< * */ + + KEY = (1 << 6)+2, /**< [A-Za-z_0-9]+ */ + CKEY = (1 << 6)+1, /**< [A-Za-z_0-9]+#[0-9]* */ + + SEP = (1<<7) +1, /**< \( */ + LPAREN = (1<<7) +2, /**< \( */ + RPAREN = (1<<7) +3, /**< \) */ + STOP = 0, + }; + +} +#endif //__AIDGE_GREGEX_TOKEN_TYPES_H__ \ No newline at end of file diff --git a/include/aidge/graphRegex/matchFsm/FsmEdge.hpp b/include/aidge/graphRegex/matchFsm/FsmEdge.hpp new file mode 100644 index 000000000..995f14789 --- /dev/null +++ b/include/aidge/graphRegex/matchFsm/FsmEdge.hpp @@ -0,0 +1,218 @@ +#ifndef __AIDGE_FSM_EDGE_H__ +#define __AIDGE_FSM_EDGE_H__ + +#include <memory> +#include <set> +#include <string> + +//#include "graphRegex/matchFsm/FsmNode.hpp" +#include "aidge/nodeTester/ConditionalInterpreter.hpp" +//#include "graphRegex/matchFsm/FsmRunTimeContext.hpp" + + +namespace Aidge{ + + class FsmNode; + class FsmRunTimeContext; + + struct EdgeTestResult { + bool success; + std::set<NodePtr> node; + }; + + /** + * @brief virtual class use test the node on the node to validate + */ + class FsmEdge: public std::enable_shared_from_this<FsmEdge> + { + private: + + /** + * @brief the relative position to this test relative to all the const key + * firse is common id , segond is the relative position + */ + std::map<size_t,int> mRelativePos; + /** + * @brief the ptr on the source node + */ + std::shared_ptr<FsmNode> mNodeSource; + /** + * @brief the ptr on the dest node + */ + std::shared_ptr<FsmNode> mNodeDest; + + public: + FsmEdge(std::shared_ptr<FsmNode> source,std::shared_ptr<FsmNode> dest, const std::shared_ptr<ConditionalInterpreter> toTest); + virtual ~FsmEdge() =default; + + /** + * @brief test is the validation of the node , it must be deffine for all types of edge + * it takes as argument an FSM traversal context and returns a set of next nodes + * @return set of next node or nullptr if not next + */ + + virtual const EdgeTestResult test(const std::shared_ptr<FsmRunTimeContext> stmContext) =0; + + /** + * @brief test is the egde test a common node + * @return true if is a common + */ + virtual bool isCommon(void); + /** + * @brief get the Common idx of the common test in this edge (if is a common edge) + * @return idx of the common + */ + virtual size_t getCommonIdx(void); + /** + * @brief get the relative postion to the common node deffine in this edge + * @return map + */ + const std::map<size_t,int>& getRelative(void); + /** + * @brief add new relative position + */ + void updateRelative( const std::map<size_t,int>& relativePos ); + /** + * @brief get source FsmNode + * @return FsmNode + */ + std::shared_ptr<FsmNode> getSourceNode(void); + /** + * @brief set a new source to the edge + * @return FsmNode + */ + void reSetSouceNode(const std::shared_ptr<FsmNode>& newSource); + /** + * @brief get dest FsmNode + * @return FsmNode + */ + std::shared_ptr<FsmNode> getDestNode(void); + /** + * @brief set a new dest to the edge + * @return FsmNode + */ + void reSetDestNode(const std::shared_ptr<FsmNode>& newDest); + /** + * @brief propagate the edge mRelativePos to the others Edge and recalcul the relative position + */ + void propagateRelativePos(void); + + /** + * @brief test to make on the node to validate + * @see ConditionalInterpreter + */ + const std::shared_ptr<ConditionalInterpreter> mToTest; + }; + + /** + * @brief class spesialisation for not commun node (node that must be match one Unique) transition + */ + class FsmEdgeUnique:public FsmEdge + { + + public: + FsmEdgeUnique(std::shared_ptr<FsmNode> source,std::shared_ptr<FsmNode> dest, const std::shared_ptr<ConditionalInterpreter> toTest); + //~FsmEdgeUnique() override {} + const EdgeTestResult test(const std::shared_ptr<FsmRunTimeContext> stmContext) override; + }; + + /** + * @brief class spesialisation for commun node transition + * @see FsmEdge + */ + class FsmEdgeCommon:public FsmEdge + { + + private: + /** + * @brief the map that defind the ralation between the commonKey find by the lexer and a unique id use to refer to the common node + */ + static std::map<std::string,int> mCommonIdxMap; + /** + * @brief the common id test in this transition + */ + int mCommonIdx; + public: + + /** + * @brief constructor commun node , + * @details during construction, + * the node key found by the lexer is converted to a unique id and the relative positions are updated. + */ + FsmEdgeCommon(std::shared_ptr<FsmNode> source,std::shared_ptr<FsmNode> dest, const std::shared_ptr<ConditionalInterpreter> toTest, const std::string commonKey); + // ~FsmEdgeCommon() override {} + const EdgeTestResult test(const std::shared_ptr<FsmRunTimeContext> stmContext) override; + bool isCommon(void) override; + + }; + + + + /** + * @brief class spesialisation for ref transition + * @see FsmEdge + */ + class FsmEdgeRef:public FsmEdge + { + private: + /** + * @brief the id of one common node that we use as an anchor + */ + const int mRefCommonIdx; + /** + * @brief the delta in terme of child or parent refer to the anchor + */ + const int mdeltaCommonIdx; + public: + FsmEdgeRef(std::shared_ptr<FsmNode> source,std::shared_ptr<FsmNode> dest, const size_t refCommonIdx,const int deltaCommonIdx); + //~FsmEdgeRef() override {} + const EdgeTestResult test(const std::shared_ptr<FsmRunTimeContext> stmContext) override; + + }; + + /** + * @brief class spesialisation for ref empty transition + * @see FsmEdge + */ + class FsmEdgeEmpty:public FsmEdge + { + + public: + FsmEdgeEmpty(std::shared_ptr<FsmNode> source,std::shared_ptr<FsmNode> dest); + //~FsmEdgeEmpty() override {} + const EdgeTestResult test(const std::shared_ptr<FsmRunTimeContext> stmContext) override; + + }; + + + +//////////////////////// +// FACTORY +//////////////////////// + +enum class FsmEdgeTypes { + EMPTY = 0, + REF, + COMMON, + UNIQUE +}; + + +class FsmEdgeFactory { + public: + /** + * @brief factory for making edge and read the info in the lexeme of the token + * @param source source node of the edge + * @param dest Dest node of the edge + * @param type type of the edge + * @param lexeme the additional information to build the edge + * @return s prt of the edge + */ + static std::shared_ptr<FsmEdge> make(std::shared_ptr<FsmNode> source, std::shared_ptr<FsmNode> dest, + FsmEdgeTypes type,std::map<std::string, std::shared_ptr<ConditionalInterpreter>> allTest, + const std::string lexeme = ""); + }; + +} + +#endif //__AIDGE_FSM_EDGE_H__ \ No newline at end of file diff --git a/include/aidge/graphRegex/matchFsm/FsmGraph.hpp b/include/aidge/graphRegex/matchFsm/FsmGraph.hpp new file mode 100644 index 000000000..a5257eb44 --- /dev/null +++ b/include/aidge/graphRegex/matchFsm/FsmGraph.hpp @@ -0,0 +1,95 @@ + +#ifndef __AIDGE_FSM_GRAPH_H__ +#define __AIDGE_FSM_GRAPH_H__ + +#include <set> +#include <vector> +#include <memory> +#include <stdexcept> //error + +#include "aidge/graphRegex/matchFsm/FsmNode.hpp" +#include "aidge/graphRegex/matchFsm/FsmEdge.hpp" +#include "aidge/graphRegex/matchFsm/MatchResult.hpp" +namespace Aidge{ + + + +class FsmGraph +{ +private: + std::set<std::size_t> mAllOrigine; + std::set<std::shared_ptr<FsmEdge>> mEdges; +public: + FsmGraph(/* args */); + virtual ~FsmGraph() = default; + +std::vector<std::shared_ptr<MatchResult>> test(std::vector<NodePtr>& StartNodes); + + + +const std::set<std::shared_ptr<FsmEdge>>& getEdge(void); +/** + * @brief add edge in the graph, as FsmEdge know the source and dest FsmNode these nodes are also add to the graph +*/ +void addEdge(std::shared_ptr<FsmEdge> edge); + +/** + * @brief get the liste of the starting states + * @details we need to use a vector because the order of the nodes is important for start node initialization \ref test() +*/ +const std::vector<std::shared_ptr<FsmNode>> getStartNodes(void); + +/** + * @brief get the set of the valide states + * @return set of valide state +*/ +const std::set<std::shared_ptr<FsmNode>> getValidNodes(void); + +/** + * @brief get the set of all the node in the graph + * @return set of all nodes +*/ +const std::set<std::shared_ptr<FsmNode>> getNodes(void); + +/** + * @brief set a groupe idx for all the nodes in the graph +*/ +void setGroupe(std::size_t groupeIdx); + +/** + * @brief make the union beteen this graph and an input graph + * @param fsmGraph graph to union +*/ +void unionG(const std::shared_ptr<FsmGraph> fsmGraph); + + +/** + * @brief make the union beteen this graph and an input graph and merge the valide state to the start state + * @param fsmGraph graph to merge +*/ +void mergeOneStartOneValid(const std::shared_ptr< FsmGraph> fsmGraph); +/** + * @brief get the number of sub FSM + * @return number of sub Fsm +*/ +std::size_t getNbSubFsm(void); + +/** + * @brief increment the origine of all node in the graph + * @param incr the incrémentation value +*/ +void incOrigineAllNodeBy(std::size_t incr); + +private: + +/** + * @brief merge tow node of the graph + * @param node +*/ +void _mergeNode(std::shared_ptr<FsmNode> source,std::shared_ptr<FsmNode> dest); + +}; + + +} +#endif //__AIDGE_FSM_GRAPH_H__ \ No newline at end of file diff --git a/include/aidge/graphRegex/matchFsm/FsmNode.hpp b/include/aidge/graphRegex/matchFsm/FsmNode.hpp new file mode 100644 index 000000000..7155e35c3 --- /dev/null +++ b/include/aidge/graphRegex/matchFsm/FsmNode.hpp @@ -0,0 +1,99 @@ +#ifndef __AIDGE_FSM_NODE_H__ +#define __AIDGE_FSM_NODE_H__ + +#include <set> +#include <vector> +#include <memory> + +//#include "graphRegex/matchFsm/FsmEdge.hpp" +//#include "graphRegex/matchFsm/FsmRunTimeContext.hpp" + +namespace Aidge{ + // Forward declaration of the class defined in graphRegex/matchFsm/FsmEdge.hpp + class FsmEdge; + struct EdgeTestResult; + class FsmRunTimeContext; + + + //------------------------------------------------------------------------------ + + // MAY BE IN UTILE + template <typename T> + struct lex_compare { + bool operator() (const std::weak_ptr<T> &lhs, const std::weak_ptr<T> &rhs)const { + auto lptr = lhs.lock(), rptr = rhs.lock(); + if (!rptr) return false; // nothing after expired pointer + if (!lptr) return true; + return lptr < rptr; + } + }; + + /** + * @brief is a node in the FSM graph, it's a state in the FSM + * @details a state can be and/or : + * - a valide state, the match is valide if it stop on this edge + * - a start state , the match start on this state + * The state is also define by this origine (is the unique id of it's expretion ) + * and it's groupe (for inner expression TODO) + */ + class FsmNode : public std::enable_shared_from_this<FsmNode> + { + private: + /** + * @brief the edge of the node + * @details the edge have a shared ref to the node so we use weak ref + */ + std::set<std::weak_ptr<FsmEdge>,lex_compare<FsmEdge>> mEdges; + /** + * @brief the parent of the node + */ + std::set<std::weak_ptr<FsmNode>,lex_compare<FsmNode>> mParents; + + std::size_t mOrigineStm = 0; + std::size_t mGroupeStm = 0; + + bool mIsAValid; + bool mIsAStart; + + public: + FsmNode(bool isAValid,bool isAStart ); + virtual ~FsmNode() = default; + /** + * @brief use to MAG the actual context , and return all the posible new context + * @details one input context can generate a multitude of contexts because a graph node + * can have more than one child, and each traversal possibility is a new context. + * @param actContext the actual context + * @return A vector of all the new context + */ + const std::vector<std::shared_ptr<FsmRunTimeContext>> test( std::shared_ptr<FsmRunTimeContext>); + + + std::size_t getOrigine(void); + void incOrigine(std::size_t inc); + + + void rmEdge(std::shared_ptr<FsmEdge>); + void addEdge(std::shared_ptr<FsmEdge>); + + const std::set<std::shared_ptr<FsmNode>> getChildNodes(void); + + const std::set<std::weak_ptr<FsmNode>,lex_compare<FsmNode>> getParentNodes(void); + const std::set<std::weak_ptr<FsmEdge>,lex_compare<FsmEdge>> getEdges(void); + + void setGroupe(std::size_t groupeIdx); + + bool isValid(void); + bool isStart(void); + void unValid(void); + void valid(void); + void unStart(void); + void start(void); + + + + void addParent(std::shared_ptr<FsmNode>); + void rmParent(std::shared_ptr<FsmNode>); + }; + +} +#endif //__AIDGE_FSM_NODE_H__ \ No newline at end of file diff --git a/include/aidge/graphRegex/matchFsm/FsmRunTimeContext.hpp b/include/aidge/graphRegex/matchFsm/FsmRunTimeContext.hpp new file mode 100644 index 000000000..07ada14db --- /dev/null +++ b/include/aidge/graphRegex/matchFsm/FsmRunTimeContext.hpp @@ -0,0 +1,174 @@ +#ifndef __AIDGE_FSM_RUN_TIME_CONTEXT_H__ +#define __AIDGE_FSM_RUN_TIME_CONTEXT_H__ + +#include <memory> +#include <vector> +#include <set> +#include <algorithm> + +//#include "graphRegex/matchFsm/FsmNode.hpp" +#include "aidge/nodeTester/ConditionalInterpreter.hpp" +#include "aidge/graph/Node.hpp" + + + +namespace Aidge{ + + class FsmNode; + + class FsmNode; + + /** + * @brief a class used to save the execution context of state machines, that is the actual state in the FSM, the actual node in the graph + * all node that have been Validate,Rejecte or Considered common + */ + class FsmRunTimeContext + { + private: + /** + * @brief the list of node rejected for all the context + */ + static std::vector<std::set<NodePtr>> mRejectedNodes; + /** + * @brief the actual state of this Context (where it's in the FSM graph) + */ + std::shared_ptr<FsmNode> mActState; + /** + * @brief the actual node of this Context (where it's in the graph) + */ + NodePtr mActOpNode; + /** + * @brief the map of the node consider as common and the common ID + * @details we need to store what node it's consider as common because of the end + * resolution of the matching, all node consider as common need to be the same in all context + */ + std::map<NodePtr,std::size_t> mCommonNodes; + /** + * @brief the map of the node that as been valid in this context , and the test that valide the node + */ + std::map<std::shared_ptr<ConditionalInterpreter>,std::set<NodePtr>> mValidNodes; + /** + * @brief the index in the rejected node of this context + */ + std::size_t mLocalIdxRejeced; + public: + /** + * @brief constructor + * @param actState the actual state in the FSM + * @param actOpNode the actual node in the graph + * @param idxRejeced the idx in the global regected node vector init -1 as sentinel value of undefind + */ + FsmRunTimeContext(std::shared_ptr<FsmNode> actState ,NodePtr actOpNode ,std::size_t idxRejeced =std::numeric_limits<std::size_t>::max() ); + FsmRunTimeContext(std::shared_ptr<FsmRunTimeContext> fsmRunTime); + FsmRunTimeContext(std::shared_ptr<FsmRunTimeContext> fsmRunTime,std::shared_ptr<FsmNode> actState ,NodePtr actOpNode ); + + virtual ~FsmRunTimeContext()=default; + + /** + * @defgroup FsmRunTimeContextRejected Function for managing rejected nodes + */ + + /** + * @ingroup FsmRunTimeContextRejected + * @brief Add a node as rejected in this context + */ + void addRejectedNode(NodePtr node); + + /** + * @ingroup FsmRunTimeContextRejected + * @brief get the rejected nodes of this context + */ + std::set<NodePtr> getRejectedNodes(void); + + + /** + * @defgroup FsmRunTimeContextTest Function for test the context + */ + + /** + * @ingroup FsmRunTimeContextTest + * @brief test if the actual state is valide + * @return bool + */ + bool isOnValidState(void); + /** + * @ingroup FsmRunTimeContextTest + * @brief test if the node is considered as common in this context + * @param node node to test + * @return bool + */ + bool isCommonDefined(NodePtr node); + /** + * @ingroup FsmRunTimeContextTest + * @brief test if has already validated in this context + * @param node node to test + * @return bool + */ + bool isAlreadyValid(NodePtr node); + /** + * @ingroup FsmRunTimeContextTest + * @brief test if this context is compatible with an others + * @details to say that two contexts are compatible is to check : + * that the contexts do not validate the same nodes (other than the common ones) + * and that the common ones have the same idx + * @param fsmContext the others context + * @return bool + */ + bool areCompatible(std::shared_ptr<FsmRunTimeContext> fsmContext); + /** + * @ingroup FsmRunTimeContextTest + * @brief test if this context is strictly equal with an others + * @param fsmContext the others context + * @return bool + */ + bool areEqual(std::shared_ptr<FsmRunTimeContext> fsmContext); + + /** + * @defgroup FsmRunTimeContextSet Function set context + */ + + + void setCommon(NodePtr node,std::size_t commonIdx); + + + void setValid(NodePtr node,std::shared_ptr<ConditionalInterpreter> tag); + + /** + * @defgroup FsmRunTimeContextGet Function get context + */ + + + /** + * @ingroup FsmRunTimeContextGet + * @brief get the sub idx state + * @return bool + */ + std::size_t getSubStmId(void); + + NodePtr getCommonNodeFromIdx(std::size_t commonIdx); + std::size_t getCommonNodeIdx(NodePtr node); + std::set<NodePtr> getCommonNodes(void); + + std::map<NodePtr,std::size_t> getCommon(void); + std::set<NodePtr> getValidNodes(void); + + std::set<NodePtr> getValidNodesNoCommon(void); + std::map<std::shared_ptr<ConditionalInterpreter>,std::set<NodePtr>> getValid(void); + + + NodePtr getActNode(void); + std::shared_ptr<FsmNode> getActState(void); + + + /** + * @defgroup FsmRunTimeContextMem + */ + + void rst(void); + + + }; + +} + +#endif //__AIDGE_FSM_RUN_TIME_CONTEXT_H__ \ No newline at end of file diff --git a/include/aidge/graphRegex/matchFsm/MatchResult.hpp b/include/aidge/graphRegex/matchFsm/MatchResult.hpp new file mode 100644 index 000000000..8c96cc0e3 --- /dev/null +++ b/include/aidge/graphRegex/matchFsm/MatchResult.hpp @@ -0,0 +1,34 @@ +#ifndef __AIDGE_MATCH_RESULT_H__ +#define __AIDGE_MATCH_RESULT_H__ + +#include <memory> + +#include "aidge/graphRegex/matchFsm/FsmRunTimeContext.hpp" + + +namespace Aidge{ + +/** + * @brief class that old the result of a matching + * give acess to all node ant there tag in the expression +*/ +class MatchResult +{ +private: + /* data */ +public: + MatchResult(std::shared_ptr<FsmRunTimeContext> contexte); + virtual ~MatchResult() = default; + + /** + * @brief get the set of the node match for une expression + * @return the set of node of the graph that corresponding to an expression + */ + std::set<NodePtr> getNodes(void); +}; + + +} + + +#endif //__AIDGE_MATCH_RESULT_H__ \ No newline at end of file diff --git a/include/aidge/nodeTester/ConditionalData.hpp b/include/aidge/nodeTester/ConditionalData.hpp new file mode 100644 index 000000000..9d889d6a0 --- /dev/null +++ b/include/aidge/nodeTester/ConditionalData.hpp @@ -0,0 +1,98 @@ + +#ifndef _AIDGE_CONDITIONAL_DATA_H_ +#define _AIDGE_CONDITIONAL_DATA_H_ + +#include <vector> +#include <string> +#include <stdexcept> //error +#include <memory> +#include <map> +namespace Aidge{ + + + +///////////////////////// +// the data type in AST Intepretation +//////////////////////// + +class BaseConditionalValue { +public: + virtual ~BaseConditionalValue() {} +}; + +template <typename T> +class ConditionalValue : public BaseConditionalValue { +public: + ConditionalValue(const T& data) : value(data) {} + T value; +}; + + +struct ConditionalData { + /** + * @brief generic type to propagete all the different values in the AST interpretation + */ + //void* value; + std::unique_ptr<BaseConditionalValue> value; + const std::type_info* type; + + ///////////////////////////////// + // + //////////////////////////////// + /** + * @brief set a value + */ + template <typename T> + void setValue(const T& newValue) { + //make sure that the old value is free + deleteValue(); + value = std::make_unique<ConditionalValue<T>>(newValue); + type = &typeid(T); + } + + /** + * @brief get the actual value + * @details recaste the value to the templaited type and checks that the conversion type is compatible with type + * @tparam the type of the return value + * @return the value + */ + template <typename T> + T getValue() const { + if (type && *type == typeid(T)) { + //const Value<T>* typedValue = dynamic_cast<const Value<T>*>(static_cast<const BaseValue*>(value)); + const ConditionalValue<T>* typedValue = dynamic_cast<const ConditionalValue<T>*>(value.get()); + if (typedValue) { + return typedValue->value; + } + } + throw std::runtime_error(std::string("DATA ERROR ") + type->name() + " != " + typeid(T).name()); + } + /////////////////////////////////// + // + /////////////////////////////////// + std::string getType() const { + return type ? type->name() : "nullptr"; + } + + + template <typename T> + bool isTypeEqualTo() const { + return (type && *type == typeid(T)); + } + + void deleteValue() { + if (type) { + value.reset(); + type = nullptr; + } + } + + ~ConditionalData() { // TODO best can we have a liste of type supported ? + deleteValue(); + } +}; + +} + + +#endif //_AIDGE_CONDITIONAL_DATA_H_ \ No newline at end of file diff --git a/include/aidge/nodeTester/ConditionalInterpreter.hpp b/include/aidge/nodeTester/ConditionalInterpreter.hpp new file mode 100644 index 000000000..e5f971a81 --- /dev/null +++ b/include/aidge/nodeTester/ConditionalInterpreter.hpp @@ -0,0 +1,326 @@ + + +#ifndef _AIDGE_CONDITIONAL_INTERPRETER_H_ +#define _AIDGE_CONDITIONAL_INTERPRETER_H_ + +#include "aidge/nodeTester/ConditionalParser.hpp" +#include "aidge/nodeTester/ConditionalData.hpp" + +#include <memory> // for shared_ptr +#include <unordered_map> +#include <functional> +#include "aidge/graph/Node.hpp" +#include <sstream> + + +namespace Aidge{ + + + +////////////////////////////// +// +///////////////////////////// +/** + * @brief class used to register any lambda function without context, + * it encapsulates the source lambda in a lambda which takes as argument ConditionalData* which are any type. + * @see ConditionalData + */ +class ConditionalRegisterFunction { + ////////////////////////// + //Safe recaste + ////////////////////////// + + /** + * @brief recaste the ConditionalData* to the argument type of the lambda + * @tparam T type of the lambda argument + * @see ConditionalData + */ + template <typename T> + T safeCastInput(ConditionalData* data) { + //cnvertion and type cheking + if (data->isTypeEqualTo<T>()){ + return data->getValue<T>(); + }else{ + throw std::invalid_argument( "incompatible input type " + data->getType() +" "+ typeid(T).name() ); + } + + } + + + /** + * @brief recaste the output of the lambda to a ConditionalData* + * @tparam T type of the lambda return + * @see ConditionalData + */ + template <typename T> + ConditionalData* safeCastOutput(T data) { + + ConditionalData* out = new ConditionalData; + out->setValue<T>(data); + + return out; + } + + + + + ////////////////////// + // get all the type of the function + ////////////////////// + + /** + * @brief Retrieves information about a function's return type and argument types. + * @tparam T The function type. + */ + template <typename T> + struct function_traits; + + + /** + * @brief Specialization of function_traits for function pointers. + * @tparam R The return type of the function. + * @tparam Args The argument types of the function. + */ + template <typename R, typename... Args> + struct function_traits<R (*)(Args...)> { + using return_type = R; + static constexpr std::size_t arity = sizeof...(Args); + + template <std::size_t N> + struct argument { + static_assert(N < arity, "Index out of range."); + using type = typename std::tuple_element<N, std::tuple<Args...>>::type; + }; + }; + + /** + * @brief Specialization of function_traits for std::function types. + * @tparam R The return type of the function. + * @tparam Args The argument types of the function. + */ + template <typename R, typename... Args> + struct function_traits<std::function<R(Args...)>> { + using return_type = R; + static constexpr std::size_t arity = sizeof...(Args); + + template <std::size_t N> + struct argument { + static_assert(N < arity, "Index out of range."); + using type = typename std::tuple_element<N, std::tuple<Args...>>::type; + }; + }; + + ///////////////////// + //change the function to ConditionalData*(std::vector<ConditionalData*>) + ///////////////////// + + /** + * @brief Converts a function to a ConditionalData*(std::vector<ConditionalData*>). + * @tparam F The type of the function to convert. + * @tparam ParamsIdx The indices of the function parameters. + * @param f The function to convert. + * @return The pointer to the converted function. + */ + template <class F, std::size_t... ParamsIdx> + auto funcPointer(F f, std::index_sequence<ParamsIdx...>) { + //wrapp the lambda in a new one that as ConditionalData as inputs and output + return [this,f](std::vector<ConditionalData*> &args) { + if (args.size() != sizeof...(ParamsIdx)){ + std::ostringstream errorMessage; + errorMessage << "bad Number of argument: get " << args.size() << " need " << sizeof...(ParamsIdx) << "\n"; + throw std::runtime_error(errorMessage.str()); + } + //assert(args.size() == sizeof...(ParamsIdx));//the size of the vector valide + + using FuncTraits = function_traits<decltype(f)>; + using outType = typename FuncTraits::return_type; + + outType result = f(safeCastInput<typename FuncTraits::template argument<ParamsIdx>::type>(args[ParamsIdx])...); + //typename + return safeCastOutput<outType>(result); + }; + } + + /** + * @brief Converts a function pointer to a ConditionalData*(std::vector<ConditionalData*>). + * @tparam R The return type of the function. + * @tparam Params The parameter types of the function. + * @param f The function pointer to convert. + * @return The pointer to the converted function. + */ + template <class R,class... Params> + auto funcPointer(R (*f)(Params...)) { + return funcPointer(f, std::index_sequence_for<Params...>{}); + } + + /** + * @brief Converts a std::function to a ConditionalData*(std::vector<ConditionalData*>). + * @tparam R The return type of the function. + * @tparam Params The parameter types of the function. + * @param f The function pointer to convert. + * @return The pointer to the converted function. + */ + template <class R,class... Params> + auto funcPointer(std::function<R(Params...)> f) { + return funcPointer(f, std::index_sequence_for<Params...>{}); + } + + + /////////////////// + // interface + /////////////////// + + public: + + /** + * @brief Default constructor + */ + ConditionalRegisterFunction(){} + + + /** + * @brief Inserts a function into the map with the provided key. + * @tparam T The function type. + * @param key The key to associate with the function. + * @param f The function to insert. + */ + template <class T> + void insert(const std::string key,T f){ + mWlambda.insert({ key, funcPointer(f)}); + } + + + /** + * @brief Runs the function associated with the given key, using the provided vector of input data. + * @param key The key of the function to run. + * @param datas The vector of input data. + * @return A pointer to the output ConditionalData object. + */ + ConditionalData* run(const std::string key,std::vector<ConditionalData*> & datas); + + private: + /// @brief map of name and the converted function. + std::map<const std::string, std::function<ConditionalData*(std::vector<ConditionalData*> &)>> mWlambda; +}; + +/////////////////// +//AST tree node +// //////////////// +/** + * @brief this class interprets AST to generate a test on a graph node. For each AST node, + * it generates an interpretation and registers lambda functions that can be used in the test expression. + * there are two lambda control mechanisms: + * - A cpp mechanism which allows any lambda to be inserted into the constructor that use templaite + * - A user mechanism limited to lambda bool(NodePtr) + * @see ConditionalParser use to get the AST + */ +class ConditionalInterpreter +{ + private: + + /** + * @brief the AST generate by the Parser + * @see ConditionalParser + */ + std::shared_ptr<AstNode<ConditionalTokenTypes>> mTree; + /** + * @brief the registery for the lambda fuction + * @see ConditionalRegisterFunction + */ + ConditionalRegisterFunction mLambdaRegiter; + + public: + /** + * @brief Constructor + * @param ConditionalExpressions The expression of the test to be performed on the nodes + */ + + ConditionalInterpreter(const std::string ConditionalExpressions); + + /** + * @brief Test a node depending of the ConditionalExpressions + * @details the AST is visit using \ref visit() whith the $ init whit the nodeOp + * @return bool the match node has the initialized expresion + * @see visit() This function uses the visit() function to perform the evaluation. + */ + bool test( const NodePtr nodeOp); + + /** + * @brief Interface for inserting custom lambda bool(NodePtr) functions in AST interpretation, + * it will be available in the ConditionalExpressions expretion as : key($) + * @param key The key that will be used to call the function in the expression + * @param f The pointer to function + */ + void insertLambda(const std::string key,std::function<bool(Aidge::NodePtr)> f); + + + ///// + + private: + /** + * @brief Recursive AST traversal function, using the for interpreting AST nodes function, + * using \ref ASTnodeInterpreterF fuctions + * @param NodeOp The node currently being tested + * @param nodes The AST given by the parsing process + */ + std::vector<ConditionalData*> visit(const ASTNodeCh& nodes, const NodePtr NodeOp ); + + /** + * @defgroup ASTnodeInterpreterF Functions for interpreting AST nodes + * @brief For each node type in the AST, function defines the processing to be performed + * they return a std::vector<ConditionalData*> which corresponds to the value(s) obtained + */ + + /** + * @ingroup ASTnodeInterpreterF + * @brief Function that does something. + */ + std::vector<ConditionalData*> fLambda(const std::shared_ptr<AstNode<ConditionalTokenTypes>>& node,std::vector<ConditionalData*> datas); + /** + * @ingroup ASTnodeInterpreterF + * @brief Converted the lexeme to a int and to ConditionalData* + */ + std::vector<ConditionalData*> fStrToInteger(const std::shared_ptr<AstNode<ConditionalTokenTypes>>& node); + /** + * @ingroup ASTnodeInterpreterF + * @brief Converted the lexeme to a float and to ConditionalData* + */ + std::vector<ConditionalData*> fStrToFloat(const std::shared_ptr<AstNode<ConditionalTokenTypes>>& node); + /** + * @ingroup ASTnodeInterpreterF + * @brief Converted the lexeme to a str and to ConditionalData* + */ + std::vector<ConditionalData*> fStrToStr(const std::shared_ptr<AstNode<ConditionalTokenTypes>>& node); + + /** + * @ingroup ASTnodeInterpreterF + * @brief makes the == operation between two previously converted ConditionalData* + */ + std::vector<ConditionalData*> fEq(std::vector<ConditionalData*> datas); + /** + * @ingroup ASTnodeInterpreterF + * @brief makes the != operation between two previously converted ConditionalData* + */ + std::vector<ConditionalData*> fNeq(std::vector<ConditionalData*> datas); + /** + * @ingroup ASTnodeInterpreterF + * @brief makes the && operation between two previously converted ConditionalData* in bool + */ + std::vector<ConditionalData*> fAnd(std::vector<ConditionalData*> datas); + /** + * @ingroup ASTnodeInterpreterF + * @brief makes the || operation between two previously converted ConditionalData* in bool + */ + std::vector<ConditionalData*> fOr(std::vector<ConditionalData*> datas); + + /** + * @ingroup ASTnodeInterpreterF + * @brief makes the ! operation + */ + std::vector<ConditionalData*> fNot(std::vector<ConditionalData*> datas); +}; + + +} + +#endif //_AIDGE_CONDITIONAL_INTERPRETER_H_ diff --git a/include/aidge/nodeTester/ConditionalLexer.hpp b/include/aidge/nodeTester/ConditionalLexer.hpp new file mode 100644 index 000000000..770f5a11b --- /dev/null +++ b/include/aidge/nodeTester/ConditionalLexer.hpp @@ -0,0 +1,86 @@ +/** + * @file + * @brief + * @version file 1.0.0 + * @author vl241552 + * @copyright + * Copyright (c) 2023 CEA, LIST, Embedded Artificial Intelligence Laboratory. All + * rights reserved. + */ + + + +#ifndef _AIDGE_CONDITIONAL_LEXER_H_ +#define _AIDGE_CONDITIONAL_LEXER_H_ + +#include <string> +#include <regex> +#include <memory> // for shared_ptr + + +#include <stdexcept> //error +#include <sstream> + +#include "aidge/nodeTester/ConditionalTypes.hpp" +#include "aidge/utilsParsing/ParsingToken.hpp" +namespace Aidge{ + + + +class ConditionalLexer +{ + +public: +ConditionalLexer( const std::string ConditionalExpressions ); + +/** + * @brief Get the next token on the ConditionalExpressions + * @return ParsingToken<ConditionalTokenTypes> + */ +std::shared_ptr<ParsingToken<ConditionalTokenTypes>> getNextToken(void); +/** + * @brief Restart at the start of the ConditionalExpressions + * + */ +void rstPosition(void); + +/** + * @brief Test if the string is completely read + * @return bool + */ +bool isEnd(void); + + +/** + * @brief Get the representation of the class + * @return string + */ +const std::string rep(){ + return mConditionalExpressions; +} + +private: + +/** + * @brief Constructs an error message to display the character not understood by the lexer + * @return error mesage + */ +std::runtime_error badTokenError(const std::string& currentChars,std::size_t position); + +/** + * @brief The expression of the test to be performed on the nodes + */ +const std::string mConditionalExpressions; +/** + * @brief The lexer's current position in mConditionalExpressions + */ +std::size_t mPosition; + +}; + +///////////////////////////////////// + + +} + +#endif //_AIDGE_CONDITIONAL_LEXER_H_ \ No newline at end of file diff --git a/include/aidge/nodeTester/ConditionalParser.hpp b/include/aidge/nodeTester/ConditionalParser.hpp new file mode 100644 index 000000000..a1d1ccad2 --- /dev/null +++ b/include/aidge/nodeTester/ConditionalParser.hpp @@ -0,0 +1,106 @@ + + + +#ifndef _AIDGE_CONDITIONAL_PARSER_H_ +#define _AIDGE_CONDITIONAL_PARSER_H_ + + +#include <memory> // for shared_ptr + +#include "aidge/nodeTester/ConditionalLexer.hpp" +#include "aidge/nodeTester/ConditionalTypes.hpp" +#include "aidge/utilsParsing/ParsingToken.hpp" +#include "aidge/utilsParsing/AstNode.hpp" + +namespace Aidge{ + +const std::map<ConditionalTokenTypes, std::size_t> ConditionalPrec{ + {ConditionalTokenTypes::AND,2}, + {ConditionalTokenTypes::OR,1} +}; + + + + +using ASTNodeCh = std::vector<std::shared_ptr<AstNode<ConditionalTokenTypes>>>; + +/** + * @brief this class uses the lexer to create an AST according to a set of gramer rules + */ +class ConditionalParser{ + + public: + /** + * @brief AST graph creation function + * @param ConditionalExpressions String representing the logical fuction to be performed + */ + ConditionalParser(const std::string ConditionalExpressions); + + /** + * @brief AST graph creation function + * @return The AST tree + */ + std::shared_ptr<AstNode<ConditionalTokenTypes>> parse(void); + + + private: + /** + * @brief restart at the start of the ConditionalExpressions for LEXER and restart mCurrentToken + */ + void rstParser(void); + + ////////////////// + + /** + * @defgroup ParsingFunctions Function for creating AST + * @brief Functions for recursive construction of the AST representing grammar rules + */ + + /** + * @ingroup ParsingFunctions + * @brief Token reading and verification function + * + */ + void ackToken(ConditionalTokenTypes tokenType); + + /** + * @ingroup ParsingFunctions + * @brief Function of grammar rules for values : (KEY|INTEGER|FOAT|STRING|LAMBDA lambda) + * @return AST node + */ + std::shared_ptr<AstNode<ConditionalTokenTypes>> constructAstVal(void); + /** + * @ingroup ParsingFunctions + * @brief Function of grammar rules for comparison : val (EQ|NEQ) val | LPAREN expr RPAREN + * @return AST node + */ + std::shared_ptr<AstNode<ConditionalTokenTypes>> constructAstCmpr(void); + /** + * @ingroup ParsingFunctions + * @brief Function of grammar rules for arguments of a lambda : LAMBDA val (ARGSEP val)* RPAREN + * @return AST node + */ + std::shared_ptr<AstNode<ConditionalTokenTypes>> constructAstLambda(void); + /** + * @ingroup ParsingFunctions + * @brief Function of grammar rules for a expresion : cmpr ((AND | OR) cmpr)* + * @return AST node + */ + std::shared_ptr<AstNode<ConditionalTokenTypes>> constructAstExpr(std::size_t precLimit = 0); + + + /** + * @brief The actual token in the parce + */ + std::shared_ptr<ParsingToken<ConditionalTokenTypes>> mCurrentToken; + /** + * @brief The lexem use + */ + ConditionalLexer mLexer; + +}; + + +} + +#endif //_AIDGE_CONDITIONAL_PARSER_H_ diff --git a/include/aidge/nodeTester/ConditionalTypes.hpp b/include/aidge/nodeTester/ConditionalTypes.hpp new file mode 100644 index 000000000..d88ea639a --- /dev/null +++ b/include/aidge/nodeTester/ConditionalTypes.hpp @@ -0,0 +1,34 @@ + + +#ifndef _AIDGE_CONDITIONAL_TYPES_H_ +#define _AIDGE_CONDITIONAL_TYPES_H_ +namespace Aidge{ + /** + * @brief enum for all types of token use in the parsing + * 7-5 type + * 4-0 id + */ + enum class ConditionalTokenTypes + { + NOT = (1 << 4)+3, /**< ! */ + AND = (1 << 4)+2, /**< && */ + OR = (1 << 4)+1, /**< || */ + + EQ = (1 << 5)+1, /**< == */ + NEQ = (1 << 5)+2, /**< != */ + + KEY = (1 << 6) +1 , /**< [A-Za-z][A-Za-z0-9_]* */ + INTEGER = (1 << 6) +2 , /**< [0-9]+ */ + FLOAT = (1 << 6) +3 , /**< [0-9]+\.[0-9]* */ + STRING = (1 << 6) +4 , /**< \'.*\' */ + BOOL = (1 << 6) +5 , /**< true|false */ + NODE = (1 << 6) +6 , /**< \$ */ + LAMBDA = (1 << 6) +7 , /**< [A-Za-z][A-Za-z0-9_]*\( */ + + ARGSEP = (1<<7) +1, /**< , */ + LPAREN = (1<<7) +2, /**< \( */ + RPAREN = (1<<7) +3, /**< \) */ + STOP = 0, + }; +} +#endif \ No newline at end of file diff --git a/include/aidge/utilsParsing/AstNode.hpp b/include/aidge/utilsParsing/AstNode.hpp index 1158ae148..9eaf30ee7 100644 --- a/include/aidge/utilsParsing/AstNode.hpp +++ b/include/aidge/utilsParsing/AstNode.hpp @@ -12,11 +12,11 @@ namespace Aidge{ template <typename EnumType> - class AstNode: public std::enable_shared_from_this<AstNode> + class AstNode: public std::enable_shared_from_this<AstNode<EnumType>> { static_assert(std::is_enum<EnumType>::value, "AstNode EnumType must be an enum type"); public: - AstNode(std::shared_ptr<ParsingToken<EnumType>> token,std::vector<std::shared_ptr<AstNode>> child ={}):mToken(token),mChild(child){} + AstNode(std::shared_ptr<ParsingToken<EnumType>> token,std::vector<std::shared_ptr<AstNode<EnumType>>> child ={}):mToken(token),mChild(child){} /** * @brief get the type of the token * @return the type diff --git a/include/aidge/utilsParsing/ParsingToken.hpp b/include/aidge/utilsParsing/ParsingToken.hpp index 78045cf30..265b5a8b0 100644 --- a/include/aidge/utilsParsing/ParsingToken.hpp +++ b/include/aidge/utilsParsing/ParsingToken.hpp @@ -4,10 +4,12 @@ #include <string> #include <type_traits> +#include <sstream> // Include the necessary header namespace Aidge{ + template <typename EnumType> - class ParsingToken: public std::enable_shared_from_this<ParsingToken> + class ParsingToken: public std::enable_shared_from_this<ParsingToken<EnumType>> { static_assert(std::is_enum<EnumType>::value, "ParsingToken EnumType must be an enum type"); public: @@ -16,7 +18,7 @@ namespace Aidge{ * @param type one of the token type * @param lexeme String representing aditional information of the token */ - ParsingToken(const EnumType type , const std::string lexeme )mLexeme(lexeme),mType(type){} + ParsingToken(const EnumType type , const std::string lexeme ):mLexeme(lexeme),mType(type){} /** * @brief get the lexeme @@ -39,7 +41,10 @@ namespace Aidge{ * @brief copy the token * @return deep copy of the token */ - std::shared_ptr<Aidge::ParsingToken> copy(); + std::shared_ptr<ParsingToken> copy(){ + auto newToken = std::make_shared<ParsingToken<EnumType>>(mType,mLexeme); + return newToken; + } //TODO std::ostringstream rep(void){ @@ -47,6 +52,7 @@ namespace Aidge{ out << " Token (" << mLexeme <<")" << "\n"; return out; } + private: /** diff --git a/src/graphRegex/GraphFsmInterpreter.cpp b/src/graphRegex/GraphFsmInterpreter.cpp new file mode 100644 index 000000000..6d04fc97c --- /dev/null +++ b/src/graphRegex/GraphFsmInterpreter.cpp @@ -0,0 +1,182 @@ +#include "aidge/graphRegex/GraphFsmInterpreter.hpp" + +using namespace Aidge; + + +GraphFsmInterpreter::GraphFsmInterpreter(const std::string graphMatchExpr,std::map<std::string,std::shared_ptr<ConditionalInterpreter>> nodesCondition):mParser(graphMatchExpr){ + mActGroupe = 0; + mNodesCondition = nodesCondition; +} +std::shared_ptr<FsmGraph> GraphFsmInterpreter::interpret(void){ + mActGroupe = 0; + std::shared_ptr<AstNode<gRegexTokenTypes>> tree = mParser.parse(); + return visit(tree); +} +std::shared_ptr<FsmGraph> GraphFsmInterpreter::visit(std::shared_ptr<AstNode<gRegexTokenTypes>> AstTree){ + + std::vector<std::shared_ptr<AstNode<gRegexTokenTypes>>> nextAstNodes = AstTree->getChilds(); + + if(AstTree->getType() == gRegexTokenTypes::SEP){ + return sepF(visit(nextAstNodes[0]),visit(nextAstNodes[1])); + }else if(AstTree->getType() == gRegexTokenTypes::NEXT){ + return nextF(visit(nextAstNodes[0]),visit(nextAstNodes[1])); + }else if(AstTree->getType() == gRegexTokenTypes::QOM){ + return qomF(visit(nextAstNodes[0])); + }else if(AstTree->getType() == gRegexTokenTypes::QZM){ + return qzmF(visit(nextAstNodes[0])); + }else if(AstTree->getType() == gRegexTokenTypes::KEY || AstTree->getType() == gRegexTokenTypes::CKEY){ + return keyF(AstTree); + }else if(AstTree->getType() == gRegexTokenTypes::LPAREN){ + mActGroupe += 1; + std::shared_ptr<FsmGraph> out = visit(nextAstNodes[0]); + mActGroupe -= 1; + return out; + }else{ + throw std::logic_error("visit Bad token type" ); + } +} + + + + +std::shared_ptr<FsmGraph> GraphFsmInterpreter::keyF(std::shared_ptr<AstNode<gRegexTokenTypes>> AstNode){ + + + std::shared_ptr<FsmNode> start = std::make_shared<FsmNode>(false,true); + std::shared_ptr<FsmNode> valid = std::make_shared<FsmNode>(true,false); + std::shared_ptr<FsmGraph> graph = std::make_shared<FsmGraph>(); + std::shared_ptr<FsmEdge> edge; + + + if(AstNode->getType() == gRegexTokenTypes::CKEY){ + edge = FsmEdgeFactory::make(start,valid,FsmEdgeTypes::COMMON,mNodesCondition,AstNode->getValue()); + }else if (AstNode->getType() == gRegexTokenTypes::KEY) + { + edge = FsmEdgeFactory::make(start,valid,FsmEdgeTypes::UNIQUE,mNodesCondition,AstNode->getValue()); + }else{ + + throw std::logic_error("keyF Bad in AST" ); + } + + graph->addEdge(edge); + graph->setGroupe(mActGroupe); + return graph; +} + + +std::shared_ptr<FsmGraph> GraphFsmInterpreter::sepF(std::shared_ptr<FsmGraph> leftFsm,std::shared_ptr<FsmGraph> rigthFsm){ + + size_t idxLeft = leftFsm->getNbSubFsm(); + rigthFsm->incOrigineAllNodeBy(idxLeft); + leftFsm->unionG(rigthFsm); + //the rigthFsm is no longer usfull + return leftFsm; +} + + +std::shared_ptr<FsmGraph> GraphFsmInterpreter::nextF(std::shared_ptr<FsmGraph> leftFsm,std::shared_ptr<FsmGraph> rigthFsm){ + /* + combine the 2 Graph + all valid node of A are merge with Start B, Start B is un Start + update the relative reference + + A B + SA -> VA + SB -> VB + A B + SA -> q -> VB + */ + leftFsm->mergeOneStartOneValid(rigthFsm); + //the rigthFsm is no longer usfull + return leftFsm; +} +std::shared_ptr<FsmGraph> GraphFsmInterpreter::qomF(std::shared_ptr<FsmGraph> fsm){ + /* + + + valid node is connect to the child of Start with the same edge condition + A + S -> V + + A + S -> V + (E|R) + V -> S + */ + + std::vector<std::shared_ptr<FsmNode>> allStart = fsm->getStartNodes(); + std::set<std::shared_ptr<FsmNode>> allValid = fsm->getValidNodes(); + std::shared_ptr<FsmEdge> edge; + + if(allStart.size() != 1){ + throw std::logic_error("qomF Bad in AST" ); + } + + for(auto start : allStart ){ + for(auto edgeStart :start->getEdges() ){ + if (auto sharedEdge = edgeStart.lock()) { + + const std::map<size_t, int> commonRef = sharedEdge->getRelative(); + bool haveCommon = !commonRef.empty(); + + for(auto valid : allValid){ + if(haveCommon){ + /* + the // quantif case + get the go back and make a lexeme id(number) + we need to go back to the ref delta min #TODO + */ + bool hasMinRef = false; + std::pair<size_t, int> minRef; + for (const auto& entry : commonRef) { + if (!hasMinRef || std::abs(minRef.second) > std::abs(entry.second)) { + hasMinRef = true; + minRef = entry; + } + } + std::stringstream lexem; + lexem << "(" << minRef.first << ", " << minRef.second << ")"; + edge = FsmEdgeFactory::make(valid,start,FsmEdgeTypes::REF,mNodesCondition, lexem.str()); + }else{ + /* + the sequensial quantif case + no reference to common + */ + edge = FsmEdgeFactory::make(valid,start,FsmEdgeTypes::EMPTY,mNodesCondition,""); + + } + fsm->addEdge(edge); + } + }else{ + throw std::runtime_error("edgeStart weak pointer is expired" ); + } + } + + } + return fsm; + +} +std::shared_ptr<FsmGraph> GraphFsmInterpreter::qzmF(std::shared_ptr<FsmGraph> fsm){ + /* + qomf and a bypass empty start to valide + */ + fsm = qomF(fsm); + + std::vector<std::shared_ptr<FsmNode>> allStart = fsm->getStartNodes(); + std::set<std::shared_ptr<FsmNode>> allValid = fsm->getValidNodes(); + std::shared_ptr<FsmEdge> edge; + + if(allStart.size() != 1){ + throw std::logic_error("qzmF Bad in AST" ); + } + + for(auto start : allStart ){ + + for(auto valid : allValid){ + edge = FsmEdgeFactory::make(start,valid,FsmEdgeTypes::EMPTY,mNodesCondition,""); + fsm->addEdge(edge); + } + } + + return fsm; + + +} \ No newline at end of file diff --git a/src/graphRegex/GraphLexer.cpp b/src/graphRegex/GraphLexer.cpp new file mode 100644 index 000000000..437c45f50 --- /dev/null +++ b/src/graphRegex/GraphLexer.cpp @@ -0,0 +1,157 @@ + +#include "aidge/graphRegex/GraphLexer.hpp" + +using namespace Aidge; + + +GraphLexer::GraphLexer( const std::string gRegexExpressions ): +mRegularExpressions(gRegexExpressions){ + mPosition = 0; +} + +std::shared_ptr<ParsingToken<gRegexTokenTypes>> GraphLexer::getNextToken(void){ + std::string currentChars = ""; + while (mPosition < mRegularExpressions.length()) + { + //erase all space + if (mRegularExpressions[mPosition] != ' ') + { + currentChars += mRegularExpressions[mPosition]; + } + else + { + mPosition++; + continue; + } + + ///// + // const lent token + ///// + + if (std::regex_match(currentChars,std::regex("\\->")))// the next TOKEN + { + mPosition++; + return std::make_shared<ParsingToken<gRegexTokenTypes>>(gRegexTokenTypes::NEXT,""); + } + else if (std::regex_match(currentChars,std::regex("\\*")))// the * TOKEN + { + mPosition++; + return std::make_shared<ParsingToken<gRegexTokenTypes>>(gRegexTokenTypes::QZM,""); + } + else if (std::regex_match(currentChars,std::regex("\\+")))// the + TOKEN + { + mPosition++; + return std::make_shared<ParsingToken<gRegexTokenTypes>>(gRegexTokenTypes::QOM,""); + } + else if (std::regex_match(currentChars,std::regex("\\(")))// the LPAREN TOKEN + { + mPosition++; + return std::make_shared<ParsingToken<gRegexTokenTypes>>(gRegexTokenTypes::LPAREN,""); + } + else if (std::regex_match(currentChars,std::regex("\\)")))// the RPAREN TOKEN + { + mPosition++; + return std::make_shared<ParsingToken<gRegexTokenTypes>>(gRegexTokenTypes::RPAREN,""); + } + + // + else if (std::regex_match(currentChars,std::regex(";")))// the SEP TOKEN + { + //test if the last sep + //std::string subStr = mRegularExpressions.substr(mPosition); + mPosition++; + return std::make_shared<ParsingToken<gRegexTokenTypes>>(gRegexTokenTypes::SEP,""); + } + + ///// + //unconst lent token + ///// + + else if (std::regex_match(currentChars,std::regex("[A-Za-z_0-9]")))// the KEY or CKEY + { + + //read all the key + bool isCKey = false; + std::regex keyRegex("[A-Za-z_0-9]+"); + std::regex cKeyRegex("[A-Za-z_0-9]+\\#[0-9]*"); + + while ( mPosition < mRegularExpressions.length()) { + + if(!std::regex_match(currentChars,keyRegex) && !std::regex_match(currentChars,cKeyRegex)) + { + currentChars.pop_back(); //the last char is the problemes + break; + } + else if (std::regex_match(currentChars,cKeyRegex)){ + isCKey = true; + } + mPosition++; + if (mPosition < mRegularExpressions.length()) currentChars += mRegularExpressions[mPosition]; + + + } + //we end the match 2 posibility + //we are at the end of the mConditionalExpressions and we need to ensure the match + //we are not we can continu + if (mPosition == mRegularExpressions.length()-1) + { + if (!std::regex_match(currentChars,keyRegex) && !std::regex_match(currentChars,cKeyRegex)) + { + throw badTokenError(currentChars,mPosition); + } + //mPosition++; // we stop all by going pos > lengt + } + + + if (isCKey){ + return std::make_shared<ParsingToken<gRegexTokenTypes>>(gRegexTokenTypes::CKEY,currentChars); + } else{ + return std::make_shared<ParsingToken<gRegexTokenTypes>>(gRegexTokenTypes::KEY,currentChars); + } + } + + mPosition++; + } + + + //no more to find no one match the currentChars + if (currentChars.empty()) { + return std::make_shared<ParsingToken<gRegexTokenTypes>>(gRegexTokenTypes::STOP,""); // Null shared pointer ; + }else{ + throw badTokenError(currentChars,mPosition); + } + +} + +void GraphLexer::rstPosition(void){ + if (isEnd()){ + mPosition = 0; + }else{ + throw badTokenError("end rst",mPosition); + } +} + +bool GraphLexer::isEnd(void){ + return mPosition >= mRegularExpressions.length(); +} + +std::runtime_error GraphLexer::badTokenError(const std::string& currentChars,std::size_t position){ + std::ostringstream errorMessage; + errorMessage << "\nBad syntax " << currentChars << " :\n" << mRegularExpressions << "\n"; + for (std::size_t i = 0; i < position; i++) { + errorMessage << ' '; + } + errorMessage << "^\n"; + + return std::runtime_error(errorMessage.str()); +} + + const std::string GraphLexer::rep(){ + std::string out = mRegularExpressions; + out += "\n"; + for (std::size_t i = 0; i < mPosition; i++) { + out += ' '; + } + out += "^\n"; + return out ; + } \ No newline at end of file diff --git a/src/graphRegex/GraphParser.cpp b/src/graphRegex/GraphParser.cpp new file mode 100644 index 000000000..be7b69040 --- /dev/null +++ b/src/graphRegex/GraphParser.cpp @@ -0,0 +1,174 @@ +#include "aidge/graphRegex/GraphParser.hpp" + +using namespace Aidge; + +GraphParser::GraphParser(const std::string gRegexExpressions): +mLexer(gRegexExpressions) +{ + mCurrentToken = mLexer.getNextToken(); +} + + +std::shared_ptr<AstNode<gRegexTokenTypes>> GraphParser::parse(void){ + + std::shared_ptr<AstNode<gRegexTokenTypes>> astTree = constructAstAllExpr(); + rstParser(); + return astTree; +} + + +void GraphParser::rstParser(void){ + mLexer.rstPosition(); + mCurrentToken = mLexer.getNextToken(); +} + + +void GraphParser::ackToken(gRegexTokenTypes tokenType){ + + if(mCurrentToken->getType() == tokenType ){ + try { + mCurrentToken = mLexer.getNextToken(); + } catch (const std::runtime_error& e) { + std::ostringstream errorMessage; + errorMessage << "Graph Lexer error in Parser :\n"<< e.what() << std::endl; + throw std::runtime_error(errorMessage.str()); + } + }else{ + std::ostringstream errorMessage; + errorMessage << "Bad syntax GraphParser " << static_cast<int>(mCurrentToken->getType()) <<"!="<< static_cast<int>(tokenType) << "\n"; + errorMessage << mLexer.rep(); + throw std::runtime_error(errorMessage.str()); + } +} + +/* +exp : KEY(QOM | QZM)? | CKEY | domain +*/ +std::shared_ptr<AstNode<gRegexTokenTypes>> GraphParser::constructAstExp(void) +{ + + try{ + std::shared_ptr<ParsingToken<gRegexTokenTypes>> token = mCurrentToken->copy(); + std::shared_ptr<AstNode<gRegexTokenTypes>> node = std::make_shared<AstNode<gRegexTokenTypes>>(token); + + if (mCurrentToken->getType() == gRegexTokenTypes::KEY ){ + ackToken(gRegexTokenTypes::KEY ); + if (mCurrentToken->getType() == gRegexTokenTypes::QOM ){ + ackToken(gRegexTokenTypes::QOM ); + std::shared_ptr<AstNode<gRegexTokenTypes>> newNode = std::make_shared<AstNode<gRegexTokenTypes>>(token, + std::vector<std::shared_ptr<AstNode<gRegexTokenTypes>>>{node}); + return newNode; + }else if (mCurrentToken->getType() == gRegexTokenTypes::QZM ){ + ackToken(gRegexTokenTypes::QZM ); + std::shared_ptr<AstNode<gRegexTokenTypes>> newNode = std::make_shared<AstNode<gRegexTokenTypes>>(token, + std::vector<std::shared_ptr<AstNode<gRegexTokenTypes>>>{node}); + return newNode; + } + return node; + }else if (mCurrentToken->getType() == gRegexTokenTypes::CKEY){ + ackToken(gRegexTokenTypes::CKEY ); + return node; + }else{ + return constructAstDomain(); + } + + } catch (const std::runtime_error& e) { + std::ostringstream errorMessage; + errorMessage << "GraphParser constructAstExp :\n"<< e.what() << std::endl; + throw std::runtime_error(errorMessage.str()); + } +} + +/* +seq :exp (NEXT seq)* +*/ +std::shared_ptr<AstNode<gRegexTokenTypes>> GraphParser::constructAstSeq(void) +{ + + try{ + + std::shared_ptr<AstNode<gRegexTokenTypes>> left = constructAstExp(); + if(mCurrentToken->getType() == gRegexTokenTypes::NEXT ) + { + std::shared_ptr<ParsingToken<gRegexTokenTypes>> token = mCurrentToken->copy(); + ackToken(gRegexTokenTypes::NEXT); + std::shared_ptr<AstNode<gRegexTokenTypes>> newNode = std::make_shared<AstNode<gRegexTokenTypes>>(token, + std::vector<std::shared_ptr<AstNode<gRegexTokenTypes>>>{left,constructAstSeq()}); + left = newNode; + } + return left; + + } catch (const std::runtime_error& e) { + std::ostringstream errorMessage; + errorMessage << "GraphParser constructAstSeq :\n"<< e.what() << std::endl; + throw std::runtime_error(errorMessage.str()); + } + +} + + +/* +LPAREN seq RPAREN (QOM | QZM) +*/ +std::shared_ptr<AstNode<gRegexTokenTypes>> GraphParser::constructAstDomain(void) +{ + + try{ + std::shared_ptr<ParsingToken<gRegexTokenTypes>> token ; + std::shared_ptr<AstNode<gRegexTokenTypes>> node ; + + token = mCurrentToken->copy(); + ackToken(gRegexTokenTypes::LPAREN); + node = std::make_shared<AstNode<gRegexTokenTypes>>(token, + std::vector<std::shared_ptr<AstNode<gRegexTokenTypes>>>{constructAstSeq()}); + ackToken(gRegexTokenTypes::RPAREN); + //(QOM | QZM) + + token = mCurrentToken->copy(); + if (mCurrentToken->getType() == gRegexTokenTypes::QOM){ + ackToken(gRegexTokenTypes::QOM); + node = std::make_shared<AstNode<gRegexTokenTypes>>(token, + std::vector<std::shared_ptr<AstNode<gRegexTokenTypes>>>{node}); + }else if (mCurrentToken->getType() == gRegexTokenTypes::QZM){ + ackToken(gRegexTokenTypes::QZM); + node = std::make_shared<AstNode<gRegexTokenTypes>>(token, + std::vector<std::shared_ptr<AstNode<gRegexTokenTypes>>>{node}); + }else{ + std::ostringstream errorMessage; + errorMessage << "Bad syntax constructAstDomain must have quantifier \n"; + throw std::runtime_error(errorMessage.str()); + } + + return node; + + } catch (const std::runtime_error& e) { + std::ostringstream errorMessage; + errorMessage << "GraphParser constructAstDomain :\n"<< e.what() << std::endl; + throw std::runtime_error(errorMessage.str()); + } +} + +/* + allExpr: seq (SEP allExpr)* +*/ +std::shared_ptr<AstNode<gRegexTokenTypes>> GraphParser::constructAstAllExpr(void) +{ + + try{ + std::shared_ptr<AstNode<gRegexTokenTypes>> left = constructAstSeq(); + if(mCurrentToken->getType() == gRegexTokenTypes::SEP ) + { + std::shared_ptr<ParsingToken<gRegexTokenTypes>> token = mCurrentToken->copy(); + ackToken(gRegexTokenTypes::SEP); + std::shared_ptr<AstNode<gRegexTokenTypes>> newNode = std::make_shared<AstNode<gRegexTokenTypes>>(token, + std::vector<std::shared_ptr<AstNode<gRegexTokenTypes>>>{left,constructAstAllExpr()}); + left = newNode; + } + return left; + + } catch (const std::runtime_error& e) { + std::ostringstream errorMessage; + errorMessage << "GraphParser constructAstDomain :\n"<< e.what() << std::endl; + throw std::runtime_error(errorMessage.str()); + } +} diff --git a/src/graphRegex/matchFsm/FsmEdge.cpp b/src/graphRegex/matchFsm/FsmEdge.cpp new file mode 100644 index 000000000..cb5f8c058 --- /dev/null +++ b/src/graphRegex/matchFsm/FsmEdge.cpp @@ -0,0 +1,269 @@ +#include "aidge/graphRegex/matchFsm/FsmEdge.hpp" +#include "aidge/graphRegex/matchFsm/FsmNode.hpp" +#include "aidge/graphRegex/matchFsm/FsmRunTimeContext.hpp" + +using namespace Aidge; + +std::map<std::string,int> FsmEdgeCommon::mCommonIdxMap; + +bool FsmEdge::isCommon(void){ + return false; +} + +size_t FsmEdge::getCommonIdx(void){ + return std::numeric_limits<std::size_t>::max(); +} +const std::map<size_t,int>& FsmEdge::getRelative(void){ + return mRelativePos; +} +void FsmEdge::updateRelative( const std::map<size_t,int>& relativePos ){ + for (const auto& kvp : relativePos) { + mRelativePos.insert(kvp); + } +} +std::shared_ptr<FsmNode> FsmEdge::getSourceNode(void){ + return mNodeSource; +} +void FsmEdge::reSetSouceNode(const std::shared_ptr<FsmNode>& newSource){ + mNodeSource->rmEdge(shared_from_this()); + mNodeSource = newSource; + mNodeSource->addEdge(shared_from_this()); + propagateRelativePos(); + +} +std::shared_ptr<FsmNode> FsmEdge::getDestNode(void){ + return mNodeDest; +} +void FsmEdge::reSetDestNode(const std::shared_ptr<FsmNode>& newDest){ + mNodeDest->rmParent(mNodeSource); + mNodeDest = newDest; + mNodeDest->addParent(mNodeSource); + propagateRelativePos(); +} +void FsmEdge::propagateRelativePos(void){ + + std::set<int> myRelativeID; + for (const auto& kvp : mRelativePos) { + myRelativeID.insert(kvp.first); + } + + for (const auto nextWeakEdge : mNodeDest->getEdges()){ + + if (auto nextEdge = nextWeakEdge.lock()) { + + if(this == nextEdge.get()){ + continue; + } + + + std::set<int> nextRelativeID; + for (const auto& kvp : nextEdge->getRelative()) { + nextRelativeID.insert(kvp.first); + } + + // Find elements in myRelativeID but not in nextRelativeID + std::set<int> idxsToPush; + std::set_difference(myRelativeID.begin(), myRelativeID.end(), + nextRelativeID.begin(), nextRelativeID.end(), + std::inserter(idxsToPush, idxsToPush.begin())); + + // Find elements in nextRelativeID but not in myRelativeID + std::set<int> idxsToGet; + std::set_difference(nextRelativeID.begin(), nextRelativeID.end(), + myRelativeID.begin(), myRelativeID.end(), + std::inserter(idxsToGet, idxsToGet.begin())); + + // test for integrity we look if 2 edge refert to the samme + // ref and are link the ref dif is one + // not working for common node + // we can go deeper by find the all pass to a ref and see if the delta is good + + // Find elements present in both myRelativeID and nextRelativeID + std::set<int> idxsTotest; + for (int idx : nextRelativeID){ + if (myRelativeID.find(idx) != myRelativeID.end()){ + if (std::abs(getRelative().at(idx) - nextEdge->getRelative().at(idx)) != 1) { + throw std::runtime_error("Bad relative"); + } + } + } + + + + // this egde have more relative info than the next + std::map<size_t,int> tmpRelative; + // we push this info to the next + for( auto idxToPush :idxsToPush ){ + tmpRelative.insert( std::make_pair(idxToPush, getRelative().at(idxToPush) +1)); + } + if(tmpRelative.size() != 0){ + nextEdge->updateRelative(tmpRelative); + nextEdge->propagateRelativePos(); + } + tmpRelative.clear(); + + + // the next node have more info than me i need to get it + for( auto idxToGet :idxsToGet ){ + tmpRelative.insert( std::make_pair(idxToGet, nextEdge->getRelative().at(idxToGet) -1)); + } + if(tmpRelative.size() != 0){ + updateRelative(tmpRelative); + + for(auto weakParent : getSourceNode()->getParentNodes()){ + if (auto parent = weakParent.lock()) { + for(auto weakPEdge : parent->getEdges()){ + if (auto pEdge = weakPEdge.lock()) { + pEdge->propagateRelativePos(); + }else{ + throw std::runtime_error("propagateRelativePos parent edge weak pointer is expired" ); + } + } + }else{ + throw std::runtime_error("propagateRelativePos parent weak pointer is expired" ); + } + } + } + tmpRelative.clear(); + }else{ + throw std::runtime_error("propagateRelativePos edge weak pointer is expired" ); + } + } +} + + + +FsmEdge::FsmEdge(std::shared_ptr<FsmNode> source,std::shared_ptr<FsmNode> dest, const std::shared_ptr<ConditionalInterpreter> toTest) +:mToTest(toTest) +{ + mNodeSource = source; + mNodeDest = dest; +} + +/////surchage + +FsmEdgeUnique::FsmEdgeUnique(std::shared_ptr<FsmNode> source,std::shared_ptr<FsmNode> dest, const std::shared_ptr<ConditionalInterpreter> toTest) +:FsmEdge(source,dest,toTest) +{ +} +const EdgeTestResult FsmEdgeUnique::test(const std::shared_ptr<FsmRunTimeContext> stmContext){ + if(stmContext == nullptr){ + return {false,std::set<NodePtr>()};//none + } + auto opNode = stmContext->getActNode(); + if(mToTest->test(opNode) && opNode->getChildren().size() <= 1){ + stmContext->setValid(opNode,mToTest); + return {true,opNode->getChildren()} ; + }else{ + stmContext->addRejectedNode(opNode); + return {false,std::set<NodePtr>()}; + } +} +///////////////////// +FsmEdgeCommon::FsmEdgeCommon(std::shared_ptr<FsmNode> source,std::shared_ptr<FsmNode> dest, const std::shared_ptr<ConditionalInterpreter> toTest, const std::string commonKey) +:FsmEdge(source,dest,toTest) +{ + //make a uid for common node + if(mCommonIdxMap.find(commonKey) == mCommonIdxMap.end()){ + mCommonIdxMap.insert(std::make_pair(commonKey, mCommonIdxMap.size())); + } + mCommonIdx = mCommonIdxMap[commonKey]; + propagateRelativePos(); +} + + +const EdgeTestResult FsmEdgeCommon::test(const std::shared_ptr<FsmRunTimeContext> stmContext){ + if(stmContext == nullptr){ + return {false,std::set<NodePtr>()};//none + } + auto opNode = stmContext->getActNode(); + + if(mToTest->test(opNode)){ + stmContext->setCommon(opNode,mCommonIdx); + stmContext->setValid(opNode,mToTest); + return {true,opNode->getChildren()} ; + }else{ + stmContext->addRejectedNode(opNode); + return {false,std::set<NodePtr>()}; + } +} +bool FsmEdgeCommon::isCommon(void){ + return true; + } +//////////////////// TODO FsmEdgeEmpty must be size_t +FsmEdgeRef::FsmEdgeRef(std::shared_ptr<FsmNode> source,std::shared_ptr<FsmNode> dest, const size_t refCommonIdx,const int deltaCommonIdx) +:FsmEdge(source,dest,nullptr),mRefCommonIdx(refCommonIdx),mdeltaCommonIdx(deltaCommonIdx) +{ + +} +const EdgeTestResult FsmEdgeRef::test(const std::shared_ptr<FsmRunTimeContext> stmContext){ + + NodePtr refNode = stmContext->getCommonNodeFromIdx(mRefCommonIdx); + if (refNode){ + + //return {true,refNode->getNodeDelta(mdeltaCommonIdx)}; TODO + } + return {false,std::set<NodePtr>()}; + + +} +//////////////////// +FsmEdgeEmpty::FsmEdgeEmpty(std::shared_ptr<FsmNode> source,std::shared_ptr<FsmNode> dest) +:FsmEdge(source,dest,nullptr) +{} +const EdgeTestResult FsmEdgeEmpty::test(const std::shared_ptr<FsmRunTimeContext> stmContext){ + if(stmContext == nullptr){ + return {false,std::set<NodePtr>()}; + } + auto opNode = stmContext->getActNode(); + return {true,std::set<NodePtr>({opNode})};//none +} + +/// factory +std::shared_ptr<FsmEdge> FsmEdgeFactory::make( +std::shared_ptr<FsmNode> source, +std::shared_ptr<FsmNode> dest, FsmEdgeTypes type, +std::map<std::string, std::shared_ptr<ConditionalInterpreter>> allTest, +const std::string lexeme) +{ + if (type == FsmEdgeTypes::EMPTY) { + if (lexeme.empty()) { + return std::make_shared<FsmEdgeEmpty>(source, dest); + } else { + throw std::invalid_argument("error lexem EMPTY"); + } + } else if (type == FsmEdgeTypes::REF) { + std::smatch m; + std::regex refRegex("\\s*\\(\\s*(\\d+)\\s*,\\s*(-?\\d+)\\s*\\)\\s*"); + if (std::regex_match(lexeme, m, refRegex)) { + int refCommonIdx = std::stoi(m[1]); + int deltaCommonIdx = std::stoi(m[2]); + return std::make_shared<FsmEdgeRef>(source, dest, refCommonIdx, deltaCommonIdx); + } else { + throw std::invalid_argument("error lexem REF " + lexeme); + } + } else if (type == FsmEdgeTypes::COMMON) { + std::smatch m; + std::regex commonRegex("\\s*(\\w+)#(\\d*)"); + if (std::regex_match(lexeme, m, commonRegex)) { + std::string edgeType = m[1]; + std::string commonId = m[2]; + size_t commonIdx = commonId.empty() ? 0 : std::stoi(commonId) + 1; + std::string commonKey = edgeType + std::to_string(commonIdx); + return std::make_shared<FsmEdgeCommon> (source, dest, allTest.at(edgeType), commonKey); + } else { + throw std::invalid_argument("error lexem COMMON " + lexeme); + } + } else if (type == FsmEdgeTypes::UNIQUE) { + std::regex uniqueRegex("\\s*(\\w+)"); + std::smatch m; + if (std::regex_match(lexeme, m, uniqueRegex)) { + std::string edgeType = m[1]; + return std::make_shared<FsmEdgeUnique>(source, dest, allTest.at(edgeType)); + } else { + throw std::invalid_argument("error lexem UNIQUE \"" + std::string(lexeme) +" eee\""); + } + } else { + throw std::invalid_argument("Bad edge Type"); + } + } \ No newline at end of file diff --git a/src/graphRegex/matchFsm/FsmGraph.cpp b/src/graphRegex/matchFsm/FsmGraph.cpp new file mode 100644 index 000000000..2ec9fe75e --- /dev/null +++ b/src/graphRegex/matchFsm/FsmGraph.cpp @@ -0,0 +1,170 @@ +#include "aidge/graphRegex/matchFsm/FsmGraph.hpp" + +using namespace Aidge; + + + +FsmGraph::FsmGraph(/* args */){ + +} + +//TODO +std::vector<std::shared_ptr<MatchResult>> FsmGraph::test(std::vector<NodePtr>& startNodes){ + std::vector<std::shared_ptr<Aidge::FsmNode>> startNodesFsm = getStartNodes(); + if(startNodes.size() != startNodesFsm.size()){ + throw std::runtime_error("bad number of Start nodes"); + } + + std::vector<std::shared_ptr<FsmRunTimeContext>> walks; + for(std::size_t i = 0; i < startNodes.size(); i++){ + walks.push_back(std::make_shared<FsmRunTimeContext>(startNodesFsm[i],startNodes[i])); + } + + std::vector<std::shared_ptr<FsmRunTimeContext>> allValidContext; + + while (!walks.empty()) + { + std::vector<std::shared_ptr<FsmRunTimeContext>> nextWalks; + + for(auto fsmContext : walks){ + //if we are in a valid st we save it + //it's one solution of the posible solution of the matching + if(fsmContext->isOnValidState()){ + //TODO not push same fsm use are_equal + allValidContext.push_back(fsmContext); + } + + std::vector<std::shared_ptr<FsmRunTimeContext>> tmpNextWalks = fsmContext->getActState()->test(fsmContext); + } + } + + +} + + +const std::set<std::shared_ptr<FsmEdge>>& FsmGraph::getEdge(void){ + return mEdges; +} + +void FsmGraph::addEdge(std::shared_ptr<FsmEdge> edge){ + mEdges.insert(edge); + mAllOrigine.insert(edge->getDestNode()->getOrigine()); + mAllOrigine.insert(edge->getSourceNode()->getOrigine()); +} + +const std::vector<std::shared_ptr<FsmNode>> FsmGraph::getStartNodes(void){ + std::set<std::shared_ptr<FsmNode>> nodes = getNodes(); + std::vector<std::shared_ptr<FsmNode>> startNodes; + for(auto node :nodes){ + if(node->isStart()){ + startNodes.push_back(node); + } + } + return startNodes; +} + +const std::set<std::shared_ptr<FsmNode>> FsmGraph::getValidNodes(void){ + std::set<std::shared_ptr<FsmNode>> nodes = getNodes(); + std::set<std::shared_ptr<FsmNode>> ValidNodes; + for(auto node :nodes){ + if(node->isValid()){ + ValidNodes.insert(node); + } + } + //may short + return ValidNodes; +} + +const std::set<std::shared_ptr<FsmNode>> FsmGraph::getNodes(void){ + std::set<std::shared_ptr<FsmNode>> nodes; + for(auto edge : mEdges){ + nodes.insert(edge->getDestNode()); + nodes.insert(edge->getSourceNode()); + } + return nodes; +} + +void FsmGraph::setGroupe(std::size_t groupeIdx){ + std::set<std::shared_ptr<FsmNode>> nodes = getNodes(); + for(auto node :nodes){ + node->setGroupe(groupeIdx); + } +} + +void FsmGraph::unionG(const std::shared_ptr<FsmGraph> fsmGraph){ + + for(auto edge : fsmGraph->getEdge()){ + addEdge(edge); + } +} + +void FsmGraph::mergeOneStartOneValid(const std::shared_ptr<FsmGraph> fsmGraph){ + std::set<std::shared_ptr<FsmNode>> validNodes = getValidNodes(); + std::vector<std::shared_ptr<FsmNode>> startNodes = fsmGraph->getStartNodes(); + + if (startNodes.size() != 1 || validNodes.size() != 1){ + + std::ostringstream errorMessage; + errorMessage <<"mergeOneStartOneValid start size: " << startNodes.size() << " valide size : " << validNodes.size() + <<" can only merge FSM 1 start 1 valide"; + throw std::runtime_error(errorMessage.str()); + } + + unionG(fsmGraph); + //for loop useless but for future merge it's coudl be used + for(auto valid : validNodes){ + valid->unValid(); + for(auto start : startNodes){ + start->unStart(); + _mergeNode(start,valid); + } + } +} + +std::size_t FsmGraph::getNbSubFsm(void){ + return mAllOrigine.size(); +} + +void FsmGraph::incOrigineAllNodeBy(std::size_t incr){ + std::set<std::shared_ptr<FsmNode>> nodes = getNodes(); + for(auto node :nodes){ + node->incOrigine(incr); + } + for(auto origin : mAllOrigine){ + origin += incr; + } +} + +void FsmGraph::_mergeNode(std::shared_ptr<FsmNode> source,std::shared_ptr<FsmNode> dest){ + std::set<std::shared_ptr<FsmNode>> nodes = getNodes(); + + if(nodes.find(source) == nodes.end() || nodes.find(dest) == nodes.end()){ + throw std::runtime_error("FsmGraph can not merge node not in the graph"); + } + nodes.clear(); + + //probagate source attribut + if(source->isValid()){ + dest->valid(); + } + if(source->isStart()){ + dest->start(); + } + + //merge source to dest by replace source by dest in all EDGE + for(auto edge : mEdges){ + if(edge->getDestNode() == source ){ + edge->reSetDestNode(dest); + }else if(edge->getSourceNode() == source ){ + edge->reSetSouceNode(dest); + } + + } + //check is source is not in graph + nodes = getNodes(); + if(nodes.find(source) != nodes.end() ){ + throw std::runtime_error("FsmGraph merge node not effective"); + } + nodes.clear(); + +} diff --git a/src/graphRegex/matchFsm/FsmNode.cpp b/src/graphRegex/matchFsm/FsmNode.cpp new file mode 100644 index 000000000..321b78610 --- /dev/null +++ b/src/graphRegex/matchFsm/FsmNode.cpp @@ -0,0 +1,117 @@ +#include "aidge/graphRegex/matchFsm/FsmNode.hpp" +#include "aidge/graphRegex/matchFsm/FsmEdge.hpp" +#include "aidge/graphRegex/matchFsm/FsmRunTimeContext.hpp" + +using namespace Aidge; + + + +FsmNode::FsmNode(bool isAValid,bool isAStart ){ + mIsAStart =isAStart; + mIsAValid =isAValid; + +} +const std::vector<std::shared_ptr<FsmRunTimeContext>> FsmNode::test( std::shared_ptr<FsmRunTimeContext> fsmContext){ + + + std::vector<std::shared_ptr<FsmRunTimeContext>> out; + std::shared_ptr<FsmRunTimeContext> newFsmContext ; + for(auto edge : mEdges){ + if (auto sharedEdge = edge.lock()) { + std::shared_ptr<FsmNode> nextState = sharedEdge->getDestNode(); + newFsmContext = std::make_shared<FsmRunTimeContext>(fsmContext); + + EdgeTestResult edgeRes = sharedEdge->test(newFsmContext); + + if(edgeRes.success){ + if(edgeRes.node.size() != 0){ + for(auto nextNode :edgeRes.node ){ + + if(!newFsmContext->isAlreadyValid(nextNode) || newFsmContext->isCommonDefined(nextNode) ){ + out.push_back( std::make_shared<FsmRunTimeContext>(newFsmContext,nextState,nextNode)); + + }else{ + out.push_back( std::make_shared<FsmRunTimeContext>(newFsmContext,nextState,nullptr)); + } + + } + } + } + newFsmContext.reset(); + + }else{ + throw std::runtime_error("test FsmNode weak pointer is expired" ); + } + + } + return out; +} + + + +std::size_t FsmNode::getOrigine(void){ + return mOrigineStm; +} +void FsmNode::incOrigine(std::size_t inc){ + mOrigineStm += inc; +} +void FsmNode::rmEdge(std::shared_ptr<FsmEdge> edge){ + mEdges.erase(edge); +} + +void FsmNode::addEdge(std::shared_ptr<FsmEdge> edge){ + mEdges.insert(edge); +} + +const std::set<std::shared_ptr<FsmNode>> FsmNode::getChildNodes(void){ + std::set<std::shared_ptr<FsmNode>> children; + for(auto edge : mEdges){ + if (auto sharedEdge = edge.lock()) { + children.insert(sharedEdge->getDestNode()); + }else{ + throw std::runtime_error("getChildNodes FsmNode weak pointer is expired" ); + } + } + return children; +} + + +const std::set<std::weak_ptr<FsmNode>,lex_compare<FsmNode>> FsmNode::getParentNodes(void){ + return mParents; +} +const std::set<std::weak_ptr<FsmEdge>,lex_compare<FsmEdge>> FsmNode::getEdges(void){ + return mEdges; +} + +void FsmNode::setGroupe(std::size_t groupeIdx){ + mGroupeStm = groupeIdx; + +} + +bool FsmNode::isValid(void){ + return mIsAValid; +} +bool FsmNode::isStart(void){ + return mIsAStart; +} +void FsmNode::unValid(void){ + mIsAValid =false; +} +void FsmNode::valid(void){ + mIsAValid =true; +} +void FsmNode::unStart(void){ + mIsAStart =false; +} +void FsmNode::start(void){ + mIsAStart =true; +} + + + +void FsmNode::addParent(std::shared_ptr<FsmNode> node){ + mParents.insert(node); +} +void FsmNode::rmParent(std::shared_ptr<FsmNode> node){ + mParents.erase(node); +} \ No newline at end of file diff --git a/src/graphRegex/matchFsm/FsmRunTimeContext.cpp b/src/graphRegex/matchFsm/FsmRunTimeContext.cpp new file mode 100644 index 000000000..3e1b5f2a1 --- /dev/null +++ b/src/graphRegex/matchFsm/FsmRunTimeContext.cpp @@ -0,0 +1,204 @@ +#include "aidge/graphRegex/matchFsm/FsmRunTimeContext.hpp" +#include "aidge/graphRegex/matchFsm/FsmNode.hpp" + +using namespace Aidge; + +std::vector<std::set<NodePtr>> FsmRunTimeContext::mRejectedNodes; + +FsmRunTimeContext::FsmRunTimeContext(std::shared_ptr<FsmNode> actState ,NodePtr actOpNode ,std::size_t idxRejeced ){ + mActOpNode = actOpNode; + mActState = actState; + + //not define case + if(idxRejeced == std::numeric_limits<std::size_t>::max()){ + mLocalIdxRejeced = mRejectedNodes.size(); + mRejectedNodes.push_back(std::set<NodePtr>()); + }else{ + if(idxRejeced > mRejectedNodes.size()-1 ){ + throw std::runtime_error("FsmRunTimeContext idxRejeced"); + } + mLocalIdxRejeced =idxRejeced; + } +} + + + +FsmRunTimeContext::FsmRunTimeContext(std::shared_ptr<FsmRunTimeContext> fsmRunTime){ + mActOpNode = fsmRunTime->mActOpNode; + mActState = fsmRunTime->mActState; + mCommonNodes = fsmRunTime->mCommonNodes; + mValidNodes = fsmRunTime->mValidNodes; + mLocalIdxRejeced = fsmRunTime->mLocalIdxRejeced; +} +FsmRunTimeContext::FsmRunTimeContext(std::shared_ptr<FsmRunTimeContext> fsmRunTime,std::shared_ptr<FsmNode> actState ,NodePtr actOpNode ){ + mActOpNode = actOpNode; + mActState = actState; + mCommonNodes = fsmRunTime->mCommonNodes; + mValidNodes = fsmRunTime->mValidNodes; + mLocalIdxRejeced = fsmRunTime->mLocalIdxRejeced; +} + +void FsmRunTimeContext::addRejectedNode(NodePtr node){ + mRejectedNodes[mLocalIdxRejeced].insert(node); +} + +std::set<NodePtr> FsmRunTimeContext::getRejectedNodes(void){ + return mRejectedNodes[mLocalIdxRejeced]; +} + +bool FsmRunTimeContext::isOnValidState(void){ + return mActState->isValid(); +} + +bool FsmRunTimeContext::isCommonDefined(NodePtr node){ + return mCommonNodes.find(node) != mCommonNodes.end(); +} + +bool FsmRunTimeContext::isAlreadyValid(NodePtr node){ + return getValidNodes().find(node) != getValidNodes().end(); +} + +bool FsmRunTimeContext::areCompatible(std::shared_ptr<FsmRunTimeContext> fsmContext){ + /* + see if 2 context can be merge + it need to have different mValidNodes exept for common + and the same idx for the common + */ + + //common node + + for (const auto& ref : getCommon()) { + for (const auto& test : fsmContext->getCommon()) { + //same index + if(ref.second == test.second){ + if(ref.first != test.first){ + return false; + } + } + } + } + + //valid nodes + std::set<NodePtr> commonElements; + + std::set_intersection( + getValidNodesNoCommon().begin(), getValidNodesNoCommon().end(), + fsmContext->getValidNodesNoCommon().begin(), fsmContext->getValidNodesNoCommon().end(), + std::inserter(commonElements, commonElements.end()) + ); + + + if (!commonElements.empty()) { + return false; + } + + return true; +} + +bool FsmRunTimeContext::areEqual(std::shared_ptr<FsmRunTimeContext> fsmContext){ + if(getActNode() != fsmContext->getActNode()){ + return false; + } + if (getActState() != fsmContext->getActState()){ + return false; + } + if (getValidNodes() != fsmContext->getValidNodes()){ + return false; + } + if (getCommon() != fsmContext->getCommon()){ + return false; + } + + + return true; +} + +void FsmRunTimeContext::setCommon(NodePtr node,std::size_t commonIdx){ + if(isCommonDefined(node)){ + if (mCommonNodes.at(node) != commonIdx){ + throw std::runtime_error("conflict idx in the Common node"); + } + }else{ + mCommonNodes[node] = commonIdx; + } +} + +void FsmRunTimeContext::setValid(NodePtr node,std::shared_ptr<ConditionalInterpreter> tag){ + //we already find a node of this type + if(mValidNodes.find(tag) != mValidNodes.end()){ + if(isAlreadyValid(node) && !isCommonDefined(node) ){ + throw std::runtime_error("setValid you valid tow time"); + } + mValidNodes[tag].insert(node); + }else{ + mValidNodes[tag] = {node}; + } + +} + +std::size_t FsmRunTimeContext::getSubStmId(void){ + return mActState->getOrigine(); +} + +NodePtr FsmRunTimeContext::getCommonNodeFromIdx(std::size_t commonIdx){ + for (const auto& pair : mCommonNodes) { + if (pair.second == commonIdx) { + return pair.first; // Return the key when the value is found + } + } + throw std::runtime_error("getCommonNodeFromIdx Value not found in the map"); +} + +std::size_t FsmRunTimeContext::getCommonNodeIdx(NodePtr node){ + if(isCommonDefined(node)){ + return mCommonNodes.at(node); + } + throw std::runtime_error("getCommonNodeIdx node not found"); +} + +std::set<NodePtr> FsmRunTimeContext::getCommonNodes(void){ + std::set<NodePtr> nodes; + // Iterate over the map and insert values into the set + for (const auto& pair : mCommonNodes) { + nodes.insert(pair.first); + } + return nodes; +} + +std::map<NodePtr,std::size_t> FsmRunTimeContext::getCommon(void){ + return mCommonNodes; +} + +std::set<NodePtr> FsmRunTimeContext::getValidNodes(void){ + // Create a set to store the values from the map + std::set<NodePtr> nodes; + // Iterate over the map and insert values into the set + for (const auto& pair : mValidNodes) { + nodes.insert(pair.second.begin(),pair.second.end()); + } + return nodes; +} + +std::set<NodePtr> FsmRunTimeContext::getValidNodesNoCommon(void){ + std::set<NodePtr> differenceSet; + std::set_difference(getValidNodes().begin(), getValidNodes().end(), getCommonNodes().begin(), getCommonNodes().end(),std::inserter(differenceSet, differenceSet.end())); + return differenceSet; +} + +std::map<std::shared_ptr<ConditionalInterpreter>,std::set<NodePtr>> FsmRunTimeContext::getValid(void){ + return mValidNodes; +} + +NodePtr FsmRunTimeContext::getActNode(void){ + return mActOpNode; +} + +std::shared_ptr<FsmNode> FsmRunTimeContext::getActState(){ + return mActState; +} + + +void FsmRunTimeContext::rst(void){ + mRejectedNodes.clear(); +} + diff --git a/src/graphRegex/matchFsm/MatchResult.cpp b/src/graphRegex/matchFsm/MatchResult.cpp new file mode 100644 index 000000000..cb06967bb --- /dev/null +++ b/src/graphRegex/matchFsm/MatchResult.cpp @@ -0,0 +1,4 @@ +#include "aidge/graphRegex/matchFsm/MatchResult.hpp" + +using namespace Aidge; + diff --git a/src/nodeTester/ConditionalInterpreter.cpp b/src/nodeTester/ConditionalInterpreter.cpp new file mode 100644 index 000000000..ccf9794a8 --- /dev/null +++ b/src/nodeTester/ConditionalInterpreter.cpp @@ -0,0 +1,359 @@ + +#include "aidge/nodeTester/ConditionalInterpreter.hpp" + +using namespace Aidge; + + +/////////////////////////////// +//ConditionalRegisterFunction +/////////////////////////////// + + ConditionalData* ConditionalRegisterFunction::run(const std::string key,std::vector<ConditionalData*> & datas){ + + auto lambdaIt = mWlambda.find(key); + if (lambdaIt != mWlambda.end()) { + return lambdaIt->second(datas); + }else { + throw std::runtime_error("can not run Lambda due to invalid key: " + key); + } + } + +////////////////////// +//ConditionalInterpreter +/////////////////////// + ConditionalInterpreter::ConditionalInterpreter(const std::string ConditionalExpressions) + :mLambdaRegiter() + { + + ConditionalParser conditionalParser = ConditionalParser(ConditionalExpressions); + mTree = conditionalParser.parse(); + ///lambda by default + mLambdaRegiter.insert("getType",+[](NodePtr NodeOp){return NodeOp->type();}); + + } + + + bool ConditionalInterpreter::test( const NodePtr nodeOp) + { + try{ + std::vector<ConditionalData*> r = visit({mTree},nodeOp); + + if (r.size() != 1){ + throw std::runtime_error("Multy output interpretation output"); + }else{ + if (!r[0]->isTypeEqualTo<bool>()){ + throw std::runtime_error("TEST OUT MUST BE A BOOL "); + }else{ + return r[0]->getValue<bool>(); + } + } + + }catch(const std::exception& e){ + std::ostringstream errorMessage; + errorMessage << "Error in test " << "\n\t" << e.what() << "\n"; + throw std::runtime_error(errorMessage.str()); + } + + } + + void ConditionalInterpreter::insertLambda(const std::string key,std::function<bool(Aidge::NodePtr)> f){ + mLambdaRegiter.insert<std::function<bool(Aidge::NodePtr)> >(key, f); + } + + ///// + std::vector<ConditionalData*> ConditionalInterpreter::visit(const ASTNodeCh& nodes, const NodePtr nodeOp ){ + std::vector<ConditionalData*> dataVector; + + for ( std::shared_ptr<AstNode<ConditionalTokenTypes>> node : nodes) { + try{ + switch (node->getType()){ + /////////////////////////////////// + //OPERATOR + /////////////////////////////////// + case ConditionalTokenTypes::NOT: + { + std::vector<ConditionalData*> tmp = fNot(visit(node->getChilds(),nodeOp)); + dataVector.insert(dataVector.end(), tmp.begin(), tmp.end()); + } + break; + case ConditionalTokenTypes::AND: + { + std::vector<ConditionalData*> tmp = fAnd(visit(node->getChilds(),nodeOp)); + dataVector.insert(dataVector.end(), tmp.begin(), tmp.end()); + } + break; + case ConditionalTokenTypes::OR: + { + std::vector<ConditionalData*> tmp = fOr(visit(node->getChilds(),nodeOp)); + dataVector.insert(dataVector.end(), tmp.begin(), tmp.end()); + } + break; + case ConditionalTokenTypes::EQ: + { + std::vector<ConditionalData*> tmp = fEq(visit(node->getChilds(),nodeOp)); + dataVector.insert(dataVector.end(), tmp.begin(), tmp.end()); + } + break; + case ConditionalTokenTypes::NEQ: + { + std::vector<ConditionalData*> tmp = fNeq(visit(node->getChilds(),nodeOp)); + dataVector.insert(dataVector.end(), tmp.begin(), tmp.end()); + } + break; + + /////////////////////////////////// + //VALUE + /////////////////////////////////// + + case ConditionalTokenTypes::KEY: + + break; + case ConditionalTokenTypes::INTEGER: + { + std::vector<ConditionalData*> tmp = fStrToInteger(node); + dataVector.insert(dataVector.end(), tmp.begin(), tmp.end()); + } + break; + case ConditionalTokenTypes::FLOAT: + { + std::vector<ConditionalData*> tmp = fStrToFloat(node); + dataVector.insert(dataVector.end(), tmp.begin(), tmp.end()); + } + break; + case ConditionalTokenTypes::STRING: + { + std::vector<ConditionalData*> tmp = fStrToStr(node); + dataVector.insert(dataVector.end(), tmp.begin(), tmp.end()); + } + break; + + case ConditionalTokenTypes::NODE: //TODO + { + + ConditionalData* data = new ConditionalData; + data->setValue<NodePtr>(nodeOp); + std::vector<ConditionalData*> tmp = {data}; + dataVector.insert(dataVector.end(), tmp.begin(), tmp.end()); + + } + break; + + case ConditionalTokenTypes::LAMBDA: + { + std::vector<ConditionalData*> tmp = fLambda(node,visit(node->getChilds(),nodeOp)); + dataVector.insert(dataVector.end(), tmp.begin(), tmp.end()); + } + break; + + case ConditionalTokenTypes::BOOL: //TODO + { + ConditionalData* data = new ConditionalData; + + if(node->getValue() == "true"){ + data->setValue<bool>(true); + }else{ + data->setValue<bool>(false); + } + + std::vector<ConditionalData*> tmp = {data}; + dataVector.insert(dataVector.end(), tmp.begin(), tmp.end()); + } + break; + + case ConditionalTokenTypes::ARGSEP: + case ConditionalTokenTypes::LPAREN: + case ConditionalTokenTypes::RPAREN: + case ConditionalTokenTypes::STOP: + default: + throw std::runtime_error("NODE TYPE NOT SUPORTED IN ConditionalInterpreter"); + } + }catch(const std::exception& e){ + std::ostringstream errorMessage; + errorMessage << "Error in visiting AST for node"<< nodeOp->name() << "\n\t" << e.what() << "\n"; + throw std::runtime_error(errorMessage.str()); + } + } + + return dataVector; + } + + + ////////////////////// + //value convertor + ///////////////////// + + + std::vector<ConditionalData*> ConditionalInterpreter::fStrToInteger(const std::shared_ptr<AstNode<ConditionalTokenTypes>>& node) + { + std::vector<ConditionalData*> dataVector; + ConditionalData* data = new ConditionalData; + data->setValue<int>(std::stoi(node->getValue())); + dataVector.push_back(data); + return dataVector; + } + + std::vector<ConditionalData*> ConditionalInterpreter::fStrToFloat(const std::shared_ptr<AstNode<ConditionalTokenTypes>>& node) + { + std::vector<ConditionalData*> dataVector; + ConditionalData* data = new ConditionalData; + data->setValue<float>(std::stof(node->getValue())); + dataVector.push_back(data); + return dataVector; + } + + std::vector<ConditionalData*> ConditionalInterpreter::fStrToStr(const std::shared_ptr<AstNode<ConditionalTokenTypes>>& node) + { + std::vector<ConditionalData*> dataVector; + ConditionalData* data = new ConditionalData; + data->setValue<std::string>(node->getValue()); + dataVector.push_back(data); + return dataVector; + } + + std::vector<ConditionalData*> ConditionalInterpreter::fLambda(const std::shared_ptr<AstNode<ConditionalTokenTypes>>& node,std::vector<ConditionalData*> datas) + { + //if the lambda have input + std::vector<ConditionalData*> dataVector; + ConditionalData* data; + try { + data = mLambdaRegiter.run(node->getValue(),datas); + } catch (const std::exception& e) { + std::ostringstream errorMessage; + errorMessage << "Error in conditional interpretation when run the "<< node->getValue() <<" Lambda\n\t" << e.what() << "\n"; + throw std::runtime_error(errorMessage.str()); + } + dataVector.push_back(data); + datas.clear(); + return dataVector; + } + + std::vector<ConditionalData*> ConditionalInterpreter::fEq(std::vector<ConditionalData*> datas) + { + if (datas.size() != 2){ + throw std::runtime_error("EQ need 2 arg and get :" + datas.size()); + } + auto a = datas[0]; + auto b = datas[1]; + + if (a->getType() != b->getType()){ + throw std::runtime_error("EQ Unsuported between type :" + a->getType() +" "+ b->getType()); + } + + std::vector<ConditionalData*> dataVector; + + ConditionalData* data = new ConditionalData; + + if (a->isTypeEqualTo<int>()) { + data->setValue<bool>( a->getValue<int>() == b->getValue<int>()); + }else if (a->isTypeEqualTo<float>()){ + data->setValue<bool>( a->getValue<float>() == b->getValue<float>()); + }else if (a->isTypeEqualTo<std::string>()){ + data->setValue<bool>( a->getValue<std::string>() == b->getValue<std::string>()); + }else if (a->isTypeEqualTo<bool>()){ + data->setValue<bool>( a->getValue<bool>() == b->getValue<bool>()); + }else{ + throw std::runtime_error("EQ Unknown type encountered :" + a->getType() ); + } + dataVector.push_back(data); + datas.clear(); + return dataVector; + } + + std::vector<ConditionalData*> ConditionalInterpreter::fNeq(std::vector<ConditionalData*> datas) + { + if (datas.size() != 2){ + throw std::runtime_error("NEQ need 2 arg and get :" + datas.size()); + } + auto a = datas[0]; + auto b = datas[1]; + + if (a->getType() != b->getType()){ + throw std::runtime_error("NEQ Unsuported between type :" + a->getType() +" "+ b->getType()); + } + + std::vector<ConditionalData*> dataVector; + + ConditionalData* data = new ConditionalData; + + if (a->isTypeEqualTo<int>()) { + data->setValue<bool>( a->getValue<int>() != b->getValue<int>()); + }else if (a->isTypeEqualTo<float>()){ + data->setValue<bool>( a->getValue<float>() != b->getValue<float>()); + }else if (a->isTypeEqualTo<std::string>()){ + data->setValue<bool>( a->getValue<std::string>() != b->getValue<std::string>()); + }else + { + throw std::runtime_error("NEQ Unknown type encountered :" + a->getType() ); + } + dataVector.push_back(data); + datas.clear(); + return dataVector; + } + + std::vector<ConditionalData*> ConditionalInterpreter::fAnd(std::vector<ConditionalData*> datas) + { + if (datas.size() != 2){ + throw std::runtime_error("AND need 2 arg and get :" + datas.size()); + } + auto a = datas[0]; + auto b = datas[1]; + + std::vector<ConditionalData*> dataVector; + + if (a->getType() != typeid(bool).name() || b->getType() != typeid(bool).name()){ + throw std::runtime_error("AND Unknown type encountered need bool get :" + a->getType() ); + } + + ConditionalData* data = new ConditionalData; + data->setValue<bool>( a->getValue<bool>() && b->getValue<bool>()); + + + dataVector.push_back(data); + datas.clear(); + return dataVector; + } + + std::vector<ConditionalData*> ConditionalInterpreter::fOr(std::vector<ConditionalData*> datas) + { + if (datas.size() != 2){ + throw std::runtime_error("OR need 2 arg and get :" + datas.size()); + } + auto a = datas[0]; + auto b = datas[1]; + + std::vector<ConditionalData*> dataVector; + + if (a->getType() != typeid(bool).name() || b->getType() != typeid(bool).name()){ + throw std::runtime_error("OR Unknown type encountered need bool get :" + a->getType() ); + } + + ConditionalData* data = new ConditionalData; + data->setValue<bool>( a->getValue<bool>() || b->getValue<bool>()); + + + dataVector.push_back(data); + datas.clear(); + return dataVector; + } + + std::vector<ConditionalData*> ConditionalInterpreter::fNot(std::vector<ConditionalData*> datas) + { + if (datas.size() != 1){ + throw std::runtime_error("not need 1 arg and get :" + datas.size()); + } + auto a = datas[0]; + + std::vector<ConditionalData*> dataVector; + + if (a->getType() != typeid(bool).name()){ + throw std::runtime_error("NOT Unknown type encountered need bool get :" + a->getType() ); + } + + ConditionalData* data = new ConditionalData; + data->setValue<bool>( !a->getValue<bool>() ); + + + dataVector.push_back(data); + datas.clear(); + return dataVector; + } diff --git a/src/nodeTester/ConditionalLexer.cpp b/src/nodeTester/ConditionalLexer.cpp new file mode 100644 index 000000000..9379bd840 --- /dev/null +++ b/src/nodeTester/ConditionalLexer.cpp @@ -0,0 +1,242 @@ +#include "aidge/nodeTester/ConditionalLexer.hpp" + +using namespace Aidge; + +////////////////// +//ConditionalLexer +////////////////// + + +ConditionalLexer::ConditionalLexer( const std::string ConditionalExpressions): +mConditionalExpressions(ConditionalExpressions) +{ + mPosition = 0; +} + +std::shared_ptr<ParsingToken<ConditionalTokenTypes>> ConditionalLexer::getNextToken(void){ + std::string currentChars = ""; + + while (mPosition < mConditionalExpressions.length()) + { + //erase all space + if (mConditionalExpressions[mPosition] != ' ') + { + currentChars += mConditionalExpressions[mPosition]; + } + else + { + mPosition++; + continue; + } + //performe tokenisation, find a regex and make a new token + + if (std::regex_match(currentChars,std::regex("\\&\\&")))// the AND TOKEN + { + mPosition++; + return std::make_shared<ParsingToken<ConditionalTokenTypes>>(ConditionalTokenTypes::AND,""); + } + else if (std::regex_match(currentChars,std::regex("\\|\\|")))// the OR TOKEN + { + mPosition++; + return std::make_shared<ParsingToken<ConditionalTokenTypes>>(ConditionalTokenTypes::OR,""); + } + else if (std::regex_match(currentChars,std::regex("\\!")))// the Not and not equ + { + mPosition++; + if ( mPosition < mConditionalExpressions.length()){ + currentChars += mConditionalExpressions[mPosition]; + if(std::regex_match(currentChars,std::regex("!="))){ + mPosition++; + return std::make_shared<ParsingToken<ConditionalTokenTypes>>(ConditionalTokenTypes::NEQ,""); + }else{ + return std::make_shared<ParsingToken<ConditionalTokenTypes>>(ConditionalTokenTypes::NOT,""); + } + } + //a not at the end not ok but it's the parseur work + return std::make_shared<ParsingToken<ConditionalTokenTypes>>(ConditionalTokenTypes::NOT,""); + } + else if (std::regex_match(currentChars,std::regex("==")))// the EQ TOKEN + { + mPosition++; + return std::make_shared<ParsingToken<ConditionalTokenTypes>>(ConditionalTokenTypes::EQ,""); + } + else if (std::regex_match(currentChars,std::regex("\\(")))// the LPAREN TOKEN + { + mPosition++; + return std::make_shared<ParsingToken<ConditionalTokenTypes>>(ConditionalTokenTypes::LPAREN,""); + } + else if (std::regex_match(currentChars,std::regex("\\)")))// the RPAREN TOKEN + { + mPosition++; + return std::make_shared<ParsingToken<ConditionalTokenTypes>>(ConditionalTokenTypes::RPAREN,""); + } + else if (std::regex_match(currentChars,std::regex(",")))// the RPAREN TOKEN + { + mPosition++; + return std::make_shared<ParsingToken<ConditionalTokenTypes>>(ConditionalTokenTypes::ARGSEP,""); + } + else if (std::regex_match(currentChars,std::regex("\\$")))// the ACTNode TOKEN + { + mPosition++; + return std::make_shared<ParsingToken<ConditionalTokenTypes>>(ConditionalTokenTypes::NODE,""); + } + + + ///// + //non const lent token + ///// + + //LAMBDA, KEY , bool //the fuction TAG + else if (std::regex_match(currentChars,std::regex("[A-Za-z_]")))// the KEY TOKEN (a char next ) + { + //read all the key + bool isLambda = false; + std::regex keyRegex("[A-Za-z_0-9]+"); + std::regex LambdaRegex("[A-Za-z_0-9]+\\("); + + while ( mPosition < mConditionalExpressions.length()) { + if(!std::regex_match(currentChars,keyRegex) && !std::regex_match(currentChars,LambdaRegex)) + { + currentChars.pop_back(); //the last char is the problemes + break; + } + else if (std::regex_match(currentChars,LambdaRegex)){ + isLambda = true; + } + mPosition++; + if (mPosition < mConditionalExpressions.length()) currentChars += mConditionalExpressions[mPosition]; + //currentChars += mConditionalExpressions[mPosition]; + } + //we end the match 2 posibility + //we are at the end of the mConditionalExpressions and we need to ensure the match + //we are not we can continu + if (mPosition == mConditionalExpressions.length()-1) + { + if (!std::regex_match(currentChars,keyRegex) && !std::regex_match(currentChars,LambdaRegex)) + { + throw badTokenError(currentChars,mPosition); + } + //mPosition++; // we stop all by going pos > lengt + } + + + if (std::regex_match(currentChars,std::regex("(true|false)"))){ + return std::make_shared<ParsingToken<ConditionalTokenTypes>>(ConditionalTokenTypes::BOOL,currentChars); + + } else if (isLambda){ + currentChars.pop_back();//pop the ( of the lambda + return std::make_shared<ParsingToken<ConditionalTokenTypes>>(ConditionalTokenTypes::LAMBDA,currentChars); + } else{ + return std::make_shared<ParsingToken<ConditionalTokenTypes>>(ConditionalTokenTypes::KEY,currentChars); + } + + } + //numeric value + else if (std::regex_match(currentChars,std::regex("[0-9]")))// the KEY TOKEN (a char next ) + { + //read all the key + bool isFloat = false; + std::regex integerRegex("[0-9]+$"); + std::regex floatRegex("[0-9]+\\.[0-9]*$"); + + while ( mPosition < mConditionalExpressions.length()) { + + if(!std::regex_match(currentChars,integerRegex) && !std::regex_match(currentChars,floatRegex)) + { + currentChars.pop_back(); // the last char match is not a good one + break; + } + else if (std::regex_match(currentChars,floatRegex)){ + isFloat = true; + } + mPosition++; + if (mPosition < mConditionalExpressions.length()) currentChars += mConditionalExpressions[mPosition]; + //currentChars += mConditionalExpressions[mPosition]; + } + //we end the match 2 posibility + //we are at the end of the mConditionalExpressions and we need to ensure the match + //we are not we can continu + if (mPosition == mConditionalExpressions.length()-1) + { + if (!std::regex_match(currentChars,integerRegex) && !std::regex_match(currentChars,floatRegex)) + { + throw badTokenError(currentChars,mPosition); + } + } + + if(isFloat){ + return std::make_shared<ParsingToken<ConditionalTokenTypes>>(ConditionalTokenTypes::FLOAT,currentChars); + }else{ + return std::make_shared<ParsingToken<ConditionalTokenTypes>>(ConditionalTokenTypes::INTEGER,currentChars); + } + + } + //string TODO + else if (std::regex_match(currentChars,std::regex("\'"))) // TODO ' or \' + { + std::regex strRegex("\'[A-Za-z_0-9\\s]*\'$"); + while ( mPosition < mConditionalExpressions.length()) { + if(std::regex_match(currentChars,strRegex)){ + break; + } + mPosition++; + if (mPosition < mConditionalExpressions.length()) currentChars += mConditionalExpressions[mPosition]; + //currentChars += mConditionalExpressions[mPosition]; + } + + //test the end condition + if (mPosition == mConditionalExpressions.length()-1 ){ + if (!std::regex_match(currentChars,strRegex)){ + throw badTokenError(currentChars,mPosition); + } + //mPosition++; // we stop all by going pos > lengt + } + + mPosition++; // go after the last " + //erase the " char + currentChars.pop_back(); + currentChars.erase(0,1); + + return std::make_shared<ParsingToken<ConditionalTokenTypes>>(ConditionalTokenTypes::STRING,currentChars); + + } + + //Array TODO + + mPosition++; + } + + //no more to find no one match the currentChars + if (currentChars.empty()) { + return std::make_shared<ParsingToken<ConditionalTokenTypes>>(ConditionalTokenTypes::STOP,""); // Null shared pointer ; + }else{ + //std::ostringstream errorMessage; + //errorMessage << "\nBad syntax " << currentChars << " :\n" << mConditionalExpressions; + throw badTokenError(currentChars,mPosition); + } + +} + +void ConditionalLexer::rstPosition(void){ + if (isEnd()){ + mPosition = 0; + }else{ + throw badTokenError("end rst",mPosition); + } + +} + +bool ConditionalLexer::isEnd(void){ + return mPosition >= mConditionalExpressions.length(); +} + +std::runtime_error ConditionalLexer::badTokenError(const std::string& currentChars,std::size_t position){ + std::ostringstream errorMessage; + errorMessage << "\nBad syntax " << currentChars << " :\n" << mConditionalExpressions << "\n"; + for (std::size_t i = 0; i < position; i++) { + errorMessage << ' '; + } + errorMessage << "^\n"; + + return std::runtime_error(errorMessage.str()); +} \ No newline at end of file diff --git a/src/nodeTester/ConditionalParser.cpp b/src/nodeTester/ConditionalParser.cpp new file mode 100644 index 000000000..7b5dddcc5 --- /dev/null +++ b/src/nodeTester/ConditionalParser.cpp @@ -0,0 +1,188 @@ + +#include "aidge/nodeTester/ConditionalParser.hpp" + +using namespace Aidge; + + +////////////////////////////// +//ConditionalParser +////////////////////////////// + +ConditionalParser::ConditionalParser(const std::string ConditionalExpressions):mLexer(ConditionalExpressions){ + mCurrentToken = mLexer.getNextToken(); +} + +void ConditionalParser::rstParser(void){ + mLexer.rstPosition(); + mCurrentToken = mLexer.getNextToken(); +} + +void ConditionalParser::ackToken(ConditionalTokenTypes tokenType){ + if(mCurrentToken->getType() == tokenType ){ + + try { + mCurrentToken = mLexer.getNextToken(); + } catch (const std::runtime_error& e) { + std::ostringstream errorMessage; + errorMessage << "Conditional Lexer error in Parser :\n"<< e.what() << std::endl; + throw std::runtime_error(errorMessage.str()); + } + }else{ + + std::ostringstream errorMessage; + errorMessage << "Bad syntax ConditionalParser " << static_cast<int>(mCurrentToken->getType()) <<"!="<< static_cast<int>(tokenType) << "\n"; + errorMessage << mLexer.rep(); + throw std::runtime_error(errorMessage.str()); + } +} + + + +std::shared_ptr<AstNode<ConditionalTokenTypes>> ConditionalParser::constructAstVal(void){ + /* + val : (KEY|INTEGER|FOAT|STRING|LAMBDA) + */ + std::shared_ptr<ParsingToken<ConditionalTokenTypes>> token = mCurrentToken->copy(); + + if (token->getType() == ConditionalTokenTypes::KEY){ + ackToken(ConditionalTokenTypes::KEY); + return std::make_shared<AstNode<ConditionalTokenTypes>>(token); + } + else if(token->getType() == ConditionalTokenTypes::INTEGER){ + ackToken(ConditionalTokenTypes::INTEGER); + return std::make_shared<AstNode<ConditionalTokenTypes>>(token); + } + else if(token->getType() == ConditionalTokenTypes::FLOAT){ + ackToken(ConditionalTokenTypes::FLOAT); + return std::make_shared<AstNode<ConditionalTokenTypes>>(token); + } + else if(token->getType() == ConditionalTokenTypes::BOOL){ + ackToken(ConditionalTokenTypes::BOOL); + return std::make_shared<AstNode<ConditionalTokenTypes>>(token); + } + else if(token->getType() == ConditionalTokenTypes::STRING){ + ackToken(ConditionalTokenTypes::STRING); + return std::make_shared<AstNode<ConditionalTokenTypes>>(token); + + }else if(token->getType() == ConditionalTokenTypes::NODE){ + ackToken(ConditionalTokenTypes::NODE); + return std::make_shared<AstNode<ConditionalTokenTypes>>(token); + + }else if(token->getType() == ConditionalTokenTypes::LAMBDA){ + return constructAstLambda(); + } + + throw std::runtime_error("ConditionalParser unknow val type "+ token->rep().str() + "\n" + mLexer.rep()); + +} + +std::shared_ptr<AstNode<ConditionalTokenTypes>> ConditionalParser::constructAstLambda(void){ + /* + AstLambda : LAMBDA val (ARGSEP val)* RPAREN + */ + std::shared_ptr<ParsingToken<ConditionalTokenTypes>> tokenLdb = mCurrentToken->copy(); + ackToken(ConditionalTokenTypes::LAMBDA); + ASTNodeCh paramLambda; + //AT LEAST ONE VALUE AS INPUT OF A LAMBDA + paramLambda.push_back(constructAstVal()); + while (mCurrentToken->getType() != ConditionalTokenTypes::RPAREN) + { + ackToken(ConditionalTokenTypes::ARGSEP); + paramLambda.push_back(constructAstVal()); + } + ackToken(ConditionalTokenTypes::RPAREN); + return std::make_shared<AstNode<ConditionalTokenTypes>>(tokenLdb,paramLambda); +} + +std::shared_ptr<AstNode<ConditionalTokenTypes>> ConditionalParser::constructAstCmpr(void){ + /* + cmpr : val (EQ|NEQ) val | LPAREN expr RPAREN + NOT ir ? + */ + std::shared_ptr<ParsingToken<ConditionalTokenTypes>> token = mCurrentToken->copy(); + //we can check the type relation ir key (EQ|NEQ) val | val (EQ|NEQ) key , but val (EQ|NEQ) val is valid ? + if (token->getType() == ConditionalTokenTypes::LPAREN) + { + ackToken(ConditionalTokenTypes::LPAREN); + std::shared_ptr<AstNode<ConditionalTokenTypes>> node = constructAstExpr(); + ackToken(ConditionalTokenTypes::RPAREN); + return node; + }else{ + + std::shared_ptr<AstNode<ConditionalTokenTypes>> node = constructAstVal(); + token = mCurrentToken->copy(); + if (token->getType() == ConditionalTokenTypes::EQ){ + ackToken(ConditionalTokenTypes::EQ); + return std::make_shared<AstNode<ConditionalTokenTypes>>(token,ASTNodeCh{node,constructAstVal()}); + }else if(token->getType() == ConditionalTokenTypes::NEQ){ + ackToken(ConditionalTokenTypes::NEQ); + return std::make_shared<AstNode<ConditionalTokenTypes>>(token,ASTNodeCh{node,constructAstVal()}); + }else{ + + throw std::runtime_error("constructAstCmpr "+ token->rep().str() + "\n" + mLexer.rep()); + } + + } +} + +std::shared_ptr<AstNode<ConditionalTokenTypes>> ConditionalParser::constructAstExpr(std::size_t precLimit /*= 0*/){ + /* + expr : cmpr ((AND | OR) cmpr)* + the NOT is not binary OP can be use in pratt + precedence H to L: TODO + AND + OR + */ + + //the not + std::shared_ptr<AstNode<ConditionalTokenTypes>> left; + std::shared_ptr<ParsingToken<ConditionalTokenTypes>> token = mCurrentToken->copy(); + + if (mCurrentToken->getType() == ConditionalTokenTypes::NOT ){ + ackToken(ConditionalTokenTypes::NOT ); + left= std::make_shared<AstNode<ConditionalTokenTypes>>(token,ASTNodeCh{constructAstCmpr()}); + }else{ + left= constructAstCmpr(); + } + + //pratt + while (mCurrentToken->getType() != ConditionalTokenTypes::STOP ) //security + { + std::shared_ptr<ParsingToken<ConditionalTokenTypes>> token = mCurrentToken->copy(); + //if the token is not in the map is not a operator so we consider a prec of 0 + if (ConditionalPrec.find(token->getType()) ==ConditionalPrec.end() ){ + return left; + } + + //if my actual operator have a prec <= of the last operator + std::size_t prec = ConditionalPrec.at(token->getType()); + if (prec <= precLimit){ + return left; + } + + //Act all AND and OR + ackToken(token->getType()); + + std::shared_ptr<AstNode<ConditionalTokenTypes>> right = constructAstExpr(prec); + + //i'm not sur what append to newNode + //std::shared_ptr<AstNode<ConditionalTokenTypes>> newNode = std::make_shared<AstNode<ConditionalTokenTypes>>(token,ASTNodeCh{left,constructAstCmpr()}); + std::shared_ptr<AstNode<ConditionalTokenTypes>> newNode = std::make_shared<AstNode<ConditionalTokenTypes>>(token,ASTNodeCh{left,right}); + left = newNode; + } + return left; +} + + +std::shared_ptr<AstNode<ConditionalTokenTypes>> ConditionalParser::parse(void){ + /* + expr : cmpr ((AND | OR) cmpr)* + cmpr : val (EQ|NEQ) val | LPAREN expr RPAREN | BOOL | LAMBDA + val : (KEY|INTEGER|FOAT|STRING|LAMBDA) + lambda : LAMBDA val (ARGSEP val)* RPAREN + */ + std::shared_ptr<AstNode<ConditionalTokenTypes>> astTree = constructAstExpr(); + + rstParser(); + return astTree; +} \ No newline at end of file diff --git a/unit_tests/graphRegex/Test_Fsm.cpp b/unit_tests/graphRegex/Test_Fsm.cpp new file mode 100644 index 000000000..518987f53 --- /dev/null +++ b/unit_tests/graphRegex/Test_Fsm.cpp @@ -0,0 +1,194 @@ +#include <memory> + +#include <catch2/catch_test_macros.hpp> + +#include "aidge/nodeTester/ConditionalInterpreter.hpp" + +#include "aidge/graphRegex/matchFsm/FsmNode.hpp" +#include "aidge/graphRegex/matchFsm/FsmEdge.hpp" +#include "aidge/graphRegex/matchFsm/FsmGraph.hpp" +#include "aidge/graphRegex/matchFsm/FsmRunTimeContext.hpp" + +using namespace Aidge; + +TEST_CASE("matchFSM", "FsmEdge") { + + SECTION("FsmEdgeUnique constructor") { + std::shared_ptr<FsmNode> nodeA = std::make_shared<FsmNode>(true,false); + std::shared_ptr<FsmNode> nodeB = std::make_shared<FsmNode>(false,true); + std::shared_ptr<ConditionalInterpreter> toTest = std::make_shared<ConditionalInterpreter>("true==true"); + FsmEdgeUnique EdgeToTest(nodeA,nodeB,toTest); + + REQUIRE(EdgeToTest.getSourceNode() == nodeA); + REQUIRE(EdgeToTest.getDestNode() == nodeB); + REQUIRE(EdgeToTest.isCommon() == false); + } + + SECTION("FsmEdgeCommon constructor") { + std::shared_ptr<FsmNode> nodeA = std::make_shared<FsmNode>(true,false); + std::shared_ptr<FsmNode> nodeB = std::make_shared<FsmNode>(false,true); + std::shared_ptr<ConditionalInterpreter> toTest = std::make_shared<ConditionalInterpreter>("true==true"); + + FsmEdgeCommon EdgeToTest(nodeA,nodeB,toTest,"A"); + + REQUIRE(EdgeToTest.getSourceNode() == nodeA); + REQUIRE(EdgeToTest.getDestNode() == nodeB); + REQUIRE(EdgeToTest.isCommon() == true); + } + + SECTION("FsmEdgeRef constructor") { + std::shared_ptr<FsmNode> nodeA = std::make_shared<FsmNode>(true,false); + std::shared_ptr<FsmNode> nodeB = std::make_shared<FsmNode>(false,true); + std::shared_ptr<ConditionalInterpreter> toTest = std::make_shared<ConditionalInterpreter>("true==true"); + + FsmEdgeRef EdgeToTest(nodeA,nodeB,0,-1); + + REQUIRE(EdgeToTest.getSourceNode() == nodeA); + REQUIRE(EdgeToTest.getDestNode() == nodeB); + REQUIRE(EdgeToTest.isCommon() == false); + } + + SECTION("FsmEdgeEmpty constructor") { + std::shared_ptr<FsmNode> nodeA = std::make_shared<FsmNode>(true,false); + std::shared_ptr<FsmNode> nodeB = std::make_shared<FsmNode>(false,true); + std::shared_ptr<ConditionalInterpreter> toTest = std::make_shared<ConditionalInterpreter>("true==true"); + + FsmEdgeEmpty EdgeToTest(nodeA,nodeB); + + REQUIRE(EdgeToTest.getSourceNode() == nodeA); + REQUIRE(EdgeToTest.getDestNode() == nodeB); + REQUIRE(EdgeToTest.isCommon() == false); + } + + + SECTION("FsmEdgeFactory"){ + + std::map<std::string, std::shared_ptr<ConditionalInterpreter>> allTest = { + {"A",std::make_shared<ConditionalInterpreter>("true==true")}, + {"B",std::make_shared<ConditionalInterpreter>("true==true")}, + {"C",std::make_shared<ConditionalInterpreter>("true==true")} + }; + +// make(std::shared_ptr<FsmNode> source, std::shared_ptr<FsmNode> dest, +// FsmEdgeTypes type,std::map<std::string, const std::shared_ptr<ConditionalInterpreter>> allTest, +// const std::string& lexeme = ""); + + std::shared_ptr<FsmNode> nodeA = std::make_shared<FsmNode>(false,true); + std::shared_ptr<FsmNode> nodeB = std::make_shared<FsmNode>(true,false); +// EMPTY = 0, +// REF, +// COMMON, +// UNIQUE + + std::shared_ptr<FsmEdge> edgeE = FsmEdgeFactory::make(nodeA,nodeB,FsmEdgeTypes::EMPTY,allTest,""); + std::shared_ptr<FsmEdge> edgeU = FsmEdgeFactory::make(nodeA,nodeB,FsmEdgeTypes::UNIQUE,allTest,"A"); + std::shared_ptr<FsmEdge> edgeC = FsmEdgeFactory::make(nodeA,nodeB,FsmEdgeTypes::COMMON,allTest,"A#"); + std::shared_ptr<FsmEdge> edgeR = FsmEdgeFactory::make(nodeA,nodeB,FsmEdgeTypes::REF,allTest,"(0,1)"); + + //test detection of bad syntax lexem + REQUIRE_THROWS(FsmEdgeFactory::make(nodeA,nodeB,FsmEdgeTypes::EMPTY,allTest,"A")); + REQUIRE_THROWS(FsmEdgeFactory::make(nodeA,nodeB,FsmEdgeTypes::UNIQUE,allTest,"A#")); + REQUIRE_THROWS(FsmEdgeFactory::make(nodeA,nodeB,FsmEdgeTypes::COMMON,allTest,"A")); + REQUIRE_THROWS(FsmEdgeFactory::make(nodeA,nodeB,FsmEdgeTypes::REF,allTest,"A")); + + REQUIRE(edgeE->getSourceNode() == nodeA); + REQUIRE(edgeE->getDestNode() == nodeB); + } + + SECTION("graph constructor") { + //make the nodes + std::shared_ptr<FsmNode> nodeA = std::make_shared<FsmNode>(true,false); + std::shared_ptr<FsmNode> nodeB = std::make_shared<FsmNode>(false,false); + std::shared_ptr<FsmNode> nodeC = std::make_shared<FsmNode>(false,true); + + //make the edges + std::shared_ptr<ConditionalInterpreter> toTest = std::make_shared<ConditionalInterpreter>("true==true"); + std::shared_ptr<FsmEdgeUnique> edgeAB = std::make_shared<FsmEdgeUnique>(nodeA,nodeB,toTest); + std::shared_ptr<FsmEdgeUnique> edgeBC = std::make_shared<FsmEdgeUnique>(nodeB,nodeC,toTest); + + std::shared_ptr<FsmGraph> graph = std::make_shared<FsmGraph>(); + + graph->addEdge(edgeAB); + graph->addEdge(edgeBC); + + + REQUIRE(graph->getValidNodes() == std::set<std::shared_ptr<FsmNode>>{nodeA}); + REQUIRE(graph->getStartNodes() == std::vector<std::shared_ptr<FsmNode>>{nodeC}); + } + + + SECTION("graph merge") { + + std::shared_ptr<ConditionalInterpreter> toTest = std::make_shared<ConditionalInterpreter>("true==true"); + + //make the nodes + std::shared_ptr<FsmNode> nodeA = std::make_shared<FsmNode>(false,true); + std::shared_ptr<FsmNode> nodeB = std::make_shared<FsmNode>(false,false); + std::shared_ptr<FsmNode> nodeC = std::make_shared<FsmNode>(true,false); + + //make the edges + + std::shared_ptr<FsmEdgeUnique> edgeAB = std::make_shared<FsmEdgeUnique>(nodeA,nodeB,toTest); + std::shared_ptr<FsmEdgeUnique> edgeBC = std::make_shared<FsmEdgeUnique>(nodeB,nodeC,toTest); + + std::shared_ptr<FsmGraph> graph = std::make_shared<FsmGraph>(); + graph->addEdge(edgeAB); + graph->addEdge(edgeBC); + + REQUIRE(graph->getValidNodes() == std::set<std::shared_ptr<FsmNode>>{nodeC}); + REQUIRE(graph->getStartNodes() == std::vector<std::shared_ptr<FsmNode>>{nodeA}); + REQUIRE(graph->getNodes() == std::set<std::shared_ptr<FsmNode>>{nodeA,nodeB,nodeC}); + + //make the nodes + std::shared_ptr<FsmNode> node2A = std::make_shared<FsmNode>(false,true); + std::shared_ptr<FsmNode> node2B = std::make_shared<FsmNode>(false,false); + std::shared_ptr<FsmNode> node2C = std::make_shared<FsmNode>(true,false); + + + std::shared_ptr<FsmEdgeUnique> edge2AB = std::make_shared<FsmEdgeUnique>(node2A,node2B,toTest); + std::shared_ptr<FsmEdgeUnique> edge2BC = std::make_shared<FsmEdgeUnique>(node2B,node2C,toTest); + + std::shared_ptr<FsmGraph> graph2 = std::make_shared<FsmGraph>(); + + + graph2->addEdge(edge2AB); + graph2->addEdge(edge2BC); + + + REQUIRE(graph2->getValidNodes() == std::set<std::shared_ptr<FsmNode>>{node2C}); + REQUIRE(graph2->getStartNodes() == std::vector<std::shared_ptr<FsmNode>>{node2A}); + REQUIRE(graph2->getNodes() == std::set<std::shared_ptr<FsmNode>>{node2A,node2B,node2C}); + + + graph->mergeOneStartOneValid(graph2); + + REQUIRE(graph->getValidNodes() == std::set<std::shared_ptr<FsmNode>>{node2C}); + REQUIRE(graph->getStartNodes() == std::vector<std::shared_ptr<FsmNode>>{nodeA}); + REQUIRE(graph->getNodes() == std::set<std::shared_ptr<FsmNode>>{nodeA,nodeB,nodeC,node2B,node2C}); + } + + + + +} + +// TEST_CASE("matchFSM", "FsmGraph") { + +// SECTION("FsmEdgeUnique constructor") { +// //make the nodes +// std::shared_ptr<FsmNode> nodeA = std::make_shared<FsmNode>(true,false); +// std::shared_ptr<FsmNode> nodeB = std::make_shared<FsmNode>(false,true); + +// //make the edges +// std::shared_ptr<ConditionalInterpreter> toTest = std::make_shared<ConditionalInterpreter>("true==true"); +// std::shared_ptr<FsmEdgeUnique> edge = std::make_shared<FsmEdgeUnique>(nodeA,nodeB,toTest); + +// std::shared_ptr<FsmGraph> graph = std::make_shared<FsmGraph>(); + +// graph->addEdge(edge); + + + +// } + +// } \ No newline at end of file diff --git a/unit_tests/graphRegex/Test_GraphFsmInterpreter.cpp b/unit_tests/graphRegex/Test_GraphFsmInterpreter.cpp new file mode 100644 index 000000000..d151f46c0 --- /dev/null +++ b/unit_tests/graphRegex/Test_GraphFsmInterpreter.cpp @@ -0,0 +1,27 @@ + +#include <catch2/catch_test_macros.hpp> + +#include "aidge/graphRegex/GraphFsmInterpreter.hpp" + + +using namespace Aidge; +TEST_CASE("GraphFsmInterpreter", "GraphFsmInterpreter") { + + SECTION("Construction") { + std::map<std::string,std::shared_ptr<ConditionalInterpreter>> allTest = { + {"A",std::make_shared<ConditionalInterpreter>("true==true")}, + {"B",std::make_shared<ConditionalInterpreter>("true==true")}, + {"C",std::make_shared<ConditionalInterpreter>("true==true")} + }; + + //GraphFsmInterpreter("A->B",allTest); + std::shared_ptr<GraphFsmInterpreter> fsmGenerator = std::make_shared<GraphFsmInterpreter>("A->B",allTest); + std::shared_ptr<FsmGraph> fsm = fsmGenerator->interpret(); + + + REQUIRE(fsm->getNodes().size() == 3); + REQUIRE(fsm->getStartNodes().size() == 1); + } + + +} \ No newline at end of file diff --git a/unit_tests/graphRegex/Test_GraphLexer.cpp b/unit_tests/graphRegex/Test_GraphLexer.cpp new file mode 100644 index 000000000..9b922e5bd --- /dev/null +++ b/unit_tests/graphRegex/Test_GraphLexer.cpp @@ -0,0 +1,118 @@ +#include <catch2/catch_test_macros.hpp> +#include "aidge/graphRegex/GraphLexer.hpp" +#include "aidge/graphRegex/GraphRegexTypes.hpp" + +#include "aidge/utilsParsing/ParsingToken.hpp" + + +#include <iostream> +#include <map> +#include <functional> + +using namespace Aidge; + +// NEXT +// QOM +// QZM +// KEY +// CKEY +// SEP +// LPAREN +// RPAREN + +TEST_CASE("GraphRegex", "Lexer") { + SECTION("RandomGenerateTest") { + + std::map<gRegexTokenTypes, std::function<std::pair<std::string, std::string>()>> LexerTestMap{ + {gRegexTokenTypes::NEXT, +[](){return std::pair<std::string, std::string>("-> ","");}}, + {gRegexTokenTypes::QOM, +[](){return std::pair<std::string, std::string>("+ ","");}}, + {gRegexTokenTypes::QZM, +[](){return std::pair<std::string, std::string>("* ","");}}, + {gRegexTokenTypes::SEP, +[](){return std::pair<std::string, std::string>("; ","");}}, + + + + {gRegexTokenTypes::KEY, +[](){ + std::size_t keyLen = (std::rand() % 20)+1; + const std::string characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_"; + std::size_t randomIndex = std::rand() % characters.size(); + std::string key; + for (std::size_t i = 0; i < keyLen; ++i) { + key += characters[randomIndex]; + randomIndex = std::rand() % characters.size(); + } + + return std::pair<std::string, std::string>(key+" ",key);} + }, + + {gRegexTokenTypes::CKEY, +[](){ + std::size_t keyLen = (std::rand() % 20)+1; + const std::string characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_"; + const std::string num = "1234567890"; + + std::size_t randomIndex = std::rand() % characters.size(); + std::size_t randomNum = std::rand() % num.size(); + std::string key; + std::string idx; + + for (std::size_t i = 0; i < keyLen; ++i) { + key += characters[randomIndex]; + idx += num[randomNum]; + randomIndex = std::rand() % characters.size(); + randomNum = std::rand() % num.size(); + } + + return std::pair<std::string, std::string>(key+"#"+idx+" ",key+"#"+idx);} + }, + + {gRegexTokenTypes::LPAREN, +[](){return std::pair<std::string, std::string>("( ","");}}, + {gRegexTokenTypes::RPAREN, +[](){return std::pair<std::string, std::string>(") ","");}} + //{gRegexTokenTypes::STOP, +[](){return std::pair<std::string, std::string>("","");}} + }; + + + ////////////////// + //TEST GENERATOR + ////////////////// + const std::size_t numRandomElements = 1000; + std::vector<std::tuple<gRegexTokenTypes, std::string>> testVector; + + std::string testString; + + for (std::size_t i = 0; i < numRandomElements; ++i) { + + int randomIndex = std::rand() % LexerTestMap.size(); + // Get an iterator to the random element in the map + auto it = std::next(LexerTestMap.begin(), randomIndex); + // Access the random key and lambda value separately using structured binding + gRegexTokenTypes randomKey = it->first; + + std::function<std::pair<std::string, std::string>()> randomValue = it->second; + std::pair<std::string, std::string> result = randomValue(); + + testString += result.first; + testVector.emplace_back(randomKey, result.second); + + + } + + GraphLexer graphLexer = GraphLexer(testString); + + for (std::tuple<gRegexTokenTypes, std::string> testToken : testVector) { + gRegexTokenTypes tokenToFind = std::get<0>(testToken); + std::string lexemToFind = std::get<1>(testToken); + std::shared_ptr<ParsingToken<gRegexTokenTypes>> token = graphLexer.getNextToken(); + + + std::ostringstream errorMessage; + errorMessage << "\n we whant :"<< lexemToFind << "\n we get : "<< token->getLexeme() <<"\n"<< "on \n" << testString << " :\n " ; + + CAPTURE(errorMessage.str()); + REQUIRE(token->getLexeme() == lexemToFind); + REQUIRE(token->getType() == tokenToFind); + } + std::shared_ptr<ParsingToken<gRegexTokenTypes>> token = graphLexer.getNextToken(); + REQUIRE(token->getType() == gRegexTokenTypes::STOP); + } + + +} \ No newline at end of file diff --git a/unit_tests/graphRegex/Test_GraphParser.cpp b/unit_tests/graphRegex/Test_GraphParser.cpp new file mode 100644 index 000000000..ff74b6765 --- /dev/null +++ b/unit_tests/graphRegex/Test_GraphParser.cpp @@ -0,0 +1,80 @@ + +#include <catch2/catch_test_macros.hpp> +#include "aidge/graphRegex/GraphParser.hpp" +#include "aidge/utilsParsing/AstNode.hpp" +#include <iostream> + + +using namespace Aidge; + std::string domain(); + std::string exp() { + int randomValue = std::rand() % 3; + switch (randomValue) { + case 0: + return "A"; + case 1 : + return "A#"; + default: + return domain(); + + } + } + + std::string seq() { + int randomValue = std::rand() % 2; + switch (randomValue) { + case 0: + return exp(); + default: + return exp()+"->"+seq(); + } + } + + std::string domain() { + int randomValue = std::rand() % 2; + + switch (randomValue) { + // case 0: + // return seq(); + // case 1: + // return seq() + "->" +domain(); + + case 0: + return "("+ seq() +")*"; + default: + return "("+ seq() +")+"; + + // case 4: + // return "("+ domain() +")*" + "->" +domain(); + // default: + // return "("+ domain() +")+" + "->" +domain(); + } + } + + std::string allExpr() { + int randomValue = std::rand() % 2; + switch (randomValue) { + case 0: + return seq(); + default : + return seq()+ ";" +allExpr(); + } + } + +/* +exp : KEY(QOM | QZM)? | CKEY | domain +seq :exp (NEXT seq)* +domain : LPAREN seq RPAREN (QOM | QZM) +allExpr: seq (SEP allExpr)* +*/ +TEST_CASE("GraphParser", "Test_GraphParser") { + + SECTION("Empty") { + for (int i = 0; i < 100; ++i) { + const std::string test = allExpr(); + std::cout << test <<"\n"; + GraphParser graphParser = GraphParser(test); + std::shared_ptr<AstNode<gRegexTokenTypes>> tree = graphParser.parse(); + } + } +} \ No newline at end of file diff --git a/unit_tests/nodeTester/Test_ConditionalInterpreter.cpp b/unit_tests/nodeTester/Test_ConditionalInterpreter.cpp new file mode 100644 index 000000000..4479adfcd --- /dev/null +++ b/unit_tests/nodeTester/Test_ConditionalInterpreter.cpp @@ -0,0 +1,30 @@ + +#include <catch2/catch_test_macros.hpp> +#include "aidge/nodeTester/ConditionalInterpreter.hpp" +#include "aidge/operator/GenericOperator.hpp" + + +using namespace Aidge; + + + +TEST_CASE("ConditionalInterpreter", "ConditionalInterpreter") { + + SECTION("custom Lambda") { + + const std::string test = " !toto($) == true " ; + ConditionalInterpreter conditionalParser = ConditionalInterpreter(test); + conditionalParser.insertLambda("toto",+[](NodePtr NodeOp){return false;}); + std::shared_ptr<Node> nodeOp = GenericOperator("conv", 0, 0, 0, "Gop1"); + + bool result = conditionalParser.test(nodeOp); + REQUIRE(result == true); + } + + SECTION("syntax error") { + + const std::string test = "'A' == 'A' ,&& "; + REQUIRE_THROWS_AS( ConditionalInterpreter(test), std::runtime_error); + + } +} \ No newline at end of file diff --git a/unit_tests/nodeTester/Test_ConditionalLexer.cpp b/unit_tests/nodeTester/Test_ConditionalLexer.cpp new file mode 100644 index 000000000..a937c2722 --- /dev/null +++ b/unit_tests/nodeTester/Test_ConditionalLexer.cpp @@ -0,0 +1,144 @@ +#include <catch2/catch_test_macros.hpp> +#include "aidge/nodeTester/ConditionalLexer.hpp" +#include "aidge/utilsParsing/ParsingToken.hpp" + +#include <iostream> +#include <map> +#include <functional> + +using namespace Aidge; + +TEST_CASE("nodeTester", "Lexer") { + SECTION("RandomGenerateTest") { + + std::map<ConditionalTokenTypes, std::function<std::pair<std::string, std::string>()>> LexerTestMap{ + {ConditionalTokenTypes::AND, +[](){return std::pair<std::string, std::string>("&& ","");}}, + {ConditionalTokenTypes::OR, +[](){return std::pair<std::string, std::string>("|| ","");}}, + {ConditionalTokenTypes::EQ, +[](){return std::pair<std::string, std::string>("== ","");}}, + {ConditionalTokenTypes::NEQ, +[](){return std::pair<std::string, std::string>("!= ","");}}, + + {ConditionalTokenTypes::KEY, +[](){return std::pair<std::string, std::string>("A ","A");}}, + + {ConditionalTokenTypes::BOOL, +[](){ + std::size_t keyLen = (std::rand() % 2); + const std::vector<std::string> characters = {"true","false"}; + + return std::pair<std::string, std::string>(characters[keyLen]+" ",characters[keyLen]);} + }, + + {ConditionalTokenTypes::INTEGER, +[](){ + std::size_t keyLen = (std::rand() % 20)+1; + const std::string characters = "1234567890"; + std::size_t randomIndex = std::rand() % characters.size(); + std::string key; + for (std::size_t i = 0; i < keyLen; ++i) { + key += characters[randomIndex]; + randomIndex = std::rand() % characters.size(); + } + return std::pair<std::string, std::string>(key+" ",key);} + }, + + {ConditionalTokenTypes::FLOAT, +[](){ + std::size_t keyLen = (std::rand() % 20)+2; + const std::string characters = "1234567890"; + std::size_t randomIndex = std::rand() % characters.size(); + std::string key; + for (std::size_t i = 0; i < keyLen/2; ++i) { + key += characters[randomIndex]; + randomIndex = std::rand() % characters.size(); + } + key += "."; + for (std::size_t i = 0; i < keyLen/2; ++i) { + key += characters[randomIndex]; + randomIndex = std::rand() % characters.size(); + } + return std::pair<std::string, std::string>(key+" ",key);} + }, + + {ConditionalTokenTypes::STRING, +[](){ + std::size_t keyLen = (std::rand() % 20)+1; + const std::string characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 "; + std::size_t randomIndex = std::rand() % characters.size(); + std::string key; + for (std::size_t i = 0; i < keyLen; ++i) { + key += characters[randomIndex]; + randomIndex = std::rand() % characters.size(); + } + + return std::pair<std::string, std::string>("'"+key+"' ",key);} + }, + + {ConditionalTokenTypes::LAMBDA, +[](){ + + std::size_t keyLen = (std::rand() % 20)+1; + const std::string characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + const std::string Startchar = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + std::size_t randomIndex = std::rand() % characters.size(); + std::size_t randomStartIndex = std::rand() % Startchar.size(); + + std::string key; + key += Startchar[randomStartIndex]; + + for (std::size_t i = 0; i < keyLen; ++i) { + key += characters[randomIndex]; + randomIndex = std::rand() % characters.size(); + } + + return std::pair<std::string, std::string>(key+"( ",key);} + }, + + {ConditionalTokenTypes::ARGSEP, +[](){return std::pair<std::string, std::string>(", ","");}}, + {ConditionalTokenTypes::NODE, +[](){return std::pair<std::string, std::string>("$ ","");}}, + {ConditionalTokenTypes::LPAREN, +[](){return std::pair<std::string, std::string>("( ","");}}, + {ConditionalTokenTypes::RPAREN, +[](){return std::pair<std::string, std::string>(") ","");}} + //{ConditionalTokenTypes::STOP, +[](){return std::pair<std::string, std::string>("","");}} + }; + + + ////////////////// + //TEST GENERATOR + ////////////////// + const std::size_t numRandomElements = 100; + std::vector<std::tuple<ConditionalTokenTypes, std::string>> testVector; + + std::string testString; + + for (std::size_t i = 0; i < numRandomElements; ++i) { + + int randomIndex = std::rand() % LexerTestMap.size(); + // Get an iterator to the random element in the map + auto it = std::next(LexerTestMap.begin(), randomIndex); + // Access the random key and lambda value separately using structured binding + ConditionalTokenTypes randomKey = it->first; + + std::function<std::pair<std::string, std::string>()> randomValue = it->second; + std::pair<std::string, std::string> result = randomValue(); + + testString += result.first; + testVector.emplace_back(randomKey, result.second); + + + } + + ConditionalLexer conditionalLexer = ConditionalLexer(testString); + + for (std::tuple<ConditionalTokenTypes, std::string> testToken : testVector) { + ConditionalTokenTypes tokenToFind = std::get<0>(testToken); + std::string lexemToFind = std::get<1>(testToken); + std::shared_ptr<ParsingToken<ConditionalTokenTypes>> token = conditionalLexer.getNextToken(); + + + std::ostringstream errorMessage; + errorMessage << "\n we whant :"<< lexemToFind << "\n we get : "<< token->getLexeme() <<"\n"<< "on \n" << testString << " :\n " ; + + CAPTURE(errorMessage.str()); + REQUIRE(token->getLexeme() == lexemToFind); + REQUIRE(token->getType() == tokenToFind); + } + std::shared_ptr<ParsingToken<ConditionalTokenTypes>> token = conditionalLexer.getNextToken(); + REQUIRE(token->getType() == ConditionalTokenTypes::STOP); + } + + +} \ No newline at end of file diff --git a/unit_tests/nodeTester/Test_ConditionalParser.cpp b/unit_tests/nodeTester/Test_ConditionalParser.cpp new file mode 100644 index 000000000..56adb92b4 --- /dev/null +++ b/unit_tests/nodeTester/Test_ConditionalParser.cpp @@ -0,0 +1,75 @@ + +#include <catch2/catch_test_macros.hpp> +#include "aidge/nodeTester/ConditionalParser.hpp" +#include "aidge/utilsParsing/AstNode.hpp" + +using namespace Aidge; + + std::string gVal() { + int randomValue = std::rand() % 5; + switch (randomValue) { + case 0: + return std::to_string(std::rand() % 101); + + case 1: + return std::to_string(std::rand() % 101)+"."+std::to_string(std::rand() % 101); + + case 2: + return " 'toto' "; + case 3: + return " A "; + + case 4: + return " A(10) "; + + default: + return " true "; + + } + } + + std::string gExpr() ; + std::string gCmpr() { + int randomValue = std::rand() % 3; + switch (randomValue) { + case 0: + return gVal() + " == " +gVal(); + case 1: + return "("+ gExpr() +")"; + default: + return gVal() + " != " +gVal(); + + } + + + return gVal() + " == " +gVal(); + } + + std::string gExpr() { + std::string out = gCmpr(); + int iterations = std::rand() % 100; + for (int i = 0; i < iterations; ++i) { + int randomValue = std::rand() % 2; + switch (randomValue) { + case 0: + return out +" && " + gCmpr(); + break; + default: + return out +" || " + gCmpr(); + break; + } + } + return out; + } + + +TEST_CASE("ConditionalParser", "ConditionalParser") { + + SECTION("Empty") { + for (int i = 0; i < 100; ++i) { + const std::string test = gExpr(); + ConditionalParser conditionalParser = ConditionalParser(test); + std::shared_ptr<AstNode<ConditionalTokenTypes>> tree = conditionalParser.parse(); + } + } +} \ No newline at end of file -- GitLab