summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--05/macro_test.c40
-rw-r--r--05/main.c22
-rw-r--r--05/preprocess.b86
3 files changed, 110 insertions, 38 deletions
diff --git a/05/macro_test.c b/05/macro_test.c
new file mode 100644
index 0000000..688c45e
--- /dev/null
+++ b/05/macro_test.c
@@ -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)
+
diff --git a/05/main.c b/05/main.c
index 102f3b1..e69de29 100644
--- a/05/main.c
+++ b/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) {
-}
diff --git a/05/preprocess.b b/05/preprocess.b
index 9c89a3e..ae4e5c5 100644
--- a/05/preprocess.b
+++ b/05/preprocess.b
@@ -166,7 +166,8 @@ function split_into_preprocessing_tokens
if c == '# goto pptoken_single_character
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
; emit a space ("Each comment is replaced by one space character.")
@@ -379,11 +380,11 @@ function split_into_preprocessing_tokens
:str_unterminated_string
string Unterminated string or character literal.
byte 0
- :bad_pptoken
- compile_error(filename, line_number, .str_bad_pptoken)
- :str_bad_pptoken
- string Bad preprocessing token.
- byte 0
+; :bad_pptoken
+; compile_error(filename, line_number, .str_bad_pptoken)
+; :str_bad_pptoken
+; string Bad preprocessing token.
+; byte 0
:no_newline_at_end_of_file
compile_error(filename, 0, .str_no_newline_at_end_of_file)
:str_no_newline_at_end_of_file
@@ -408,9 +409,9 @@ function print_pptokens
p = pptokens
:print_pptokens_loop
if *1p == 0 goto print_pptokens_loop_end
- ; putc('{)
+ putc('{)
puts(p)
- ; putc('})
+ putc('})
p += strlen(p)
p += 1
goto print_pptokens_loop
@@ -530,6 +531,8 @@ function translation_phase_4
fputs(2, .str_directive_error)
exit(1)
: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:
; {$line_number filename} e.g. {$77 main.c}
local new_line_number
@@ -581,6 +584,7 @@ function translation_phase_4
:undef_not_function
goto process_pptoken
:pp_directive_define
+ local definition
pptoken_skip(&in)
pptoken_skip_spaces(&in)
macro_name = in
@@ -597,8 +601,19 @@ function translation_phase_4
; copy name
p = strcpy(p, macro_name)
p += 1
+
+ definition = in
; copy contents
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
p += 1
object_macros_size = p - object_macros
@@ -640,6 +655,8 @@ function translation_phase_4
p = strcpy(p, macro_name)
p += 1
+ definition = in
+
:fmacro_body_loop
if *1in == 10 goto fmacro_body_loop_end
param_name = param_names
@@ -666,6 +683,14 @@ function translation_phase_4
pptoken_skip(&in)
goto fmacro_body_loop
: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
p += 1
function_macros_size = p - function_macros
@@ -744,6 +769,18 @@ function look_up_function_macro
argument 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
; 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
@@ -794,7 +831,7 @@ function macro_replacement
goto check_banned_objmacros_loop
:check_banned_objmacros_loop_end
-
+ :objmacro_replacement
replacement = look_up_object_macro(in)
if replacement == 0 goto no_replacement
@@ -822,7 +859,7 @@ function macro_replacement
:check_banned_fmacros_loop_end
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
macro_name = in
@@ -848,10 +885,12 @@ function macro_replacement
in += b ; skip argument
c = *1in
in += 2 ; skip , or )
- pptoken_skip_spaces(&in)
*1p = 255
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)
; print arguments:
@@ -878,15 +917,30 @@ function macro_replacement
q = fmacro_get_arg(filename, line_number, arguments, *1p)
*1fmacro_out = '"
fmacro_out += 1
- ; @NONSTANDARD: this doesn't work if the argument contains " or \
:fmacro_stringify_loop
c = *1q
q += 1
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
- *1fmacro_out = c
- fmacro_out += 1
- goto fmacro_stringify_loop
+ :fmacro_stringify_emit
+ *1fmacro_out = c
+ 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
*1fmacro_out = '"
fmacro_out += 1