GavinJoyce
Trying to write a simple nimble_parsec parser
Hi,
I’m trying to write a parser using NimbleParsec v1.4.2 — Documentation which parses arbitrary text wrapped in a character like an underscore.
Here are some examples of what I’m hoping the parser will do:
"hello there" => ["hello there"]
"hello _Alex_!" => ["hello ", "_Alex_", "!"]
"Ũnicode is _allŏwed everywhere_ _ŏk?_" => ["Ũnicode is ", "_allŏwed everywhere_", "_ŏk?_"]
Here’s my attempt:
defmodule Txtparser do
import NimbleParsec
defcombinatorp :plain_string,
utf8_string([], min: 1)
|> lookahead_not(parsec(:wrapped_string))
defcombinatorp :wrapped_string,
string("_")
|> parsec(:plain_string)
|> string("_")
defparsec(:parse,
repeat(
choice(
[parsec(:wrapped_string), parsec(:plain_string)]
)
)
)
end
But I think I’m misundertanding how lookahead_not works, as the following is happening
"hi _there_" => ["hi _there_"]
instead of this desired result:
"hi _there_" => ["hi ", "_there_"]
First Post!
ityonemo
maybe I’m missing something. how are you getting them split into separate chunks? When I run your code on "foo _bar_ foo" I get
{:ok, ["foo _bar_ foo"], "", %{}, {1, 0}, 13}
0
Most Liked
ityonemo
a few NimbleParsec tips:
- try not to use defcombinatorp
- don’t be afraid to break up into smaller testable chunks
- try to limit circular references.
- test test test, I like having the
Mix.env == :testdefparsec block pattern - don’t be afraid to set variables in you “module-building-script”
- use post_traverse
here is what I got:
defmodule Np do
import NimbleParsec
basic_chars =
utf8_string([not: ?_], min: 1)
wrapped_string =
string("_")
|> concat(basic_chars)
|> string("_")
plain_string =
times(
utf8_char([])
|> lookahead_not(wrapped_string),
min: 1
)
|> choice([eos(), utf8_char([])])
|> post_traverse(:post_plain)
parser =
repeat(
choice([
wrapped_string
|> post_traverse(:post_wrapped),
plain_string
])
)
defparsec(:parse, parser)
defp post_wrapped(_rest, parsed, context, _line, _offset) do
{[parsed |> Enum.reverse |> List.to_string], context}
end
defp post_plain(_rest, parsed, context, _line, _offset) do
{[parsed |> Enum.reverse |> List.to_string], context}
end
if Mix.env() == :test do
defparsec(:basic_chars, basic_chars)
defparsec(:wrapped_string, wrapped_string)
defparsec(:plain_string, plain_string)
end
end
np_test.exs
defmodule NpTest do
use ExUnit.Case
defmacrop assert_parses(result, what) do
quote bind_quoted: [result: result, what: what] do
assert {:ok, ^result, _, _, _, _} = what
end
end
defmacrop assert_leaves(result, leftover, what) do
quote bind_quoted: [result: result, leftover: leftover, what: what] do
assert {:ok, ^result, ^leftover, _, _, _} = what
end
end
defmacrop assert_fails(what) do
quote bind_quoted: [what: what] do
assert {:error, _, _, _, _, _} = what
end
end
test "basic_chars" do
assert_parses ["foo"], Np.basic_chars("foo")
assert_leaves ["foo "], "_foo_", Np.basic_chars("foo _foo_")
assert_fails Np.basic_chars("_foo_")
end
test "wrapped_string" do
assert_parses ["_", "foo", "_"], Np.wrapped_string("_foo_")
assert_leaves ["_", "foo", "_"], " bar", Np.wrapped_string("_foo_ bar")
end
test "plain string" do
assert_parses ["foo"], Np.plain_string("foo")
assert_leaves ["foo "], "_bar_", Np.plain_string("foo _bar_")
end
end
8
GavinJoyce
Popular in Questions
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible.
total = 10
while total != 0
...
New
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
I tried installing
elixir 1.11.2
erlang 23.3.4
via asdf in my zsh shell. Enabled the versions locally and globally.
When I list them ...
New
Other popular topics
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
Hi All,
I set a environment variables in dev.exs , like below code.
when i start server, how can i set the ${enable} value?
thanks.
d...
New
Seen any cool LiveView demos, sample apps or examples? Please post them here! :003:
New
Hello all!
I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1]
15:22:35.803 [error] gen_event {lager_file_backend...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex









