在 Haskell 中使用 Alex 制作解析骰子卷的词法分析器

Posted

技术标签:

【中文标题】在 Haskell 中使用 Alex 制作解析骰子卷的词法分析器【英文标题】:Using Alex in Haskell to make a lexer that parses Dice Rolls 【发布时间】:2020-07-13 04:48:16 【问题描述】:

我正在使用 Alex + Happy 在 Haskell 中为 DSL 制作解析器。 我的 DSL 使用 掷骰子 作为可能表达式的一部分。

有时我有一个想要解析的表达式,如下所示:

[some code...]  3D6  [... rest of the code]

应该大致翻译为:

TokenInt ... value = 3, TokenD, TokenInt ... value = 6

我的 DSL 也使用 variables(基本上是字符串),所以我有一个特殊的标记来处理变量名。 所以,有了这个令牌:

"D"                                  \pos str -> TokenD pos 
$alpha [$alpha $digit \_ \']*        \pos str -> TokenName pos str
$digit+                              \pos str -> TokenInt pos (read str) 

我现在使用解析时得到的结果是:

TokenInt ... value = 3, TokenName  ... , name = "D6"

这意味着我的词法分析器“读取”了一个 Integer 和一个名为“D6”的 Variable

我尝试了很多东西,例如,我将令牌 D 更改为:

$digit "D" $digit                    \pos str -> TokenD pos 

但这只是消耗数字:(

我可以用数字解析掷骰子吗? 或者至少解析TokenInt-TokenD-TokenInt?

PS:我使用 PosN 作为包装器,不确定是否相关。

【问题讨论】:

好问题,尽管恕我直言,在这种情况下,这样做的更好方法可能是手动滚动词法分析器。对于这么小的语言,工具可以使事情变得比手动做事情更难。不过,这也可以很容易地以更大的语言出现,因此无论如何这是一个很好的问题。 您实际上可以继续分别对它们进行词法分析,并将Dn 解析为后缀运算符,该运算符通过其操作数迭代n 面的骰子;甚至将 D 解析为 infix 运算符,左侧为 die 计数,右侧为 die 值。那么掷骰不限于固定骰子,您可以允许动态表达式,例如x D y(或样式如x D(y))≅replicateM x (rollD y) 【参考方案1】:

我的做法是将TokenD 类型扩展为TokenD Int Int,因此为了方便我会使用basic 包装器

$digit+ D $digit+  dice 
...
dice :: String -> Token
dice s = TokenD (read $ head ls) (read $ last ls)
  where ls = split 'D' s

split 可以找到here。

这是一个额外的步骤,通常在句法分析期间完成,但在这里不会造成太大影响。

我也不能让 Alex 将 $alpha 解析为 TokenD 而不是 TokenName。如果我们有Di 而不是D 那没问题。来自 Alex 的文档:

当输入流匹配多个规则时,匹配输入流最长前缀的规则获胜。如果仍有多个规则匹配相同数量的字符,则文件中最早出现的规则获胜。

但是你的代码应该可以工作。我不知道这是否是 Alex 的问题。

【讨论】:

我想保留 PosN 包装器,因为我可以用它来标记解析错误的位置。但是我喜欢这个主意,我想我可以使用它。谢谢! 是的,您仍然可以使用 posn。任何包装器的想法都是一样的。【参考方案2】:

我决定我可以使用以 小写字母 开头的变量(如 Haskell 变量),因此我将词法分析器更改为仅在变量以小写字母开头时才解析它们。 这也解决了一些其他保留字可能出现的问题。

我仍然很想知道是否有其他解决方案,但问题本身已经解决了。

谢谢大家!

【讨论】:

以上是关于在 Haskell 中使用 Alex 制作解析骰子卷的词法分析器的主要内容,如果未能解决你的问题,请参考以下文章

Haskell:如何将 IO 输入字符串解析为 Float(或 Int 或其他)?

分析使用 Haskell 中的解析器组合库编写的解析器

旋转骰子

如何在资源有限的 Haskell 中解析大型 XML 文件?

在 C# 中解析骰子表达式(例如 3d6+5):从哪里开始?

如何在 Haskell 中解析 IO 字符串?