注意:这个问题不是关于"Java没有指针">
在 C 语言中,代码identifier1 * identifier2
在两个可能的含义上是模棱两可的:
- 如果标识符 1 是类型,则这可能是指针声明。
- 如果标识符 1 是一个变量,那么这可能是一个乘法语句。
问题是我在构建语法树时无法选择正确的生产。我检查了 Clang 的代码,似乎 Clang 必须将类型检查(通过使用符号表)放入解析阶段(如果我错了,请纠正我)。
然后我检查了javac(OpenJDK)的代码,似乎在解析阶段,不涉及语义分析。解析器可以几乎不使用令牌构建 AST。
所以我很好奇Java是否有同样的模棱两可的语法问题?如果解析器不知道标识符的类型,它就不能选择正确的生产?
或者更通用地说,Java 是否有语法模糊,解析器无法选择没有其他信息而不是令牌流的生产?
对于语言,标记化始终与上下文相关。但是,Java没有如此敏感的运算符。但是,您可以以这样的方式链接令牌,以产生歧义,但不仅仅是作为更大的语法语句的一部分:
A < B
可以是public class A < B > { ... }
或if (A < B) { ... }
的一部分。 第一个是泛型类定义,第二个是比较。
这只是我帽子顶部的第一个例子,但我认为还有更多。 但是,运算符通常定义得非常狭窄,并且不能(如在类似 C/C++ 的语言中)重载。此外,除了在C/C++中只有一个访问器运算符(点:.
),只有一个例外(从Java 8开始,双冒号::
)。 在C++有一堆,所以它不那么混乱。
关于Java是否总是在语法上可判定的具体问题: 是的。实现良好的编译器始终可以根据令牌流决定存在哪个令牌。
我不这么认为Java有这个问题,因为Java是强类型的。 此外,Java不支持指针,因此不会出现上述问题。 我希望这能回答你的问题。
您的问题不容易回答;这取决于您拥有的生产规则。你说:
there's two production:
<pointer> ::= * {<type-qualifier>}* {<pointer>}?
or
<multiplicative-expression> ::= <multiplicative-expression> * <cast-expression>
但这不是唯一可能的解析器!
用 C 看
foo * bar;
它可以是名为bar
的指针,用于键入foo
,也可以将foo
乘以bar
解析为令牌流:
identifier_or_type ASTERISK identifier_or_type SEMICOLON
剩下的就看解析器"业务逻辑"了。所以这里在解析器级别根本没有歧义,规则背后的逻辑决定了两种情况之间的差异。
像foo.bar.bla.i
这样的表达式不能单独使用语法以有意义的方式解析。foo
、bar
和bla
中的每一个都可以是包名的一部分、静态变量(这不适用于foo
)或内部类的名称。
例:
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;
}
}
这将打印21
或42
当静态变量bla
被注释掉或不被注释掉时。