Java 是否有歧义语法,需要有关标识符的更多信息?

Does Java has ambiguous syntax which needs more information about an identifier?

注意:这个问题不是关于 "Java do not have pointers"

在C语言中,代码identifier1 * identifier2有歧义,有两种可能的含义:

  1. 如果标识符 1 是一个类型,那么这可能是一个指针声明。
  2. 如果标识符 1 是一个变量,那么这可能是一个乘法语句。

问题是我在构建语法树时无法选择正确的产生式。我检查了Clang的代码,似乎Clang必须将类型检查(通过使用符号table)放到解析阶段(如果我错了请纠正我)。

然后我查看了javac(OpenJDK)的代码,好像在parsing阶段,没有涉及到语义分析。解析器可以几乎不使用标记来构建 AST。

所以我很好奇 Java 是否也有同样的语法歧义问题?如果解析器不知道标识符的类型,就不能选择正确的产生式的问题?

或更通用,Java 是否有语法歧义,解析器无法在没有除令牌流以外的其他信息的情况下选择产生式?

我不这么认为 Java 有这个问题,因为 Java 是强类型的。 此外,Java 不支持指针,因此不会出现上述问题。 我希望这能回答你的问题。

对于语言而言,标记化始终是上下文相关的。但是 Java 没有这么敏感的运算符。但是,您可以以这种方式链接令牌,它会产生歧义,但不仅是作为更大语法语句的一部分:

A < B 可以是 public class A < B > { ... }if (A < B) { ... } 的一部分。 第一个是通用 class 定义,第二个是比较。

这只是我的第一个例子,但我想还有更多。 但是,运算符的定义通常非常狭窄,并且不能重载(如 C/C++ 类语言)。此外,除了 C/C++ 之外,只有一个访问运算符(点:.),只有一个例外(自 Java 8 起,双冒号 ::). 在C++中有一堆,所以不那么混乱。

关于 Java 是否总是句法可判定的具体问题: 是的。一个实施良好的编译器总是可以根据令牌流决定存在什么令牌。

您的问题不容易回答;这取决于您拥有的生产规则。你说:

there's two production:
<pointer> ::= * {<type-qualifier>}* {<pointer>}?
or
<multiplicative-expression> ::= <multiplicative-expression> * <cast-expression>

但这不是唯一可能的解析器!

看的时候用C

foo * bar;

可以是一个名为 bar 的指针,指向 foofoobar 的乘积可以解析为令牌流:

identifier_or_type ASTERISK identifier_or_type SEMICOLON

剩下的就交给解析器了"business logic"。所以这里在 parser 级别完全没有歧义,规则背后的逻辑使两种情况有所不同。

foo.bar.bla.i 这样的表达式不能单独使用语法进行有意义的解析。 foobarbla可以是包名的一部分、静态变量(这个不适用于foo),也可以是一个包名内部 class.

示例:

public class Main {
    public static void main(String[] args) {
        System.out.println(foo.bar.bla.i);
    }
}

package foo;
public class bar {

    public static class bla {
        public static int i = 42;
    }

//  public static NotBla bla = new NotBla();
    public static class NotBla {
        public static int i = 21;
    }
}

当静态变量 bla 是否被注释掉时,这将打印 2142