lib/loupe/language.ex

defmodule Loupe.Language do
  @moduledoc """
  Loupe's compiler entrypoint.
  """
  alias Loupe.Language.Ast

  alias Loupe.Errors.LexerError
  alias Loupe.Errors.ParserError

  @type compile_error :: ParserError.t() | LexerError.t()
  @type sigil_definition :: {char(), binary()}

  @doc "Compiles a query to AST"
  @spec compile(String.t() | charlist()) :: {:ok, Ast.t()} | {:error, compile_error()}
  def compile(string) when is_binary(string) do
    string
    |> String.to_charlist()
    |> compile()
  end

  def compile(charlist) do
    with {:ok, tokens, _} <- :loupe_lexer.string(charlist),
         {:ok, ast} <- :loupe_parser.parse(tokens) do
      {:ok, new_ast(ast)}
    else
      {:error, {line, :loupe_parser, messages}} ->
        {:error, %ParserError{line: line, message: messages}}

      {:error, {line, :loupe_lexer, messages}, _} ->
        {:error, %LexerError{line: line, message: messages}}
    end
  end

  defp new_ast({action, quantifier, schema, predicates}) do
    Ast.new(action, schema, quantifier, predicates)
  end
end