Skip to content

Commit 40a384a

Browse files
committed
Tentative implementation of the next function
1 parent e7fcaff commit 40a384a

File tree

1 file changed

+94
-50
lines changed

1 file changed

+94
-50
lines changed

lib/elixir/lib/option_parser.ex

Lines changed: 94 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ defmodule OptionParser do
8686
8787
"""
8888
def parse(argv, opts \\ []) when is_list(argv) and is_list(opts) do
89-
parse(argv, opts, true)
89+
config = compile_config(opts, all: true)
90+
do_parse(argv, config, [], [], [])
9091
end
9192

9293
@doc """
@@ -105,74 +106,117 @@ defmodule OptionParser do
105106
106107
"""
107108
def parse_head(argv, opts \\ []) when is_list(argv) and is_list(opts) do
108-
parse(argv, opts, false)
109+
config = compile_config(opts, all: false)
110+
do_parse(argv, config, [], [], [])
109111
end
110112

111-
## Helpers
112113

113-
defp parse(argv, opts, bool) do
114-
aliases = opts[:aliases] || []
115-
switches = opts[:switches] || []
116-
parse(argv, aliases, switches, bool)
114+
defp do_parse([], _config, opts, args, invalid) do
115+
{Enum.reverse(opts), Enum.reverse(args), Enum.reverse(invalid)}
116+
end
117+
118+
defp do_parse(argv, {aliases, switches, all}=config, opts, args, invalid) do
119+
case next(argv, aliases, switches) do
120+
{:ok, option, value, rest} ->
121+
# the option exist and it was successfully parsed
122+
kinds = List.wrap Keyword.get(switches, option)
123+
new_opts = do_store_option(opts, option, value, kinds)
124+
do_parse(rest, config, new_opts, args, invalid)
125+
126+
{:error, option, value, rest} ->
127+
# the option exist but it has wrong value
128+
do_parse(rest, config, opts, args, [{option, value}|invalid])
129+
130+
{:error, option, rest} ->
131+
# the option does not exist (for strict cases)
132+
do_parse(rest, config, opts, args, [{option, nil}|invalid])
133+
134+
{:error, ["--"|rest]} ->
135+
{Enum.reverse(opts), Enum.reverse(args, rest), Enum.reverse(invalid)}
136+
137+
{:error, [arg|rest]=remaining_args} ->
138+
# there is no option
139+
if all do
140+
do_parse(rest, config, opts, [arg|args], invalid)
141+
else
142+
{Enum.reverse(opts), Enum.reverse(args, remaining_args), Enum.reverse(invalid)}
143+
end
144+
end
117145
end
118146

119-
defp parse(argv, aliases, switches, all) do
120-
parse(argv, aliases, switches, [], [], [], all)
147+
148+
@doc """
149+
Low-level function that parses one option.
150+
"""
151+
def next(argv, opts \\ []) when is_list(argv) and is_list(opts) do
152+
{aliases, switches, _} = compile_config(opts, false)
153+
next(argv, aliases, switches)
121154
end
122155

123-
defp parse(["--"|t], _aliases, _switches, dict, args, invalid, _all) do
124-
{Enum.reverse(dict), Enum.reverse(args, t), Enum.reverse(invalid)}
156+
defp next([], _aliases, _switches) do
157+
{:error, []}
125158
end
126159

127-
defp parse(["-" <> option|t], aliases, switches, dict, args, invalid, all) do
160+
defp next(["--"|_]=argv, _aliases, _switches) do
161+
{:error, argv}
162+
end
163+
164+
defp next(["-" <> option|rest], aliases, switches) do
128165
{option, value} = split_option(option)
129166
{option, kinds, value} = normalize_option(option, value, switches, aliases)
130-
{value, kinds, t} = normalize_value(value, kinds, t)
131-
{dict, invalid} = store_option(dict, invalid, option, value, kinds)
132-
parse(t, aliases, switches, dict, args, invalid, all)
133-
end
134167

135-
defp parse([h|t], aliases, switches, dict, args, invalid, true) do
136-
parse(t, aliases, switches, dict, [h|args], invalid, true)
168+
#FIXME: don't modify rest for unknown options in strict mode
169+
{value, kinds, rest} = normalize_value(value, kinds, rest)
170+
171+
case validate_option(option, value, kinds) do
172+
{:ok, new_value} ->
173+
{:ok, option, new_value, rest}
174+
175+
:invalid ->
176+
{:error, option, value, rest}
177+
end
137178
end
138179

139-
defp parse([], _, _switches, dict, args, invalid, true) do
140-
{Enum.reverse(dict), Enum.reverse(args), Enum.reverse(invalid)}
180+
defp next(argv, _aliases, _switches) do
181+
{:error, argv}
141182
end
142183

143-
defp parse(value, _, _switches, dict, _args, invalid, false) do
144-
{Enum.reverse(dict), value, Enum.reverse(invalid)}
184+
## Helpers
185+
186+
defp compile_config(opts, all: flag) do
187+
aliases = opts[:aliases] || []
188+
switches = opts[:switches] || []
189+
{aliases, switches, flag}
145190
end
146191

147-
defp store_option(dict, invalid, option, value, kinds) do
148-
{invalid_option, value} =
149-
cond do
150-
:invalid in kinds ->
151-
{option, value}
152-
:boolean in kinds ->
153-
case value do
154-
t when t in [true, "true"] -> {nil, true}
155-
f when f in [false, "false"] -> {nil, false}
156-
_ -> {option, value}
157-
end
158-
:integer in kinds ->
159-
case Integer.parse(value) do
160-
{value, ""} -> {nil, value}
161-
_ -> {option, value}
162-
end
163-
:float in kinds ->
164-
case Float.parse(value) do
165-
{value, ""} -> {nil, value}
166-
_ -> {option, value}
167-
end
168-
true ->
169-
{nil, value}
170-
end
171-
172-
if invalid_option do
173-
{dict, [{option, value}|invalid]}
192+
defp validate_option(option, value, kinds) do
193+
{invalid_opt, value} = cond do
194+
:invalid in kinds ->
195+
{option, value}
196+
:boolean in kinds ->
197+
case value do
198+
t when t in [true, "true"] -> {nil, true}
199+
f when f in [false, "false"] -> {nil, false}
200+
_ -> {option, value}
201+
end
202+
:integer in kinds ->
203+
case Integer.parse(value) do
204+
{value, ""} -> {nil, value}
205+
_ -> {option, value}
206+
end
207+
:float in kinds ->
208+
case Float.parse(value) do
209+
{value, ""} -> {nil, value}
210+
_ -> {option, value}
211+
end
212+
true ->
213+
{nil, value}
214+
end
215+
216+
if invalid_opt do
217+
:invalid
174218
else
175-
{do_store_option(dict, option, value, kinds), invalid}
219+
{:ok, value}
176220
end
177221
end
178222

0 commit comments

Comments
 (0)