Skip to content

Instantly share code, notes, and snippets.

@sescobb27
Last active September 12, 2018 18:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sescobb27/20534f8ed946d141b6365e69f4817de6 to your computer and use it in GitHub Desktop.
Save sescobb27/20534f8ed946d141b6365e69f4817de6 to your computer and use it in GitHub Desktop.
simple Dockerfile parser, doesn't support complex RUN expressions with continuation (\)
defmodule DockerApi.DockerfileParser do
@comment ~r/^\s*#/
@continuation ~r/^.*\\\s*$/
@instruction ~r/^\s*(\w+)\s+(.*)$/
def parse(path) do
File.stream!(path)
|> Stream.map(fn line ->
parse_line(line)
end)
|> join_lines()
end
defp parse_line(line) do
line = String.trim(line)
if line == "" || Regex.match?(@comment, line) do
nil
else
# line: "RUN set -xe \\"
case Regex.run(@instruction, line) do
# if line is not an instruction it is a continuation of another continuation
nil ->
if Regex.match?(@continuation, line) do
{:continue, String.slice(line, 0..-2)}
else
{:end, line}
end
# ["RUN set -xe \\", "RUN", "set -xe \\"]
[_instruction, command, value] ->
if Regex.match?(@continuation, line) do
# remove trailing continuation (\)
{:continue, {command, String.slice(value, 0..-2)}}
else
{:end, {command, value}}
end
end
end
end
# example
# [
# {:continue, "hola"},
# {:continue, "como"},
# {:end, "estas"},
# {:continue, "bien"},
# {:end, "gracias"},
# {:end, "chao"}
# ] |> Enum.reduce([], fn
# {:continue, _} = val, [] ->
# [val]
# {:continue, val}, [{:continue, prev} | rest] ->
# [{:continue, prev <> val} | rest]
# {:continue, _} = val, acc ->
# [val | acc]
# {:end, val}, [] ->
# [val]
# {:end, val}, [{:continue, prev} | rest] ->
# [prev <> val | rest]
# {:end, val}, acc ->
# [val | acc]
# end)
defp join_lines(lines) do
lines
|> Enum.reduce([], &do_join/2)
|> Enum.reverse()
end
# nil line (comment/empty line)
defp do_join(nil, acc) do
acc
end
# first line - accomulator empty
defp do_join({:continue, _} = val, []) do
[val]
end
# a continuation of a previous continuation - need to join lines
defp do_join({:continue, val}, [{:continue, {prev_command, prev_value}} | rest]) do
[{:continue, {prev_command, prev_value <> " " <> val}} | rest]
end
# a new continuation - other continuation already finished
defp do_join({:continue, _} = val, acc) do
[val | acc]
end
# first line - single instruction
defp do_join({:end, val}, []) do
[val]
end
# the end of a continuation
defp do_join({:end, val}, [{:continue, {prev_command, prev_value}} | rest]) do
[{prev_command, prev_value <> " " <> val} | rest]
end
# single instruction
defp do_join({:end, val}, acc) do
[val | acc]
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment