kicad/common/libeval_compiler/libeval_compiler.cpp

1031 lines
26 KiB
C++
Raw Normal View History

2020-06-04 11:04:03 +00:00
/*
This file is part of libeval, a simple math expression evaluator
Copyright (C) 2017 Michael Geselbracht, mgeselbracht3@gmail.com
Copyright (C) 2019-2020 KiCad Developers, see AUTHORS.txt for contributors.
2020-06-04 11:04:03 +00:00
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 <memory>
2020-06-04 11:04:03 +00:00
#include <set>
#include <vector>
#ifdef DEBUG
#include <cstdarg>
2020-06-04 11:04:03 +00:00
#endif
#include <libeval_compiler/libeval_compiler.h>
2020-06-04 11:04:03 +00:00
/* 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 <libeval_compiler/grammar.c>
#include <libeval_compiler/grammar.h>
2020-06-04 11:04:03 +00:00
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#define libeval_dbg(level, fmt, ...) \
wxLogTrace( "libeval_compiler", fmt, __VA_ARGS__ );
2020-06-04 11:04:03 +00:00
2020-07-22 13:31:11 +00:00
TREE_NODE* newNode( LIBEVAL::COMPILER* compiler, int op, const T_TOKEN_VALUE& value )
{
auto t2 = new TREE_NODE();
t2->valid = true;
t2->value.str = value.str ? new wxString( *value.str ) : nullptr;
t2->value.num = value.num;
t2->value.idx = value.idx;
t2->op = op;
t2->leaf[0] = nullptr;
t2->leaf[1] = nullptr;
t2->isTerminal = false;
t2->srcPos = compiler->GetSourcePos();
t2->uop = nullptr;
if(t2->value.str)
compiler->GcItem( t2->value.str );
compiler->GcItem( t2 );
return t2;
}
static const wxString formatOpName( int op )
2020-06-04 11:04:03 +00:00
{
static const struct
{
int op;
wxString mnemonic;
}
simpleOps[] =
{
{ TR_OP_MUL, "MUL" }, { TR_OP_DIV, "DIV" }, { TR_OP_ADD, "ADD" },
2020-06-04 11:04:03 +00:00
{ 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, "" }
};
2020-06-04 11:04:03 +00:00
for( int i = 0; simpleOps[i].op >= 0; i++ )
{
if( simpleOps[i].op == op )
return simpleOps[i].mnemonic;
}
return "???";
}
wxString UOP::Format() const
2020-06-04 11:04:03 +00:00
{
wxString str;
2020-06-04 11:04:03 +00:00
switch( m_op )
{
case TR_UOP_PUSH_VAR:
str = wxString::Format( "PUSH VAR [%p]", m_ref.get() );
2020-06-04 11:04:03 +00:00
break;
2020-06-04 11:04:03 +00:00
case TR_UOP_PUSH_VALUE:
{
if( !m_value )
str = wxString::Format( "PUSH nullptr" );
else if( m_value->GetType() == VT_NUMERIC )
str = wxString::Format( "PUSH NUM [%.10f]", m_value->AsDouble() );
2020-06-04 11:04:03 +00:00
else
str = wxString::Format( "PUSH STR [%ls]", GetChars( m_value->AsString() ) );
2020-06-04 11:04:03 +00:00
}
break;
case TR_OP_METHOD_CALL:
str = wxString::Format( "MCALL" );
break;
case TR_OP_FUNC_CALL:
str = wxString::Format( "FCALL" );
break;
2020-06-04 11:04:03 +00:00
default:
str = wxString::Format( "%s %d", formatOpName( m_op ).c_str(), m_op );
2020-06-04 11:04:03 +00:00
break;
}
2020-06-04 11:04:03 +00:00
return str;
}
2020-07-22 13:31:11 +00:00
UCODE::~UCODE()
{
for ( auto op : m_ucode )
{
printf("destroy uop %p\n", op );
delete op;
}
}
wxString UCODE::Dump() const
2020-06-04 11:04:03 +00:00
{
wxString rv;
2020-07-22 13:31:11 +00:00
2020-06-04 11:04:03 +00:00
for( auto op : m_ucode )
{
rv += op->Format();
rv += "\n";
}
2020-07-22 13:31:11 +00:00
2020-06-04 11:04:03 +00:00
return rv;
};
wxString TOKENIZER::GetChars( std::function<bool( wxUniChar )> cond ) const
2020-06-04 11:04:03 +00:00
{
wxString rv;
2020-06-04 11:04:03 +00:00
size_t p = m_pos;
// printf("p %d len %d\n", p, str.length() );
2020-07-22 13:31:11 +00:00
2020-06-04 11:04:03 +00:00
while( p < m_str.length() && cond( m_str[p] ) )
{
rv.append( 1, m_str[p] );
p++;
}
2020-07-22 13:31:11 +00:00
2020-06-04 11:04:03 +00:00
return rv;
}
bool TOKENIZER::MatchAhead( const wxString& match, std::function<bool( wxUniChar )> stopCond ) const
2020-06-04 11:04:03 +00:00
{
int remaining = m_str.Length() - m_pos;
2020-07-22 13:31:11 +00:00
2020-06-04 11:04:03 +00:00
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()] ) );
2020-07-22 13:31:11 +00:00
2020-06-04 11:04:03 +00:00
return false;
}
COMPILER::COMPILER() :
m_lexerState( COMPILER::LS_DEFAULT )
2020-06-04 11:04:03 +00:00
{
m_localeDecimalSeparator = '.';
m_sourcePos = 0;
m_parseFinished = false;
m_unitResolver = std::make_unique<UNIT_RESOLVER>();
2020-06-04 11:04:03 +00:00
m_parser = LIBEVAL::ParseAlloc( malloc );
2020-07-20 12:51:17 +00:00
m_tree = nullptr;
m_errorStatus.pendingError = false;
2020-06-04 11:04:03 +00:00
}
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();
if( m_tree )
{
freeTree( m_tree );
}
m_tree = nullptr;
for( auto tok : m_gcItems )
delete tok;
for( auto tok: m_gcStrings )
delete tok;
m_gcItems.clear();
m_gcStrings.clear();
2020-06-04 11:04:03 +00:00
}
void COMPILER::parseError( const char* s )
{
reportError( CST_PARSE, s );
2020-06-04 11:04:03 +00:00
}
void COMPILER::parseOk()
{
m_parseFinished = true;
}
bool COMPILER::Compile( const wxString& aString, UCODE* aCode, CONTEXT* aPreflightContext )
2020-06-04 11:04:03 +00:00
{
// Feed parser token after token until end of input.
newString( aString );
if( m_tree )
{
freeTree( m_tree );
}
m_tree = nullptr;
2020-06-04 11:04:03 +00:00
m_parseFinished = false;
T_TOKEN tok;
tok.value.str = nullptr;
libeval_dbg(0, "str: '%s' empty: %d\n", aString.c_str(), !!aString.empty() );
2020-06-04 11:04:03 +00:00
if( aString.empty() )
{
m_parseFinished = true;
return generateUCode( aCode, aPreflightContext );
2020-06-04 11:04:03 +00:00
}
do
{
m_sourcePos = m_tokenizer.GetPos();
2020-06-04 11:04:03 +00:00
tok = getToken();
if( tok.value.str )
GcItem( tok.value.str );
libeval_dbg(10, "parse: tok %d\n", tok.token );
Parse( m_parser, tok.token, tok, this );
if ( m_errorStatus.pendingError )
return false;
2020-06-04 11:04:03 +00:00
if( m_parseFinished || tok.token == G_ENDS )
{
// Reset parser by passing zero as token ID, value is ignored.
Parse( m_parser, 0, tok, this );
2020-06-04 11:04:03 +00:00
break;
}
} while( tok.token );
return generateUCode( aCode, aPreflightContext );
2020-06-04 11:04:03 +00:00
}
void COMPILER::newString( const wxString& aString )
2020-06-04 11:04:03 +00:00
{
Clear();
m_lexerState = LS_DEFAULT;
m_tokenizer.Restart( aString );
m_parseFinished = false;
}
T_TOKEN COMPILER::getToken()
2020-06-04 11:04:03 +00:00
{
T_TOKEN rv;
rv.value.str = nullptr;
2020-06-04 11:04:03 +00:00
bool done = false;
2020-07-22 13:31:11 +00:00
2020-06-04 11:04:03 +00:00
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( T_TOKEN& aToken )
2020-06-04 11:04:03 +00:00
{
wxString str = m_tokenizer.GetChars( []( int c ) -> bool { return c != '\''; } );
2020-06-04 11:04:03 +00:00
//printf("STR LIT '%s'\n", (const char *)str.c_str() );
aToken.token = G_STRING;
aToken.value.str = new wxString( str );
2020-06-04 11:04:03 +00:00
m_tokenizer.NextChar( str.length() + 1 );
m_lexerState = LS_DEFAULT;
return true;
}
int COMPILER::resolveUnits()
{
int unitId = 0;
2020-07-22 13:31:11 +00:00
for( const wxString& unitName : m_unitResolver->GetSupportedUnits() )
2020-06-04 11:04:03 +00:00
{
if( m_tokenizer.MatchAhead( unitName, []( int c ) -> bool { return !isalnum( c ); } ) )
{
libeval_dbg(10, "Match unit '%s'\n", unitName.c_str() );
2020-06-04 11:04:03 +00:00
m_tokenizer.NextChar( unitName.length() );
return unitId;
}
unitId++;
}
return -1;
}
bool COMPILER::lexDefault( T_TOKEN& aToken )
2020-06-04 11:04:03 +00:00
{
T_TOKEN retval;
wxString current;
2020-06-04 11:04:03 +00:00
int convertFrom;
wxString msg;
2020-06-04 11:04:03 +00:00
retval.value.str = nullptr;
2020-06-04 11:04:03 +00:00
retval.token = G_ENDS;
//printf( "tokdone %d\n", !!m_tokenizer.Done() );
if( m_tokenizer.Done() )
{
aToken = retval;
return true;
}
auto isDecimalSeparator =
[&]( wxUniChar ch ) -> bool
{
return ( ch == m_localeDecimalSeparator || ch == '.' || ch == ',' );
};
2020-06-04 11:04:03 +00:00
// Lambda: get value as string, store into clToken.token and update current index.
auto extractNumber =
[&]()
{
bool haveSeparator = false;
wxUniChar ch = m_tokenizer.GetChar();
2020-06-04 11:04:03 +00:00
do
{
if( isDecimalSeparator( ch ) && haveSeparator )
break;
2020-06-04 11:04:03 +00:00
current.append( 1, ch );
2020-06-04 11:04:03 +00:00
if( isDecimalSeparator( ch ) )
haveSeparator = true;
2020-06-04 11:04:03 +00:00
m_tokenizer.NextChar();
ch = m_tokenizer.GetChar();
} while( isdigit( ch ) || isDecimalSeparator( ch ) );
2020-06-04 11:04:03 +00:00
// 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;
}
2020-06-04 11:04:03 +00:00
//printf("-> NUM: '%s'\n", (const char *) current.c_str() );
};
2020-06-04 11:04:03 +00:00
int ch;
// Start processing of first/next token: Remove whitespace
for( ;; )
{
ch = m_tokenizer.GetChar();
if( ch == ' ' )
m_tokenizer.NextChar();
else
break;
}
2020-07-20 12:51:17 +00:00
libeval_dbg(10, "LEX ch '%c' pos %lu\n", ch, (unsigned long)m_tokenizer.GetPos() );
2020-06-04 11:04:03 +00:00
if( ch == 0 )
{
/* End of input */
}
else if( isdigit( ch ) )
{
// VALUE
extractNumber();
retval.token = G_VALUE;
retval.value.str = new wxString( current );
2020-06-04 11:04:03 +00:00
}
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.idx = convertFrom;
2020-06-04 11:04:03 +00:00
}
else if( ch == '\'' ) // string literal
2020-06-04 11:04:03 +00:00
{
//printf( "MATCH STRING LITERAL\n" );
m_lexerState = LS_STRING;
m_tokenizer.NextChar();
return false;
}
2020-07-23 10:34:45 +00:00
else if( isalpha( ch ) || ch == '_' )
2020-06-04 11:04:03 +00:00
{
//printf("ALPHA\n");
2020-07-23 10:34:45 +00:00
current = m_tokenizer.GetChars( []( int c ) -> bool { return isalnum( c ) || c == '_'; } );
2020-06-04 11:04:03 +00:00
//printf("Len: %d\n", current.length() );
//printf("id '%s'\n", (const char *) current.c_str() );
2020-07-23 10:34:45 +00:00
//fflush( stdout );
2020-06-04 11:04:03 +00:00
retval.token = G_IDENTIFIER;
retval.value.str = new wxString( current );
2020-06-04 11:04:03 +00:00
m_tokenizer.NextChar( current.length() );
}
else if( m_tokenizer.MatchAhead( "==", []( int c ) -> bool { return c != '='; } ) )
{
retval.token = G_EQUAL;
m_tokenizer.NextChar( 2 );
}
else if( m_tokenizer.MatchAhead( "!=", []( int c ) -> bool { return c != '='; } ) )
{
retval.token = G_NOT_EQUAL;
2020-06-04 11:04:03 +00:00
m_tokenizer.NextChar( 2 );
}
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
{
// Single char tokens
switch( ch )
{
2020-07-22 13:31:11 +00:00
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;
2020-06-04 11:04:03 +00:00
default:
reportError( CST_PARSE, wxString::Format( _( "Unrecognized character '%c'" ), (char) ch ) );
2020-07-22 13:31:11 +00:00
break;
2020-06-04 11:04:03 +00:00
}
m_tokenizer.NextChar();
}
aToken = retval;
return true;
}
const wxString formatNode( TREE_NODE* node )
2020-06-04 11:04:03 +00:00
{
return *(node->value.str);
2020-06-04 11:04:03 +00:00
}
void dumpNode( wxString& buf, TREE_NODE* tok, int depth = 0 )
2020-06-04 11:04:03 +00:00
{
wxString str;
str.Printf( "\n[%p L0:%-20p L1:%-20p] ", tok, tok->leaf[0], tok->leaf[1] );
2020-06-04 11:04:03 +00:00
buf += str;
2020-07-22 13:31:11 +00:00
2020-06-04 11:04:03 +00:00
for( int i = 0; i < 2 * depth; i++ )
buf += " ";
if( tok->op & TR_OP_BINARY_MASK )
{
buf += formatOpName( tok->op );
2020-06-04 11:04:03 +00:00
dumpNode( buf, tok->leaf[0], depth + 1 );
dumpNode( buf, tok->leaf[1], depth + 1 );
}
switch( tok->op )
{
case TR_NUMBER:
buf += "NUMERIC: ";
buf += formatNode( tok );
2020-06-04 11:04:03 +00:00
if( tok->leaf[0] )
dumpNode( buf, tok->leaf[0], depth + 1 );
2020-06-04 11:04:03 +00:00
break;
2020-07-22 13:31:11 +00:00
2020-06-04 11:04:03 +00:00
case TR_STRING:
buf += "STRING: ";
buf += formatNode( tok );
2020-06-04 11:04:03 +00:00
break;
2020-07-22 13:31:11 +00:00
2020-06-04 11:04:03 +00:00
case TR_IDENTIFIER:
buf += "ID: ";
buf += formatNode( tok );
2020-06-04 11:04:03 +00:00
break;
2020-07-22 13:31:11 +00:00
2020-06-04 11:04:03 +00:00
case TR_STRUCT_REF:
buf += "SREF: ";
2020-06-04 11:04:03 +00:00
dumpNode( buf, tok->leaf[0], depth + 1 );
dumpNode( buf, tok->leaf[1], depth + 1 );
break;
2020-07-22 13:31:11 +00:00
case TR_OP_FUNC_CALL:
buf += "CALL '";
buf += *tok->leaf[0]->value.str;
buf += "': ";
dumpNode( buf, tok->leaf[1], depth + 1 );
break;
2020-07-22 13:31:11 +00:00
2020-06-04 11:04:03 +00:00
case TR_UNIT:
str.Printf( "UNIT: %d ", tok->value.idx );
2020-06-04 11:04:03 +00:00
buf += str;
break;
}
}
2020-07-22 13:31:11 +00:00
void CONTEXT::ReportError( const wxString& aErrorMsg )
{
m_errorStatus.pendingError = true;
m_errorStatus.message = aErrorMsg;
m_errorStatus.srcPos = -1;
m_errorStatus.stage = CST_RUNTIME;
if( m_errorCallback )
m_errorCallback( m_errorStatus );
}
void COMPILER::reportError( COMPILATION_STAGE stage, const wxString& aErrorMsg, int aPos )
{
if( aPos == -1 )
aPos = m_sourcePos;
// fixme: no HTML or anything UI-related here.
#if 0
wxString rest;
wxString first = aErrorMsg.BeforeFirst( '|', &rest );
wxString msg = wxString::Format( _( "ERROR: <a href='%d:%d'>%s</a>%s" ),
m_originLine,
m_originOffset + aPos,
first,
rest );
#endif
m_errorStatus.pendingError = true;
m_errorStatus.stage = stage;
m_errorStatus.message = aErrorMsg;
m_errorStatus.srcPos = aPos;
if( m_errorCallback )
m_errorCallback( m_errorStatus );
}
2020-07-22 13:31:11 +00:00
void COMPILER::setRoot( TREE_NODE *root )
2020-06-04 11:04:03 +00:00
{
m_tree = root;
2020-06-04 11:04:03 +00:00
}
void COMPILER::freeTree( LIBEVAL::TREE_NODE *tree )
{
printf("->FreeTre %p\n", tree );
if ( tree->leaf[0] )
freeTree( tree->leaf[0] );
if ( tree->leaf[1] )
freeTree( tree->leaf[1] );
2020-06-04 11:04:03 +00:00
if( tree->uop )
{
printf("Free uop %p\n", tree->uop );
delete tree->uop;
}
/* if( tree->value.str )
delete tree->value.str;
delete tree;*/
2020-07-29 21:11:30 +00:00
}
void TREE_NODE::SetUop( int aOp, double aValue )
{
if( uop )
delete uop;
std::unique_ptr<VALUE> val( new VALUE( aValue ) );
uop = new UOP( aOp, std::move( val ) );
}
void TREE_NODE::SetUop( int aOp, const wxString& aValue )
{
if( uop )
delete uop;
std::unique_ptr<VALUE> val( new VALUE( aValue ) );
uop = new UOP( aOp, std::move( val ) );
}
void TREE_NODE::SetUop( int aOp, std::unique_ptr<VAR_REF> aRef )
{
if( uop )
delete uop;
uop = new UOP( aOp, std::move( aRef ) );
}
void TREE_NODE::SetUop( int aOp, FUNC_CALL_REF aFunc, std::unique_ptr<VAR_REF> aRef )
{
if( uop )
delete uop;
uop = new UOP( aOp, std::move( aFunc ), std::move( aRef ) );
}
bool COMPILER::generateUCode( UCODE* aCode, CONTEXT* aPreflightContext )
2020-06-04 11:04:03 +00:00
{
std::vector<TREE_NODE*> stack;
std::set<TREE_NODE*> visitedNodes;
wxString msg;
2020-06-04 11:04:03 +00:00
auto visited = [&]( TREE_NODE* node ) -> bool
{
return visitedNodes.find( node ) != visitedNodes.end();
};
2020-06-04 11:04:03 +00:00
if( !m_tree )
{
// Empty expression returns true
aCode->AddOp( makeUop( TR_UOP_PUSH_VALUE, 1.0 ) );
return true;
}
2020-06-04 11:04:03 +00:00
stack.push_back( m_tree );
wxString dump;
dumpNode( dump, m_tree, 0 );
libeval_dbg(3,"Tree dump:\n%s\n\n", (const char*) dump.c_str() );
2020-06-04 11:04:03 +00:00
while( !stack.empty() )
{
TREE_NODE* node = stack.back();
2020-07-20 12:51:17 +00:00
libeval_dbg( 4, "process node %p [op %d] [stack %lu]\n",
node, node->op, (unsigned long)stack.size() );
2020-06-04 11:04:03 +00:00
// process terminal nodes first
switch( node->op )
{
case TR_OP_FUNC_CALL:
break;
2020-07-20 12:51:17 +00:00
2020-06-04 11:04:03 +00:00
case TR_STRUCT_REF:
{
assert( node->leaf[0]->op == TR_IDENTIFIER );
//assert( node->leaf[1]->op == TR_IDENTIFIER );
switch( node->leaf[1]->op )
{
case TR_IDENTIFIER:
{
wxString itemName = *node->leaf[0]->value.str;
wxString propName = *node->leaf[1]->value.str;
std::unique_ptr<VAR_REF> vref = aCode->CreateVarRef( itemName, propName );
if( !vref )
{
msg.Printf( _( "Unrecognized item '%s'" ), itemName );
reportError( CST_CODEGEN, msg, node->leaf[0]->srcPos - (int) strlen( itemName ) );
return false;
}
if( vref->GetType() == VT_PARSE_ERROR )
{
msg.Printf( _( "Unrecognized property '%s'" ), propName );
reportError( CST_CODEGEN, msg, node->leaf[1]->srcPos - (int) strlen( propName ) );
return false;
}
node->SetUop( TR_UOP_PUSH_VAR, std::move( vref ) );
node->isTerminal = true;
break;
}
case TR_OP_FUNC_CALL:
{
wxString itemName = *node->leaf[0]->value.str;
std::unique_ptr<VAR_REF> vref = aCode->CreateVarRef( itemName, "" );
if( !vref )
{
msg.Printf( _( "Unrecognized item '%s'" ), itemName );
reportError( CST_CODEGEN, msg, node->leaf[0]->srcPos - (int) strlen( itemName ) );
return false;
}
wxString functionName = *node->leaf[1]->leaf[0]->value.str;
auto func = aCode->CreateFuncCall( functionName );
libeval_dbg(10, "emit func call: %s\n", (const char*) functionName.c_str() );
if( !func )
{
msg.Printf( _( "Unrecognized function '%s'" ), functionName );
reportError( CST_CODEGEN, msg, node->leaf[1]->leaf[0]->srcPos + 1 );
return false;
}
// Preflight the function call
// fixme - this won't really work because of dynamic typing...
#if 0
wxString paramStr;
if( node->value.wstr )
paramStr = *node->value.wstr;
VALUE* param = aPreflightContext->AllocValue();
param->Set( paramStr );
aPreflightContext->Push( param );
try
{
func( aPreflightContext, vref );
aPreflightContext->Pop(); // return value
}
catch( ... )
{
}
#endif
/* SREF -> FUNC_CALL -> leaf0/1 */
// node->leaf[1]->leaf[0]->leaf[0] = nullptr;
// node->leaf[1]->leaf[0]->leaf[1] = nullptr;
#if 0
if( aPreflightContext->IsErrorPending() )
{
reportError( CST_CODEGEN, aPreflightContext->GetError().message,
node->leaf[1]->leaf[1]->srcPos
- (int) paramStr.length() - 1 );
return false;
}
#endif
visitedNodes.insert( node->leaf[0] );
visitedNodes.insert( node->leaf[1]->leaf[0] );
node->SetUop( TR_OP_METHOD_CALL, func, std::move( vref ) );
node->isTerminal = false;
}
break;
}
}
2020-06-04 11:04:03 +00:00
break;
case TR_NUMBER:
{
2020-07-22 13:31:11 +00:00
TREE_NODE* son = node->leaf[0];
double value;
2020-06-04 11:04:03 +00:00
if( son && son->op == TR_UNIT )
{
//printf( "HandleUnit: %s unit %d\n", node->value.str, son->value.type );
int units = son->value.idx;
value = m_unitResolver->Convert( *node->value.str, units );
2020-06-04 11:04:03 +00:00
visitedNodes.insert( son );
}
else
{
value = wxAtof( *node->value.str );
}
node->SetUop( TR_UOP_PUSH_VALUE, value );
node->isTerminal = true;
2020-06-04 11:04:03 +00:00
break;
}
2020-07-20 12:51:17 +00:00
2020-06-04 11:04:03 +00:00
case TR_STRING:
{
node->SetUop( TR_UOP_PUSH_VALUE, *node->value.str );
node->isTerminal = true;
2020-06-04 11:04:03 +00:00
break;
}
2020-07-20 12:51:17 +00:00
case TR_IDENTIFIER:
{
std::unique_ptr<VAR_REF> vref = aCode->CreateVarRef( *node->value.str, "" );
if( !vref )
{
msg.Printf( _( "Unrecognized item '%s'" ), *node->value.str );
reportError( CST_CODEGEN, msg, node->srcPos - (int) strlen( *node->value.str ) );
return false;
}
node->SetUop( TR_UOP_PUSH_VALUE, std::move( vref ) );
break;
}
2020-06-04 11:04:03 +00:00
default:
node->SetUop( node->op );
2020-06-04 11:04:03 +00:00
break;
}
if( !node->isTerminal && node->leaf[0] && !visited( node->leaf[0] ) )
2020-06-04 11:04:03 +00:00
{
stack.push_back( node->leaf[0] );
visitedNodes.insert( node->leaf[0] );
continue;
2020-06-04 11:04:03 +00:00
}
else if( !node->isTerminal && node->leaf[1] && !visited( node->leaf[1] ) )
2020-06-04 11:04:03 +00:00
{
stack.push_back( node->leaf[1] );
visitedNodes.insert( node->leaf[1] );
continue;
2020-06-04 11:04:03 +00:00
}
visitedNodes.insert( node );
2020-07-22 13:31:11 +00:00
if( node->uop )
{
2020-07-22 13:31:11 +00:00
aCode->AddOp( node->uop );
node->uop = nullptr;
}
stack.pop_back();
2020-06-04 11:04:03 +00:00
}
libeval_dbg(2,"dump: \n%s\n", aCode->Dump().c_str() );
2020-06-04 11:04:03 +00:00
return true;
}
void UOP::Exec( CONTEXT* ctx )
2020-06-04 11:04:03 +00:00
{
switch( m_op )
{
case TR_UOP_PUSH_VAR:
{
auto value = ctx->AllocValue();
value->Set( m_ref->GetValue( ctx ) );
2020-06-04 11:04:03 +00:00
ctx->Push( value );
}
break;
2020-06-04 11:04:03 +00:00
case TR_UOP_PUSH_VALUE:
ctx->Push( m_value.get() );
2020-06-04 11:04:03 +00:00
return;
case TR_OP_METHOD_CALL:
//printf("CALL METHOD %s\n" );
m_func( ctx, m_ref.get() );
return;
2020-06-04 11:04:03 +00:00
default:
break;
}
if( m_op & TR_OP_BINARY_MASK )
{
LIBEVAL::VALUE* arg2 = ctx->Pop();
LIBEVAL::VALUE* arg1 = ctx->Pop();
2020-07-27 17:50:21 +00:00
double arg2Value = arg2 ? arg2->AsDouble() : 0.0;
double arg1Value = arg1 ? arg1->AsDouble() : 0.0;
double result;
2020-06-04 11:04:03 +00:00
switch( m_op )
{
case TR_OP_ADD:
2020-07-27 17:50:21 +00:00
result = arg1Value + arg2Value;
2020-06-04 11:04:03 +00:00
break;
case TR_OP_SUB:
2020-07-27 17:50:21 +00:00
result = arg1Value - arg2Value;
2020-06-04 11:04:03 +00:00
break;
case TR_OP_MUL:
2020-07-27 17:50:21 +00:00
result = arg1Value * arg2Value;
2020-06-04 11:04:03 +00:00
break;
case TR_OP_DIV:
2020-07-27 17:50:21 +00:00
result = arg1Value / arg2Value;
2020-06-04 11:04:03 +00:00
break;
case TR_OP_LESS_EQUAL:
2020-07-27 17:50:21 +00:00
result = arg1Value <= arg2Value ? 1 : 0;
2020-06-04 11:04:03 +00:00
break;
case TR_OP_GREATER_EQUAL:
2020-07-27 17:50:21 +00:00
result = arg1Value >= arg2Value ? 1 : 0;
2020-06-04 11:04:03 +00:00
break;
case TR_OP_LESS:
2020-07-27 17:50:21 +00:00
result = arg1Value < arg2Value ? 1 : 0;
2020-06-04 11:04:03 +00:00
break;
case TR_OP_GREATER:
2020-07-27 17:50:21 +00:00
result = arg1Value > arg2Value ? 1 : 0;
2020-06-04 11:04:03 +00:00
break;
case TR_OP_EQUAL:
2020-07-27 17:50:21 +00:00
result = arg1 && arg2 && arg1->EqualTo( arg2 ) ? 1 : 0;
2020-06-04 11:04:03 +00:00
break;
case TR_OP_NOT_EQUAL:
2020-07-27 17:50:21 +00:00
result = arg1 && arg2 && arg1->EqualTo( arg2 ) ? 0 : 1;
2020-06-04 11:04:03 +00:00
break;
case TR_OP_BOOL_AND:
2020-07-27 17:50:21 +00:00
result = arg1Value != 0.0 && arg2Value != 0.0 ? 1 : 0;
2020-06-04 11:04:03 +00:00
break;
case TR_OP_BOOL_OR:
2020-07-27 17:50:21 +00:00
result = arg1Value != 0.0 || arg2Value != 0.0 ? 1 : 0;
2020-06-04 11:04:03 +00:00
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 )
2020-06-04 11:04:03 +00:00
{
for( UOP* op : m_ucode )
op->Exec( ctx );
2020-06-04 11:04:03 +00:00
assert( ctx->SP() == 1 );
return ctx->Pop();
2020-06-04 11:04:03 +00:00
}
2020-06-04 11:04:03 +00:00
} // namespace LIBEVAL