lib/gcode/model/expr/unary.ex

defmodule Gcode.Model.Expr.Unary do
  use Gcode.Option
  defstruct op: none(), expr: none()
  alias Gcode.Model.{Expr, Expr.Unary}
  import Gcode.Model.Expr.Helpers
  use Gcode.Result

  @moduledoc """
  Represents a unary (or prefix) expression in G-code.  A unary consists of a
  single operand and an operator.
  """

  @operators ~w[! + - #]a

  @typedoc "Valid unary operators"
  @type operator :: :! | :+ | :- | :"#"

  @type t :: %Unary{op: Option.t(atom), expr: Option.t(Expr.t())}

  @doc """
  Wrap an inner expression and operator in a unary expression.
  """
  @spec init(operator, Expr.t()) :: Result.t(t)
  def init(operator, expr) when operator in @operators and is_expression(expr),
    do: ok(%Unary{op: some(operator), expr: some(expr)})

  def init(operator, expr) when operator in @operators,
    do: error({:expression_error, "Expected expression, but received #{inspect(expr)}"})

  def init(operator, expr) when is_expression(expr),
    do: error({:expression_error, "Expected unary operator, but received #{inspect(operator)}"})

  def init(operator, expr),
    do:
      error(
        {:expression_error,
         "Expected unary operator and expression, but received #{inspect(operator: operator, expression: expr)}"}
      )
end