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
90concept Utf8Byte = std::same_as<T, char> || std::same_as<T, unsigned char> || std::same_as<T, std::byte>;
98 static constexpr bool is_ascii( std::byte b )
noexcept
100 return ( b & std::byte{ 0x80 } ) == std::byte{ 0x00 };
105 return ( b & std::byte{ 0xC0 } ) == std::byte{ 0x80 };
113 if( ( first & std::byte{ 0xE0 } ) == std::byte{ 0xC0 } )
116 if( ( first & std::byte{ 0xF0 } ) == std::byte{ 0xE0 } )
119 if( ( first & std::byte{ 0xF8 } ) == std::byte{ 0xF0 } )
127 static std::u32string
to_utf32( std::string_view utf8 )
130 result.reserve( utf8.size() );
132 auto bytes = std::as_bytes( std::span{ utf8.data(), utf8.size() } );
134 for(
size_t i = 0; i <
bytes.size(); )
136 std::byte first =
bytes[i];
139 if( len == 0 || i + len >
bytes.size() )
142 result.push_back( U
'\uFFFD' );
147 char32_t codepoint = 0;
151 case 1: codepoint = std::to_integer<char32_t>( first );
break;
157 result.push_back( U
'\uFFFD' );
161 codepoint = ( std::to_integer<char32_t>( first & std::byte{ 0x1F } ) << 6 )
162 | std::to_integer<char32_t>(
bytes[i + 1] & std::byte{ 0x3F } );
170 result.push_back( U
'\uFFFD' );
174 codepoint = ( std::to_integer<char32_t>( first & std::byte{ 0x0F } ) << 12 )
175 | ( std::to_integer<char32_t>(
bytes[i + 1] & std::byte{ 0x3F } ) << 6 )
176 | std::to_integer<char32_t>(
bytes[i + 2] & std::byte{ 0x3F } );
185 result.push_back( U
'\uFFFD' );
189 codepoint = ( std::to_integer<char32_t>( first & std::byte{ 0x07 } ) << 18 )
190 | ( std::to_integer<char32_t>(
bytes[i + 1] & std::byte{ 0x3F } ) << 12 )
191 | ( std::to_integer<char32_t>(
bytes[i + 2] & std::byte{ 0x3F } ) << 6 )
192 | std::to_integer<char32_t>(
bytes[i + 3] & std::byte{ 0x3F } );
198 if( codepoint > 0x10FFFF || ( codepoint >= 0xD800 && codepoint <= 0xDFFF ) )
200 result.push_back( U
'\uFFFD' );
202 else if( len == 2 && codepoint < 0x80 )
204 result.push_back( U
'\uFFFD' );
206 else if( len == 3 && codepoint < 0x800 )
208 result.push_back( U
'\uFFFD' );
210 else if( len == 4 && codepoint < 0x10000 )
212 result.push_back( U
'\uFFFD' );
216 result.push_back( codepoint );
226 static std::string
to_utf8( std::u32string_view utf32 )
229 result.reserve( utf32.size() * 4 );
231 for(
char32_t cp : utf32 )
236 result.push_back(
static_cast<char>( cp ) );
238 else if( cp <= 0x7FF )
241 result.push_back(
static_cast<char>( 0xC0 | ( cp >> 6 ) ) );
242 result.push_back(
static_cast<char>( 0x80 | ( cp & 0x3F ) ) );
244 else if( cp <= 0xFFFF )
247 if( cp >= 0xD800 && cp <= 0xDFFF )
250 result.append(
"\uFFFD" );
254 result.push_back(
static_cast<char>( 0xE0 | ( cp >> 12 ) ) );
255 result.push_back(
static_cast<char>( 0x80 | ( ( cp >> 6 ) & 0x3F ) ) );
256 result.push_back(
static_cast<char>( 0x80 | ( cp & 0x3F ) ) );
259 else if( cp <= 0x10FFFF )
262 result.push_back(
static_cast<char>( 0xF0 | ( cp >> 18 ) ) );
263 result.push_back(
static_cast<char>( 0x80 | ( ( cp >> 12 ) & 0x3F ) ) );
264 result.push_back(
static_cast<char>( 0x80 | ( ( cp >> 6 ) & 0x3F ) ) );
265 result.push_back(
static_cast<char>( 0x80 | ( cp & 0x3F ) ) );
270 result.append(
"\uFFFD" );
288 return cp == U
' ' || cp == U
'\t' || cp == U
'\r' || cp == U
'\n' || cp == U
'\f' || cp == U
'\v' || cp == U
'\u00A0'
290 cp == U
'\u2000' || cp == U
'\u2001' || cp == U
'\u2002' || cp == U
'\u2003' || cp == U
'\u2004'
291 || cp == U
'\u2005' || cp == U
'\u2006' || cp == U
'\u2007' || cp == U
'\u2008' || cp == U
'\u2009'
292 || cp == U
'\u200A' || cp == U
'\u2028' || cp == U
'\u2029' || cp == U
'\u202F' || cp == U
'\u205F'
298 return cp >= U
'0' && cp <= U
'9';
303 return ( cp >= U
'a' && cp <= U
'z' ) || ( cp >= U
'A' && cp <= U
'Z' );
327 static constexpr auto prefixes = std::to_array<PREFIX>( { { U
'a', 1e-18 },
347 return std::ranges::any_of(
prefixes,
356 auto it = std::ranges::find_if(
prefixes,
361 return it !=
prefixes.end() ? it->multiplier : 1.0;
442 if( aFromUnit == aToUnit )
451 case UNIT::Mil: toMM = 25.4 / 1000.0;
break;
452 case UNIT::UM: toMM = 1.0 / 1000.0;
break;
463 case UNIT::Mil: fromMM = 1000.0 / 25.4;
break;
464 case UNIT::UM: fromMM = 1000.0;
break;
466 case UNIT::CM: fromMM = 1.0 / 10.0;
break;
470 return toMM * fromMM;
495 size_t peek_pos =
m_pos + offset;
496 return peek_pos <
m_text.size() ?
m_text[peek_pos] : U
'\0';
503 if( c != U
'e' && c != U
'E' )
516 for(
size_t i = 0; i < count &&
m_pos <
m_text.size(); ++i )
551 std::strncpy( token.text, value.c_str(),
sizeof( token.text ) - 1 );
552 token.text[
sizeof( token.text ) - 1] =
'\0';
561 token.dValue = value;
569 std::u32string content;
570 content.reserve( 64 );
584 content.push_back( U
'\n' );
587 content.push_back( U
'\t' );
590 content.push_back( U
'\r' );
593 content.push_back( U
'\\' );
596 content.push_back( U
'"' );
599 content.push_back( U
'\'' );
602 content.push_back( U
'\0' );
609 for(
int i = 0; i < 2 &&
m_pos <
m_text.size(); ++i )
612 if( ( hex_char >= U
'0' && hex_char <= U
'9' )
613 || ( hex_char >= U
'A' && hex_char <= U
'F' )
614 || ( hex_char >= U
'a' && hex_char <= U
'f' ) )
616 hex.push_back( hex_char );
630 unsigned long value = std::stoul( hex_str,
nullptr, 16 );
632 if( value <= 0x10FFFF )
633 content.push_back(
static_cast<char32_t>( value ) );
635 content.push_back( U
'\uFFFD' );
639 content.push_back( U
'\uFFFD' );
644 content.append( U
"\\x" );
650 content.push_back( U
'\\' );
651 content.push_back( escaped );
655 else if( c == U
'\n' )
657 add_error(
"Unterminated string literal" );
662 content.push_back( c );
673 add_error(
"Missing closing quote in string literal" );
681 std::u32string number_text;
682 number_text.reserve( 32 );
684 double multiplier = 1.0;
685 bool hasPrefix =
false;
688 auto canConsumePrefix = [&](
char32_t aPrefix )
693 if( aPrefix == U
'R' || aPrefix == U
'F' )
699 auto consumePrefix = [&](
char32_t aPrefix )
722 number_text.push_back( U
'.' );
733 std::u32string potential_unit;
734 size_t temp_pos =
m_pos;
736 while( temp_pos <
m_text.size() )
738 char32_t unit_char =
m_text[temp_pos];
742 potential_unit.push_back( unit_char );
752 if( !potential_unit.empty() )
762 else if( canConsumePrefix( c ) )
768 else if( canConsumePrefix( c ) )
774 else if( canConsumePrefix( c ) )
782 number_text.push_back( U
'.' );
818 add_error(
"Invalid scientific notation: missing exponent digits" );
830 std::u32string potential_unit;
831 size_t temp_pos =
m_pos;
833 while( temp_pos <
m_text.size() )
835 char32_t unit_char =
m_text[temp_pos];
839 potential_unit.push_back( unit_char );
848 if( !potential_unit.empty() )
860 else if( canConsumePrefix( c ) )
873 if( !number_str.empty() && number_str !=
"." )
875 auto result = fast_float::from_chars( number_str.data(), number_str.data() + number_str.size(), value );
877 if(
result.ec != std::errc() ||
result.ptr != number_str.data() + number_str.size() )
878 throw std::invalid_argument( fmt::format(
"Cannot convert '{}' to number", number_str ) );
882 if( !std::isfinite( value ) )
889 catch(
const std::exception& e )
891 add_error( fmt::format(
"Invalid number format: {}", e.what() ) );
899 size_t whitespace_start =
m_pos;
905 std::u32string unit_text;
915 unit_text.push_back( c );
924 if( !unit_text.empty() )
934 value = converted_value;
939 m_pos = whitespace_start;
945 m_pos = whitespace_start;
954 std::u32string identifier;
955 identifier.reserve( 64 );
977 if( ( current == U
'@' &&
next == U
'{' ) || ( current == U
'$' &&
next == U
'{' ) )
980 text.push_back( current );
1016 if( current == U
'@' &&
next == U
'{' )
1025 if( current == U
'$' &&
next == U
'{' )
1035 if( current == U
'}' )
1051 if( current == U
'<' &&
next == U
'=' )
1057 if( current == U
'>' &&
next == U
'=' )
1063 if( current == U
'=' &&
next == U
'=' )
1069 if( current == U
'!' &&
next == U
'=' )
1080 static constexpr std::array<std::pair<char32_t, TEXT_EVAL_TOKEN>, 11> single_char_tokens{
1094 if(
auto it = std::ranges::find_if( single_char_tokens,
1095 [current](
const auto& pair )
1097 return pair.first == current;
1099 it != single_char_tokens.end() )
1108 if( current == U
'"' || current == U
'\'' )
1169 m_lastErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
1179 m_lastErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
1188 m_lastErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
1193 bool aClearVariablesOnEvaluate ) :
1199 m_lastErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
1213 m_lastErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
1226 if(
this != &aOther )
1234 m_lastErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
1258 if(
this != &aOther )
1352 if( std::holds_alternative<double>( it->second ) )
1354 double val = std::get<double>( it->second );
1357 if( val == std::floor( val ) &&
std::abs( val ) < 1e15 )
1358 return wxString::Format(
"%.0f", val );
1361 return wxString::Format(
"%.10g", val );
1375 std::vector<wxString> names;
1387 for(
const auto& [
name, value] : aVariables )
1394 for(
const auto& [
name, value] : aVariables )
1401 std::unordered_map<wxString, double> emptyNumVars;
1402 std::unordered_map<wxString, wxString> emptyStringVars;
1403 return Evaluate( aInput, emptyNumVars, emptyStringVars );
1408 const std::unordered_map<wxString, double>& aTempVariables )
1410 std::unordered_map<wxString, wxString> emptyStringVars;
1411 return Evaluate( aInput, aTempVariables, emptyStringVars );
1416 const std::unordered_map<wxString, double>& aTempNumericVars,
1417 const std::unordered_map<wxString, wxString>& aTempStringVars )
1434 m_lastErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
1474 std::vector<wxString>
result;
1478 const std::vector<std::string>& errors =
m_lastErrors->GetErrors();
1479 result.reserve( errors.size() );
1481 for(
const std::string& error : errors )
1511 wxString testInput =
"@{" + aExpression +
"}";
1529 for(
const std::string& error :
m_lastErrors->GetErrors() )
1531 if( error.find(
"Syntax error" ) != std::string::npos
1532 || error.find(
"Parser failed" ) != std::string::npos )
1548 while( ( pos = aInput.find(
"@{", pos ) ) != wxString::npos )
1560 std::vector<wxString> expressions;
1563 while( ( pos = aInput.find(
"@{", pos ) ) != wxString::npos )
1565 size_t start = pos + 2;
1566 size_t end = aInput.find(
"}", start );
1568 if(
end != wxString::npos )
1570 expressions.push_back( aInput.substr( start,
end - start ) );
1585 return aWxStr.ToStdString( wxConvUTF8 );
1591 return wxString( aStdStr.c_str(), wxConvUTF8 );
1596 const wxString& aInput,
1597 const std::unordered_map<wxString, double>& aTempNumericVars,
1598 const std::unordered_map<wxString, wxString>& aTempStringVars )
const
1600 wxString
result = aInput;
1604 std::vector<std::pair<size_t, size_t>> expressionRanges;
1607 while( ( pos =
result.find(
"@{", pos ) ) != std::string::npos )
1610 size_t braceCount = 1;
1611 size_t searchPos = start + 2;
1614 while( searchPos <
result.length() && braceCount > 0 )
1616 if(
result[searchPos] ==
'{' )
1618 else if(
result[searchPos] ==
'}' )
1623 if( braceCount == 0 )
1624 expressionRanges.emplace_back( start, searchPos );
1632 while( ( pos =
result.find(
"${", pos ) ) != std::string::npos )
1635 bool insideExpression =
false;
1637 for(
const auto& range : expressionRanges )
1639 if( pos >= range.first && pos < range.second )
1641 insideExpression =
true;
1646 if( insideExpression )
1650 size_t closePos =
result.find(
"}", pos + 2 );
1652 if( closePos != std::string::npos )
1655 size_t afterBrace = closePos + 1;
1656 bool followedByUnit =
false;
1658 if( afterBrace <
result.length() )
1663 for(
const auto& unit : units )
1665 if( afterBrace + unit.length() <=
result.length()
1666 &&
result.substr( afterBrace, unit.length() ) == unit )
1668 followedByUnit =
true;
1674 if( !followedByUnit )
1689 size_t closePos =
result.find(
"}", pos + 2 );
1691 if( closePos == std::string::npos )
1698 wxString varName =
result.substr( pos + 2, closePos - pos - 2 );
1699 wxString replacement;
1703 auto stringIt = aTempStringVars.find( varName );
1705 if( stringIt != aTempStringVars.end() )
1707 replacement = stringIt->second;
1713 auto numIt = aTempNumericVars.find( varName );
1715 if( numIt != aTempNumericVars.end() )
1717 replacement = wxString::FromDouble( numIt->second );
1730 if( std::holds_alternative<std::string>( value ) )
1735 else if( std::holds_alternative<double>( value ) )
1737 replacement = wxString::FromDouble( std::get<double>( value ) );
1747 result.replace( pos, closePos - pos + 1, replacement );
1748 pos += replacement.length();
1754 m_lastErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
1767 const std::unordered_map<wxString, wxString>* aTempStringVars )
const
1769 return [
this, aTempNumericVars, aTempStringVars]
1777 if( customResult.HasValue() )
1778 return customResult;
1786 if( aTempStringVars )
1790 if(
auto it = aTempStringVars->find( wxVarName ); it != aTempStringVars->end() )
1798 if( aTempNumericVars )
1802 if(
auto it = aTempNumericVars->find( wxVarName ); it != aTempNumericVars->end() )
1816 wxString testString = wxString::Format(
"${%s}", varName );
1819 bool wasResolved =
false;
1820 std::function<bool( wxString* )> resolver =
1821 [&wasResolved]( wxString* token ) ->
bool
1835 std::function<bool( wxString* )> valueResolver =
1836 []( wxString* token ) ->
bool
1846 if( resolvedValue != testString )
1854 auto result = fast_float::from_chars( resolvedStd.data(),
1855 resolvedStd.data() + resolvedStd.size(),
1858 if(
result.ec != std::errc()
1859 ||
result.ptr != resolvedStd.data() + resolvedStd.size() )
1861 throw std::invalid_argument( fmt::format(
"Cannot convert '{}' to number",
1875 catch(
const std::exception& )
1901 if( partialResult != aInput )
1904 return { std::move( partialResult ), partialHadErrors };
1910 catch(
const std::bad_alloc& )
1915 return { aInput,
true };
1917 catch(
const std::exception& e )
1920 m_lastErrors->AddError( fmt::format(
"Exception: {}", e.what() ) );
1922 return { aInput,
true };
1927std::pair<std::string, bool>
1930 std::string
result = aInput;
1931 bool hadAnyErrors =
false;
1935 std::vector<std::pair<size_t, size_t>> expressionRanges;
1938 while( ( pos =
result.find(
"@{", pos ) ) != std::string::npos )
1941 size_t exprStart = pos + 2;
1942 size_t braceCount = 1;
1943 size_t searchPos = exprStart;
1946 while( searchPos <
result.length() && braceCount > 0 )
1948 if(
result[searchPos] ==
'{' )
1950 else if(
result[searchPos] ==
'}' )
1956 if( braceCount == 0 )
1958 size_t end = searchPos;
1959 expressionRanges.emplace_back( start,
end );
1969 for(
auto it = expressionRanges.rbegin(); it != expressionRanges.rend(); ++it )
1971 auto [start,
end] = *it;
1972 std::string fullExpr =
result.substr( start,
end - start );
1973 std::string innerExpr =
result.substr( start + 2,
end - start - 3 );
1979 std::string testExpr =
"@{" + innerExpr +
"}";
1982 auto tempErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
1989 if( !evalHadErrors )
1992 result.replace( start,
end - start, evalResult );
1997 hadAnyErrors =
true;
2001 oldErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
2003 oldErrors->AddError( fmt::format(
"Failed to evaluate expression: {}", fullExpr ) );
2013 m_lastErrors = std::make_unique<calc_parser::ERROR_COLLECTOR>();
2015 m_lastErrors->AddError( fmt::format(
"Exception in expression: {}", fullExpr ) );
2016 hadAnyErrors =
true;
2020 return { std::move(
result ), hadAnyErrors };
2027 if( aInput.empty() )
2029 return { std::string{},
false };
2033 struct ERROR_COLLECTOR_GUARD
2035 ~ERROR_COLLECTOR_GUARD()
2054 auto parser_deleter =
2057 KI_EVAL::ParseFree( p, free );
2061 std::unique_ptr<void,
decltype( parser_deleter )> parser{ KI_EVAL::ParseAlloc( malloc ), parser_deleter };
2068 return { aInput,
true };
2072 std::unique_ptr<calc_parser::DOC> document;
2079 token_type = tokenizer.get_next_token( token_value );
2082 KI_EVAL::Parse( parser.get(), (
int) token_type, token_value, &document );
2097 std::move( aVariableCallback ) );
2102 return { aInput,
true };
2104 return { std::move(
result ), had_errors };
2108 return { aInput,
true };
2110 catch(
const std::bad_alloc& )
2115 return { aInput,
true };
2117 catch(
const std::exception& e )
2120 m_lastErrors->AddError( fmt::format(
"Exception: {}", e.what() ) );
2122 return { aInput,
true };
2177 wxString processedExpression = aString;
2180 std::vector<wxString> varNames =
m_evaluator.GetVariableNames();
2183 std::sort( varNames.begin(), varNames.end(),
2184 [](
const wxString& a,
const wxString& b )
2186 return a.length() > b.length();
2190 for(
const wxString& varName : varNames )
2194 wxString pattern =
"\\b" + varName +
"\\b";
2195 wxString replacement =
"${" + varName +
"}";
2201 while( ( pos = processedExpression.find( varName, pos ) ) != wxString::npos )
2204 bool isWholeWord =
true;
2209 wxChar before = processedExpression[pos - 1];
2211 if( wxIsalnum( before ) || before ==
'_' || before ==
'$' )
2212 isWholeWord =
false;
2216 if( isWholeWord && pos + varName.length() < processedExpression.length() )
2218 wxChar after = processedExpression[pos + varName.length()];
2220 if( wxIsalnum( after ) || after ==
'_' )
2221 isWholeWord =
false;
2226 processedExpression.replace( pos, varName.length(), replacement );
2227 pos += replacement.length();
2231 pos += varName.length();
2237 wxString wrappedExpression =
"@{" + processedExpression +
"}";
2278 wxString value =
m_evaluator.GetVariable( aString );
2283 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.
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.
void SetVariables(const std::unordered_map< wxString, double > &aVariables)
Set multiple variables at once from a map.
std::pair< std::string, bool > evaluateWithFullParser(const std::string &aInput, VAR_CALLBACK aVariableCallback)
Full parser evaluation (original behavior) - fails completely on any error.
std::string wxStringToStdString(const wxString &aWxStr) const
Convert wxString to std::string using UTF-8 encoding.
std::unordered_map< std::string, calc_parser::VALUE > m_variables
std::pair< std::string, bool > evaluateWithParser(const std::string &aInput, VAR_CALLBACK aVariableCallback)
Parse and evaluate the input string using the expression parser.
size_t GetErrorCount() const
Get count of errors from the last evaluation.
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 SetVariableCallback(VAR_CALLBACK aCallback)
Set a custom variable resolver callback.
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)
VAR_CALLBACK 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::function< calc_parser::RESULT< calc_parser::VALUE >(const std::string &aVariableName)> VAR_CALLBACK
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.
VAR_CALLBACK m_customCallback
wxString GetVariable(const wxString &aName) const
Get the current value of a stored variable.
std::pair< std::string, bool > evaluateWithPartialErrorRecovery(const std::string &aInput, VAR_CALLBACK aVariableCallback)
Parse and evaluate with partial error recovery - malformed expressions left unchanged.
size_t CountExpressions(const wxString &aInput) const
Count the number of '@{}' expressions in input string.
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
static calc_parser::TOKEN_TYPE make_string_token(const std::string &value) noexcept
calc_parser::TOKEN_TYPE parse_number()
TEXT_EVAL_TOKEN get_next_token(calc_parser::TOKEN_TYPE &token_value)
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)
utf8_utils::CHARACTER_CLASSIFIER CLASSIFIER
calc_parser::TOKEN_TYPE parse_text_content()
calc_parser::TOKEN_TYPE parse_identifier()
bool at_exponent() const noexcept
constexpr size_t get_line() const 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 std::pair< std::string, bool > Process(const DOC &aDoc, VAR_CALLBACK aVariableCallback)
Process document using callback for variable resolution.
const std::vector< std::string > & GetErrors() const
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, RESOLUTION_CONTEXT aContext)
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
RESULT< T > MakeError(std::string aMsg)
std::variant< double, std::string > VALUE
RESULT< T > MakeValue(T aVal)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
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 auto prefixes
static constexpr bool is_si_prefix(UnicodeCodepoint auto cp) noexcept
static std::vector< uint8_t > bytes(const std::string &aText)
static const long long MM
wxString result
Test unit parsing edge cases and error handling.