在函数的参数中解析不带参数的函数调用-FParsec

时间:2018-12-31 15:21:14

标签: f# fparsec

我正在尝试通过使用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)])]

你能让我步入正轨吗?

1 个答案:

答案 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)