如何在运算符优先级解析器中插入缩进检查?

时间:2017-05-03 09:21:14

标签: parsing f# fparsec

我正在处理我正在制作的语言的解析阶段,并且遇到以下问题。

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

1 个答案:

答案 0 :(得分:0)

expr s

我在2.5周前没有意识到这一点,但是当一个新的块被打开并且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 被调用时会发生的事情是,术语解析器被新的缩进覆盖,并且没有办法备份它退出时恢复它。为了我的目的,我做了一些环顾四周并设法调整了Pratt自上而下的解析方法。

道格拉斯·克罗克福德(Douglas Crockford)就这个方法提出了a talk

led

执行实际优先级解析的tdopselect distinct top 1000 u.id as userID , u.firstName as userFirstName , u.email as userEmail , u.phone as userPhone , ueo.opensEmailCounter , ush.opensSmsCounter from dbo.Users u left join ( select userID , count(*) as opensEmailCounter from dbo.UserEmailsOpens where targetID = 4 group by userID ) ueo on u.id = ueo.userID left join ( select userID , count(*) as opensSmsCounter from dbo.UserSmsHistory where targetID = 4 and opened = 1 group by userID ) ush on u.id = ush.userID where u.deleted = 0 and IsNull(u.firstName, '') != '' and IsNull(u.email, '') != '' and IsNull(u.phone, '') != '' 函数长度为10行。上面只是我所用语言的full parser的片段 - 在语法方面它类似于F#并且是缩进敏感的。这是Douglas Crockford F# translation更为直截了当的Javascript example