libeval_compiler: initial version
This commit is contained in:
parent
bff209b07c
commit
b82ec2161f
|
@ -53,6 +53,7 @@ add_subdirectory( eeschema )
|
|||
|
||||
add_subdirectory( libs )
|
||||
add_subdirectory( utils/kicad2step )
|
||||
add_subdirectory( libeval_compiler )
|
||||
|
||||
# Utility/debugging/profiling programs
|
||||
add_subdirectory( common_tools )
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
#
|
||||
# This program source code file is part of KiCad, a free EDA CAD application.
|
||||
#
|
||||
# Copyright (C) 2017 CERN
|
||||
# @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
|
||||
#
|
||||
# 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 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.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, you may find one here:
|
||||
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
# or you may search the http://www.gnu.org website for the version 2 license,
|
||||
# or you may write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
|
||||
find_package(Boost COMPONENTS unit_test_framework REQUIRED)
|
||||
find_package( wxWidgets 3.0.0 COMPONENTS gl aui adv html core net base xml stc REQUIRED )
|
||||
|
||||
find_program(LEMON lemon)
|
||||
|
||||
if( LEMON )
|
||||
|
||||
macro( generate_lemon_grammar TGT_NAME GRAMMAR_LEMON GRAMMAR_C )
|
||||
add_custom_target( ${TGT_NAME}
|
||||
DEPENDS ${GRAMMAR_LEMON}
|
||||
COMMAND ${LEMON} -q ${GRAMMAR_LEMON}
|
||||
COMMENT "Running Lemon on ${GRAMMAR_LEMON} -> ${GRAMMAR_C}"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
endmacro()
|
||||
|
||||
generate_lemon_grammar( libeval_grammar2 grammar.lemon grammar.c )
|
||||
|
||||
endif()
|
||||
|
||||
add_definitions(-DBOOST_TEST_DYN_LINK -DPCBNEW)
|
||||
|
||||
if( BUILD_GITHUB_PLUGIN )
|
||||
set( GITHUB_PLUGIN_LIBRARIES github_plugin )
|
||||
endif()
|
||||
|
||||
add_dependencies( pnsrouter pcbcommon pcad2kicadpcb libeval_grammar2 ${GITHUB_PLUGIN_LIBRARIES} )
|
||||
|
||||
add_executable( libeval_compiler_test
|
||||
libeval_compiler_test.cpp
|
||||
libeval_compiler.cpp
|
||||
../qa_utils/mocks.cpp
|
||||
../../common/base_units.cpp
|
||||
../../3d-viewer/3d_viewer/3d_viewer_settings.cpp
|
||||
)
|
||||
|
||||
include_directories( BEFORE ${INC_BEFORE} )
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/3d-viewer
|
||||
${CMAKE_SOURCE_DIR}/common
|
||||
${CMAKE_SOURCE_DIR}/pcbnew
|
||||
${CMAKE_SOURCE_DIR}/pcbnew/router
|
||||
${CMAKE_SOURCE_DIR}/pcbnew/tools
|
||||
${CMAKE_SOURCE_DIR}/pcbnew/dialogs
|
||||
${CMAKE_SOURCE_DIR}/polygon
|
||||
${CMAKE_SOURCE_DIR}/common/geometry
|
||||
${CMAKE_SOURCE_DIR}/qa/common
|
||||
${CMAKE_SOURCE_DIR}/qa/qa_utils
|
||||
${Boost_INCLUDE_DIR}
|
||||
${INC_AFTER}
|
||||
)
|
||||
|
||||
target_link_libraries( libeval_compiler_test
|
||||
pnsrouter
|
||||
common
|
||||
pcbcommon
|
||||
bitmaps
|
||||
pnsrouter
|
||||
common
|
||||
pcbcommon
|
||||
bitmaps
|
||||
pnsrouter
|
||||
common
|
||||
pcbcommon
|
||||
bitmaps
|
||||
pnsrouter
|
||||
common
|
||||
pcbcommon
|
||||
bitmaps
|
||||
gal
|
||||
pcad2kicadpcb
|
||||
altium2kicadpcb
|
||||
common
|
||||
pcbcommon
|
||||
${GITHUB_PLUGIN_LIBRARIES}
|
||||
common
|
||||
pcbcommon
|
||||
${Boost_FILESYSTEM_LIBRARY}
|
||||
${Boost_SYSTEM_LIBRARY}
|
||||
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
|
||||
${wxWidgets_LIBRARIES}
|
||||
)
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,24 @@
|
|||
#define G_IDENTIFIER 1
|
||||
#define G_ASSIGN 2
|
||||
#define G_SEMCOL 3
|
||||
#define G_BOOL_OR 4
|
||||
#define G_BOOL_AND 5
|
||||
#define G_BOOL_XOR 6
|
||||
#define G_LESS_THAN 7
|
||||
#define G_GREATER_THAN 8
|
||||
#define G_LESS_EQUAL_THAN 9
|
||||
#define G_GREATER_EQUAL_THAN 10
|
||||
#define G_EQUAL 11
|
||||
#define G_NOT_EQUAL 12
|
||||
#define G_UNIT 13
|
||||
#define G_BOOL_NOT 14
|
||||
#define G_PLUS 15
|
||||
#define G_MINUS 16
|
||||
#define G_DIVIDE 17
|
||||
#define G_MULT 18
|
||||
#define G_STRUCT_REF 19
|
||||
#define G_ENDS 20
|
||||
#define G_VALUE 21
|
||||
#define G_STRING 22
|
||||
#define G_PARENL 23
|
||||
#define G_PARENR 24
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
This file is part of libeval, a simple math expression evaluator
|
||||
|
||||
Copyright (C) 2017 Michael Geselbracht, mgeselbracht3@gmail.com
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
%token_type { LIBEVAL::TREE_NODE }
|
||||
%extra_argument { LIBEVAL::COMPILER* pEval }
|
||||
|
||||
%nonassoc G_IDENTIFIER G_ASSIGN G_SEMCOL.
|
||||
%nonassoc G_BOOL_OR G_BOOL_AND G_BOOL_XOR.
|
||||
%nonassoc G_LESS_THAN G_GREATER_THAN G_LESS_EQUAL_THAN G_GREATER_EQUAL_THAN.
|
||||
%nonassoc G_EQUAL G_NOT_EQUAL.
|
||||
%right G_UNIT.
|
||||
%left G_BOOL_NOT.
|
||||
%left G_PLUS G_MINUS.
|
||||
%left G_DIVIDE G_MULT.
|
||||
%nonassoc G_STRUCT_REF.
|
||||
|
||||
%include {
|
||||
#include <assert.h>
|
||||
#include "libeval_compiler.h"
|
||||
}
|
||||
|
||||
%syntax_error {
|
||||
pEval->parseError("Syntax error");
|
||||
}
|
||||
|
||||
%parse_accept {
|
||||
pEval->parseOk();
|
||||
}
|
||||
|
||||
main ::= in.
|
||||
|
||||
/* Allow multiple statements in input string: x=1; y=2 */
|
||||
in ::= stmt.
|
||||
in ::= in stmt.
|
||||
|
||||
/* A statement can be empty, an expr or an expr followed by ';' */
|
||||
stmt ::= G_ENDS.
|
||||
stmt ::= expr(A) G_ENDS. { pEval->setRoot(A); }
|
||||
//stmt ::= expr G_SEMCOL. { pEval->setRoot(NULL) }
|
||||
|
||||
expr(A) ::= G_VALUE(B). { A.op = TR_NUMBER; A.value = B.value; A.leaf[0] = A.leaf[1] = NULL; A.valid = true; }
|
||||
expr(A) ::= G_VALUE(B) G_UNIT(C). { A.op = TR_NUMBER; A.value = B.value; A.leaf[0] = newNode( TR_UNIT, C.value.type, ""); A.leaf[1] = NULL; A.valid = true; }
|
||||
expr(A) ::= G_STRING(B). { A.op = TR_STRING; A.value = B.value; A.leaf[0] = A.leaf[1] = NULL; A.valid = true; }
|
||||
expr(A) ::= G_IDENTIFIER(B). { A.op = TR_IDENTIFIER; A.value = B.value; A.leaf[0] = A.leaf[1] = NULL; A.valid = true; }
|
||||
expr(A) ::= expr(B) G_LESS_THAN expr(C). { A.op = TR_OP_LESS; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
|
||||
expr(A) ::= expr(B) G_GREATER_THAN expr(C). { A.op = TR_OP_GREATER; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
|
||||
expr(A) ::= expr(B) G_LESS_EQUAL_THAN expr(C). { A.op = TR_OP_LESS_EQUAL; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
|
||||
expr(A) ::= expr(B) G_GREATER_EQUAL_THAN expr(C). { A.op = TR_OP_GREATER_EQUAL; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
|
||||
expr(A) ::= expr(B) G_NOT_EQUAL expr(C). { A.op = TR_OP_NOT_EQUAL; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
|
||||
expr(A) ::= expr(B) G_BOOL_AND expr(C). { A.op = TR_OP_BOOL_AND; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
|
||||
expr(A) ::= expr(B) G_BOOL_OR expr(C). { A.op = TR_OP_BOOL_OR; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
|
||||
expr(A) ::= expr(B) G_PLUS expr(C). { A.op = TR_OP_ADD; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
|
||||
expr(A) ::= expr(B) G_MINUS expr(C). { A.op = TR_OP_SUB; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
|
||||
expr(A) ::= expr(B) G_MULT expr(C). { A.op = TR_OP_MUL; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
|
||||
expr(A) ::= expr(B) G_DIVIDE expr(C). { A.op = TR_OP_DIV; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
|
||||
expr(A) ::= expr(B) G_EQUAL expr(C). { A.op = TR_OP_EQUAL; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
|
||||
|
||||
expr(A) ::= expr(B) G_STRUCT_REF expr(C). { A.op = TR_STRUCT_REF; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
|
||||
|
||||
expr(A) ::= G_PARENL expr(B) G_PARENR. { A.op = B.op; A.value = B.value; A.valid=B.valid; A.leaf[0] = B.leaf[0]; A.leaf[1] = B.leaf[1]; }
|
||||
expr(A) ::= G_IDENTIFIER(func_name) G_PARENL expr(B) G_PARENR. { A.op = TR_OP_FUNC_CALL; A.leaf[0] = copyNode(func_name); A.leaf[1] = copyNode(B); A.valid=1; }
|
|
@ -0,0 +1,735 @@
|
|||
/*
|
||||
This file is part of libeval, a simple math expression evaluator
|
||||
|
||||
Copyright (C) 2017 Michael Geselbracht, mgeselbracht3@gmail.com
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
|
||||
#include "libeval_compiler.h"
|
||||
|
||||
/* The (generated) lemon parser is written in C.
|
||||
* In order to keep its symbol from the global namespace include the parser code with
|
||||
* a C++ namespace.
|
||||
*/
|
||||
namespace LIBEVAL
|
||||
{
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
#pragma GCC diagnostic ignored "-Wsign-compare"
|
||||
#endif
|
||||
|
||||
#include "grammar.c"
|
||||
#include "grammar.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
static void libeval_dbg( const char* fmt, ... )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
va_list ap;
|
||||
va_start( ap, fmt );
|
||||
fprintf( stderr, "libeval: " );
|
||||
vfprintf( stderr, fmt, ap );
|
||||
va_end( ap );
|
||||
#endif
|
||||
}
|
||||
|
||||
static const std::string formatOpName( int op )
|
||||
{
|
||||
static const struct
|
||||
{
|
||||
int op;
|
||||
std::string mnemonic;
|
||||
} simpleOps[] = { { TR_OP_MUL, "MUL" }, { TR_OP_DIV, "DIV" }, { TR_OP_ADD, "ADD" },
|
||||
{ TR_OP_SUB, "SUB" }, { TR_OP_LESS, "LESS" }, { TR_OP_GREATER, "GREATER" },
|
||||
{ TR_OP_LESS_EQUAL, "LESS_EQUAL" }, { TR_OP_GREATER_EQUAL, "GREATER_EQUAL" },
|
||||
{ TR_OP_EQUAL, "EQUAL" }, { TR_OP_NOT_EQUAL, "NEQUAL" }, { TR_OP_BOOL_AND, "AND" },
|
||||
{ TR_OP_BOOL_OR, "OR" }, { TR_OP_BOOL_NOT, "NOT" }, { -1, "" } };
|
||||
|
||||
for( int i = 0; simpleOps[i].op >= 0; i++ )
|
||||
{
|
||||
if( simpleOps[i].op == op )
|
||||
{
|
||||
return simpleOps[i].mnemonic;
|
||||
}
|
||||
}
|
||||
|
||||
return "???";
|
||||
}
|
||||
|
||||
|
||||
std::string UCODE::UOP::Format() const
|
||||
{
|
||||
char str[1024];
|
||||
|
||||
switch( m_op )
|
||||
{
|
||||
case TR_UOP_PUSH_VAR:
|
||||
sprintf( str, "PUSH VAR [%p]", m_arg );
|
||||
break;
|
||||
case TR_UOP_PUSH_VALUE:
|
||||
{
|
||||
auto val = reinterpret_cast<VALUE*>( m_arg );
|
||||
if( val->GetType() == VT_NUMERIC )
|
||||
sprintf( str, "PUSH NUM [%.10f]", val->AsDouble() );
|
||||
else
|
||||
sprintf( str, "PUSH STR [%s]", val->AsString().c_str() );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
sprintf( str, "%s", formatOpName( m_op ).c_str() );
|
||||
break;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string UCODE::Dump() const
|
||||
{
|
||||
std::string rv;
|
||||
for( auto op : m_ucode )
|
||||
{
|
||||
rv += op->Format();
|
||||
rv += "\n";
|
||||
}
|
||||
return rv;
|
||||
};
|
||||
|
||||
std::string TOKENIZER::GetChars( std::function<bool( int )> cond ) const
|
||||
{
|
||||
std::string rv;
|
||||
size_t p = m_pos;
|
||||
// printf("p %d len %d\n", p, str.length() );
|
||||
while( p < m_str.length() && cond( m_str[p] ) )
|
||||
{
|
||||
rv.append( 1, m_str[p] );
|
||||
p++;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool TOKENIZER::MatchAhead( std::string match, std::function<bool( int )> stopCond ) const
|
||||
{
|
||||
int remaining = m_str.length() - m_pos;
|
||||
if( remaining < (int) match.length() )
|
||||
return false;
|
||||
|
||||
if( m_str.substr( m_pos, match.length() ) == match )
|
||||
{
|
||||
return ( remaining == (int) match.length() || stopCond( m_str[m_pos + match.length()] ) );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
COMPILER::COMPILER()
|
||||
{
|
||||
m_localeDecimalSeparator = '.';
|
||||
m_parseError = false;
|
||||
m_parseFinished = false;
|
||||
m_unitResolver.reset( new UNIT_RESOLVER );
|
||||
m_parser = LIBEVAL::ParseAlloc( malloc );
|
||||
}
|
||||
|
||||
|
||||
COMPILER::~COMPILER()
|
||||
{
|
||||
LIBEVAL::ParseFree( m_parser, free );
|
||||
|
||||
// Allow explicit call to destructor
|
||||
m_parser = nullptr;
|
||||
|
||||
Clear();
|
||||
}
|
||||
|
||||
|
||||
void COMPILER::Clear()
|
||||
{
|
||||
//free( current.token );
|
||||
m_tokenizer.Clear();
|
||||
m_parseError = true;
|
||||
}
|
||||
|
||||
|
||||
void COMPILER::parseError( const char* s )
|
||||
{
|
||||
m_parseError = true;
|
||||
}
|
||||
|
||||
|
||||
void COMPILER::parseOk()
|
||||
{
|
||||
m_parseFinished = true;
|
||||
}
|
||||
|
||||
|
||||
bool COMPILER::Compile( const std::string& aString, UCODE* aCode )
|
||||
{
|
||||
// Feed parser token after token until end of input.
|
||||
|
||||
newString( aString );
|
||||
m_tree = nullptr;
|
||||
m_parseError = false;
|
||||
m_parseFinished = false;
|
||||
T_TOKEN tok;
|
||||
|
||||
libeval_dbg( "str: '%s' empty: %d\n", aString.c_str(), !!aString.empty() );
|
||||
|
||||
if( aString.empty() )
|
||||
{
|
||||
m_parseFinished = true;
|
||||
return generateUCode( aCode );
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
tok = getToken();
|
||||
libeval_dbg( "parse: tok %d\n", tok.token );
|
||||
Parse( m_parser, tok.token, tok.value, this );
|
||||
//printf('error')
|
||||
if( m_parseError )
|
||||
{
|
||||
//printf( "PARSE ERR\n" );
|
||||
m_parseErrorToken = "";
|
||||
m_parseErrorPos = m_tokenizer.GetPos();
|
||||
return false;
|
||||
}
|
||||
|
||||
if( m_parseFinished || tok.token == G_ENDS )
|
||||
{
|
||||
// Reset parser by passing zero as token ID, value is ignored.
|
||||
Parse( m_parser, 0, tok.value, this );
|
||||
break;
|
||||
}
|
||||
} while( tok.token );
|
||||
|
||||
return generateUCode( aCode );
|
||||
}
|
||||
|
||||
|
||||
void COMPILER::newString( const std::string& aString )
|
||||
{
|
||||
Clear();
|
||||
|
||||
m_lexerState = LS_DEFAULT;
|
||||
m_tokenizer.Restart( aString );
|
||||
m_parseFinished = false;
|
||||
}
|
||||
|
||||
COMPILER::T_TOKEN COMPILER::getToken()
|
||||
{
|
||||
T_TOKEN rv;
|
||||
bool done = false;
|
||||
do
|
||||
{
|
||||
|
||||
switch( m_lexerState )
|
||||
{
|
||||
case LS_DEFAULT:
|
||||
done = lexDefault( rv );
|
||||
break;
|
||||
case LS_STRING:
|
||||
done = lexString( rv );
|
||||
break;
|
||||
}
|
||||
//printf( "-> lstate %d done %d\n", m_lexerState, !!done );
|
||||
} while( !done );
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
bool COMPILER::lexString( COMPILER::T_TOKEN& aToken )
|
||||
{
|
||||
auto str = m_tokenizer.GetChars( []( int c ) -> bool { return c != '"'; } );
|
||||
//printf("STR LIT '%s'\n", (const char *)str.c_str() );
|
||||
|
||||
aToken.token = G_STRING;
|
||||
strcpy( aToken.value.value.str, str.c_str() );
|
||||
|
||||
m_tokenizer.NextChar( str.length() + 1 );
|
||||
m_lexerState = LS_DEFAULT;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int COMPILER::resolveUnits()
|
||||
{
|
||||
int unitId = 0;
|
||||
for( auto unitName : m_unitResolver->GetSupportedUnits() )
|
||||
{
|
||||
if( m_tokenizer.MatchAhead( unitName, []( int c ) -> bool { return !isalnum( c ); } ) )
|
||||
{
|
||||
libeval_dbg( "Match unit '%s'\n", unitName.c_str() );
|
||||
m_tokenizer.NextChar( unitName.length() );
|
||||
return unitId;
|
||||
}
|
||||
|
||||
unitId++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool COMPILER::lexDefault( COMPILER::T_TOKEN& aToken )
|
||||
{
|
||||
T_TOKEN retval;
|
||||
std::string current;
|
||||
size_t idx;
|
||||
int convertFrom;
|
||||
|
||||
retval.token = G_ENDS;
|
||||
|
||||
//printf( "tokdone %d\n", !!m_tokenizer.Done() );
|
||||
if( m_tokenizer.Done() )
|
||||
{
|
||||
aToken = retval;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto isDecimalSeparator = [&]( char ch ) -> bool {
|
||||
return ( ch == m_localeDecimalSeparator || ch == '.' || ch == ',' );
|
||||
};
|
||||
|
||||
// Lambda: get value as string, store into clToken.token and update current index.
|
||||
auto extractNumber = [&]() {
|
||||
bool haveSeparator = false;
|
||||
idx = 0;
|
||||
auto ch = m_tokenizer.GetChar();
|
||||
|
||||
do
|
||||
{
|
||||
if( isDecimalSeparator( ch ) && haveSeparator )
|
||||
break;
|
||||
|
||||
current.append( 1, ch );
|
||||
|
||||
if( isDecimalSeparator( ch ) )
|
||||
haveSeparator = true;
|
||||
|
||||
m_tokenizer.NextChar();
|
||||
ch = m_tokenizer.GetChar();
|
||||
} while( isdigit( ch ) || isDecimalSeparator( ch ) );
|
||||
|
||||
// Ensure that the systems decimal separator is used
|
||||
for( int i = current.length(); i; i-- )
|
||||
if( isDecimalSeparator( current[i - 1] ) )
|
||||
current[i - 1] = m_localeDecimalSeparator;
|
||||
|
||||
|
||||
//printf("-> NUM: '%s'\n", (const char *) current.c_str() );
|
||||
};
|
||||
|
||||
|
||||
int ch;
|
||||
|
||||
// Start processing of first/next token: Remove whitespace
|
||||
for( ;; )
|
||||
{
|
||||
ch = m_tokenizer.GetChar();
|
||||
|
||||
if( ch == ' ' )
|
||||
m_tokenizer.NextChar();
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
libeval_dbg( "LEX ch '%c' pos %d\n", ch, m_tokenizer.GetPos() );
|
||||
|
||||
if( ch == 0 )
|
||||
{
|
||||
/* End of input */
|
||||
}
|
||||
else if( isdigit( ch ) )
|
||||
{
|
||||
// VALUE
|
||||
extractNumber();
|
||||
retval.token = G_VALUE;
|
||||
strcpy( retval.value.value.str, current.c_str() );
|
||||
}
|
||||
else if( ( convertFrom = resolveUnits() ) >= 0 )
|
||||
{
|
||||
//printf("unit\n");
|
||||
// UNIT
|
||||
// Units are appended to a VALUE.
|
||||
// Determine factor to default unit if unit for value is given.
|
||||
// Example: Default is mm, unit is inch: factor is 25.4
|
||||
// The factor is assigned to the terminal UNIT. The actual
|
||||
// conversion is done within a parser action.
|
||||
retval.token = G_UNIT;
|
||||
retval.value.value.type = convertFrom;
|
||||
}
|
||||
else if( ch == '\"' ) // string literal
|
||||
{
|
||||
//printf( "MATCH STRING LITERAL\n" );
|
||||
m_lexerState = LS_STRING;
|
||||
m_tokenizer.NextChar();
|
||||
return false;
|
||||
}
|
||||
else if( isalpha( ch ) )
|
||||
{
|
||||
//printf("ALPHA\n");
|
||||
current = m_tokenizer.GetChars( []( int c ) -> bool { return isalnum( c ); } );
|
||||
//printf("Len: %d\n", current.length() );
|
||||
//printf("id '%s'\n", (const char *) current.c_str() );
|
||||
fflush( stdout );
|
||||
retval.token = G_IDENTIFIER;
|
||||
strcpy( retval.value.value.str, current.c_str() );
|
||||
m_tokenizer.NextChar( current.length() );
|
||||
}
|
||||
else if( m_tokenizer.MatchAhead( "==", []( int c ) -> bool { return c != '='; } ) )
|
||||
{
|
||||
retval.token = G_EQUAL;
|
||||
m_tokenizer.NextChar( 2 );
|
||||
//printf( "nc pos %d\n", m_tokenizer.GetPos() );
|
||||
}
|
||||
else if( m_tokenizer.MatchAhead( "<=", []( int c ) -> bool { return c != '='; } ) )
|
||||
{
|
||||
retval.token = G_LESS_EQUAL_THAN;
|
||||
m_tokenizer.NextChar( 2 );
|
||||
}
|
||||
else if( m_tokenizer.MatchAhead( ">=", []( int c ) -> bool { return c != '='; } ) )
|
||||
{
|
||||
retval.token = G_GREATER_EQUAL_THAN;
|
||||
m_tokenizer.NextChar( 2 );
|
||||
}
|
||||
else if( m_tokenizer.MatchAhead( "&&", []( int c ) -> bool { return c != '&'; } ) )
|
||||
{
|
||||
retval.token = G_BOOL_AND;
|
||||
m_tokenizer.NextChar( 2 );
|
||||
}
|
||||
else if( m_tokenizer.MatchAhead( "||", []( int c ) -> bool { return c != '|'; } ) )
|
||||
{
|
||||
retval.token = G_BOOL_OR;
|
||||
m_tokenizer.NextChar( 2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
//printf( "WTF: '%c'\n", ch );
|
||||
|
||||
// Single char tokens
|
||||
switch( ch )
|
||||
{
|
||||
case '+':
|
||||
retval.token = G_PLUS;
|
||||
break;
|
||||
case '!':
|
||||
retval.token = G_BOOL_NOT;
|
||||
break;
|
||||
case '-':
|
||||
retval.token = G_MINUS;
|
||||
break;
|
||||
case '*':
|
||||
retval.token = G_MULT;
|
||||
break;
|
||||
case '/':
|
||||
retval.token = G_DIVIDE;
|
||||
break;
|
||||
case '<':
|
||||
retval.token = G_LESS_THAN;
|
||||
break;
|
||||
case '>':
|
||||
retval.token = G_GREATER_THAN;
|
||||
break;
|
||||
case '(':
|
||||
retval.token = G_PARENL;
|
||||
break;
|
||||
case ')':
|
||||
retval.token = G_PARENR;
|
||||
break;
|
||||
case ';':
|
||||
retval.token = G_SEMCOL;
|
||||
break;
|
||||
case '.':
|
||||
retval.token = G_STRUCT_REF;
|
||||
break;
|
||||
default:
|
||||
m_parseError = true;
|
||||
break; /* invalid character */
|
||||
}
|
||||
|
||||
m_tokenizer.NextChar();
|
||||
}
|
||||
|
||||
aToken = retval;
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string formatNode( TREE_NODE* tok )
|
||||
{
|
||||
char str[1024];
|
||||
// printf("fmt tok %p v %p ", tok, tok->value.v );
|
||||
fflush( stdout );
|
||||
sprintf( str, "%s", (const char*) tok->value.str );
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
void dumpNode( std::string& buf, TREE_NODE* tok, int depth = 0 )
|
||||
{
|
||||
char str[1024];
|
||||
sprintf( str, "\n[%p] ", tok ); //[tok %p] ", tok);
|
||||
buf += str;
|
||||
for( int i = 0; i < 2 * depth; i++ )
|
||||
buf += " ";
|
||||
|
||||
if( tok->op & TR_OP_BINARY_MASK )
|
||||
{
|
||||
sprintf( str, "%s", (const char*) formatOpName( tok->op ).c_str() );
|
||||
buf += str;
|
||||
dumpNode( buf, tok->leaf[0], depth + 1 );
|
||||
dumpNode( buf, tok->leaf[1], depth + 1 );
|
||||
}
|
||||
|
||||
switch( tok->op )
|
||||
{
|
||||
case TR_NUMBER:
|
||||
sprintf( str, "NUMERIC: " );
|
||||
buf += str;
|
||||
sprintf( str, "%s", formatNode( tok ).c_str() );
|
||||
buf += str;
|
||||
if( tok->leaf[0] )
|
||||
dumpNode( buf, tok->leaf[0], depth + 1 );
|
||||
break;
|
||||
case TR_STRING:
|
||||
sprintf( str, "STRING: " );
|
||||
buf += str;
|
||||
sprintf( str, "%s", formatNode( tok ).c_str() );
|
||||
buf += str;
|
||||
break;
|
||||
case TR_IDENTIFIER:
|
||||
sprintf( str, "ID: " );
|
||||
buf += str;
|
||||
sprintf( str, "%s", formatNode( tok ).c_str() );
|
||||
buf += str;
|
||||
break;
|
||||
case TR_STRUCT_REF:
|
||||
sprintf( str, "SREF: " );
|
||||
buf += str;
|
||||
dumpNode( buf, tok->leaf[0], depth + 1 );
|
||||
dumpNode( buf, tok->leaf[1], depth + 1 );
|
||||
break;
|
||||
case TR_UNIT:
|
||||
sprintf( str, "UNIT: %d ", tok->value.type );
|
||||
buf += str;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void COMPILER::setRoot( TREE_NODE root )
|
||||
{
|
||||
m_tree = copyNode( root );
|
||||
}
|
||||
|
||||
|
||||
bool COMPILER::generateUCode( UCODE* aCode )
|
||||
{
|
||||
std::vector<TREE_NODE*> stack;
|
||||
std::set<TREE_NODE*> visitedNodes;
|
||||
|
||||
auto visited = [&]( TREE_NODE* node ) -> bool {
|
||||
return visitedNodes.find( node ) != visitedNodes.end();
|
||||
};
|
||||
|
||||
UCODE code;
|
||||
|
||||
assert( m_tree );
|
||||
|
||||
stack.push_back( m_tree );
|
||||
|
||||
//printf("compile: tree %p\n", m_tree);
|
||||
|
||||
while( !stack.empty() )
|
||||
{
|
||||
auto node = stack.back();
|
||||
bool isTerminalNode = true;
|
||||
|
||||
// printf( "process node %p [op %d] [stack %d]\n", node, node->op, stack.size() );
|
||||
|
||||
// process terminal nodes first
|
||||
switch( node->op )
|
||||
{
|
||||
case TR_STRUCT_REF:
|
||||
{
|
||||
assert( node->leaf[0]->op == TR_IDENTIFIER );
|
||||
assert( node->leaf[1]->op == TR_IDENTIFIER );
|
||||
|
||||
auto vref = aCode->createVarRef( node->leaf[0]->value.str, node->leaf[1]->value.str );
|
||||
aCode->AddOp( TR_UOP_PUSH_VAR, vref );
|
||||
break;
|
||||
}
|
||||
|
||||
case TR_NUMBER:
|
||||
{
|
||||
auto son = node->leaf[0];
|
||||
|
||||
double value = atof( node->value.str ); // fixme: locale
|
||||
|
||||
if( son && son->op == TR_UNIT )
|
||||
{
|
||||
//printf( "HandleUnit: %s unit %d\n", node->value.str, son->value.type );
|
||||
|
||||
value = m_unitResolver->Convert( node->value.str, son->value.type );
|
||||
visitedNodes.insert( son );
|
||||
}
|
||||
|
||||
aCode->AddOp( TR_UOP_PUSH_VALUE, value );
|
||||
|
||||
break;
|
||||
}
|
||||
case TR_STRING:
|
||||
{
|
||||
aCode->AddOp( TR_UOP_PUSH_VALUE, node->value.str );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
isTerminalNode = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if( isTerminalNode )
|
||||
{
|
||||
visitedNodes.insert( node );
|
||||
stack.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
if( node->leaf[0] && !visited( node->leaf[0] ) )
|
||||
{
|
||||
stack.push_back( node->leaf[0] );
|
||||
}
|
||||
else if( node->leaf[1] && !visited( node->leaf[1] ) )
|
||||
{
|
||||
stack.push_back( node->leaf[1] );
|
||||
}
|
||||
else
|
||||
{
|
||||
aCode->AddOp( node->op );
|
||||
visitedNodes.insert( node );
|
||||
stack.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void UCODE::UOP::Exec( CONTEXT* ctx, UCODE* ucode )
|
||||
{
|
||||
|
||||
switch( m_op )
|
||||
{
|
||||
case TR_UOP_PUSH_VAR:
|
||||
{
|
||||
auto value = ctx->AllocValue();
|
||||
value->Set( reinterpret_cast<VAR_REF*>( m_arg )->GetValue( ucode ) );
|
||||
ctx->Push( value );
|
||||
break;
|
||||
}
|
||||
case TR_UOP_PUSH_VALUE:
|
||||
ctx->Push( reinterpret_cast<VALUE*>( m_arg ) );
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if( m_op & TR_OP_BINARY_MASK )
|
||||
{
|
||||
auto arg2 = ctx->Pop();
|
||||
auto arg1 = ctx->Pop();
|
||||
double result;
|
||||
|
||||
switch( m_op )
|
||||
{
|
||||
case TR_OP_ADD:
|
||||
result = arg1->AsDouble() + arg2->AsDouble();
|
||||
break;
|
||||
case TR_OP_SUB:
|
||||
result = arg1->AsDouble() - arg2->AsDouble();
|
||||
break;
|
||||
case TR_OP_MUL:
|
||||
result = arg1->AsDouble() * arg2->AsDouble();
|
||||
break;
|
||||
case TR_OP_DIV:
|
||||
result = arg1->AsDouble() / arg2->AsDouble();
|
||||
break;
|
||||
case TR_OP_LESS_EQUAL:
|
||||
result = arg1->AsDouble() <= arg2->AsDouble() ? 1 : 0;
|
||||
break;
|
||||
case TR_OP_GREATER_EQUAL:
|
||||
result = arg1->AsDouble() >= arg2->AsDouble() ? 1 : 0;
|
||||
break;
|
||||
case TR_OP_LESS:
|
||||
result = arg1->AsDouble() < arg2->AsDouble() ? 1 : 0;
|
||||
break;
|
||||
case TR_OP_GREATER:
|
||||
result = arg1->AsDouble() > arg2->AsDouble() ? 1 : 0;
|
||||
break;
|
||||
case TR_OP_EQUAL:
|
||||
result = arg1->EqualTo( arg2 ) ? 1 : 0;
|
||||
break;
|
||||
case TR_OP_NOT_EQUAL:
|
||||
result = arg1->EqualTo( arg2 ) ? 0 : 1;
|
||||
break;
|
||||
case TR_OP_BOOL_AND:
|
||||
result = ( ( arg1->AsDouble() != 0.0 ? true : false )
|
||||
&& ( arg2->AsDouble() != 0.0 ? true : false ) ) ?
|
||||
1 :
|
||||
0;
|
||||
break;
|
||||
case TR_OP_BOOL_OR:
|
||||
result = ( ( arg1->AsDouble() != 0.0 ? true : false )
|
||||
|| ( arg2->AsDouble() != 0.0 ? true : false ) ) ?
|
||||
1 :
|
||||
0;
|
||||
break;
|
||||
default:
|
||||
result = 0.0;
|
||||
break;
|
||||
}
|
||||
|
||||
auto rp = ctx->AllocValue();
|
||||
rp->Set( result );
|
||||
ctx->Push( rp );
|
||||
return;
|
||||
}
|
||||
else if( m_op & TR_OP_UNARY_MASK )
|
||||
{
|
||||
// fixme : not operator
|
||||
}
|
||||
}
|
||||
|
||||
VALUE* UCODE::Run()
|
||||
{
|
||||
CONTEXT ctx;
|
||||
for( const auto op : m_ucode )
|
||||
op->Exec( &ctx, this );
|
||||
|
||||
assert( ctx.SP() == 1 );
|
||||
return ctx.Pop();
|
||||
}
|
||||
|
||||
} // namespace LIBEVAL
|
|
@ -0,0 +1,471 @@
|
|||
/*
|
||||
This file is part of libeval, a simple math expression evaluator
|
||||
|
||||
Copyright (C) 2007 Michael Geselbracht, mgeselbracht3@gmail.com
|
||||
|
||||
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_UOP_PUSH_VAR 1
|
||||
#define TR_UOP_PUSH_VALUE 2
|
||||
|
||||
// This namespace is used for the lemon parser
|
||||
namespace LIBEVAL
|
||||
{
|
||||
|
||||
struct ERROR_STATUS
|
||||
{
|
||||
bool pendingError;
|
||||
|
||||
enum STAGE {
|
||||
CST_PARSE = 0,
|
||||
CST_CODEGEN,
|
||||
CST_RUNTIME
|
||||
};
|
||||
|
||||
std::string message;
|
||||
std::string failingObject;
|
||||
int failingPosition;
|
||||
};
|
||||
|
||||
|
||||
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
|
||||
|
||||
struct TREE_NODE
|
||||
{
|
||||
struct value_s
|
||||
{
|
||||
char str[LIBEVAL_MAX_LITERAL_LENGTH];
|
||||
int type;
|
||||
} value;
|
||||
int op;
|
||||
TREE_NODE* leaf[2];
|
||||
bool valid;
|
||||
};
|
||||
|
||||
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];
|
||||
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;
|
||||
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
|
||||
{
|
||||
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;
|
||||
};
|
||||
|
||||
class UOP
|
||||
{
|
||||
public:
|
||||
UOP( int op, void* arg ) : m_op( op ), m_arg( arg )
|
||||
{};
|
||||
|
||||
void Exec( CONTEXT* ctx, UCODE *ucode );
|
||||
std::string Format() const;
|
||||
|
||||
private:
|
||||
int m_op;
|
||||
void *m_arg;
|
||||
};
|
||||
|
||||
class VAR_REF
|
||||
{
|
||||
public:
|
||||
virtual VAR_TYPE_T GetType( const UCODE* aUcode ) const = 0;
|
||||
virtual VALUE GetValue( const UCODE* aUcode ) const = 0;
|
||||
};
|
||||
|
||||
|
||||
void AddOp( int op, double value )
|
||||
{
|
||||
auto uop = new UOP( op, new VALUE( value ) );
|
||||
m_ucode.push_back( uop );
|
||||
}
|
||||
|
||||
void AddOp( int op, std::string value )
|
||||
{
|
||||
auto uop = new UOP( op, new VALUE( value ) );
|
||||
m_ucode.push_back( uop );
|
||||
}
|
||||
|
||||
void AddOp( int op, VAR_REF* aRef = nullptr )
|
||||
{
|
||||
auto uop = new UOP( op, aRef );
|
||||
m_ucode.push_back( uop );
|
||||
}
|
||||
|
||||
VALUE* Run();
|
||||
std::string Dump() const;
|
||||
|
||||
virtual VAR_REF* createVarRef( const std::string& var, const std::string& field )
|
||||
{
|
||||
return NULL;
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<UOP*> m_ucode;
|
||||
};
|
||||
|
||||
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_parseError;
|
||||
}
|
||||
|
||||
const std::string GetParseErrorToken() const
|
||||
{
|
||||
return m_parseErrorToken;
|
||||
}
|
||||
|
||||
const std::string GetParseErrorMessage() const
|
||||
{
|
||||
return m_parseErrorMessage;
|
||||
}
|
||||
|
||||
int GetParseErrorPosition() const
|
||||
{
|
||||
return m_parseErrorPos;
|
||||
}
|
||||
|
||||
void setRoot( LIBEVAL::TREE_NODE root );
|
||||
|
||||
bool Compile( const std::string& aString, UCODE* aCode );
|
||||
std::string DumpTree();
|
||||
|
||||
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 );
|
||||
|
||||
void* m_parser; // the current lemon parser state machine
|
||||
int resolveUnits();
|
||||
|
||||
/* Token state for input string. */
|
||||
|
||||
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;
|
||||
|
||||
TREE_NODE* m_tree;
|
||||
};
|
||||
|
||||
|
||||
} // namespace LIBEVAL
|
||||
|
||||
#endif /* LIBEVAL_COMPILER_H_ */
|
|
@ -0,0 +1,324 @@
|
|||
#include <wx/wx.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include "class_board.h"
|
||||
#include "class_track.h"
|
||||
#include "libeval_compiler.h"
|
||||
|
||||
|
||||
#include <io_mgr.h>
|
||||
#include <kicad_plugin.h>
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#include <profile.h>
|
||||
|
||||
class PCB_EXPR_VAR_REF;
|
||||
|
||||
class PCB_EXPR_UCODE : public LIBEVAL::UCODE
|
||||
{
|
||||
public:
|
||||
|
||||
virtual VAR_REF *createVarRef( const std::string &var, const std::string &field ) override;
|
||||
|
||||
void SetItems( BOARD_ITEM *a, BOARD_ITEM* b )
|
||||
{
|
||||
m_items[0] = a;
|
||||
m_items[1] = b;
|
||||
}
|
||||
|
||||
BOARD_ITEM *GetItem( int index ) const
|
||||
{
|
||||
return m_items[index];
|
||||
}
|
||||
|
||||
private:
|
||||
BOARD_ITEM *m_items[2];
|
||||
};
|
||||
|
||||
|
||||
class PCB_EXPR_VAR_REF : public LIBEVAL::UCODE::VAR_REF
|
||||
{
|
||||
public:
|
||||
PCB_EXPR_VAR_REF ( int aItemIndex )
|
||||
: m_itemIndex(aItemIndex)
|
||||
{
|
||||
//printf("*** createVarRef %p %d\n", this, aItemIndex );
|
||||
}
|
||||
|
||||
void SetType( LIBEVAL::VAR_TYPE_T type )
|
||||
{
|
||||
m_type = type;
|
||||
}
|
||||
|
||||
void AddAllowedClass ( TYPE_ID type_hash, PROPERTY_BASE *prop )
|
||||
{
|
||||
m_matchingTypes[type_hash] = prop;
|
||||
}
|
||||
|
||||
virtual LIBEVAL::VAR_TYPE_T GetType( const LIBEVAL::UCODE* aUcode ) const override
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
virtual LIBEVAL::VALUE GetValue( const LIBEVAL::UCODE* aUcode ) const override
|
||||
{
|
||||
auto ucode = static_cast<const PCB_EXPR_UCODE*> (aUcode);
|
||||
auto item = ucode->GetItem( m_itemIndex );
|
||||
|
||||
auto it = m_matchingTypes.find( TYPE_HASH( *item ) );
|
||||
|
||||
if( it == m_matchingTypes.end() )
|
||||
{
|
||||
printf("Null field!\n");
|
||||
return LIBEVAL::VALUE(0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if( m_type == LIBEVAL::VT_NUMERIC )
|
||||
return LIBEVAL::VALUE( (double) item->Get<int>( it->second ) );
|
||||
else
|
||||
{
|
||||
wxString str = item->Get<wxString>( it->second );
|
||||
//printf("item %p GetStr '%s'\n", item, (const char*) str.c_str());
|
||||
return LIBEVAL::VALUE( (const char*) str.c_str() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::unordered_map<TYPE_ID, PROPERTY_BASE*> m_matchingTypes;
|
||||
int m_itemIndex;
|
||||
LIBEVAL::VAR_TYPE_T m_type;
|
||||
};
|
||||
|
||||
LIBEVAL::UCODE::VAR_REF *PCB_EXPR_UCODE::createVarRef( const std::string &var, const std::string &field )
|
||||
{
|
||||
PCB_EXPR_VAR_REF *rv;
|
||||
|
||||
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
|
||||
|
||||
auto classes = propMgr.GetAllClasses();
|
||||
auto vref = new PCB_EXPR_VAR_REF( var == "A" ? 0 : 1 );
|
||||
|
||||
for ( auto cls : classes )
|
||||
{
|
||||
if( propMgr.IsOfType( cls.type, TYPE_HASH( BOARD_ITEM ) ) )
|
||||
{
|
||||
PROPERTY_BASE* prop = propMgr.GetProperty( cls.type, field );
|
||||
if( prop )
|
||||
{
|
||||
//printf("Field '%s' class %s ptr %p\n", field.c_str(), (const char *) cls.name.c_str(), prop );
|
||||
vref->AddAllowedClass( cls.type, prop );
|
||||
if ( prop->TypeHash() == TYPE_HASH(int) )
|
||||
vref->SetType( LIBEVAL::VT_NUMERIC );
|
||||
else if ( prop->TypeHash() == TYPE_HASH(wxString) )
|
||||
vref->SetType( LIBEVAL::VT_STRING );
|
||||
else {
|
||||
printf("Unknown property type\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vref;
|
||||
}
|
||||
|
||||
|
||||
BOARD* loadBoard( const std::string& filename )
|
||||
{
|
||||
PLUGIN::RELEASER pi( new PCB_IO );
|
||||
BOARD* brd = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
brd = pi->Load( wxString( filename.c_str() ), NULL, NULL );
|
||||
}
|
||||
catch( const IO_ERROR& ioe )
|
||||
{
|
||||
wxString msg = wxString::Format( _( "Error loading board.\n%s" ),
|
||||
ioe.Problem() );
|
||||
|
||||
printf( "%s\n", (const char*) msg.mb_str() );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return brd;
|
||||
}
|
||||
|
||||
|
||||
class PCB_UNIT_RESOLVER : public LIBEVAL::UNIT_RESOLVER
|
||||
{
|
||||
public:
|
||||
virtual ~PCB_UNIT_RESOLVER()
|
||||
{
|
||||
}
|
||||
|
||||
virtual const std::vector<std::string>& GetSupportedUnits() const override
|
||||
{
|
||||
static const std::vector<std::string> pcbUnits = {"mil", "mm", "in"};
|
||||
|
||||
return pcbUnits;
|
||||
}
|
||||
|
||||
virtual double Convert( const std::string aString, int unitId ) const override
|
||||
{
|
||||
double v = atof(aString.c_str());
|
||||
switch(unitId)
|
||||
{
|
||||
case 0 :
|
||||
return Mils2iu( v );
|
||||
case 1:
|
||||
return Millimeter2iu( v );
|
||||
case 2:
|
||||
return Mils2iu( v * 1000.0 );
|
||||
default:
|
||||
return v;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
class PCB_EXPR_COMPILER : public LIBEVAL::COMPILER
|
||||
{
|
||||
public:
|
||||
PCB_EXPR_COMPILER()
|
||||
{
|
||||
m_unitResolver.reset( new PCB_UNIT_RESOLVER );
|
||||
}
|
||||
};
|
||||
|
||||
bool testEvalExpr( const std::string expr, LIBEVAL::VALUE expectedResult, bool expectError = false, BOARD_ITEM* itemA = nullptr, BOARD_ITEM* itemB = nullptr )
|
||||
{
|
||||
PCB_EXPR_COMPILER compiler;
|
||||
PCB_EXPR_UCODE ucode;
|
||||
bool ok = true;
|
||||
|
||||
ucode.SetItems( itemA, itemB );
|
||||
|
||||
bool error = !compiler.Compile( expr, &ucode );
|
||||
|
||||
if( error )
|
||||
{
|
||||
if ( expectError )
|
||||
{
|
||||
printf("result: OK (expected parse error)\n");
|
||||
ok = true;
|
||||
return ok;
|
||||
} else {
|
||||
printf("result: FAIL (unexpected parse error)\n");
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
LIBEVAL::VALUE result;
|
||||
|
||||
if( ok )
|
||||
{
|
||||
result = *ucode.Run();
|
||||
ok = (result == expectedResult);
|
||||
}
|
||||
|
||||
if( expectedResult.GetType() == LIBEVAL::VT_NUMERIC )
|
||||
printf("result: %s (got %.10f expected: %.10f)\n", ok ? "OK" : "FAIL", result.AsDouble(), expectedResult.AsDouble() );
|
||||
else
|
||||
printf("result: %s (got '%s' expected: '%s')\n", ok ? "OK" : "FAIL", result.AsString().c_str(), expectedResult.AsString().c_str() );
|
||||
|
||||
if (!ok )
|
||||
{
|
||||
printf("Offending code dump: \n%s\n", ucode.Dump().c_str() );
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool EvaluatePCBExpression( const std::string& aExpr, int& aResult )
|
||||
{
|
||||
PCB_EXPR_COMPILER compiler;
|
||||
PCB_EXPR_UCODE ucode;
|
||||
if( !compiler.Compile( aExpr, &ucode ) )
|
||||
return false;
|
||||
|
||||
auto result = ucode.Run();
|
||||
return true;
|
||||
}
|
||||
|
||||
class PCB_EXPR_EVALUATOR
|
||||
{
|
||||
public:
|
||||
PCB_EXPR_EVALUATOR();
|
||||
~PCB_EXPR_EVALUATOR();
|
||||
|
||||
bool Evaluate( const wxString& aExpr );
|
||||
int Result() const { return m_result; }
|
||||
wxString GetErrorString();
|
||||
|
||||
private:
|
||||
bool m_error;
|
||||
int m_result;
|
||||
|
||||
PCB_EXPR_COMPILER m_compiler;
|
||||
PCB_EXPR_UCODE m_ucode;
|
||||
}
|
||||
|
||||
int main( int argc, char *argv[] )
|
||||
{
|
||||
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
|
||||
propMgr.Rebuild();
|
||||
|
||||
|
||||
using VAL = LIBEVAL::VALUE;
|
||||
|
||||
/* testEvalExpr( "10mm + 20 mm", VAL(30e6) );
|
||||
testEvalExpr( "3*(7+8)", VAL(3*(7+8)) );
|
||||
testEvalExpr( "3*7+8", VAL(3*7+8) );
|
||||
testEvalExpr( "(3*7)+8", VAL(3*7+8) );
|
||||
testEvalExpr( "10mm + 20)", VAL(0), true );
|
||||
*/
|
||||
|
||||
BOARD brd;
|
||||
|
||||
NETINFO_LIST& netInfo = brd.GetNetInfo();
|
||||
|
||||
NETCLASSPTR netclass1( new NETCLASS("HV") );
|
||||
NETCLASSPTR netclass2( new NETCLASS("otherClass" ) );
|
||||
|
||||
printf("netcl1 classname '%s'\n", (const char *) netclass1->GetName().c_str() );
|
||||
|
||||
|
||||
auto net1info = new NETINFO_ITEM( &brd, "net1", 1);
|
||||
auto net2info = new NETINFO_ITEM( &brd, "net2", 2);
|
||||
|
||||
|
||||
net1info->SetClass( netclass1 );
|
||||
net2info->SetClass( netclass2 );
|
||||
|
||||
printf("netX classname '%s'\n", net1info->GetClassName().c_str() );
|
||||
|
||||
|
||||
printf("net1 class %p %p\n", net1info->GetNetClass(), netclass1.get() );
|
||||
|
||||
TRACK trackA(&brd);
|
||||
TRACK trackB(&brd);
|
||||
|
||||
printf("net1 classname '%s'\n", (const char*) net1info->GetClassName().c_str() );
|
||||
|
||||
trackA.SetNet( net1info );
|
||||
trackB.SetNet( net2info );
|
||||
|
||||
trackA.SetWidth( Mils2iu( 10 ));
|
||||
trackB.SetWidth( Mils2iu( 20 ));
|
||||
|
||||
printf("TrkA %p netclass '%s'\n", &trackA, (const char*) trackA.GetNetClassName().c_str() );
|
||||
printf("TrkB %p netclass '%s'\n", &trackB, (const char*) trackB.GetNetClassName().c_str() );
|
||||
|
||||
testEvalExpr( "A.Width > B.Width", VAL(0.0), false, &trackA, &trackB );
|
||||
testEvalExpr( "A.Width + B.Width", VAL(Mils2iu(10) + Mils2iu(20)), false, &trackA, &trackB );
|
||||
|
||||
testEvalExpr( "A.Netclass", VAL( (const char*) trackA.GetNetClassName().c_str() ), false, &trackA, &trackB );
|
||||
testEvalExpr( "A.Netclass == \"HV\" && B.netclass == \"otherClass\"", VAL( 1.0 ), false, &trackA, &trackB );
|
||||
testEvalExpr( "A.Netclass + 1.0", VAL( 1.0 ), false, &trackA, &trackB );
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -121,6 +121,7 @@ program;
|
|||
|
||||
PGM_BASE& Pgm()
|
||||
{
|
||||
printf("Pgm @ %p\n", &program );
|
||||
return program;
|
||||
}
|
||||
|
||||
|
@ -129,7 +130,7 @@ PGM_BASE& Pgm()
|
|||
// is run from a python script, mot from a Kicad application
|
||||
PGM_BASE* PgmOrNull()
|
||||
{
|
||||
return &program;
|
||||
return nullptr; //&program;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue