2017-10-14 133 views
3

我試圖用haskell中的Parsec解析Abap語言的一個片段。 Abap中的陳述是用點分隔的。函數定義的語法是:使用Parsec解析命令式語言的奇怪行爲

FORM <name> <arguments>. 
    <statements>. 
ENDFORM. 

我將使用它作爲最小示例。 這是我在編寫haskell和解析器中的對應類型時的嘗試。除了上面描述的函數定義之外,其他所有語句都可以使用該組件。

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" []]]]) 

如此看來第一BRACH總是失敗,只有第二個通用分支是成功的。但是,如果第二分支(解析通用報表)的評論解析形式的突然成功:

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" []]) 

這怎麼可能?看起來第一個分支成功了,爲什麼它在第一個例子中不起作用 - 我錯過了什麼?

非常感謝提前!

回答

2

尋找我,你的解析器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"] []]) 
+0

就像一個魅力!非常感謝! – jules