2014-05-16 1 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많은 불평하는 원인이 빈 문자열을 받아 토큰의 원인이 제로 문자열을 받아들입니다.

다음과 같이 변경하여 작업해야하는 라인인지 확인했습니다. 그건 당신이contentEscapeds 그들 사이contentNormals 일치 할 필요는 말한다 때문에

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

또한 나는 멀리 stringsSepBy contentNormal contentEscaped에서 얻었다. 따라서 {{b}} c는 괜찮지 만 {{b}}, {{b}} c와 {b}}는 실패합니다.

+0

고마워요! 그냥 String으로 파이핑하면됩니다.concat 완벽하게 작동합니다 ('many1 (contentNormal <|> contentEscaped) | >> String.Concat'),하지만 올바르게 작동하도록 notEmpty를 얻을 수 있는지 알고 싶습니다. – jjmac

1

notEmpty은 입력을 소비하는 데 사용할 수 있습니다. 입력을 소비하지 않고 파서가 성공하면 파서의 "현재 위치"는 앞으로 이동하지 않으므로 명령문이 many 안에 있으면 예외없이 무한 루프가됩니다. stringsSepBy 성공 제로 요소를 분석이다 제로 요소를 얻는 경우에, 당신은 그것을 실패 notEmpty을 사용할 수 또한

stringsSepBy contentNormal contentEscaped |> notEmpty 

을, 당신이 허용해야하므로, 태그에 공백을 포함 할 수 있습니다 구문 분석하기 위해 전체 예제를 얻기 위해 노력 ident이 일치하도록 공간을 포함 :

또 다른 약간의 조정 만 Token list 오히려 Token list * unit 이상의 튜플 반환하는 것입니다
let isIdentifierChar c = isLetter c || isDigit c || c = '_' || c = ' ' 

(uniteof의 결과입니다) :

let tokens = many token .>> eof 
+0

도움을 주셔서 감사합니다! 중괄호 이스케이프를 올바르게 코딩했는지 궁금합니다. notEmpty에 파이프를 연결하면 이스케이프 처리 된 브레이싱이 문자열의 시작 부분이나 끝 부분에 있지 않은 경우에만 작동합니다. 예를 들어,''{{b}} c "'는 구문 분석하지만, 만약 a 나 c를 제거하면 에러가납니다. 어떤 충고? – jjmac

+0

'stringsSepBy'는이 경우에 이해가되지 않습니다. 생각해 보면 이스케이프 처리 된 중괄호로 구분 된 일반 내용보다는 이스케이프 처리 된 중괄호 또는 일반 내용이 될 수있는 많은 문자열을 찾고 있습니다. 그래서 질의 대답은이 문제에 대한 해결책입니다. –