more correction to match standard

This commit is contained in:
pommicket 2022-01-10 11:46:19 -05:00
parent e009f63506
commit a0c535620e
3 changed files with 110 additions and 38 deletions

40
05/macro_test.c Normal file
View 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)

View file

@ -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) {
}

View file

@ -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