I'm trying to learn FParsec and am trying to match strings which follow on of two patterns.
The string can either be an ordanary string like "string"
or it can be a string with one dot in it, like "st.ring"
.
The parser should look like this: Parser<(string Option * string),unit>
. The first string is optional depending of if the string is splitted by a dot or not. The optional string represent the part of the string which is before the "."
.
I have tried a few different things but I feel this attempt was the closes:
let charstilldot = manyCharsTill anyChar (pstring ".")
let parser = opt(charstilldot) .>>. (many1Chars anyChar)
This works with input like this "st.ring"
but not "string"
since not dot exists in the latter.
I would verry much appriciate some help, thank you!
EDIT: I have solution which basicly parse the arguments in order and swap the arguments depending of their is a dot or not in the string
let colTargetWithoutDot : Parser<string Option,unit> = spaces |>> fun _ -> None
let colTargetWithDot = (pstring "." >>. alphastring) |>> Some
let specificColumn = alphastring .>>. (colTargetWithDot <|> colTargetWithoutDot) |>> (fun (h,t) ->
match h,t with
| h,None -> (None,h)
| h,Some(t) -> (Some(h),t))
However this is not pretty so I would still appriciate another solution!
CodePudding user response:
I think the main problem here is that charstilldot
consumes characters even when it fails. In that situation, many1chars
then fails because the entire input has already been consumed. The easiest way to address this is by using attempt
to rollback when there is no dot:
let charstilldot = attempt (manyCharsTill anyChar (pstring "."))
let parser = opt(charstilldot) .>>. (many1Chars anyChar)
Result:
"str.ing"
->(Some "str", "ing")
"string"
->(None, "string")
I think there are other good solutions as well, but I've tried to give you one that requires the least change to your current code.