我认为这个问题是自给自足的。 C语言的语法是通过Context Free Grammars完全定义的,还是我们有语言构造,在解析过程中可能需要非Context Free定义?
我认为非CFL构造的一个例子是变量在使用之前的声明。但是在Compilers(Aho Ullman Sethi)中,声明C语言不会根据名称来区分标识符。 Lexical Analyzer将所有标识符标记为“id”。 如果C没有完全由CFG定义,那么任何人都可以在C中给出非CFL构造的示例吗?
答案 0 :(得分:2)
问题是你没有定义“C的语法”。
如果你将它定义为CS意义上的语言C,意思是所有有效C程序的集合,那么C - 以及除了turing tarpits和Lisp之外的几乎所有其他语言 - 都不是上下文无关的。原因是不与解释 C程序的问题有关(例如,判断a * b;
是乘法还是声明)。相反,它只是因为无上下文语法无法帮助您确定给定字符串是否是有效的C程序。即使像int main() { return 0; }
这样简单的东西也需要比上下文无关语法更强大的机制,因为你必须(1)记住返回类型,(2)检查return
匹配返回类型后发生的事情。 a * b;
面临着类似的问题 - 您不需要知道它是否是乘法,但如果它是乘法,则必须是a
和b
类型的有效操作。我不确定上下文敏感语法对于所有C是否足够,因为对有效C程序的一些限制非常微妙,即使你排除未定义的行为(其中一些甚至可能是不可判定的) )。
当然,上述观点几乎没有用。一般来说,在谈论语法时,我们只对一个有效程序的非常好的近似感兴趣:我们想要一个语法来排除尽可能多的不是C的字符串而不会在语法中产生过多的复杂性(例如,{{ 1}}或1 a
)。其他所有内容都留给编译器的后期阶段,并调用语义错误或类似的东西,以区别于第一类错误。 这些“近似”语法几乎总是无上下文语法(包括在C的情况下),所以如果你想调用有效程序集“语法”的近似值,C确实是由上下文定义的自由语法。很多人都这样做,所以你会和你在一起。
答案 1 :(得分:1)
语言标准定义的C
语言包括预处理器。以下是语法上正确的C程序:
#define START int main(
#define MIDDLE ){
START int argc, char** argv MIDDLE return 0; }
似乎真的很有诱惑力回答这个问题(出现了很多)“肯定,有一个CFG for C”,基于提取标准中的语法子集,该语法本身是模棱两可的并且可以识别语言的超集。这个CFG很有意思,甚至很有用,但它不是C 。
事实上,标准中的产品甚至没有尝试描述语法上正确的源文件是什么。他们形容:
源文件的词法结构(以及预处理后有效令牌的词法结构)。
各个预处理程序指令的语法
后处理语言语法的超集,它依赖于一些其他机制来区分typedef-name
和identifier
的其他用途,以及区分它的机制constant-expression
以及conditional-expression
的其他用途。
有许多人认为第3点的问题是“语义”,而不是“句法”。然而,C的性质(甚至更多它的堂兄C ++)是不可能将“语义”与解析程序解开。例如,以下是语法上正确的C程序:
#define base 7
#if base * 2 < 10
&one ?= two*}}
#endif
int main(void){ return 0; }
因此,如果您的意思是“是CFG定义的C语言的语法”,答案必须是否定的。如果你的意思是,“是否有一个CFG定义某种语言的语法,它代表字符串,这是C语言中程序翻译的中间产品”,答案可能是肯定的,尽管有些人认为必须准确地确定constant-expression
和typedef-name
是什么使语法必然与上下文相关,而其他语言则不然。
答案 2 :(得分:0)
这里有两件事:
答案 3 :(得分:0)
C语言的语法是通过Context Free Grammars完全定义的吗?
是的。这是BNF中C的语法:
http://www.cs.man.ac.uk/~pjj/bnf/c_syntax.bnf
如果您在任何规则的左侧看不到一个符号,那么该语法不受上下文限制。这就是context free grammars (Wikipedia)的定义:
在形式语言理论中,无上下文语法(CFG)是一种形式语法,其中每个生成规则的形式都是
V → w
其中V是单个非终结符号,w是一串终端和/或非终结符号(w可以为空)。
由于其他人提到歧义,我想澄清一下。想象一下以下语法:
A -> B x | C x
B -> y
C -> y
这是一个含糊不清的语法。但是,它仍然是一个无上下文语法。这两个是完全不同的概念。
显然,C的语义分析器是上下文敏感的。来自重复问题的This answer有进一步的解释。
答案 4 :(得分:0)
如果您的“C语法”是指某些C编译器接受的所有有效C字符串,并且在运行预处理器之后,但忽略输入错误,那么这就是答案:是但并非毫不含糊。
首先,您可以假设输入程序是根据C标准进行标记化的。语法将描述这些令牌之间的关系,而不是裸字符。这种无上下文的语法可以在关于C的书籍和使用解析器生成器的实现中找到。这种标记化是一个很大的假设,因为相当一些工作进入“lexing”C。所以,我认为如果我们没有使用无上下文语法描述词汇级别,我们还没有用无上下文语法描述C 。标记生成器和解析器之间的分段与扫描器生成器(更喜欢关键字,最长匹配等)所产生的排序相结合,是计算能力的一个主要增长,这在无上下文语法中是不容易模拟的。
所以,如果你不假设一个tokenizer,例如可以使用符号表来区分类型名称和变量名称,那么无上下文语法就会变得更难。然而:这里的诀窍是接受歧义。我们可以完全描述C的语法,包括其在无上下文语法中的标记。只有语法是模糊的,并对同一个字符串产生不同的解释。例如,对于A *a;
,它将具有乘法和指针声明的推导。没问题,语法仍然按照您的要求描述C语法,而不是毫不含糊。
请注意,我们假设首先运行预处理器,我相信您的问题与预处理前的代码无关。描述使用无上下文语法将是疯狂的,因为语法正确性取决于扩展用户定义的宏的语义。基本上,程序员每次定义宏时都会扩展C语言的语法。在CWI,我们为C语言编写了无上下文语法,给出了一组已知的宏定义来扩展C语言并且结果很好,但这不是一般解决方案。