2017-05-03 6 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のトップダウン解析方法を自分の目的に合わせて調整しました。

ここでは、ダグラス・クロックフォードの方法による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です。

関連する問題