2014-07-08 6 views
1

я должен написать синтаксический анализатор, который анализирует пары ключ-значение в файл, который выглядит следующим образом:fparsec ключ-значение синтаксический анализатор не может разобрать

as235 242kj25klj Pairs:A=a1|B=b1|C=c1

kjlkjlkjlkj Pairs:A=a2|B=b2|C=c2

Обратите внимание, что строки содержат некоторое количество мусора, этикетку, а затем пары ключ-значение.

The F # код, который я написал следующий:

#r"FParsec.dll" 

open FParsec 

let parse keys label = 
    let pkey = keys |> Seq.map pstring |> choice 

    let pvalue = manyCharsTill anyChar (anyOf "|\n") 

    let ppair = pkey .>> (skipChar '=') .>>. pvalue 

    let ppairSeq = many ppair 

    let pline = skipManyTill anyChar (pstring label) 
       >>. ppairSeq .>> newline 

    let pfile = many (opt pline) |>> Seq.choose id 

    run pfile 
    >> function 
    | Success (result, _, _) -> result 
    | Failure (errorMsg, _, _) -> failwith errorMsg 

""" 
as235 242kj25klj Pairs:A=a1|B=b1|C=c1 

lkjlkjlkjlkj Pairs:A=a2|B=b2|C=c2 



""" 
|> parse ["A";"B";"C"] "Pairs:" 
|> List.ofSeq 
|> printfn "%A" 

Ожидаемый результат:

[[("A","a1"); "B","b1"; "C","c1"] 
[("A","a2"); "B","b2"; "C","c2"]] 

... но вместо этого я получаю следующее сообщение об ошибке:

System.Exception: Error: Error in Ln: 8 Col: 1 
Note: The error occurred at the end of the input stream. 
Expecting: any char or 'Pairs:' 

Любые идеи о том, как я могу исправить этот парсер?

Спасибо!

UPDATE: после комментария Stephan я попытался исправить это, но безуспешно. Это одна из моих последних попыток, которую я ожидал, но это не так.

let pkey = keys |> Seq.map pstring |> choice 

let pvalue = manyCharsTill anyChar (anyOf "|\n") 

let ppair = pkey .>> (skipChar '=') .>>. pvalue 

let ppairSeq = manyTill ppair newline 

let pnonEmptyLine = 
    skipManyTill anyChar (pstring label) 
    >>. ppairSeq 
    |>> Some 

let pemptyLine = spaces >>. newline >>% None 

let pline = pemptyLine <|> pnonEmptyLine 

let pfile = manyTill pline eof |>> Seq.choose id 

Теперь сообщение об ошибке:

Error in Ln: 2 Col: 5 

    as235 242kj25klj Pairs:A=a1|B=b1|C=c1 

    ^

Expecting: newline 
+1

Параметр 'pline' анализатор, кажется, не в состоянии после употребления ввода, как' anyChar' также потребляет переводы строк, которые, вероятно, не то, что вы разыскивается. Обратите внимание, что 'many (opt pline)' в конечном итоге приведет к исключению, так как 'opt x' может преуспеть, не потребляя входные данные. Чтобы исправить это, вы можете пропустить пустые строки как (конечные) пробелы, или ваш пустой анализатор строк должен фактически использовать новую строку. –

+0

Думаю, я понимаю, что вы имеете в виду, но я не знаю, как исправить парсер. Я отправил свою попытку как обновление – vidi

ответ

4

Мой коллега нашел решение, и я отправляю здесь для тех, кто имеют аналогичные проблемы. Также синтаксический анализатор еще лучше, потому что ему не нужен набор ключей. Я использует левую сторону «=» в качестве ключа и с правой стороны в качестве значения:

let parse label str = 
    let poperand = manyChars (noneOf "=|\n") 

    let ppair = poperand .>> skipChar '=' .>>. poperand 

    let ppairSeq = sepBy ppair (pchar '|') 

    let pLineWithPairs = skipManyTill anyChar (pstring label) >>. ppairSeq |>> Some 

    let pLineWithoutPairs = (restOfLine false) >>% None 

    let pLogLine = (attempt pLineWithPairs) <|> pLineWithoutPairs 

    let pfile = sepBy pLogLine newline |>> Seq.choose id 

    match run pfile str with 
    | Success (result, _, _) -> result 
    | Failure (errorMsg, _, _) -> sprintf "Error: %s" errorMsg |> failwith 
+1

Обратите внимание, что ваш парсер 'pLineWithPairs' с радостью проанализирует несколько строк (которые не содержат метки). Я бы рекомендовал использовать 'manySatisfyL' для определения' poperand' для повышения производительности. Если вы хотите избежать выделения значений опций 'Some x', вы можете разобрать пустые строки как« пробельные символы »между интересующими вас линиями. –