119 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Elixir
		
	
	
	
	
	
			
		
		
	
	
			119 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Elixir
		
	
	
	
	
	
defmodule Mix.Tasks.NimbleParsec.Compile do
 | 
						|
  @shortdoc "Compiles a parser and injects its content into the parser file"
 | 
						|
 | 
						|
  @moduledoc ~S"""
 | 
						|
  Compiles a parser from a template.
 | 
						|
 | 
						|
      $ mix nimble_parsec.compile template.ex.exs
 | 
						|
 | 
						|
  This task is useful to generate parsers that have no runtime dependency
 | 
						|
  on NimbleParsec.
 | 
						|
 | 
						|
  ## Examples
 | 
						|
 | 
						|
  Let's define a template file:
 | 
						|
 | 
						|
      # lib/my_parser.ex.exs
 | 
						|
      defmodule MyParser do
 | 
						|
        @moduledoc false
 | 
						|
 | 
						|
        # parsec:MyParser
 | 
						|
        import NimbleParsec
 | 
						|
 | 
						|
        date =
 | 
						|
          integer(4)
 | 
						|
          |> ignore(string("-"))
 | 
						|
          |> integer(2)
 | 
						|
          |> ignore(string("-"))
 | 
						|
          |> integer(2)
 | 
						|
 | 
						|
        time =
 | 
						|
          integer(2)
 | 
						|
          |> ignore(string(":"))
 | 
						|
          |> integer(2)
 | 
						|
          |> ignore(string(":"))
 | 
						|
          |> integer(2)
 | 
						|
          |> optional(string("Z"))
 | 
						|
 | 
						|
        defparsec :datetime, date |> ignore(string("T")) |> concat(time)
 | 
						|
 | 
						|
        # parsec:MyParser
 | 
						|
      end
 | 
						|
 | 
						|
  After running:
 | 
						|
 | 
						|
      $ mix nimble_parsec.compile lib/my_parser.ex.exs
 | 
						|
 | 
						|
  The following file will be generated:
 | 
						|
 | 
						|
      # lib/my_parser.ex
 | 
						|
      defmodule MyParser do
 | 
						|
        @moduledoc false
 | 
						|
 | 
						|
        def datetime(binary, opts \\ []) do
 | 
						|
          ...
 | 
						|
        end
 | 
						|
 | 
						|
        defp datetime__0(...) do
 | 
						|
          ...
 | 
						|
        end
 | 
						|
 | 
						|
        ...
 | 
						|
      end
 | 
						|
 | 
						|
  The file will be automatically formatted if using Elixir v1.6+.
 | 
						|
 | 
						|
  ## Options
 | 
						|
 | 
						|
    * `-o` - configures the output location. Defaults to the input
 | 
						|
      file without its last extension
 | 
						|
 | 
						|
  """
 | 
						|
 | 
						|
  use Mix.Task
 | 
						|
 | 
						|
  @impl true
 | 
						|
  def run(args) do
 | 
						|
    Mix.Task.reenable("nimble_parsec.compile")
 | 
						|
    {opts, files} = OptionParser.parse!(args, strict: [output: :string], aliases: [o: :output])
 | 
						|
    Mix.Task.run("compile")
 | 
						|
 | 
						|
    case files do
 | 
						|
      [file] -> compile(file, opts)
 | 
						|
      _ -> Mix.raise("Expected a single file to be given to nimble_parsec.compile")
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  defp compile(input, opts) do
 | 
						|
    output = opts[:output] || Path.rootname(input)
 | 
						|
    Mix.shell().info("Generating #{output}")
 | 
						|
 | 
						|
    {:ok, _} = NimbleParsec.Recorder.start_link([])
 | 
						|
 | 
						|
    try do
 | 
						|
      Code.compiler_options(ignore_module_conflict: true)
 | 
						|
      Code.require_file(input)
 | 
						|
 | 
						|
      input
 | 
						|
      |> File.read!()
 | 
						|
      |> NimbleParsec.Recorder.replay(input)
 | 
						|
      |> write_to_disk(input, output)
 | 
						|
    after
 | 
						|
      Code.compiler_options(ignore_module_conflict: false)
 | 
						|
      NimbleParsec.Recorder.stop()
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  defp write_to_disk(contents, input, output) do
 | 
						|
    now = DateTime.utc_now() |> Map.put(:microsecond, {0, 0}) |> to_string
 | 
						|
 | 
						|
    prelude = """
 | 
						|
    # Generated from #{input}, do not edit.
 | 
						|
    # Generated at #{now}.
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    File.write!(output, [prelude | contents])
 | 
						|
  end
 | 
						|
end
 |