Парсеры на самом деле являются сокращениями типов для функций из потоков в ответы:
Parser<_,_> is just CharStream<_> -> Reply<_>
Имея это в виду, вы можете легко написать собственный анализатор для позиций:
let position : CharStream<_> -> Reply<Position> = fun stream -> Reply(stream.Position)
(* OR *)
let position : Parser<_,_> = fun stream -> Reply stream.Position
и привязывать информацию о положении к каждому биту, который вы анализируете с помощью
position .>>. yourParser (*or tuple2 position yourParser*)
анализатор положения не потребляет никакого ввода, и таким образом его можно безопасно комбинировать таким образом.
Вы можете сохранить требуемое изменение кода ограниченным одной строкой и избежать неконтролируемого распространения кода:
type AST = Slash of int64
| Hash of int64
let slash : Parser<AST,_> = char '/' >>. pint64 |>> Slash
let hash : Parser<AST,_> = char '#' >>. pint64 |>> Hash
let ast : Parser<AST,_> = slash <|> hash
(*if this is the final parser used for parsing lists of your ASTs*)
let manyAst : Parser< AST list,_> = many (ast .>> spaces)
let manyAstP : Parser<(Position * AST) list,_> = many ((position .>>. ast) .>> spaces)
(*you can opt in to parse position information for every bit
you parse just by modifiying only the combined parser *)
Обновление : FParsec имеет предопределенный парсер для позиций:
http://www.quanttec.com/fparsec/reference/charparsers.html#members.getPosition