使用 Parsec 解析命令式语言的奇怪行为
Strange behaviour parsing an imperative language using Parsec
我正在尝试在 haskell 中使用 Parsec 解析 Abap 语言的一个片段。 Abap 中的语句由点分隔。函数定义的语法是:
FORM <name> <arguments>.
<statements>.
ENDFORM.
我会用它作为一个最小的例子。
这是我在 haskell 和解析器中编写相应类型的尝试。 GenStatement
-构造函数用于除上述函数定义之外的所有其他语句。
module Main where
import Control.Applicative
import Data.Functor.Identity
import qualified Text.Parsec as P
import qualified Text.Parsec.String as S
import Text.Parsec.Language
import qualified Text.Parsec.Token as T
type Args = String
type Name = String
data AbapExpr -- ABAP Program
= Form Name Args [AbapExpr]
| GenStatement String [AbapExpr]
deriving (Show, Read)
lexer :: T.TokenParser ()
lexer = T.makeTokenParser style
where
caseSensitive = False
keys = ["form", "endform"]
style = emptyDef
{ T.reservedNames = keys
, T.identStart = P.alphaNum <|> P.char '_'
, T.identLetter = P.alphaNum <|> P.char '_'
}
dot :: S.Parser String
dot = T.dot lexer
reserved :: String -> S.Parser ()
reserved = T.reserved lexer
identifier :: S.Parser String
identifier = T.identifier lexer
argsP :: S.Parser String
argsP = P.manyTill P.anyChar (P.try (P.lookAhead dot))
genericStatementP :: S.Parser String
genericStatementP = P.manyTill P.anyChar (P.try dot)
abapExprP = P.try (P.between (reserved "form")
(reserved "endform" >> dot)
abapFormP)
<|> abapStmtP
where
abapFormP = Form <$> identifier <*> argsP <* dot <*> many abapExprP
abapStmtP = GenStatement <$> genericStatementP <*> many abapExprP
使用以下输入测试解析器会导致奇怪的行为。
-- a wrapper for convenience
parse :: S.Parser a -> String -> Either P.ParseError a
parse = flip P.parse "Test"
testParse1 = parse abapExprP "form foo arg1 arg2 arg2. form bar arg1. endform. endform."
结果
Right (GenStatement "form foo arg1 arg2 arg2" [GenStatement "form bar arg1" [GenStatement "endform" [GenStatement "endform" []]]])
所以看起来第一个分支总是失败,只有第二个通用分支成功。但是,如果第二个分支(解析通用语句)被注释,则解析表单突然成功:
abapExprP = P.try (P.between (reserved "form")
(reserved "endform" >> dot)
abapFormP)
-- <|> abapStmtP
where
abapFormP = Form <$> identifier <*> argsP <* dot <*> many abapExprP
-- abapStmtP = GenStatement <$> genericStatementP <*> many abapExprP
现在我们得到
Right (Form "foo" "arg1 arg2 arg2" [Form "bar" "arg1" []])
这怎么可能?似乎第一个分支成功了,那么为什么它在第一个示例中不起作用 - 我错过了什么?
非常感谢!
帮我看看你的解析器 genericStatementP
解析 任何 字符直到出现一个点(你正在使用 P.anyChar
)。因此它无法识别您的词法分析器的保留关键字。
我认为你必须定义:
type Args = [String]
和:
argsP :: S.Parser [String]
argsP = P.manyTill identifier (P.try (P.lookAhead dot))
genericStatementP :: S.Parser String
genericStatementP = identifier
通过这些更改,我得到以下结果:
Right (Form "foo" ["arg1","arg2","arg2"] [Form "bar" ["arg1"] []])
我正在尝试在 haskell 中使用 Parsec 解析 Abap 语言的一个片段。 Abap 中的语句由点分隔。函数定义的语法是:
FORM <name> <arguments>.
<statements>.
ENDFORM.
我会用它作为一个最小的例子。
这是我在 haskell 和解析器中编写相应类型的尝试。 GenStatement
-构造函数用于除上述函数定义之外的所有其他语句。
module Main where
import Control.Applicative
import Data.Functor.Identity
import qualified Text.Parsec as P
import qualified Text.Parsec.String as S
import Text.Parsec.Language
import qualified Text.Parsec.Token as T
type Args = String
type Name = String
data AbapExpr -- ABAP Program
= Form Name Args [AbapExpr]
| GenStatement String [AbapExpr]
deriving (Show, Read)
lexer :: T.TokenParser ()
lexer = T.makeTokenParser style
where
caseSensitive = False
keys = ["form", "endform"]
style = emptyDef
{ T.reservedNames = keys
, T.identStart = P.alphaNum <|> P.char '_'
, T.identLetter = P.alphaNum <|> P.char '_'
}
dot :: S.Parser String
dot = T.dot lexer
reserved :: String -> S.Parser ()
reserved = T.reserved lexer
identifier :: S.Parser String
identifier = T.identifier lexer
argsP :: S.Parser String
argsP = P.manyTill P.anyChar (P.try (P.lookAhead dot))
genericStatementP :: S.Parser String
genericStatementP = P.manyTill P.anyChar (P.try dot)
abapExprP = P.try (P.between (reserved "form")
(reserved "endform" >> dot)
abapFormP)
<|> abapStmtP
where
abapFormP = Form <$> identifier <*> argsP <* dot <*> many abapExprP
abapStmtP = GenStatement <$> genericStatementP <*> many abapExprP
使用以下输入测试解析器会导致奇怪的行为。
-- a wrapper for convenience
parse :: S.Parser a -> String -> Either P.ParseError a
parse = flip P.parse "Test"
testParse1 = parse abapExprP "form foo arg1 arg2 arg2. form bar arg1. endform. endform."
结果
Right (GenStatement "form foo arg1 arg2 arg2" [GenStatement "form bar arg1" [GenStatement "endform" [GenStatement "endform" []]]])
所以看起来第一个分支总是失败,只有第二个通用分支成功。但是,如果第二个分支(解析通用语句)被注释,则解析表单突然成功:
abapExprP = P.try (P.between (reserved "form")
(reserved "endform" >> dot)
abapFormP)
-- <|> abapStmtP
where
abapFormP = Form <$> identifier <*> argsP <* dot <*> many abapExprP
-- abapStmtP = GenStatement <$> genericStatementP <*> many abapExprP
现在我们得到
Right (Form "foo" "arg1 arg2 arg2" [Form "bar" "arg1" []])
这怎么可能?似乎第一个分支成功了,那么为什么它在第一个示例中不起作用 - 我错过了什么?
非常感谢!
帮我看看你的解析器 genericStatementP
解析 任何 字符直到出现一个点(你正在使用 P.anyChar
)。因此它无法识别您的词法分析器的保留关键字。
我认为你必须定义:
type Args = [String]
和:
argsP :: S.Parser [String]
argsP = P.manyTill identifier (P.try (P.lookAhead dot))
genericStatementP :: S.Parser String
genericStatementP = identifier
通过这些更改,我得到以下结果:
Right (Form "foo" ["arg1","arg2","arg2"] [Form "bar" ["arg1"] []])