Hi!
From what I wrote in 7GUIs implementation in Elm, I used elm/parser and I’m not too sure I’m using it the right way, so wanted to ask for feedback (and learn!).
Inputs
I am parsing spreadsheet cell formulas, where they can look something like this (only relevant examples)
=A1
=sum(A1,B2,C3)
=sum(A1:B2,C3:C4,sum(C5,D7))
The relevant part of the ask for feedback (although I’d love to get it from all of it), is about the ambiguity between parsing a cell position like A1
and a range like A1:B1
, in the middle of the input. I guess if you’ve done much parsing you already know where I’m going.
Parsers / Code to review
Position parser
type alias Position =
{ column : Char
, row : Int
}
parser : Parser Position
parser =
succeed
(\column row ->
Position (String.uncons column |> Maybe.map Tuple.first |> Maybe.withDefault 'A') row
)
|= (getChompedString <| succeed () |. chompIf Char.isUpper)
|= int
Although I got the job done, I still feel weird about this little parser. Particularly about the getChompedString
and the “make my 1 char string a char” with String.uncons
.
Position + Range parsing together
Initially, I wrote the range parser naively, and then put them up together from simpler to more complex parsing:
type alias Range =
{ from : Position, to : Position }
range : Parser Range
range =
succeed Range
|= Position.parser
|. symbol ":"
|= Position.parser
expression : Parser Expression
expression =
oneOf
[ map EFloat float
, map ECoord Position.parser
, map ERange range
, map EApplication application
]
At this point, everything worked well except for the ranges, which would fail. I tried changing the order in the oneOf:
expression : Parser Expression
expression =
oneOf
[ map EFloat float
, map ERange range
, map ECoord Position.parser
, map EApplication application
]
But then the positions wouldn’t work.
I don’t think I fully grasp oneOf
, since it seems that it tries a parser and if it fails (like the float
one) it tries the other ones, but I guess since the range
parser is composed of other parsers then it is committed to that path and doesn’t go back on the oneOf
.
In the end, I ended up making the first part of range backtrackable
, so that if it can’t parse a :
it can go back and try other parsers (I guess that’s what backtrackable
does?), and I put the range parser first because it is the longest. So that it tries it out and if it doesn’t seem like a range it goes on.
range : Parser Range
range =
succeed Range
|= backtrackable Position.parser
|. symbol ":"
|= Position.parser
expression : Parser Expression
expression =
oneOf
[ map EFloat float
, map ERange range
, map ECoord Position.parser
, map EApplication application
]
Questions
- Do you have any suggestions for the position, range, or expression parsers? Anything that could be improved or done differently?
- Is my understanding of
backtrackable
correct? - What/When do you use
commit
? What does it do?
Thank you!