【问题标题】:Shorten active pattern in a recursive descent parser缩短递归下降解析器中的活动模式
【发布时间】:2017-05-15 09:19:53
【问题描述】:

我想构建类型为 Token list -> Ast * Token list 的活动模式。

我有一个简单的帕斯卡语法<program> ::= "program" <id> ";" <block> "."。通过将代码改造成这个语法,我必须使ProgramIdentifierBlock 模式像:

let rec (|Program|_|) tokens =
    match tokens with
    | Token.Program :: tokens ->
        match tokens with
        | Identifier (identifier, tokens) ->
            match tokens with
            | Semicolon :: tokens ->
                match tokens with
                | Block (block, tokens) ->
                    match tokens with
                    | Dot :: tokens -> Some (Ast.Program (identifier, block), tokens)
                    | _ -> failwithf "Expected %A" Dot
                | _ -> failwith "Expected Block"
            | _ -> failwithf "Expected %A" Semicolon
        | _ -> failwith "Expected Identifier"
    | _ -> failwithf "Expected %A" Token.Program

and (|Identifier|_|) tokens =
    match tokens with
    | Token.Identifier identifier :: tokens ->
        Some (Ast.Identifier identifier, tokens)
    | _ -> failwith "Expected Identifier"
...

因为有很多重复的OptionNone,所以我尝试将模式缩短为:

type rec (|Program|_|) tokens =
    match tokens with
    | Token.Program :: Identifier (ident, Semicolon :: Block (block, Dot :: rest)) ->
        Some (Ast.Program (identifier, block), rest)
    | _ -> None

新模式能否按预期工作?我怎样才能使活动模式更短,但不知何故也会在输入错误时返回错误?

另外,我读到 FParsec 使用monad 来解析和保留错误,我怎么能做出类似 FParsec 的简单计算表达式。

【问题讨论】:

  • 对于编写计算表达式,fsharpforfunandprofit.com/series/computation-expressions.html 是您的最佳指南。
  • 另外,你的 AST 设计对我来说有点奇怪。为什么IdentifierBlock 联合案例吸收了“内部”的以下标记,而Program 标记却没有?我希望看到更像match tokens with Identifier ident :: tokens 的东西,即Identifier 案例仅“吸收”标识符的名称。将以下所有标记都放在 Identifier 案例“内部”是没有意义的。我不知道我是否清楚地传达了我想说的话;你明白我的意思吗,还是我应该试着改写一下?
  • @rmunn 所有活动模式都会吸收令牌并返回剩余的令牌。因为我从stackoverflow.com/a/4418823/7821462 读到它们应该有Token list -> Ast * Token list 类型。
  • 您的 Identifier 令牌违反了该规则。无论你有什么创建Identifier的函数都应该有一个类似Token list -> Identifier * Token list的签名,但它的签名是Token list -> Identifier。其余的代币已被吸收到Identifier 代币的后半部分,这不是应该的。
  • @rmunn 也许你应该再检查一下签名,我添加了Identifier 模式以获得清晰的视觉效果,它返回Ast.Identifier * Token list

标签: .net parsing linked-list f# pattern-matching


【解决方案1】:

简单的方法就是把所有东西都压平:

let rec private (|Program|_|) tokens =
    match tokens with
    | Token.Program ::Identifier (identifier, Semicolon :: Block (block,Dot :: tokens) ) ->Some (Ast.Program (identifier, block), tokens)
    | Token.Program ::Identifier (identifier, Semicolon :: Block (block,_) ) ->failwith "Expected %A" Dot 
    | Token.Program ::Identifier (identifier, Semicolon ::_ ) ->failwith "Expected %A" Block
....

【讨论】:

  • 你知道如何冲刺标识符或阻止标识符吗?例如当我有type Token = | Identifier of string 时,我想宣布“预期标识符”,而failwithf "Expected %A" (Identifier "") 将无法按预期工作。
  • 暂时想不出来
猜你喜欢
  • 2015-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多