【问题标题】:Compiler Design : Is "variable not declared" a syntactic error or semantic error?编译器设计:“变量未声明”是语法错误还是语义错误?
【发布时间】:2012-10-23 13:25:29
【问题描述】:

在类型检查或解析输入时是否会产生这种类型的错误? 应该在什么类型下解决错误?

【问题讨论】:

  • 这取决于我认为的语言。对于静态类型语言(例如 C、Scala),我会认为它是语法/“句法”错误,并希望它在编译期间发生;在具有动态变量绑定的语言(例如 JavaScript、Ruby)中,我认为这是一个语义错误,并希望它在运行时发生。无论如何,我投了“不具建设性”的票。
  • 那么在静态类型语言中,类型检查是在解析过程中完成的?
  • 解析和类型检查确实应该被视为整个编译过程的两个不同阶段。但是,这将取决于实施;这是可以想象的- ick! - 在解析期间强制执行范围规则。类型检查不仅仅是“范围检查”:不仅必须定义绑定(甚至可能分配一个值),而且类型必须兼容。
  • 糟糕,澄清我的第一条评论:我会将“未声明的变量”归类为语法,而我可以说“不兼容的类型”是静态环境中的语义。
  • 呵呵,有趣的问题。我不同意投票关闭它的人,这是与编程相关且特定的。 (顺便说一句,我在设计我的脚本语言时偶然发现了完全相同的问题 - 我还没有完成它,但我发现这更容易实现为“语义”,运行时类型的错误。)

标签: parsing compiler-construction semantics syntactic-sugar typechecking


【解决方案1】:

该程序符合语言语法,因此在语法上是正确的。语言语法不包含诸如“必须声明标识符”之类的任何语句,并且确实没有任何方法可以这样做。在 Algol-68 项目中,按照这些思路构建两级语法的尝试失败了,据我所知,此后一直没有尝试过。

每个的含义是一个语义问题。 Frank deRemer 将此类问题称为“静态语义”。

【讨论】:

    【解决方案2】:

    对于需要声明标识符的语言(有很多),带有未声明标识符的程序是错误的,因此缺少声明显然是语法错误。

    处理这个问题的常用方法是在符号表中合并有关符号的信息,以便解析器可以使用这些信息。

    以下是标识符类型如何影响解析的几个示例:

    C/C++

    一个经典案例:

    (a)-b;
    

    取决于a,可以是强制转换还是减法:

    #include <stdio.h>
    
    #if TYPEDEF
    typedef double a;
    #else
    double a = 3.0;
    #endif
    
    int main() {
      int b = 3;
      printf("%g\n", (a)-b);
      return 0;
    }
    

    因此,如果 a 根本没有被声明,编译器必须将程序拒绝为语法错误(这正是标准使用的词。)

    XML

    这个很简单:

    <block>Hello, world</blob>
    

    这是格式错误的 XML,但无法使用 CFG 检测到。 (尽管如此,所有 XML 解析器都会正确地拒绝它为格式错误。)在 HTML/SGML 的情况下,在某些明确定义的情况下可能会省略结束标记,解析比较棘手,但仍然具有确定性;同样,标签的精确声明将决定有效输入的解析,并且很容易根据声明提出不同解析的输入。

    英语

    好的,不是编程语言。我有很多其他编程语言示例,但我认为这可能会触发其他一些直觉。

    考虑两个语法正确的句子:

    The sheep is in the meadow.
    The sheep are in the meadow.
    

    现在,怎么样:

    The cow is in the meadow.
    (*) The cow are in the meadow.
    

    第二句是可以理解的,虽然是模棱两可的(是名词还是动词错了?),但它在语法上肯定是不正确的。但是为了知道这一点(以及其他类似的例子),我们必须知道sheep 有一个未标记的复数。确实,许多动物都有无标记的复数,所以我认为以下所有这些都是语法:

    The caribou are in the meadow.
    The antelope are in the meadow.
    The buffalo are in the meadow.
    

    但绝对不是:

    (*) The mouse are in the meadow.
    (*) The bird are in the meadow.
    

    等等


    似乎存在一个普遍的误解,即由于句法分析器使用上下文无关语法分析器,因此语法分析限制解析上下文无关语法。这根本不是真的。

    在 C(和家族)的情况下,语法分析器使用符号表来帮助它解析。在 XML 的情况下,它使用标签堆栈,而在泛化 SGML(包括 HTML)的情况下,它也使用标签声明。因此,作为一个整体考虑的语法分析器比CFG更强大,而CFG只是分析的一部分。

    给定程序通过语法分析的事实并不意味着它在语义上是正确的。例如,语法分析器需要知道a 是否是类型才能正确解析(a)-b,但它不需要知道转换是否实际上是可能的,在a 的情况下是一个类型,或者ab 可以有意义地减去,在a 是一个变量的情况下。这些验证可以在构建解析树后的类型分析期间发生,但它们仍然是编译时错误。

    【讨论】:

    • 所以你是说虽然 syntax 格式不正确(语法上不正确,但语义上不正确),由于解析器的无能,在语义分析期间会捕获错误,并将其视为语法错误?
    • @arg21:编辑了我对 resopnd 的回答,而不是写很长的评论。
    • 在您的 (a)-b 示例中,如果无法进行强制转换(如果 a 是一种类型)会发生什么?它会因为错误的语义而产生编译时语法错误吗?同样在研究了一些关于 YACC 的基本知识之后,我明白了你所说的解析器不限于 CFG 的意思。
    • @arg21,C++ 有更广泛的可能的不良演员表。您可以尝试使用 c++ 编译器,看看会得到什么结果。在 C 语言中,一个可能的错误是:typedef int a[4];,它将生成 error: used type 'a' (aka 'int [4]') where arithmetic or pointer type is required(至少,对于 clang,它通常比 gcc 有更好的错误消息)。
    • 对解析器的语义反馈在许多语言中无疑是必需的,但它的存在不会将语义错误转换为语法错误。
    【解决方案3】:

    在我看来,这并不是严格意义上的语法错误——也不是语义错误。如果我要为静态类型的编译语言(如 C 或 C++)实现此功能,那么我不会将检查放入解析器(因为解析器实际上无法检查此错误),而是放入代码生成器(编译器中遍历抽象语法树并将其转换为汇编代码的部分)。所以在我看来,它介于语法错误和语义错误之间:这是一个语法相关的错误,只能通过对代码进行语义分析来检查。

    但是,如果我们考虑一种原始脚本语言,其中直接执行 AST(无需编译为字节码,也无需 JIT),那么评估器/执行器函数本身会遍历 AST 并找到未声明的变量 - 在这种情况下,这将是一个运行时错误。区别在于“AST_walk()”例程位于程序生命周期的不同部分(编译时间和运行时),语言是脚本还是编译语言。

    【讨论】:

    • 我不认为您可以编写 C++ 编译器而不考虑构建 AST 时的声明。请参阅我对 C 中一个简单案例的回答,但 C++ 中有更复杂的问题。
    • @rici 好吧,你为什么不能呢?我认为您将抽象语法树与抽象语义图混淆了。
    • 那么,如何为 (a)-b 绘制 AST?它适用于语法中两个完全不同的产生式。
    • @rici SUBTRACT(EXPRESSION(TERM a), TERM b),如果这就是你的意思。
    • 如果是CAST(TYPE a, UNARY_MINUS(TERM b)) 呢?
    【解决方案4】:

    在我看来这是一个语义错误,因为即使您使用的是以前未绑定的标识符,您的语言也能很好地解析——即句法分析只检查程序的格式是否正确。语义分析实际上检查您的程序是否具有有效含义——例如绑定,范围或类型。正如@pst 所说,您可以在解析期间进行范围检查,但这是一个实现细节。 AFAIK 旧的编译器曾经这样做是为了节省一些时间和空间,但我认为今天如果你没有一些硬性能/内存限制,这种方法是有问题的。

    【讨论】:

    • 我也同意这是一个语义错误,但是在编译时产生的语法和语义错误之间是否存在任何语言(如 C、Java)分类,或者它们只是作为列表产生有多少错误?
    • 我认为这是一个内部实现细节。例如,GCC 似乎只是报告错误列表,而没有考虑它们是语法还是语义。
    • 感谢 Alex 帮了大忙。我曾经认为语义错误只在运行时报告。我的错。
    • 大多数潜在类型语言(也称为动态语言)就是这种情况。
    • 我认为当 C++(或其他)编译器创建 AST(抽象语法树)时,它会发现该变量不存在,因此它将此类事情视为语法错误。在 C++ 中,在某些情况下,类型或变量名在给定点都是有效的东西。还有一些只有类型是有效的,所以如果你给一个变量的名字或者一个错误的名字,那么你就会有语法错误。我仍然认为找不到符号是语义错误,但我理解在这种情况下“语法错误”消息背后的基本原理。
    猜你喜欢
    • 2012-08-06
    • 1970-01-01
    • 2014-09-22
    • 2011-08-03
    • 1970-01-01
    • 2013-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多