diff options
author | Hugo Hörnquist <hugo@lysator.liu.se> | 2022-07-12 02:37:49 +0200 |
---|---|---|
committer | Hugo Hörnquist <hugo@lysator.liu.se> | 2022-07-12 02:37:49 +0200 |
commit | ccc7848110b06479cf8a38ee843d4d3adc01a27c (patch) | |
tree | 2db89619c228b5e17f2df5b76b2eaf90060b0928 | |
parent | Remove linear update environment procedures. (diff) | |
download | calp-ccc7848110b06479cf8a38ee843d4d3adc01a27c.tar.gz calp-ccc7848110b06479cf8a38ee843d4d3adc01a27c.tar.xz |
work
-rw-r--r-- | module/c/cpp-environment/function-like-macro.scm | 2 | ||||
-rw-r--r-- | module/c/cpp-types.scm | 14 | ||||
-rw-r--r-- | module/c/lex2.scm | 19 | ||||
-rw-r--r-- | module/c/preprocessor2.scm | 163 | ||||
-rw-r--r-- | module/c/unlex.scm | 12 | ||||
-rw-r--r-- | tests/test/cpp/preprocessor2.scm | 185 |
6 files changed, 320 insertions, 75 deletions
diff --git a/module/c/cpp-environment/function-like-macro.scm b/module/c/cpp-environment/function-like-macro.scm index a4b58487..59b47c9c 100644 --- a/module/c/cpp-environment/function-like-macro.scm +++ b/module/c/cpp-environment/function-like-macro.scm @@ -12,7 +12,7 @@ (define-type (function-like-macro printer: (lambda (r p) - (format p "#<#define ~a(~a) ~a>" + (format p "#<#define ~a~a ~a>" (identifier r) (append (identifier-list r) (if (variadic? r) diff --git a/module/c/cpp-types.scm b/module/c/cpp-types.scm index e5e73d32..555120d6 100644 --- a/module/c/cpp-types.scm +++ b/module/c/cpp-types.scm @@ -11,19 +11,25 @@ punctuator-token? number-token? string-token? + h-string-token? + q-string-token? )) (define (whitespace-token? x) - (eq? 'whitespace (lexeme-type x))) + (and (lexeme? x) + (eq? 'whitespace (lexeme-type x)))) (define (comment-token? x) - (eq? 'comment (lexeme-type x))) + (and (lexeme? x) + (eq? 'comment (lexeme-type x)))) (define (preprocessing-token? x) - (eq? 'preprocessing-token (lexeme-type x))) + (and (lexeme? x) + (eq? 'preprocessing-token (lexeme-type x)))) (define (placemaker-token? x) - (eq? 'placemaker (lexeme-type x))) + (and (lexeme? x) + (eq? 'placemaker (lexeme-type x)))) (define (newline-token? x) (and (whitespace-token? x) diff --git a/module/c/lex2.scm b/module/c/lex2.scm index c00a029c..50cf56e3 100644 --- a/module/c/lex2.scm +++ b/module/c/lex2.scm @@ -285,12 +285,12 @@ ;; (6.4.7) (define-peg-pattern h-char body - (or (and (not-followed-by (or ">" "\n")) peg-any) + (or (and (not-followed-by (or ">" "\n")) peg-any) escape-sequence)) ;; (6.4.7) (define-peg-pattern q-char body - (or (and (not-followed-by (or "\"" "\n")) peg-any) + (or (and (not-followed-by (or "\"" "\n")) peg-any) escape-sequence)) ;;; A.1.9 Preprocessing numbers @@ -347,9 +347,18 @@ (`(comment ,body) (lexeme body: body type: 'comment )) (`(preprocessing-token ,body) - (lexeme body: body type: 'preprocessing-token)))) + (case body + ;; "unflatten" + ((string-literal) + (lexeme body: '(string-literal "") type: 'preprocessing-token)) + (else + (lexeme body: body type: 'preprocessing-token)))) + ;; "unflatten" + ('comment (lexeme body: "" type: 'comment)))) ;; returns a list of lexemes (define (lex string) - (map lex-output->lexeme-object - (cdr (peg:tree (match-pattern preprocessing-tokens string))))) + (if (string-null? string) + '() + (map lex-output->lexeme-object + (cdr (peg:tree (match-pattern preprocessing-tokens string)))))) diff --git a/module/c/preprocessor2.scm b/module/c/preprocessor2.scm index 7e6de2e1..44931b68 100644 --- a/module/c/preprocessor2.scm +++ b/module/c/preprocessor2.scm @@ -7,7 +7,7 @@ :use-module (c eval2) :use-module ((c cpp-environment function-like-macro) :select (function-like-macro variadic? identifier-list)) - :use-module ((c cpp-environment object-like-macro) :select (object-like-macro)) + :use-module ((c cpp-environment object-like-macro) :select (object-like-macro object-like-macro?)) :use-module ((c cpp-environment internal-macro) :select (internal-macro)) :use-module ((hnh util) :select (-> intersperse aif swap unless)) :use-module ((hnh util lens) :select (set modify cdr*)) @@ -26,6 +26,13 @@ (define parameter-map? (of-type? (alist-of string? (list-of lexeme?)))) +(define (concat-token? token) (equal? "##" (punctuator-token? token))) +(define (stringify-token? token) (equal? "#" (punctuator-token? token))) +(define (left-parenthesis-token? token) (equal? "(" (punctuator-token? token))) +(define (right-parenthesis-token? token) (equal? ")" (punctuator-token? token))) +(define (comma-token? token) (equal? "," (punctuator-token? token))) +(define (ellipsis-token? token) (equal? "..." (punctuator-token? token))) + ;; parameters is a lexeme list, as returned by parse-parameter-list (define (build-parameter-map macro parameters) (typecheck macro macro?) @@ -46,7 +53,7 @@ (typecheck parameter-map parameter-map?) (let loop ((tokens (macro-body macro))) (cond ((null? tokens) '()) - ((equal? "#" (punctuator-token? (car tokens))) + ((stringify-token? (car tokens)) (let* ((head rest (car+cdr (drop-whitespace (cdr tokens)))) (x (identifier-token? head))) (cond ((assoc-ref parameter-map x) @@ -69,7 +76,7 @@ (right tokens)) (cond ((null? right) (reverse left)) - ((equal? "##" (punctuator-token? (car right))) + ((concat-token? (car right)) (let ((l (drop-whitespace left)) (r (drop-whitespace (cdr right)))) (cond ((or (null? l) (null? r)) @@ -86,11 +93,32 @@ (else (loop (cdr l) (cons (concatenate-tokens (car l) (car r)) (cdr r))))))) (else - (let ((pre post (break (lambda (token) (equal? "##" (punctuator-token? token))) - right))) + (let ((pre post (break concat-token? right))) (loop (append left (reverse pre)) post)))))) +(define (check-arity macro parameters) + (if (variadic? macro) + (unless (>= (length parameters) + (length (macro-identifier-list macro))) + (scm-error 'cpp-arity-error "apply-macro" + "Too few arguments to variadic macro ~s, expected at least ~s, got ~s" + (list (macro-identifier macro) + (length (macro-identifier-list macro)) + (length parameters)) + (list macro))) + (unless (or (and (= 0 (length (macro-identifier-list macro))) + (= 1 (length parameters)) + (null? (car parameters))) + (= (length (macro-identifier-list macro)) + (length parameters))) + (scm-error 'cpp-arity-error "apply-macro" + "Wrong number of arguments to macro ~s, expected ~s, got ~s" + (list (macro-identifier macro) + (length (macro-identifier-list macro)) + (length parameters)) + (list macro))))) + ;; expand function like macro ;; parameter is a list of lexeme-lists, each "top level" element matching one ;; argument to the macro @@ -99,24 +127,8 @@ ;; Each element should be the lexeme list for that argument (typecheck parameters (list-of (list-of lexeme?))) (typecheck macro macro?) - (when (and (variadic? macro) - (> (length (macro-identifier-list macro)) - (length parameters))) - (scm-error 'cpp-arity-error "apply-macro" - "Too few arguments to variadic macro ~s, expected at least ~s, got ~s" - (list (macro-identifier macro) - (length (macro-identifier-list macro)) - (length parameters)) - (list macro))) - (when (and (not (variadic? macro)) - (not (= (length (macro-identifier-list macro)) - (length parameters)))) - (scm-error 'cpp-arity-error "apply-macro" - "Wrong number of arguments to macro ~s, expected ~s, got ~s" - (list (macro-identifier macro) - (length (macro-identifier-list macro)) - (length parameters)) - (list macro))) + (check-arity macro parameters) + (let () (define (resolve-cpp-variables tokens parameter-map) @@ -124,16 +136,24 @@ (assoc-ref parameter-map id)) ;; expand parameters, and place placemaker tokens - (let loop ((tokens tokens)) + (let loop ((tokens tokens) (last #f)) (cond ((null? tokens) '()) ((identifier-token? (car tokens)) bound-identifier? - => (lambda (id) (let ((replacement (assoc-ref parameter-map id))) - (if (null? replacement) - (cons (placemaker) (loop (cdr tokens))) - ;; TODO macroexpand replacement here? - (append replacement (loop (cdr tokens))))))) - (else (cons (car tokens) (loop (cdr tokens))))))) + => (lambda (id) + (let ((replacement (assoc-ref parameter-map id))) + (if (null? replacement) + (cons (placemaker) (loop (cdr tokens) #f)) + ;; macroexpand replacement here! But only if the token isn't used with ## (or #) + (append + (if (or (concat-token? last) + (next-token-matches? concat-token? tokens)) + replacement + (resolve-token-stream environment replacement once?: #t)) + (loop (cdr tokens) #f)))))) + ((whitespace-token? (car tokens)) + (cons (car tokens) (loop (cdr tokens) last))) + (else (cons (car tokens) (loop (cdr tokens) (car tokens))))))) (define parameter-map (build-parameter-map macro parameters)) @@ -162,26 +182,41 @@ (let ((name (macro-identifier macro))) (cond ((object-macro? macro) + ;; #define f(a) f(x * (a)) + ;; #define w 0,1 + ;; #define m(a) a(w) + ;; m(f) + ;; ⇒ f(0,1) + ;; instead of expected + ;; f(2 * (0,1)) (values environment (append (fold (swap mark-noexpand) (expand## (macro-body macro)) (cons name noexpand-list)) remaining-tokens))) ((function-macro? macro) - (let ((containing remaining newlines (parse-parameter-list remaining-tokens))) - (values (bump-line environment newlines) - (append (fold (swap mark-noexpand) - (apply-macro environment macro containing) - (cons name noexpand-list)) - remaining)))) + (if (next-token-matches? left-parenthesis-token? remaining-tokens) + (let ((containing remaining newlines (parse-parameter-list remaining-tokens))) + (values (bump-line environment newlines) + (append (fold (swap mark-noexpand) + (apply-macro environment macro containing) + (cons name noexpand-list)) + remaining))) + (values environment + (append (mark-noexpand (lex (macro-identifier macro)) (macro-identifier macro)) + remaining-tokens)))) ((internal-macro? macro) - (let ((containing remaining newlines (parse-parameter-list remaining-tokens))) - (values (bump-line environment newlines) - (append (fold (swap mark-noexpand) - ((macro-body macro) environment containing) - (cons name noexpand-list)) - remaining)))) + (if (next-token-matches? left-parenthesis-token? remaining-tokens) + (let ((containing remaining newlines (parse-parameter-list remaining-tokens))) + (values (bump-line environment newlines) + (append (fold (swap mark-noexpand) + ((macro-body macro) environment containing) + (cons name noexpand-list)) + remaining))) + (values environment + (append (mark-noexpand (lex (macro-identifier macro)) (macro-identifier macro)) + remaining-tokens)))) (else (scm-error 'wrong-type-arg "expand-macro" @@ -199,13 +234,13 @@ (cond ((null? tokens) (values #f (reverse done))) ((identifier-token? (car tokens)) => (lambda (id) (loop (cdr tokens) (cons id done)))) - ((equal? '(punctuator "...") (lexeme-body (car tokens))) + ((ellipsis-token? (car tokens)) (unless (null? (cdr tokens)) (scm-error 'cpp-error "parse-identifier-list" "'...' only allowed as last argument in identifier list. Rest: ~s" (list (cdr tokens)) #f)) (values #t (reverse done))) - ((equal? '(punctuator ",") (lexeme-body (car tokens))) + ((comma-token? (car tokens)) (loop (cdr tokens) done)) (else (scm-error 'cpp-error "parse-identifier-list" "Unexpected preprocessing-token in identifier list: ~s" @@ -213,6 +248,12 @@ +(define (next-token-matches? predicate tokens) + (let ((tokens (drop-whitespace tokens))) + (if (null? tokens) + #f + (predicate (car tokens))))) + ;; returns three values: ;; - a list of tokens where each is a parameter to the function like macro @@ -241,21 +282,19 @@ ((whitespace-token? (car tokens)) (loop (cdr tokens) current: current*)) - ((equal? '(punctuator "(") (lexeme-body (car tokens))) + ((left-parenthesis-token? (car tokens)) (loop (cdr tokens) depth: (1+ depth) current: current*)) - ((equal? '(punctuator ")") (lexeme-body (car tokens))) + ((right-parenthesis-token? (car tokens)) (if (= 1 depth) ;; return value (values - (reverse (if (null? current) - parameters - (cons (reverse current) parameters))) + (reverse (cons (reverse current) parameters)) (cdr tokens) newlines) (loop (cdr tokens) depth: (1- depth) current: current*))) - ((equal? '(punctuator ",") (lexeme-body (car tokens))) + ((comma-token? (car tokens)) (if (= 1 depth) (loop (cdr tokens) current: '() @@ -376,7 +415,8 @@ (else #f))) ;; Expands a token-stream which contains no pre-processing directives (#if:s, ...) -(define (resolve-token-stream environment tokens) +;; If @var{once?} is true then the resulting body won't be scanned again for tokens to expand +(define* (resolve-token-stream environment tokens key: once?) (typecheck environment cpp-environment?) (typecheck tokens (list-of lexeme?)) ;; (pprint-environment environment) @@ -392,7 +432,8 @@ (identifier-token? token) (lexeme-noexpand token) (cdr tokens))) - loop))) + ;; Here is the after expansion + (if once? (lambda (_ t) t) loop)))) (else (cons (car tokens) (loop environment (cdr tokens))))))) @@ -440,14 +481,14 @@ (cond ((null? tokens) '()) ((h-string-token? (car tokens)) => (lambda (str) - (unless (null? (remove-whitespace (cdr tokens))) + (unless (null? (drop-whitespace (cdr tokens))) (err "Unexpected tokens after #include <>")) (handle-preprocessing-tokens environment (-> str resolve-h-file read-file tokenize)))) ((q-string-token? (car tokens)) => (lambda (str) - (unless (null? (remove-whitespace (cdr tokens))) + (unless (null? (drop-whitespace (cdr tokens))) (err "Unexpected tokens after #include \"\"")) (handle-preprocessing-tokens environment @@ -493,12 +534,11 @@ (add-identifier identifier (cond ((and (not (null? tail)) - (equal? '(punctuator "(") (lexeme-body (car tail)))) + (left-parenthesis-token? (car tail))) ;; function like macro (let ((identifier-list replacement-list - (break (lambda (token) (equal? '(punctuator ")") (lexeme-body token))) - (cdr tail)))) + (break right-parenthesis-token? (cdr tail)))) (let ((variadic? identifiers (parse-identifier-list identifier-list))) (function-like-macro identifier: identifier @@ -535,7 +575,7 @@ (let ((environment (bump-line environment)) (tokens* (drop-whitespace (cdr tokens)))) (cond ((null? tokens*) (values environment '())) - ((equal? '(punctuator "#") (lexeme-body (car tokens*))) + ((equal? "#" (punctuator-token? (car tokens*))) (let ((line-tokens remaining-tokens (tokens-until-eol (cdr tokens*)))) ;; drop whitespace after to not "eat" the next newline token (let ((line-tokens (drop-whitespace line-tokens))) @@ -564,7 +604,7 @@ (lambda (environment tokens) (loop environment (append tokens remaining-tokens)))) - (let ((operation + (let ((operation ; (environment, list token) → environment (case directive ((if) resolve-for-if) ((ifdef) @@ -591,7 +631,10 @@ (values env* (append (unless (in-comment-block? environment) (resolve-token-stream environment line-tokens)) - tokens*)))))))) + ;; The initial newline is presreved here, for better output, + ;; and to keep at least one whitespace token when there was one previously. + ;; possibly also keep a newline for line-directives. + (append (unless (null? remaining-tokens) (lex "\n")) tokens*))))))))) (else (err "Unexpected middle of line"))))) diff --git a/module/c/unlex.scm b/module/c/unlex.scm index 9f4b25b9..18e800d9 100644 --- a/module/c/unlex.scm +++ b/module/c/unlex.scm @@ -8,10 +8,17 @@ stringify-token stringify-tokens)) -;; takes a list of preprocessing-token's, and return a "source" string (define (unlex tokens) (typecheck tokens (list-of lexeme?)) (string-concatenate + (map (lambda (x) (cond (x preprocessing-token? => stringify-token) + ((whitespace-token? x) (lexeme-body x)))) + tokens))) + +;; takes a list of preprocessing-token's, and return a "source" string +(define (unlex-aggressive tokens) + (typecheck tokens (list-of lexeme?)) + (string-concatenate (map (lambda (x) (cond ((preprocessing-token? x) (stringify-token x)) ((whitespace-token? x) " "))) @@ -34,4 +41,5 @@ ;; takes a token list, and return a single string literal token (define (stringify-tokens tokens) - (lexeme type: 'preprocessing-token body: `(string-literal ,(unlex tokens)))) + (lexeme type: 'preprocessing-token + body: `(string-literal ,(unlex-aggressive tokens)))) diff --git a/tests/test/cpp/preprocessor2.scm b/tests/test/cpp/preprocessor2.scm index 01c46dff..2d95269a 100644 --- a/tests/test/cpp/preprocessor2.scm +++ b/tests/test/cpp/preprocessor2.scm @@ -30,6 +30,8 @@ stringify-tokens ) ) + :use-module ((c cpp-types) + :select (punctuator-token? identifier-token?)) :use-module (c lex2) ) @@ -61,7 +63,7 @@ (define* (run str optional: (env (make-environment))) (let ((env tokens (handle-preprocessing-tokens env (tokenize str)))) - (remove-noexpand tokens))) + (drop-whitespace-both (remove-noexpand tokens)))) (test-group "Tokens until End Of Line" @@ -133,7 +135,7 @@ (test-group "Parameter list" (test-group "Empty parameter list" (let ((containing remaining nls (parse-parameter-list (lex "()")))) - (test-equal '() containing) + (test-equal '(()) containing) (test-equal '() remaining) (test-equal 0 nls))) @@ -426,6 +428,7 @@ (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") @@ -433,6 +436,8 @@ 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)"))) @@ -745,7 +750,7 @@ wf(x, y) #define X 100")) (test-equal "#undef" - (append (lex "X") (lex "10") (lex "X")) + (lex "X\n10\nX") (run " X #define X 10 @@ -771,3 +776,177 @@ X ;; 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-environment> cpp-if-status: (outside) cpp-variables: #<hash-table 7f6f5974d6a0 2/31> cpp-file-stack: (("*outside*" . 1))> +;; ⇒ (#<<lexeme> type: preprocessing-token body: (identifier "h") noexpand: ("f" "h")> +;; #<<lexeme> type: preprocessing-token body: (punctuator "(") noexpand: ()> +;; #<<lexeme> type: preprocessing-token body: (pp-number "5") noexpand: ()> +;; #<<lexeme> type: preprocessing-token body: (punctuator ")") noexpand: ()>) + +(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() };")) |