| //+------------------------------------------------------------------+
|
| //| ruleparser.mqh |
|
| //| Copyright 2000-2025, MetaQuotes Ltd. |
|
| //| https://www.mql5.com |
|
| //+------------------------------------------------------------------+
|
| //| Implementation of Fuzzy library in MetaQuotes Language 5 |
|
| //| |
|
| //| The features of the library include: |
|
| //| - Create Mamdani fuzzy model |
|
| //| - Create Sugeno fuzzy model |
|
| //| - Normal membership function |
|
| //| - Triangular membership function |
|
| //| - Trapezoidal membership function |
|
| //| - Constant membership function |
|
| //| - Defuzzification method of center of gravity (COG) |
|
| //| - Defuzzification method of bisector of area (BOA) |
|
| //| - Defuzzification method of mean of maxima (MeOM) |
|
| //| |
|
| //| This file is free software; you can redistribute it and/or |
|
| //| modify it under the terms of the GNU General Public License as |
|
| //| published by the Free Software Foundation (www.fsf.org); either |
|
| //| version 2 of the License, or (at your option) any later version. |
|
| //| |
|
| //| This program is distributed in the hope that it will be useful, |
|
| //| but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| //| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| //| GNU General Public License for more details. |
|
| //+------------------------------------------------------------------+
|
| #include <Arrays\List.mqh>
|
| #include <Arrays\ArrayObj.mqh>
|
| #include "Dictionary.mqh"
|
| #include "FuzzyRule.mqh"
|
| #include "Helper.mqh"
|
| #include "InferenceMethod.mqh"
|
| #include "SugenoVariable.mqh"
|
| //+------------------------------------------------------------------+
|
| //| Purpose: Analysis of the fuzzy rules |
|
| //+------------------------------------------------------------------+
|
| //+------------------------------------------------------------------+
|
| //| Base class for all expression |
|
| //+------------------------------------------------------------------+
|
| class IExpression : public CObject
|
| {
|
| public:
|
| //--- method gets the text of expression
|
| virtual string Text(void)=NULL;
|
| //--- method to check type
|
| virtual bool IsTypeOf(EnLexem type) { return(type==TYPE_CLASS_IExpression); }
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Class for creating lexem |
|
| //+------------------------------------------------------------------+
|
| class CLexem : public IExpression
|
| {
|
| public:
|
| //--- method gets the text of expression
|
| virtual string Text(void)=NULL;
|
| //--- method to check type
|
| virtual bool IsTypeOf(EnLexem type) { return(type==TYPE_CLASS_Lexem); }
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Class condition expression |
|
| //+------------------------------------------------------------------+
|
| class CConditionExpression : public IExpression
|
| {
|
| private:
|
| CArrayObj *m_expressions; // List of expression
|
| CFuzzyCondition *m_condition; // Fuzzy condition
|
|
|
| public:
|
| CConditionExpression(CArrayObj *expressions,CFuzzyCondition *condition);
|
| ~CConditionExpression(void);
|
| //--- methods gets or sets array of expression
|
| CArrayObj *Expressions(void) { return (m_expressions); }
|
| void Expressions(CArrayObj *value) { m_expressions=value; }
|
| //--- methods gets or sets fuzzy condition
|
| CFuzzyCondition *Condition(void) { return m_condition; }
|
| void Condition(CFuzzyCondition *value) { m_condition=value; }
|
| //--- method gets the text of expressions
|
| string Text(void);
|
| //--- method to check type
|
| virtual bool IsTypeOf(EnLexem type) { return(type==TYPE_CLASS_ConditionExpression); }
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Constructor with parameters |
|
| //+------------------------------------------------------------------+
|
| CConditionExpression::CConditionExpression(CArrayObj *expressions,CFuzzyCondition *condition)
|
| {
|
| m_expressions=expressions;
|
| m_condition=condition;
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Destructor |
|
| //+------------------------------------------------------------------+
|
| CConditionExpression::~CConditionExpression(void)
|
| {
|
|
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Convert all expressions to text(string) |
|
| //+------------------------------------------------------------------+
|
| string CConditionExpression::Text(void)
|
| {
|
| string sb;
|
| for(int i=0; i<m_expressions.Total(); i++)
|
| {
|
| IExpression *ex=m_expressions.At(i);
|
| string str=ex.Text();
|
| StringAdd(sb,str);
|
| }
|
| //--- return text of all expressions
|
| return ((string)sb);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Class keyword lexem |
|
| //+------------------------------------------------------------------+
|
| class CKeywordLexem : public CLexem
|
| {
|
| private:
|
| string m_name; // Name of keyword lexem
|
|
|
| public:
|
| CKeywordLexem(const string name);
|
| ~CKeywordLexem(void);
|
| //--- method gets the text of lexem
|
| string Text(void) { return(m_name); }
|
| //--- method to check type
|
| virtual bool IsTypeOf(EnLexem type) { return(type==TYPE_CLASS_KeywordLexem); }
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Constructor with parameters |
|
| //+------------------------------------------------------------------+
|
| CKeywordLexem::CKeywordLexem(const string name)
|
| {
|
| m_name=name;
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Destructor |
|
| //+------------------------------------------------------------------+
|
| CKeywordLexem::~CKeywordLexem(void)
|
| {
|
|
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Class for handling variable lexem |
|
| //+------------------------------------------------------------------+
|
| class CVarLexem : public CLexem
|
| {
|
| private:
|
| INamedVariable *m_var; // Variable type
|
| bool m_input; // Confirmation of the variable
|
|
|
| public:
|
| CVarLexem(INamedVariable *var,bool in);
|
| ~CVarLexem(void);
|
| //--- methods gets or sets the varriable
|
| INamedVariable *Var(void) { return (m_var); }
|
| void Var(INamedVariable *var) { m_var=var; }
|
| //--- method gets the text of lexem
|
| string Text(void) { return(m_var.Name()); }
|
| //--- methods gets or sets mark is input varriable
|
| bool Input(void) { return (m_input); }
|
| void Input(bool value) { m_input=value; }
|
| //--- method to check type
|
| virtual bool IsTypeOf(EnLexem type) { return(type==TYPE_CLASS_VarLexem); }
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Constructor with parameters |
|
| //+------------------------------------------------------------------+
|
| CVarLexem::CVarLexem(INamedVariable *var,bool in)
|
| {
|
| m_var=var;
|
| m_input=in;
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Destructor |
|
| //+------------------------------------------------------------------+
|
| CVarLexem::~CVarLexem(void)
|
| {
|
|
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Class alternative lexem |
|
| //+------------------------------------------------------------------+
|
| class IAltLexem : public CLexem
|
| {
|
| public:
|
| //--- methods gets or sets alternative lexem
|
| virtual IAltLexem *Alternative(void)=NULL;
|
| virtual void Alternative(IAltLexem *value)=NULL;
|
| //--- method to check type
|
| virtual bool IsTypeOf(EnLexem type) { return(type==TYPE_CLASS_AltLexem); }
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Class term lexem |
|
| //+------------------------------------------------------------------+
|
| class CTermLexem : public IAltLexem
|
| {
|
| private:
|
| INamedValue *m_term; // Value type
|
| IAltLexem *m_alternative; // Alternative lexem
|
| bool m_input;
|
|
|
| public:
|
| CTermLexem(INamedValue *term,bool in);
|
| ~CTermLexem(void);
|
| //--- methods gets or sets the term
|
| INamedValue *Term(void) { return(m_term); }
|
| void Term(INamedValue *value) { m_term=value; }
|
| //--- method gets the text of lexem
|
| string Text(void) { return m_term.Name(); }
|
| //--- methods gets or sets the alternative lexem
|
| IAltLexem *Alternative(void) { return(m_alternative); }
|
| void Alternative(IAltLexem *value) { m_alternative=value; }
|
| //--- method to check type
|
| virtual bool IsTypeOf(EnLexem type) { return(type==TYPE_CLASS_TermLexem); }
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Constructor with parameters |
|
| //+------------------------------------------------------------------+
|
| CTermLexem::CTermLexem(INamedValue *term,bool in)
|
| {
|
| m_term=term;
|
| m_input=in;
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Destructor |
|
| //+------------------------------------------------------------------+
|
| CTermLexem::~CTermLexem(void)
|
| {
|
| if(CheckPointer(m_alternative)==POINTER_DYNAMIC)
|
| delete m_alternative;
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Class responsible for parsing |
|
| //+------------------------------------------------------------------+
|
| class CRuleParser : public INamedVariable
|
| {
|
| public:
|
| //--- methods of parsing
|
| static IParsableRule *Parse(const string rule,IParsableRule *emptyRule,CList *in,CList *out);
|
| private:
|
| static CList *BuildLexemsList(CList *in,CList *out);
|
| static void BuildLexemsList(INamedVariable *var,bool in,CList *lexems);
|
| static CArrayObj *ParseLexems(const string rule,CList *lexems);
|
| static CArrayObj *ExtractSingleCondidtions(CArrayObj *conditionExpression,CList *in,CList *lexems);
|
| static CConditions *ParseConditions(CArrayObj *conditionExpression,CList *in,CList *lexems);
|
| static int FindPairBracket(CArrayObj *expressions,CList *lexems);
|
| static ICondition *ParseConditionsRecurse(CArrayObj *expressions,CList *lexems);
|
| static CSingleCondition *ParseConclusion(CArrayObj *conditionExpression,CList *out,CList *lexems);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Parse |
|
| //+------------------------------------------------------------------+
|
| static IParsableRule *CRuleParser::Parse(const string rule,IParsableRule *emptyRule,CList *in,CList *out)
|
| {
|
| if(StringLen(rule)==0)
|
| {
|
| Print("Rule cannot be empty.");
|
| //--- return
|
| return (NULL);
|
| }
|
| //--- Surround brakes with spaces, remove double spaces
|
| string sb=NULL;
|
| char ch;
|
| for(int i=0; i<StringLen(rule); i++)
|
| {
|
| ch=(char)StringGetCharacter(rule,i);
|
| if((ch=='(') || (ch==')'))
|
| {
|
| if(StringLen(sb)>0 && StringGetCharacter(sb,StringLen(sb)-1)==' ')
|
| {
|
| //--- Do not duplicate spaces
|
| }
|
| else
|
| {
|
| sb+=CharToString(' ');
|
| }
|
| sb+=CharToString(ch);
|
| sb+=CharToString(' ');
|
| }
|
| else
|
| {
|
| if(ch==' ' && StringLen(sb)>0 && StringGetCharacter(sb,StringLen(sb)-1)==' ')
|
| {
|
| // Do not duplicate spaces
|
| }
|
| else
|
| {
|
| sb+=CharToString(ch);
|
| }
|
| }
|
| }
|
| //--- Remove spaces
|
| //+------------------------------------------------------------------+
|
| //| Use conditional compilation to determine |
|
| //| the type of program MQL4 or MQL5 because they have |
|
| //| different realization of StringTrimRight() and StringTrimLeft() |
|
| //+------------------------------------------------------------------+
|
| #ifdef __MQL5__
|
| StringTrimRight(sb);
|
| StringTrimLeft(sb);
|
| #else
|
| #ifdef __MQL4__
|
| sb=StringTrimRight(sb);
|
| sb=StringTrimLeft(sb);
|
| #endif
|
| #endif
|
| string prepRule=sb;
|
| //--- Build lexems dictionary
|
| CList *lexemsDict=BuildLexemsList(in,out);
|
| //--- At first we parse lexems
|
| CArrayObj *expressions=ParseLexems(prepRule,lexemsDict);
|
| if(expressions.Total()==0)
|
| {
|
| Print("No valid identifiers found.");
|
| //--- return
|
| return (NULL);
|
| }
|
| //--- Find condition & conclusion parts part
|
| CDictionary_String_Obj *p_so;
|
| for(int i=0; i<lexemsDict.Total(); i++)
|
| {
|
| p_so=lexemsDict.GetNodeAtIndex(i);
|
| if(p_so.Key()=="if")
|
| {
|
| break;
|
| }
|
| }
|
| if(expressions.At(0)!=p_so.Value())
|
| {
|
| Print("'if' should be the first identifier.");
|
| //--- return
|
| return (NULL);
|
| }
|
| int thenIndex=-1;
|
| for(int i=1; i<expressions.Total(); i++)
|
| {
|
| for(int j=0; j<lexemsDict.Total(); j++)
|
| {
|
| p_so=lexemsDict.GetNodeAtIndex(j);
|
| if(p_so.Key()=="then"){break;}
|
| }
|
| if(expressions.At(i)==p_so.Value())
|
| {
|
| thenIndex=i;
|
| break;
|
| }
|
| }
|
| if(thenIndex==-1)
|
| {
|
| Print("'then' identifier not found.");
|
| //--- return
|
| return (NULL);
|
| }
|
| int conditionLen=thenIndex-1;
|
| if(conditionLen<1)
|
| {
|
| Print("Condition part of the rule not found.");
|
| //--- return
|
| return (NULL);
|
| }
|
| int conclusionLen=expressions.Total()-thenIndex-1;
|
| if(conclusionLen<1)
|
| {
|
| Print("Conclusion part of the rule not found.");
|
| //--- return
|
| return (NULL);
|
| }
|
| CArrayObj *conditionExpressions=GetRange(expressions,1,conditionLen);
|
| CArrayObj *conclusionExpressions=GetRange(expressions,thenIndex+1,conclusionLen);
|
| CConditions *conditions=ParseConditions(conditionExpressions,in,lexemsDict);
|
| delete emptyRule.Condition();
|
| emptyRule.Condition(conditions);
|
| CSingleCondition *conclusion=ParseConclusion(conclusionExpressions,out,lexemsDict);
|
| if(emptyRule.IsTypeOf(TYPE_CLASS_MamdaniFuzzyRule))
|
| {
|
| CMamdaniFuzzyRule *mamdani_rule=emptyRule;
|
| mamdani_rule.Conclusion(conclusion);
|
| emptyRule=mamdani_rule;
|
| }
|
| if(emptyRule.IsTypeOf(TYPE_CLASS_SugenoFuzzyRule))
|
| {
|
| CSugenoFuzzyRule *sugeno_rule=emptyRule;
|
| sugeno_rule.Conclusion(conclusion);
|
| emptyRule=sugeno_rule;
|
| }
|
| //--- return empty rule
|
| conditionExpressions.FreeMode(false);
|
| delete conditionExpressions;
|
| conclusionExpressions.FreeMode(false);
|
| delete conclusionExpressions;
|
| expressions.FreeMode(false);
|
| delete expressions;
|
| delete lexemsDict;
|
| return (emptyRule);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Build lexems list |
|
| //+------------------------------------------------------------------+
|
| static CList *CRuleParser::BuildLexemsList(CList *in,CList *out)
|
| {
|
| CList *lexems=new CList();
|
| for(int i=0;i<ArraySize(KEYWORDS); i++)
|
| {
|
| string keyword=KEYWORDS[i];
|
| CKeywordLexem *keywordLexem=new CKeywordLexem(keyword);
|
| CDictionary_String_Obj *p_so=new CDictionary_String_Obj;
|
| p_so.SetAll(keywordLexem.Text(),keywordLexem);
|
| lexems.Add(p_so);
|
| }
|
| for(int i=0; i<in.Total(); i++)
|
| {
|
| INamedVariable *var=in.GetNodeAtIndex(i);
|
| BuildLexemsList(var,true,lexems);
|
| }
|
| for(int i=0; i<out.Total(); i++)
|
| {
|
| INamedVariable *var=out.GetNodeAtIndex(i);
|
| BuildLexemsList(var,false,lexems);
|
| }
|
| //--- return lexems
|
| return (lexems);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Build lexems list |
|
| //+------------------------------------------------------------------+
|
| static void CRuleParser::BuildLexemsList(INamedVariable *var,bool in,CList *lexems)
|
| {
|
| CVarLexem *varLexem=new CVarLexem(var,in);
|
| CDictionary_String_Obj *p_so_var=new CDictionary_String_Obj;
|
| p_so_var.SetAll(varLexem.Text(),varLexem);
|
| lexems.Add(p_so_var);
|
| for(int i=0; i<var.Values().Total(); i++)
|
| {
|
| INamedValue *term=var.Values().GetNodeAtIndex(i);
|
| CTermLexem *termLexem=new CTermLexem(term,in);
|
| CLexem *foundLexem;
|
| bool contain=false;
|
| for(int j=0; j<lexems.Total(); j++)
|
| {
|
| CDictionary_String_Obj *p_so=lexems.GetNodeAtIndex(j);
|
| foundLexem=p_so.Value();
|
| if(p_so.Key()==termLexem.Text())
|
| {
|
| contain=true;
|
| break;
|
| }
|
| }
|
| if(contain==false)
|
| {
|
| CDictionary_String_Obj *p_so_val=new CDictionary_String_Obj;
|
| p_so_val.SetAll(termLexem.Text(),termLexem);
|
| lexems.Add(p_so_val);
|
| }
|
| else
|
| {
|
| if(foundLexem.IsTypeOf(TYPE_CLASS_TermLexem))
|
| {
|
| //--- There can be more than one terms with the same name.
|
| //--- TODO: But only if they belong to defferent variables.
|
| CTermLexem *foundTermLexem=foundLexem;
|
| while(foundTermLexem.Alternative()!=NULL)
|
| {
|
| foundTermLexem=foundTermLexem.Alternative();
|
| }
|
| foundTermLexem.Alternative(termLexem);
|
| }
|
| else
|
| {
|
| //--- Only terms of different vatiables can have the same name
|
| Print("Found more than one lexems with the same name");
|
| }
|
| }
|
| }
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Parse lexems |
|
| //+------------------------------------------------------------------+
|
| static CArrayObj *CRuleParser::ParseLexems(const string rule,CList *lexems)
|
| {
|
| CArrayObj *expressions=new CArrayObj;
|
| string words[];
|
| StringSplit(rule,' ',words);
|
| int index=0;
|
| for(int i=0; i<ArraySize(words); i++)
|
| {
|
| string word=words[i];
|
| CObject *lexem=NULL;
|
| if(TryGetValue(lexems,word,lexem))
|
| {
|
| expressions.Add(lexem);
|
| }
|
| else
|
| {
|
| Print(StringFormat("Unknown identifier : %s",word));
|
| //--- return NULL
|
| return(NULL);
|
| }
|
| }
|
| //--- return expressions
|
| return(expressions);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Extract single condidtions |
|
| //+------------------------------------------------------------------+
|
| static CArrayObj *CRuleParser::ExtractSingleCondidtions(CArrayObj *conditionExpression,CList *in,CList *lexems)
|
| {
|
| CArrayObj *copyExpressions=conditionExpression;
|
| CArrayObj *expressions=new CArrayObj;
|
| int index=0;
|
| while(copyExpressions.Total()-index>0)
|
| {
|
| IExpression *expr0=copyExpressions.At(index);
|
| if(expr0.IsTypeOf(TYPE_CLASS_VarLexem))
|
| {
|
| //--- Parse variable
|
| CVarLexem *varLexem=copyExpressions.At(index);
|
| if(copyExpressions.Total()<3)
|
| {
|
| Print(StringFormat("Condition strated with '%s' is incorrect.",varLexem.Text()));
|
| //--- return
|
| return(NULL);
|
| }
|
| if(varLexem.Input()==false)
|
| {
|
| Print("The variable in condition part must be an input variable.");
|
| //--- return
|
| return(NULL);
|
| }
|
| //--- Parse 'is' lexem
|
| CLexem *exprIs=copyExpressions.At(index+1);
|
| CDictionary_String_Obj *p_so;
|
| for(int i=0;i<lexems.Total();i++)
|
| {
|
| p_so=lexems.GetNodeAtIndex(i);
|
| if(p_so.Key()=="is")
|
| {
|
| break;
|
| }
|
| }
|
| if(exprIs!=p_so.Value())
|
| {
|
| Print(StringFormat("'is' keyword must go after '%s' identifier.",varLexem.Text()));
|
| //--- return
|
| return(NULL);
|
| }
|
| //--- Parse 'not' lexem (if exists)
|
| int cur=2;
|
| bool not=false;
|
| for(int i=0;i<lexems.Total();i++)
|
| {
|
| p_so=lexems.GetNodeAtIndex(i);
|
| if(p_so.Key()=="not")
|
| {
|
| break;
|
| }
|
| }
|
| if(copyExpressions.At(cur+index)==p_so.Value())
|
| {
|
| not=true;
|
| cur++;
|
| if(copyExpressions.Total()-index<=cur)
|
| {
|
| Print("Error at 'not' in condition part of the rule.");
|
| //--- return
|
| return(NULL);
|
| }
|
| }
|
| //--- "slightly"
|
| //--- "somewhat"
|
| //--- "very"
|
| //--- "extremely"
|
| //--- Parse hedge modifier (if exists)
|
| HedgeType hedge=None;
|
| for(int i=0;i<lexems.Total();i++)
|
| {
|
| p_so=lexems.GetNodeAtIndex(i);
|
| if(p_so.Key()=="slightly")
|
| {
|
| if(copyExpressions.At(cur+index)==p_so.Value())
|
| {
|
| hedge=Slightly;
|
| break;
|
| }
|
| }
|
| else if(p_so.Key()=="somewhat")
|
| {
|
| if(copyExpressions.At(cur+index)==p_so.Value())
|
| {
|
| hedge=Somewhat;
|
| break;
|
| }
|
| }
|
| else if(p_so.Key()=="very")
|
| {
|
| if(copyExpressions.At(cur+index)==p_so.Value())
|
| {
|
| hedge=Very;
|
| break;
|
| }
|
| }
|
| else if(p_so.Key()=="extremely")
|
| {
|
| if(copyExpressions.At(cur+index)==p_so.Value())
|
| {
|
| hedge=Extremely;
|
| break;
|
| }
|
| }
|
| }
|
| if(hedge!=None)
|
| {
|
| cur++;
|
| if(copyExpressions.Total()-index<=cur)
|
| {
|
| Print("Error in condition part of the rule.");
|
| //--- return
|
| return(NULL);
|
| }
|
| }
|
| //--- Parse term
|
| CLexem *exprTerm=copyExpressions.At(cur+index);
|
| if(!exprTerm.IsTypeOf(TYPE_CLASS_TermLexem))
|
| {
|
| Print(StringFormat("Wrong identifier '%s' in conditional part of the rule.",exprTerm.Text()));
|
| //--- return
|
| return(NULL);
|
| }
|
| IAltLexem *altLexem=exprTerm;
|
| CTermLexem *termLexem=NULL;
|
| do
|
| {
|
| if(!altLexem.IsTypeOf(TYPE_CLASS_TermLexem))
|
| {
|
| continue;
|
| }
|
| termLexem=altLexem;
|
| if(varLexem.Var().Values().IndexOf(termLexem.Term())==-1)
|
| {
|
| termLexem=NULL;
|
| continue;
|
| }
|
| }
|
| while((altLexem=altLexem.Alternative())!=NULL && termLexem==NULL);
|
| if(termLexem==NULL)
|
| {
|
| Print(StringFormat("Wrong identifier '%s' in conditional part of the rule.",exprTerm.Text()));
|
| //--- return
|
| return(NULL);
|
| }
|
| //--- Add new condition expression
|
| CFuzzyCondition *condition=new CFuzzyCondition(varLexem.Var(),termLexem.Term(),not,hedge);
|
| CArrayObj *cutExpression=GetRange(copyExpressions,index,cur+1);
|
| expressions.Add(new CConditionExpression(cutExpression,condition));
|
| cutExpression.FreeMode(false);
|
| delete cutExpression;
|
| index=index+cur+1;
|
| }
|
| else
|
| {
|
| CDictionary_String_Obj *p_so;
|
| IExpression *expr=copyExpressions.At(index);
|
| bool contain=false;
|
| for(int i=0; i<lexems.Total(); i++)
|
| {
|
| p_so=lexems.GetNodeAtIndex(i);
|
| if(p_so.Key()=="and" || p_so.Key()=="or" || p_so.Key()=="(" || p_so.Key()==")")
|
| {
|
| if(expr==p_so.Value())
|
| {
|
| contain=true;
|
| break;
|
| }
|
| }
|
| }
|
| if(contain==true)
|
| {
|
| expressions.Add(copyExpressions.At(index));
|
| index=index+1;
|
| }
|
| else
|
| {
|
| CLexem *unknownLexem=expr;
|
| Print(StringFormat("CLexem '%s' found at the wrong place in condition part of the rule.",unknownLexem.Text()));
|
| //--- return
|
| return (NULL);
|
| }
|
| }
|
| }
|
| //--- return expressions
|
| return (expressions);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Parse conditions |
|
| //+------------------------------------------------------------------+
|
| static CConditions *CRuleParser::ParseConditions(CArrayObj *conditionExpression,CList *in,CList *lexems)
|
| {
|
| //--- Extract single conditions
|
| CArrayObj *expressions=ExtractSingleCondidtions(conditionExpression,in,lexems);
|
| if(expressions.Total()==0)
|
| {
|
| Print("No valid conditions found in conditions part of the rule.");
|
| //--- return
|
| return (NULL);
|
| }
|
| ICondition *cond=ParseConditionsRecurse(expressions,lexems);
|
| delete expressions;
|
| if(cond.IsTypeOf(TYPE_CLASS_Conditions))
|
| {
|
| //--- return conditions
|
| return (cond);
|
| }
|
| else
|
| {
|
| delete cond;
|
| delete expressions;
|
| CConditions *conditions=new CConditions();
|
| //--- return conditions
|
| return (conditions);
|
| }
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Find pair bracket |
|
| //+------------------------------------------------------------------+
|
| static int CRuleParser::FindPairBracket(CArrayObj *expressions,CList *lexems)
|
| {
|
| //--- Assume that '(' stands at first place
|
| int bracketsOpened=1;
|
| int closeBracket=-1;
|
| CDictionary_String_Obj *p_so_open;
|
| CDictionary_String_Obj *p_so_close;
|
| for(int i=0; i<lexems.Total(); i++)
|
| {
|
| CDictionary_String_Obj *p_so=lexems.GetNodeAtIndex(i);
|
| if(p_so.Key()=="(")
|
| {
|
| p_so_open=p_so;
|
| }
|
| if(p_so.Key()==")")
|
| {
|
| p_so_close=p_so;
|
| }
|
| }
|
| for(int i=1; i<expressions.Total(); i++)
|
| {
|
| if(expressions.At(i)==p_so_open.Value())
|
| {
|
| bracketsOpened++;
|
| }
|
| else if(expressions.At(i)==p_so_close.Value())
|
| {
|
| bracketsOpened--;
|
| if(bracketsOpened==0)
|
| {
|
| closeBracket=i;
|
| break;
|
| }
|
| }
|
| }
|
| //--- return index of bracket
|
| return (closeBracket);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Parse conditions recurse |
|
| //+------------------------------------------------------------------+
|
| static ICondition *CRuleParser::ParseConditionsRecurse(CArrayObj *expressions,CList *lexems)
|
| {
|
| if(expressions.Total()<1)
|
| {
|
| Print("Empty condition found.");
|
| //--- return
|
| return(NULL);
|
| }
|
| CDictionary_String_Obj *p_so;
|
| for(int i=0; i<lexems.Total();i++)
|
| {
|
| p_so=lexems.GetNodeAtIndex(i);
|
| if(p_so.Key()=="(")
|
| {
|
| break;
|
| }
|
| }
|
| IExpression *expr=expressions.At(0);
|
| if(expressions.At(0)==p_so.Value() && FindPairBracket(expressions,lexems)==expressions.Total())
|
| {
|
| //--- Remove extra brackets
|
| //-- return conditions
|
| CArrayObj *cutExpression=GetRange(expressions,1,expressions.Total()-2);
|
| ICondition *cond=ParseConditionsRecurse(cutExpression,lexems);
|
| cutExpression.FreeMode(false);
|
| delete cutExpression;
|
| return cond;
|
| }
|
| else if(expressions.Total()==1 && expr.IsTypeOf(TYPE_CLASS_ConditionExpression))
|
| {
|
| //-- return single conditions
|
| CConditionExpression *condExp=expressions.At(0);
|
| return condExp.Condition();
|
| }
|
| else
|
| {
|
| //--- Parse list of one level conditions connected by or/and
|
| CArrayObj *copyExpressions=expressions;
|
| CConditionExpression *condExp;
|
| CConditions *conds=new CConditions();
|
| int index=0;
|
| bool setOrAnd=false;
|
| while(copyExpressions.Total()-index>0)
|
| {
|
| ICondition *cond=NULL;
|
| for(int i=0; i<lexems.Total();i++)
|
| {
|
| p_so=lexems.GetNodeAtIndex(i);
|
| if(p_so.Key()=="(")
|
| {
|
| break;
|
| }
|
| }
|
| if(copyExpressions.At(0)==p_so.Value())
|
| {
|
| //--- Find pair bracket
|
| int closeBracket=FindPairBracket(copyExpressions,lexems);
|
| if(closeBracket==-1)
|
| {
|
| Print("Parenthesis error.");
|
| //--- return
|
| return (NULL);
|
| }
|
| CArrayObj *cutExpression=GetRange(copyExpressions,index+1,closeBracket-1);
|
| cond=ParseConditionsRecurse(cutExpression,lexems);
|
| cutExpression.FreeMode(false);
|
| delete cutExpression;
|
| index=index+closeBracket+1;
|
| }
|
| else if(expr.IsTypeOf(TYPE_CLASS_ConditionExpression))
|
| {
|
| condExp=copyExpressions.At(index);
|
| cond=condExp.Condition();
|
| index=index+1;
|
| }
|
| else
|
| {
|
| condExp=copyExpressions.At(index);
|
| Print(StringFormat("Wrong expression in condition part at '%s'",condExp.Text()));
|
| //--- return
|
| return (NULL);
|
| }
|
| //--- And condition to the list
|
| conds.ConditionsList().Add(cond);
|
| p_so=NULL;
|
| CDictionary_String_Obj *p_so_and;
|
| CDictionary_String_Obj *p_so_or;
|
| for(int i=0; i<lexems.Total(); i++)
|
| {
|
| p_so=lexems.GetNodeAtIndex(i);
|
| if(p_so.Key()=="and")
|
| {
|
| p_so_and=p_so;
|
| }
|
| if(p_so.Key()=="or")
|
| {
|
| p_so_or=p_so;
|
| }
|
| }
|
| if(copyExpressions.Total()-index>0)
|
| {
|
| if((copyExpressions.At(index)==p_so_and.Value() && p_so_and.Key()=="and") || (copyExpressions.At(index)==p_so_or.Value() && p_so_or.Key()=="or"))
|
| {
|
| if(copyExpressions.Total()-index<2)
|
| {
|
| condExp=copyExpressions.At(index);
|
| Print(StringFormat("Error at %s in condition part.",condExp.Text()));
|
| //--- return
|
| return (NULL);
|
| }
|
| //--- Set and/or for conditions list
|
| OperatorType newOp=NULL;
|
| if(copyExpressions.At(index)==p_so_and.Value() && p_so_and.Key()=="and")
|
| {
|
| newOp=And;
|
| }
|
| if(copyExpressions.At(index)==p_so_or.Value() && p_so_or.Key()=="or")
|
| {
|
| newOp=Or;
|
| }
|
| if(setOrAnd)
|
| {
|
| if(conds.Op()!=newOp)
|
| {
|
| Print("At the one nesting level cannot be mixed and/or operations.");
|
| //--- return
|
| return (NULL);
|
| }
|
| }
|
| else
|
| {
|
| conds.Op(newOp);
|
| setOrAnd=true;
|
| }
|
| index=index+1;
|
| }
|
| else
|
| {
|
| string str;
|
| condExp=copyExpressions.At(index);
|
| str=condExp.Text();
|
| condExp=copyExpressions.At(index+1);
|
| Print(StringFormat("%s cannot goes after %s",str,condExp.Text()));
|
| //--- return
|
| return (NULL);
|
| }
|
| }
|
| }
|
| //--- return conditions
|
| return (conds);
|
| }
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Parse conclusion |
|
| //+------------------------------------------------------------------+
|
| static CSingleCondition *CRuleParser::ParseConclusion(CArrayObj *conditionExpression,CList *out,CList *lexems)
|
| {
|
| CArrayObj *copyExpression=conditionExpression;
|
| //--- Remove extra brackets
|
| CDictionary_String_Obj *p_so;
|
| CDictionary_String_Obj *p_so_open;
|
| CDictionary_String_Obj *p_so_close;
|
| for(int i=0; i<lexems.Total(); i++)
|
| {
|
| p_so=lexems.GetNodeAtIndex(i);
|
| if(p_so.Key()=="(")
|
| {
|
| p_so_open=p_so;
|
| }
|
| if(p_so.Key()==")")
|
| {
|
| p_so_close=p_so;
|
| }
|
| }
|
| int index=0;
|
| int Total=copyExpression.Total();
|
| while(Total>=2 && (copyExpression.At(index)==p_so_open.Value() && copyExpression.At(index+conditionExpression.Total()-1)==p_so_close.Value()))
|
| {
|
| index=index+1;
|
| Total=Total-2;
|
| }
|
| if(Total!=3)
|
| {
|
| Print("Conclusion part of the rule should be in form: 'variable is term'");
|
| //--- return
|
| return (NULL);
|
| }
|
| //--- Parse variable
|
| CLexem *exprVariable=copyExpression.At(index);
|
| if(!exprVariable.IsTypeOf(TYPE_CLASS_VarLexem))
|
| {
|
| Print(StringFormat("Wrong identifier '%s' in conclusion part of the rule.",exprVariable.Text()));
|
| //--- return
|
| return (NULL);
|
| }
|
| CVarLexem *varLexem=exprVariable;
|
| if(varLexem.Input()==true)
|
| {
|
| Print("The variable in conclusion part must be an output variable.");
|
| //--- return
|
| return (NULL);
|
| }
|
| //--- Parse 'is' lexem
|
| CLexem *exprIs=copyExpression.At(index+1);
|
| for(int i=0; i<lexems.Total(); i++)
|
| {
|
| p_so=lexems.GetNodeAtIndex(i);
|
| if(p_so.Key()=="is")
|
| {
|
| break;
|
| }
|
| }
|
| if(exprIs!=p_so.Value())
|
| {
|
| Print(StringFormat("'is' keyword must go after %s identifier.",varLexem.Text()));
|
| //--- return
|
| return (NULL);
|
| }
|
| //--- Parse term
|
| CLexem *exprTerm=copyExpression.At(index+2);
|
| if(!exprTerm.IsTypeOf(TYPE_CLASS_TermLexem))
|
| {
|
| Print(StringFormat("Wrong identifier '%s' in conclusion part of the rule.",exprTerm.Text()));
|
| }
|
| IAltLexem *altLexem=exprTerm;
|
| CTermLexem *termLexem=NULL;
|
| do
|
| {
|
| if(!altLexem.IsTypeOf(TYPE_CLASS_TermLexem))
|
| {
|
| continue;
|
| }
|
| termLexem=altLexem;
|
| if(varLexem.Var().Values().IndexOf(termLexem.Term())==-1)
|
| {
|
| termLexem=NULL;
|
| continue;
|
| }
|
| }
|
| while((altLexem=altLexem.Alternative())!=NULL && termLexem==NULL);
|
| if(termLexem==NULL)
|
| {
|
| Print(StringFormat("Wrong identifier '%s' in conclusion part of the rule.",exprTerm.Text()));
|
| //--- return
|
| return (NULL);
|
| }
|
| //--- Return fuzzy rule's conclusion
|
| INamedVariable *var=varLexem.Var();
|
| INamedValue *term=termLexem.Term();
|
| //--- return single condition
|
| return new CSingleCondition(var, term, false);
|
| }
|
| //+------------------------------------------------------------------+
|