From 666e395002dc3d5ce17614f8ab2eb66a47ee5470 Mon Sep 17 00:00:00 2001 From: vl241552 <vincent.lorrain@cea.fr> Date: Tue, 25 Jul 2023 09:06:11 +0000 Subject: [PATCH] Add the new POC for nodeTest using a string expression and lambda registry --- .../include/nodeTester/ConditionalData.hpp | 137 +++++++ .../nodeTester/ConditionalInterpreter.hpp | 291 ++++++++++++++ .../include/nodeTester/ConditionalLexer.hpp | 153 ++++++++ .../include/nodeTester/ConditionalParser.hpp | 116 ++++++ .../src/nodeTester/ConditionalInterpreter.cpp | 357 ++++++++++++++++++ .../_Core/src/nodeTester/ConditionalLexer.cpp | 323 ++++++++++++++++ .../src/nodeTester/ConditionalParser.cpp | 206 ++++++++++ .../tests/Test_ConditionalInterpreter.cpp | 30 ++ aidge/_Core/tests/Test_ConditionalLexer.cpp | 142 +++++++ aidge/_Core/tests/Test_ConditionalParser.cpp | 74 ++++ 10 files changed, 1829 insertions(+) create mode 100644 aidge/_Core/include/nodeTester/ConditionalData.hpp create mode 100644 aidge/_Core/include/nodeTester/ConditionalInterpreter.hpp create mode 100644 aidge/_Core/include/nodeTester/ConditionalLexer.hpp create mode 100644 aidge/_Core/include/nodeTester/ConditionalParser.hpp create mode 100644 aidge/_Core/src/nodeTester/ConditionalInterpreter.cpp create mode 100644 aidge/_Core/src/nodeTester/ConditionalLexer.cpp create mode 100644 aidge/_Core/src/nodeTester/ConditionalParser.cpp create mode 100644 aidge/_Core/tests/Test_ConditionalInterpreter.cpp create mode 100644 aidge/_Core/tests/Test_ConditionalLexer.cpp create mode 100644 aidge/_Core/tests/Test_ConditionalParser.cpp diff --git a/aidge/_Core/include/nodeTester/ConditionalData.hpp b/aidge/_Core/include/nodeTester/ConditionalData.hpp new file mode 100644 index 00000000..7477cae9 --- /dev/null +++ b/aidge/_Core/include/nodeTester/ConditionalData.hpp @@ -0,0 +1,137 @@ + +#ifndef _AIDGE_CONDITIONAL_DATA_H_ +#define _AIDGE_CONDITIONAL_DATA_H_ + +#include <vector> +#include <string> +#include <stdexcept> //error +#include <memory> +#include <map> +namespace Aidge{ + + +// struct ConditionalTokenTypes2 { + +// enum class logicalOperators { +// AND = 1, +// OR, +// NOT +// }; + +// enum class logicalComparators { +// EQ, +// NEQ +// }; + +// enum class value { +// KEY, +// INTEGER, +// FLOAT, +// STRING, +// NODE, +// LAMBDA, +// }; + +// enum class tag { +// ARGSEP, +// LPAREN, +// RPAREN, +// STOP +// }; + +// const std::map<logicalComparators, int> precLogicalOperators{ +// {logicalOperators::NOT,3}, +// {logicalOperators::AND,2}, +// {logicalOperators::OR,1} +// }; +// }; + + + + + +///////////////////////// +// 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) { + // if (std::is_pointer<T>::value) { + // throw std::runtime_error("Pointer type not allowed."); + // } + deleteValue(); //the old one + //value = new Value<T>(newValue); + value = std::make_unique<ConditionalValue<T>>(newValue); + type = &typeid(T); + } + + /** + * @brief set a value for vetor + */ + 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/aidge/_Core/include/nodeTester/ConditionalInterpreter.hpp b/aidge/_Core/include/nodeTester/ConditionalInterpreter.hpp new file mode 100644 index 00000000..eb685f88 --- /dev/null +++ b/aidge/_Core/include/nodeTester/ConditionalInterpreter.hpp @@ -0,0 +1,291 @@ + + +#ifndef _AIDGE_CONDITIONAL_INTERPRETER_H_ +#define _AIDGE_CONDITIONAL_INTERPRETER_H_ + +#include "nodeTester/ConditionalParser.hpp" +#include "nodeTester/ConditionalData.hpp" + +#include <memory> // for shared_ptr +#include <unordered_map> +#include <functional> +#include "graph/Node.hpp" +#include <sstream> + + +namespace Aidge{ + + + +////////////////////////////// +// +///////////////////////////// + +class ConditionalRegisterFunction { + ////////////////////////// + //Safe recaste + ////////////////////////// + + + 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() ); + } + + } + + + //convert the output of f to a 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...>{}); + } + + 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 +// //////////////// +class ConditionalInterpreter +{ + private: + + + std::shared_ptr<Aidge::AstConditionalNode> mTree; + 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 + * @return bool + */ + bool test( const NodePtr nodeOp); + + /** + * @brief Interface for inserting custom lambda bool(NodePtr) functions in AST interpretation + * @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<AstConditionalNode>& 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<AstConditionalNode>& node); + /** + * @ingroup ASTnodeInterpreterF + * @brief Converted the lexeme to a float and to ConditionalData* + */ + std::vector<ConditionalData*> fStrToFloat(const std::shared_ptr<AstConditionalNode>& node); + /** + * @ingroup ASTnodeInterpreterF + * @brief Converted the lexeme to a str and to ConditionalData* + */ + std::vector<ConditionalData*> fStrToStr(const std::shared_ptr<AstConditionalNode>& 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/aidge/_Core/include/nodeTester/ConditionalLexer.hpp b/aidge/_Core/include/nodeTester/ConditionalLexer.hpp new file mode 100644 index 00000000..5e0cb0f7 --- /dev/null +++ b/aidge/_Core/include/nodeTester/ConditionalLexer.hpp @@ -0,0 +1,153 @@ +/** + * @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> + +namespace Aidge{ + + + +/** + * 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 , + INTEGER = (1 << 6) +2 , + FLOAT = (1 << 6) +3 , + STRING = (1 << 6) +4 , + BOOL = (1 << 6) +5 , + NODE = (1 << 6) +6 , //the node ptr + LAMBDA = (1 << 6) +7 , //the fuction TAG + + ARGSEP = (1<<7) +1,//sep fuction , + LPAREN = (1<<7) +2, + RPAREN = (1<<7) +3, + STOP = 0, +}; + +class ConditionalToken +{ +/** + * @brief token holder +*/ + +public: +/** + * @brief Token container + * @param type one of the token type + * @param lexeme String representing aditional information of the token + */ +ConditionalToken(const ConditionalTokenTypes type , const std::string lexeme ); +/** + * @brief get the lexeme + * + * @return std::string + */ +const std::string getLexeme(void); + +/** + * @brief get the token type + * + * @return ConditionalTokenTypes + */ +const ConditionalTokenTypes getType(void); + + +std::shared_ptr<Aidge::ConditionalToken> copy(); + + +std::ostringstream rep(void); +private: + +const std::string mLexeme; +const ConditionalTokenTypes mType; + +}; + + + +class ConditionalLexer +{ + +public: +ConditionalLexer( const std::string ConditionalExpressions ); + +/** + * @brief Get the next token on the ConditionalExpressions + * + * @return ConditionalToken + */ +std::shared_ptr<ConditionalToken> 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/aidge/_Core/include/nodeTester/ConditionalParser.hpp b/aidge/_Core/include/nodeTester/ConditionalParser.hpp new file mode 100644 index 00000000..c744b1b5 --- /dev/null +++ b/aidge/_Core/include/nodeTester/ConditionalParser.hpp @@ -0,0 +1,116 @@ + + + +#ifndef _AIDGE_CONDITIONAL_PARSER_H_ +#define _AIDGE_CONDITIONAL_PARSER_H_ + +#include "nodeTester/ConditionalLexer.hpp" +#include <memory> // for shared_ptr + +namespace Aidge{ + +const std::map<ConditionalTokenTypes, std::size_t> ConditionalPrec{ + {ConditionalTokenTypes::AND,2}, + {ConditionalTokenTypes::OR,1} +}; + + +class AstConditionalNode; // Forward declaration +using ASTNodeCh = std::vector<std::shared_ptr<AstConditionalNode>>; + +class AstConditionalNode: public std::enable_shared_from_this<AstConditionalNode> +{ + public: + AstConditionalNode(std::shared_ptr<ConditionalToken> token,ASTNodeCh child ={}); + + std::string getValue() const; + ConditionalTokenTypes getType() const; + const ASTNodeCh& getChilds() const ; + bool isLeaf() const ; + std::size_t nbChild() const; + private: + const std::shared_ptr<ConditionalToken> mToken; + const ASTNodeCh mChild; +}; + + +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<AstConditionalNode> 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<AstConditionalNode> constructAstVal(void); + /** + * @ingroup ParsingFunctions + * @brief Function of grammar rules for comparison : val (EQ|NEQ) val | LPAREN expr RPAREN + * @return AST node + */ + std::shared_ptr<AstConditionalNode> 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<AstConditionalNode> constructAstLambda(void); + /** + * @ingroup ParsingFunctions + * @brief Function of grammar rules for a expresion : cmpr ((AND | OR) cmpr)* + * @return AST node + */ + std::shared_ptr<AstConditionalNode> constructAstExpr(std::size_t precLimit = 0); + + + /** + * @brief The actual token in the parce + */ + std::shared_ptr<ConditionalToken> mCurrentToken; + /** + * @brief The lexem use + */ + ConditionalLexer mLexer; + +}; + + +} + +#endif //_AIDGE_CONDITIONAL_PARSER_H_ diff --git a/aidge/_Core/src/nodeTester/ConditionalInterpreter.cpp b/aidge/_Core/src/nodeTester/ConditionalInterpreter.cpp new file mode 100644 index 00000000..15c1e456 --- /dev/null +++ b/aidge/_Core/src/nodeTester/ConditionalInterpreter.cpp @@ -0,0 +1,357 @@ +#include "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<AstConditionalNode> 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<AstConditionalNode>& 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<AstConditionalNode>& 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<AstConditionalNode>& 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<AstConditionalNode>& 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/aidge/_Core/src/nodeTester/ConditionalLexer.cpp b/aidge/_Core/src/nodeTester/ConditionalLexer.cpp new file mode 100644 index 00000000..52406947 --- /dev/null +++ b/aidge/_Core/src/nodeTester/ConditionalLexer.cpp @@ -0,0 +1,323 @@ +#include "nodeTester/ConditionalLexer.hpp" + +using namespace Aidge; + +////////////////// +//ConditionalToken +////////////////// +ConditionalToken::ConditionalToken(const ConditionalTokenTypes type , const std::string lexeme ): +mLexeme(lexeme), +mType(type) +{ + +} + +const std::string ConditionalToken::getLexeme(void){ + return mLexeme; +} + +const ConditionalTokenTypes ConditionalToken::getType(void){ + return mType; +} + +std::shared_ptr<Aidge::ConditionalToken> ConditionalToken::copy(){ + auto newToken = std::make_shared<ConditionalToken>(mType,mLexeme); + return newToken; +} + +std::ostringstream ConditionalToken::rep(void){ + std::ostringstream out; + + switch (mType){ + case ConditionalTokenTypes::AND: + out << "AND" ; + break; + case ConditionalTokenTypes::OR: + out << "OR" ; + break; + case ConditionalTokenTypes::EQ: + out << "EQ" ; + break; + case ConditionalTokenTypes::NEQ: + out << "NEQ" ; + break; + + case ConditionalTokenTypes::KEY: + out << "KEY" ; + break; + case ConditionalTokenTypes::INTEGER: + out << "INTEGER" ; + break; + case ConditionalTokenTypes::FLOAT: + out << "FLOAT" ; + break; + case ConditionalTokenTypes::STRING: + out << "STRING" ; + break; + + case ConditionalTokenTypes::LAMBDA: + out << "LAMBDA" ; + break; + case ConditionalTokenTypes::ARGSEP: + out << "ARGSEP" ; + break; + case ConditionalTokenTypes::NODE: + out << "NODE" ; + break; + + case ConditionalTokenTypes::LPAREN: + out << "LPAREN" ; + break; + case ConditionalTokenTypes::RPAREN: + out << "RPAREN" ; + break; + case ConditionalTokenTypes::STOP: + out << "STOP" ; + break; + default: + out << "???" ; + } + out << " (" << mLexeme <<")" << "\n"; + + return out; +} +////////////////// +//ConditionalLexer +////////////////// + + +ConditionalLexer::ConditionalLexer( const std::string ConditionalExpressions): +mConditionalExpressions(ConditionalExpressions) +{ + mPosition = 0; +} + +std::shared_ptr<ConditionalToken> 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<ConditionalToken>(ConditionalTokenTypes::AND,""); + } + else if (std::regex_match(currentChars,std::regex("\\|\\|")))// the OR TOKEN + { + mPosition++; + return std::make_shared<ConditionalToken>(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<ConditionalToken>(ConditionalTokenTypes::NEQ,""); + }else{ + return std::make_shared<ConditionalToken>(ConditionalTokenTypes::NOT,""); + } + } + //a not at the end not ok but it's the parseur work + return std::make_shared<ConditionalToken>(ConditionalTokenTypes::NOT,""); + } + else if (std::regex_match(currentChars,std::regex("==")))// the EQ TOKEN + { + mPosition++; + return std::make_shared<ConditionalToken>(ConditionalTokenTypes::EQ,""); + } + // else if (std::regex_match(currentChars,std::regex("!=")))// the NEQ TOKEN + // { + // mPosition++; + // return std::make_shared<ConditionalToken>(ConditionalTokenTypes::NEQ,""); + // } + else if (std::regex_match(currentChars,std::regex("\\(")))// the LPAREN TOKEN + { + mPosition++; + return std::make_shared<ConditionalToken>(ConditionalTokenTypes::LPAREN,""); + } + else if (std::regex_match(currentChars,std::regex("\\)")))// the RPAREN TOKEN + { + mPosition++; + return std::make_shared<ConditionalToken>(ConditionalTokenTypes::RPAREN,""); + } + else if (std::regex_match(currentChars,std::regex(",")))// the RPAREN TOKEN + { + mPosition++; + return std::make_shared<ConditionalToken>(ConditionalTokenTypes::ARGSEP,""); + } + else if (std::regex_match(currentChars,std::regex("\\$")))// the ACTNode TOKEN + { + mPosition++; + return std::make_shared<ConditionalToken>(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++; + 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<ConditionalToken>(ConditionalTokenTypes::BOOL,currentChars); + + } else if (isLambda){ + currentChars.pop_back();//pop the ( of the lambda + return std::make_shared<ConditionalToken>(ConditionalTokenTypes::LAMBDA,currentChars); + } else{ + return std::make_shared<ConditionalToken>(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++; + 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<ConditionalToken>(ConditionalTokenTypes::FLOAT,currentChars); + }else{ + return std::make_shared<ConditionalToken>(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++; + 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<ConditionalToken>(ConditionalTokenTypes::STRING,currentChars); + + } + + //Array TODO + + mPosition++; + } + + //no more to find no one match the currentChars + if (currentChars.empty()) { + return std::make_shared<ConditionalToken>(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/aidge/_Core/src/nodeTester/ConditionalParser.cpp b/aidge/_Core/src/nodeTester/ConditionalParser.cpp new file mode 100644 index 00000000..ca5dab51 --- /dev/null +++ b/aidge/_Core/src/nodeTester/ConditionalParser.cpp @@ -0,0 +1,206 @@ + +#include "nodeTester/ConditionalParser.hpp" + +using namespace Aidge; + + +AstConditionalNode::AstConditionalNode(std::shared_ptr<ConditionalToken> token,ASTNodeCh child ) +:mToken(token),mChild(child){} + +std::string AstConditionalNode::getValue() const{ + return mToken->getLexeme(); +} + +ConditionalTokenTypes AstConditionalNode::getType() const{ + return mToken->getType(); +} + +bool AstConditionalNode::isLeaf() const { + return mChild.size() == 0; +} + +const ASTNodeCh& AstConditionalNode::getChilds() const { + return mChild; +} + +std::size_t AstConditionalNode::nbChild() const{ + return mChild.size(); +} + +////////////////////////////// +//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<AstConditionalNode> ConditionalParser::constructAstVal(void){ + /* + val : (KEY|INTEGER|FOAT|STRING|LAMBDA) + */ + std::shared_ptr<Aidge::ConditionalToken> token = mCurrentToken->copy(); + + if (token->getType() == ConditionalTokenTypes::KEY){ + ackToken(ConditionalTokenTypes::KEY); + return std::make_shared<AstConditionalNode>(token); + } + else if(token->getType() == ConditionalTokenTypes::INTEGER){ + ackToken(ConditionalTokenTypes::INTEGER); + return std::make_shared<AstConditionalNode>(token); + } + else if(token->getType() == ConditionalTokenTypes::FLOAT){ + ackToken(ConditionalTokenTypes::FLOAT); + return std::make_shared<AstConditionalNode>(token); + } + else if(token->getType() == ConditionalTokenTypes::BOOL){ + ackToken(ConditionalTokenTypes::BOOL); + return std::make_shared<AstConditionalNode>(token); + } + else if(token->getType() == ConditionalTokenTypes::STRING){ + ackToken(ConditionalTokenTypes::STRING); + return std::make_shared<AstConditionalNode>(token); + + }else if(token->getType() == ConditionalTokenTypes::NODE){ + ackToken(ConditionalTokenTypes::NODE); + return std::make_shared<AstConditionalNode>(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<AstConditionalNode> ConditionalParser::constructAstLambda(void){ + /* + AstLambda : LAMBDA val (ARGSEP val)* RPAREN + */ + std::shared_ptr<Aidge::ConditionalToken> 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<AstConditionalNode>(tokenLdb,paramLambda); +} + +std::shared_ptr<AstConditionalNode> ConditionalParser::constructAstCmpr(void){ + /* + cmpr : val (EQ|NEQ) val | LPAREN expr RPAREN + NOT ir ? + */ + std::shared_ptr<Aidge::ConditionalToken> 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<AstConditionalNode> node = constructAstExpr(); + ackToken(ConditionalTokenTypes::RPAREN); + return node; + }else{ + + std::shared_ptr<AstConditionalNode> node = constructAstVal(); + token = mCurrentToken->copy(); + if (token->getType() == ConditionalTokenTypes::EQ){ + ackToken(ConditionalTokenTypes::EQ); + return std::make_shared<AstConditionalNode>(token,ASTNodeCh{node,constructAstVal()}); + }else if(token->getType() == ConditionalTokenTypes::NEQ){ + ackToken(ConditionalTokenTypes::NEQ); + return std::make_shared<AstConditionalNode>(token,ASTNodeCh{node,constructAstVal()}); + } + + } +} + +std::shared_ptr<AstConditionalNode> 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<AstConditionalNode> left; + std::shared_ptr<Aidge::ConditionalToken> token = mCurrentToken->copy(); + + if (mCurrentToken->getType() == ConditionalTokenTypes::NOT ){ + ackToken(ConditionalTokenTypes::NOT ); + left= std::make_shared<AstConditionalNode>(token,ASTNodeCh{constructAstCmpr()}); + }else{ + left= constructAstCmpr(); + } + + //pratt + while (mCurrentToken->getType() != ConditionalTokenTypes::STOP ) //security + { + std::shared_ptr<Aidge::ConditionalToken> 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<AstConditionalNode> right = constructAstExpr(prec); + + //i'm not sur what append to newNode + //std::shared_ptr<AstConditionalNode> newNode = std::make_shared<AstConditionalNode>(token,ASTNodeCh{left,constructAstCmpr()}); + std::shared_ptr<AstConditionalNode> newNode = std::make_shared<AstConditionalNode>(token,ASTNodeCh{left,right}); + left = newNode; + } + return left; +} + + +std::shared_ptr<AstConditionalNode> 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<AstConditionalNode> astTree = constructAstExpr(); + + rstParser(); + return astTree; +} \ No newline at end of file diff --git a/aidge/_Core/tests/Test_ConditionalInterpreter.cpp b/aidge/_Core/tests/Test_ConditionalInterpreter.cpp new file mode 100644 index 00000000..ecf8adc0 --- /dev/null +++ b/aidge/_Core/tests/Test_ConditionalInterpreter.cpp @@ -0,0 +1,30 @@ + +#include <catch2/catch_test_macros.hpp> +#include "nodeTester/ConditionalInterpreter.hpp" +#include "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/aidge/_Core/tests/Test_ConditionalLexer.cpp b/aidge/_Core/tests/Test_ConditionalLexer.cpp new file mode 100644 index 00000000..b14500d2 --- /dev/null +++ b/aidge/_Core/tests/Test_ConditionalLexer.cpp @@ -0,0 +1,142 @@ +#include <catch2/catch_test_macros.hpp> +#include "nodeTester/ConditionalLexer.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<Aidge::ConditionalToken> 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<Aidge::ConditionalToken> token = conditionalLexer.getNextToken(); + REQUIRE(token->getType() == ConditionalTokenTypes::STOP); + } + + +} \ No newline at end of file diff --git a/aidge/_Core/tests/Test_ConditionalParser.cpp b/aidge/_Core/tests/Test_ConditionalParser.cpp new file mode 100644 index 00000000..3176e649 --- /dev/null +++ b/aidge/_Core/tests/Test_ConditionalParser.cpp @@ -0,0 +1,74 @@ + +#include <catch2/catch_test_macros.hpp> +#include "nodeTester/ConditionalParser.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 < 1000; ++i) { + const std::string test = gExpr(); + ConditionalParser conditionalParser = ConditionalParser(test); + std::shared_ptr<Aidge::AstConditionalNode> tree = conditionalParser.parse(); + } + } +} \ No newline at end of file -- GitLab