我正在尝试编写一个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
? (我想如果我在令牌管理器中完成所有这些操作)
一旦我弄清楚要做什么,我会跟进一些代码。
答案 0 :(得分:1)
好的,我得到了它的工作。我决定用词法动作而不是用MORE
处理来做这件事,这样看起来更简单。另外,我将lexing / parsing分成两部分以简化:
至于根据长度前缀获取令牌,第一部分是定义代表命令的令牌:
<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
处理,因此我必须处理空格(未完全实现)我的代码示例)。