<div dir="ltr"><div>Here's a solution (at the bottom) with the datum/syntax converters. flatten is built-in, free-vars is basically filtering stuff and then removing duplicates, with-vars can take advantage of the built-in nested for, and then tauta.</div>
<div><br></div><div>tauta can get the "quoted" form of the expression like when you used eval by calling syntax->datum on the syntax given to tauta, minus "tauta" itself, which is the x pattern. But the pattern itself is not a syntax object so requires the syntax quote "#'", at which point you can de-syntaxify it with syntax->datum, getting '(or (and P Q) etc.). You can try this in the REPL, just do (syntax->datum #'(or (and P Q) etc.)).</div>
<div><br></div><div>Once free vars has this as '(P Q), map this list by re-syntaxifying the symbols with datum->syntax. The first parameter to datum->syntax basically is the key to "breaking" hygiene lisp-style. We want to say that the free variable symbols in "x" are the *same* as in our new datum->syntaxified symbols that get passed to with-vars to bind in the for/and, so we say they ultimately belong to the same lexical context (your top level code) by passing the same syntax ("stx") that tauta was called in. In other words, those generated vars are created as if you yourself defined them in the module. Without passing stx to datum->syntax, it would be as if someone else defined them elsewhere but coincidentally with the same names, and for safety hygienic systems disallow this shadowing (their x and your x are more like x1 and x2, being secretly renamed by the system). So we tell it we really mean our own code's context. In general, you can break hygiene this way by using whichever syntax object refers to wherever you want to pretend the syntax came from.</div>
<div><br></div><div>#lang racket<br>(require (for-syntax racket))</div><div><br></div><div>(define-syntax-rule (with-vars (vars ...) x)<br> (for*/and ([vars (in-list '(#t #f))]<br> ...)<br> x))</div>
<div><br></div><div>(define-for-syntax (free-vars xs)<br> (define (keyword? x)<br> (member x '(and or then -> <-> not<br> equiv display displayln)))<br> (remove-duplicates<br> (filter (compose not keyword?)<br>
(flatten xs))))</div><div><br></div><div>(define (then x y) (or y (not x)))</div><div>(define (equiv x y) (and (then x y) (then y x)))</div><div><br></div><div>(define-syntax (tauta stx)<br> (syntax-case stx ()<br>
[(_ x)<br> (with-syntax ([(vars ...) (map (λ (var) (datum->syntax stx var))<br> (free-vars (syntax->datum #'x)))])<br> #'(with-vars (vars ...) x))]))<br></div>
</div>