A test file for my ISO C/C++ Preprocessor
Copyright(c) 2008, HuYan
All rights reserved.
Trigraph------------------------------------------------------------------------------------------------------------------------
in normal text
int a ??( ??) = ??< 1, 2, 3 ??>; //equivalent to next line
int a [ ] = { 1, 2, 3 };
in preprocessing directives
??=define MACRO_TRIGRAPH(a,b) a ??=??= b //equivalent to next line
#define MACRO_TRIGRAPH(a,b) a ## b
in comments
/* ??= ??) ??! ??( ??' ??> ??/n ??< ??- */ //equivalent to next line
/* # ] | [ ^ } /n { ~ */
in backslash
ch??/
ar* s="string ??/
literal";
equivalent to
ch/
ar* s="string /
literal";
and equivalent to
char* s="string literal";
Backslash------------------------------------------------------------------------------------------------------------------------
in normal text
ch/
ar* s="string /
literal";
equivalent to
char* s="string literal";
in preprocessing directives
#/
defi/
ne MACRO_/
BACKSLASH/
(a,b) a #/
# b
equivalent to
#define MACRO_BACKSLASH(a,b) a ## b
in comments
//a single-/
line comment
//
/a single-line comment
//
*a multi-line comment*/
/
in the end of file
next backslash is unexpected if it is the last character of this file/
next backslash is unexpected if next newline is the end of this file/
Single-line comment-----------------------------------------------------------------------------------------------------------------------
in characters constant and string literal
'a//b' //a characters constant, not syntax error
"a//b" //4 characters string literal, not syntax error
between tokens (replacement to a space)
m=n//**/o //equivalent to m=n , not equivalent to m=n/o
in header name (error)
#include <file.h//> //undefined behavior, not equivalent to #include <file.h>
#include "file.h//" //undefined behavior, not equivalent to #include <file.h>
attempt defined it as macro (error)
#define SINGLE_LINE_COMMENT //
SINGLE_LINE_COMMENT abc //error, 'abc' not comment
#define GLUE(x,y) x##y
GLUE(/,/) abc //error, 'abc' not comment
Multi-line comment------------------------------------------------------------------------------------------------------------------------
in characters constant and string literal
'a//b' //a characters constant, not syntax error
"a/**/b" //6 characters string literal, not syntax error
between tokens (replacement to a space)
/**/#/**/define/**/MACRO_COMMENT/**/replacement/**/list/**/ //equivalent to next line
# define MACRO_COMMENT replacement list
within punctuator or identifier (error)
i+/**/+ //equivalent to i+ +, not equivalent to i++
#incl/**/ude <file.h> //error, equivalent to #incl ude <file.h>
in header name (error)
#include <file.h/**/> //undefined behavior, not equivalent to #include <file.h>
#include "file.h/**/" //undefined behavior, not equivalent to #include <file.h>
nesting
/*****************************/ //ok
/* // */ //ok,a multi-line comment
/* /**/ */ //error, multi-line comments do not nest ('*/' found outside of comment)
// /*
*/ //error, '*/' found outside of comment
//attempt defined it as macro (error)
#define BEGIN_COMMENT /*
#define END_COMMENT */
BEGIN_COMMENT abc END_COMMENT //error, 'abc' not comment
Identifier----------------------------------------------------------------------------------------------------------------------------------
_ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz //an identifier
/u1234_/uABCD_/U00001234_/U0000abcd //an identifier (contains universal character names)
Preprocessing operator or punctuator--------------------------------------------------------------------------------------------------------
parsing
>>=++++ //three punctuators, '>>=' and two '++'
x+++++y //parsed as x ++ ++ + y (although this expression is invalid), not x ++ + ++ y
reserved operator
#define delete DELETE //error, 'delete' is an operator in C++, not an identifier
Preprocessing number------------------------------------------------------------------------------------------------------------------------
familiar
123L //valid preprocessing number, a long integer
.123 //valid preprocessing number, a floating number
0123 //valid preprocessing number, a octal number
0x123abc //valid preprocessing number, a hexadecimal number
3.14e10 //valid preprocessing number
3.14e+10 //valid preprocessing number
3.14E-10 //valid preprocessing number
definition
123.456._.abc.ABC.e+.e-.E+.E- //valid preprocessing number
.123.456._.abc.ABC.e+.e-.E+.E- //valid preprocessing number
./u1234_/uABCD_/U00001234_/U0000abcd //a preprocessing number (contains universal character names)
+123 //not a preprocessing number
-123 //not a preprocessing number
123+ //not a preprocessing number
123- //not a preprocessing number
importance
#define X123 456
0X123 //'X123' is not an identifier and will not be expanded, because '0X123' is a preprocessing number
Header name-------------------------------------------------------------------------------------------------------------------------------
parsing
#include <file.h>= //'>=' is not a punctuator because <file.h> is a header name
a < b && c > d //< b && c > is not a header name
#if a < b && c > d //< b && c > is not a header name
#endif
matching
#include <file.h //error, missing matched separator
#include "file.h //error, missing matched quotes
#include L"file.h" //error, unexpected 'L'
unexpected characters in header names
#include <' / " // /* > //undefined behavior
#include " ' / // /* " //undefined behavior
processing before string connecting
#define BASE_NAME "file"
#define EXTEND_NAME ".h"
#include BASE_NAME EXTEND_NAME //equivalent to #include "file" ".h" , but not equivalent to #include "file.txt"
Character constant------------------------------------------------------------------------------------------------------------------------
empty character (error)
'' //error
L'' //error
matching
' //error, missing matched single-quotes
L' //error, missing matched single-quotes
'abc/' //error, missing matched single-quotes, /' is only a escape sequence
L'abc/' //error, missing matched single-quotes, /' is only a escape sequence
escape sequences
'/a /b /f /n /r /t /v /' /" /? //' //valid escape sequences
'/c /d /e / /+' //invalid escape sequences
' ? " ' //ok, may represented by ' /? /" '
value range of escape sequences
'/400' //error, too large value of octal escape sequence
'/377' //ok
L'/777' //ok
'/x100' //error, too large value of hexadecimal escape sequence
'/xff' //ok
L'/x10000' //error, too large value of escape hexadecimal sequence
L'/xffff' //ok
String literal----------------------------------------------------------------------------------------------------------------------------
empty string
"" //ok
L"" //ok
matching
" //error, missing matched quotes
L" //error, missing matched quotes
"abc/" //error, missing matched quotes, /" is only a escape sequence
L"abc/" //error, missing matched quotes, /" is only a escape sequence
escape sequences
"/a /b /f /n /r /t /v /" /" /? //" //valid escape sequences
"/c /d /e / /+" //invalid escape sequences
" ? ' " //ok, may represented by " /? /' "
value range of escape sequences
"/400" //error, too large value of octal escape sequence
"/377" //ok
L"/777" //ok
"/x100" //error, too large value of hexadecimal escape sequence
"/xff" //ok
L"/x10000" //error, too large value of escape hexadecimal sequence
L"/xffff" //ok
Universal character name-----------------------------------------------------------------------------------------------------------------------
within identifier
/u1234_/uABCD_/U00001234_/U0000abcd //valid
within character constant
'/u1234_/uABCD_/U00001234_/U0000abcd' //valid
within string literal
"/u1234_/uABCD_/U00001234_/U0000abcd" //valid
within preprocessing number
./u1234_/uABCD_/U00001234_/U0000abcd //valid
value range
/u0065_/U00000065 //error, a universal character name cannot designates a basic source character (uncompleted)
'/u0065 /U00000065' //error, a universal character name cannot designates a basic source character (uncompleted)
"/u0065 /U00000065" //error, a universal character name cannot designates a basic source character (uncompleted)
./u0065_/U00000065 //error, a universal character name cannot designates a basic source character (uncompleted)
/u0019_/u007f_/u009f_/U00000019_/U0000007f_/U0000009f //error, a universal character name cannot less than 0x20, or in the range 0x7F-0x9F
'/u0019 /u007f /u009f /U00000019 /U0000007f /U0000009f' //error, a universal character name cannot less than 0x20, or in the range 0x7F-0x9F
"/u0019 /u007f /u009f /U00000019 /U0000007f /U0000009f" //error, a universal character name cannot less than 0x20, or in the range 0x7F-0x9F
./u0019_/u007f_/u009f_/U00000019_/U0000007f_/U0000009f //error, a universal character name cannot less than 0x20, or in the range 0x7F-0x9F
Preprocessing directive and normal text--------------------------------------------------------------------------------------------------------
null directive
# //no effect
normal text
#define EMPTY
EMPTY #include <file.h> //a text line, will expend to #include <file.h> but not a preprocessing directive
#define INCLUDE1 #include <file.h>
INCLUDE1 //a text line, will expend to #include <file.h> but not a preprocessing directive
#define INCLUDE2 #include
INCLUDE2 <file.h> //a text line, will expend to #include <file.h> but not a preprocessing directive
directive name
#define INCLUDE3 include <file.h>
#INCLUDE3 //error, nonexistent directive name
#define INCLUDE4 include
#INCLUDE4 <file.h> //error, nonexistent directive name
#define pragma error
#pragma "abc" //the directive name is 'pragma' but not 'error', because the replacement was not occured
Source file inclusion---------------------------------------------------------------------------------------------------------------------------
#define MAKE_HEADER(s) #s
#include MAKE_HEADER(file.h) //ok, macro expanded, the header name is "file.h"
//#include "stdio.h" //working but warning
//#line __LINE__ "new.h" //next line is error even if current line is reserved
#include "test.txt" //error, cannot include itselt, though current file name was modified to "new.h" by last line
#include "b.txt" //begin a recursive inclusion, b.txt and c.txt will include each other
Conditional inclusion---------------------------------------------------------------------------------------------------------------------------
syntax checking in skipped groups
#if 0 //checking carefully and responsibly even if in skipped groups
"abc //fogot a quotes
'/400' //too large value of octal escape sequence
#include <d:/file.h> //unexpected '/' character
#define PI3.14 //forgot spaces after PI
#define MAX(a,a) a>b?a:b //careless spelling of second parameter
#define defined otherwise //do not know identifier 'defined' is reserved
#line "file.h" 100 //two arguments reversed
#elsc //careless spelling, follow lines before #endif will be ignored unecpectly
#define PI 3.14 //ignored unecpectly
#endif
unreplaced macros
#define UNREPLACED something
#ifdef UNREPLACED //UNREPLACED will not be expanded
#endif
#ifndef UNREPLACED //UNREPLACED will not be expanded
#endif
#if defined(UNREPLACED) //UNREPLACED will not be expanded
#elif defined(UNREPLACED) //UNREPLACED will not be expanded
#endif
matching
#endif //error, missing matched 'if' branch
#else //error, missing matched 'if' branch
#elif 1 //error, missing matched 'if' branch
#if 0
#elif 0
#else
//#elif 0 //error if this line reserved, 'elif' branch shall not occur after 'else' branch
#endif //dismatch if this line is removed
branch testing
#define A
#ifndef A
#ifdef B
a if
#elif 0
a elif a
#elif 1
a elif b
#else
a else
#endif
#elif 0
#ifdef b
b if
#elif 0
b elif a
#elif 1
b elif b
#else
b else
#endif
#elif 1
#ifdef c
c if
#elif 0
c elif a
#elif 1
c elif b
#else
c else
#endif
#else
#ifdef d
d if
#elif 1
d elif a
#elif 1
d elif b
#else
d else
#endif
#endif
Macro definition-----------------------------------------------------------------------------------------------------------------------------
white separator
#define PI3.14 //invalid directive format, forgeting space after macro name
#define PI()3.14 //invalid directive format, forgeting space after ')'
#define MACRO_MAX (a,b) ((a)>(b)?(a):(b)) //will be defined as object-like macro, not function-like macro
redefinition
#define ERR_PARA(a,a) something //error, parameter 'a' redefined
#define OBJECT_LIKE something
#define OBJECT_LIKE() something //error, cannot redefine object-like macro as function-like macro
#define FUNCTION_LIKE() something
#define FUNCTION_LIKE something //error, cannot redefine function-like macro as object-like macro
#define OBJ_LIKE (1)
#define OBJ_LIKE (0) //error, redefine with different token sequence in replacement list
#define FUNC_LIKE(a) something
#define FUNC_LIKE(b) something //error, redefine with different parameter list
#define MACRO_CONNECT(a,b,c) a##b##c
#define MACRO_CONNECT( a , b , c ) a ## b ## c //ok, the same definition to MACRO_CONNECT (uncompleted)
Macro invocation and replacement-----------------------------------------------------------------------------------------------------------------------------
#define MULTI_ARGS(a,b) b,a
MULTI_ARGS //valid text, but not a macro invocation
arguments number
MULTI_ARGS() //warning, not enough arguments
MULTI_ARGS(1,) //valid, the second argument is empty
MULTI_ARGS(,2) //valid, the first argument is empty
MULTI_ARGS(,) //valid, both arguments are empty
MULTI_ARGS(1,2,3,4) //valid, though arguments too much
whites (spaces, tabs and newlines) within arguments
MULTI_ARGS ( //valid, though whites involved
1 ,
2 )
quotes matching
//MULTI_ARGS(a,b //error, missing matched ')'
//MULTI_ARGS( isdigit(32), isdigit(65) //error, missing matched ')'
MULTI_ARGS( isdigit(32), isdigit(65) ) //valid, four middle parentheses skipped
MULTI_ARGS( MULTI_ARGS(1,2), MULTI_ARGS(3,4) ) //valid, nesting invocation (will expand to 4,3,2,1)
MULTI_ARGS(
1,
#define PI 3.14 //error, preprocessing directives cannot occur within a macro invocation
)
replacement time
#define MACROX 2
#define MACROY MACROX //MACROX does not replaced to 2 now
#undef MACROX
#define MACROX 3
int i=MACROY; //replacement occurs, MACROY will expanded to 3 but not 2
The # operator (string making)----------------------------------------------------------------------------------------------------------------
definition
#define ERR_JIN1(a) # //error, # should be followed by a parameter in the replacement list of a function-like macro (uncompleted)
#define ERR_JIN2(a) a#b //error, # should be followed by a parameter in the replacement list of a function-like macro (uncompleted)
whites (spaces, tabs and newlines) within arguments
#define MKS(s) #s
MKS(abc) //expands to "abc"
MKS( abc ) //expands to "abc", whites before the first token and after the last token within the argument is deleted
MKS(
abc
) //expands to "abc", (uncompleted)
MKS( a
b
c ) //expands to "a b c", each white within the argument becomes a single space
arguments substitution
#define S1(s) #s
#define S2(s) S1(s)
#define MYNAME huyan
S1( MYNAME ) //expands to "MYNAME"
S2( MYNAME ) //expands to "huyan"
#define FORMAT(x) #x "=%d/n",x
#define MYNUM 100
printf( FORMAT(MYNUM) ) //expands to printf( "MYNUM" "=%d/n",100 ), not printf( "100" "=%d/n",100 )
making valid string literal
#define S3(s) #s
printf("%s", S3(a/nb "a/nb" // '//' /n '/n')); //expands to next line
printf("%s", "a/nb /"a//nb/" // '' /n '//n'"); //not next sick line
//printf("%s", "a/nb "a/nb" // '//' /n '/n'"); //error, dismatched quotes
The ## operator (token connection)------------------------------------------------------------------------------------------------------------
definition
#define ERR_JINJIN1(a) ## a //error, ## cannot occur at the beginning of the replacement list
#define ERR_JINJIN2(a) a ## //error, ## cannot occur at the end of the replacement list
#define hash_hash1 ## //error, cannot define ## to a macro directly
#define hash_hash2 # ## # //ok
hash_hash2 //expands to ##
arguments substitution
#define CONNECT(x) I ## x
#define CONNECT2(x) CONNECT(x)
#define S(a) a
#define IS(a) a
int I2=5;
CONNECT( S(1) ) //expands to IS(1), and further expands to 1
CONNECT2( S(2) ) //expands to CONNECT2( 2 ), further expands to CONNECT( 2 ), and last expands to I2
example from ISO C Standard
#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); //equivalent to char p[] = "x ## y";
Complex macro replacement (rescanning and further replacement)---------------------------------------------------------------------------------
#define MACROS(a) a
#define MACROT(b) b*b
MACROS ( MACROT ) ( 3 ) //expands to 3*3
int M1=3;
#define M1 M2
#define M2 M1*M1 //recursive definition
printf("%d/n",M1); //expands to M1*M1 and output 9
#define Puts(s) printf("%s is ",#s); Puts(s)
char translator[]={-70,-6,-47,-27,0};
Puts(translator); //expands to printf("%s is","translator"); Puts(translator);
example from ISO C Standard
#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); //expands to F(2 * (y+1)) + F(2 * (F(2 * (Z[0])))) % F(2 * (0)) + T(1);
G(X+(3,4)-W) | H 5) & M(F)^M(M); //expands to F(2 * (2+(3,4)-0,1)) | F(2 * (~ 5)) & F(2 * (0,1))^M(0,1);
P() i[Q()] = { Q(1), R(2,3), R(4,), R(,5), R(,) }; //expands to int i[] = { 1, 23, 4, 5, };
char c[2][6] = { STR(hello), STR() }; //expands to char c[2][6] = { "hello", "" };
#define Str(s) # s
#define Xstr(s) Str(s)
#define Debug(s, t) printf("x" # s "= %d, x" # t "= %s", x ## s, x ## t)
#define INCFILE(n) vers ## n
#define Glue(a, b) a ## b
#define Xglue(a, b) Glue(a, b)
#define HIGHLOW "hello"
#define LOW LOW ", world"
Debug(1, 2); //expands to printf("x" "1" "= %d, x" "2" "= %s", x1, x2);
fputs(Str(strncmp("abc/0d", "abc", '/4') == 0) Str(: @/n), s); //expands to fputs("strncmp(/"abc//0d/", /"abc/", '//4') == 0" ": @/n",s);
#include Xstr(INCFILE(2).h) //expands to #include "vers2.h"
Glue(HIGH, LOW); //expands to "hello";
Xglue(HIGH, LOW) //expands to "hello" ", world"
Macro with variable arguments-----------------------------------------------------------------------------------------------------------------------
definition
#define ERR_VAM1 __VA_ARGS__ //error, unexpected __VA_ARGS__ in the replacement list of normal macro
#define ERR_VAM2(a,b,c) __VA_ARGS__ //error, unexpected __VA_ARGS__ in the replacement list of normal macro
#define ERR_VAM3(a,b,c,...) a,b,c //warning, missing __VA_ARGS__ in the replacement list of variable arguments macro
#define ERR_VAM4(__VA_ARGS__) something //error, the parameter cannot be __VA_ARGS__
#define ERR_VAM5(__VA_ARGS__,...) __VA_ARGS__ //error, the parameter cannot be __VA_ARGS__
#define __VA_ARGS__ otherwise //error, the identifier '__VA_ARGS__' is reserved
#undef __VA_ARGS__ //error, the identifier '__VA_ARGS__' is reserved
#define VAM1(...) __VA_ARGS__ //ok
#define VAM2(a,b,c,...) __VA_ARGS__ //ok
arguments substitution
#define VAM3(...) #__VA_ARGS__
VAM3() //expands to ""
VAM3(1) //expands to "1"
VAM3(a,b,c) //expands to "a,b,c"
#define VAM4(a,b,...) a b #__VA_ARGS__
VAM4(1) //warning, not enough arguments
VAM4(1,2) //expands to 1 2 ""
VAM4(1,2,3,4,5) //expands to 1 2 "3,4,5"
VAM4( ((a), (b)), ((c), (d)), ((e),(f)), ((g),(h)) ) //expands to ((a), (b)) ((c), (d)) "((e),(f)), ((g),(h))"
example from ISO C Standard
#define Debug2(...) fprintf(stderr, __VA_ARGS__)
#define Showlist(...) puts(#__VA_ARGS__)
#define Report(test, ...) ((test)?puts(#test):printf(__VA_ARGS__))
Debug2("Flag"); //expands to fprintf(stderr, "Flag" );
Debug2("x = %d/n", x); //expands to fprintf(stderr, "x = %d/n", x );
Showlist(The first, second, and third items.); //expands to puts( "The first, second, and third items." );
Report(x>y, "x is %d but y is %d", x, y); //expands to ((x>y)?puts("x>y"):printf("x is %d but y is %d", x, y));
Predefined macro names and reserved identifiers------------------------------------------------------------------------------------------------
following predefined macros will be expanded
__DATE__
__FILE__
__LINE__
__STDC__
__STDC_HOSTED__
__STDC_VERSION__
__TIME__
redefine and undefine (error)
#define __DATE__ otherwise //error, cannot redefine predefined macros
#define defined otherwise //error, the identifier 'defined' is reserved
#undef __DATE__ //error, cannot undefine predefined macros
#undef defined //error, the identifier 'defined' is reserved
Line control-----------------------------------------------------------------------------------------------------------------------------------
//#line 100 //set next line number to 100
//#line 200 "file1.h" //set next line number to 200 and current file name to "file1.h"
expanded macro arguments
#define LINE_NUMBER1 300
//#line LINE_NUMBER1 //set next line number to 300 (the macro LINE_NUMBER1 expanded)
#define LINE_NUMBER2 400
//#line LINE_NUMBER2 "file2.h" //set next line number to 400 and current file name to "file2.h"
#define FILE_NAME "file3.h"
//#line 500 FILE_NAME //set next line number to 500 and current file name to "file3.h"
#define LINE_FILE 600 "test.h"
//#line LINE_FILE //set next line number to 600 and current file name to "test.h"
arguments format
#line 0 //error, the line number cannot less than 1
#line 2147483648 //error, the line number cannot more than 2147483647
#line +100 //error, the line number need not prefixed by '+' or '-'
#line "file.h" 100 //error, the first argument shall be a number (after macro replacement)
#line 100 L"file.h" //error, the file name need not prefixed by 'L' (cannot be wide string literal)
//#line 100 "file.h/" //error, missing matched quotes, the /" is only a escape sequence
counting of line number
//#line 100 //set next line number to 100, but next broken '__LINE__' will expands to 101
__LINE/
__ ;
Error directive------------------------------------------------------------------------------------------------------------------------------
//#error //stop preprocessing
//#error message1 //stop preprocessing and output 'message1'
expanded macro arguments
#define MESSAGE2 message2
//#error MESSAGE2 //macro expanded, stop preprocessing and output 'message2'
#define MAX_VALUE 100
//#error the "MAX_VALUE" cannot more than MAX_VALUE //stop preprocessing and output 'the "MAX_VALUE" cannot more than 100'
Pragma directive-----------------------------------------------------------------------------------------------------------------------------
#pragma some_instructions //valid, but all #pragma directives will be ignored and removed
_Pragma operator (uncompleted)
_Pragma ("string literal") //it will regard as normal text
[The End]