栏目分类:
子分类:
返回
终身学习网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
终身学习网 > IT > 面试经验 > 面试问答

JavaCC可以通过上下文区分令牌吗?

面试问答 更新时间:发布时间: 百科书网 趣学号

JavaCC FAQ中概述了执行此操作的三种方法。

  • 一种是像您一样使用词法状态。此方法可能很棘手,但这是处理最长匹配的长度取决于上下文或跳过规则取决于上下文的情况的唯一方法。对于您的问题,它可能比您需要的更为复杂。
  • 第二种是使用一种令牌类型,并在令牌映射图像的基础上使用语义超前来使解析器在某些情况下专门处理某些令牌。有关更多信息,请参见常见问题解答。
  • 第三种方法(通常是最简单的方法)是在词汇层次上进行区分,然后在句法层次上忽略这些区别。通常,这是处理可以兼作标识符的关键字的最佳方法。

下面,我将给出第三种方法的三个示例。


使用关键字作为标识符

如果您要做的就是允许将关键字 用作变量名,则有一种非常简单的方法。在词法分析器中放入通常的规则。

TOKEN: { <CLASS: "class"> }TOKEN: { < VARNAME: ["a-"z","A"-Z"](["a-"z","A"-Z"])* > } // Or what you will

在解析器中写一个生产

Token varName() { Token t ; } : {{    (t = <CLASS> | t = <VARNAME>)    {return t ;}}

然后

varName()
在解析器中的其他地方使用。


原始海报的组装者

转向原始问题中的汇编器示例,让我们以JPC指令为例。JPC(跳转条件)指令后跟比较运算符(例如Z,B等),然后是一个可以是包括标识符在内的许多事物的操作数。例如我们可以

JPC Z fred

但是我们也可以有一个名为JPC或Z的标识符,因此

JPC Z JPC

JPC Z Z

也是有效的JPC指令。

在词汇部分,我们有

TOKEN : // Oppres{    <I_CAL: "CAL"> |   <I_JPC: "JPC"> |   ... // other op pres    <CMP_OP: "Z" | "B" | "BE" | "A" | "AE" | "NZ">|   <T_REGISTER : "R0" | "R1" | "R2" | "R3" | "RP" | "RF" |"RS" | "RB">}... // Other lexical rules.TOKEN : // Be sure this rule comes after all keywords.{    < IDENTIFIER: <LETTER> (<LETTER>|<DIGIT>)* >}

在解析器中,我们有

Instruction Instruction():{    Instruction inst = new Instruction();    Token o = null,dataType = null,calType = null,cmpType = null;    Operand a = null,b = null; }{    ...    o = <I_JPC> cmpType = <CMP_OP> a = Operand()    ...}Operand Operand():{    Token t ; ... }{     t = <T_REGISTER> ...|    t = Identifier()  ...    ...}Token Identifier : {    Token t ; }{    t = <IDENTIFIER> {return t ;}|   t = <I_CAL>      {return t ;}|   t = <I_JPC>      {return t ;}|   t = <CMP_OP>     {return t ;}| ... // All other keywords}

我建议从可以用作标识符的其他关键字列表中排除寄存器名称。

如果您确实将其包括

<T_REGISTER>
在该列表中,则操作数中将存在歧义,因为
Operand
看起来像这样

Operand Operand():{    Token t ; ... }{     t = <T_REGISTER> ...|    t = Identifier()  ...    ...}

现在有一个歧义,因为

JPC Z R0

有两个解析。在作为操作数的上下文中,我们希望像“
R0”这样的标记被解析为寄存器而不是标识符。幸运的是,JavaCC会更喜欢较早的选择,因此这将发生。您将收到JavaCC的警告。您可以忽略该警告。(我在源代码中添加了注释,以便其他程序员不必担心。)或者您可以通过先行规范抑制警告。

Operand Operand():{    Token t ; ... }{     LOOKAHEAD(1) t = <T_REGISTER> ...|    t = Identifier()  ...    ...}

使用正确的上下文

到目前为止,所有示例都使用左上下文。即,我们可以说出如何仅基于令牌左侧的令牌序列来对待令牌。让我们看一个关键字的解释是基于右边的标记的情况。

考虑这种简单的命令式语言,其中所有关键字都可以用作变量名。

P -> Block <EOF>Block -> [S Block]S -> Assignment | IfElseAssignment -> LHS ":=" ExpLHS -> VarNameIfElse -> "if" Exp Block ["else" Block] "end"Exp -> VarNameVarName -> <ID> | if | else | end

这种语法是明确的。您可以通过添加新的语句,表达式和左手边来使语法更加复杂。只要语法保持明确,这种复杂性可能与我接下来要说的没有太大区别。随时尝试。

语法不是LL(1)。必须在两个地方基于多个将来的令牌进行选择。一个是在下一个标记为“ if”
之间

Assignment
以及
IfElse
何时选择下一个标记。考虑块

a := bif := a

a := bif q    b := cend

我们可以期待像这样的“:=”

void S() : {} {    LOOKAHEAD( LHS() ":=" ) Assignment()|    IfElse() }

我们需要向前看的另一个地方是在块的开头遇到“ else”或“ end”的情况。考虑

if x    end := y    else := zend

我们可以解决这个问题

void Block() : {} {    LOOKAHEAD( LHS() ":=" | "if" ) S() Block()|    {}}


转载请注明:文章转载自 www.051e.com
本文地址:http://www.051e.com/it/419479.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 ©2023-2025 051e.com

ICP备案号:京ICP备12030808号