Rule authors want to write age>=18=>allow, no spaces, no parentheses. Racket's default reader may hand that over as one symbol, so a macro would be picking apart a spelling. A custom reader can draw the token lines itself.
Then the rest splits. Expansion rejects malformed rules and turns them into calls. Runtime fetches the age and deals with it being missing. If an attempt form shows up, someone has to say whether its retry repeats the test, the action, or both.