/*!
Tulisp is a Lisp interpreter that can be embedded into Rust programs.  The
syntax tries to closely match that of Emacs Lisp.  It was primarily designed to
be a configuration language, but it also works well as a general purpose
embedded scripting language.

One of the many benefits of using the Emacs Lisp syntax is that we can reuse its
documentation for the builtin functions and macros.  And for people who are
already familiar with Emacs Lisp, there's no need to learn an extra language.

## Getting started

Tulisp requires `rustc` version 1.88 or higher.

It is very easy to get started.  Here's an example:
*/

/*!
  ```rust
  use tulisp::{TulispContext, Error};

  fn main() -> Result<(), Error> {
      // Create a new Tulisp execution context.
      let mut ctx = TulispContext::new();

      // Add a function called `add_nums` to `ctx`.
      ctx.add_function("add_round", |a: f64, b: f64| -> i64 {
          (a + b).round() as i64
      });

      // Write a lisp program that calls `add_nums`
      let program = "(add_round 10.2 20.0)";

      // Evaluate the program, and save the result.
      let sum: i64 = ctx.eval_string(program)?.try_into()?;

      assert_eq!(sum, 30);
      Ok(())
  }
  ```

## Features

- `defun`s, `defmacro`s and `lambda`s
- `intern` to find/create symbols dynamically
- Backquote/Unquote forms (for example `` `(answer . ,(+ 2 3)) ``)
- Threading macros (`thread-first` and `thread-last`)
- Methods for reading from alists and plists
- Lexical scopes and lexical binding
- Tailcall Optimization
- Proc macros for exposing rust functions to tulisp
- Decl macros for
  [creating lists](https://docs.rs/tulisp/latest/tulisp/macro.list.html)
  and
  [destructuring lists](https://docs.rs/tulisp/latest/tulisp/macro.destruct_bind.html)
  quickly.
- Easy to use [interpreter](https://docs.rs/tulisp/latest/tulisp/struct.TulispContext.html) and [object](https://docs.rs/tulisp/latest/tulisp/struct.TulispObject.html)s
- Backtraces for errors

## Performance

Tulisp has a light-weight tree-walking interpreter with very low startup times and sufficient speed for many config/simulation needs.

## Builtin functions

A list of currently available builtin functions can be found [here](builtin).
*/

/*!
## Next steps

1. Values in _Tulisp_ are represented in rust as [`TulispObject`]s.  That struct
   implements methods for performing operations on Tulisp values.

1. [`TulispContext`] tracks the state of the interpreter and provides methods
   for executing _Tulisp_ programs.
*/

mod eval;
mod macros;
mod parse;

pub mod builtin;

mod cons;
pub use cons::{BaseIter, Iter};

mod context;
pub use context::{Plist, Plistable, Rest, TulispContext};

mod error;
pub use error::{Error, ErrorKind};

pub mod lists;

mod number;
pub use number::Number;

mod value;
pub use value::TulispAny;
#[doc(hidden)]
pub use value::TulispValue;

mod object;
pub use {
    object::TulispObject, object::conversions::TulispConvertible, object::wrappers::generic::Shared,
};

#[cfg(test)]
mod test_utils {
    #[track_caller]
    fn eval_string(ctx: &mut crate::TulispContext, s: &str) -> Result<crate::TulispObject, String> {
        ctx.eval_string(s).map_err(|e| e.format(ctx))
    }

    #[track_caller]
    fn must_eval_string(ctx: &mut crate::TulispContext, s: &str) -> crate::TulispObject {
        match eval_string(ctx, s) {
            Ok(t) => t,
            Err(e) => {
                panic!("{e}");
            }
        }
    }

    #[track_caller]
    pub(crate) fn eval_assert_equal(ctx: &mut crate::TulispContext, a: &str, b: &str) {
        let av = must_eval_string(ctx, a);
        let bv = must_eval_string(ctx, b);
        assert!(
            crate::TulispObject::equal(&av, &bv),
            "{}(=> {}) != {}(=> {})",
            a,
            av,
            b,
            bv
        );
    }

    #[track_caller]
    pub(crate) fn eval_assert(ctx: &mut crate::TulispContext, a: &str) {
        let av = must_eval_string(ctx, a);
        assert!(av.is_truthy(), "{}(=> {}) is not true", a, av);
    }

    #[track_caller]
    pub(crate) fn eval_assert_not(ctx: &mut crate::TulispContext, a: &str) {
        let av = must_eval_string(ctx, a);
        assert!(av.null(), "{}(=> {}) is not nil", a, av);
    }

    #[track_caller]
    pub(crate) fn eval_assert_error(ctx: &mut crate::TulispContext, a: &str, msg: &str) {
        match eval_string(ctx, a) {
            Ok(v) => panic!("Expected error but got {} for {}", v, a),
            Err(e) => {
                assert_eq!(e.to_string(), msg, "Error message mismatch for {}", a);
            }
        }
    }
}
