使用Megaparsec 5.按照本指南,我可以通过组合StateT和ParsecT(非定义类型应该是显而易见的/不相关的)来实现反向跟踪用户状态:
type MyParser a = StateT UserState (ParsecT Dec T.Text Identity) a
Run Code Online (Sandbox Code Playgroud)
如果我运行解析器p :: MyParser a,像这样:
parsed = runParser (runStateT p initialUserState) "" input
Run Code Online (Sandbox Code Playgroud)
类型parsed是:
Either (ParseError Char Dec) (a, UserState)
Run Code Online (Sandbox Code Playgroud)
这意味着,如果出现错误,用户状态将丢失.
在这两种情况下都有办法吗?
编辑: 我可能,如果出现错误,可能使用自定义错误组件而不是Dec(5.0中引入的功能)并将用户状态封装在那里?
我的问题是关于DSL设计.它与内部与外部DSL有关,但更具体.背景信息:我已经完成了DSL in Action和其他教程.内部和外部之间的区别对我来说很清楚.我也有在Haskell开发外部DSL的经验.
我们来看一个非常简单的例子.下面是(简化的)关系代数表达式:
SELECT conditions (
CROSS (A,B)
)
Run Code Online (Sandbox Code Playgroud)
代数表达式(Haskell中的ADT)可以很容易地重写.例如,这可以简单地重写为:
JOIN conditions (A,B)
Run Code Online (Sandbox Code Playgroud)
在我开发的DSL中,我总是采用这种方法:编写一个解析器来创建像上面那样的代数表达式.然后,使用允许像Haskell一样进行模式匹配的语言,应用一些重写并最终转换为目标语言.
这是问题所在.
对于我想开发的新DSL,我宁愿选择内部DSL.主要是因为我想利用宿主语言功能(在这种情况下可能是Scala).关于这是否是正确选择的争论不是重点.让我们假设它是一个不错的选择.
我想念的是:如果我选择内部DSL,那么就无法解析ADT.我喜欢的模式匹配重写在哪里适合这个?我必须放弃它吗?是否有最佳实践来充分利用这两个世界?或者我没有在这里看到正确的东西?