2017-01-09 3 views
3
내가 fparsec와 아래 달성하기 위해 노력하고

에 대한 차별 조합과 대안 및 노동 조합 (1 + (2 * 3)) // DSL 샘플 입력 (재귀) 나는 fparsec에서fparsec 구문 분석 DSL

type AirthmeticExpression =   
    | Constant of float 
    | AddNumber of AirthmeticExpression * AirthmeticExpression 
    | Mul of AirthmeticExpression * AirthmeticExpression 

이 createParserForwardedToRef

let parseExpression, implementation = createParserForwardedToRef<AirthmeticExpression, unit>();; 

let parseAdd = between pstring"(" pstring ")" (tuple2 (parseExpression .>> pstring " + ") parseExpression) |>> AddNumber 

let parseMul = between pstring"(" pstring ")" (tuple2 (parseExpression .>> pstring " * ") parseExpression) |>> Mul 

implementation := parseConstant <|> parseAdd <|> parseMull 

로 추가 및 도난뿐만 파서 (P1) 입력을 소비하고 P2를 시도하지 않습니다 실패하면 fparsec 문서는 대안을 말한다.

내 경우 Add와 Mul은 모두 연산자 이전에 동일한 패턴을 가지고 있으므로 p1 만 작동합니다. 입력을 구문 분석 할 수 있도록 어떻게 리팩토링 할 수 있습니까? fparsec doc 솔루션 예제에서, 구문 분석을하고 Discriminated union 인스턴스를 생성하지 않은 것처럼 작동했습니다. 내 경우 Add 또는 Mul 중 하나를 만들 수 있도록 일치하는 패턴을 알아야합니다.

+0

해결하기 위해 접두어 연산자 개념을 사용할 수 있습니다. 하지만 내 DSL 입력의 가독성을 위해 중위 연산자 개념을 원합니다. + (1,2)와 같은 간단한 연산자 접두사 개념을 위해. 그러나 문자열 ({in})이나 숫자 ({between})와 같은 복잡한 연산자를 사용하여 문법을 발전시킵니다. 접두사는 가독성이 떨어집니다. – abhishek

+0

대안을'시도 '로 감싸십시오. –

+0

Fyodor에게 답장을 보내 주셔서 감사합니다. 나는 f #과 fprasec에 익숙하지 않다. 당신은 그 코드를 더 잘 만들거나 더 잘 만들 수 있는가? 다중 시도 구문 분석기를 사용하면 어떤 식별자가 일치했는지 알 수 있으므로 식별자 식별자를 적절하게 인스턴스화 할 수 있습니다. 'pstring "+"<|> pstring "*"' 과 같은 피연산자에 대한 문자열 파서를 만든 다음 tuple3을 사용하여 (피연산자, 연산자, righthandoperand) 가져 오려고했습니다. 그런 다음 Operator에서 if/else를 사용하여 어떤 연산자가 일치하고 인스턴스가 내 discriminator 유형인지 결정합니다. 그것은 작동하지만 올바른 기능적 접근법이 아니라고 생각합니다. – abhishek

답변

1

편집 : 원래 댓글은 @FyodorSoikin에 의해 지적 된 것과 마찬가지로 결함이있었습니다.

어제부터 연산자에 대한 공통 파서를 만든 다음이를 사용하는 작업에 대해 단일 파서를 사용하는 것으로 의견에 옳은 길을 걷고 있습니다. 이 기능을보다 효율적으로 사용하려면 운영자 파서가 적용 할 조합 사례를 반환하도록 할 수 있습니다. 이렇게하면 전체 작업을 구문 분석 할 때 함수로 호출 할 수 있습니다.

let parseExpression, implementation = createParserForwardedToRef<AirthmeticExpression, unit>();; 

let parseOperator = // : Parser<AirthmeticExpression * AirthmeticExpression -> AirthmeticExpression> 
     (pstring " + " |>> AddNumber) 
    <|> (pstring " * " |>> Mul) 

let parseOperation = 
    pipe3 parseConstant parseOperator parseConstant 
     (fun x op y -> op (x, y)) // Here, op is either AddNumber or Mul 
    |> between (pstring "(") (pstring ")") 

implementation := parseConstant <|> parseOperation 

원래 코멘트 :

하나의 가능성이 코멘트에 말했듯이 attempt을 사용하는 것입니다,하지만 기능은 일반적으로 최후의 수단으로 사용되어야한다. 더 나은 해결책은 포장을 제외하는 것입니다.

let parseExpression, implementation = createParserForwardedToRef<AirthmeticExpression, unit>();; 

let parseAdd = tuple2 (parseExpression .>> pstring " + ") parseExpression |>> AddNumber 

let parseMul = tuple2 (parseExpression .>> pstring " * ") parseExpression |>> Mul 

let parseOp = between (pstring "(") (pstring ")") (parseAdd <|> parseMul) 

implementation := parseConstant <|> parseOp 
+0

하지만 'parseAdd'가 첫 번째 표현식을 소비 한 다음 곱셈 기호를 만나서 실패하고 스트림을 절반 만 소비하면 결과가'parseMul' 파서도 실패하게 만들지 않을까요? –

+0

@FyodorSoikin 실제로, 그것은 더 좋은 방법을 게시하는 아주 좋은 접근 방법이 아니 었습니다. – Tarmil

+0

나는 이것이 또한 작동하지 않을까 두렵다. "1 + 2"구문 분석을 시도하십시오. –