【问题标题】:Getting a fixed length token获取固定长度的令牌
【发布时间】:2016-12-10 19:34:34
【问题描述】:

我正在尝试编写一个 javacc 解析器来读取 GraphViz xdot format 文件。这种文件格式有一个非常规则的语法,但我很难弄清楚如何为它的扩展部分获取令牌。

问题在于,一些标记前面有一个长度值,表示标记有多长,而一些参数有一个长度值,指定如何需要后面的标记。

这是一个例子:

graph [_draw_="c 9 -#fffffe00 C 7 -#ffffff P 4 0 0 0 13095 1541.31 13095 1541.31 0 ",
    bb="0,0,1541.3,13095",
    rankdir=LR,
    size="12,12",
    xdotversion=1.7
];

我遇到问题的扩展部分是_draw_= 标记后面的引用字符串。在此字符串中,第一个数字 9 表示“-”开始字符之后的下一个标记的长度。如果标记是由空格包围的一系列字符(易于定义标记),但在其他情况下,这些以下标记可能嵌入了空格,所以我认为不可能定义一个通用的正则表达式。

此外,此字符串中第一个“P”字符后面是一个 4,表示后面有 4 对数字。解析器怎么知道使用这个数字来获取接下来的 8 个数字令牌,或者令牌管理器应该以某种方式返回 8 个数字字符串?

我知道我可以将字符串的全部内容作为一个块获取,然后使用一些字符串匹配在 java(不使用 javacc)中手动解析它。但是我想知道在 javacc 中是否有一些技术可以做到这一点。

我怀疑在读取长度标记后,我需要切换到不同的词法状态,并在其中匹配每个带有 MORE 修饰符的字符,并在满足要求的计数。这是在正确的轨道上吗?如何在词法动作中指示标记是完整的?

另外,我需要担心LOOKAHEAD 吗? (如果我在令牌管理器中执行所有这些操作,我认为不会)

一旦我弄清楚该怎么做,我会跟进一些代码。

【问题讨论】:

  • 我认为您使用 MORE 的想法是完美的。为什么不实施它,然后回答您自己的问题。无需担心 LOOKAHEAD。
  • 要指示一个令牌的结束,您使用一个 TOKEN 产品。因此,当预期还有一个字符时,切换到任何字符都是 TOKEN 的状态。

标签: graphviz javacc


【解决方案1】:

好的,我开始工作了。我决定以词法操作而不是使用MORE 处理来执行此操作,这样看起来更简单。此外,我将词法分析/解析分成两部分以使事情变得更简单:

  1. 先解析出包含扩展语法的字符串,然后
  2. 在第二遍中使用扩展语法解析这些字符串。

至于根据长度前缀获取token,第一部分是定义代表命令的token:

<Extended>   TOKEN :
{
        <LINECOLOR:       "c"> { singleCount = 1; }
      | <FILLCOLOR:       "C"> { singleCount = 1; }
      | <FONT:            "F"> { singleCount = 2; }
      | <TEXT:            "T"> { singleCount = 5; }
      | <TEXTCHARS:       "t">
      | <SPLINE:          "B"> { coordCount = 1; }
      | <FILLEDSPLINE:    "b"> { coordCount = 1; }
      | <FILLEDELLIPSE:   "E">
      | <UNFILLEDELLIPSE: "e">
      | <POLYLINE:        "L"> { coordCount = 1; }
      | <FILLEDPOLYGON:   "P"> { coordCount = 1; }
      | <UNFILLEDPOLYGON: "p"> { coordCount = 1; }
      | <STYLE:           "S"> { singleCount = 1; }
}

我在词法动作中添加了语句来指示长度标记将出现在命令标记之后的位置。在大多数情况下,它是子命令之后的第一个标记,但在某些情况下,需要先解析其他中间标记。

接下来是定义标记以匹配长度计数。当触发消耗指定长度的标记时,词法操作然后解码长度计数并消耗输入缓冲区中适当数量的字符。获得正确数据后,它会根据需要切换令牌图像和类型。此标记还匹配默认词法状态下的数字字符串。

/*
 * Special number token.  Normally just grabs a number.
 * In the Extended lexical state can grab a fixed length
 * string or a list of coordinate pairs.
 */
<DEFAULT,Extended> TOKEN:
{
        <#NUM: (["0"-"9"]) >
    | <NUMBER: (<NUM>)+ | (<NUM>)* "." (<NUM>)+ | (<NUM>)+ "." (<NUM>)* >
    {
        if (curLexState == Extended && singleCount-- == 1) {
            // Get a single fixed length parameter
            int len = Integer.parseInt(matchedToken.image);
            StringBuilder sb = new StringBuilder();
            try {
                while (input_stream.readChar() != '-')
                   { /* Do nothing */; };
                for (int i=0; i<len; i++)
                sb.append(input_stream.readChar());
            } catch (IOException ioe) {
                throw new TokenMgrError(true, curLexState, input_stream.getLine(), input_stream.getColumn(), sb.toString(), (char)0, TokenMgrError.LEXICAL_ERROR);
            }

            matchedToken.image = sb.toString();
            matchedToken.endLine = input_stream.getEndLine();
            matchedToken.endColumn = input_stream.getEndColumn();
            matchedToken.kind = SINGLE;
        }
        if (curLexState == Extended && coordCount-- == 1) {
            // Get a list of coordinate pairs
            int len = Integer.parseInt(matchedToken.image);
            StringBuilder sb = new StringBuilder();
            try {
                for (int i=0; i<len*2; i++) {
                char c;
                while ((c=input_stream.readChar()) == ' ')
                    { /* Do nothing */; };
                if (i>0) sb.append(" ");
                do
                    {sb.append(c);}
                while
                    ((c=input_stream.readChar()) != ' ');
                }
            } catch (IOException ioe) {
                throw new TokenMgrError(true, curLexState, input_stream.getLine(), input_stream.getColumn(), sb.toString(), (char)0, TokenMgrError.LEXICAL_ERROR);
            }

            matchedToken.image = sb.toString();
            matchedToken.endLine = input_stream.getEndLine();
            matchedToken.endColumn = input_stream.getEndColumn();
            matchedToken.kind = COORDS;
        }
    }
}

必须声明特殊标记,以及保持标记倒计时的变量以进行解码:

/*
 * Special extended token identifiers
 */
<Extended> TOKEN:
{
      <COORDS: <NUMBER>>
    | <SINGLE: <NUMBER>>
}

TOKEN_MGR_DECLS:
{
    /*
     * These keep track of where the length prefixed strings
     * and coordinate pairs start in the extended xdot commands.
     * Set these values in the lexical actions for the sub-command
     * definitions.
     */
    int singleCount = -1;;
    int coordCount = -1;;
}

一旦我了解了词法操作的工作原理以及令牌管理器 API 可以做什么,这实际上就很容易了。

要在解析器中使用它,请切换到扩展词法状态并使用如下产生式:

<LINECOLOR> color=<SINGLE>

<UNFILLEDPOLYGON> scoords=<COORDS>

<TEXT> sx=<NUMBER> sy=<NUMBER> sj=<NUMBER> sw=<NUMBER> label=<SINGLE>

就是这样。它似乎运作良好。

我这样做的唯一缺点是SKIP 处理不会发生在我从输入流中手动读取的字符上,所以我必须注意空格(在我的代码中没有完全实现示例)。

【讨论】:

    猜你喜欢
    • 2023-03-03
    • 1970-01-01
    • 2016-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-10
    • 2017-09-24
    • 2018-03-02
    相关资源
    最近更新 更多