Java 是否有歧义语法,需要有关标识符的更多信息?
Does Java has ambiguous syntax which needs more information about an identifier?
注意:这个问题不是关于 "Java do not have pointers"
在C语言中,代码identifier1 * identifier2
有歧义,有两种可能的含义:
- 如果标识符 1 是一个类型,那么这可能是一个指针声明。
- 如果标识符 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
的指针,指向 foo
或 foo
与 bar
的乘积可以解析为令牌流:
identifier_or_type ASTERISK identifier_or_type SEMICOLON
剩下的就交给解析器了"business logic"。所以这里在 parser 级别完全没有歧义,规则背后的逻辑使两种情况有所不同。
像 foo.bar.bla.i
这样的表达式不能单独使用语法进行有意义的解析。 foo
、bar
、bla
可以是包名的一部分、静态变量(这个不适用于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
是否被注释掉时,这将打印 21
或 42
。
注意:这个问题不是关于 "Java do not have pointers"
在C语言中,代码identifier1 * identifier2
有歧义,有两种可能的含义:
- 如果标识符 1 是一个类型,那么这可能是一个指针声明。
- 如果标识符 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
的指针,指向 foo
或 foo
与 bar
的乘积可以解析为令牌流:
identifier_or_type ASTERISK identifier_or_type SEMICOLON
剩下的就交给解析器了"business logic"。所以这里在 parser 级别完全没有歧义,规则背后的逻辑使两种情况有所不同。
像 foo.bar.bla.i
这样的表达式不能单独使用语法进行有意义的解析。 foo
、bar
、bla
可以是包名的一部分、静态变量(这个不适用于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
是否被注释掉时,这将打印 21
或 42
。