我正在尝试通过使用FParsec的Monadic解析器组合来实现分析器。我还使用了一个缩进模块,但这对于当前问题并不重要。
所以我试图解析我的小AST的这个分支:
type Identifier = string
type Expression =
...
| Call of Identifier * Expression list
...
type Program = Program of Expression list
我有这个实现:
// Identifier parser
let private identifier =
many1Satisfy2L isLetter
(fun c -> isLetter c || isDigit c) "identifier"
// Monadic parser
let call = parse {
let! id = identifier
let! parameters = sepBy parameter spaces1
return Call(id, parameters)
}
and expression =
... // <|>
attempt call // <|>
...
parameter
表示函数调用中所有可接受的表达式作为参数:
// All possible parameter
let parameter =
attempt pint32 <|> // A number, for example.
attempt (identifier |>> fun callid -> Call(callid, [])) <|>
attempt (between (pstring "(") (pstring ")") call)
如您所见,解析器中有两个使用“ C / call”的元素。它们与此对应,例如,
add x 1 // Call the function `add` and passing it another call (`x`) and the simple literal `1`
add (add 8 2) 10 // Call the function `add` and passing it another call (`add 8 2`) and the simple literal `10`
当然,这些元素也可以交织在一起:
add (add x 1) 7
我显然无法解决的问题,否则我不会问这个问题,那就是生成的树看起来不像预期的那样:
add x 1
给出:
Success: Program [Call ("add",[Call ("x",[]); Literal (Int 1)])]
换句话说,解析器似乎将以下x
标识为x
的参数。
但是,第二种方法可行。 add (add 8 2) 10
给出:
Success: Program
[Call
("add",[Call ("add",[Literal (Int 8); Literal (Int 2)]); Literal (Int 10)])]
你能让我步入正轨吗?
答案 0 :(得分:1)
在我看来,以下几行与x
相匹配:
attempt (identifier |>> fun callid -> Call(callid, [])) <|>
单个标识符被视为call
。
因此,您不必担心会得到:[Call ("add",[Call ("x",[]); Literal (Int 1)])
您认为这是错误的,您的预期结果是什么?我本以为这是一个identifer取消引用表达式。
还有一个提示,您似乎选择了OCaml风格的调用功能,例如:f x y
也许您也应该采用OCaml风格,即函数始终采用单个参数并返回单个值。
然后将 add x 1
解析为:Apply (Apply (Identifier "add", Identifier "x"), Literal 1)