Java 在不使用标记器状态的情况下消除标记的歧义
我无法让JavaCC通过标记在语法中的位置来正确地消除歧义。我有以下JJTree文件(我将其称为Java 在不使用标记器状态的情况下消除标记的歧义,java,parsing,tokenize,javacc,Java,Parsing,Tokenize,Javacc,我无法让JavaCC通过标记在语法中的位置来正确地消除歧义。我有以下JJTree文件(我将其称为bug.jjt): 现在,在编译完这个之后,您可以从命令行为runmyparser提供一个要解析的字符串作为参数。如果成功,它将打印生产,如果失败,它将输出错误 我尝试了两种简单的输入:foo state和state state。第一个解析,但第二个不解析,因为两个状态字符串都标记为。当我将LOOKAHEAD设置为3时,我希望它使用语法并看到一个字符串state必须是,另一个必须是,LOOKAHEAD
bug.jjt
):
现在,在编译完这个之后,您可以从命令行为runmyparser提供一个要解析的字符串作为参数。如果成功,它将打印生产
,如果失败,它将输出错误
我尝试了两种简单的输入:
foo state
和state state
。第一个解析,但第二个不解析,因为两个状态
字符串都标记为
。当我将LOOKAHEAD
设置为3时,我希望它使用语法并看到一个字符串state
必须是
,另一个必须是,LOOKAHEAD
选项仅适用于解析器(生成规则)。标记器不受此影响:它将生成标记,而不必担心生成规则试图匹配什么。输入“状态”
将始终标记为状态
,即使解析器试图匹配产品名称
您可以这样做(未经测试,前面是伪语法代码!):
LOOKAHEAD
选项仅适用于解析器(生成规则)。标记器不受此影响:它将生成标记,而不必担心生成规则试图匹配什么。输入“状态”
将始终标记为状态
,即使解析器试图匹配产品名称
您可以这样做(未经测试,前面是伪语法代码!):
当lexer将字符组合成令牌时,Lookahead并不涉及lexer。解析器在匹配由终端(令牌)组成的非终端时使用它
如果您定义“状态”以产生令牌状态,那么这就是它
我同意你的观点,对于允许关键字用作标识符,标记器状态不是一个好的解决方案。这真的有必要吗?HLL有充分的理由不允许这样做
OTOH,如果您可以只使用
重写语法,您可能会在语义分析过程中推迟对关键字的识别。先行词在将字符组合为标记时不涉及词法。解析器在匹配由终端(令牌)组成的非终端时使用它
如果您定义“状态”以产生令牌状态,那么这就是它
我同意你的观点,对于允许关键字用作标识符,标记器状态不是一个好的解决方案。这真的有必要吗?HLL有充分的理由不允许这样做
OTOH,如果你可以只使用
重写语法,你可能会在语义分析过程中推迟对关键词的识别。这将在下面的问题4.19中讨论
这里概述了三种战略
在语法中添加选项。参见巴特·基尔斯的答案
使用语义前瞻。对于这种方法,您摆脱了产品定义的状态
,并像这样编写语法
void SimpleNode production():
{}
{
(
<PROD_NAME>
( LOOKAHEAD({getToken(1).kind == PROD_NAME && getToken(1).image.equals("state")})
<PROD_NAME>
...
|
...other choices...
)
)
{return jjtThis;}
}
name state : "a" | "b" name ;
|__||______||_________________||_________
DEF- S0 S1 DEFAULT
AULT
<*> SKIP: { " " }
<S0> TOKEN: { <STATE: "state"> : S1 }
<DEFAULT> TOKEN:{ <PROD_NAME: (["a"-"z"])+ > : S0 }
<S0,S1> TOKEN:{ <PROD_NAME: (["a"-"z"])+ > }
<S1> TOKEN: { <SEMICOLON : ";" > : DEFAULT
<S0, DEFAULT> TOKEN : { <SEMICOLON : ";" > }
<*> TOKEN {
COLON : ":"
| ...etc...
}
其中,DUMMY
是一种从未进入的状态
使用词汇状态。OP问题的标题表明他不想这样做,但不是为什么。如果状态切换可以包含在令牌管理器中,则可以执行此操作。假设一个文件是一系列产品,每个产品都是这样的
name state : "a" | "b" name ;
也就是说,它以一个名称开始,然后是关键字“state”一个冒号,一些标记,最后是分号。(我只是在编造,因为我不知道OP试图解析哪种语言。)然后可以使用三种词汇状态DEFAULT、S0和S1
- 默认情况下,任何字母序列(包括“状态”)都是产品名称。默认情况下,识别产品名称会将状态切换为S0
- 在S0中,除“状态”外的任何字母序列都是产品名称,“状态”是状态。在S0中,识别状态令牌导致令牌赋予器切换到状态S1
- 在S1中,任何字母序列(包括“状态”)都是产品名称。在S1中,识别分号会将状态切换为默认状态
我们的例子是这样标记的
void SimpleNode production():
{}
{
(
<PROD_NAME>
( LOOKAHEAD({getToken(1).kind == PROD_NAME && getToken(1).image.equals("state")})
<PROD_NAME>
...
|
...other choices...
)
)
{return jjtThis;}
}
name state : "a" | "b" name ;
|__||______||_________________||_________
DEF- S0 S1 DEFAULT
AULT
<*> SKIP: { " " }
<S0> TOKEN: { <STATE: "state"> : S1 }
<DEFAULT> TOKEN:{ <PROD_NAME: (["a"-"z"])+ > : S0 }
<S0,S1> TOKEN:{ <PROD_NAME: (["a"-"z"])+ > }
<S1> TOKEN: { <SEMICOLON : ";" > : DEFAULT
<S0, DEFAULT> TOKEN : { <SEMICOLON : ";" > }
<*> TOKEN {
COLON : ":"
| ...etc...
}
作品是这样写的
void SimpleNode production():
{}
{
(
<PROD_NAME>
( LOOKAHEAD({getToken(1).kind == PROD_NAME && getToken(1).image.equals("state")})
<PROD_NAME>
...
|
...other choices...
)
)
{return jjtThis;}
}
name state : "a" | "b" name ;
|__||______||_________________||_________
DEF- S0 S1 DEFAULT
AULT
<*> SKIP: { " " }
<S0> TOKEN: { <STATE: "state"> : S1 }
<DEFAULT> TOKEN:{ <PROD_NAME: (["a"-"z"])+ > : S0 }
<S0,S1> TOKEN:{ <PROD_NAME: (["a"-"z"])+ > }
<S1> TOKEN: { <SEMICOLON : ";" > : DEFAULT
<S0, DEFAULT> TOKEN : { <SEMICOLON : ";" > }
<*> TOKEN {
COLON : ":"
| ...etc...
}
跳过:{'}
令牌:{:S1}
令牌:{:S0}
令牌:{}
令牌:{:默认值
令牌:{}
代币{
冒号:“
|……等等。。。
}
解析器可以将状态切换命令发送回标记器,但要使其正确且脆弱是很困难的。参见常见问题解答中的问题3.12。这将在问题4.19中介绍
这里概述了三种战略
在语法中添加选项。参见巴特·基尔斯的答案
使用语义前瞻。对于这种方法,您摆脱了产品定义的状态
,并像这样编写语法
void SimpleNode production():
{}
{
(
<PROD_NAME>
( LOOKAHEAD({getToken(1).kind == PROD_NAME && getToken(1).image.equals("state")})
<PROD_NAME>
...
|
...other choices...
)
)
{return jjtThis;}
}
name state : "a" | "b" name ;
|__||______||_________________||_________
DEF- S0 S1 DEFAULT
AULT
<*> SKIP: { " " }
<S0> TOKEN: { <STATE: "state"> : S1 }
<DEFAULT> TOKEN:{ <PROD_NAME: (["a"-"z"])+ > : S0 }
<S0,S1> TOKEN:{ <PROD_NAME: (["a"-"z"])+ > }
<S1> TOKEN: { <SEMICOLON : ";" > : DEFAULT
<S0, DEFAULT> TOKEN : { <SEMICOLON : ";" > }
<*> TOKEN {
COLON : ":"
| ...etc...
}
其中,DUMMY
是一种从未进入的状态
使用词汇状态。OP问题的标题表明他不想这样做,但不是为什么。如果状态切换可以包含在令牌管理器中,则可以这样做。假设一个文件是一系列产品,每个产品都是这样的
name state : "a" | "b" name ;
也就是说,它从一个名称开始,然后是关键字“state”一个冒号,一些标记,最后是分号。(我只是在编这个,因为我不知道OP试图解析哪种语言。)然后可以使用三种词汇状态DEFAULT、S0和S1
- 默认情况下,任何字母序列(包括“状态”)都是产品名称。默认情况下,识别产品名称会将状态切换为S0
- 在S0中,除“state”外的任何字母序列都是产品名称,“state”是状态。在S0中,识别状态标记会导致标记器切换到状态S1<