【发布时间】:2019-02-02 22:50:06
【问题描述】:
我尝试解析一个函数的调用,这里是变种:
add 8 2
add x y
add (inc x) (dec y)
funcWithoutArgs
根据我在代码中分布分析器的方式,也许还有它们的编码方式,我会得到错误,以及成功但不需要的分析。 例如,这个:
add 4 7
返回以下 AST:
[Call ("foo",[Number 4]);
Number 7]
因此他只取第一个参数。
当我这样做时:
foo x y
他把这个 AST 发回给我:
[Call ("foo",[Call ("x",[Call ("y",[])])])]
这不是我想要的,因为在这里,每个参数都调用下一个作为参数。
另一个例子,当我这样做时:
foo x y
inc x
我明白了:
[Call ("foo",[Call ("x",[Call ("y",[Call ("inc",[Call ("x",[])])])])])]
它的作用与上面相同,但也调用该行后面的代码。当我向我的分析器询问新行时(请参阅代码),它会向我发送以下信息:
[Call ("foo",[]); Call ("x",[]); Call ("y",[]); Call ("inc",[]); Call ("x",[])]
即使在括号中也不起作用:
foo (x) (y)
给:
[Call ("foo",[]); Call ("x",[]); Call ("y",[])]
还有:
add (inc x) (dec y)
给:
Error in Ln: 1 Col: 1
Note: The error occurred on an empty line.
The parser backtracked after:
Error in Ln: 2 Col: 5
add (inc x) (dec y)
^
Expecting: end of input or integer number (32-bit, signed)
The parser backtracked after:
Error in Ln: 2 Col: 10
add (inc x) (dec y)
^
Expecting: ')'
[]
简而言之,我的函数调用分析器无法正常工作。每次我更改某些内容时,例如换行、尝试或不同的层次结构,有些东西不起作用…… 你知道如何解决这个非常烦人的问题吗?
这是使用的最少功能代码:
open FParsec
// Ast
type Expression =
| Number of int
| Call of string * Expression list
type Program = Expression list
// Tools
let private bws p =
spaces >>? p .>>? spaces
let private suiteOf p =
sepEndBy p spaces1
let inline private betweenParentheses p label =
between (pstring "(") (pstring ")") p
<?> (label + " between parentheses")
let private identifier =
many1Satisfy2 isLetter (fun c -> isLetter c)
// Expressions
let rec private call = parse {
let! call = pipe2 (spaces >>? identifier) (spaces >>? parameters)
(fun id parameters -> Call(id, parameters)) // .>>? newline
return call
}
and private parameters = suiteOf expression
and private callFuncWithoutArgs =
identifier |>> fun id -> Call(id, [])
and private number = pint32 |>> Number
and private betweenParenthesesExpression =
parse { let! ex = betweenParentheses expression "expression"
return ex }
and private expression =
bws (attempt betweenParenthesesExpression <|>
attempt number <|>
attempt call <|>
callFuncWithoutArgs)
// -------------------------------
let parse code =
let parser = many expression .>>? eof
match run parser code with
| Success(result, _, _) -> result
| Failure(msg, _, _) ->
printfn "%s" msg
[]
System.Console.Clear()
parse @"
add 4 7
foo x y
inc x
foo (x) (y)
add (inc x) (dec y)
" |> printfn "%A"
【问题讨论】:
-
第一个问题:为什么要将
funcWithoutArgs解析为函数调用而不是标识符?在我所知道的每一种语言中,调用函数都需要与引用函数不同的语法:例如,func()是函数调用,而func只是对函数的引用。我认为您的问题的一部分(尽管不是全部)可能源于funcWithoutArgs正在解析为函数调用这一事实。 -
第二个问题:您声明
[Call ("foo",[Call ("x",[Call ("y",[])])])]不是您希望foo x y解析为的内容,但我无法从您示例中的类型判断您会 想要foo x y解析到。我假设您希望它解析为[Call ("foo", [Identifier "x"; Identifier "y"])]之类的东西,但在您的精简示例中,DU 中没有Identifier。那么你希望foo x y实际解析到什么?你真正想要的结果是什么? -
@rmunn 第一个答案:
funcWithoutArgs顾名思义,应该是一个函数调用,没有参数,实际上可以看作是一个简单的标识符。例如,在 OCaml、Haskell 甚至 F# 中,据我所知,没有区别,因为变量/数据只是可以应用的数据。这就是我一直试图复制的。你会建议什么?也许要创建一个新的Variable节点? -
第二个答案:其实
foo x y应该是这样分析的:[call ("foo",[Call ("x", []); call ("y", []))]。按照我上面说的。