defmodule Rhai.AST do
@moduledoc """
Compiled AST (abstract syntax tree) of a Rhai script.
"""
defstruct [
# The actual NIF Resource.
resource: nil,
# Normally the compiler will happily do stuff like inlining the
# resource in attributes. This will convert the resource into an
# empty binary with no warning. This will make that harder to
# accidentaly do.
# It also serves as a handy way to tell file handles apart.
reference: nil
]
@type t :: %__MODULE__{}
@doc """
Create an empty AST.
"""
def empty do
resource = Rhai.Native.ast_empty()
wrap_resource(resource)
end
@doc """
Get the source if any.
"""
@spec source(t()) :: String.t() | nil
def source(%__MODULE__{resource: resource}) do
Rhai.Native.ast_source(resource)
end
@doc """
Set the source.
"""
@spec set_source(t(), String.t()) :: t()
def set_source(%__MODULE__{resource: resource} = ast, source) do
Rhai.Native.ast_set_source(resource, source)
ast
end
@doc """
Clear the source.
"""
@spec clear_source(t()) :: t()
def clear_source(%__MODULE__{resource: resource} = ast) do
Rhai.Native.ast_clear_source(resource)
ast
end
@doc """
Merge two AST into one. Both AST’s are untouched and a new, merged, version is returned.
Statements in the second AST are simply appended to the end of the first without any processing.
Thus, the return value of the first AST (if using expression-statement syntax) is buried.
Of course, if the first AST uses a return statement at the end, then the second AST will essentially be dead code.
All script-defined functions in the second AST overwrite similarly-named functions in the first AST with the same number of parameters.
See [example](https://docs.rs/rhai/latest/rhai/struct.AST.html#example-1) in the Rhai documentation.
"""
@spec merge(t(), t()) :: t()
def merge(
%__MODULE__{resource: resource},
%__MODULE__{resource: other_resource}
) do
resource
|> Rhai.Native.ast_merge(other_resource)
|> wrap_resource()
end
@doc """
Combine one AST with another. The second AST is consumed.
Statements in the second AST are simply appended to the end of the first without any processing.
Thus, the return value of the first AST (if using expression-statement syntax) is buried.
Of course, if the first AST uses a return statement at the end, then the second AST will essentially be dead code.
All script-defined functions in the second AST overwrite similarly-named functions in the first AST with the same number of parameters.
See [example](https://docs.rs/rhai/latest/rhai/struct.AST.html#example-2) in the Rhai documentation.
"""
@spec combine(t(), t()) :: t()
def combine(
%__MODULE__{resource: resource},
%__MODULE__{resource: other_resource}
) do
resource
|> Rhai.Native.ast_combine(other_resource)
|> wrap_resource()
end
@doc """
Clear all function definitions in the AST.
"""
@spec clear_functions(t()) :: t()
def clear_functions(%__MODULE__{resource: resource} = ast) do
Rhai.Native.ast_clear_functions(resource)
ast
end
@doc """
Clear all statements in the AST, leaving only function definitions.
"""
@spec clear_statements(t()) :: t()
def clear_statements(%__MODULE__{resource: resource} = ast) do
Rhai.Native.ast_clear_statements(resource)
ast
end
@doc """
Clone the AST’s functions into a new AST. No statements are cloned.
"""
@spec clone_functions_only(t()) :: t()
def clone_functions_only(%__MODULE__{resource: resource}) do
resource
|> Rhai.Native.ast_clone_functions_only()
|> wrap_resource()
end
@doc """
Does this AST contain script-defined functions?
"""
@spec has_functions?(t()) :: bool
def has_functions?(%__MODULE__{resource: resource}) do
Rhai.Native.ast_has_functions(resource)
end
@doc false
def wrap_resource(resource) do
%__MODULE__{
resource: resource,
reference: make_ref()
}
end
end