Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Parsing Parsec解析不同类型语句的列表_Parsing_Haskell_Parsec - Fatal编程技术网

Parsing Parsec解析不同类型语句的列表

Parsing Parsec解析不同类型语句的列表,parsing,haskell,parsec,Parsing,Haskell,Parsec,我正在尝试解析(目前)Dot语言的一个子集。 语法是,我的代码如下 import System.Environment import System.IO import qualified Text.Parsec.Token as P import Text.ParserCombinators.Parsec.Char -- for letter import Text.Parsec import qualified Control.Applicative as App import Lib ty

我正在尝试解析(目前)Dot语言的一个子集。 语法是,我的代码如下

import System.Environment
import System.IO
import qualified Text.Parsec.Token as P
import Text.ParserCombinators.Parsec.Char -- for letter
import Text.Parsec
import qualified Control.Applicative as App

import Lib
type Id = String
data Dot = Undirected Id  Stmts
         | Directed Id  Stmts
         deriving (Show)

data Stmt = NodeStmt Node | EdgeStmt Edges
          deriving (Show)
type Stmts = [Stmt]

data Node = Node Id Attributes deriving (Show)
data Edge =  Edge Id Id deriving (Show)
type Edges = [Edge]

data Attribute = Attribute Id Id deriving (Show)
type Attributes = [Attribute]

dotDef :: P.LanguageDef st
dotDef = P.LanguageDef
  { P.commentStart    = "/*"
  , P.commentEnd      = "*/"
  , P.commentLine     = "//"
  , P.nestedComments  = True
  , P.identStart      = letter
  , P.identLetter     = alphaNum
  , P.reservedNames   = ["node", "edge", "graph", "digraph", "subgraph", "strict" ]
  , P.caseSensitive   = True
  , P.opStart         = oneOf "-="
  , P.opLetter        = oneOf "->"
  , P.reservedOpNames = []
  }



lexer = P.makeTokenParser dotDef

brackets    = P.brackets lexer
braces      = P.braces lexer

identifier  = P.identifier lexer
reserved    = P.reserved lexer

semi = P.semi lexer
comma = P.comma lexer

reservedOp = P.reservedOp lexer

eq_op = reservedOp "="
undir_edge_op = reservedOp "--"
dir_edge_op = reservedOp "->"

edge_op = undir_edge_op <|> dir_edge_op

-- -> Attribute
attribute = do
  id1 <- identifier
  eq_op
  id2 <- identifier
  optional (semi <|> comma)
  return $ Attribute id1 id2

a_list = many attribute

bracked_alist =
  brackets $ option [] a_list

attributes =
  do
    nestedAttributes <- many1 bracked_alist
    return $ concat nestedAttributes


nodeStmt = do
  nodeName <- identifier
  attr <- option [] attributes
  return $ NodeStmt $ Node nodeName attr

dropLast = reverse . tail . reverse

edgeStmt = do
  nodes <- identifier `sepBy1` edge_op
  return $ EdgeStmt $ fmap (\x -> Edge (fst x) (snd x)) (zip (dropLast nodes) (tail nodes))


stmt = do
  x <- nodeStmt <|> edgeStmt
  optional semi
  return x

stmt_list = many stmt
graphDecl = do
  reserved "graph"
  varName <- option "" identifier
  stms <- braces stmt_list
  return $ Undirected varName stms

digraphDecl = do
  reserved "digraph"
  varName <- option "" identifier
  stms <- braces stmt_list
  return $ Directed varName stms

topLevel3 = do
  spaces
  graphDecl <|> digraphDecl

main :: IO ()
main = do
  (file:_) <- getArgs
  content <- readFile file
  case parse topLevel3 "" content of
    Right g -> print g
    Left err -> print err
如果对第1行或第2行进行了注释,则可以正常工作,但如果同时启用了这两个行,则会失败

(第3行第10列):意外“-”应为标识符或“}”

我的理解是解析器选择第一个匹配规则(带回溯)。在这里,edge和node语句都以and标识符开头,所以它总是选择这个

我尝试在
stmt
中反转顺序,但没有任何运气。 我还试图在stmt、nodeStmt和edgeStmt中撒一些
try
,但也没有运气


感谢您的帮助。

请注意,无论第1行是否被注释掉,我都会收到相同的错误,因此:

digraph PZIFOZBO{
        a->b
        }
还表示
意外“-”

正如我认为您已经正确诊断的那样,这里的问题是
stmt
解析器首先尝试
nodeStmt
。它成功地解析了
“a”
,留下了
“->b”
,但
->b
不是有效的语句。请注意,Parsec在没有try的情况下不会自动回溯,因此当它“发现”无法解析
->b
时,它不会返回并重新考虑该决策

您可以通过交换
stmt
中的顺序来“修复”此问题:

x <- edgeStmt <|> nodeStmt
并将
stmt
调整为“
try
”,首先调整边缘语句,然后回溯到节点语句:

stmt = do
  x <- try edgeStmt <|> nodeStmt
  optional semi
  return x
stmt=do

另外,
dropLast
只是库函数
init
zipWith
结合了
map
zip
,所以你可以用
return$EdgeStmt$zipWith Edge(init节点)(tail节点)
替换
edgeStat
中的最后一句话,我以为dropLast有什么用的,但是我找不到,所以我写了我自己的^^。关于原来的问题,首先,谢谢你的回答。如果我想,我如何进一步扩展解析器来处理stmt中的第四种情况(即ID'='ID)?更多的尝试?是的,我认为
x
import Control.Monad (guard)
edgeStmt = do
  nodes <- identifier `sepBy1` edge_op
  guard $ length nodes > 1
  return $ EdgeStmt $ fmap (\x -> Edge (fst x) (snd x)) (zip (dropLast nodes) (tail nodes))
stmt = do
  x <- try edgeStmt <|> nodeStmt
  optional semi
  return x