在 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 中解析大型 XML 文件?