@@ -522,3 +522,132 @@ TEST(ParserTest, NewLine)
522522 ASSERT_EQ (tree.rootBlackboard ()->get <int >(" A" ), 5 );
523523 ASSERT_EQ (tree.rootBlackboard ()->get <int >(" B" ), 6 );
524524}
525+
526+ TEST (ParserTest, TokenizerEdgeCases)
527+ {
528+ // Unterminated string
529+ EXPECT_FALSE (BT::ValidateScript (" 'hello" ));
530+
531+ // Hex edge cases
532+ EXPECT_FALSE (BT::ValidateScript (" 0x" ));
533+ EXPECT_FALSE (BT::ValidateScript (" 0xG" ));
534+
535+ // Exponent without digits
536+ EXPECT_FALSE (BT::ValidateScript (" 3e" ));
537+ EXPECT_FALSE (BT::ValidateScript (" 3e+" ));
538+
539+ // DotDot adjacent to integer: "65..66" should parse as 65 .. 66
540+ BT::Ast::Environment env = { BT::Blackboard::create (), {} };
541+ auto result = BT::ParseScriptAndExecute (env, " A:='65'; B:='66'; A..B" );
542+ EXPECT_TRUE (result.has_value ());
543+ EXPECT_EQ (result.value ().cast <std::string>(), " 6566" );
544+
545+ // Empty and whitespace-only scripts
546+ EXPECT_FALSE (BT::ValidateScript (" " ));
547+ EXPECT_FALSE (BT::ValidateScript (" " ));
548+ EXPECT_FALSE (BT::ValidateScript (" \t\n\r " ));
549+ }
550+
551+ TEST (ParserTest, ChainedComparisons)
552+ {
553+ BT::Ast::Environment env = { BT::Blackboard::create (), {} };
554+ auto Parse = [&env](const char * str) { return BT::ParseScriptAndExecute (env, str); };
555+
556+ // 1 < 2 < 3 should be true (chained: 1<2 AND 2<3)
557+ EXPECT_EQ (Parse (" 1 < 2 < 3" ).value ().cast <int >(), 1 );
558+
559+ // 3 > 2 > 1 should be true
560+ EXPECT_EQ (Parse (" 3 > 2 > 1" ).value ().cast <int >(), 1 );
561+
562+ // 1 < 2 > 3 should be false (1<2 is true, but 2>3 is false)
563+ EXPECT_EQ (Parse (" 1 < 2 > 3" ).value ().cast <int >(), 0 );
564+
565+ // Chained equality
566+ EXPECT_EQ (Parse (" 5 == 5 == 5" ).value ().cast <int >(), 1 );
567+ EXPECT_EQ (Parse (" 5 == 5 != 3" ).value ().cast <int >(), 1 );
568+
569+ // 1 <= 2 <= 3
570+ EXPECT_EQ (Parse (" 1 <= 2 <= 3" ).value ().cast <int >(), 1 );
571+
572+ // 3 >= 2 >= 1
573+ EXPECT_EQ (Parse (" 3 >= 2 >= 1" ).value ().cast <int >(), 1 );
574+ }
575+
576+ TEST (ParserTest, OperatorPrecedence)
577+ {
578+ BT::Ast::Environment env = { BT::Blackboard::create (), {} };
579+ auto Parse = [&env](const char * str) { return BT::ParseScriptAndExecute (env, str); };
580+
581+ // Bitwise AND binds tighter than bitwise OR
582+ // 6 | 3 & 5 should be 6 | (3 & 5) = 6 | 1 = 7
583+ EXPECT_EQ (Parse (" 6 | 3 & 5" ).value ().cast <int >(), 7 );
584+
585+ // Bitwise OR binds tighter than logical AND
586+ // true && (6 | 0) should be true
587+ EXPECT_EQ (Parse (" true && (6 | 0)" ).value ().cast <int >(), 1 );
588+
589+ // Logical AND binds tighter than logical OR
590+ // false || true && true should be false || (true && true) = true
591+ EXPECT_EQ (Parse (" false || true && true" ).value ().cast <int >(), 1 );
592+
593+ // false && true || true should be (false && true) || true = true
594+ EXPECT_EQ (Parse (" false && true || true" ).value ().cast <int >(), 1 );
595+
596+ // Parentheses override precedence
597+ EXPECT_EQ (Parse (" (2 + 3) * 4" ).value ().cast <double >(), 20.0 );
598+ EXPECT_EQ (Parse (" 2 * (3 + 4)" ).value ().cast <double >(), 14.0 );
599+ }
600+
601+ TEST (ParserTest, UnaryOperators)
602+ {
603+ BT::Ast::Environment env = { BT::Blackboard::create (), {} };
604+ auto Parse = [&env](const char * str) { return BT::ParseScriptAndExecute (env, str); };
605+
606+ // Logical NOT
607+ EXPECT_EQ (Parse (" !true" ).value ().cast <int >(), 0 );
608+ EXPECT_EQ (Parse (" !false" ).value ().cast <int >(), 1 );
609+ EXPECT_EQ (Parse (" !!true" ).value ().cast <int >(), 1 );
610+
611+ // Bitwise complement
612+ auto result = Parse (" ~0" );
613+ EXPECT_TRUE (result.has_value ());
614+ EXPECT_EQ (result.value ().cast <int64_t >(), ~int64_t (0 ));
615+
616+ // Unary minus
617+ EXPECT_EQ (Parse (" -(3 + 2)" ).value ().cast <double >(), -5.0 );
618+
619+ // Unary minus in expressions
620+ EXPECT_EQ (Parse (" 10 + -3" ).value ().cast <double >(), 7.0 );
621+ }
622+
623+ TEST (ParserTest, TernaryExpressions)
624+ {
625+ BT::Ast::Environment env = { BT::Blackboard::create (), {} };
626+ auto Parse = [&env](const char * str) { return BT::ParseScriptAndExecute (env, str); };
627+
628+ EXPECT_EQ (Parse (" true ? 1 : 2" ).value ().cast <int >(), 1 );
629+ EXPECT_EQ (Parse (" false ? 1 : 2" ).value ().cast <int >(), 2 );
630+
631+ // Ternary with expressions in branches
632+ EXPECT_EQ (Parse (" true ? 2 + 3 : 10" ).value ().cast <double >(), 5.0 );
633+ EXPECT_EQ (Parse (" false ? 10 : 2 + 3" ).value ().cast <double >(), 5.0 );
634+
635+ // Ternary with comparison as condition
636+ EXPECT_EQ (Parse (" 3 > 2 ? 'yes' : 'no'" ).value ().cast <std::string>(), " yes" );
637+ EXPECT_EQ (Parse (" 3 < 2 ? 'yes' : 'no'" ).value ().cast <std::string>(), " no" );
638+ }
639+
640+ TEST (ParserTest, MultipleStatements)
641+ {
642+ BT::Ast::Environment env = { BT::Blackboard::create (), {} };
643+ auto Parse = [&env](const char * str) { return BT::ParseScriptAndExecute (env, str); };
644+
645+ // Multiple semicolons
646+ Parse (" a:=1;;; b:=2;;" );
647+ EXPECT_EQ (env.vars ->get <double >(" a" ), 1.0 );
648+ EXPECT_EQ (env.vars ->get <double >(" b" ), 2.0 );
649+
650+ // Last expression is the return value
651+ auto result = Parse (" a:=10; b:=20; a+b" );
652+ EXPECT_EQ (result.value ().cast <double >(), 30.0 );
653+ }
0 commit comments