|
|
/*
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 <https://www.gnu.org/licenses/>.
*/
#ifndef __LIBEVAL_COMPILER_H
#define __LIBEVAL_COMPILER_H
#include <cstddef>
#include <functional>
#include <map>
#include <string>
#include <stack>
#include <base_units.h>
#include <wx/intl.h>
#if defined(WIN32)
// This gets leaked by python headers on MSVC only and will cause chaos
#undef COMPILER
#endif
#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 0x205
#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; int srcPos;};
enum VAR_TYPE_T{ VT_STRING = 1, VT_NUMERIC, VT_UNDEFINED, VT_PARSE_ERROR};
enum TOKEN_TYPE_T{ TR_UNDEFINED = 0, TR_NUMBER = 1, TR_IDENTIFIER = 2, TR_ASSIGN = 3, TR_STRUCT_REF = 4, TR_STRING = 5, TR_UNIT = 6, TR_ARG_LIST = 7, TR_NULL = 8};
class UOP;class UCODE;class CONTEXT;class VAR_REF;
typedef std::function<void( CONTEXT*, void* )> FUNC_CALL_REF;
struct T_TOKEN_VALUE{ wxString* str; double num; int idx;};
// Lemon can't handle c'tors and d'tors, so we provide a poor-man's version.
constexpr T_TOKEN_VALUE defaultTokenValue = { nullptr, 0.0, 0 };
struct T_TOKEN{ int token; T_TOKEN_VALUE value;};
// Lemon can't handle c'tors and d'tors, so we provide a poor-man's version.
constexpr T_TOKEN defaultToken = { TR_UNDEFINED, defaultTokenValue };
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, bool aStringIsWildcard ); void SetUop( int aOp, std::unique_ptr<VAR_REF> aRef = nullptr ); void SetUop( int aOp, FUNC_CALL_REF aFunc, std::unique_ptr<VAR_REF> 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<wxString>& GetSupportedUnits() const { static const std::vector<wxString> nullUnits;
return nullUnits; }
virtual wxString GetSupportedUnitsMessage() const { return wxEmptyString; }
virtual double Convert( const wxString& aString, int unitType ) const { return 0.0; };};
class VALUE{public: VALUE() : m_type( VT_UNDEFINED ), m_valueDbl( 0 ), m_stringIsWildcard( false ), m_isDeferredDbl( false ), m_isDeferredStr( false ) {};
VALUE( const wxString& aStr, bool aIsWildcard = false ) : m_type( VT_STRING ), m_valueDbl( 0 ), m_valueStr( aStr ), m_stringIsWildcard( aIsWildcard ), m_isDeferredDbl( false ), m_isDeferredStr( false ) {};
VALUE( const double aVal ) : m_type( VT_NUMERIC ), m_valueDbl( aVal ), m_stringIsWildcard( false ), m_isDeferredDbl( false ), m_isDeferredStr( false ) {};
virtual ~VALUE() {};
virtual double AsDouble() const { if( m_isDeferredDbl ) { m_valueDbl = m_lambdaDbl(); m_isDeferredDbl = false; }
return m_valueDbl; }
virtual const wxString& AsString() const { if( m_isDeferredStr ) { m_valueStr = m_lambdaStr(); m_isDeferredStr = false; }
return m_valueStr; }
virtual bool EqualTo( CONTEXT* aCtx, const VALUE* b ) const;
// NB: this is not an inverse of EqualTo as they both return false for undefined values.
virtual bool NotEqualTo( CONTEXT* aCtx, const VALUE* b ) const;
VAR_TYPE_T GetType() const { return m_type; };
void Set( double aValue ) { m_type = VT_NUMERIC; m_valueDbl = aValue; }
void SetDeferredEval( std::function<double()> aLambda ) { m_type = VT_NUMERIC; m_lambdaDbl = aLambda; m_isDeferredDbl = true; }
void SetDeferredEval( std::function<wxString()> aLambda ) { m_type = VT_STRING; m_lambdaStr = aLambda; m_isDeferredStr = true; }
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; }
private: VAR_TYPE_T m_type; mutable double m_valueDbl; // mutable to support deferred evaluation
mutable wxString m_valueStr; // mutable to support deferred evaluation
bool m_stringIsWildcard;
mutable bool m_isDeferredDbl; std::function<double()> m_lambdaDbl;
mutable bool m_isDeferredStr; std::function<wxString()> m_lambdaStr;};
class VAR_REF{public: VAR_REF() {}; virtual ~VAR_REF() {};
virtual VAR_TYPE_T GetType() const = 0; virtual VALUE* GetValue( CONTEXT* aCtx ) = 0;};
class CONTEXT{public: CONTEXT() : m_stack(), m_stackPtr( 0 ) { m_ownedValues.reserve( 20 ); }
virtual ~CONTEXT() { for( VALUE* v : m_ownedValues ) { delete v; } }
VALUE* AllocValue() { m_ownedValues.emplace_back( new VALUE ); return m_ownedValues.back(); }
VALUE* StoreValue( VALUE* aValue ) { m_ownedValues.emplace_back( aValue ); return m_ownedValues.back(); }
void Push( VALUE* v ) { m_stack[ m_stackPtr++ ] = v; }
VALUE* Pop() { if( m_stackPtr == 0 ) { ReportError( _( "Malformed expression" ) ); return AllocValue(); }
return m_stack[ --m_stackPtr ]; }
int SP() const { return m_stackPtr; };
void SetErrorCallback( std::function<void( const wxString& aMessage, int aOffset )> aCallback ) { m_errorCallback = std::move( aCallback ); }
bool HasErrorCallback() { return m_errorCallback != nullptr; }
void ReportError( const wxString& aErrorMsg );
private: std::vector<VALUE*> m_ownedValues; VALUE* m_stack[100]; // std::stack not performant enough
int m_stackPtr;
std::function<void( const wxString& aMessage, int aOffset )> 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<VAR_REF> CreateVarRef( const wxString& var, const wxString& field ) { return nullptr; };
virtual FUNC_CALL_REF CreateFuncCall( const wxString& name ) { return nullptr; };
protected:
std::vector<UOP*> m_ucode;};
class UOP{public: UOP( int op, std::unique_ptr<VALUE> value ) : m_op( op ), m_ref(nullptr), m_value( std::move( value ) ) {};
UOP( int op, std::unique_ptr<VAR_REF> vref ) : m_op( op ), m_ref( std::move( vref ) ), m_value(nullptr) {};
UOP( int op, FUNC_CALL_REF func, std::unique_ptr<VAR_REF> 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<VAR_REF> m_ref; std::unique_ptr<VALUE> 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( const std::function<bool( wxUniChar )>& cond ) const;
bool MatchAhead( const wxString& match, const std::function<bool( wxUniChar )>& stopCond ) const;
private: wxString m_str; size_t m_pos = 0;};
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<void( const wxString& aMessage, int aOffset )> aCallback ) { m_errorCallback = std::move( 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<UNIT_RESOLVER> m_unitResolver;
int m_sourcePos; bool m_parseFinished; ERROR_STATUS m_errorStatus;
std::function<void( const wxString& aMessage, int aOffset )> m_errorCallback;
TREE_NODE* m_tree;
std::vector<TREE_NODE*> m_gcItems; std::vector<wxString*> m_gcStrings;};
} // namespace LIBEVAL
#endif /* LIBEVAL_COMPILER_H_ */
|