F# 解析函数的调用-FParsec
我尝试解析函数的调用,以下是变量:F# 解析函数的调用-FParsec,f#,fparsec,F#,Fparsec,我尝试解析函数的调用,以下是变量: add 8 2 add x y add (inc x) (dec y) funcWithoutArgs 根据我在代码中如何分配分析器,以及它们的编码方式,我会得到错误,以及成功但不需要的分析。 例如,这: add 4 7 返回以下值: [Call ("foo",[Number 4]); Number 7] 因此,他只接受第一个参数 当我这样做时: foo x y 他今天早上把我送回来: [Call ("foo",[Call ("x",[Call ("
add 8 2
add x y
add (inc x) (dec y)
funcWithoutArgs
根据我在代码中如何分配分析器,以及它们的编码方式,我会得到错误,以及成功但不需要的分析。
例如,这:
add 4 7
返回以下值:
[Call ("foo",[Number 4]);
Number 7]
因此,他只接受第一个参数
当我这样做时:
foo x y
他今天早上把我送回来:
[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)
给出:
以及:
给出:
简而言之,我的函数调用分析器不能正常工作。每次我改变一些东西,比如一条新的线路,一次尝试,或者一个不同的层次结构,一些东西都不起作用。。。
你知道如何解决这个非常烦人的问题吗
以下是使用的最低功能代码:
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"
打开FParsec
//Ast
类型表达式=
|整数的数目
|字符串*表达式列表的调用
类型程序=表达式列表
//工具
让私人bws p=
空间>>?p.>>?空间
让私人套房=
sepEndBy p空间1
让内联private在parentheses p标签之间=
在(pstring)之间
(标签+“括号之间”)
让私有标识符=
许多令人满意的2胰岛(乐趣c->胰岛c)
//表情
让rec private call=parse{
let!call=pipe2(空格>>?标识符)(空格>>?参数)
(趣味id参数->调用(id,参数))/。>>?换行符
回电
}
and private parameters=suiteOf expression
和私用callFuncWithoutArgs=
标识符|>>乐趣id->呼叫(id,[])
和私人号码=pint32 |>>号码
和private之间的表达式=
解析{let!ex=betweenParentheses表达式“expression”
返回值}
和私人表达=
bws(在ParentHesseExpression之间的尝试
尝试号码
尝试呼叫
callFuncWithoutArgs)
// -------------------------------
让我们分析代码=
让解析器=多个表达式。>>?eof
将运行解析器代码与
|成功(结果)->result
|失败(消息,消息)->
printfn“%s”消息
[]
System.Console.Clear()
解析@”
加4 7
富x y
公司x
傅(x)(y)
加上(包括x)(12月y日)
|>printfn“%A”
您的主要问题是解析器的高级设计错误
您当前的设计是表达式可以是:
fooxy
,让我们按照解析器的顺序应用这些规则。没有括号,foo
不是数字,所以它不是3就是4。首先,我们尝试3foo
后跟xy
:是否将xy
解析为表达式?为什么,是的,它是这样做的:它解析为带有参数的调用,x
是函数,y
是参数。由于xy
匹配3,它根据规则3进行解析,而不检查规则4,因此fooxy
匹配类似foo(xy)
将:使用单个参数调用foo
,这是使用参数y
调用x
如何解决这个问题?好的,您可以尝试交换3和4的顺序,以便在使用参数的调用之前检查没有参数的函数调用(这将使xy
解析为justx
。但这将失败,因为fooxy
将匹配为justfoo
。因此将规则4放在规则3之前不起作用
真正的解决方案是将表达式的规则分为两个级别。“内部”级别,我称之为“值”,可以是:
createParserForwardedToRef
。让我们看看如何使用此设计解析foo x y
:
首先,foo
解析为一个标识符,因此检查它是否可能是一个带有参数的函数调用。x
解析为一个值吗?是的,在值的规则3下。y
解析为一个值吗?是的,在值的规则3下。fooxy
解析为一个函数调用
现在如何处理funcWithoutParameters
?它将使表达式规则1失败,因为它后面没有参数列表。因此将检查表达式规则2,然后根据值规则3进行匹配
好的,对伪代码进行基本的健全性检查是可行的,所以让我们把它转换成代码。但是首先,我要提到解析器中的另一个问题,我还没有提到,那就是您没有意识到FParsec空格
解析器。所以当您将表达式
解析器包装在bws
(“空格之间”)中时,它还将在解析文本后使用换行符。因此,当您解析以下内容时:
foo a b
inc c
suiteOf expression
查看列表a b inc c
,并将所有这些转换为foo
的参数
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"
foo a b
inc c
open FParsec
// Ast
type Expression =
| Number of int
| Call of string * Expression list
type Program = Expression list
// Tools
let private justSpaces = skipMany (pchar ' ' <|> pchar '\t')
let private justSpaces1 = skipMany1 (pchar ' ' <|> pchar '\t')
let private bws p =
spaces >>? p .>>? spaces
let private suiteOf p =
sepEndBy1 p (justSpaces1)
let inline private betweenParentheses p label =
between (pstring "(") (pstring ")") p
<?> (label + " between parentheses")
let private identifier =
many1Satisfy2 isLetter (fun c -> isLetter c)
// Expressions
let private expression, expressionImpl = createParserForwardedToRef()
let private betweenParenthesesExpression =
parse { let! ex = betweenParentheses expression "expression"
return ex }
let private callFuncWithoutArgs =
(identifier |>> fun id -> Call(id, []))
let private number = pint32 |>> Number
let private value =
justSpaces >>? (attempt betweenParenthesesExpression <|>
attempt number <|>
callFuncWithoutArgs)
let private parameters = suiteOf value
let rec private callImpl = parse {
let! call = pipe2 (justSpaces >>? identifier) (justSpaces >>? parameters)
(fun id parameters -> Call(id, parameters))
return call }
let call = callImpl
expressionImpl.Value <-
bws (attempt call <|>
value)
// -------------------------------
let parse code =
let parser = many expression .>>? (spaces >>. 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"
let (<!>) (p: Parser<_,_>) label : Parser<_,_> =
fun stream ->
printfn "%A: Entering %s" stream.Position label
let reply = p stream
printfn "%A: Leaving %s (%A)" stream.Position label reply.Status
reply
(Ln: 2, Col: 20): Entering expression
(Ln: 3, Col: 1): Entering call
(Ln: 3, Col: 5): Entering parameters
(Ln: 3, Col: 5): Entering bwParens
(Ln: 3, Col: 5): Leaving bwParens (Error)
(Ln: 3, Col: 5): Entering number
(Ln: 3, Col: 6): Leaving number (Ok)
(Ln: 3, Col: 7): Entering bwParens
(Ln: 3, Col: 7): Leaving bwParens (Error)
(Ln: 3, Col: 7): Entering number
(Ln: 3, Col: 8): Leaving number (Ok)
(Ln: 3, Col: 8): Leaving parameters (Ok)
(Ln: 3, Col: 8): Leaving call (Ok)
(Ln: 3, Col: 8): Leaving expression (Ok)