more correction to match standard
This commit is contained in:
parent
e009f63506
commit
a0c535620e
3 changed files with 110 additions and 38 deletions
40
05/macro_test.c
Normal file
40
05/macro_test.c
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#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
|
||||||
|
|
||||||
|
f(y+1) + f(f(z)) % t(t(g)(0) + t)(1);
|
||||||
|
g(x+(3,4)-w) | h 5) & m(f)^m(m);
|
||||||
|
|
||||||
|
#undef x
|
||||||
|
#undef g
|
||||||
|
#undef z
|
||||||
|
#undef h
|
||||||
|
#undef m
|
||||||
|
#undef w
|
||||||
|
#undef t
|
||||||
|
#undef f
|
||||||
|
|
||||||
|
#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 /* from previous #include example */
|
||||||
|
#define glue(a, b) a ## b
|
||||||
|
#define xglue(a, b) glue(a, b)
|
||||||
|
#define HIGHLOW "hello"
|
||||||
|
#define LOW LOW ", world"
|
||||||
|
|
||||||
|
debug(1, 2);
|
||||||
|
fputs(str(strncmp("abc\0d", "abc", '\4') /* this goes away */
|
||||||
|
== 0) str(: @\n), s);
|
||||||
|
include xstr(INCFILE(2).h)
|
||||||
|
glue(HIGH, LOW);
|
||||||
|
xglue(HIGH, LOW)
|
||||||
|
|
22
05/main.c
22
05/main.c
|
@ -1,22 +0,0 @@
|
||||||
#define z s z
|
|
||||||
z
|
|
||||||
#define f(x) f(2 * x)
|
|
||||||
f(f(4))
|
|
||||||
|
|
||||||
#define STRINGIFY2(x) # x
|
|
||||||
#define STRINGIFY(x) STRINGIFY2(x)
|
|
||||||
#define JOIN2(x,y) x ## y
|
|
||||||
#define JOIN(x,y) JOIN2(x, y)
|
|
||||||
#define X 22
|
|
||||||
|
|
||||||
JOIN(X, X)
|
|
||||||
|
|
||||||
STRINGIFY(X)
|
|
||||||
|
|
||||||
#line 6
|
|
||||||
|
|
||||||
#line 7 "some_file.c"
|
|
||||||
#pragma whatever
|
|
||||||
|
|
||||||
main(void) {
|
|
||||||
}
|
|
|
@ -166,7 +166,8 @@ function split_into_preprocessing_tokens
|
||||||
if c == '# goto pptoken_single_character
|
if c == '# goto pptoken_single_character
|
||||||
if c == '. goto pptoken_dot
|
if c == '. goto pptoken_dot
|
||||||
|
|
||||||
goto bad_pptoken
|
; " each non-white-space character that cannot be one of the above"
|
||||||
|
goto pptoken_single_character
|
||||||
|
|
||||||
:pptoken_comment
|
:pptoken_comment
|
||||||
; emit a space ("Each comment is replaced by one space character.")
|
; emit a space ("Each comment is replaced by one space character.")
|
||||||
|
@ -379,11 +380,11 @@ function split_into_preprocessing_tokens
|
||||||
:str_unterminated_string
|
:str_unterminated_string
|
||||||
string Unterminated string or character literal.
|
string Unterminated string or character literal.
|
||||||
byte 0
|
byte 0
|
||||||
:bad_pptoken
|
; :bad_pptoken
|
||||||
compile_error(filename, line_number, .str_bad_pptoken)
|
; compile_error(filename, line_number, .str_bad_pptoken)
|
||||||
:str_bad_pptoken
|
; :str_bad_pptoken
|
||||||
string Bad preprocessing token.
|
; string Bad preprocessing token.
|
||||||
byte 0
|
; byte 0
|
||||||
:no_newline_at_end_of_file
|
:no_newline_at_end_of_file
|
||||||
compile_error(filename, 0, .str_no_newline_at_end_of_file)
|
compile_error(filename, 0, .str_no_newline_at_end_of_file)
|
||||||
:str_no_newline_at_end_of_file
|
:str_no_newline_at_end_of_file
|
||||||
|
@ -408,9 +409,9 @@ function print_pptokens
|
||||||
p = pptokens
|
p = pptokens
|
||||||
:print_pptokens_loop
|
:print_pptokens_loop
|
||||||
if *1p == 0 goto print_pptokens_loop_end
|
if *1p == 0 goto print_pptokens_loop_end
|
||||||
; putc('{)
|
putc('{)
|
||||||
puts(p)
|
puts(p)
|
||||||
; putc('})
|
putc('})
|
||||||
p += strlen(p)
|
p += strlen(p)
|
||||||
p += 1
|
p += 1
|
||||||
goto print_pptokens_loop
|
goto print_pptokens_loop
|
||||||
|
@ -530,6 +531,8 @@ function translation_phase_4
|
||||||
fputs(2, .str_directive_error)
|
fputs(2, .str_directive_error)
|
||||||
exit(1)
|
exit(1)
|
||||||
:pp_directive_line
|
:pp_directive_line
|
||||||
|
; @NONSTANDARD: we don't do macro replacements in #line directives
|
||||||
|
|
||||||
; at this stage, we just turn #line directives into a nicer format:
|
; at this stage, we just turn #line directives into a nicer format:
|
||||||
; {$line_number filename} e.g. {$77 main.c}
|
; {$line_number filename} e.g. {$77 main.c}
|
||||||
local new_line_number
|
local new_line_number
|
||||||
|
@ -581,6 +584,7 @@ function translation_phase_4
|
||||||
:undef_not_function
|
:undef_not_function
|
||||||
goto process_pptoken
|
goto process_pptoken
|
||||||
:pp_directive_define
|
:pp_directive_define
|
||||||
|
local definition
|
||||||
pptoken_skip(&in)
|
pptoken_skip(&in)
|
||||||
pptoken_skip_spaces(&in)
|
pptoken_skip_spaces(&in)
|
||||||
macro_name = in
|
macro_name = in
|
||||||
|
@ -597,8 +601,19 @@ function translation_phase_4
|
||||||
; copy name
|
; copy name
|
||||||
p = strcpy(p, macro_name)
|
p = strcpy(p, macro_name)
|
||||||
p += 1
|
p += 1
|
||||||
|
|
||||||
|
definition = in
|
||||||
; copy contents
|
; copy contents
|
||||||
memccpy_advance(&p, &in, 10) ; copy until newline
|
memccpy_advance(&p, &in, 10) ; copy until newline
|
||||||
|
|
||||||
|
if in == definition goto objmacro_cont
|
||||||
|
|
||||||
|
; remove terminal space if there is one
|
||||||
|
p -= 2
|
||||||
|
if *1p == 32 goto objmacro_cont
|
||||||
|
p += 2
|
||||||
|
|
||||||
|
:objmacro_cont
|
||||||
*1p = 255 ; replace newline with special "macro end" character
|
*1p = 255 ; replace newline with special "macro end" character
|
||||||
p += 1
|
p += 1
|
||||||
object_macros_size = p - object_macros
|
object_macros_size = p - object_macros
|
||||||
|
@ -640,6 +655,8 @@ function translation_phase_4
|
||||||
p = strcpy(p, macro_name)
|
p = strcpy(p, macro_name)
|
||||||
p += 1
|
p += 1
|
||||||
|
|
||||||
|
definition = in
|
||||||
|
|
||||||
:fmacro_body_loop
|
:fmacro_body_loop
|
||||||
if *1in == 10 goto fmacro_body_loop_end
|
if *1in == 10 goto fmacro_body_loop_end
|
||||||
param_name = param_names
|
param_name = param_names
|
||||||
|
@ -666,6 +683,14 @@ function translation_phase_4
|
||||||
pptoken_skip(&in)
|
pptoken_skip(&in)
|
||||||
goto fmacro_body_loop
|
goto fmacro_body_loop
|
||||||
:fmacro_body_loop_end
|
:fmacro_body_loop_end
|
||||||
|
|
||||||
|
if in == definition goto fmacro_cont
|
||||||
|
; remove terminal space if there is one
|
||||||
|
p -= 2
|
||||||
|
if *1p == 32 goto fmacro_cont
|
||||||
|
p += 2
|
||||||
|
:fmacro_cont
|
||||||
|
|
||||||
*1p = 255
|
*1p = 255
|
||||||
p += 1
|
p += 1
|
||||||
function_macros_size = p - function_macros
|
function_macros_size = p - function_macros
|
||||||
|
@ -744,6 +769,18 @@ function look_up_function_macro
|
||||||
argument name
|
argument name
|
||||||
return look_up_macro(function_macros, name)
|
return look_up_macro(function_macros, name)
|
||||||
|
|
||||||
|
; @NONSTANDARD:
|
||||||
|
; Macro replacement isn't handled properly in the following ways:
|
||||||
|
; - function-like macros are not evaluated if the ( is not on the same line as the name of the macro
|
||||||
|
; - if an object-like macro is defined to a function-like macro, the function-like macro is not evaluated, e.g.:
|
||||||
|
; #define f(x) 2*x
|
||||||
|
; #define g f
|
||||||
|
; g(2) => f(2) rather than 2*2
|
||||||
|
; - when a macro refers to itself, it can be re-evaluated where that shouldn't happen, e.g.
|
||||||
|
; #define z z[0]
|
||||||
|
; #define f(x) x
|
||||||
|
; f(f(z)) => z[0][0] rather than z[0]
|
||||||
|
; These shouldn't be too much of an issue, though.
|
||||||
; replace pptoken(s) at *p_in into *p_out, advancing both
|
; replace pptoken(s) at *p_in into *p_out, advancing both
|
||||||
; NOTE: if *p_in starts with a function-like macro replacement, it is replaced fully,
|
; NOTE: if *p_in starts with a function-like macro replacement, it is replaced fully,
|
||||||
; otherwise this function only reads 1 token from *p_in
|
; otherwise this function only reads 1 token from *p_in
|
||||||
|
@ -794,7 +831,7 @@ function macro_replacement
|
||||||
goto check_banned_objmacros_loop
|
goto check_banned_objmacros_loop
|
||||||
:check_banned_objmacros_loop_end
|
:check_banned_objmacros_loop_end
|
||||||
|
|
||||||
|
:objmacro_replacement
|
||||||
replacement = look_up_object_macro(in)
|
replacement = look_up_object_macro(in)
|
||||||
if replacement == 0 goto no_replacement
|
if replacement == 0 goto no_replacement
|
||||||
|
|
||||||
|
@ -822,7 +859,7 @@ function macro_replacement
|
||||||
:check_banned_fmacros_loop_end
|
:check_banned_fmacros_loop_end
|
||||||
|
|
||||||
replacement = look_up_function_macro(in)
|
replacement = look_up_function_macro(in)
|
||||||
if replacement == 0 goto no_replacement
|
if replacement == 0 goto objmacro_replacement ; not a fmacro, check if it's an objmacro
|
||||||
local macro_name
|
local macro_name
|
||||||
macro_name = in
|
macro_name = in
|
||||||
|
|
||||||
|
@ -848,10 +885,12 @@ function macro_replacement
|
||||||
in += b ; skip argument
|
in += b ; skip argument
|
||||||
c = *1in
|
c = *1in
|
||||||
in += 2 ; skip , or )
|
in += 2 ; skip , or )
|
||||||
pptoken_skip_spaces(&in)
|
|
||||||
*1p = 255
|
*1p = 255
|
||||||
p += 1
|
p += 1
|
||||||
if c == ', goto fmacro_arg_loop
|
if c == ') goto fmacro_arg_loop_end
|
||||||
|
pptoken_skip_spaces(&in)
|
||||||
|
goto fmacro_arg_loop
|
||||||
|
:fmacro_arg_loop_end
|
||||||
*1p = 255 ; use an additional 255-character to mark the end (note: macro arguments may not be empty)
|
*1p = 255 ; use an additional 255-character to mark the end (note: macro arguments may not be empty)
|
||||||
|
|
||||||
; print arguments:
|
; print arguments:
|
||||||
|
@ -878,15 +917,30 @@ function macro_replacement
|
||||||
q = fmacro_get_arg(filename, line_number, arguments, *1p)
|
q = fmacro_get_arg(filename, line_number, arguments, *1p)
|
||||||
*1fmacro_out = '"
|
*1fmacro_out = '"
|
||||||
fmacro_out += 1
|
fmacro_out += 1
|
||||||
; @NONSTANDARD: this doesn't work if the argument contains " or \
|
|
||||||
:fmacro_stringify_loop
|
:fmacro_stringify_loop
|
||||||
c = *1q
|
c = *1q
|
||||||
q += 1
|
q += 1
|
||||||
if c == 255 goto fmacro_stringify_loop_end
|
if c == 255 goto fmacro_stringify_loop_end
|
||||||
|
if c == '\ goto fmacro_stringify_escape
|
||||||
|
if c == '" goto fmacro_stringify_escape
|
||||||
|
if c == 10 goto fmacro_stringify_space ; replace newline with space
|
||||||
|
if c == 32 goto fmacro_stringify_space
|
||||||
if c == 0 goto fmacro_stringify_loop
|
if c == 0 goto fmacro_stringify_loop
|
||||||
*1fmacro_out = c
|
:fmacro_stringify_emit
|
||||||
fmacro_out += 1
|
*1fmacro_out = c
|
||||||
goto fmacro_stringify_loop
|
fmacro_out += 1
|
||||||
|
goto fmacro_stringify_loop
|
||||||
|
:fmacro_stringify_escape
|
||||||
|
*1fmacro_out = '\
|
||||||
|
fmacro_out += 1
|
||||||
|
goto fmacro_stringify_emit
|
||||||
|
:fmacro_stringify_space
|
||||||
|
b = fmacro_out - 1
|
||||||
|
if *1b == 32 goto fmacro_stringify_loop ; don't emit two spaces in a row
|
||||||
|
*1fmacro_out = 32
|
||||||
|
fmacro_out += 1
|
||||||
|
goto fmacro_stringify_loop
|
||||||
|
|
||||||
:fmacro_stringify_loop_end
|
:fmacro_stringify_loop_end
|
||||||
*1fmacro_out = '"
|
*1fmacro_out = '"
|
||||||
fmacro_out += 1
|
fmacro_out += 1
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue