Module implementing a full C-preprocessor-style constant expression evaluator using a top-down recursive descent parser. The module provides the ability to evaluate integer constant expressions of the kind used in classical preprocessor.
This includes support for:
The implementation consists of two major phases:
Parsing and evaluation via top-down recursive descent A classic predictive (LL(1)) recursive descent parser is used, where each non-terminal in the grammar is implemented as a separate parsing function with the exact precedence level. The grammar is directly derived from the C standard operator precedence table:
parse_expression ? parse_conditional parse_conditional ? parse_or (parse_or '?' parse_expression ':' parse_conditional) parse_or ? parse_and ( '||' parse_and )* parse_and ? parse_bitwise_or ( '&&' parse_bitwise_or )* parse_bitwise_or ? parse_bitwise_xor ( '|' parse_bitwise_xor )* parse_bitwise_xor ? parse_bitwise_and ( '^' parse_bitwise_and )* parse_bitwise_and ? parse_equality ( '&' parse_equality )* parse_equality ? parse_relational ( ('==' | '!=') parse_relational )* parse_relational ? parse_shifting ( ('<' | '>' | '<=' | '>=') parse_shifting )* parse_shifting ? parse_additive ( ('<<' | '>>') parse_additive )* parse_additive ? parse_multiplicative ( ('+' | '-') parse_multiplicative )* parse_multiplicative ? parse_power ( ('' | '/' | '') parse_unary ) parse_unary ? ('!' | '-' | '+' | '~') parse_unary | parse_power parse_power ? parse_unary ( '**' parse_unary )* (right-associative) parse_atom ? number | identifier (macro expansion) | 'defined' ( identifier ) | 'defined' identifier | '(' parse_expression ')'
Each parsing function consumes tokens from the global position pos and returns the integer value of the sub-expression it recognizes. Because the grammar is factored by precedence, left-associativity is achieved naturally via left-recursive loops, while right-associativity for the power operator (**) is handled by calling parse_unary on the right-hand side first.
Macro expansion occurs lazily inside parse_atom when an identifier token is encountered:
The parser is fully re-entrant and has no global state.
Public interface:
This design guarantees correct operator precedence without the need for an explicit abstract syntax tree or stack-based shunting-yard algorithm, while remaining easy to read, maintain, and extend.
Data Types | |
| interface | evaluate_expression |
| Evaluates a preprocessor-style expression with macro substitution. Tokenizes the input expression, expands macros where appropriate, parses it according to operator precedence, and computes the integer result. Returns .true. if evaluation succeeded and the result is non-zero. More... | |
| interface | strtol |
| Converts a string to integer. More... | |
| recursive integer function, public parse_expression | ( | character(*), intent(in) | expr, |
| type(token), dimension(:), intent(in) | tokens, | ||
| integer, intent(in) | ntokens, | ||
| integer, intent(inout) | pos, | ||
| type(macro), dimension(:), intent(inout), allocatable | macros, | ||
| type(context), intent(in) | ctx ) |
Parses a sequence of tokens starting at position pos as a full expression. Entry point for the recursive descent parser. Delegates to parse_or().
| [in] | expr | Expression to be processed |
| [in] | tokens | Array of tokens to parse |
| [in] | ntokens | Number of valid tokens in the array |
| [in,out] | pos | Current parsing position (updated as tokens are consumed) |
| [in,out] | macros | Defined macros for expansion and defined() checks |
| [in] | ctx | Context |
Remarks
Definition at line 195 of file operators.f90.