2014-05-16 65 views
2

我試圖解析與FParsec,其中每個令牌可以是一個文本或標籤的塊標記列表 - 例如:如何解析標記列表與FParsec

這是一個{類型測試}測試的,並且它{成功或失敗}

這裏是解析器:

type Parser<'t> = Parser<'t, unit> 

type Token = 
| Text of string 
| Tag of string 

let escape fromString toString : Parser<_> = 
    pstring fromString |>> (fun c -> toString) 

let content : Parser<_> = 
    let contentNormal = many1Satisfy (fun c -> c <> '{' && c <> '}') 
    let openBraceEscaped = escape "{{" "{" 
    let closeBraceEscaped = escape "}}" "}" 
    let contentEscaped = openBraceEscaped <|> closeBraceEscaped 
    stringsSepBy contentNormal contentEscaped 

let ident : Parser<_> = 
    let isIdentifierFirstChar c = isLetter c || c = '_' 
    let isIdentifierChar c = isLetter c || isDigit c || c = '_' 
    spaces >>. many1Satisfy2L isIdentifierFirstChar isIdentifierChar "identifier" .>> spaces 

let text = content |>> Text 

let tag = 
    ident |> between (skipString "{") (skipString "}") 
    |>> Tag 

let token = text <|> tag 
let tokens = many token .>>. eof 

以下測試工作:

> run token "abc def" ;; 
val it : ParserResult<Token,unit> = Success: Text "abc def" 

> run token "{abc def}" ;; 
val it : ParserResult<Token,unit> = Success: Tag "abc def" 

而是試圖在異常運行令牌結果:

> run tokens "{abc} def" ;; 
System.InvalidOperationException: (Ln: 1, Col: 10): The combinator 'many' was 
    applied to a parser that succeeds without consuming input and without 
    changing the parser state in any other way. (If no exception had been raised, 
    the combinator likely would have entered an infinite loop.) 

我已經超過this stackoverflow question但沒有我嘗試過的作品。我甚至增加了以下內容,但我得到了同樣的異常:

let tokenFwd, tokenRef = createParserForwardedToRef<Token, unit>() 
do tokenRef := choice [tag; text] 
let readEndOfInput : Parser<unit, unit> = spaces >>. eof 
let readExprs = many tokenFwd 
let readExprsTillEnd = readExprs .>> readEndOfInput 

run readExprsTillEnd "{abc} def" // System.InvalidOperationException ... The combinator 'many' was applied ... 

我相信問題是stringsSepBy內容,但我想不出任何其他方式來獲得與逃脫的項目串

任何幫助將不勝感激 - 我已經經歷了幾天,現在無法弄清楚。

回答

2

stringsSepBy接受零個字符串,導致令牌接受一個空字符串,導致很多抱怨。

我將其更改爲以下內容以驗證這是您需要處理的行。

many1 (contentNormal <|> contentEscaped) |>> fun l -> String.concat "" l 

而且我從stringsSepBy contentNormal contentEscaped了,因爲說你需要匹配contentNormalscontentEscapeds在他們之間。所以{{b}} c可以,但{{b}},{{b}} c和{{b}}將失敗。

+0

謝謝!只是管道到字符串。concat可以很好地工作('many1(contentNormal <|> contentEscaped)| >> String.Concat'),但是我想看看我能不能得到空的也能正常工作 – jjmac

1

notEmpty可用於消耗輸入。如果你沒有使用任何輸入,而是讓解析器成功,那麼解析器的「當前位置」不會向前移動,因此當執行該操作的語句位於many內部時,它將進入無限循環而沒有異常。 stringsSepBy是成功和解析零個元素,你可以使用notEmpty失敗,如果它得到零個元素:

stringsSepBy contentNormal contentEscaped |> notEmpty 

而且,我試圖讓你完整的例子來分析,該標籤可以包含空格,所以你需要允許ident包括空格,以匹配:

let isIdentifierChar c = isLetter c || isDigit c || c = '_' || c = ' ' 

另一個小的調整將只返回一個Token list而不是Token list * unit元組(uniteof結果):

let tokens = many token .>> eof 
+0

謝謝你的幫助!我不知道是否我的支架轉義編碼正確 - 當我pipe to notEmpty時,轉義支撐只有在不在字符串的開頭或結尾時才起作用。例如,''a {{b}} c「'解析,但是如果我刪除了a或c,那麼我得到一個錯誤。有什麼建議? – jjmac

+0

'stringsSepBy'在這種情況下沒有意義。如果你仔細想想,你真的需要尋找很多字符串,這些字符串可以是大括號或普通內容,而不是被大括號分開的普通內容。所以jyoung的回答就是解決這個問題 –