处理语法歧义(扑克文件解析)

时间:2012-06-18 11:24:17

标签: java parsing ambiguity javacc

我目前正在制作扑克手历史解析器,作为我的单身汉项目的一部分。我在过去的几天里一直在做一些研究,并且遇到了一些不错的解析器生成器(我选择了JavaCC,因为项目本身将用Java编码)。

尽管手历史语法非常基本且直截了当,但由于玩家昵称中允许的字符集,存在歧义问题。

假设我们有以下格式的一行:

Seat 5: myNickname (1500 in chips)

令牌myNickname可以包含任何字符以及空格。这意味着,(1500 in chipSeat 5:都是有效的昵称 - 这最终会导致歧义问题。除了长度(4-12个字符)之外,对玩家的昵称没有限制。

我需要解析和存储几个数据以及玩家的昵称(例如在这种特殊情况下的座位位置和筹码数量),所以我的问题是,我有什么选择?

我很乐意使用JavaCC来做这件事:

SeatRecord seat() :
{ Token seatPos, nickname, chipStack; }
{
    "Seat" seatPos=<INTEGER> ":" nickname=<NICKNAME> "(" chipStack=<INTEGER> 
    "in chips)"
    {
        return new SeatRecord(seatPos.image, nickname.image, chipStack.image); 
    }
}  

现在哪个不起作用(由于上述问题)

我还搜索了GLR解析器(显然处理了暧昧的语法) - 但是除了Bison之外,它们似乎大部分被遗弃或记录不清,但是它不支持Java的GLR解析器,并且可能过于复杂离开工作(除了歧义问题,语法本身是非常基本的,正如我所提到的)

或者我应该坚持自己对字符串进行标记,并使用indexOf(), lastIndexOf()等来解析我需要的数据?只有当它是剩下的唯一选择时我才会这样做,因为它太丑了恕我直言,我可能会错过一些情况(这会导致错误的解析)

3 个答案:

答案 0 :(得分:7)

如果输入格式与指定的一样简单,则可以使用简单的正则表达式:

^Seat ([0-9]+): (.*) \(([0-9]+) in chips\)$

在这种情况下,正则表达式引擎的NFA解决了您的歧义,括号是捕获组,以便您可以提取您感兴趣的信息。

答案 1 :(得分:2)

您有两种解决方案:

  • 为名称添加一些限制。我几乎不记得任何广泛使用的系统会接受这样的昵称。让它们使用字母数字字符和“_”分隔符。您也可以为座位添加关键字,例如,这样的单词不能是昵称。
  • 您还可以根据语法构建有限自动机进行解析。我认为,FSM可以处理这种歧义语法。一旦你拥有它,你就可以解析你想要的一切。

无论如何,我认为原始设计存在问题。昵称不应该允许这样的一组名称。此外,为什么不能使用标识符而不是名称 - 名称可以存储在数据库中。

答案 2 :(得分:2)

您系统的语法可能如下所示(写成无上下文语法):

S -> seating nickname chips

seating -> "Seat " number ":"
number -> "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
number -> number number

nickname -> "a" | "b" | "c" ...... | "z" | ...."+" | "?" | number
nickname -> nickname nickname 

chips -> "(" number "in chips)"

注意表格的规则:

number -> number number

这基本上允许无限的语法。注意&#34;无限语法&#34;并不意味着你封装了一切。上面的行基本上等同于正则表达式(\d*)

我发现在CFG中输入语法然后将其转换为常规语法可以帮助我大部分时间。更多关于如何做到这一点here。祝你好运!