好的,我开始工作了。我决定以词法操作而不是使用MORE 处理来执行此操作,这样看起来更简单。此外,我将词法分析/解析分成两部分以使事情变得更简单:
- 先解析出包含扩展语法的字符串,然后
- 在第二遍中使用扩展语法解析这些字符串。
至于根据长度前缀获取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 处理不会发生在我从输入流中手动读取的字符上,所以我必须注意空格(在我的代码中没有完全实现示例)。