C语言的语法是否由CFG完全定义?

时间:2013-10-25 16:58:43

标签: c programming-languages context-free-grammar compiler-construction

我认为这个问题是自给自足的。 C语言的语法是通过Context Free Grammars完全定义的,还是我们有语言构造,在解析过程中可能需要非Context Free定义?

我认为非CFL构造的一个例子是变量在使用之前的声明。但是在Compilers(Aho Ullman Sethi)中,声明C语言不会根据名称来区分标识符。 Lexical Analyzer将所有标识符标记为“id”。 如果C没有完全由CFG定义,那么任何人都可以在C中给出非CFL构造的示例吗?

5 个答案:

答案 0 :(得分:2)

问题是你没有定义“C的语法”。

如果你将它定义为CS意义上的语言C,意思是所有有效C程序的集合,那么C - 以及除了turing tarpits和Lisp之外的几乎所有其他语言 - 都不是上下文无关的。原因是解释 C程序的问题有关(例如,判断a * b;是乘法还是声明)。相反,它只是因为无上下文语法无法帮助您确定给定字符串是否是有效的C程序。即使像int main() { return 0; }这样简单的东西也需要比上下文无关语法更强大的机制,因为你必须(1)记住返回类型,(2)检查return匹配返回类型后发生的事情。 a * b;面临着类似的问题 - 您不需要知道它是否是乘法,但如果它是乘法,则必须是ab类型的有效操作。我不确定上下文敏感语法对于所有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

事实上,标准中的产品甚至没有尝试描述语法上正确的源文件是什么。他们形容:

  1. 源文件的词法结构(以及预处理后有效令牌的词法结构)。

  2. 各个预处理程序指令的语法

  3. 后处理语言语法的超集,它依赖于一些其他机制来区分typedef-nameidentifier的其他用途,以及区分它的机制constant-expression以及conditional-expression的其他用途。

  4. 有许多人认为第3点的问题是“语义”,而不是“句法”。然而,C的性质(甚至更多它的堂兄C ++)是不可能将“语义”与解析程序解开。例如,以下是语法上正确的C程序:

    #define base 7
    #if base * 2 < 10
      &one ?= two*}}
    #endif
    
    int main(void){ return 0; }
    

    因此,如果您的意思是“是CFG定义的C语言的语法”,答案必须是否定的。如果你的意思是,“是否有一个CFG定义某种语言的语法,它代表字符串,这是C语言中程序翻译的中间产品”,答案可能是肯定的,尽管有些人认为必须准确地确定constant-expressiontypedef-name是什么使语法必然与上下文相关,而其他语言则不然。

答案 2 :(得分:0)

这里有两件事:

  1. 语言的结构(语法):这是无上下文的,因为你不需要知道周围环境来弄清楚什么是标识符以及什么是函数。
  2. 程序的含义(语义):这不是上下文,因为你需要知道是否已经声明了一个标识符,当你引用它时它是什么类型。

答案 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语言并且结果很好,但这不是一般解决方案。