21#include <fmt/format.h> 
   25#include <fast_float/fast_float.h> 
   34#pragma GCC diagnostic push 
   35#pragma GCC diagnostic ignored "-Wunused-variable" 
   36#pragma GCC diagnostic ignored "-Wsign-compare" 
   37#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" 
   40#include <text_eval/text_eval.c> 
   43#pragma GCC diagnostic pop 
 
   86    concept Utf8Byte = std::same_as<T, char> || std::same_as<T, unsigned char> || std::same_as<T, std::byte>;
 
   93        static constexpr bool is_ascii( std::byte b ) 
noexcept 
   95            return ( b & std::byte{ 0x80 } ) == std::byte{ 0x00 };
 
 
  100            return ( b & std::byte{ 0xC0 } ) == std::byte{ 0x80 };
 
 
  107            if( ( first & std::byte{ 0xE0 } ) == std::byte{ 0xC0 } )
 
  109            if( ( first & std::byte{ 0xF0 } ) == std::byte{ 0xE0 } )
 
  111            if( ( first & std::byte{ 0xF8 } ) == std::byte{ 0xF0 } )
 
 
  118        static std::u32string 
to_utf32( std::string_view utf8 )
 
  121            result.reserve( utf8.size() ); 
 
  123            auto bytes = std::as_bytes( std::span{ utf8.data(), utf8.size() } );
 
  125            for( 
size_t i = 0; i < bytes.size(); )
 
  127                std::byte first = bytes[i];
 
  130                if( len == 0 || i + len > bytes.size() )
 
  133                    result.push_back( U
'\uFFFD' );
 
  138                char32_t codepoint = 0;
 
  142                case 1: codepoint = std::to_integer<char32_t>( first ); 
break;
 
  148                        result.push_back( U
'\uFFFD' );
 
  152                    codepoint = ( std::to_integer<char32_t>( first & std::byte{ 0x1F } ) << 6 )
 
  153                                | std::to_integer<char32_t>( bytes[i + 1] & std::byte{ 0x3F } );
 
  161                        result.push_back( U
'\uFFFD' );
 
  165                    codepoint = ( std::to_integer<char32_t>( first & std::byte{ 0x0F } ) << 12 )
 
  166                                | ( std::to_integer<char32_t>( bytes[i + 1] & std::byte{ 0x3F } ) << 6 )
 
  167                                | std::to_integer<char32_t>( bytes[i + 2] & std::byte{ 0x3F } );
 
  176                        result.push_back( U
'\uFFFD' );
 
  180                    codepoint = ( std::to_integer<char32_t>( first & std::byte{ 0x07 } ) << 18 )
 
  181                                | ( std::to_integer<char32_t>( bytes[i + 1] & std::byte{ 0x3F } ) << 12 )
 
  182                                | ( std::to_integer<char32_t>( bytes[i + 2] & std::byte{ 0x3F } ) << 6 )
 
  183                                | std::to_integer<char32_t>( bytes[i + 3] & std::byte{ 0x3F } );
 
  189                if( codepoint > 0x10FFFF || ( codepoint >= 0xD800 && codepoint <= 0xDFFF ) )
 
  191                    result.push_back( U
'\uFFFD' ); 
 
  193                else if( len == 2 && codepoint < 0x80 )
 
  195                    result.push_back( U
'\uFFFD' ); 
 
  197                else if( len == 3 && codepoint < 0x800 )
 
  199                    result.push_back( U
'\uFFFD' ); 
 
  201                else if( len == 4 && codepoint < 0x10000 )
 
  203                    result.push_back( U
'\uFFFD' ); 
 
  207                    result.push_back( codepoint );
 
 
  217        static std::string 
to_utf8( std::u32string_view utf32 )
 
  220            result.reserve( utf32.size() * 4 ); 
 
  222            for( 
char32_t cp : utf32 )
 
  227                    result.push_back( 
static_cast<char>( cp ) );
 
  229                else if( cp <= 0x7FF )
 
  232                    result.push_back( 
static_cast<char>( 0xC0 | ( cp >> 6 ) ) );
 
  233                    result.push_back( 
static_cast<char>( 0x80 | ( cp & 0x3F ) ) );
 
  235                else if( cp <= 0xFFFF )
 
  238                    if( cp >= 0xD800 && cp <= 0xDFFF )
 
  241                        result.append( 
"\uFFFD" ); 
 
  245                        result.push_back( 
static_cast<char>( 0xE0 | ( cp >> 12 ) ) );
 
  246                        result.push_back( 
static_cast<char>( 0x80 | ( ( cp >> 6 ) & 0x3F ) ) );
 
  247                        result.push_back( 
static_cast<char>( 0x80 | ( cp & 0x3F ) ) );
 
  250                else if( cp <= 0x10FFFF )
 
  253                    result.push_back( 
static_cast<char>( 0xF0 | ( cp >> 18 ) ) );
 
  254                    result.push_back( 
static_cast<char>( 0x80 | ( ( cp >> 12 ) & 0x3F ) ) );
 
  255                    result.push_back( 
static_cast<char>( 0x80 | ( ( cp >> 6 ) & 0x3F ) ) );
 
  256                    result.push_back( 
static_cast<char>( 0x80 | ( cp & 0x3F ) ) );
 
  261                    result.append( 
"\uFFFD" ); 
 
 
 
  275            return cp == U
' ' || cp == U
'\t' || cp == U
'\r' || cp == U
'\n' ||
 
  276                   cp == U
'\f' || cp == U
'\v' || cp == U
'\u00A0' || 
 
  277                   cp == U
'\u2000' || cp == U
'\u2001' || cp == U
'\u2002' || cp == U
'\u2003' ||
 
  278                   cp == U
'\u2004' || cp == U
'\u2005' || cp == U
'\u2006' || cp == U
'\u2007' ||
 
  279                   cp == U
'\u2008' || cp == U
'\u2009' || cp == U
'\u200A' || cp == U
'\u2028' ||
 
  280                   cp == U
'\u2029' || cp == U
'\u202F' || cp == U
'\u205F' || cp == U
'\u3000';
 
 
  284            return cp >= U
'0' && cp <= U
'9';
 
 
  288            return (cp >= U
'a' && cp <= U
'z') || (cp >= U
'A' && cp <= U
'Z');
 
 
  293            return is_ascii_alpha(cp) || (cp >= 0x80 && cp <= 0x10FFFF && cp != 0xFFFD);
 
 
 
  309        static constexpr std::array<PREFIX, 18> 
prefixes = { {
 
  314            {U
'u', 1e-6},  {U
'µ', 1e-6},  {U
'μ', 1e-6}, 
 
  316            {U
'k', 1e3},   {U
'K', 1e3},
 
 
  326            return std::ranges::any_of( 
prefixes,
 
 
  335            auto it = std::ranges::find_if( 
prefixes,
 
  340            return it != 
prefixes.end() ? it->multiplier : 1.0;
 
 
 
 
  415        if( aFromUnit == aToUnit )
 
 
 
  478    [[nodiscard]] 
char32_t peek_char( 
size_t offset = 1 ) const noexcept
 
  480        size_t peek_pos = 
m_pos + offset;
 
  481        return peek_pos < 
m_text.size() ? 
m_text[peek_pos] : U
'\0';
 
 
  486        for( 
size_t i = 0; i < count && 
m_pos < 
m_text.size(); ++i )
 
 
  511            auto error_msg = fmt::format( 
"Line {}, Column {}: {}", 
m_line, 
m_column, message );
 
 
  520        std::strncpy( token.text, value.c_str(), 
sizeof( token.text ) - 1 );
 
  521        token.text[
sizeof( token.text ) - 1] = 
'\0';
 
 
  530        token.dValue = value;
 
 
  538        std::u32string content;
 
  539        content.reserve( 64 ); 
 
  552                case U
'n':  content.push_back( U
'\n' ); 
break;
 
  553                case U
't':  content.push_back( U
'\t' ); 
break;
 
  554                case U
'r':  content.push_back( U
'\r' ); 
break;
 
  555                case U
'\\': content.push_back( U
'\\' ); 
break;
 
  556                case U
'"':  content.push_back( U
'"' ); 
break;
 
  557                case U
'\'': content.push_back( U
'\'' ); 
break;
 
  558                case U
'0':  content.push_back( U
'\0' ); 
break;
 
  564                    for( 
int i = 0; i < 2 && 
m_pos < 
m_text.size(); ++i )
 
  567                        if( ( hex_char >= U
'0' && hex_char <= U
'9' )
 
  568                         || ( hex_char >= U
'A' && hex_char <= U
'F' )
 
  569                         || ( hex_char >= U
'a' && hex_char <= U
'f' ) )
 
  571                            hex.push_back( hex_char );
 
  585                            auto value = std::stoul( hex_str, 
nullptr, 16 );
 
  587                            if( value <= 0x10FFFF )
 
  588                                content.push_back( 
static_cast<char32_t>( value ) );
 
  590                                content.push_back( U
'\uFFFD' );
 
  594                            content.push_back( U
'\uFFFD' );
 
  599                        content.append( U
"\\x" );
 
  605                    content.push_back( U
'\\' );
 
  606                    content.push_back( escaped );
 
  610            else if( c == U
'\n' )
 
  612                add_error( 
"Unterminated string literal" );
 
  617                content.push_back( c );
 
  625            add_error(
"Missing closing quote in string literal");
 
 
  633        std::u32string number_text;
 
  634        number_text.reserve( 32 );
 
  636        bool   has_decimal = 
false;
 
  637        double multiplier = 1.0;
 
  655                number_text.push_back( U
'.' );
 
  663                std::u32string potential_unit;
 
  664                size_t         temp_pos = 
m_pos;
 
  666                while( temp_pos < 
m_text.size() )
 
  668                    char32_t unit_char = 
m_text[temp_pos];
 
  672                        potential_unit.push_back( unit_char );
 
  682                if( !potential_unit.empty() )
 
  696                        number_text.push_back( U
'.' );
 
  705                    number_text.push_back( U
'.' );
 
  714                number_text.push_back( U
'.' );
 
  732            if( !number_str.empty() && number_str != 
"." )
 
  734                auto result = fast_float::from_chars( number_str.data(), number_str.data() + number_str.size(), value );
 
  736                if( 
result.ec != std::errc() || 
result.ptr != number_str.data() + number_str.size() )
 
  737                    throw std::invalid_argument( fmt::format( 
"Cannot convert '{}' to number", number_str ) );
 
  741                if( !std::isfinite( value ) )
 
  748        catch( 
const std::exception& e )
 
  750            add_error( fmt::format( 
"Invalid number format: {}", e.what() ) );
 
  758            size_t whitespace_start = 
m_pos;
 
  765            std::u32string unit_text;
 
  775                    unit_text.push_back( c );
 
  784            if( !unit_text.empty() )
 
  794                    value = converted_value;
 
  799                    m_pos = whitespace_start;
 
  805                m_pos = whitespace_start;
 
 
  813        std::u32string identifier;
 
  814        identifier.reserve(64);
 
 
  834            if ((current == U
'@' && 
next == U
'{') ||
 
  835                (current == U
'$' && 
next == U
'{')) {
 
  839            text.push_back(current);
 
 
  879        if( current == U
'@' && 
next == U
'{' )
 
  888        if( current == U
'$' && 
next == U
'{' )
 
  898        if( current == U
'}' )
 
  912        if( current == U
'<' && 
next == U
'=' )
 
  918        if( current == U
'>' && 
next == U
'=' )
 
  924        if( current == U
'=' && 
next == U
'=' )
 
  930        if( current == U
'!' && 
next == U
'=' )
 
  941            static constexpr std::array<std::pair<char32_t, TextEvalToken>, 11> single_char_tokens{{
 
  955            if( 
auto it = std::ranges::find_if( single_char_tokens,
 
  956                                                [current]( 
const auto& pair ) { 
return pair.first == current; } );
 
  957                                                it != single_char_tokens.end() )
 
  966        if( current == U
'"' || current == U
'\'' )
 
 
 
 1010    m_lastErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
 
 
 1014                                         bool aClearVariablesOnEvaluate ) :
 
 1020    m_lastErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
 
 
 1028    m_lastErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
 
 
 1032                                         bool aClearVariablesOnEvaluate ) :
 
 1038    m_lastErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
 
 
 1050    m_lastErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
 
 
 1061    if( 
this != &aOther )
 
 1069        m_lastErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
 
 
 1091    if( 
this != &aOther )
 
 
 1171        if( std::holds_alternative<double>( it->second ) )
 
 1173            double val = std::get<double>( it->second );
 
 1175            if( val == std::floor( val ) && 
std::abs( val ) < 1e15 )
 
 1176                return wxString::Format( 
"%.0f", val );
 
 1178                return wxString::Format( 
"%g", val );
 
 
 1190    std::vector<wxString> names;
 
 
 1201    for( 
const auto& [
name, value] : aVariables )
 
 
 1207    for( 
const auto& [
name, value] : aVariables )
 
 
 1213    std::unordered_map<wxString, double> emptyNumVars;
 
 1214    std::unordered_map<wxString, wxString> emptyStringVars;
 
 1215    return Evaluate( aInput, emptyNumVars, emptyStringVars );
 
 
 1219                                       const std::unordered_map<wxString, double>& aTempVariables )
 
 1221    std::unordered_map<wxString, wxString> emptyStringVars;
 
 1222    return Evaluate( aInput, aTempVariables, emptyStringVars );
 
 
 1226                                       const std::unordered_map<wxString, double>& aTempNumericVars,
 
 1227                                       const std::unordered_map<wxString, wxString>& aTempStringVars )
 
 1245        m_lastErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
 
 
 1283    std::vector<wxString> 
result;
 
 1288        result.reserve( errors.size() );
 
 1290        for( 
const auto& error : errors )
 
 
 1316    wxString testInput = 
"@{" + aExpression + 
"}";
 
 1333        for( 
const auto& error : errors )
 
 1335            if( error.find( 
"Syntax error" ) != std::string::npos ||
 
 1336                error.find( 
"Parser failed" ) != std::string::npos )
 
 
 1351    while( ( pos = aInput.find( 
"@{", pos ) ) != wxString::npos )
 
 
 1362    std::vector<wxString> expressions;
 
 1365    while( ( pos = aInput.find( 
"@{", pos ) ) != wxString::npos )
 
 1367        size_t start = pos + 2; 
 
 1368        size_t end = aInput.find( 
"}", start );
 
 1370        if( 
end != wxString::npos )
 
 1372            expressions.push_back( aInput.substr( start, 
end - start ) );
 
 
 1386    return aWxStr.ToStdString( wxConvUTF8 );
 
 
 1391    return wxString( aStdStr.c_str(), wxConvUTF8 );
 
 
 1395    const wxString& aInput,
 
 1396    const std::unordered_map<wxString, double>& aTempNumericVars,
 
 1397    const std::unordered_map<wxString, wxString>& aTempStringVars )
 const 
 1399    wxString 
result = aInput;
 
 1403    std::vector<std::pair<size_t, size_t>> expressionRanges;
 
 1406    while( (pos = 
result.find( 
"@{", pos )) != std::string::npos )
 
 1409        size_t braceCount = 1;
 
 1410        size_t searchPos = start + 2; 
 
 1413        while( searchPos < 
result.length() && braceCount > 0 )
 
 1415            if( 
result[searchPos] == 
'{' )
 
 1417            else if( 
result[searchPos] == 
'}' )
 
 1422        if( braceCount == 0 )
 
 1424            expressionRanges.emplace_back( start, searchPos ); 
 
 1432    while( (pos = 
result.find( 
"${", pos )) != std::string::npos )
 
 1435        bool insideExpression = 
false;
 
 1436        for( 
const auto& range : expressionRanges )
 
 1438            if( pos >= range.first && pos < range.second )
 
 1440                insideExpression = 
true;
 
 1445        if( insideExpression )
 
 1449            size_t closePos = 
result.find( 
"}", pos + 2 );
 
 1450            if( closePos != std::string::npos )
 
 1453                size_t afterBrace = closePos + 1;
 
 1454                bool followedByUnit = 
false;
 
 1456                if( afterBrace < 
result.length() )
 
 1460                    for( 
const auto& unit : units )
 
 1462                        if( afterBrace + unit.length() <= 
result.length() &&
 
 1463                            result.substr( afterBrace, unit.length() ) == unit )
 
 1465                            followedByUnit = 
true;
 
 1471                if( !followedByUnit )
 
 1486        size_t closePos = 
result.find( 
"}", pos + 2 );
 
 1487        if( closePos == std::string::npos )
 
 1494        wxString varName = 
result.substr( pos + 2, closePos - pos - 2 );
 
 1495        wxString replacement;
 
 1499        auto stringIt = aTempStringVars.find( varName );
 
 1500        if( stringIt != aTempStringVars.end() )
 
 1502            replacement = stringIt->second;
 
 1508            auto numIt = aTempNumericVars.find( varName );
 
 1509            if( numIt != aTempNumericVars.end() )
 
 1511                replacement = wxString::FromDouble( numIt->second );
 
 1522                    if( std::holds_alternative<std::string>( value ) )
 
 1527                    else if( std::holds_alternative<double>( value ) )
 
 1529                        replacement = wxString::FromDouble( std::get<double>( value ) );
 
 1539            result.replace( pos, closePos - pos + 1, replacement );
 
 1540            pos += replacement.length();
 
 1546                m_lastErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
 
 
 1556    const std::unordered_map<wxString, double>* aTempNumericVars,
 
 1557    const std::unordered_map<wxString, wxString>* aTempStringVars )
 const 
 1565            if( customResult.HasValue() )
 
 1566                return customResult;
 
 1574        if( aTempStringVars )
 
 1577            if( 
auto it = aTempStringVars->find( wxVarName ); it != aTempStringVars->end() )
 
 1585        if( aTempNumericVars )
 
 1588            if( 
auto it = aTempNumericVars->find( wxVarName ); it != aTempNumericVars->end() )
 
 1604            wxString testString = wxString::Format( 
"${%s}", varName );
 
 1607            bool wasResolved = 
false;
 
 1608            std::function<bool( wxString* )> 
resolver =
 
 1609                [&wasResolved]( wxString* token ) -> 
bool 
 1623                std::function<bool( wxString* )> valueResolver =
 
 1624                    []( wxString* token ) -> 
bool 
 1631                wxString resolvedValue = 
ExpandTextVars( testString, &valueResolver );
 
 1634                if( resolvedValue != testString )
 
 1642                        auto result = fast_float::from_chars( resolvedStd.data(), resolvedStd.data() + resolvedStd.size(), numValue );
 
 1644                        if( 
result.ec != std::errc() || 
result.ptr != resolvedStd.data() + resolvedStd.size() )
 
 1645                            throw std::invalid_argument( fmt::format( 
"Cannot convert '{}' to number", resolvedStd ) );
 
 1657        catch( 
const std::exception& e )
 
 1670            fmt::format( 
"Undefined variable: {}", aVarName ) );
 
 
 1675    const std::string& aInput,
 
 1683        if (partialResult != aInput) {
 
 1685            return {std::move(partialResult), partialHadErrors};
 
 1691    } 
catch (
const std::bad_alloc&) {
 
 1695        return {aInput, 
true};
 
 1696    } 
catch (
const std::exception& e) {
 
 1698            m_lastErrors->AddError(fmt::format(
"Exception: {}", e.what()));
 
 1700        return {aInput, 
true};
 
 
 1705    const std::string& aInput,
 
 1708    std::string 
result = aInput;
 
 1709    bool        hadAnyErrors = 
false;
 
 1713    std::vector<std::pair<size_t, size_t>> expressionRanges;
 
 1716    while( ( pos = 
result.find( 
"@{", pos ) ) != std::string::npos )
 
 1719        size_t exprStart = pos + 2; 
 
 1720        size_t braceCount = 1;
 
 1721        size_t searchPos = exprStart;
 
 1724        while( searchPos < 
result.length() && braceCount > 0 )
 
 1726            if( 
result[searchPos] == 
'{' )
 
 1730            else if( 
result[searchPos] == 
'}' )
 
 1737        if( braceCount == 0 )
 
 1739            size_t end = searchPos; 
 
 1740            expressionRanges.emplace_back( start, 
end );
 
 1750    for( 
auto it = expressionRanges.rbegin(); it != expressionRanges.rend(); ++it )
 
 1752        auto [start, 
end] = *it;
 
 1753        std::string fullExpr = 
result.substr( start, 
end - start );
 
 1754        std::string innerExpr = 
result.substr( start + 2, 
end - start - 3 ); 
 
 1760            std::string testExpr = 
"@{" + innerExpr + 
"}";
 
 1763            auto tempErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
 
 1770            if( !evalHadErrors )
 
 1773                result.replace( start, 
end - start, evalResult );
 
 1778                hadAnyErrors = 
true;
 
 1782                    oldErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
 
 1784                oldErrors->AddError( fmt::format( 
"Failed to evaluate expression: {}", fullExpr ) );
 
 1794                m_lastErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
 
 1796            m_lastErrors->AddError( fmt::format( 
"Exception in expression: {}", fullExpr ) );
 
 1797            hadAnyErrors = 
true;
 
 1801    return { std::move( 
result ), hadAnyErrors };
 
 
 1807    if( aInput.empty() )
 
 1809        return { std::string{}, 
false };
 
 1813    struct ErrorCollectorGuard
 
 1833        auto parser_deleter = []( 
void* p )
 
 1835            KI_EVAL::ParseFree( p, free );
 
 1839        std::unique_ptr<void, 
decltype( parser_deleter )> parser{ KI_EVAL::ParseAlloc( malloc ), parser_deleter };
 
 1847            return { aInput, 
true };
 
 1858            token_type = tokenizer.get_next_token( token_value );
 
 1861            KI_EVAL::Parse( parser.get(), 
static_cast<int>( token_type ), token_value, &document );
 
 1878            auto [
result, had_errors] = processor.
Process( *document, std::move( aVariableCallback ) );
 
 1885                return { aInput, 
true };
 
 1889            return { std::move( 
result ), had_errors };
 
 1894        return { aInput, 
true };
 
 1896    catch( 
const std::bad_alloc& )
 
 1902        return { aInput, 
true };
 
 1904    catch( 
const std::exception& e )
 
 1908            m_lastErrors->AddError( fmt::format( 
"Exception: {}", e.what() ) );
 
 1910        return { aInput, 
true };
 
 
 1956    wxString processedExpression = aString;
 
 1962    std::sort( varNames.begin(), varNames.end(),
 
 1963               []( 
const wxString& a, 
const wxString& b ) { return a.length() > b.length(); } );
 
 1966    for( 
const auto& varName : varNames )
 
 1970        wxString pattern = 
"\\b" + varName + 
"\\b";
 
 1971        wxString replacement = 
"${" + varName + 
"}";
 
 1977        while( ( pos = processedExpression.find( varName, pos ) ) != wxString::npos )
 
 1980            bool isWholeWord = 
true;
 
 1985                wxChar before = processedExpression[pos - 1];
 
 1986                if( wxIsalnum( before ) || before == 
'_' || before == 
'$' )
 
 1987                    isWholeWord = 
false;
 
 1991            if( isWholeWord && pos + varName.length() < processedExpression.length() )
 
 1993                wxChar after = processedExpression[pos + varName.length()];
 
 1994                if( wxIsalnum( after ) || after == 
'_' )
 
 1995                    isWholeWord = 
false;
 
 2000                processedExpression.replace( pos, varName.length(), replacement );
 
 2001                pos += replacement.length();
 
 2005                pos += varName.length();
 
 2011    wxString wrappedExpression = 
"@{" + processedExpression + 
"}";
 
 
 2049    wxString value = 
m_evaluator.GetVariable( aString );
 
 2054    if( !value.ToDouble( &
result ) )
 
 
EDA_UNITS GetDefaultUnits() const
Get the current default units.
 
bool HasVariableCallback() const
Check if a custom variable callback is set.
 
wxString Evaluate(const wxString &aInput)
Main evaluation function - processes input string and evaluates all} expressions.
 
bool TestExpression(const wxString &aExpression)
Test if an expression can be parsed without evaluating it.
 
bool RemoveVariable(const wxString &aName)
Remove a variable from the evaluator.
 
bool HasErrors() const
Check if the last evaluation had errors.
 
std::pair< std::string, bool > evaluateWithFullParser(const std::string &aInput, VariableCallback aVariableCallback)
Full parser evaluation (original behavior) - fails completely on any error.
 
wxString GetErrorSummary() const
Get detailed error information from the last evaluation.
 
void SetDefaultUnits(EDA_UNITS aUnits)
Set the default units for expressions.
 
bool GetClearVariablesOnEvaluate() const
Check if automatic variable clearing is enabled.
 
std::unordered_map< std::string, calc_parser::Value > m_variables
 
void SetVariables(const std::unordered_map< wxString, double > &aVariables)
Set multiple variables at once from a map.
 
std::pair< std::string, bool > evaluateWithPartialErrorRecovery(const std::string &aInput, VariableCallback aVariableCallback)
Parse and evaluate with partial error recovery - malformed expressions left unchanged.
 
std::string wxStringToStdString(const wxString &aWxStr) const
Convert wxString to std::string using UTF-8 encoding.
 
size_t GetErrorCount() const
Get count of errors from the last evaluation.
 
VariableCallback createCombinedCallback(const std::unordered_map< wxString, double > *aTempNumericVars=nullptr, const std::unordered_map< wxString, wxString > *aTempStringVars=nullptr) const
Create a callback function that combines all variable sources.
 
std::vector< wxString > ExtractExpressions(const wxString &aInput) const
Extract all} expressions from input without evaluating.
 
void ClearErrors()
Clear any stored error information.
 
std::unique_ptr< calc_parser::ERROR_COLLECTOR > m_lastErrors
 
wxString stdStringToWxString(const std::string &aStdStr) const
Convert std::string to wxString using UTF-8 encoding.
 
std::vector< wxString > GetErrors() const
Get individual error messages from the last evaluation.
 
void ClearVariableCallback()
Clear the custom variable resolver callback.
 
void ClearVariables()
Clear all stored variables.
 
bool HasVariable(const wxString &aName) const
Check if a variable exists in stored variables.
 
std::vector< wxString > GetVariableNames() const
Get all stored variable names currently defined.
 
~EXPRESSION_EVALUATOR()
Destructor.
 
EXPRESSION_EVALUATOR & operator=(const EXPRESSION_EVALUATOR &aOther)
 
std::function< calc_parser::Result< calc_parser::Value >(const std::string &aVariableName)> VariableCallback
 
std::pair< std::string, bool > evaluateWithParser(const std::string &aInput, VariableCallback aVariableCallback)
Parse and evaluate the input string using the expression parser.
 
void SetVariable(const wxString &aName, double aValue)
Set a numeric variable for use in expressions.
 
void SetClearVariablesOnEvaluate(bool aEnable)
Enable or disable automatic variable clearing after evaluation.
 
void SetVariableCallback(VariableCallback aCallback)
Set a custom variable resolver callback.
 
wxString GetVariable(const wxString &aName) const
Get the current value of a stored variable.
 
size_t CountExpressions(const wxString &aInput) const
Count the number of} expressions in input string.
 
VariableCallback m_customCallback
 
wxString expandVariablesOutsideExpressions(const wxString &aInput, const std::unordered_map< wxString, double > &aTempNumericVars, const std::unordered_map< wxString, wxString > &aTempStringVars) const
Expand ${variable} patterns that are outside} expressions.
 
bool m_clearVariablesOnEvaluate
 
EXPRESSION_EVALUATOR(bool aClearVariablesOnEvaluate=false)
Construct a new Expression Evaluator in static variable mode.
 
TOKENIZER_CONTEXT m_context
 
calc_parser::TOKEN_TYPE parse_number()
 
void add_error(std::string_view message) const
 
static constexpr calc_parser::TOKEN_TYPE make_number_token(double value) noexcept
 
void advance_position(size_t count=1) noexcept
 
utf8_utils::SI_PREFIX_HANDLER SI_HANDLER
 
constexpr size_t get_column() const noexcept
 
void skip_whitespace() noexcept
 
bool has_more_tokens() const noexcept
 
calc_parser::ERROR_COLLECTOR * m_errorCollector
 
char32_t peek_char(size_t offset=1) const noexcept
 
calc_parser::TOKEN_TYPE parse_string_literal(char32_t quote_char)
 
KIEVAL_TEXT_TOKENIZER(std::string_view input, calc_parser::ERROR_COLLECTOR *error_collector=nullptr, EDA_UNITS default_units=EDA_UNITS::MM)
 
TextEvalToken get_next_token(calc_parser::TOKEN_TYPE &token_value)
 
utf8_utils::CHARACTER_CLASSIFIER CLASSIFIER
 
calc_parser::TOKEN_TYPE parse_text_content()
 
calc_parser::TOKEN_TYPE parse_identifier()
 
constexpr size_t get_line() const noexcept
 
static calc_parser::TOKEN_TYPE make_string_token(std::string value) noexcept
 
char32_t current_char() const noexcept
 
void LocaleChanged()
Handle locale changes (for decimal separator)
 
~NUMERIC_EVALUATOR_COMPAT()
Destructor.
 
void SetDefaultUnits(EDA_UNITS aUnits)
Set default units for evaluation.
 
bool Process(const wxString &aString)
Process and evaluate an expression.
 
wxString Result() const
Get the result of the last evaluation.
 
void RemoveVar(const wxString &aString)
Remove a single variable.
 
void SetVar(const wxString &aString, double aValue)
Set a variable value.
 
void ClearVar()
Remove all variables.
 
EXPRESSION_EVALUATOR m_evaluator
 
double GetVar(const wxString &aString)
Get a variable value.
 
void Clear()
Clear parser state but retain variables.
 
NUMERIC_EVALUATOR_COMPAT(EDA_UNITS aUnits)
Constructor with default units.
 
bool IsValid() const
Check if the last evaluation was successful.
 
wxString OriginalText() const
Get the original input text.
 
static auto Process(const DOC &aDoc, VariableCallback aVariableCallback) -> std::pair< std::string, bool >
Process document using callback for variable resolution.
 
auto GetErrors() const -> const std::vector< std::string > &
 
static constexpr Unit parseUnit(std::string_view unitStr) noexcept
Parse a unit string and return the corresponding Unit enum.
 
static double convertToEdaUnits(double value, std::string_view unitStr, EDA_UNITS targetUnits)
Convert a value with unit string to target EDA_UNITS.
 
static std::vector< std::string > getAllUnitStrings()
Get all unit strings in parsing order (longest first)
 
static constexpr int sequence_length(std::byte first) noexcept
 
static std::string to_utf8(std::u32string_view utf32)
 
static constexpr bool is_ascii(std::byte b) noexcept
 
static std::u32string to_utf32(std::string_view utf8)
 
static constexpr bool is_continuation(std::byte b) noexcept
 
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
 
static FILENAME_RESOLVER * resolver
 
double convertToDefaultUnits(double aValue, const std::string &aUnitStr, EDA_UNITS aDefaultUnits)
 
Unit parseUnit(const std::string &aUnitStr)
 
double getConversionFactor(Unit aFromUnit, Unit aToUnit)
 
Unit edaUnitsToInternal(EDA_UNITS aUnits)
 
thread_local ERROR_COLLECTOR * g_errorCollector
 
auto MakeValue(T aVal) -> Result< T >
 
std::variant< double, std::string > Value
 
auto MakeError(std::string aMsg) -> Result< T >
 
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
 
double fromMM(double aMMValue)
 
double toMM(double aIUValue)
 
static constexpr bool is_digit(UnicodeCodepoint auto cp) noexcept
 
static constexpr bool is_alpha(UnicodeCodepoint auto cp) noexcept
 
static constexpr bool is_whitespace(UnicodeCodepoint auto cp) noexcept
 
static constexpr bool is_ascii_alpha(UnicodeCodepoint auto cp) noexcept
 
static constexpr bool is_alnum(UnicodeCodepoint auto cp) noexcept
 
static constexpr double get_multiplier(UnicodeCodepoint auto cp) noexcept
 
static constexpr std::array< PREFIX, 18 > prefixes
 
static constexpr bool is_si_prefix(UnicodeCodepoint auto cp) noexcept
 
wxString result
Test unit parsing edge cases and error handling.