501 lines
10 KiB
C++
501 lines
10 KiB
C++
/*
|
|
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 <base_units.h>
|
|
|
|
#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;
|
|
|
|
struct ERROR_STATUS
|
|
{
|
|
bool pendingError;
|
|
|
|
enum STAGE {
|
|
CST_PARSE = 0,
|
|
CST_CODEGEN,
|
|
CST_RUNTIME
|
|
};
|
|
|
|
STAGE stage;
|
|
std::string message;
|
|
std::string failingObject;
|
|
int failingPosition;
|
|
|
|
std::string Format() const;
|
|
};
|
|
|
|
|
|
enum VAR_TYPE_T
|
|
{
|
|
VT_STRING = 1,
|
|
VT_NUMERIC,
|
|
VT_UNDEFINED
|
|
};
|
|
|
|
enum TOKEN_TYPE_T
|
|
{
|
|
TR_NUMBER = 1,
|
|
TR_IDENTIFIER = 2,
|
|
TR_ASSIGN = 3,
|
|
TR_STRUCT_REF = 4,
|
|
TR_STRING = 5,
|
|
TR_UNIT = 6
|
|
};
|
|
|
|
#define LIBEVAL_MAX_LITERAL_LENGTH 1024
|
|
|
|
class UOP;
|
|
|
|
struct TREE_NODE
|
|
{
|
|
struct value_s
|
|
{
|
|
char str[LIBEVAL_MAX_LITERAL_LENGTH];
|
|
int type;
|
|
} value;
|
|
int op;
|
|
TREE_NODE* leaf[2];
|
|
UOP* uop;
|
|
bool valid;
|
|
bool isTerminal;
|
|
};
|
|
|
|
static inline TREE_NODE* copyNode( TREE_NODE& t )
|
|
{
|
|
auto t2 = new TREE_NODE();
|
|
t2->valid = t.valid;
|
|
strcpy(t2->value.str, t.value.str);
|
|
t2->op = t.op;
|
|
t2->value.type = t.value.type;
|
|
t2->leaf[0] = t.leaf[0];
|
|
t2->leaf[1] = t.leaf[1];
|
|
t2->isTerminal = false;
|
|
t2->uop = nullptr;
|
|
return t2;
|
|
}
|
|
|
|
|
|
|
|
static inline TREE_NODE* newNode( int op, int type, std::string value )
|
|
{
|
|
auto t2 = new TREE_NODE();
|
|
t2->valid = true;
|
|
strcpy(t2->value.str, value.c_str());
|
|
t2->op = op;
|
|
t2->value.type = type;
|
|
t2->leaf[0] = nullptr;
|
|
t2->leaf[1] = nullptr;
|
|
t2->isTerminal = false;
|
|
t2->uop = nullptr;
|
|
return t2;
|
|
}
|
|
|
|
|
|
class UNIT_RESOLVER {
|
|
public:
|
|
UNIT_RESOLVER()
|
|
{
|
|
}
|
|
|
|
virtual ~UNIT_RESOLVER()
|
|
{
|
|
}
|
|
|
|
virtual const std::vector<std::string>& GetSupportedUnits() const
|
|
{
|
|
static const std::vector<std::string> nullUnits;
|
|
|
|
return nullUnits;
|
|
}
|
|
|
|
virtual double Convert( const std::string aString, int unitType ) const
|
|
{
|
|
return 0.0;
|
|
};
|
|
};
|
|
|
|
class VALUE {
|
|
public:
|
|
VALUE():
|
|
m_type(VT_UNDEFINED),
|
|
m_valueDbl( 0 )
|
|
{};
|
|
|
|
VALUE( const std::string& 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 std::string& AsString() const
|
|
{
|
|
return m_valueStr;
|
|
}
|
|
|
|
bool operator==( const VALUE& b ) const
|
|
{
|
|
if( m_type != b.m_type )
|
|
return false;
|
|
if( m_type == VT_NUMERIC && m_valueDbl != b.m_valueDbl )
|
|
return false;
|
|
if( m_type == VT_STRING && m_valueStr != b.m_valueStr )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
VAR_TYPE_T GetType() const { return m_type; };
|
|
|
|
void Set( double aValue )
|
|
{
|
|
m_type = VT_NUMERIC;
|
|
m_valueDbl = aValue;
|
|
}
|
|
|
|
void Set( std::string 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
|
|
{
|
|
assert ( m_type == v2->m_type );
|
|
if(m_type == VT_STRING)
|
|
return m_valueStr == v2->m_valueStr;
|
|
else
|
|
return m_valueDbl == v2->m_valueDbl;
|
|
}
|
|
|
|
private:
|
|
VAR_TYPE_T m_type;
|
|
double m_valueDbl;
|
|
std::string m_valueStr;
|
|
};
|
|
|
|
class UCODE;
|
|
|
|
class VAR_REF
|
|
{
|
|
public:
|
|
virtual VAR_TYPE_T GetType( UCODE* aUcode ) = 0;
|
|
virtual VALUE GetValue( UCODE* aUcode ) = 0;
|
|
};
|
|
|
|
|
|
class UCODE
|
|
{
|
|
public:
|
|
|
|
|
|
class CONTEXT
|
|
{
|
|
public:
|
|
const int c_memSize = 128;
|
|
|
|
CONTEXT()
|
|
{
|
|
m_sp = 0;
|
|
for (int i = 0; i < c_memSize; i++ )
|
|
m_memory.push_back( VALUE() );
|
|
}
|
|
|
|
VALUE* AllocValue()
|
|
{
|
|
assert( m_memPos < c_memSize );
|
|
auto rv = &m_memory[ m_memPos++ ];
|
|
return rv;
|
|
}
|
|
|
|
void Push( VALUE* v )
|
|
{
|
|
m_stack[m_sp++] = v;
|
|
}
|
|
|
|
VALUE* Pop()
|
|
{
|
|
m_sp--;
|
|
return m_stack[m_sp];
|
|
}
|
|
|
|
int SP() const
|
|
{
|
|
return m_sp;
|
|
}
|
|
private:
|
|
std::vector<VALUE> m_memory;
|
|
VALUE* m_stack[128];
|
|
int m_sp = 0;
|
|
int m_memPos = 0;
|
|
};
|
|
|
|
typedef std::function<void(UCODE*, CONTEXT*, void*)> FUNC_PTR;
|
|
|
|
void AddOp( UOP* uop )
|
|
{
|
|
m_ucode.push_back(uop);
|
|
}
|
|
|
|
VALUE* Run();
|
|
std::string Dump() const;
|
|
void RuntimeError( const std::string aErrorMsg );
|
|
|
|
virtual VAR_REF* createVarRef( COMPILER* aCompiler, const std::string& var, const std::string& field )
|
|
{
|
|
return nullptr;
|
|
};
|
|
|
|
virtual FUNC_PTR createFuncCall( COMPILER* aCompiler, const std::string& name )
|
|
{
|
|
return nullptr;
|
|
};
|
|
|
|
private:
|
|
std::vector<UOP*> m_ucode;
|
|
};
|
|
|
|
|
|
class UOP
|
|
{
|
|
public:
|
|
UOP( int op, void* arg ) : m_op( op ), m_arg( arg )
|
|
{};
|
|
UOP( int op, UCODE::FUNC_PTR func, void *arg ) : m_op( op ), m_arg(arg), m_func( func )
|
|
{};
|
|
|
|
void Exec( UCODE::CONTEXT* ctx, UCODE *ucode );
|
|
std::string Format() const;
|
|
|
|
private:
|
|
int m_op;
|
|
void *m_arg;
|
|
UCODE::FUNC_PTR m_func;
|
|
};
|
|
|
|
class TOKENIZER
|
|
{
|
|
public:
|
|
void Restart( const std::string& 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;
|
|
}
|
|
|
|
|
|
std::string GetChars( std::function<bool( int )> cond ) const;
|
|
bool MatchAhead( std::string match, std::function<bool( int )> stopCond ) const;
|
|
private:
|
|
std::string 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();
|
|
|
|
/* Check if previous invokation of process() was successful */
|
|
inline bool IsValid() const
|
|
{
|
|
return !m_errorStatus.pendingError;
|
|
}
|
|
|
|
void setRoot( LIBEVAL::TREE_NODE root );
|
|
|
|
bool Compile( const std::string& aString, UCODE* aCode );
|
|
std::string DumpTree();
|
|
void ReportError( const std::string aErrorMsg );
|
|
ERROR_STATUS GetErrorStatus();
|
|
|
|
protected:
|
|
enum LEXER_STATE
|
|
{
|
|
LS_DEFAULT = 0,
|
|
LS_STRING = 1,
|
|
};
|
|
|
|
LEXER_STATE m_lexerState;
|
|
|
|
bool generateUCode( UCODE* aCode );
|
|
|
|
|
|
/* Token type used by the tokenizer */
|
|
struct T_TOKEN
|
|
{
|
|
int token;
|
|
TREE_NODE value;
|
|
};
|
|
|
|
/* Begin processing of a new input string */
|
|
void newString( const std::string& aString );
|
|
|
|
/* Tokenizer: Next token/value taken from input string. */
|
|
T_TOKEN getToken();
|
|
bool lexDefault( T_TOKEN& aToken );
|
|
bool lexString( T_TOKEN& aToken );
|
|
|
|
/* Used by processing loop */
|
|
void parse( int token, TREE_NODE value );
|
|
|
|
int resolveUnits();
|
|
|
|
UOP* makeUop( int op, double value )
|
|
{
|
|
auto uop = new UOP( op, new VALUE( value ) );
|
|
return uop;
|
|
}
|
|
|
|
UOP* makeUop( int op, std::string value )
|
|
{
|
|
auto uop = new UOP( op, new VALUE( value ) );
|
|
return uop;
|
|
}
|
|
|
|
UOP* makeUop( int op, VAR_REF* aRef = nullptr )
|
|
{
|
|
auto uop = new UOP( op, aRef );
|
|
return uop;
|
|
}
|
|
|
|
UOP* makeUop( int op, UCODE::FUNC_PTR aFunc, void *arg = nullptr )
|
|
{
|
|
auto uop = new UOP( op, aFunc, arg );
|
|
return uop;
|
|
}
|
|
|
|
|
|
/* Token state for input string. */
|
|
|
|
void* m_parser; // the current lemon parser state machine
|
|
TOKENIZER m_tokenizer;
|
|
char m_localeDecimalSeparator;
|
|
|
|
/* Parse progress. Set by parser actions. */
|
|
bool m_parseError;
|
|
bool m_parseFinished;
|
|
std::string m_parseErrorMessage;
|
|
std::string m_parseErrorToken;
|
|
int m_parseErrorPos;
|
|
|
|
std::unique_ptr<UNIT_RESOLVER> m_unitResolver;
|
|
ERROR_STATUS m_errorStatus;
|
|
|
|
TREE_NODE* m_tree;
|
|
};
|
|
|
|
|
|
} // namespace LIBEVAL
|
|
|
|
#endif /* LIBEVAL_COMPILER_H_ */
|