【发布时间】:2017-05-15 09:19:53
【问题描述】:
我想构建类型为 Token list -> Ast * Token list 的活动模式。
我有一个简单的帕斯卡语法<program> ::= "program" <id> ";" <block> "."。通过将代码改造成这个语法,我必须使Program、Identifier 和Block 模式像:
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 设计对我来说有点奇怪。为什么
Identifier和Block联合案例吸收了“内部”的以下标记,而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