仅对任意文件进行预处理器?
Preprocessor only on arbitrary file?
我想证明预处理器完全独立于构建过程。它是不同于 C 语言的另一种文法和词法分析器。事实上,我想证明预处理器可以应用于任何类型的文件。
所以我有这个任意文件:
#define FOO
#ifdef FOO
I am foo
#endif
#
#
#
Something
#pragma Hello World
我认为这可行:
$ gcc -E test.txt -o-
gcc: warning: test.txt: linker input file unused because linking not done
不幸的是,它只适用于此:
$ cat test.txt | gcc -E -
为什么 GCC 会出现这个错误?
你需要告诉gcc
这是一个C文件。
gcc -xc -E test.txt
您可以使用 C 预处理器,cpp
(或更传统的形式,/lib/cpp
):
cpp test.txt
或
/lib/cpp test.txt
C 编译器使用文件名后缀作为必须编译的文件(以 .c
结尾)和只需要 linked 的文件(以 .o
或 .so
) 对于以 .s
结尾的文件,它调用汇编程序 as(1)
,对于以 .f
结尾的文件,它调用 fortran 编译器,对于 .cc
它切换到 C++ 编译。
实际上,通常情况下,C 编译器会将它们不匹配的所有内容都作为 linker 文件,因此一旦您将 linker 文件传递给它,它就会尝试 link它,调用 linker ld(1)
。这就是您的 .txt
文件所发生的情况。 linker 有一些类似的方法来识别 ld(1)
针对对象或共享对象文件的脚本。
顺便说一句,CPP 语言确实是一种宏语言,但与C 有一些无法避免的相似之处。它至少必须识别 C 标识符,因为宏名称与 C 标识符具有相同的语法,并且它必须检查标识符是否与宏名称匹配。另一方面...它必须识别 C 注释和 C 字符串(它确实消除了编译器的注释),因为宏扩展不会在它们内部扩展,它还必须识别括号(它们被认为是宏参数检测和 ,
符号,用于分隔参数)。它还识别(在宏字符串内)标记 #
(将参数字符串化)和 ##
(将两个符号连接并合并为一个)(最后一个运算符必须强制 cpp 识别几乎任何 C令牌,因为如果您尝试将 +##+
之类的内容合并到 ++
中,它必须检查错误)
所以,结论是:cpp不一定要把整个C语法都实现为一种语言,但必须识别C语言的token几乎完全。 C 语言的标准强制 c 预处理器对输入进行标记,因此 ##
运算符可用于合并标记(并检查有效性)这意味着,如果您定义如下宏:
#define M(p) +p
然后你这样称呼它:
a = +M(-c);
您将得到类似于以下的字符串:
a = + +-c;
在输出中(它将在两个 +
符号之间插入一个 space,因此它们不会合并到 ++
运算符中。符号 +
和 -
放在一起,因为它们永远不会作为一个标记被扫描)见下一个示例(输入前面有 >
符号)
$ cpp - <<EOF
> #define M(p) +p
> a = +M(p);
> b = -M(p);
> p = +M(+p);
> p = +M(-p);
> EOF
# 1 "<stdin>"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 346 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "<stdin>" 2
a = + +p;
b = -+p;
p = + + +p;
p = + +-p;
另一个例子将显示解析标记的更多困难(输入用 >
分隔,stderr 用 >>
分隔,stdout 不带引号):
$ cpp - <<EOF
#define M(a,b) a##b
> a = M(a+,+b)
> a = M(a+,-b)
> a = M(a,+b)
> a = M(a,b)
> a = M(a,300)
> a = M(a,300.2)
> EOF
>> <stdin>:3:5: error: pasting formed '+-', an invalid preprocessing token
>> a = M(a+,-b)
>> ^
>> <stdin>:1:17: note: expanded from macro 'M'
>> #define M(a,b) a##b
>> ^
>> <stdin>:4:5: error: pasting formed 'a+', an invalid preprocessing token
>> a = M(a,+b)
>> ^
>> <stdin>:1:17: note: expanded from macro 'M'
>> #define M(a,b) a##b
>> ^
>> <stdin>:7:5: error: pasting formed 'a300.2', an invalid preprocessing token
>> a = M(a,300.2)
>> ^
>> <stdin>:1:17: note: expanded from macro 'M'
>> #define M(a,b) a##b
>> ^
>> 3 errors generated.
# 1 "<stdin>"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 346 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "<stdin>" 2
a = a++b
a = a+-b
a = a+b
a = ab
a = a300
a = a 300.2
正如您在此示例中看到的那样,合并 a
和 300
没问题,因为一个标记构成了一个标识符,它是有效的并且 cpp(1)
不会抱怨,但是当合并 a
和 300.2
时,生成的标记 a300.2
在 C 中不是有效标记,因此它被拒绝(它也没有加入并且工具插入一个 space,使编译器将两个标记视为单独的---如果将它们连接在一起,它们将被扫描为标记 a300
和 .2
).
如果要使用独立于语言的宏预处理器,请考虑使用 m4(1)
作为宏语言。它在很多方面都比 cpp
强大得多。但要注意,由于它允许的宏扩展的复杂性,它很难学习。
我想证明预处理器完全独立于构建过程。它是不同于 C 语言的另一种文法和词法分析器。事实上,我想证明预处理器可以应用于任何类型的文件。
所以我有这个任意文件:
#define FOO
#ifdef FOO
I am foo
#endif
#
#
#
Something
#pragma Hello World
我认为这可行:
$ gcc -E test.txt -o-
gcc: warning: test.txt: linker input file unused because linking not done
不幸的是,它只适用于此:
$ cat test.txt | gcc -E -
为什么 GCC 会出现这个错误?
你需要告诉gcc
这是一个C文件。
gcc -xc -E test.txt
您可以使用 C 预处理器,cpp
(或更传统的形式,/lib/cpp
):
cpp test.txt
或
/lib/cpp test.txt
C 编译器使用文件名后缀作为必须编译的文件(以 .c
结尾)和只需要 linked 的文件(以 .o
或 .so
) 对于以 .s
结尾的文件,它调用汇编程序 as(1)
,对于以 .f
结尾的文件,它调用 fortran 编译器,对于 .cc
它切换到 C++ 编译。
实际上,通常情况下,C 编译器会将它们不匹配的所有内容都作为 linker 文件,因此一旦您将 linker 文件传递给它,它就会尝试 link它,调用 linker ld(1)
。这就是您的 .txt
文件所发生的情况。 linker 有一些类似的方法来识别 ld(1)
针对对象或共享对象文件的脚本。
顺便说一句,CPP 语言确实是一种宏语言,但与C 有一些无法避免的相似之处。它至少必须识别 C 标识符,因为宏名称与 C 标识符具有相同的语法,并且它必须检查标识符是否与宏名称匹配。另一方面...它必须识别 C 注释和 C 字符串(它确实消除了编译器的注释),因为宏扩展不会在它们内部扩展,它还必须识别括号(它们被认为是宏参数检测和 ,
符号,用于分隔参数)。它还识别(在宏字符串内)标记 #
(将参数字符串化)和 ##
(将两个符号连接并合并为一个)(最后一个运算符必须强制 cpp 识别几乎任何 C令牌,因为如果您尝试将 +##+
之类的内容合并到 ++
中,它必须检查错误)
所以,结论是:cpp不一定要把整个C语法都实现为一种语言,但必须识别C语言的token几乎完全。 C 语言的标准强制 c 预处理器对输入进行标记,因此 ##
运算符可用于合并标记(并检查有效性)这意味着,如果您定义如下宏:
#define M(p) +p
然后你这样称呼它:
a = +M(-c);
您将得到类似于以下的字符串:
a = + +-c;
在输出中(它将在两个 +
符号之间插入一个 space,因此它们不会合并到 ++
运算符中。符号 +
和 -
放在一起,因为它们永远不会作为一个标记被扫描)见下一个示例(输入前面有 >
符号)
$ cpp - <<EOF
> #define M(p) +p
> a = +M(p);
> b = -M(p);
> p = +M(+p);
> p = +M(-p);
> EOF
# 1 "<stdin>"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 346 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "<stdin>" 2
a = + +p;
b = -+p;
p = + + +p;
p = + +-p;
另一个例子将显示解析标记的更多困难(输入用 >
分隔,stderr 用 >>
分隔,stdout 不带引号):
$ cpp - <<EOF
#define M(a,b) a##b
> a = M(a+,+b)
> a = M(a+,-b)
> a = M(a,+b)
> a = M(a,b)
> a = M(a,300)
> a = M(a,300.2)
> EOF
>> <stdin>:3:5: error: pasting formed '+-', an invalid preprocessing token
>> a = M(a+,-b)
>> ^
>> <stdin>:1:17: note: expanded from macro 'M'
>> #define M(a,b) a##b
>> ^
>> <stdin>:4:5: error: pasting formed 'a+', an invalid preprocessing token
>> a = M(a,+b)
>> ^
>> <stdin>:1:17: note: expanded from macro 'M'
>> #define M(a,b) a##b
>> ^
>> <stdin>:7:5: error: pasting formed 'a300.2', an invalid preprocessing token
>> a = M(a,300.2)
>> ^
>> <stdin>:1:17: note: expanded from macro 'M'
>> #define M(a,b) a##b
>> ^
>> 3 errors generated.
# 1 "<stdin>"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 346 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "<stdin>" 2
a = a++b
a = a+-b
a = a+b
a = ab
a = a300
a = a 300.2
正如您在此示例中看到的那样,合并 a
和 300
没问题,因为一个标记构成了一个标识符,它是有效的并且 cpp(1)
不会抱怨,但是当合并 a
和 300.2
时,生成的标记 a300.2
在 C 中不是有效标记,因此它被拒绝(它也没有加入并且工具插入一个 space,使编译器将两个标记视为单独的---如果将它们连接在一起,它们将被扫描为标记 a300
和 .2
).
如果要使用独立于语言的宏预处理器,请考虑使用 m4(1)
作为宏语言。它在很多方面都比 cpp
强大得多。但要注意,由于它允许的宏扩展的复杂性,它很难学习。