Looking a bit deeper into the details I'm thinking you should probably generate the DEVPROP_FILTER_EXPRESSION directly while parsing, instead of using a separate IR.
You could have:
``` struct aqs_expr { DEVPROP_FILTER_EXPRESSION *filters; UINT filters_len; }; ```
And get rid of pretty much all the accessory stuff. `get_boolean_expr` would allocate a 1 filter expression, `get_compare_expr` would reallocate lhs filters to grow and append rhs, with the open/close ops around.