#![doc = include_str!("../README.md")]
#![warn(missing_docs)]

// Needed for the roto macros
extern crate self as roto;

mod ast;

#[cfg(feature = "cli")]
mod cli;
mod codegen;
mod file_tree;
#[cfg(test)]
mod file_tree_tests;
mod ir_printer;
mod label;
mod lir;
mod mir;
mod module;
pub(crate) mod parser;
mod pipeline;
mod runtime;
pub mod tools;
mod typechecker;
mod value;

#[cfg(feature = "cli")]
pub use cli::cli;

#[cfg(all(test, not(miri)))]
pub(crate) use pipeline::{source_file, src};

pub use crate::value::List;
pub use codegen::{TypedFunc, check::RotoFunc};
pub use file_tree::{FileSpec, FileTree, SourceFile};
pub(crate) use pipeline::RotoError;
pub use pipeline::{Package, RotoReport};
pub use roto_macros::{
    Context, roto_function, roto_method, roto_static_method,
};
pub use runtime::{
    Ctx, NoCtx, RegistrationError, Runtime,
    context::{Context, ContextDescription},
    func::RegisterableFn,
    items::{
        Constant, Function, Impl, Item, Library, Module, Registerable, Type,
        Use,
    },
};
pub use value::{Val, Value, Verdict};

/// Create a list of items to be registered.
///
/// This macro evaluates to a [`Library`], which can be passed to
/// [`Runtime::from_lib`] and [`Runtime::add`] to be registered.
///
/// This macro automates some of the boilerplate around registering items, such
/// as adding documentation comments and extracting the names and parameter
/// names of functions. Within functions, all regular Rust syntax is accepted.
///
/// Documentation comments can be added to any of the items. These will be stored and
/// added to the documentation generated by Roto.
///
/// # Usage
///
/// ```
/// use roto::{library, Runtime};
///
/// let lib = library! {
///     /// Double the argument
///     fn double(x: i32) -> i32 {
///         2 * x
///     }
/// };
///
/// let rt = Runtime::from_lib(lib).unwrap();
/// // or
/// # let lib = library! {
/// #     /// Double the argument
/// #     fn double(x: i32) -> i32 {
/// #         2 * x
/// #     }
/// # };
/// let mut rt = Runtime::new();
/// rt.add(lib).unwrap();
/// ```
///
/// # Syntax
///
/// ## Types
///
/// Types are declared with a `type` alias style syntax. The declaration must
/// be  annotated with either `#[clone]` or `#[copy]`. Only types implementing
/// `Copy` can be marked with `#[copy]`. Since the Rust type (the right-hand
/// side) must implement [`Value`], you should wrap any custom type in `Val`.
///
/// ```rust
/// # #[derive(Clone, Copy)]
/// # struct Foo;
/// # use roto::Val;
/// # roto::library! {
/// /// A `Clone` type `Val<Foo>` registered as `Foo`
/// #[clone] type Foo = Val<Foo>;
///
/// /// A `Copy` type `Val<Foo>` registered as `Foo`
/// #[copy] type Foo = Val<Foo>;
/// # };
/// ```
///
/// ## Functions
///
/// Both `fn`-style and closure-style syntax is supported for functions. As expected,
/// closures can use variables from their environment.
///
/// ```rust
/// # #[derive(Clone)]
/// # struct Foo;
/// # #[derive(Clone)]
/// # struct Bar;
/// # use roto::Val;
/// # roto::library! {
/// /// A function
/// fn foo(a: i32, b: Val<Foo>) -> Val<Bar> {
///     todo!()
/// }
///
/// /// A closure
/// let foo = |a: i32, b: Val<Foo>| -> Val<Bar> {
///     todo!()
/// };
///
/// /// A closure with `move`
/// let foo = move |a: i32, b: Val<Foo>| -> Val<Bar> {
///     todo!()
/// };
/// # };
/// ```
///
/// ## Constants
///
/// ```rust
/// # #[derive(Clone)]
/// # struct Foo(i32);
/// # use roto::Val;
/// # roto::library! {
/// /// A constant `BAR` of type `u32`
/// const BAR: u32 = 42;
///
/// /// A constant `BAZ` of a custom type `Val<Foo>`
/// const BAZ: Val<Foo> = Val(Foo(42));
/// # };
/// ```
///
/// ## Modules
///
/// ```rust
/// # roto::library! {
/// /// Make a new module with some items
/// mod foo {
///     // Add items here (with the same syntax)
/// }
/// # };
/// ```
///
/// ## Impl blocks
///
/// <div class="warning">
///
/// Using `self` is not supported yet, but all functions declared
/// in an `impl` block with the "self" type as first parameter will be
/// registered as method.
///
/// </div>
///
/// ```rust
/// # #[derive(Clone)]
/// # struct Foo;
/// # use roto::Val;
/// # roto::library! {
/// /// Add (static) methods to the `Val<Foo>`
/// ///
/// /// Note that you have to use the Rust type, not the Roto name.
/// impl Val<Foo> {
///     // Add items here (with the same syntax)
/// }
/// # };
/// ```
///
/// ## Use
///
/// Imports can be added to the runtime as well, this will make the item
/// available to Roto in 2 locations: the original location and the location
/// where this `use` is located. The path must consist of modules _Roto_ names
/// of types.
///
/// <div class="warning">
///
/// Renaming items (e.g. `as`) and wildcard imports (e.g. `*`) are
/// not supported yet.
///
/// </div>
///
/// ```rust
/// # roto::library! {
/// /// Add imports, note that this uses Roto names for types
/// use Option::{Some, None};
/// # };
/// ```
///
/// ## Including other library items
///
/// ```rust
/// # let some_registerable = roto::library!{};
/// # roto::library! {
/// /// Include some registerable, including libraries
/// include!(some_registerable);
/// # };
///
/// # let some_registerable = roto::library!{};
/// # roto::library! {
/// // This is especially useful in combination with `impl` and `mod`:
/// mod foo { include!(some_registerable); }
/// # };
/// ```
///
/// # Tips
///
///  * It's possible to use `#[doc = some_expression]` to add documentation.
///  * Note that throughout this macro, you should use the _Rust_ name of a
///    type, instead of the _Roto_ name. There are only 2 exceptions: while
///    defining the type and while specifying imports.
///  * The order in which the items are specified within this macro is not
///    significant. You can even register functions that use types that you
///    declare later in the macro.
///  * It's not necessary to use this macro if you prefer not to or if you
///    want to define a library in a more dynamic way. This macro expands to
///    relatively simple function calls, though the API is a bit more verbose.
///    For the types involved, see the [`Library`] type.
pub use roto_macros::library;

/// Items exported only for use in macros
pub mod __internal {
    use crate::Value;
    pub use crate::runtime::context::ContextField;

    /// Stub function that can be used as an assertion that a type implements [`Value`].
    ///
    /// This is used by [`library!`](crate::library).
    pub fn implements_value<T: Value>() {}
}

pub(crate) const FIND_HELP: &str = "\n\
    If you are seeing this error you have found a bug in the Roto compiler.\n\
    Please open an issue at https://github.com/NLnetLabs/roto.";

/// Panic with an internal compiler error
///
/// Calling this macro instead of [`panic!`] signals a bug in the compiler
macro_rules! ice {
    () => {
        panic!("Internal compiler error{}", $crate::FIND_HELP)
    };
    ($s:literal) => {
        panic!("Internal compiler error: {}{}", format!($s), $crate::FIND_HELP)
    };
    ($s:literal, $($t:tt)*) => {
        panic!("Internal compiler error: {}{}", format!($s, $($t)*), $crate::FIND_HELP)
    }
}

pub(crate) use ice;

/// A source file location
///
/// Generally constructed with the [`location!`] macro.
#[derive(Debug, Clone)]
pub struct Location {
    /// Path of the file
    pub file: &'static str,

    /// Line number of the location
    pub line: u32,

    /// Column number of the location
    pub column: u32,
}

impl std::fmt::Display for Location {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        format!("{}:{}:{}", self.file, self.line, self.column).fmt(f)
    }
}

/// Rust source code location
#[macro_export]
macro_rules! location {
    () => {
        $crate::Location {
            file: file!(),
            line: line!(),
            column: column!(),
        }
    };
}
