似乎已经达成共识,你应该使用Parsec作为应用而不是monad.应用解析比monadic解析有什么好处?
monadic解析了吗?
我是Haskell和Parsec的新手.在阅读了第16章使用真实世界Haskell的Parsec之后,我脑子里出现了一个问题:为什么以及何时Parsec比Yacc/Bison/Antlr等其他解析器生成器更好?
我的理解是Parsec创建了一个很好的编写解析器的DSL,而Haskell使它非常容易和富有表现力.但解析是一种标准/流行的技术,值得使用自己的语言,输出到多种目标语言.那么我们何时应该使用Parsec而不是从Bison/Antlr生成Haskell代码呢?
这个问题可能会超越技术,进入行业实践领域.从头开始编写解析器时,与Bison/Antlr或类似的东西相比,拾取Haskell/Parsec有什么好处?
顺便说一句:我的问题与这个问题非常相似,但在那里没有得到令人满意的回答.
Text
Text.Parsec
Text.Parsec.ByteString
Text.Parsec.ByteString.Lazy
Text.Parsec.Char
Text.Parsec.Combinator
Text.Parsec.Error
Text.Parsec.Expr
Text.Parsec.Language
Text.Parsec.Perm
Text.Parsec.Pos
Text.Parsec.Prim
Text.Parsec.String
Text.Parsec.Token
ParserCombinators
Text.ParserCombinators.Parsec
Text.ParserCombinators.Parsec.Char
Text.ParserCombinators.Parsec.Combinator
Text.ParserCombinators.Parsec.Error
Text.ParserCombinators.Parsec.Expr
Text.ParserCombinators.Parsec.Language
Text.ParserCombinators.Parsec.Perm
Text.ParserCombinators.Parsec.Pos
Text.ParserCombinators.Parsec.Prim
Text.ParserCombinators.Parsec.Token
Run Code Online (Sandbox Code Playgroud)
它们是一样的吗?
在像Haskell的Parsec这样的解析器组合库中编写解析器时,通常有2个选择:
String输入拆分为标记,然后执行解析[Token]String第一种方法通常似乎有意义,因为许多解析输入可以理解为由空格分隔的标记.
在其他地方,我看到人们建议不要进行标记化(或扫描或激发,有人称之为),简单性被引用作为主要原因.
lexing和不做之间的一般权衡是什么?
使用Parsec 3.1,可以解析几种类型的输入:
[Char] 同 Text.Parsec.StringData.ByteString 同 Text.Parsec.ByteStringData.ByteString.Lazy 同 Text.Parsec.ByteString.Lazy我没有看到该Data.Text模块的任何内容.我想解析Unicode内容而不会遇到String效率低下的问题.所以我基于Text.Parsec.ByteString模块创建了以下模块:
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Text.Parsec.Text
( Parser, GenParser
) where
import Text.Parsec.Prim
import qualified Data.Text as T
instance (Monad m) => Stream T.Text m Char where
uncons = return . T.uncons
type Parser = Parsec T.Text ()
type GenParser t st = Parsec T.Text st
Run Code Online (Sandbox Code Playgroud)
附加评论:
我必须{-# LANGUAGE NoMonomorphismRestriction #-} …
大约6年前,我在OCaml中对自己的解析器组合器进行了基准测试,结果发现它们比当时提供的解析器生成器慢〜5倍.我最近重新审视了这个主题,并对Haskell的Parsec和一个用F#编写的简单的手动优先攀爬解析器进行了基准测试,并惊讶地发现F#比Haskell快25倍.
这是我用来从文件中读取大型数学表达式的Haskell代码,解析并评估它:
import Control.Applicative
import Text.Parsec hiding ((<|>))
expr = chainl1 term ((+) <$ char '+' <|> (-) <$ char '-')
term = chainl1 fact ((*) <$ char '*' <|> div <$ char '/')
fact = read <$> many1 digit <|> char '(' *> expr <* char ')'
eval :: String -> Int
eval = either (error . show) id . parse expr "" . filter (/= ' ')
main :: IO ()
main = do
file <- …Run Code Online (Sandbox Code Playgroud) 要学习如何编写和解析无上下文语法,我想选择一个工具.对于Haskell,有两个很大的选项:Happy,它从语法描述中生成解析器,以及*Parsec,它允许您直接在Haskell中编写解析器代码.
这两种方法的(dis)优势是什么?
我正在尝试为一个简单的函数式语言创建一个解析器,有点像Caml,但我似乎陷入了最简单的事情.
所以我想知道是否有一些更完整的parsec解析器示例,超出了"这是你解析2 + 3"的方法.特别是函数调用等.
我读过"给你写一个方案",但方案的语法很简单,并没有真正帮助学习.
我遇到的大多数问题是如何使用try,<|>并且choice正确,因为我真的不明白为什么parsec似乎永远不会a(6)使用此解析器解析为函数调用:
expr = choice [number, call, ident]
number = liftM Number float <?> "Number"
ident = liftM Identifier identifier <?> "Identifier"
call = do
name <- identifier
args <- parens $ commaSep expr
return $ FuncCall name args
<?> "Function call"
Run Code Online (Sandbox Code Playgroud)
编辑添加了一些完成的代码,虽然这实际上不是我问的问题:
AST.hs
module AST where
data AST
= Number Double
| Identifier String
| Operation BinOp AST AST
| FuncCall String [AST]
deriving …Run Code Online (Sandbox Code Playgroud) 我已经读过Haskell解析器组合器(在Parsec中)可以解析上下文敏感语法.Scala解析器组合器也是如此吗?如果是这样,这就是"进入"(又名">>")功能的用途吗?
与Haskell相比,Scala实现解析器组合器有哪些优点/缺点?他们接受同一类语法吗?是否更容易生成错误消息或使用其中一个进行其他各种有用的事情?
packrat解析(在Scala 2.8中引入)如何适应这张图片?
是否有网页或其他资源显示一种语言的实现中不同的运算符/函数/ DSL-sugar如何映射到另一种语言?
haskell ×10
parsec ×10
parsing ×2
applicative ×1
attoparsec ×1
f# ×1
happy ×1
lexer ×1
monads ×1
scala ×1