1 #ifndef CORELIB___EXPR__HPP
2 #define CORELIB___EXPR__HPP
3 
4 
5 /*  $Id: expr.hpp 574926 2018-11-20 20:23:54Z ucko $
6  * ===========================================================================
7  *
8  *                            PUBLIC DOMAIN NOTICE
9  *               National Center for Biotechnology Information
10  *
11  *  This software/database is a "United States Government Work" under the
12  *  terms of the United States Copyright Act.  It was written as part of
13  *  the author's official duties as a United States Government employee and
14  *  thus cannot be copyrighted.  This software/database is freely available
15  *  to the public for use. The National Library of Medicine and the U.S.
16  *  Government have not placed any restriction on its use or reproduction.
17  *
18  *  Although all reasonable efforts have been taken to ensure the accuracy
19  *  and reliability of the software and data, the NLM and the U.S.
20  *  Government do not and cannot warrant the performance or results that
21  *  may be obtained by using this software or data. The NLM and the U.S.
22  *  Government disclaim all warranties, express or implied, including
23  *  warranties of performance, merchantability or fitness for any particular
24  *  purpose.
25  *
26  *  Please cite the author in any work or product based on this material.
27  *
28  * ===========================================================================
29  *
30  * Author: Sergey Sikorskiy, Mikhail Zakharov
31  *
32  * File Description:
33  *      Expression parsing and evaluation.
34  *
35  * ===========================================================================
36  */
37 
38 #include <corelib/ncbistd.hpp>
39 
40 BEGIN_NCBI_SCOPE
41 
42 
43 
44 ////////////////////////////////////////////////////////////////////////////////
45 class CExprSymbol;
46 
47 class NCBI_XNCBI_EXPORT CExprValue
48 {
49 public:
50     CExprValue(void);
CExprValue(VT * value)51     template <typename VT> CExprValue(VT* value)
52     {
53         // If you got here, you are using wrong data type.
54         value->please_use_Int8_double_bool_instead();
55     }
56     CExprValue(Uint4 value);
57     CExprValue(Int4 value);
58     CExprValue(Uint8 value);
59     CExprValue(Int8 value);
60     CExprValue(double value);
61     CExprValue(bool value);
62     CExprValue(string value);
63     CExprValue(const CExprValue& value);
64 
65 public:
66     /// Value type.
67     enum EValue {
68         eINT,
69         eFLOAT,
70         eBOOL,
71         eSTRING
72     };
73 
74 public:
GetType(void) const75     EValue GetType(void) const
76     {
77         return m_Tag;
78     }
SetType(EValue type)79     void SetType(EValue type)
80     {
81         m_Tag = type;
82     }
83 
GetString(void) const84     string GetString(void) const
85     {
86         string str;
87 
88         switch (m_Tag) {
89             case eINT:
90                 NStr::NumericToString(str, ival);
91                 return str;
92             case eBOOL:
93                 return bval ? "true" : "false";
94             case eSTRING:
95                 return m_sval;
96             default:
97                 break;
98         }
99 
100         return NStr::DoubleToString(fval);
101     }
102 
GetDouble(void) const103     double GetDouble(void) const
104     {
105         switch (m_Tag) {
106             case eINT:
107                 return static_cast<double>(ival);
108             case eBOOL:
109                 return bval ? 1.0 : 0.0;
110             case eSTRING:
111                 return 0;
112             default:
113                 break;
114         }
115 
116         return fval;
117     }
118 
GetInt(void) const119     Int8 GetInt(void) const
120     {
121         switch (m_Tag) {
122             case eFLOAT:
123                 return static_cast<Int8>(fval);
124             case eBOOL:
125                 return bval ? 1 : 0;
126             case eSTRING:
127                 return 0;
128             default:
129                 break;
130         }
131 
132         return ival;
133     }
134 
GetBool(void) const135     bool GetBool(void) const
136     {
137         switch (m_Tag) {
138             case eINT:
139                 return ival != 0;
140             case eFLOAT:
141                 return fval != 0.0;
142             case eSTRING:
143                 return false;
144             default:
145                 break;
146         }
147 
148         return bval;
149     }
150 
151 public:
152     union {
153         Int8    ival;
154         double  fval;
155         bool    bval;
156     };
157     string m_sval;
158 
159     CExprSymbol*    m_Var;
160     int             m_Pos;
161 
162 private:
163     EValue          m_Tag;
164 };
165 
166 ////////////////////////////////////////////////////////////////////////////////
167 class NCBI_XNCBI_EXPORT CExprSymbol
168 {
169 public:
170     typedef Int8    (*FIntFunc1)    (Int8);
171     typedef Int8    (*FIntFunc2)    (Int8,Int8);
172     typedef double  (*FFloatFunc1)  (double);
173     typedef double  (*FFloatFunc2)  (double, double);
174     typedef bool    (*FBoolFunc1)   (bool);
175     typedef bool    (*FBoolFunc2)   (bool, bool);
176     typedef bool    (*FStringFunc1)  (const string&);
177 
178     CExprSymbol(void);
CExprSymbol(const char * name,VT * value)179     template <typename VT> CExprSymbol(const char* name, VT* value)
180     {
181         // If you got here, you are using wrong data type.
182         value->please_use_Int8_double_bool_instead();
183     }
184     CExprSymbol(const char* name, Uint4 value);
185     CExprSymbol(const char* name, Int4 value);
186     CExprSymbol(const char* name, Uint8 value);
187     CExprSymbol(const char* name, Int8 value);
188     CExprSymbol(const char* name, bool value);
189     CExprSymbol(const char* name, double value);
190     CExprSymbol(const char* name, string value);
191     CExprSymbol(const char* name, FIntFunc1 value);
192     CExprSymbol(const char* name, FIntFunc2 value);
193     CExprSymbol(const char* name, FFloatFunc1 value);
194     CExprSymbol(const char* name, FFloatFunc2 value);
195     CExprSymbol(const char* name, FBoolFunc1 value);
196     CExprSymbol(const char* name, FBoolFunc2 value);
197     CExprSymbol(const char* name, FStringFunc1 value);
198 
199     ~CExprSymbol(void);
200 
201 
202 public:
203     enum ESymbol {
204         eVARIABLE,
205         eIFUNC1,
206         eIFUNC2,
207         eFFUNC1,
208         eFFUNC2,
209         eBFUNC1,
210         eBFUNC2,
211         eSFUNC1
212     };
213 
214 public:
215     ESymbol         m_Tag;
216     union {
217         FIntFunc1   m_IntFunc1;
218         FIntFunc2   m_IntFunc2;
219         FFloatFunc1 m_FloatFunc1;
220         FFloatFunc2 m_FloatFunc2;
221         FBoolFunc1  m_BoolFunc1;
222         FBoolFunc2  m_BoolFunc2;
223         FStringFunc1 m_StringFunc1;
224     };
225     CExprValue      m_Val;
226     string          m_Name;
227     CExprSymbol*    m_Next;
228 };
229 
230 ////////////////////////////////////////////////////////////////////////////////
231 class NCBI_XNCBI_EXPORT CExprParserException : EXCEPTION_VIRTUAL_BASE public CException
232 {
233 public:
234     enum EErrCode {
235         eParseError,
236         eTypeConversionError
237     };
238 
239 
240     CExprParserException(
241         const CDiagCompileInfo& info,
242         const CException* prev_exception, EErrCode err_code,
243         const string& message, int pos,
244         EDiagSev severity = eDiag_Error)
245     : CException(info, prev_exception, message, severity)
246     , m_Pos(pos)
247     NCBI_EXCEPTION_DEFAULT_IMPLEMENTATION(CExprParserException, CException);
248 
249     virtual const char* GetErrCodeString(void) const override;
250 
251     virtual void ReportExtra(ostream& out) const override;
252 
253 public:
GetPos(void) const254     int GetPos(void) const
255     {
256         return m_Pos;
257     }
258 
259 protected:
260     virtual void x_Assign(const CException& src) override;
261 
262 private:
263     int    m_Pos;
264 };
265 
266 ////////////////////////////////////////////////////////////////////////////////
267 class NCBI_XNCBI_EXPORT CExprParser
268 {
269 public:
270     /// Parser flags
271     enum EAutoVar {
272         fAllowAutoVar = 0,          //< create variables without previous declaration
273         fDenyAutoVar  = (1 << 0),   //< call AddSymbol() to register a variable
274         fLogicalOnly  = (1 << 1),   //< '/','.','[0-9]' is interpreted as a part of a symbol (to allow directories)
275 
276         // Legacy
277         eAllowAutoVar = fAllowAutoVar,
278         eDenyAutoVar  = fDenyAutoVar
279     };
280 
281     typedef int TParserFlags;
282 
283     CExprParser(TParserFlags auto_var = 0);
284     ~CExprParser(void);
285 
286 public:
287     void Parse(const char* str);
288 
GetResult(void) const289     const CExprValue& GetResult(void) const
290     {
291         if (m_v_sp != 1) {
292             ReportError("Result is not available");
293         }
294 
295         return m_VStack[0];
296     }
297 
298     template <typename VT>
299     CExprSymbol* AddSymbol(const char* name, VT value);
300 
301 private:
302     enum EOperator {
303         eBEGIN, eOPERAND, eERROR, eEND,
304         eLPAR, eRPAR, eFUNC, ePOSTINC, ePOSTDEC,
305         ePREINC, ePREDEC, ePLUS, eMINUS, eNOT, eCOM,
306         ePOW,
307         eMUL, eDIV, eMOD,
308         eADD, eSUB,
309         eASL, eASR, eLSR,
310         eGT, eGE, eLT, eLE,
311         eEQ, eNE,
312         eAND,
313         eXOR,
314         eOR,
315         eSET, eSETADD, eSETSUB, eSETMUL, eSETDIV, eSETMOD, eSETASL, eSETASR, eSETLSR,
316         eSETAND, eSETXOR, eSETOR, eSETPOW,
317         eCOMMA,
318         eTERMINALS
319     };
320 
321 private:
322     EOperator Scan(bool operand);
323     bool Assign(void);
324     CExprSymbol* GetSymbol(const char* name) const;
325 
ReportError(int pos,const string & msg)326     static void ReportError(int pos, const string& msg)
327     {
328         NCBI_THROW2(CExprParserException, eParseError, msg, pos);
329     }
ReportError(const string & msg) const330     void ReportError(const string& msg) const { ReportError(m_Pos - 1, msg); }
331 
332     EOperator IfChar(
333             char c, EOperator val,
334             EOperator val_def);
335     EOperator IfElseChar(
336             char c1, EOperator val1,
337             char c2, EOperator val2,
338             EOperator val_def);
339     EOperator IfLongest2ElseChar(
340             char c1, char c2,
341             EOperator val_true_longest,
342             EOperator val_true,
343             EOperator val_false,
344             EOperator val_def);
345 
AutoCreateVariable(void) const346     TParserFlags AutoCreateVariable(void) const
347     {
348         return m_ParserFlags & fDenyAutoVar;
349     }
350 
LogicalOnly(void) const351     bool LogicalOnly(void) const { return (m_ParserFlags & fLogicalOnly) != 0;}
352 
353 private:
354     enum {hash_table_size = 1013};
355     CExprSymbol* hash_table[hash_table_size];
356 
357     enum {max_stack_size = 256};
358     enum {max_expression_length = 1024};
359 
360     static int sm_lpr[eTERMINALS];
361     static int sm_rpr[eTERMINALS];
362 
363     CExprValue  m_VStack[max_stack_size];
364     int         m_v_sp;
365     EOperator   m_OStack[max_stack_size];
366     int         m_o_sp;
367     const char* m_Buf;
368     int         m_Pos;
369     int         m_TmpVarCount;
370     TParserFlags    m_ParserFlags;
371 };
372 
373 
374 ////////////////////////////////////////////////////////////////////////////////
375 // Inline methods.
376 //
377 
378 NCBI_XNCBI_EXPORT
379 unsigned string_hash_function(const char* p);
380 
381 template <typename VT>
382 inline
AddSymbol(const char * name,VT value)383 CExprSymbol* CExprParser::AddSymbol(const char* name, VT value)
384 {
385     CExprSymbol* sp = GetSymbol(name);
386 
387     if (!sp) {
388         // Add ...
389         sp = new CExprSymbol(name, value);
390 
391         unsigned h = string_hash_function(name) % hash_table_size;
392         sp->m_Next = hash_table[h];
393         hash_table[h] = sp;
394     }
395 
396     return sp;
397 }
398 
399 inline
400 CExprParser::EOperator
IfChar(char c,EOperator val,EOperator val_def)401 CExprParser::IfChar(
402         char c, EOperator val,
403         EOperator val_def)
404 {
405     if (m_Buf[m_Pos] == c) {
406         m_Pos += 1;
407         return val;
408     }
409 
410     return val_def;
411 }
412 
413 inline
414 CExprParser::EOperator
IfElseChar(char c1,EOperator val1,char c2,EOperator val2,EOperator val_def)415 CExprParser::IfElseChar(
416         char c1, EOperator val1,
417         char c2, EOperator val2,
418         EOperator val_def)
419 {
420     if (m_Buf[m_Pos] == c1) {
421         m_Pos += 1;
422         return val1;
423     } else if (m_Buf[m_Pos] == c2) {
424         m_Pos += 1;
425         return val2;
426     }
427 
428     return val_def;
429 }
430 
431 inline
432 CExprParser::EOperator
IfLongest2ElseChar(char c1,char c2,EOperator val_true_longest,EOperator val_true,EOperator val_false,EOperator val_def)433 CExprParser::IfLongest2ElseChar(
434         char c1, char c2,
435         EOperator val_true_longest,
436         EOperator val_true,
437         EOperator val_false,
438         EOperator val_def)
439 {
440     if (m_Buf[m_Pos] == c1) {
441         m_Pos += 1;
442         return IfChar(c2, val_true_longest, val_true);
443     } else if (m_Buf[m_Pos] == c2) {
444         m_Pos += 1;
445         return val_false;
446     }
447 
448     return val_def;
449 }
450 
451 END_NCBI_SCOPE
452 
453 #endif  /* CORELIB___EXPR__HPP */
454 
455