55    evaluator.
SetVariable( std::string(
"project_name"), std::string(
"My PCB Project") );
 
   56    evaluator.
SetVariable( std::string(
"designer"), std::string(
"John Doe") );
 
   59        std::string expression;
 
   60        std::string expectedPattern; 
 
   63        std::string description;
 
   66    const std::vector<TestCase> cases = {
 
   69            "Board area: @{${board_width} * ${board_height}} mm²",
 
   70            "Board area: 8000 mm²",
 
   72            "Board area calculation" 
   75            "Perimeter: @{2 * (${board_width} + ${board_height})} mm",
 
   78            "Board perimeter calculation" 
   81            "Diagonal: @{format(sqrt(pow(${board_width}, 2) + pow(${board_height}, 2)), 1)} mm",
 
   84            "Board diagonal calculation" 
   89            "Project: ${project_name} | Designer: ${designer} | Rev: @{${revision}}",
 
   90            "Project: My PCB Project | Designer: John Doe | Rev: 3",
 
   92            "Title block information" 
   95            "Components: @{${component_count}} | Density: @{format(${component_count} / (${board_width} * ${board_height} / 10000), 2)} per cm²",
 
   96            "Components: 45 | Density: 56.25 per cm²",
 
   98            "Component density calculation" 
  103            "Created: @{dateformat(today())} | Build: @{today()} days since epoch",
 
  104            R
"(Created: \d{4}-\d{2}-\d{2} \| Build: \d+ days since epoch)", 
  106            "Date-based tracking" 
  111            "Status: @{if(${component_count} > 50, \"Complex\", \"Simple\")} design",
 
  112            "Status: Simple design",
 
  114            "Conditional design complexity" 
  117            "Status: @{if(${trace_width} >= 0.2, \"Standard\", \"Fine pitch\")} (@{${trace_width}}mm)",
 
  118            "Status: Standard (0.2mm)",
 
  120            "Conditional trace width description" 
  125            "PCB Summary:\n- Size: @{${board_width}}×@{${board_height}}mm\n- Area: @{${board_width} * ${board_height}}mm²\n- Components: @{${component_count}}",
 
  126            "PCB Summary:\n- Size: 100×80mm\n- Area: 8000mm²\n- Components: 45",
 
  128            "Multi-line documentation" 
  133            "Invalid: @{${undefined_var}} test",
 
  134            "Invalid: @{${undefined_var}} test",
 
  136            "Undefined variable behavior" 
  140    for( 
const auto& testCase : cases )
 
  142        auto result = evaluator.
Evaluate( wxString::FromUTF8( testCase.expression ) );
 
  144        if( testCase.shouldError )
 
  146            BOOST_CHECK_MESSAGE( evaluator.
HasErrors(),
 
  147                                "Expected error for: " + testCase.description );
 
  151            BOOST_CHECK_MESSAGE( !evaluator.
HasErrors(),
 
  152                                "Unexpected error for: " + testCase.description +
 
  155            if( testCase.isRegex )
 
  157                std::regex pattern( testCase.expectedPattern );
 
  158                BOOST_CHECK_MESSAGE( std::regex_match( 
result.ToStdString( wxConvUTF8 ), pattern ),
 
  159                                    "Result '" + 
result.ToStdString( wxConvUTF8 ) + 
"' doesn't match pattern '" +
 
  160                                    testCase.expectedPattern + 
"' for: " + testCase.description );
 
  164                BOOST_CHECK_MESSAGE( 
result.ToStdString( wxConvUTF8 ) == testCase.expectedPattern,
 
  165                                    "Expected '" + testCase.expectedPattern + 
"' but got '" +
 
  166                                    result.ToStdString( wxConvUTF8 ) + 
"' for: " + testCase.description );
 
 
  179        if( varName == 
"dynamic_value" )
 
  181        else if( varName == 
"dynamic_string" )
 
  183        else if( varName == 
"computed_value" )
 
  192        std::string expression;
 
  198    const std::vector<TestCase> cases = {
 
  199        { 
"@{${dynamic_value}}", 
"42", 0, 
false },
 
  200        { 
"Message: ${dynamic_string}", 
"Message: Hello from callback", 0, 
false },
 
  201        { 
"@{format(${computed_value}, 1)}", 
"70.7", 0.1, 
false },
 
  202        { 
"@{${dynamic_value} + ${computed_value}}", 
"112.7", 0.1, 
false },
 
  203        { 
"${nonexistent}", 
"${nonexistent}", 0, 
true },
 
  206    for( 
const auto& testCase : cases )
 
  208        auto result = evaluator.
Evaluate( wxString::FromUTF8( testCase.expression ) );
 
  210        if( testCase.shouldError )
 
  218            if( testCase.tolerance > 0 )
 
  221                std::regex numberRegex( R
"([\d.]+)" ); 
  223                std::string resultStr = result.ToStdString( wxConvUTF8 ); 
  224                if( std::regex_search( resultStr, match, numberRegex ) )
 
  226                    double actualValue = std::stod( match[0].str() );
 
  227                    double expectedValue = std::stod( testCase.expected );
 
  228                    BOOST_CHECK_CLOSE( actualValue, expectedValue, testCase.tolerance * 100 );
 
 
  245    std::vector<std::unique_ptr<EXPRESSION_EVALUATOR>> evaluators;
 
  247    for( 
int i = 0; i < 10; ++i )
 
  249        auto evaluator = std::make_unique<EXPRESSION_EVALUATOR>();
 
  250        evaluator->SetVariable( 
"thread_id", 
static_cast<double>( i ) );
 
  251        evaluator->SetVariable( 
"multiplier", 5.0 );
 
  252        evaluators.push_back( std::move( evaluator ) );
 
  256    for( 
int i = 0; i < 10; ++i )
 
  258        auto result = evaluators[i]->Evaluate( 
"@{${thread_id} * ${multiplier}}" );
 
  259        BOOST_CHECK( !evaluators[i]->HasErrors() );
 
  261        double expected = 
static_cast<double>( i * 5 );
 
  262        double actual = std::stod( 
result.ToStdString( wxConvUTF8 ) );
 
 
  275    std::string complexExpression = 
"@{";
 
  276    for( 
int i = 0; i < 100; ++i )
 
  278        if( i > 0 ) complexExpression += 
" + ";
 
  279        complexExpression += std::to_string( i );
 
  281    complexExpression += 
"}";
 
  283    auto result = evaluator.
Evaluate( wxString::FromUTF8( complexExpression ) );
 
  290    for( 
int i = 0; i < 1000; ++i )
 
  292        auto expr = 
"@{" + std::to_string( i ) + 
" * 2}";
 
 
  307        std::string expression;
 
  311        std::string description;
 
  314    const std::vector<TestCase> cases = {
 
  316        { 
"@{ 2 + 3 }", 
"5", 
false, 0.0, 
"Spaces in expression" },
 
  317        { 
"@{\t2\t+\t3\t}", 
"5", 
false, 0.0, 
"Tabs in expression" },
 
  318        { 
"@{\n2\n+\n3\n}", 
"5", 
false, 0.0, 
"Newlines in expression" },
 
  321        { 
"@{\"Hello\\\"World\\\"\"}", 
"Hello\"World\"", 
false, 0.0, 
"Escaped quotes in string" },
 
  322        { 
"@{\"Line1\\nLine2\"}", 
"Line1\nLine2", 
false, 0.0, 
"Newline in string" },
 
  325        { 
"A: @{1+1}, B: @{2*2}, C: @{3^2}", 
"A: 2, B: 4, C: 9", 
false, 0.0, 
"Multiple calculations" },
 
  328        { 
"@{((((2))))}", 
"2", 
false, 0.0, 
"Multiple nested parentheses" },
 
  329        { 
"@{(2 + 3) * (4 + 5)}", 
"45", 
false, 0.0, 
"Grouped operations" },
 
  332        { 
"No calculations here", 
"No calculations here", 
false, 0.0, 
"Plain text" },
 
  333        { 
"", 
"", 
false, 0.0, 
"Empty string" },
 
  334        { 
"@{0}", 
"0", 
false, 0.0, 
"Zero value" },
 
  335        { 
"@{-0}", 
"0", 
false, 0.0, 
"Negative zero" },
 
  338        { 
"@{0.1 + 0.2}", 
"0.3", 
false, 0.01, 
"Floating point precision" },
 
  339        { 
"@{1.0 / 3.0}", 
"0.333333", 
false, 0.01, 
"Repeating decimal" },
 
  342        { 
"@{1000000 * 1000000}", 
"1e+12", 
false, 0.01, 
"Large number result" },
 
  345        { 
"Good @{2+2} bad @{2+} good @{3+3}", 
"Good 4 bad @{2+} good 6", 
true, 0.0, 
"Error recovery" },
 
  348    for( 
const auto& testCase : cases )
 
  350        auto result = evaluator.
Evaluate( wxString::FromUTF8( testCase.expression ) );
 
  352        if( testCase.shouldError )
 
  354            BOOST_CHECK_MESSAGE( evaluator.
HasErrors(), 
"Expected error for: " + testCase.description );
 
  358            if( testCase.precision > 0.0 )
 
  361                std::regex numberRegex( R
"([\d.eE+-]+)" ); 
  363                std::string resultStr = result.ToStdString( wxConvUTF8 ); 
  364                if( std::regex_search( resultStr, match, numberRegex ) )
 
  366                    double actualValue = std::stod( match[0].str() );
 
  367                    double expectedValue = std::stod( testCase.expected );
 
  368                    BOOST_CHECK_CLOSE( actualValue, expectedValue, testCase.precision * 100 );
 
  373                BOOST_CHECK_MESSAGE( !evaluator.
HasErrors(),
 
  374                                    "Unexpected error for: " + testCase.description +
 
  376                BOOST_CHECK_MESSAGE( 
result.ToStdString( wxConvUTF8 ) == testCase.expected,
 
  377                                    "Expected '" + testCase.expected + 
"' but got '" +
 
  378                                    result.ToStdString( wxConvUTF8 ) + 
"' for: " + testCase.description );
 
 
  400    std::vector<std::string> expressions = {
 
  401        "Layer @{${board_layers}}/4",
 
  402        "Components: @{${component_count}}",
 
  403        "Nets: @{${net_count}}",
 
  404        "Vias: @{${via_count}}",
 
  405        "Area: @{${board_width} * ${board_height}} mm²",
 
  406        "Density: @{format(${component_count} / (${board_width} * ${board_height} / 100), 1)} /cm²",
 
  407        "Via density: @{format(${via_count} / (${board_width} * ${board_height} / 100), 1)} /cm²",
 
  408        "Layer utilization: @{format(${net_count} / ${board_layers}, 1)} nets/layer",
 
  409        "Design complexity: @{if(${component_count} > 100, \"High\", \"Low\")}",
 
  410        "Board aspect ratio: @{format(${board_width} / ${board_height}, 2)}:1",
 
  413    auto start = std::chrono::high_resolution_clock::now();
 
  416    for( 
int iteration = 0; iteration < 100; ++iteration )
 
  418        for( 
const auto& expr : expressions )
 
  422            BOOST_CHECK( !
result.empty() );
 
  426    auto end = std::chrono::high_resolution_clock::now();
 
  427    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>( 
end - start );
 
  430    BOOST_CHECK_LT( duration.count(), 100 );
 
  433    for( 
auto& expr : expressions )
 
  435        auto result1 = evaluator.
Evaluate( wxString::FromUTF8( expr ) );
 
  436        auto result2 = evaluator.
Evaluate( wxString::FromUTF8( expr ) );
 
 
High-level wrapper for evaluating mathematical and string expressions in wxString format.
 
wxString Evaluate(const wxString &aInput)
Main evaluation function - processes input string and evaluates all} expressions.
 
bool HasErrors() const
Check if the last evaluation had errors.
 
wxString GetErrorSummary() const
Get detailed error information from the last evaluation.
 
void SetVariable(const wxString &aName, double aValue)
Set a numeric variable for use in expressions.
 
auto MakeValue(T aVal) -> Result< T >
 
auto MakeError(std::string aMsg) -> Result< T >
 
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
 
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
 
BOOST_AUTO_TEST_SUITE_END()
 
VECTOR3I expected(15, 30, 45)
 
wxString result
Test unit parsing edge cases and error handling.
 
BOOST_CHECK_EQUAL(result, "25.4")
 
BOOST_AUTO_TEST_CASE(RealWorldScenarios)
Declare the test suite.