F# 创建对第一个解析器缩进敏感的sepBy解析器组合器
通过FParsec并使用它(它的代码很短,已经复制到问题的末尾),我试图设计一个类似于sebby的解析器,它对传入参数的第一个解析器的缩进很敏感。通常,如果我给出:F# 创建对第一个解析器缩进敏感的sepBy解析器组合器,f#,indentation,fparsec,F#,Indentation,Fparsec,通过FParsec并使用它(它的代码很短,已经复制到问题的末尾),我试图设计一个类似于sebby的解析器,它对传入参数的第一个解析器的缩进很敏感。通常,如果我给出:indentedsebby(pstring“Example”)(pchar.),我希望这种类型的程序是可以接受的: Example .Example .Example.Example .Example.Example.Example .Example 但不是这个: Example .Example .Example.
indentedsebby(pstring“Example”)(pchar.)
,我希望这种类型的程序是可以接受的:
Example
.Example
.Example.Example
.Example.Example.Example
.Example
但不是这个:
Example
.Example
.Example.Example
.Example
因此,第一个位置(以及第一个解析器)设置其余部分的缩进。为此,我尝试了使用FParsec的默认sebby解析器对缩进库进行简单重写,得到如下结果:
open FParsec
open IndentParsec
let indentSepBy p sep =
parse {
let! pos = getPosition
return! sepBy (greater pos p) (greater pos sep)
}
let test = indentSepBy (pstring "Example") (pchar '.')
let text = "Example.Example" (*Simple for start*)
应用此方法,我得到以下错误消息(来自FParsec):
如果我移除缩进相关的解析器
let indentSepBy p sep = (*so it's just trivially equivalent to the sepBy parser*)
parse {
let! pos = getPosition
return! sepBy p sep
}
问题不再出现,结果是我们所期望的。因此,我不明白是哪个参数导致了这个错误。这似乎很可能是缩进库中的一个问题,但我无法解决它。。。以下是我所讨论的lib,我将其缩短为要点:
open FParsec
module IndentParser =
type Indentation =
| Fail
| Any
| Greater of Position
| Exact of Position
| AtLeast of Position
| StartIndent of Position
member this.Position =
match this with
| Any
| Fail -> None
| Greater p -> Some p
| Exact p -> Some p
| AtLeast p -> Some p
| StartIndent p -> Some p
type IndentState<'T> = { Indent: Indentation; UserState: 'T }
type IndentParser<'T, 'UserState> = Parser<'T, IndentState<'UserState>>
let indentState u = { Indent = Any; UserState = u }
let runParser p u s = runParserOnString p (indentState u) "" s
let runParserOnFile p u path =
runParserOnFile p (indentState u) path System.Text.Encoding.UTF8
let getIndentation: IndentParser<_, _> =
fun stream ->
match stream.UserState with
| { Indent = i } -> Reply i
let putIndentation newi: IndentParser<unit, _> =
fun stream ->
stream.UserState <- { stream.UserState with Indent = newi }
Reply(Unchecked.defaultof<unit>)
let failf fmt = fail << sprintf fmt
let acceptable i (pos: Position) =
match i with
| Any _ -> true
| Fail -> false
| Greater bp -> bp.Column < pos.Column
| Exact ep -> ep.Column = pos.Column
| AtLeast ap -> ap.Column <= pos.Column
| StartIndent _ -> true
let nestableIn i o =
match i, o with
| Greater i, Greater o -> o.Column < i.Column
| Greater i, Exact o -> o.Column < i.Column
| Exact i, Exact o -> o.Column = i.Column
| Exact i, Greater o -> o.Column <= i.Column
| _, _ -> true
let tokeniser p =
parse {
let! pos = getPosition
let! i = getIndentation
if acceptable i pos
then return! p
else return! failf "incorrect indentation at %A" pos
}
let nestP i o p =
parse {
do! putIndentation i
let! x = p
do! notFollowedBy (tokeniser anyChar)
<?> (sprintf "unterminated %A" i)
do! putIndentation o
return x
}
let indented<'a, 'u> i (p: Parser<'a, _>): IndentParser<_, 'u> =
parse {
do! putIndentation i
do! spaces
return! tokeniser p
}
let exact<'a, 'u> pos p: IndentParser<'a, 'u> = indented (Exact pos) p
let greater<'a, 'u> pos p: IndentParser<'a, 'u> = indented (Greater pos) p
let atLeast<'a, 'u> pos p: IndentParser<'a, 'u> = indented (AtLeast pos) p
let any<'a, 'u> pos p: IndentParser<'a, 'u> = indented Any p
打开FParsec
模块缩进分析器=
类型缩进=
|失败
|任何
|更大的位置
|精确位置
|至少在位置上
|起始位置凹痕
议员:这是我的立场=
与此匹配
|任何
|失败->无
|大p->一些p
|精确p->Some p
|至少p->一些p
|StartIndent p->Some p
类型缩进状态
设indentState u={Indent=Any;UserState=u}
让runParser p u s=runParserOnString p(indentState u)“”s
让我们运行parseron文件pu路径=
runparseronp文件(indentState u)路径System.Text.Encoding.UTF8
让getIndentation:IndentParser=
趣味流->
将stream.UserState与
|{Indent=i}->回复i
让我们来看看newi:IndentParser=
趣味流->
stream.UserState错误
|较大bp->bp.列<位置列
|精确ep->ep柱=位置柱
|至少ap->ap.列为真
让NestableinI o=
将i、o与
|大i,大o->o.列o.列o.列=i.列
|精确i,大o->o.列为真
让标记器p=
解析{
let!pos=getPosition
让我来!i=Get
如果可以的话,我会购买
然后回来
否则返回!失败“在%A位置的缩进不正确”
}
让nestP i o p=
解析{
做吧!普蒂
设!x=p
do!notfollowerby(标记器anyChar)
(sprintf“未终止%A”i)
做!普托
返回x
}
设缩进i(p:Parser)=
解析{
做吧!普蒂
做!空格
返回!标记器p
}
设精确位置p:IndentParser=缩进(精确位置)p
设更大位置p:IndentParser=缩进(更大位置)p
让至少位置p:IndentParser=indented(至少位置)p
让任意位置p:IndentParser=缩进任意p
我认为这里有两个问题:
“示例”
缩进到自身之外,这是不可能的。您应该让第一个解析器成功,而不管当前位置如何greater
不是原子的,因此当它失败时,您的解析器将处于无效状态。这可能被视为库中的错误,也可能不被视为库中的错误。在任何情况下,您都可以通过尝试将其原子化
let indentSepBy p sep =
parse {
let! pos = getPosition
let! head = p
let! tail =
let p' = attempt (greater pos p)
let sep' = attempt (greater pos sep)
many (sep' >>. p')
return head :: tail
}
您可以按如下方式进行测试:
let test =
indentSepBy (pstring "Example") (pchar '.')
let run text =
printfn "***"
runParser (test .>> eof) () text
|> printfn "%A"
[<EntryPoint>]
let main argv =
run "Example.Example" // success
run "Example\n.Example" // failure
run "Example\n .Example" // success
0
let测试=
indentseby(pstring“示例”)(pchar.”)
让我们运行文本=
printfn“***”
runParser(test.>>eof)()文本
|>printfn“%A”
[]
让主argv=
运行“Example.Example”//success
运行“示例\n.Example”//failure
运行“示例\n.Example”//success
0
请注意,我已经强制测试解析器通过eof
使用整个输入。否则,当它实际上无法解析整个字符串时,它将错误地报告成功
let test =
indentSepBy (pstring "Example") (pchar '.')
let run text =
printfn "***"
runParser (test .>> eof) () text
|> printfn "%A"
[<EntryPoint>]
let main argv =
run "Example.Example" // success
run "Example\n.Example" // failure
run "Example\n .Example" // success
0