(define-module (test cpp preprocessor2) :use-module (srfi srfi-64) :use-module (srfi srfi-64 util) :use-module (srfi srfi-64 test-error) :use-module (srfi srfi-71) :use-module (srfi srfi-88) :use-module ((hnh util) :select (-> unval)) :use-module ((hnh util lens) :select (set)) :use-module (c preprocessor2) :use-module ((c cpp-environment) :select (extend-environment make-environment get-identifier enter-file in-environment? macro-identifier-list macro-body cpp-file-stack)) :use-module ((c cpp-environment function-like-macro) :select (function-like-macro)) :use-module ((c cpp-environment object-like-macro) :select (object-like-macro)) :use-module ((c cpp-util) :select (drop-whitespace-both tokens-until-eol squeeze-whitespace cleanup-whitespace )) :use-module ((c unlex) :select ( stringify-token stringify-tokens ) ) :use-module ((c cpp-types) :select (punctuator-token? identifier-token?)) :use-module (c lex2) ) ;; (test-expect-fail "x ## y") (define apply-macro (@@ (c preprocessor2) apply-macro)) (define build-parameter-map (@@ (c preprocessor2) build-parameter-map)) (define expand# (@@ (c preprocessor2) expand#)) (define expand## (@@ (c preprocessor2) expand##)) (define expand-macro (@@ (c preprocessor2) expand-macro)) (define handle-line-directive (@@ (c preprocessor2) handle-line-directive)) (define handle-preprocessing-tokens (@@ (c preprocessor2) handle-preprocessing-tokens)) (define join-file-line (@@ (c preprocessor2) join-file-line)) (define mark-noexpand (@@ (c preprocessor2) mark-noexpand)) (define maybe-extend-identifier (@@ (c preprocessor2) maybe-extend-identifier)) (define parse-identifier-list (@@ (c preprocessor2) parse-identifier-list)) (define parse-parameter-list (@@ (c preprocessor2) parse-parameter-list)) (define resolve-define (@@ (c preprocessor2) resolve-define)) (define resolve-token-stream (@@ (c preprocessor2) resolve-token-stream)) (define tokenize (@@ (c preprocessor2) tokenize)) ;; Remove the noexpand list from each token. ;; Allows equal? with fresh tokens (define (remove-noexpand tokens) ;; (typecheck tokens (list-of token?)) (map (lambda (token) (set token lexeme-noexpand '())) tokens)) (define* (run str optional: (env (make-environment))) (let ((env tokens (handle-preprocessing-tokens env (tokenize str)))) (drop-whitespace-both (remove-noexpand tokens)))) (test-group "Tokens until End Of Line" (call-with-values (lambda () (tokens-until-eol (lex "before\nafter"))) (lambda (bef aft) (test-equal (lex "before") bef) (test-equal (lex "\nafter") aft)))) (test-equal "Squeeze whitespace" (lex "bef aft") (squeeze-whitespace (append (lex "bef ") (lex " aft")))) (test-group "Stringify" (test-equal "(" (stringify-token (car (lex "(")))) ;; TODO more cases (test-equal (car (lex "\"(a, b)\"")) (stringify-tokens (lex "(a, b)")))) (test-group "Parse identifier list" (test-group "Single argument" (let ((rest args (parse-identifier-list (lex "x")))) (test-assert (not rest)) (test-equal '("x") args))) (test-group "Multiple parameters" (let ((rest args (parse-identifier-list (lex "x, y")))) (test-assert (not rest)) (test-equal '("x" "y") args))) (test-group "Rest args after regular" (let ((rest args (parse-identifier-list (lex "x, ...")))) (test-assert rest) (test-equal '("x") args))) (test-group "Only rest args" (let ((rest args (parse-identifier-list (lex "...")))) (test-assert rest) (test-equal '() args))) (test-group "Errors" (test-error "Compound forms are invalid" 'cpp-error (parse-identifier-list (lex "(y)"))) (test-error "Non-identifier atoms are invalid" 'cpp-error (parse-identifier-list (lex "1"))) (test-error "Rest args not at end is invalid" 'cpp-error (parse-identifier-list (lex "..., y"))))) (test-equal "Clean up whitespace" (lex "( 2 , 4 )") (cleanup-whitespace (lex " \n ( 2 , \n 4 ) \t "))) ;; Parameter lists (the callsite arguments to the macro) (test-group "Parameter list" (test-group "Empty parameter list" (let ((containing remaining nls (parse-parameter-list (lex "()")))) (test-equal '(()) containing) (test-equal '() remaining) (test-equal 0 nls))) (test-group "Single value in parameter list" (let ((containing remaining nls (parse-parameter-list (lex "(x)")))) (test-equal (list (lex "x")) containing) (test-equal '() remaining) (test-equal 0 nls))) (test-group "Two values in parameter list" (let ((containing remaining nls (parse-parameter-list (lex "(x, y)")))) (test-equal (list (lex "x") (lex " y")) containing) (test-equal '() remaining) (test-equal 0 nls))) (test-group "Three values in parameter list" (let ((containing remaining nls (parse-parameter-list (lex "(x, y, z)")))) (test-equal (list (lex "x") (lex " y") (lex " z")) containing) (test-equal '() remaining) (test-equal 0 nls))) (test-group "Numeric parameter" (let ((containing remaining nls (parse-parameter-list (lex "(1)")))) (test-equal (list (lex "1")) containing) (test-equal '() remaining) (test-equal 0 nls)) ) (test-group "Two values, one of which is a paretheseed pair" (let ((containing remaining nls (parse-parameter-list (lex "(x, (y, z))")))) (test-equal (list (lex "x") (lex " (y, z)")) containing) (test-equal '() remaining) (test-equal 0 nls)))) (test-group "Build parameter map" (test-equal "Simplest case, zero arguments" '() (let ((m (function-like-macro identifier: "str" identifier-list: '() body: (lex "#x")))) (build-parameter-map m '()))) (test-equal "Single (simple) argument" `(("x" . ,(lex "x"))) (let ((m (function-like-macro identifier: "str" identifier-list: '("x") body: '()))) (build-parameter-map m (list (lex "x"))))) (test-equal "Single advanced argument" `(("x" . ,(lex "(x)"))) (let ((m (function-like-macro identifier: "str" identifier-list: '("x") body: '()))) (build-parameter-map m (list (lex "(x)"))))) (test-group "Rest arguments" (test-equal "Single simple" `(("__VA_ARGS__" . ,(lex "x"))) (let ((m (function-like-macro identifier: "str" identifier-list: '() variadic?: #t body: '()))) (build-parameter-map m (list (lex "x"))))) (test-equal "Two simple" `(("__VA_ARGS__" . ,(lex "x,y"))) (let ((m (function-like-macro identifier: "str" identifier-list: '() variadic?: #t body: '()))) (build-parameter-map m (list (lex "x,y"))))))) (test-group "Expand stringifiers" (let ((m (function-like-macro identifier: "str" identifier-list: '("x") body: (lex "#x")))) (test-equal "Correct stringification of one param" (lex "\"10\"") (expand# m (build-parameter-map m (list (lex "10")))))) (let ((m (function-like-macro identifier: "str" identifier-list: '() body: (lex "#x")))) (test-error "Stringification fails for non-parameters" 'macro-expand-error (expand# m (build-parameter-map m (list (lex "x")))))) (let ((m (function-like-macro identifier: "f" identifier-list: '() variadic?: #t body: (lex "# __VA_ARGS__")))) (test-equal "Stringify __VA_ARGS__" (lex "\"10, 20\"") (expand# m (build-parameter-map m (list (lex "10, 20"))))))) ;; TODO expand-join ;; token ## token2 (let ((e (join-file-line (make-environment)))) (test-equal "__FILE__ default value" (object-like-macro identifier: "__FILE__" body: (lex "\"*outside*\"")) (get-identifier e "__FILE__")) (test-equal "__LINE__ default value" (object-like-macro identifier: "__LINE__" body: (lex "1")) (get-identifier e "__LINE__"))) (test-group "Token streams" (test-group "Non-expanding" (test-equal "Null stream" '() (resolve-token-stream (make-environment) '())) (test-equal "Constant resolve to themselves" (lex "1") (resolve-token-stream (make-environment) (lex "1"))) (test-equal "Identifier-likes not in environment stay put" (lex "x") (remove-noexpand (resolve-token-stream (make-environment) (lex "x")))) (test-equal "Identifier-likes with stuff after keep stuff after" (lex "x 1") (remove-noexpand (resolve-token-stream (make-environment) (lex "x 1"))))) (test-group "Object likes" (test-equal "Expansion of single token" (lex "10") (remove-noexpand (resolve-token-stream (extend-environment (make-environment) (list (object-like-macro identifier: "x" body: (lex "10")))) (lex "x")))) (test-equal "Expansion keeps stuff after" (lex "10 1") (remove-noexpand (resolve-token-stream (extend-environment (make-environment) (list (object-like-macro identifier: "x" body: (lex "10")))) (lex "x 1")))) (test-equal "Multiple object like macros in one stream" (lex "10 20") (remove-noexpand (resolve-token-stream (extend-environment (make-environment) (list (object-like-macro identifier: "x" body: (lex "10")) (object-like-macro identifier: "y" body: (lex "20")))) (lex "x y"))))) ;; TODO ;; (test-group "Function likes") ;; (test-group "Mix of object and function likes") ) (test-group "Macro expansion" (test-group "Expand macro part 1" ;; Expand object like macros ;; apply-macro depends on this, but expand macro with function like macros ;; depend on apply-macro, thereby the two parter (test-group "Object like macros" (call-with-values (lambda () (expand-macro (make-environment) (object-like-macro identifier: "x" body: (lex "1 + 2")) '() '())) (lambda (_ tokens) (test-equal "Simplest case" (lex "1 + 2") (remove-noexpand tokens)))) (call-with-values (lambda () (expand-macro (make-environment) (object-like-macro identifier: "x" body: (lex "1+2")) '() (cdr (lex "x something else")))) (lambda (_ tokens) (test-equal "Expansion with stuff after" (lex "1+2 something else") (remove-noexpand tokens)))) ;; (call-with-values (expand-macro (make-environment))) )) (test-group "Maybe extend identifier" (test-equal "Non-identifier returns remaining" (lex "x") (remove-noexpand ((unval maybe-extend-identifier 1) (make-environment) "x" '()'()))) (test-equal "Non-identifiers remaining tokens are returned verbatim" (append (lex "x") (lex "after")) (remove-noexpand ((unval maybe-extend-identifier 1) (make-environment) "x" '() (lex "after")))) (test-equal "Object like identifier expands" (lex "1 + 2") (remove-noexpand ((unval maybe-extend-identifier 1) (extend-environment (make-environment) (list (object-like-macro identifier: "x" body: (lex "1 + 2")))) "x" '() '()))) (test-equal "Object like macro still returns remaining verbatim" (append (lex "1 + 2") (lex "after")) (remove-noexpand ((unval maybe-extend-identifier 1) (extend-environment (make-environment) (list (object-like-macro identifier: "x" body: (lex "1 + 2")))) "x" '() (lex "after")))) ) (test-group "Apply macro" (test-equal "zero arg macro on nothing" (lex "1") (remove-noexpand (apply-macro (make-environment) (function-like-macro identifier: "f" identifier-list: '() body: (lex "1")) '()))) (test-equal "Single arg macro" (lex "10") (remove-noexpand (apply-macro (make-environment) (function-like-macro identifier: "f" identifier-list: '("x") body: (lex "x")) ((unval parse-parameter-list) (lex "(10)"))))) (test-equal "Two arg macro" (lex "10 + 20") (remove-noexpand (apply-macro (make-environment) (function-like-macro identifier: "f" identifier-list: '("x" "y") body: (lex "x + y")) ((unval parse-parameter-list) (lex "(10, 20)")))))) (test-group "Expand macro part 2" (test-group "Function like macros" (let ((e (make-environment))) (let ((m (function-like-macro identifier: "f" identifier-list: '() body: (lex "1")))) (call-with-values (lambda () (expand-macro e m '() (lex "()"))) (lambda (_ tokens*) (test-equal (lex "1") (remove-noexpand tokens*)))) (test-error "Arity error for to many args" 'cpp-arity-error (expand-macro e m '() (lex "(10)")))) (let ((m (function-like-macro identifier: "f" identifier-list: '("x") variadic?: #t body: (lex "__VA_ARGS__ x")))) (call-with-values (lambda () (expand-macro e m '() (lex "(1)"))) (lambda (_ tokens*) (test-equal (lex " 1") (remove-noexpand tokens*)))) ;; This doesn't fail, since a single required argument is satisfied by the default nothing #; (test-error "Arity error on too few args (with variadic)" 'cpp-arity-error (expand-macro e m '() (lex "()"))) (call-with-values (lambda () (expand-macro e m '() (lex "(1,2,3)"))) (lambda (_ tokens*) (test-equal (lex "2,3 1") (remove-noexpand tokens*)))) ) )))) (let ((e (make-environment))) (test-group "Resolve token stream with function likes" (test-equal "Macro expanding to its parameter" (lex "0") (remove-noexpand (resolve-token-stream (extend-environment e (list (function-like-macro identifier: "f" identifier-list: '("x") body: (lex "x")))) (lex "f(0)")))) (test-equal "Macro expanding parameter multiple times" (lex "(2) * (2)") (remove-noexpand (resolve-token-stream (extend-environment e (list (function-like-macro identifier: "f" identifier-list: '("x") body: (lex "(x) * (x)")))) (lex "f(2)"))) ) (test-equal "Object like contains another object like" (lex "z") (remove-noexpand (resolve-token-stream (extend-environment e (list (object-like-macro identifier: "x" body: (lex "y")) (object-like-macro identifier: "y" body: (lex "z")))) (lex "x")))) (test-equal "function like contains another macro" (lex "10") (remove-noexpand (resolve-token-stream (extend-environment e (list (function-like-macro identifier: "f" identifier-list: '("x") body: (lex "g(x)")) (function-like-macro identifier: "g" identifier-list: '("y") body: (lex "y")))) (lex "f(10)")))) (test-equal "function like containing another macro using the same parameter name" (lex "10") (remove-noexpand (resolve-token-stream (extend-environment e (list (function-like-macro identifier: "f" identifier-list: '("x") body: (lex "g(x)")) (function-like-macro identifier: "g" identifier-list: '("x") body: (lex "x")))) (lex "f(10)")))) (test-equal "function like contains another macro" (lex "10 * 2 + 20 * 2 + 30") (remove-noexpand (resolve-token-stream (extend-environment e (list (function-like-macro identifier: "f" identifier-list: '("x" "y") body: (lex "g(x) + g(y)")) (function-like-macro identifier: "g" identifier-list: '("x") body: (lex "x * 2")))) (lex "f(10, 20) + 30")))))) (let ((e (extend-environment (make-environment) (list (@ (c preprocessor2) defined-macro))))) (test-group "defined() macro" (test-equal "defined(NOT_DEFINED)" (lex "0") (remove-noexpand (resolve-token-stream e (lex "defined(X)")))) (test-equal "defined(DEFINED)" (lex "1") (remove-noexpand (resolve-token-stream (extend-environment e (list (object-like-macro identifier: "X" body: (lex "10")))) (lex "defined(X)")))))) (let ((env (resolve-define (make-environment) (lex "f(x) x+1")))) (test-assert "New binding added" (in-environment? env "f")) (let ((m (get-identifier env "f"))) (test-equal "Macro parameters" '("x") (macro-identifier-list m)) (test-equal "Macro body" (lex "x+1") (macro-body m)))) ;; This should issue a warning, since the standard requires a space after the ending parenthe here (6.10.3) ;; (resolve-define (make-environment) ;; (lex "f(x)x+1")) (test-group "Recursive macros" (let ((env (resolve-define (make-environment) (lex "x x")))) (test-equal "Macro expanding to itself leaves the token" (mark-noexpand (lex "x") "x") (resolve-token-stream env (lex "x")))) ;; Test from C standard 6.10.3.4 p. 4 ;; Both the expansion "2*f(9)" and "2*9*g" are valid. ;; The case chosen here is mostly a consequence of how the code works (let ((env (-> (make-environment) (resolve-define (lex "f(a) a*g")) (resolve-define (lex "g(a) f(a)"))))) (test-equal "Mutual recursion with two function like macros" (lex "2*f(9)") (remove-noexpand (resolve-token-stream env (lex "f(2)(9)"))))) (let ((env (-> (make-environment) (resolve-define (lex "f 2 * g")) (resolve-define (lex "g(x) x + f"))))) (test-equal "Mutual recursion with object and function like macro" (lex "2 * 10 + f") (remove-noexpand (resolve-token-stream env (lex "f(10)"))))) (let ((env (-> (make-environment) (resolve-define (lex "x 2*y")) (resolve-define (lex "y 3*x"))))) (test-equal "Mutual recursion with two object likes" (lex "2*3*x") (remove-noexpand (resolve-token-stream env (lex "x")))))) (test-group "Line directive" (let ((e (make-environment))) (test-equal "#line " '(("*outside*" . 9)) (cpp-file-stack (handle-line-directive e (lex "10")))) (test-equal "#line " '(("file" . 9)) (cpp-file-stack (handle-line-directive e (lex "10 \"file\"")))) (test-equal "#line " '(("*outside*" . 9)) (cpp-file-stack (handle-line-directive (resolve-define e (lex "x 10")) (lex "x")))))) ;; resolve-h-file ;; resolve-q-file ;; handle-pragma ;; include ;; if ;; else ;; ifdef ;; ifndef ;; elif (call-with-values (lambda () (handle-preprocessing-tokens (make-environment) (lex "1"))) (lambda (env tokens) (test-equal "Simplest case" (lex "1") tokens))) (test-equal "Define" (lex "1") (run " #define x 1 x")) (test-group "__LINE__ and __FILE__" (test-group "__LINE__" (test-equal "only __LINE__" (lex "1") (run "__LINE__")) (test-equal "__LINE__ after linebreak" (lex "2") (run "\n__LINE__")) (test-equal "__LINE__ through macro" (lex "5") (drop-whitespace-both (run " // 1 #define x __LINE__ // 2 // 3 // 4 x // 5")) ) (test-equal "__LINE__ standalone" (lex "5") (drop-whitespace-both (run " // 1 // 2 // 3 // 4 __LINE__")))) (test-equal "__FILE__" (lex "\"sample-file.c\"") (run "__FILE__" (enter-file (make-environment) "sample-file.c"))) (test-group "#line" (test-equal "Updating line" (lex "10") (run "#line 10\n__LINE__")) (test-equal "Updating line and file" (lex "10 \"file.c\"") (run "#line 10 \"file.c\"\n__LINE__ __FILE__")) ) ) (test-group "expand##" (test-error 'cpp-error (expand## (lex "a ##"))) (test-error 'cpp-error (expand## (lex "## a"))) (test-error 'cpp-error (expand## (lex "##"))) (test-equal (lex "ab") (expand## (lex "a ## b"))) ) (test-group "Token concatenation" (test-equal "Token concatenation in function like macro" (lex "ab") (run " #define f() a ## b f()")) (test-equal "token concatentanion in object like macro" (lex "ab") (run " #define x a ## b x")) (test-equal "Token concatenation with parameter" (lex "ab") (run " #define f(x) x ## b f(a)")) ;; 6.10.3.3 p. 4 (test-equal "x ## y" (lex "char p[] = \"x ## y\"") (run " #define hash_hash # ## # #define mkstr(a) # a #define in_between(a) mkstr(a) #define join(c, d) in_between(c hash_hash d) char p[] = join(x, y)"))) (test-group "__VA_ARGS__" (test-equal "__VA_ARGS__ split its arguments" (lex "1") (run " #define fst(x, y) x #define f(...) fst(__VA_ARGS__) f(1,2) ")) (test-equal "Stringify __VA_ARGS__" (lex "\"1,2\"") (run " #define g(...) #__VA_ARGS__ g(1,2) ")) (test-equal "__VA_ARGS__ keep whitespace" (lex "x, y") (run " #define args(...) __VA_ARGS__ args(x, y) ")) (test-equal "Concat with __VA_ARGS__" (lex "fx,y") (run " #define wf(...) f ## __VA_ARGS__ wf(x,y) ")) (test-equal "Concat with __VA_ARGS__ (keeping whitespace)" (lex "fx, y") (run " #define wf(...) f ## __VA_ARGS__ wf(x, y) "))) (test-equal "Usage before #define" (lex "X") (run "X #define X 100")) (test-equal "#undef" (lex "X\n10\nX") (run " X #define X 10 X #undef X X ") ) ;; #undef ;; #error (test-group "Pragma" (test-group "#pragma" (test-equal "#Pragma STDC FP_CONTRACT ON" (with-output-to-string (lambda () (run "#pragma STDC FP_CONTRACT ON")))) ) (test-group "_Pragma" 'noop)) ;; if ;; else ;; ifdef ;; ifndef ;; elif (define next-token-matches? (@@ (c preprocessor2) next-token-matches?)) (test-group "Next token matches?" (test-assert "Zero tokens never match" (not (next-token-matches? (const #t) '()))) (test-assert "Non-matching token" (not (next-token-matches? punctuator-token? (lex "x+y")))) (test-assert "Maching token" (next-token-matches? identifier-token? (lex "x+y"))) (test-assert "Matching token, after whitespace" (next-token-matches? identifier-token? (lex " \n x + y")))) (test-equal "Function likes aren't expanded if not followed by a left parenthese" (lex "f") (run " #define f(x) f")) (test-equal "Parameter expansion times" (lex "fx fy") (run " #define fw(x) f ## x #define ffw(x) fw(x) #define x y fw(x) ffw(x) ")) (test-equal (lex "(5 + 10)") (run " #define x 10 #define f(a) a #define g h #define h(x) (x + 10) f(g)(5)")) ;; (expand-macro ;; (extend-environment ;; (make-environment) ;; (list (object-like-macro identifier: "g" ;; body: (lex "h")) ;; (function-like-macro identifier: "h" ;; identifier:-list '("x") ;; body: (lex "(x + 10)")))) ;; (function-like-macro identifier: "f" ;; identifier:-list '("a") ;; body: (lex "a")) ;; '() ;; (lex "(g)(5)")) ;; ;; ⇒ #< cpp-if-status: (outside) cpp-variables: # cpp-file-stack: (("*outside*" . 1))> ;; ⇒ (#< type: preprocessing-token body: (identifier "h") noexpand: ("f" "h")> ;; #< type: preprocessing-token body: (punctuator "(") noexpand: ()> ;; #< type: preprocessing-token body: (pp-number "5") noexpand: ()> ;; #< type: preprocessing-token body: (punctuator ")") noexpand: ()>) (test-equal "non-adjacent parameter list" (lex "2*10") (run " #define f(x) 2*x f (10)")) (test-equal "parameter-list on own line" (lex "2*10") (run " #define f(x) 2*x f (10)")) (define unlex (@ (c unlex) unlex)) (test-group "6.10.3.5 Scope of macro definitions" (test-equal "Example3" (unlex (lex "f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1); f(2 * (2+(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1); int i[] = { 1, 23, 4, 5, }; char c[2][6] = { \"hello\", \"\" };")) (unlex (drop-whitespace-both (run " #define x 3 #define f(a) f(x * (a)) #undef x #define x 2 #define g f #define z z[0] #define h g(~ #define m(a) a(w) #define w 0,1 #define t(a) a #define p() int #define q(x) x #define r(x,y) x ## y #define str(x) # x f(y+1) + f(f(z)) % t(t(g)(0) + t)(1); g(x+(3,4)-w) | h 5) & m (f)^m(m); p() i[q()] = { q(1), r(2,3), r(4,), r(,5), r(,) }; char c[2][6] = { str(hello), str() };")))) ) ;; (tokenize " ;; f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1); ;; f(2 * (2+(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1); ;; int i[] = { 1, 23, 4, 5, }; ;; char c[2][6] = { \"hello\", \"\" };") ;; (define env ;; (handle-preprocessing-tokens (make-environment) (tokenize " ;; #define x 3 ;; #define f(a) f(x * (a)) ;; #undef x ;; #define x 2 ;; #define g f ;; #define z z[0] ;; #define h g(~ ;; #define m(a) a(w) ;; #define w 0,1 ;; #define t(a) a ;; #define p() int ;; #define q(x) x ;; #define r(x,y) x ## y ;; #define str(x) # x ;; "))) ;; (handle-preprocessing-tokens ;; env (tokenize ;; "f(y+1) + f(f(z)) % t(t(g)(0) + t)(1); ;; g(x+(3,4)-w) | h 5) & m ;; (f)^m(m); ;; p() i[q()] = { q(1), r(2,3), r(4,), r(,5), r(,) }; ;; char c[2][6] = { str(hello), str() };")) ;; (let ((env tokens ;; (handle-preprocessing-tokens ;; (make-environment) ;; (tokenize " ;; #define x 3 ;; #define f(a) f(x * (a)) ;; #undef x ;; #define x 2 ;; #define g f ;; #define z z[0] ;; #define h g(~ ;; #define m(a) a(w) ;; #define w 0,1 ;; #define t(a) a ;; #define p() int ;; #define q(x) x ;; #define r(x,y) x ## y ;; #define str(x) # x ;; f(y+1) + f(f(z)) % t(t(g)(0) + t)(1); ;; g(x+(3,4)-w) | h 5) & m ;; (f)^m(m); ;; p() i[q()] = { q(1), r(2,3), r(4,), r(,5), r(,) }; ;; char c[2][6] = { str(hello), str() };")))) ;; (remove-noexpand tokens)) ;; (test-equal "anything" ;; (run ;; " ;; #define x 3 ;; #define f(a) f(x * (a)) ;; #undef x ;; #define x 2 ;; #define g f ;; #define z z[0] ;; #define h g(~ ;; #define m(a) a(w) ;; #define w 0,1 ;; #define t(a) a ;; #define p() int ;; #define q(x) x ;; #define r(x,y) x ## y ;; #define str(x) # x ;; f(y+1) + f(f(z)) % t(t(g)(0) + t)(1); ;; g(x+(3,4)-w) | h 5) & m ;; (f)^m(m); ;; p() i[q()] = { q(1), r(2,3), r(4,), r(,5), r(,) }; ;; char c[2][6] = { str(hello), str() };"))