2017-05-03 1 views
1

나는 만들고있는 언어에 대한 파싱 단계를 진행하고 있으며 다음과 관련하여 어려움을 겪고 있습니다. 연산자 우선 순위 구문 분석기에 들여 쓰기 검사를 삽입하는 방법은 무엇입니까?

let test2 = // I'd like this to be an error. 
    """ 
    2 
    + 2 
    """ 

let result = run (spaces >>. expr) test2 

val result : ParserResult<CudaExpr,unit> = 
    Success: Add (LitInt32 2,LitInt32 2) 

은 이미 용어가 잘못

2 + 
2 

들여 쓰기 나에게 오류를 줄 때 다음과 같은 예를 만들기 위해 관리하지만, 운전자가 잘못 들여 쓰기 수준에하지 않는 경우. 구문 분석 전 검사와 같은 것이 필요합니다.

let operators expr i = 
    let f expr (s: CharStream<_>) = if i <= s.Column then expr s else pzero s 
    opp.TermParser <- f expr 
    f opp.ExpressionParser 

위의 기능은 운영자 단계가 어떻게 구성되어 있는지 그리고 당신이 볼 수 있듯이, 용어 파서는 들여 쓰기 검사를 수행하는 기능에 싸여 얻을하지만 마지막 줄에 결함이 있습니다.

다음은 전체 구문 분석기의 간단한 예입니다.

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

open FParsec 

type Expr = 
    | V of string 
    | Add of Expr * Expr 

let identifier = many1Satisfy2L isAsciiLetter (fun x -> isAsciiLetter x || isDigit x || x = ''') "identifier" .>> spaces |>> V 

let indentations expressions (s: CharStream<_>) = 
    let i = s.Column 
    let expr_indent expr (s: CharStream<_>) = 
     let expr (s: CharStream<_>) = if i <= s.Column then expr s else pzero s 
     many1 expr s 

    expr_indent (expressions i) s 

let expr = 
    let opp = new OperatorPrecedenceParser<_,_,_>() 
    opp.AddOperator(InfixOperator("+", spaces, 6, Associativity.Left, fun x y -> Add(x,y))) 

    let operators expr i = 
     let f (s: CharStream<_>) = if i <= s.Column then expr s else pzero s 
     opp.TermParser <- f 
     f opp.ExpressionParser 

    let rec expr s = indentations (operators identifier) s 

    expr 

let test2 = // I'd like this to be an error. 
    """ 
    a 
    + 
    b 
    """ 

let result = run (spaces >>. expr) test2 

전체 구문 분석기는 here입니다.

+2

[사용자 상태] (http://www.quanttec.com/fparsec/users-guide/parsing-with-user-state.html)를 활용하여 들여 쓰기 레벨을 추적하고 싶을 것이라고 상상해보십시오. 표현의 조건을 확인한 다음 연산자 나 다른 용어를 파싱 할 때 일관성이 있는지 확인하십시오. – rmunn

+0

나는 그것을 고려했다. 그러나 나는이 방법이 더 단순하다는 것을 알았다. –

답변

0
let operators expr i = 
    let f (s: CharStream<_>) = if i <= s.Column then expr s else pzero s 
    opp.TermParser <- f 
    f opp.ExpressionParser 

내가 2.5 주 전에 그것을 실현,하지만 새로운 블록을 열 얻고 expr s가 호출 될 때 발생하는 용어 파서는 새로운 들여 쓰기로 덮어 가져옵니다이며 백업 할 수있는 방법이 없기하지 않았고 출구에서 복원하십시오. 나는 주변을 둘러 보았고 Pratt의 분석법을 내 목적에 맞게 분석 할 수 있었다.

여기는 더글러스 크록 포드 (Douglas Crockford)의 a talk입니다. 실제 우선 파싱 할

let poperator: Parser<_,_> = 
    let f c = (isAsciiIdContinue c || isAnyOf [|' ';'\t';'\n';'\"';'(';')';'{';'}';'[';']'|] c) = false 
    (many1Satisfy f .>> spaces) 
    >>= fun token -> 
     match dict_operator.TryGetValue token with 
     | true, x -> preturn x 
     | false, _ -> fail "unknown operator" 

let rec led poperator term left (prec,asoc,m) = 
    match asoc with 
    | Associativity.Left | Associativity.None -> tdop poperator term prec |>> m left 
    | Associativity.Right -> tdop poperator term (prec-1) |>> m left 
    | _ -> failwith "impossible" 

and tdop poperator term rbp = 
    let rec f left = 
     poperator >>= fun (prec,asoc,m as v) -> 
      if rbp < prec then led poperator term left v >>= loop 
      else pzero 
    and loop left = attempt (f left) <|>% left 
    term >>= loop 

let operators expr i (s: CharStream<_>) = 
    let expr_indent expr (s: CharStream<_>) = expr_indent i (<=) expr s 
    let op s = expr_indent poperator s 
    let term s = expr_indent expr s 
    tdop op term 0 s 

ledtdop 기능과 10 줄 길다. 위의 코드는 제가 작성한 언어에 대한 full parser의 조각입니다. 구문과 관련하여 F #과 유사하며 들여 쓰기가 중요합니다. 더글러스 크로 포드 (Douglas Crockford)의 Javascript example에 대해보다 직설적 인 F# translation이 있습니다.