Skip to content

fix(scripting): accept double-quoted string literals in script expressions - #42

Merged
griswaldbrooks merged 2 commits into
main-picknikfrom
fm/btcpp-23851-double-quoted-strings
Oct 15, 2026
Merged

griswaldbrooks merged 2 commits into
main-picknikfrom
fm/btcpp-23851-double-quoted-strings

Conversation

@griswaldbrooks

Copy link
Copy Markdown

Intent

Fix moveit_pro issue https://github.com/PickNikRobotics/moveit_pro/issues/23851 in PickNik's BehaviorTree.CPP fork, so double-quoted string literals in BT Script and precondition expressions parse again and customer behavior trees keep working on upgrade to MoveIt Pro 10.2.
Context: MoveIt Pro 10.2.0-rc4 pins ros-jazzy-behaviortree-cpp-picknik 4.9.0 (10.1 shipped 4.7.2). Under 4.9.0, any expression with a double-quoted string fails at tree creation with Invalid token '"' at position N, for example viz_marker_name:="place_pose_"..viz_idx, mask_label:="mask "..index, tool_object_name!="", execute_trajectories == "true", header := "SAM3 prompt="..prompt. The same expressions with single quotes parse. The 4.7.2-line grammar accepted both quote styles (include/behaviortree_cpp/scripting/any_types.hpp, StringLiteral with single_quoted and quoted, around lines 129-133 at a109849). Eleven shipped example Objectives in moveit_pro_example_ws (lab_sim Pick All Pill Bottles, nine factory_sim entries, hangar_sim Calibrate SAM3 Mask Areas) fail this way.
Deliverable: a fork PR restoring double-quoted string literal support.

What Changed

  • The script tokenizer (src/script_tokenizer.cpp) treats " as a string-literal delimiter again, alongside '. The closing quote must match the opening one, so 'say "hello"' and "it's ready" both parse, and "abc' / 'abc" are still rejected as unterminated. This restores what the 4.7.2 grammar accepted: expressions like viz_marker_name:="place_pose_"..viz_idx and tool_object_name!="" no longer fail at tree creation with Invalid token '"'.
  • Adds three tests to tests/script_parser_test.cpp:
    • StringLiteralQuotes covers both quote styles: empty strings, a quote of the other style inside a literal, backslashes, newlines, concatenating a single-quoted literal with a double-quoted one, and unterminated or mismatched literals.
    • DoubleQuotedObjectiveExpressions runs the expressions reported from the MoveIt Pro example Objectives.
    • DoubleQuotedXmlScriptAndPreconditions builds a tree from XML that uses &quot; strings in <Script code>, _failureIf, and _successIf.
  • Removes stale lexy references from CLAUDE.md.

🤖 Generated with Claude Code

Risk Assessment

✅ Low: A 3-line change to the one shared tokenizer that every Script, ValidateScript and precondition path goes through. A bare " was always an Error token before, so no expression that parsed before can now parse differently. It also matches the 4.7.2 lexy grammar (single_quoted | quoted on ascii characters): the closing quote must match the opener, there is no escape processing, and newlines inside strings are allowed.

Testing

I built the base and fixed library versions inside the workspace and drove both through real BehaviorTreeFactory consumers, using the issue's expressions and the actual v10.2 moveit_pro_example_ws Objective XMLs. On base, all 7 distinct double-quoted shipped expressions and 19 Objective trees fail with the issue's exact Invalid token '"' error. With the fix, all 79 shipped expressions parse, all 178 Objective trees in lab_sim, factory_sim and hangar_sim create without error, and the expressions evaluate to the right values (place_pose_3, mask 2, SAM3 prompt=bottles threshold=0.5 returned 4 masks...). Single-quoted strings, quotes of the other kind nested inside a literal, and rejection of mismatched or unterminated quotes all still behave correctly. The new parser tests fail on base and pass with the fix, and the existing pre/postcondition tests pass. Real MoveIt Pro Behaviors were stand-in stubs, because the MoveIt Pro runtime is not available here. Script parsing itself always ran through the real library. This is a C++ library change with no UI, so there are no screenshots. Build outputs and the downloaded workspace snapshot have been removed from the worktree.

  • Live validation: ✅ go - 8 of 9 scenarios driven live against the product
Scenario Result Live Evidence
Loading a BT XML whose Script, ScriptCondition, Precondition and _skipIf/_successIf/onSuccess use the issue's double-quoted expressions (viz_marker_name:="place_pose"..viz_idx, mask_label:="mask "..… ✅ pass live bt_run_transcript.txt: base gives TREE CREATION FAILED: Invalid token &#39;&#34;&#39; at position 17. Fixed gives status SUCCESS with viz_marker_name="place_pose_3", mask_label="mask 2", header="SAM3 prompt=bot…
Double-quoted conditions evaluate correctly, not just parse: execute_trajectories=="false" fails the ScriptCondition, and an empty tool_object_name triggers the Precondition else=FAILURE ✅ pass live bt_run_transcript.txt: the fixed runs with execute_trajectories=false and tool_object_name='' both return FAILURE
All 79 script/precondition expressions in the moveit_pro_example_ws v10.2 Objectives parse with the fix (7 double-quoted ones fail on base) ✅ pass live example_ws_validate_summary.txt (base parsed=72 failed=7, fixed parsed=79 failed=0); example_ws_dq_tree_run.txt shows the evaluated values, e.g. line=" mask0: area=1234 px bbox=64x48"
Every Objective tree in lab_sim, factory_sim and hangar_sim (including Pick All Pill Bottles and Calibrate SAM3 Mask Areas) passes createTree with the fix; base fails 19 of them with Invalid token '"' ✅ pass live objectives_{lab,factory,hangar}sim{base,target}.txt: fixed created 91+46+41 trees with 0 failures. MoveIt Pro Behaviors were generated stand-ins; the Script and condition nodes were the real library…
Raw double quotes inside a single-quoted XML attribute (code='greeting := "hello " .. name') work ✅ pass live bt_run_transcript.txt: fixed gives greeting="hello world", and _failureIf=&#39;name == &#34;&#34;&#39; fails for an empty name
Mixed quote styles: 'single'.."double", a double quote inside single quotes, an apostrophe inside double quotes, an empty "", backslashes kept literally, delimiters inside a string, and 'abc'=="abc" a… ✅ pass live bt_run_transcript.txt mixed_quote_styles.xml: fixed gives SUCCESS with combo="singledouble", q1='say "hi"', q2="it's ready", path="C:\tmp\x". The shipped string1 with "moveit_pro shell" inside single…
Adversarial: a mismatched ("...') or unterminated (") string literal is still rejected at tree creation with an error ✅ pass live bt_run_transcript.txt: fixed gives Invalid token &#39;&#34;mask &#39;..index&#39; at position 9 and Error in the script &#34;tool_object_name != &#34;&#34; Invalid token &#39;&#34;&#39; at position 20, exit 2
Single-quoted string expressions that already worked on 4.9.0 still behave the same ✅ pass live bt_run_transcript.txt single_quoted_control.xml: SUCCESS with viz_marker_name="place_pose_7" on both base and fixed
The new regression tests fail before the fix and pass after; existing pre/postcondition script tests still pass ⏸️ untested no The prior payload recorded this scenario as live=false: it ran unit tests only (gtest-parser-base.txt, gtest-parser-fixed.txt, gtest-prepost-fixed.txt), not the live product, so it did not establish a…
Evidence: Consumer app transcript: issue expressions and adversarial XML trees, base vs fixed
################ base (f8a8fe7) ################
$ [base] bt_run issue_expressions.xml viz_idx=int:3 index=int:2 prompt=str:bottles tool_object_name=str:gripper execute_trajectories=str:true
TREE CREATION FAILED: Invalid token '"' at position 17
exit=2

$ [base] bt_run issue_expressions.xml viz_idx=int:3 index=int:2 prompt=str:bottles tool_object_name=str:gripper execute_trajectories=str:false
TREE CREATION FAILED: Invalid token '"' at position 17
exit=2

$ [base] bt_run issue_expressions.xml viz_idx=int:3 index=int:2 prompt=str:bottles tool_object_name=str: execute_trajectories=str:true
TREE CREATION FAILED: Invalid token '"' at position 17
exit=2

$ [base] bt_run raw_double_quotes_single_quoted_attr.xml name=str:world
TREE CREATION FAILED: Invalid token '"' at position 12
exit=2

$ [base] bt_run raw_double_quotes_single_quoted_attr.xml name=str:
TREE CREATION FAILED: Invalid token '"' at position 12
exit=2

$ [base] bt_run mixed_quote_styles.xml 
TREE CREATION FAILED: Invalid token '"' at position 19
exit=2

$ [base] bt_run bad_mismatched_quotes.xml index=int:1
TREE CREATION FAILED: Invalid token '"' at position 9
exit=2

$ [base] bt_run bad_unterminated_double_quote.xml tool_object_name=str:x
TREE CREATION FAILED: Error in the script "tool_object_name != ""
Invalid token '"' at position 20
exit=2

$ [base] bt_run single_quoted_control.xml viz_idx=int:7 execute_trajectories=str:true
status: SUCCESS
  execute_trajectories = "true"
  viz_marker_name = "place_pose_7"
  viz_idx = 7
exit=0

################ target (70b4988) ################
$ [target] bt_run issue_expressions.xml viz_idx=int:3 index=int:2 prompt=str:bottles tool_object_name=str:gripper execute_trajectories=str:true
status: SUCCESS
  header = "SAM3 prompt=bottles"
  picked = "yes"
  mask_label = "mask 2"
  post_ran = "done"
  tool_object_name = "gripper"
  prompt = "bottles"
  execute_trajectories = "true"
  index = 2
  viz_marker_name = "place_pose_3"
  viz_idx = 3
exit=0

$ [target] bt_run issue_expressions.xml viz_idx=int:3 index=int:2 prompt=str:bottles tool_object_name=str:gripper execute_trajectories=str:false
status: FAILURE
  header = "SAM3 prompt=bottles"
  mask_label = "mask 2"
  tool_object_name = "gripper"
  prompt = "bottles"
  execute_trajectories = "false"
  index = 2
  viz_marker_name = "place_pose_3"
  viz_idx = 3
exit=1

$ [target] bt_run issue_expressions.xml viz_idx=int:3 index=int:2 prompt=str:bottles tool_object_name=str: execute_trajectories=str:true
status: FAILURE
  header = "SAM3 prompt=bottles"
  mask_label = "mask 2"
  tool_object_name = ""
  prompt = "bottles"
  execute_trajectories = "true"
  index = 2
  viz_marker_name = "place_pose_3"
  viz_idx = 3
exit=1

$ [target] bt_run raw_double_quotes_single_quoted_attr.xml name=str:world
status: SUCCESS
  greeting = "hello world"
  name = "world"
exit=0

$ [target] bt_run raw_double_quotes_single_quoted_attr.xml name=str:
status: FAILURE
  greeting = "hello "
  name = ""
exit=1

$ [target] bt_run mixed_quote_styles.xml 
status: SUCCESS
  empty_dq = ""
  q2 = "it's ready"
  q1 = "say "hi""
  path = "C:\tmp\x"
  combo = "singledouble"
exit=0

$ [target] bt_run bad_mismatched_quotes.xml index=int:1
TREE CREATION FAILED: Invalid token '"mask '..index' at position 9
exit=2

$ [target] bt_run bad_unterminated_double_quote.xml tool_object_name=str:x
TREE CREATION FAILED: Error in the script "tool_object_name != ""
Invalid token '"' at position 20
exit=2

$ [target] bt_run single_quoted_control.xml viz_idx=int:7 execute_trajectories=str:true
status: SUCCESS
  execute_trajectories = "true"
  viz_marker_name = "place_pose_7"
  viz_idx = 7
exit=0
Evidence: Shipped example_ws v10.2 expressions: ParseScript base vs fixed summary

base: parsed=72 failed=7 (each: Invalid token '"' at position N, e.g. viz_marker_name:="place_pose_"..viz_idx -> position 17) fixed: parsed=79 failed=0

################ base ################
exit=1
FAIL  execute_trajectories == "true"
      -> Invalid token '"' at position 24
--
FAIL  header := "SAM3 prompt="..prompt.." threshold="..threshold.." returned "..mask_count.." masks. Cycling visualization..."
      -> Invalid token '"' at position 10
--
FAIL  line := " mask"..idx..": area="..area.." px bbox="..w.."x"..h
      -> Invalid token '"' at position 8
--
FAIL  mask_label := "area="..area.." px"
      -> Invalid token '"' at position 14
FAIL  mask_label:="mask "..index
      -> Invalid token '"' at position 12
--
FAIL  tool_object_name!=""
      -> Invalid token '"' at position 18
--
FAIL  viz_marker_name:="place_pose_"..viz_idx
      -> Invalid token '"' at position 17
parsed=72 failed=7
################ target ################
exit=0
parsed=79 failed=0
Evidence: Shipped double-quoted expressions executed in a tree (fixed build)
$ [base] bt_run example_ws_dq_expressions_tree.xml viz_idx=int:1 index=int:2 prompt=str:bottles threshold=str:0.5 mask_count=int:4 idx=int:0 area=int:1234 w=int:64 h=int:48 tool_object_name=str:hammer execute_trajectories=str:true
TREE CREATION FAILED: Invalid token '"' at position 24
exit=2

$ [target] bt_run example_ws_dq_expressions_tree.xml viz_idx=int:1 index=int:2 prompt=str:bottles threshold=str:0.5 mask_count=int:4 idx=int:0 area=int:1234 w=int:64 h=int:48 tool_object_name=str:hammer execute_trajectories=str:true
status: SUCCESS
  string1 = "Copy and run the following command in your "moveit_pro shell" terminal, then rerun this Objective to get the secret message."
  mask_label = "mask 2"
  viz_idx = 1
  index = 2
  viz_marker_name = "place_pose_1"
  mask_count = 4
  idx = 0
  execute_trajectories = "true"
  threshold = "0.5"
  w = 64
  area = 1234
  h = 48
  tool_object_name = "hammer"
  header = "SAM3 prompt=bottles threshold=0.5 returned 4 masks. Cycling visualization..."
  prompt = "bottles"
  line = " mask0: area=1234 px bbox=64x48"
exit=0
Evidence: Real Objective createTree results, fixed build (lab_sim)
OK    3 Waypoints Pick and Place
OK    3 Waypoints Pick and Place with AprilTags
OK    Add Point Cloud to Vector
OK    Add Poses to MTC Task
OK    Add Table to Planning Scene
OK    Add Waypoints to MTC Task
OK    AddBottlesToPlanningScene
OK    AddCollisionBoxInFrontOfEndEffector
OK    AddCollisionObjectToPlanningScene
OK    Clear Previous Obstacles
OK    Close Gripper
OK    Collect Training Data with Behaviors
OK    ComputeLinkPoseFromWaypoint
OK    Constrained Pick and Place Subtree
OK    Create PCL Poses Vector
OK    Create Pose Vector
OK    Cycle Between Waypoints
OK    Detect AprilTags on Table
OK    ErrorCheckVectorSize
OK    Fan Pose About Tool-Z Subtree
OK    Fit Bottle Model Subtree
OK    Force Relaxation
OK    Fuse Multiple Views Subtree
OK    Get AprilTag Pose from Image
OK    Get Bottle Grasp via ICP Subtree
OK    Get Collision Free Grasp Subtree
OK    Get Fresh Point Cloud
OK    Get Transform Frame Pose
OK    GetPoseAtGripperandDisplay
OK    Grasp Planning
OK    Grasp Pose Tuning With April Tag
OK    Grasp Pose Using Yaml
OK    Joint Diagnostic
OK    Live Surface Coverage Demo
OK    Load MuJoCo Robot State
OK    Load and Execute Joint Trajectory
OK    LoadAndVisualizePointCloud
OK    Look at Table
OK    ML Find Bottles on Table from Image Exemplar
OK    ML Segment Bottles from File
OK    ML Segment Image
OK    ML Segment Image Loop
OK    ML Segment Point Cloud
OK    ML Segment Point Cloud from Clicked Point
OK    MPC Pose Tracking
OK    MPC Pose Tracking Dynamic Point Cloud Avoidance with Sphere Down Sample
OK    MPC Pose Tracking Static Point Cloud Avoidance with Sphere Down Sample
OK    MPC Pose Tracking With Point Cloud Avoidance
OK    Move Along Square
OK    Move Flasks to Burners
OK    Octomap Example
OK    Open Gripper
OK    Pick 1 Pill Bottle with SAM3
OK    Pick All Bottles with AprilTags
OK    Pick All Pill Bottles
OK    Pick April Tag Labeled Object
OK    Pick April Tag Labeled Object With Approval
OK    Pick from AprilTag Detections
OK    Pick from Pose
OK    Pick from Pose Vector
OK    Pick from Pose With Approval
OK    Pick object
OK    Pick up Object
OK    Place Object
OK    Place at Pose
OK    Place at Pose Vector
OK    Place at Pose with Approval
OK    Plan Move To Pose
OK    Plan and Save Trajectory
OK    Playback Square Trajectory
OK    Push Button
OK    Push Button With a Trajectory
OK    Record Square Trajectory
OK    Record Surface Coverage Demo
OK    Record and Replay Scanning Motion
OK    Reflect Poses Subtree
OK    Register CAD Part
OK    Register CAD Part Subtree
OK    Reset MuJoCo Robot
OK    Save Robot Joint State
OK    Scan Scene - Multiple Point Clouds
OK    Segment Bottle Subtree
OK    Select Region and Record Coverage
OK    Select Region for Coverage Path
OK    Stationary Admittance
OK    Stitch Multiple Point Clouds Together
OK    Take Scene Camera Snapshot
OK    Take Wrist Camera Image
OK    Take Wrist Camera Snapshot
OK    Vector and String Example
OK    Visualize Grasp Link
created=91 failed=0
Evidence: Real Objective createTree results, fixed build (factory_sim)
OK    Activate Vacuum
OK    Add Bin Rim
OK    Add Bins to Planning Scene
OK    Automask Camera Iterate Masks
OK    Automask from Camera
OK    Automask from File
OK    Automated Pickup Tool Subtree
OK    Automated Return Held Tool
OK    Automated Return Tool Subtree
OK    Automated Tool Changing Example
OK    Automated Tool Changing Example Subtree
OK    Deactivate Vacuum
OK    Detach and Remove Tool
OK    Drop Bracket Part in Right Bin Subtree
OK    Drop Bracket Part on Jig Subtree
OK    Indicate Jig Empty
OK    Indicate Jig Working
OK    Indicate Part Ready
OK    Inspect Convex Bowl
OK    Load Bin Configuration
OK    Load Bracket Part Pointcloud Subtree
OK    Look for Bracket Part Subtree
OK    Pick Bracket Part from Jig Subtree
OK    Pick Brackets from Left Bin
OK    Pick Up Tool from Holder
OK    Pick and Place Brackets from Left Bin
OK    Place Tool in Tool Holder
OK    Plan And Execute With Orientation Samples
OK    Plan And Execute With Position Only
OK    Plan Path Along Surface
OK    PlanAndExecuteWithPoseOrientation
OK    Reachability Analysis - blocks
OK    Reachability Analysis - bowls
OK    Reachability Analysis for mesh
OK    Reachability Analysis for plane
OK    Remove Tools from PlanningScene
OK    Reset Planning Scene
OK    Retract
OK    Setup Initial Planning Scene
OK    Take Snapshot
OK    Tool Attachment Example
OK    Vacuum Drop Bracket Part Subtree
OK    Vacuum Pick Bracket Part Subtree
OK    Validate Bin Pose Values
OK    Visualize Bins and Jig
OK    Visualize Bins and Jig Subtree
created=46 failed=0
Evidence: Real Objective createTree results, fixed build (hangar_sim)
OK    Calibrate SAM3 Mask Areas
OK    Cartesian Draw Geometry From File
OK    Cartesian Path with Collision Checking
OK    Cartesian Plan Simple Square
OK    Close Gripper
OK    Compute IK, Plan and Move
OK    Find and Spray Plane
OK    Generate Grid Pattern on Airplane
OK    Get Point Cloud Center Pose
OK    Localize Robot
OK    Localize Robot If Needed
OK    ML Move Boxes to Loading Zone
OK    Move Boxes Looping
OK    Move Boxes to Loading Zone Start from Waypoint
OK    Move to Arm Upright
OK    Move to Look at Plane
OK    Move to Pose No Preview
OK    Move to a StampedPose
OK    Navigate to Clicked Point
OK    Navigate to Clicked Point with Replanning
OK    Open Gripper
OK    Plan Path Along Surface
OK    Plan Path Along Surface - Loop
OK    Plan Path Along Surface 3 Passes
OK    Point-to-Point Trajectory
OK    Raster Path Along Fuselage
OK    Refine Localization In Place
OK    Refine Localization In Place Subtree
OK    Request Teleoperation
OK    Segment Image from Point
OK    Segment Image from Text Prompt
OK    Segment Point Cloud from Clicked Point
OK    Solution - Draw Picknik
OK    Solution - Draw Square
OK    Solution - Find and Spray Plane
OK    Solution - Generate coverage path
OK    Solution - Move Forward 2m
OK    Solution - Spray Plane
OK    Take Scene Camera Snapshot
OK    Take Wrist Camera Snapshot
OK    Wait for Trajectory Approval if User Available
created=41 failed=0
Evidence: Real Objective createTree results, base build (factory_sim: 17 Invalid token failures)
OK    Activate Vacuum
OK    Add Bin Rim
OK    Add Bins to Planning Scene
FAIL  Automask Camera Iterate Masks  ->  Invalid token '"' at position 12
OK    Automask from Camera
OK    Automask from File
OK    Automated Pickup Tool Subtree
FAIL  Automated Return Held Tool  ->  Invalid token '"' at position 18
FAIL  Automated Return Tool Subtree  ->  Invalid token '"' at position 18
FAIL  Automated Tool Changing Example  ->  Invalid token '"' at position 18
FAIL  Automated Tool Changing Example Subtree  ->  Invalid token '"' at position 18
OK    Deactivate Vacuum
OK    Detach and Remove Tool
OK    Drop Bracket Part in Right Bin Subtree
OK    Drop Bracket Part on Jig Subtree
OK    Indicate Jig Empty
OK    Indicate Jig Working
OK    Indicate Part Ready
FAIL  Inspect Convex Bowl  ->  Invalid token '"' at position 18
OK    Load Bin Configuration
OK    Load Bracket Part Pointcloud Subtree
OK    Look for Bracket Part Subtree
OK    Pick Bracket Part from Jig Subtree
FAIL  Pick Brackets from Left Bin  ->  Invalid token '"' at position 18
OK    Pick Up Tool from Holder
FAIL  Pick and Place Brackets from Left Bin  ->  Invalid token '"' at position 18
FAIL  Place Tool in Tool Holder  ->  Invalid token '"' at position 18
FAIL  Plan And Execute With Orientation Samples  ->  Invalid token '"' at position 24
FAIL  Plan And Execute With Position Only  ->  Invalid token '"' at position 24
OK    Plan Path Along Surface
FAIL  PlanAndExecuteWithPoseOrientation  ->  Invalid token '"' at position 24
FAIL  Reachability Analysis - blocks  ->  Invalid token '"' at position 24
FAIL  Reachability Analysis - bowls  ->  Invalid token '"' at position 24
FAIL  Reachability Analysis for mesh  ->  Invalid token '"' at position 24
FAIL  Reachability Analysis for plane  ->  Invalid token '"' at position 24
OK    Remove Tools from PlanningScene
OK    Reset Planning Scene
OK    Retract
OK    Setup Initial Planning Scene
OK    Take Snapshot
FAIL  Tool Attachment Example  ->  Invalid token '"' at position 18
OK    Vacuum Drop Bracket Part Subtree
OK    Vacuum Pick Bracket Part Subtree
OK    Validate Bin Pose Values
OK    Visualize Bins and Jig
OK    Visualize Bins and Jig Subtree
created=29 failed=17
Evidence: Objective createTree summary, base vs fixed
lab_sim base created=90 failed=1 fixed created=91 failed=0
factory_sim base created=29 failed=17 fixed created=46 failed=0
hangar_sim base created=40 failed=1 fixed created=41 failed=0
Every base failure: Invalid token '"' at position N (e.g. Pick All Pill Bottles -> position 17, Calibrate SAM3 Mask Areas -> position 10)
Evidence: ParserTest results on base (3 new tests fail)
Running main() from ~/.no-mistakes/worktrees/db21b7c392ba/01M50FZ038TKQZCP30Y8FYFE7E/build-nm-gtest/src/googletest/src/gtest_main.cc
Note: Google Test filter = ParserTest.*
[==========] Running 21 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 21 tests from ParserTest
[ RUN      ] ParserTest.AnyTypes
[       OK ] ParserTest.AnyTypes (0 ms)
[ RUN      ] ParserTest.StringLiteralQuotes
tests/script_parser_test.cpp:113: Failure
Value of: result.has_value()
  Actual: false
Expected: true
Google Test trace:
tests/script_parser_test.cpp:111: "hello world "

[  FAILED  ] ParserTest.StringLiteralQuotes (0 ms)
[ RUN      ] ParserTest.DoubleQuotedObjectiveExpressions
unknown file: Failure
C++ exception with description "Invalid token '"' at position 17" thrown in the test body.

[  FAILED  ] ParserTest.DoubleQuotedObjectiveExpressions (0 ms)
[ RUN      ] ParserTest.DoubleQuotedXmlScriptAndPreconditions
unknown file: Failure
C++ exception with description "Invalid token '"' at position 17" thrown in the test body.

[  FAILED  ] ParserTest.DoubleQuotedXmlScriptAndPreconditions (0 ms)
[ RUN      ] ParserTest.AnyTypes_Failing
[       OK ] ParserTest.AnyTypes_Failing (0 ms)
[ RUN      ] ParserTest.Equations
[       OK ] ParserTest.Equations (0 ms)
[ RUN      ] ParserTest.NotInitializedComparison
[       OK ] ParserTest.NotInitializedComparison (0 ms)
[ RUN      ] ParserTest.EnumsBasic
[       OK ] ParserTest.EnumsBasic (0 ms)
[ RUN      ] ParserTest.EnumsXML
[       OK ] ParserTest.EnumsXML (0 ms)
[ RUN      ] ParserTest.Enums_Issue_523
Robot says: SECOND low batteries!
[       OK ] ParserTest.Enums_Issue_523 (0 ms)
[ RUN      ] ParserTest.Issue595
[       OK ] ParserTest.Issue595 (0 ms)
[ RUN      ] ParserTest.OperatorAssociativity_Issue1029
[       OK ] ParserTest.OperatorAssociativity_Issue1029 (0 ms)
[ RUN      ] ParserTest.ValidateScriptLargeError_Issue923
[       OK ] ParserTest.ValidateScriptLargeError_Issue923 (0 ms)
[ RUN      ] ParserTest.CompareWithNegativeNumber_Issue832
[       OK ] ParserTest.CompareWithNegativeNumber_Issue832 (0 ms)
[ RUN      ] ParserTest.NewLine
[       OK ] ParserTest.NewLine (0 ms)
[ RUN      ] ParserTest.TokenizerEdgeCases
[       OK ] ParserTest.TokenizerEdgeCases (0 ms)
[ RUN      ] ParserTest.ChainedComparisons
[       OK ] ParserTest.ChainedComparisons (0 ms)
[ RUN      ] ParserTest.OperatorPrecedence
[       OK ] ParserTest.OperatorPrecedence (0 ms)
[ RUN      ] ParserTest.UnaryOperators
[       OK ] ParserTest.UnaryOperators (0 ms)
[ RUN      ] ParserTest.TernaryExpressions
[       OK ] ParserTest.TernaryExpressions (0 ms)
[ RUN      ] ParserTest.MultipleStatements
[       OK ] ParserTest.MultipleStatements (0 ms)
[----------] 21 tests from ParserTest (1 ms total)

[----------] Global test environment tear-down
[==========] 21 tests from 1 test suite ran. (1 ms total)
[  PASSED  ] 18 tests.
[  FAILED  ] 3 tests, listed below:
[  FAILED  ] ParserTest.StringLiteralQuotes
[  FAILED  ] ParserTest.DoubleQuotedObjectiveExpressions
[  FAILED  ] ParserTest.DoubleQuotedXmlScriptAndPreconditions

 3 FAILED TESTS
Evidence: ParserTest results on fixed build (all pass)
Running main() from ~/.no-mistakes/worktrees/db21b7c392ba/01M50FZ038TKQZCP30Y8FYFE7E/build-nm-gtest/src/googletest/src/gtest_main.cc
Note: Google Test filter = ParserTest.*
[==========] Running 21 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 21 tests from ParserTest
[ RUN      ] ParserTest.AnyTypes
[       OK ] ParserTest.AnyTypes (0 ms)
[ RUN      ] ParserTest.StringLiteralQuotes
[       OK ] ParserTest.StringLiteralQuotes (0 ms)
[ RUN      ] ParserTest.DoubleQuotedObjectiveExpressions
[       OK ] ParserTest.DoubleQuotedObjectiveExpressions (0 ms)
[ RUN      ] ParserTest.DoubleQuotedXmlScriptAndPreconditions
[       OK ] ParserTest.DoubleQuotedXmlScriptAndPreconditions (0 ms)
[ RUN      ] ParserTest.AnyTypes_Failing
[       OK ] ParserTest.AnyTypes_Failing (0 ms)
[ RUN      ] ParserTest.Equations
[       OK ] ParserTest.Equations (0 ms)
[ RUN      ] ParserTest.NotInitializedComparison
[       OK ] ParserTest.NotInitializedComparison (0 ms)
[ RUN      ] ParserTest.EnumsBasic
[       OK ] ParserTest.EnumsBasic (0 ms)
[ RUN      ] ParserTest.EnumsXML
[       OK ] ParserTest.EnumsXML (0 ms)
[ RUN      ] ParserTest.Enums_Issue_523
Robot says: SECOND low batteries!
[       OK ] ParserTest.Enums_Issue_523 (0 ms)
[ RUN      ] ParserTest.Issue595
[       OK ] ParserTest.Issue595 (0 ms)
[ RUN      ] ParserTest.OperatorAssociativity_Issue1029
[       OK ] ParserTest.OperatorAssociativity_Issue1029 (0 ms)
[ RUN      ] ParserTest.ValidateScriptLargeError_Issue923
[       OK ] ParserTest.ValidateScriptLargeError_Issue923 (0 ms)
[ RUN      ] ParserTest.CompareWithNegativeNumber_Issue832
[       OK ] ParserTest.CompareWithNegativeNumber_Issue832 (0 ms)
[ RUN      ] ParserTest.NewLine
[       OK ] ParserTest.NewLine (0 ms)
[ RUN      ] ParserTest.TokenizerEdgeCases
[       OK ] ParserTest.TokenizerEdgeCases (0 ms)
[ RUN      ] ParserTest.ChainedComparisons
[       OK ] ParserTest.ChainedComparisons (0 ms)
[ RUN      ] ParserTest.OperatorPrecedence
[       OK ] ParserTest.OperatorPrecedence (0 ms)
[ RUN      ] ParserTest.UnaryOperators
[       OK ] ParserTest.UnaryOperators (0 ms)
[ RUN      ] ParserTest.TernaryExpressions
[       OK ] ParserTest.TernaryExpressions (0 ms)
[ RUN      ] ParserTest.MultipleStatements
[       OK ] ParserTest.MultipleStatements (0 ms)
[----------] 21 tests from ParserTest (1 ms total)

[----------] Global test environment tear-down
[==========] 21 tests from 1 test suite ran. (1 ms total)
[  PASSED  ] 21 tests.
  • Evidence: Harness sources and XML fixtures used (local file: ~/.no-mistakes/evidence/01M50FZ038TKQZCP30Y8FYFE7E/harness)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 8 of 9 scenarios driven live against the product
Scenario Result Live Evidence
Loading a BT XML whose Script, ScriptCondition, Precondition and _skipIf/_successIf/onSuccess use the issue's double-quoted expressions (viz_marker_name:="place_pose"..viz_idx, mask_label:="mask "..… ✅ pass live bt_run_transcript.txt: base gives TREE CREATION FAILED: Invalid token &#39;&#34;&#39; at position 17. Fixed gives status SUCCESS with viz_marker_name="place_pose_3", mask_label="mask 2", header="SAM3 prompt=bot…
Double-quoted conditions evaluate correctly, not just parse: execute_trajectories=="false" fails the ScriptCondition, and an empty tool_object_name triggers the Precondition else=FAILURE ✅ pass live bt_run_transcript.txt: the fixed runs with execute_trajectories=false and tool_object_name='' both return FAILURE
All 79 script/precondition expressions in the moveit_pro_example_ws v10.2 Objectives parse with the fix (7 double-quoted ones fail on base) ✅ pass live example_ws_validate_summary.txt (base parsed=72 failed=7, fixed parsed=79 failed=0); example_ws_dq_tree_run.txt shows the evaluated values, e.g. line=" mask0: area=1234 px bbox=64x48"
Every Objective tree in lab_sim, factory_sim and hangar_sim (including Pick All Pill Bottles and Calibrate SAM3 Mask Areas) passes createTree with the fix; base fails 19 of them with Invalid token '"' ✅ pass live objectives_{lab,factory,hangar}sim{base,target}.txt: fixed created 91+46+41 trees with 0 failures. MoveIt Pro Behaviors were generated stand-ins; the Script and condition nodes were the real library…
Raw double quotes inside a single-quoted XML attribute (code='greeting := "hello " .. name') work ✅ pass live bt_run_transcript.txt: fixed gives greeting="hello world", and _failureIf=&#39;name == &#34;&#34;&#39; fails for an empty name
Mixed quote styles: 'single'.."double", a double quote inside single quotes, an apostrophe inside double quotes, an empty "", backslashes kept literally, delimiters inside a string, and 'abc'=="abc" a… ✅ pass live bt_run_transcript.txt mixed_quote_styles.xml: fixed gives SUCCESS with combo="singledouble", q1='say "hi"', q2="it's ready", path="C:\tmp\x". The shipped string1 with "moveit_pro shell" inside single…
Adversarial: a mismatched ("...') or unterminated (") string literal is still rejected at tree creation with an error ✅ pass live bt_run_transcript.txt: fixed gives Invalid token &#39;&#34;mask &#39;..index&#39; at position 9 and Error in the script &#34;tool_object_name != &#34;&#34; Invalid token &#39;&#34;&#39; at position 20, exit 2
Single-quoted string expressions that already worked on 4.9.0 still behave the same ✅ pass live bt_run_transcript.txt single_quoted_control.xml: SUCCESS with viz_marker_name="place_pose_7" on both base and fixed
The new regression tests fail before the fix and pass after; existing pre/postcondition script tests still pass ⏸️ untested no The prior payload recorded this scenario as live=false: it ran unit tests only (gtest-parser-base.txt, gtest-parser-fixed.txt, gtest-prepost-fixed.txt), not the live product, so it did not establish a…
  • Built libbehaviortree_cpp_picknik.so.4.9.0 at the base (f8a8fe7) and fixed (70b4988) commits, plus googletest v1.14 built inside the workspace. GTest, SQLite and ZeroMQ are not installed on the host.
  • script_parser_test --gtest_filter=&#39;ParserTest.*&#39; compiled against the fixed lib (21/21 pass) and against the base lib (the 3 new tests StringLiteralQuotes, DoubleQuotedObjectiveExpressions and DoubleQuotedXmlScriptAndPreconditions fail, which reproduces the bug)
  • bt_run &lt;tree.xml&gt; key=type:value...: a consumer app that loads an XML file with BehaviorTreeFactory::createTreeFromFile, ticks it and prints the blackboard. Ran 9 cases on base and fixed: the issue's expressions across Script, ScriptCondition, Precondition if, _skipIf, _onSuccess and _successIf; raw &#34; inside a single-quoted XML attribute; mixed quote styles; mismatched and unterminated quotes; a single-quoted control case
  • bt_validate (BT::ParseScript) on all 79 unique script/precondition expressions pulled from moveit_pro_example_ws v10.2 Objective XMLs (commit f11fc2a), base vs fixed
  • bt_run example_ws_dq_expressions_tree.xml: ran the shipped double-quoted expressions with seeded values and checked the resulting strings
  • bt_load_objectives: registered every Objective XML in the lab_sim, factory_sim and hangar_sim objectives/ dirs, with generated stand-in nodes for MoveIt Pro Behaviors (built-in Script, ScriptCondition and Precondition nodes stay real), then called createTree() on every Objective, base vs fixed
  • gtest_preconditions.cpp + gtest_postconditions.cpp compiled against the fixed lib (16/16 pass), as a regression check of other script-condition parsing
✅ **Document** - passed

✅ No issues found.

⚠️ **Lint** - 1 info
  • ℹ️ src/script_tokenizer.cpp - The clang-tidy pre-commit hook skipped itself because clangd-21 is not installed and there is no build/compile_commands.json, so the changed files (src/script_tokenizer.cpp, tests/script_parser_test.cpp) got no static analysis. clang-format, codespell and the standard hooks all passed. To cover this, run ./run_clang_tidy.sh in an environment that has clang-tidy-21 and a configured build.
✅ **Push** - passed

✅ No issues found.

The 4.9 tokenizer only recognized single quotes, so existing Script and
precondition expressions failed on their opening double quote. Recognize
both delimiters and require the closing quote to match the opening one.

Cover the reported Objective expressions, quote parity and malformed
strings, and XML Script/precondition attributes using &quot;.

Refs PickNikRobotics/moveit_pro#23851
@coderabbitai

coderabbitai Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Team
  • Run ID: d3752d8e-6556-49a6-8654-523318cb29bd
📥 Commits

Reviewing files that changed from the base of the PR and between f8a8fe7 and 4be6540.

📒 Files selected for processing: 3
  • CLAUDE.md
  • src/script_tokenizer.cpp
  • tests/script_parser_test.cpp

Included review availability: This review used your included allowance. 3 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 4 reviews per hour.

📝 Summary

Summary by CodeRabbit

  • New Features
    • Script expressions now support both single- and double-quoted string literals, including empty strings, multiline content, and concatenation across adjacent quoted segments.
    • Double-quoted strings can be used in assignments, comparisons, and behavior-tree preconditions.
  • Bug Fixes
    • Unterminated or mismatched quoted strings are rejected during parsing.

The tokenizer now recognizes single- and double-quoted string literals. New tests cover literal parsing, expressions, and XML behavior-tree scripts that use double-quoted strings.

Changes: Script string literals

Layer / File(s) Summary
Match string literal quote delimiters
src/script_tokenizer.cpp, CLAUDE.md
The tokenizer accepts either quote character and uses the matching character as the closing delimiter. CLAUDE.md no longer describes the scripting parser as lexy-based or lists lexy as a vendored dependency.
Validate literals and expression behavior
tests/script_parser_test.cpp
Tests cover both quote styles, literal edge cases, concatenation, assignments, comparisons, and XML behavior-tree scripts and preconditions using double-quoted strings.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to 4be65

No merge-blocking issue is identified. Complete the normal test checks before merging.

Pre-merge checks: Passed 4
Check name Status Explanation
Description check Passed The pull request description is complete and relevant. It explains the issue, intended fix, code changes, regression tests, risk, validation results, and known limitations. It also addresses the repos…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Human Review Check Passed PASS: The PR is low-risk under the stated criteria. The authoritative diff changes only CLAUDE.md, the shared script tokenizer, and parser tests. The code change is limited to matching single- or doub…
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

⚠️ This pull request shows signs of AI-generated slop (ai_padded_prose). It has been flagged by CodeRabbit slop detection and should be reviewed carefully.

Comment @coderabbitai help to get the list of available commands.

✨ Finishing Touches: 💡 1
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR

@griswaldbrooks
griswaldbrooks added this pull request to the merge queue Oct 15, 2026
Merged via the queue into main-picknik with commit 1cc7638 Oct 15, 2026
15 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants