获得固定长度的令牌

时间:2016-12-10 19:34:34

标签: graphviz javacc

我正在尝试编写一个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修饰符的字符,并有一个词法操作切换回{{1}满足所需计数后的状态。这是沿着正确的轨道吗?如何在词法操作中指示令牌已完成?

另外,我是否需要担心TOKEN? (我想如果我在令牌管理器中完成所有这些操作)

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

1 个答案:

答案 0 :(得分:1)

好的,我得到了它的工作。我决定用词法动作而不是用MORE处理来做这件事,这样看起来更简单。另外,我将lexing / parsing分成两部分以简化:

  1. 首先解析包含扩展语法的字符串,然后
  2. 在第二遍中使用扩展语法解析这些字符串。
  3. 至于根据长度前缀获取令牌,第一部分是定义代表命令的令牌:

    <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处理,因此我必须处理空格(未完全实现)我的代码示例)。