Current section

Files

Jump to
zigler lib zig parser imports.ex
Raw

lib/zig/parser/imports.ex

defmodule Zig.Parser.Imports do
@moduledoc """
For parsing, looking for imports, cimports, and usingnamespace directives.
To be completed later
"""
import NimbleParsec
defstruct imports: [], identifier: nil, pub: false
@typep identifier_t :: :usingnamespace | String.t()
@type t :: %__MODULE__{
imports: [{identifier_t, Path.t()} | {:pub, identifier_t, Path.t()}],
identifier: atom,
pub: boolean
}
# designed to ninja in this struct as necessary.
@type line_info :: {non_neg_integer, non_neg_integer}
@type parsec_retval :: {[String.t()], t}
initialize = post_traverse(empty(), :initializer)
@spec initializer(String.t(), [String.t()], t, line_info, non_neg_integer) ::
parsec_retval
defp initializer(_, _, context, _, _), do: {[], struct(__MODULE__, context)}
whitespace = ascii_string([?\s, ?\n], min: 1)
filename = ascii_string([not: ?"], min: 1)
identifier =
ascii_char([?a..?z, ?A..?Z, ?_])
|> optional(ascii_string([?a..?z, ?A..?Z, ?0..?9, ?_], min: 1))
|> reduce({IO, :iodata_to_binary, []})
usingnamespace =
optional(
string("pub")
|> concat(ignore(whitespace))
)
|> string("usingnamespace")
|> ignore(whitespace)
import_const =
optional(
string("pub")
|> concat(ignore(whitespace))
)
|> ignore(
string("const")
|> concat(whitespace)
)
|> concat(identifier)
|> ignore(
optional(whitespace)
|> string("=")
|> optional(whitespace)
)
prefix =
choice([usingnamespace, import_const])
|> post_traverse(:register_identifier)
import_stmt =
prefix
|> ignore(
string("@import")
|> optional(whitespace)
|> string("(")
|> optional(whitespace)
|> string("\"")
)
|> concat(filename)
|> ignore(
string("\"")
|> optional(whitespace)
|> string(")")
)
|> post_traverse(:register_import)
include_stmt =
ignore(
string("@cInclude")
|> optional(whitespace)
|> string("(")
|> optional(whitespace)
|> string("\"")
)
|> concat(filename)
|> ignore(
string("\"")
|> optional(whitespace)
|> string(")")
)
|> post_traverse(:register_include)
defp register_identifier(_rest, ["usingnamespace" | rest], context, _, _) do
{[], %{context | identifier: :usingnamespace, pub: pub?(rest)}}
end
defp register_identifier(_rest, [identifier | rest], context, _, _) do
{[], %{context | identifier: identifier, pub: pub?(rest)}}
end
defp pub?([]), do: false
defp pub?(["pub"]), do: true
defp register_import(_rest, [path], context, _, _) do
{[],
%{
context
| imports: [make_identifier(path, context) | context.imports],
identifier: nil,
pub: false
}}
end
defp make_identifier(path, context) do
if context.pub do
{:pub, context.identifier, path}
else
{context.identifier, path}
end
end
defp register_include(_rest, [path], context, _, _) do
{[], %{context | imports: [{:cinclude, path} | context.imports]}}
end
if Mix.env() == :test do
defparsec(:parse_import_const, concat(initialize, import_const))
defparsec(:parse_import_stmt, concat(initialize, import_stmt))
defparsec(:parse_include_stmt, concat(initialize, include_stmt))
end
parse_imports =
initialize
|> repeat(
choice([
import_stmt,
include_stmt,
ascii_char([0..255])
])
)
defparsec(:parse_imports, parse_imports)
def parse(code) do
{:ok, _, _, %Zig.Parser.Imports{imports: imports}, _, _} = parse_imports(code)
imports
end
end