/* This file is part of libeval, a simple math expression evaluator Copyright (C) 2007 Michael Geselbracht, mgeselbracht3@gmail.com Copyright (C) 2019-2020 KiCad Developers, see AUTHORS.txt for contributors. This program 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, either version 3 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. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef __LIBEVAL_COMPILER_H #define __LIBEVAL_COMPILER_H #include #include #include #include #include #include #define TR_OP_BINARY_MASK 0x200 #define TR_OP_UNARY_MASK 0x100 #define TR_OP_MUL 0x201 #define TR_OP_DIV 0x202 #define TR_OP_ADD 0x203 #define TR_OP_SUB 0x204 #define TR_OP_LESS 0x25 #define TR_OP_GREATER 0x206 #define TR_OP_LESS_EQUAL 0x207 #define TR_OP_GREATER_EQUAL 0x208 #define TR_OP_EQUAL 0x209 #define TR_OP_NOT_EQUAL 0x20a #define TR_OP_BOOL_AND 0x20b #define TR_OP_BOOL_OR 0x20c #define TR_OP_BOOL_NOT 0x100 #define TR_OP_FUNC_CALL 24 #define TR_OP_METHOD_CALL 25 #define TR_UOP_PUSH_VAR 1 #define TR_UOP_PUSH_VALUE 2 // This namespace is used for the lemon parser namespace LIBEVAL { class COMPILER; enum COMPILATION_STAGE { CST_PARSE = 0, CST_CODEGEN, CST_RUNTIME }; struct ERROR_STATUS { bool pendingError = false; COMPILATION_STAGE stage; wxString message; // Note: use wxString for GUI-related strings int srcPos; }; enum VAR_TYPE_T { VT_STRING = 1, VT_NUMERIC, VT_UNDEFINED, VT_PARSE_ERROR }; enum TOKEN_TYPE_T { TR_NUMBER = 1, TR_IDENTIFIER = 2, TR_ASSIGN = 3, TR_STRUCT_REF = 4, TR_STRING = 5, TR_UNIT = 6 }; class UOP; class UCODE; class CONTEXT; class VAR_REF; typedef std::function FUNC_CALL_REF; struct T_TOKEN_VALUE { wxString *str; double num; int idx; }; constexpr T_TOKEN_VALUE defaultTokenValue = { nullptr, 0.0, 0 }; struct T_TOKEN { int token; T_TOKEN_VALUE value; }; class TREE_NODE { public: T_TOKEN_VALUE value; int op; TREE_NODE* leaf[2]; UOP* uop; bool valid; bool isTerminal; bool isVisited; int srcPos; void SetUop( int aOp, double aValue ); void SetUop( int aOp, const wxString& aValue ); void SetUop( int aOp, std::unique_ptr aRef = nullptr ); void SetUop( int aOp, FUNC_CALL_REF aFunc, std::unique_ptr aRef = nullptr ); }; TREE_NODE* newNode( LIBEVAL::COMPILER* compiler, int op, const T_TOKEN_VALUE& value = defaultTokenValue); class UNIT_RESOLVER { public: UNIT_RESOLVER() { } virtual ~UNIT_RESOLVER() { } virtual const std::vector& GetSupportedUnits() const { static const std::vector nullUnits; return nullUnits; } virtual double Convert( const wxString& aString, int unitType ) const { return 0.0; }; }; class VALUE { public: VALUE(): m_type(VT_UNDEFINED), m_valueDbl( 0 ) {}; VALUE( const wxString& aStr ) : m_type( VT_STRING ), m_valueDbl( 0 ), m_valueStr( aStr ) {}; VALUE( const double aVal ) : m_type( VT_NUMERIC ), m_valueDbl( aVal ) {}; double AsDouble() const { return m_valueDbl; } const wxString& AsString() const { return m_valueStr; } bool operator==( const VALUE& b ) const { if( m_type == VT_NUMERIC && b.m_type == VT_NUMERIC ) return m_valueDbl == b.m_valueDbl; else if( m_type == VT_STRING && b.m_type == VT_STRING ) return m_valueStr == b.m_valueStr; return false; } VAR_TYPE_T GetType() const { return m_type; }; void Set( double aValue ) { m_type = VT_NUMERIC; m_valueDbl = aValue; } void Set( const wxString& aValue ) { m_type = VT_STRING; m_valueStr = aValue; } void Set( const VALUE &val ) { m_type = val.m_type; m_valueDbl = val.m_valueDbl; if( m_type == VT_STRING ) m_valueStr = val.m_valueStr; } bool EqualTo( const VALUE* v2 ) const { return operator==( *v2 ); } private: VAR_TYPE_T m_type; double m_valueDbl; wxString m_valueStr; }; class VAR_REF { public: VAR_REF() {}; virtual ~VAR_REF() {}; virtual VAR_TYPE_T GetType() = 0; virtual VALUE GetValue( CONTEXT* aCtx ) = 0; }; class CONTEXT { public: virtual ~CONTEXT() { for( VALUE* value : m_ownedValues ) delete value; } VALUE* AllocValue() { VALUE* value = new VALUE(); m_ownedValues.push_back( value ); return value; } void Push( VALUE* v ) { m_stack.push( v ); } VALUE* Pop() { VALUE* value = m_stack.top(); m_stack.pop(); return value; } int SP() const { return m_stack.size(); }; void SetErrorCallback( std::function aCallback ); void ReportError( const wxString& aErrorMsg ); bool IsErrorPending() const { return m_errorStatus.pendingError; } const ERROR_STATUS& GetError() const { return m_errorStatus; } private: std::vector m_ownedValues; std::stack m_stack; ERROR_STATUS m_errorStatus; std::function m_errorCallback; }; class UCODE { public: virtual ~UCODE(); void AddOp( UOP* uop ) { m_ucode.push_back(uop); } VALUE* Run( CONTEXT* ctx ); wxString Dump() const; virtual std::unique_ptr CreateVarRef( const wxString& var, const wxString& field ) { return nullptr; }; virtual FUNC_CALL_REF CreateFuncCall( const wxString& name ) { return nullptr; }; protected: std::vector m_ucode; }; class UOP { public: UOP( int op, std::unique_ptr value ) : m_op( op ), m_ref(nullptr), m_value( std::move( value ) ) {}; UOP( int op, std::unique_ptr vref ) : m_op( op ), m_ref( std::move( vref ) ), m_value(nullptr) {}; UOP( int op, FUNC_CALL_REF func, std::unique_ptr vref = nullptr ) : m_op( op ), m_func( std::move( func ) ), m_ref( std::move( vref ) ), m_value(nullptr) {}; ~UOP() { } void Exec( CONTEXT* ctx ); wxString Format() const; private: int m_op; FUNC_CALL_REF m_func; std::unique_ptr m_ref; std::unique_ptr m_value; }; class TOKENIZER { public: void Restart( const wxString& aStr ) { m_str = aStr; m_pos = 0; } void Clear() { m_str = ""; m_pos = 0; } int GetChar() const { if( m_pos >= m_str.length() ) return 0; return m_str[m_pos]; } bool Done() const { return m_pos >= m_str.length(); } void NextChar( int aAdvance = 1 ) { m_pos += aAdvance; } size_t GetPos() const { return m_pos; } wxString GetChars( std::function cond ) const; bool MatchAhead( const wxString& match, std::function stopCond ) const; private: wxString m_str; size_t m_pos; }; class COMPILER { public: COMPILER(); virtual ~COMPILER(); /* * clear() should be invoked by the client if a new input string is to be processed. It * will reset the parser. User defined variables are retained. */ void Clear(); /* Used by the lemon parser */ void parseError( const char* s ); void parseOk(); int GetSourcePos() const { return m_sourcePos; } void setRoot( LIBEVAL::TREE_NODE *root ); void freeTree( LIBEVAL::TREE_NODE *tree ); bool Compile( const wxString& aString, UCODE* aCode, CONTEXT* aPreflightContext ); void SetErrorCallback( std::function aCallback ) { m_errorCallback = aCallback; } bool IsErrorPending() const { return m_errorStatus.pendingError; } const ERROR_STATUS& GetError() const { return m_errorStatus; } void GcItem( TREE_NODE* aItem ) { m_gcItems.push_back( aItem ); } void GcItem( wxString* aItem ) { m_gcStrings.push_back( aItem ); } protected: enum LEXER_STATE { LS_DEFAULT = 0, LS_STRING = 1, }; LEXER_STATE m_lexerState; bool generateUCode( UCODE* aCode, CONTEXT* aPreflightContext ); void reportError( COMPILATION_STAGE stage, const wxString& aErrorMsg, int aPos = -1 ); /* Begin processing of a new input string */ void newString( const wxString& aString ); /* Tokenizer: Next token/value taken from input string. */ T_TOKEN getToken(); bool lexDefault( T_TOKEN& aToken ); bool lexString( T_TOKEN& aToken ); int resolveUnits(); protected: /* Token state for input string. */ void* m_parser; // the current lemon parser state machine TOKENIZER m_tokenizer; char m_localeDecimalSeparator; std::unique_ptr m_unitResolver; int m_sourcePos; bool m_parseFinished; TREE_NODE* m_tree; ERROR_STATUS m_errorStatus; std::vector m_gcItems; std::vector m_gcStrings; std::function m_errorCallback; }; } // namespace LIBEVAL #endif /* LIBEVAL_COMPILER_H_ */