darraghenright
Hi there! ![]()
I should preface this by saying I’m an absolute beginner to parser combinators and I’m pretty much an Elixir hobbyist so far.
In any case, I’ve very recently become interested in exploring them since watching Saša Jurić’s Parsing from first principles video and I’ve just started scratching the surface with nimble_parsec.
The introductory datetime example got me thinking about how one might go about parsing integers with additional constraints. As a standalone example, how one would go about parsing a “valid month” value; e.g: in the range 1..12 with an optional leading 0.
Strings like "1", "01" and "12" would be valid, returning 1, 1 and 12 respectively.
Conversely, strings like "0", "00", "001" and "13" are invalid.
I am not sure what an idiomatic approach is here, so I suspect my various attempts so far have been very naive. The following is my best attempt, where I accept any integer between one or two characters and then validate with post_traverse:
defmodule MonthParser do
import NimbleParsec
def valid_month?(_rest, [n] = args, context, _line, _offset) when n >= 1 and n <= 12,
do: {args, context}
def valid_day?(_rest, [n], _context, _line, _offset),
do: {:error, "Invalid month: #{n}"}
month =
integer(min: 1, max: 2)
|> post_traverse(:valid_month?)
|> eos()
defparsec :month, month
end
This seems satisfactory enough:
iex(1)> MonthParser.month "0"
{:error, "Invalid month: 0", "", %{}, {1, 0}, 1}
iex(2)> MonthParser.month "00"
{:error, "Invalid month: 0", "", %{}, {1, 0}, 2}
iex(3)> MonthParser.month "01"
{:ok, [1], "", %{}, {1, 0}, 2}
iex(4)> MonthParser.month "1"
{:ok, [1], "", %{}, {1, 0}, 1}
iex(5)> MonthParser.month "31"
{:ok, [31], "", %{}, {1, 0}, 2}
iex(6)> MonthParser.month "32"
{:error, "Invalid month: 32", "", %{}, {1, 0}, 2}
However, in the spirit of education I’d love to learn about better solutions. This being Elixir I assume there’s a far more elegant and succinct solution ![]()
Additionally, I am wondering in this example if range validation might be better somewhere else — in other words, maybe worrying about the validity of the values comes later? I am imagining a more complex scenario where a full date is being parsed, where the validity of the date depends on the month.
Thanks! ![]()
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #ai
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 7- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
kip
Thats the approach I generally use.
In some cases, like the one you outline, I may opt for a more explicit expression of validity like:
dbern
If you want some examples of NimbleParsec and date time parsing you can look at TaxJar’s date_time_parser.
I helped make it. I won’t say it’s a shining example of parsing well, but it works.
darraghenright
Thanks! That’s a good suggestion, try to be as explicit as possible, which is obviously a lot clearer and a bit more declarative.
Just wondering, are the strings defined in descending numeric order for a reason?
darraghenright
Very cool! Thanks for sharing, looks like there’s a lot of good material to learn from in here.
ityonemo
A few notes:
I can’t say I’m an expert (some of these suggestions I’m about to give are very much my own), and I’ve only really been using nimbleparsec for a few months. If you’d like a more complex example, here are some of the highlights of a relatively complex parser (it parses zig code), note that these are things that may only apply to more complex situations:
https://github.com/ityonemo/zigler/blob/master/lib/zigler/parser.ex#L150
https://github.com/ityonemo/zigler/blob/master/lib/zigler/parser.ex#L186
https://github.com/ityonemo/zigler/blob/master/lib/zigler/parser.ex#L276
https://github.com/ityonemo/zigler/blob/master/lib/zigler/parser.ex#L431
kip
Its not in descending number order, its in descending string order. If we don’t capture the 2-digit numbers first, like
string("12")then we would capturestring("1")and then the next character would be “2” which would be a parse error. When parsing strings like this its important to parse the longest strings first for that reason.darraghenright
I forgot to come back again and say thanks to everyone for their replies since my last visit.
@kip — Excellent point about string length, seems so obvious in retrospect, and of course this would factor when including comparisons for leading zero values; i.e:
string("01")should come beforestring("1")(orstring("9")for that matter!@ityonemo — point taken about functions that end with
Thanks for all the reference material, super helpful stuff.
?. I’m usually more vigilant about that convention (I promise)