parquet_read_filter Submodule

Filter-expression parsing: turns one parquet_filter%add rule ("(ra > 180 and dec <= 0) or id is_null") into the packed leaf arrays plus the postfix (RPN) node list the C++ evaluator runs (see parquet_reader_set_filter in parquet_wrapper.cpp). Purely syntactic -- no schema access at all, so nothing here can tell whether a column exists or whether a value suits its type; that validation stays C++-side, where the schema is.

Parsing runs Fortran-side, rather than sending the raw text across the bind(C) boundary, for three reasons: a syntax error is reported through the same error-context helpers every other Fortran-side failure uses, the parser is unit-testable in process with no file and no reader (which is why the single-clause tokenizer has always been Fortran), and the boundary keeps carrying fixed-width packed strings, the convention tools/check_bindc_boundary.py already checks.

The grammar (precedence not > and > or; keywords case-insensitive):

expr     := or_expr
or_expr  := and_expr { or and_expr }
and_expr := not_expr { and not_expr }
not_expr := [ not ] not_expr | primary
primary  := '(' expr ')' | clause
clause   := NAME OP [ VALUE ]

A clause is exactly what it has always been, and is still tokenized by parquet_tokenize_filter_rule -- so every clause-level error message (and therefore every error scenario asserting one) is unchanged by the arrival of the expression layer.

The lexer reports token boundaries into the caller's own rule text rather than copying each token out, so lexing a rule costs three small integer arrays regardless of how long the rule is (a filter_max_rule_len-sized rule would otherwise need a token array of that length squared, in the tens of megabytes).


Uses