/* liblmin.h - include file for minimal embeddable LISP interpreter
**
** You may use this software however you like as long as you keep my
** name on it and don't sue me.
**
** For commentary on this license please see http://acme.com/license.html
*/

#ifndef _LIBLMIN_H_
#define _LIBLMIN_H_

typedef ssize_t lmin_atomidx;
typedef ssize_t lmin_cellidx;

typedef enum { NIL, ATOM, CONS, PRIM, CLOSURE, MACRO, NUM, CHR, STR, STREAM, VECTOR, DATA, ERREX } lmin_celltype;

extern lmin_cellidx lmin_nil, lmin_t, lmin_err_incomplete, lmin_err_eof;
extern lmin_cellidx lmin_stdin, lmin_stdout, lmin_stderr;

/* Initialize the lmin package. */
void lmin_init( void );

/* Terminate lmin, when your program exits. */
void lmin_term( void );

/* Parse a string into an lmin expression. */
lmin_cellidx lmin_parse( const char* str );

/* Make a global definition. */
lmin_cellidx lmin_define( lmin_cellidx var, lmin_cellidx val );

/* Evaluate an lmin expression. If the evaluation environment e is
** nil, the global environment is used.
*/
lmin_cellidx lmin_eval( lmin_cellidx c, lmin_cellidx e );

/* Get the length of a list. */
size_t lmin_list_length( lmin_cellidx c );

/* Convert an expression to a STR. */
lmin_cellidx lmin_tostring( lmin_cellidx c );

/* Convert an expression to a C string (that gets garbage collected later). */
const char* lmin_tocstring( lmin_cellidx c );

/* Evaluate an argument list. Usually the first thing done by a C
** primitive function.
*/
lmin_cellidx lmin_eval_args( lmin_cellidx c, lmin_cellidx e );

/* Check an evaluated argument list for any errors. The first one
** found gets returned, and if there are none then the return is t.
** Usually the second thing done by a C primitive function.
*/
lmin_cellidx lmin_check_errs( lmin_cellidx vals );

/* Check an evaluated argument list against the formal parameters
** list, including optional typespecs. Usually the third thing
** done by a C primitive function.
**
** A typespec is a string containing:
**   - a type name, one of: atom list executable number integer character string
**   - multiple type names separated by vertical bars
**   - a trailing * or ? indicating zero-or-more or zero-or-one
** If the parameter list includes a * or ?, it must be on the last typespec.
*/
lmin_cellidx lmin_check_types( lmin_cellidx vars, lmin_cellidx vals );

/* Write an expression to a file pointer. */
void lmin_write( lmin_cellidx c, FILE* fp );

/* Write an expression to a file pointer, followed by a newline. */
void lmin_write_line( lmin_cellidx c, FILE* fp );

/* Read an expression from a file pointer. */
lmin_cellidx lmin_read( FILE* fp, int* linesP );

/* Show an ERREX. */
void lmin_show_errex( FILE* fp, const char* filename, int linenum, lmin_cellidx errex );

/* Do garbage collection. */
void lmin_gc( void );

/* Mark a cell permanent - immune from garbage collection. */
lmin_cellidx lmin_perm( lmin_cellidx c );

/* Get a cell's lmin_celltype. */
lmin_celltype lmin_cell_type_get( lmin_cellidx c );

/* Get the name of an lmin_celltype. */
const char* lmin_celltype_str( lmin_celltype ct );

/* Parse and eval the expressions in an array of strings. Useful
** for loading some compiled-in functions.
*/
void lmin_load_addon( const char* name, const char** strs, int nstrs );

/* Call a LISP function from C. The first arg is the name of the
** function; the rest are lmin_cellidx; the last argument must be
** lmin_nil.
*/
lmin_cellidx lmin_call( const char* name, ... );

/* Some list accessors. */
lmin_cellidx lmin_first( lmin_cellidx c );
lmin_cellidx lmin_second( lmin_cellidx c );
lmin_cellidx lmin_third( lmin_cellidx c );
lmin_cellidx lmin_fourth( lmin_cellidx c );
lmin_cellidx lmin_fifth( lmin_cellidx c );


/* Each cell type can have a maker, a predicate, and a getter.
** Sometimes more than one.
*/

/* NIL */
bool lmin_nil_is( lmin_cellidx c );

/* ATOM */
lmin_cellidx lmin_atom_make( const char* str );
bool lmin_atom_is( lmin_cellidx c );
lmin_atomidx lmin_atom_idx_get( lmin_cellidx c );
const char* lmin_atom_str_get( lmin_cellidx c );

/* CONS */
lmin_cellidx lmin_cons_make( lmin_cellidx a, lmin_cellidx d );
bool lmin_cons_is( lmin_cellidx c );
lmin_cellidx lmin_cons_car_get( lmin_cellidx c );
lmin_cellidx lmin_cons_cdr_get( lmin_cellidx c );
lmin_cellidx lmin_cons_car_set( lmin_cellidx c, lmin_cellidx ar );
lmin_cellidx lmin_cons_cdr_set( lmin_cellidx c, lmin_cellidx dr );

/* PRIM */
typedef lmin_cellidx (*lmin_prim_fp)( lmin_cellidx f, lmin_cellidx c, lmin_cellidx e );
void lmin_prim_make( const char* name, lmin_prim_fp fp, const char* vars, bool special );
bool lmin_prim_is( lmin_cellidx c );
lmin_prim_fp lmin_prim_fp_get( lmin_cellidx c );
lmin_cellidx lmin_prim_vars_get( lmin_cellidx c );

/* CLOSURE */
lmin_cellidx lmin_closure_make( lmin_cellidx vars, lmin_cellidx body, lmin_cellidx defenv );
bool lmin_closure_is( lmin_cellidx c );
lmin_cellidx lmin_closure_vars_get( lmin_cellidx c );
lmin_cellidx lmin_closure_body_get( lmin_cellidx c );
lmin_cellidx lmin_closure_defenv_get( lmin_cellidx c );

/* MACRO */
lmin_cellidx lmin_macro_make( lmin_cellidx vars, lmin_cellidx body );
bool lmin_macro_is( lmin_cellidx c );
lmin_cellidx lmin_macro_vars_get( lmin_cellidx c );
lmin_cellidx lmin_macro_body_get( lmin_cellidx c );

/* NUM */
lmin_cellidx lmin_num_make( long l, double d, bool is_integer );
lmin_cellidx lmin_num_integer_make( long l );
lmin_cellidx lmin_num_float_make( double d );
bool lmin_num_is( lmin_cellidx c );
bool lmin_num_integer_is( lmin_cellidx c );
long lmin_num_integer_get( lmin_cellidx c );
double lmin_num_float_get( lmin_cellidx c );

/* CHR */
lmin_cellidx lmin_chr_make( char ch );
bool lmin_chr_is( lmin_cellidx c );
char lmin_chr_get( lmin_cellidx c );

/* STR */
lmin_cellidx lmin_str_make( const char* s );
/* Alternate maker routine where the length is passed in and the source
** string need not be null terminated. Useful for extracting substrings.
*/
lmin_cellidx lmin_str_makel( const char* s, size_t len );
/* Another alternate maker routine which takes two source strings
** and concatenates them.
*/
lmin_cellidx lmin_str_make2( const char* s1, const char* s2 );
bool lmin_str_is( lmin_cellidx c );
const char* lmin_str_get( lmin_cellidx c );
size_t lmin_str_len_get( lmin_cellidx c );
lmin_cellidx lmin_str_chrat( lmin_cellidx c, size_t p );

/* STREAM */
bool lmin_stream_is( lmin_cellidx c );
lmin_cellidx lmin_stream_make( FILE* fp );
FILE* lmin_stream_get( lmin_cellidx c );
lmin_cellidx lmin_stream_open( const char* path, const char* mode );
void lmin_stream_close( lmin_cellidx c );
void lmin_stream_flush( lmin_cellidx c );

/* VECTOR */
lmin_cellidx lmin_vector_make( size_t len, lmin_cellidx fill );
bool lmin_vector_is( lmin_cellidx c );
lmin_cellidx* lmin_vector_getvec( lmin_cellidx c );
size_t lmin_vector_len_get( lmin_cellidx c );
void lmin_vector_len_set( lmin_cellidx c, size_t len );
lmin_cellidx lmin_vector_get( lmin_cellidx c, size_t idx );
lmin_cellidx lmin_vector_set( lmin_cellidx c, size_t idx, lmin_cellidx val );
lmin_cellidx lmin_vector_fill_get( lmin_cellidx c );
void lmin_vector_fill_set( lmin_cellidx c, lmin_cellidx fill );

/* DATA */
typedef void (*lmin_freefp)( void* p );
lmin_cellidx lmin_data_make( void* ptr, lmin_freefp freefp );
bool lmin_data_is( lmin_cellidx c );
void* lmin_data_ptr_get( lmin_cellidx c );
lmin_freefp lmin_data_freefp_get( lmin_cellidx c );

/* ERREX */
bool lmin_errex_is( lmin_cellidx c );
lmin_cellidx lmin_errex_payload_get( lmin_cellidx c );
lmin_cellidx lmin_errex_callstack_get( lmin_cellidx c );
/* ERREX err */
lmin_cellidx lmin_errex_err_str_make( lmin_cellidx msg );
lmin_cellidx lmin_errex_err_make( const char* format, ... ) __attribute__((format(printf,1,2)));
bool lmin_errex_err_is( lmin_cellidx c );
const char* lmin_errex_err_str_get( lmin_cellidx c );
/* ERREX exception */
lmin_cellidx lmin_errex_exception_make( lmin_cellidx tag, lmin_cellidx value );
bool lmin_errex_exception_is( lmin_cellidx c );
lmin_cellidx lmin_errex_exception_tag_get( lmin_cellidx c );
lmin_cellidx lmin_errex_exception_value_get( lmin_cellidx c );

#endif /* _LIBLMIN_H_ */
