2016-09-12 4 views
2

FParsec에서 균등 json 형 배열을 구문 분석하는 데 문제가 있습니다. 문제를 재현 한 짧은 예제로 분해했습니다. 내가 P_INT_VALUEP_BOOL_VALUE<|> 연산자를 사용하여 순서를 바꿀 경우FParsec에서 균등리스트를 어떻게 파싱 할 수 있습니까?

test P_LIST_VALUE "[1,2,3]" 
Success: CList [CInt 1L; CInt 2L; CInt 3L] 

test P_LIST_VALUE "[true,false]" 
Failure: Error in Ln: 1 Col: 2 
[true,false] 
^ 
Expecting: integer number (64-bit, signed) or ']' 

는 다음 [true,false]이 성공적으로 구문 분석 : 나는 그것을 밖으로 시도하는 test 기능을 사용하면

#r @"..\packages\FParsec.1.0.2\lib\net40-client\FParsecCS.dll" 
#r @"..\packages\FParsec.1.0.2\lib\net40-client\FParsec.dll" 

open System 
open FParsec 

let test p str = 
     match run p str with 
     | Success(result, _, _) -> printfn "Success: %A" result 
     | Failure(errormsg, _, _) -> printfn "Failure: %s" errormsg 


type CValue = CInt of int64 
      | CBool of bool 
      | CList of CValue list 

let P_WHITESPACE = spaces 
let P_COMMA = pstring "," 
let P_L_SBRACE = pstring "[" .>> P_WHITESPACE 
let P_R_SBRACE = P_WHITESPACE >>. pstring "]" 

let P_INT_VALUE = pint64 |>> CInt 

let P_TRUE = stringReturn "true" (CBool true) 
let P_FALSE = stringReturn "false" (CBool false) 
let P_BOOL_VALUE = P_TRUE <|> P_FALSE 


let P_LIST_VALUE = 
    let commaDelimitedList ptype = sepBy (ptype .>> P_WHITESPACE) (P_COMMA .>> P_WHITESPACE) 
    let delimitedList = (commaDelimitedList P_INT_VALUE) <|> (commaDelimitedList P_BOOL_VALUE) 
    let enclosedList = between P_L_SBRACE P_R_SBRACE delimitedList 
    enclosedList |>> CList 

, 나는 다음과 같은 결과를 얻을 수 그러나 [1,2,3]도 비슷한 오류와 함께 실패합니다. 그래서 기본적으로, 내가 처음 사용하는 구문 분석기가 사용하려고 시도한 것입니다.

<|> LHS가 사용자 상태를 변경하는 경우 운영자가 RHS 파서를 시도하지 않는다는 것을 이해합니다. 그러나 어떻게 될지 알 수 없습니다. P_BOOL_VALUE 및 P_INT_VALUE에는 시작 문자가 공통으로 없으므로 잘못된 데이터 유형을 구문 분석 할 때 두 가지 모두 즉시 실패해야합니다. Ints는 '거짓'또는 '참'으로 시작하지 않으며 bool은 숫자로 시작하지 않습니다.

내가 뭘 잘못하고 있니?

+1

귀하의 코드를 테스트하지는 않았지만 두 힌트를 '시도'에 포함 시키십시오. – bytebuster

+0

'시도 '가 필요하지 않아야하며 방금 테스트 한대로 문제가 해결되지 않습니다. 솔직히 이것이 작동하지 않는 이유를 찾을 수 없습니다. – Tarmil

답변

2

아, 알아 냈습니다. 오류 메시지의 힌트는 or ']'입니다. 문제는 빈 입력에 sepBy이 성공하므로 t에 도달하면 빈 목록과 함께 성공적으로 반환 된 다음 제어가 between으로 돌아가므로 시도가 끝나고 ]이라는 종료를 찾지 못합니다.

let P_LIST_VALUE = 
    let commaDelimitedList ptype = sepBy1 (ptype .>> P_WHITESPACE) (P_COMMA .>> P_WHITESPACE) 
    let delimitedList = (commaDelimitedList P_INT_VALUE) <|> (commaDelimitedList P_BOOL_VALUE) <|> preturn [] 
    let enclosedList = between P_L_SBRACE P_R_SBRACE delimitedList 
    enclosedList |>> CList 

주 대신 sepBysepBy1의 사용과를 처리 할 수 ​​<|> preturn []의 추가 :

이 솔루션은이처럼 INT/부울 특정 파서에서 빈 목록 케이스를 이동하는 것입니다 빈 경우는 delimitedList에 한 번만.

부수적으로, 나는 정확한 응용 프로그램을 모르지만, 일반적으로 파서에서 타이핑을 시행하는 것은 좋지 않습니다. 이를 구현하는 가장 일반적인 방법은 commaDelimitedList (P_INT_VALUE <|> P_BOOL_VALUE) (원래 commaDelimitedList)을 구문 분석 한 다음 후속 분석 단계에서 타이핑을 확인하는 것입니다.

+1

성공! 그게 효과가있어! 그래서 그것을 올바르게 이해한다면 파서가 P_INT_VALUE를 실패시키는 't'에 부딪치게됩니다. 그 다음에 sepBy는 빈 입력을 받고 성공했다고 가정합니다. 다음 사이에 ']'을 구문 분석하고 실패합니다. 사실 파서에서 이기종 목록을 수락 한 다음 분석 단계에서 유효성을 검사한다고 생각했습니다. 웬일인지 파서에서 다루는 것이 더 낫다고 생각했습니다. 이 길로가는 단점은 무엇입니까? –