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

C语言的语法是否完全由CFGs定义?,c,programming-languages,context-free-grammar,compiler-construction,C,Programming Languages,Context Free Grammar,Compiler Construction,我认为这个问题是自给自足的。C语言的语法是完全通过上下文无关语法定义的,还是在解析过程中有可能需要非上下文无关定义的语言结构 我认为非CFL构造的一个例子是在使用变量之前声明变量。但是在编译器(Aho Ullman Sethi)中,有一种说法是C语言不根据标识符的名称来区分标识符。词法分析器将所有标识符标记为“id”。 如果C不是完全由CFGs定义的,请任何人给出C中非CFL构造的示例?这里有两件事: 语言的结构(语法):这是上下文无关的,因为你不需要知道周围的环境来确定什么是标识符,什么是函数

我认为这个问题是自给自足的。C语言的语法是完全通过上下文无关语法定义的,还是在解析过程中有可能需要非上下文无关定义的语言结构

我认为非CFL构造的一个例子是在使用变量之前声明变量。但是在编译器(Aho Ullman Sethi)中,有一种说法是C语言不根据标识符的名称来区分标识符。词法分析器将所有标识符标记为“id”。
如果C不是完全由CFGs定义的,请任何人给出C中非CFL构造的示例?

这里有两件事:

  • 语言的结构(语法):这是上下文无关的,因为你不需要知道周围的环境来确定什么是标识符,什么是函数
  • 程序的含义(语义):这不是上下文无关的,因为您需要知道标识符是否已声明,以及引用它时使用的类型
  • C语言的语法是否完全通过上下文无关语法定义

    是的。这是BNF中C的语法:

    如果在任何规则的左侧只看到一个符号,那么语法是上下文无关的。这正是以下定义:

    在形式语言理论中,上下文无关语法(CFG)是一种形式语法,其中每个产生式规则都是这种形式

    V → w
    
    其中V是单个非终端符号,w是终端和/或非终端的字符串(w可以为空)


    由于其他人提到模棱两可,我想澄清一点。想象一下下面的语法:

    A -> B x | C x
    B -> y
    C -> y
    
    这是一个模棱两可的语法。然而,它仍然是一种上下文无关的语法。这两个概念完全不同



    显然,C的语义分析器是上下文敏感的。重复的问题有进一步的解释。

    问题是您没有定义“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的字符串,而不会在语法中产生过多的复杂性(例如,
    1A
    )。其他一切都留待编译器的后期阶段处理,称为语义错误或类似错误,以区别于第一类错误。这些“近似”语法几乎总是与上下文无关的语法(包括C的语法),因此,如果您想将有效程序集的这种近似称为“语法”,C实际上是由上下文无关语法定义的。很多人都这样做,所以你会有好朋友。

    语言标准定义的
    C
    语言包括预处理器。以下是语法正确的C程序:

    #define START int main(
    #define MIDDLE ){
    
    START int argc, char** argv MIDDLE return 0; }
    
    #define base 7
    #if base * 2 < 10
      &one ?= two*}}
    #endif
    
    int main(void){ return 0; }
    
    在提取标准中语法子集的基础上,回答这个问题(经常出现)“当然,C有一个CFG”,这似乎很有诱惑力,因为标准中的语法本身是模糊的,可以识别语言的超集。CFG很有趣,甚至很有用,但它不是C

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

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

  • 单个预处理器指令的语法

  • 后处理语言语法的超集,它依赖于某种其他机制来区分
    类型定义名称
    标识符
    的其他用途,以及区分
    常量表达式
    条件表达式
    的其他用途

  • 有许多人认为,第3点中的问题是“语义”,而不是“句法”。然而,C的本质(更重要的是它的表亲C++)是不可能从程序的解析中分离出“语义”。例如,以下是语法正确的C程序:

    #define START int main(
    #define MIDDLE ){
    
    START int argc, char** argv MIDDLE return 0; }
    
    #define base 7
    #if base * 2 < 10
      &one ?= two*}}
    #endif
    
    int main(void){ return 0; }
    
    #定义基础7
    #如果基*2<10
    &一个?=两个*}
    #恩迪夫
    int main(void){返回0;}
    
    因此,如果你真的是说“C语言的语法是由CFG定义的”,答案一定是否定的。如果你是说,“是否有一个CFG定义了某种语言的语法,它代表了字符串,这些字符串是C语言程序翻译的中间产物”,答案可能是肯定的,尽管有些人认为有必要精确定义
    常量表达式和
    类型