/* -*- mode: c ; c-file-style: "canonware-c-style" -*-
 ******************************************************************************
 *
 * Copyright (C) 1996-2002 Jason Evans <jasone@canonware.com>.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice(s), this list of conditions and the following disclaimer
 *    unmodified other than the allowable addition of one or more
 *    copyright notices.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice(s), this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************
 *
 * Version: Onyx 3.0.2
 *
 ******************************************************************************/

#ifdef CW_DBG
#define CW_NXO_THREAD_BUFFER_SIZE 8
#else
#define CW_NXO_THREAD_BUFFER_SIZE 256
#endif

typedef struct cw_nxo_threadp_s cw_nxo_threadp_t;
typedef struct cw_nxoe_thread_s cw_nxoe_thread_t;

typedef struct cw_nxoe_threadts_s cw_nxoe_threadts_t;
typedef enum
{
    THREADTS_START,
    THREADTS_SLASH_CONT,
    THREADTS_COMMENT,
    THREADTS_INTEGER,
    THREADTS_INTEGER_RADIX,
#ifdef CW_REAL
    THREADTS_REAL_FRAC,
    THREADTS_REAL_EXP,
#endif
    THREADTS_STRING,
    THREADTS_STRING_NEWLINE_CONT,
    THREADTS_STRING_PROT_CONT,
    THREADTS_STRING_CRLF_CONT,
    THREADTS_STRING_HEX_CONT,
    THREADTS_STRING_HEX_FINISH,
    THREADTS_NAME
} cw_nxo_threadts_t;

struct cw_nxo_threadp_s
{
#ifdef CW_DBG
    cw_uint32_t magic;
#endif

    /* Current line number (counting starts at 1 by convention) and column
     * number (counting starts at 0). */
    cw_uint32_t line;
    cw_sint32_t column;
};

struct cw_nxoe_thread_s
{
    cw_nxoe_t nxoe;

    /* nx this thread is part of. */
    cw_nx_t *nx;

    /* Self, for the self operator, and as a convenience in various places
     * internally. */
    cw_nxo_t self;

#ifdef CW_THREADS
    /* Used by nxo_thread_thread(), nxo_thread_detach(), and
     * nxo_thread_join(). */
    cw_thd_t *thd;
    cw_mtx_t lock;
    cw_cnd_t done_cnd;
    cw_cnd_t join_cnd;
    cw_bool_t done:1;
    cw_bool_t gone:1;
    cw_bool_t detached:1;
    cw_bool_t joined:1;
#endif

    /* Used for remembering the current state of reference iteration. */
    cw_uint32_t ref_iter;

#ifdef CW_THREADS
    /* TRUE  : New array, dict, file, and string objects are implicitly
     *         locked.
     * FALSE : No implicit locking for new objects. */
    cw_bool_t locking:1;
#endif

    /* Stacks. */

    /* Execution stack. */
    cw_nxo_t estack;

    /* Execution index stack. */
    cw_nxo_t istack;
    
    /* Operand stack. */
    cw_nxo_t ostack;

    /* Dictionary stack. */
    cw_nxo_t dstack;

    /* Temp stack. */
    cw_nxo_t tstack;

    /* Files. */
    cw_nxo_t stdin_nxo;
    cw_nxo_t stdout_nxo;
    cw_nxo_t stderr_nxo;

    /* Tokenizer state.  If a token is broken across two or more input strings,
     * data are copied to an internal buffer, and state machine state is
     * preserved so that the buffered data need not be processed again. */

    /* Current scanner state. */
    cw_nxo_threadts_t state;

    /* Every time a '{' token is encountered by the scanner, this value is
     * incremented, and this value is decremented every time the scanner
     * encounters a '}' token.  Execution of objects is deferred if this value
     * is non-zero. */
    cw_uint32_t defer_count;

    /* Offset of first invalid character in tok_str. */
    cw_uint32_t index;

    /* Pointer to the token buffer.  As long as index is less than
     * CW_NXO_THREAD_BUFFER_SIZE, tok_str actually points to buffer.  Otherwise,
     * adequate space is allocated (using exponential doubling), and the
     * contents of tok_buffer are copied to the allocated buffer.
     *
     * If a temporary buffer is allocated, it is discarded as soon as the token
     * is handled.  That is, tok_buffer is used for every token until (if)
     * tok_buffer overflows. */
    cw_uint8_t *tok_str;
    cw_uint32_t buffer_len; /* Only valid if buffer overflowed. */
    cw_uint8_t buffer[CW_NXO_THREAD_BUFFER_SIZE];

    union
    {
	/* integer/real.  Which fields are valid (and whether the number is an
	 * integer or real) is implicit in the scanner state. */
	struct
	{
	    /* Mantissa. */
	    cw_bool_t mant_neg:1; /* FALSE: Positive. TRUE: Negative. */

	    /* Radix number base for mantissa (integers only). */
	    cw_uint32_t radix_base:7; /* Radix (2-36).  Error detection requires
				       * space to store up to 99. */

	    /* Whole part of mantissa (or radix integer). */
	    cw_bool_t whole:1; /* FALSE: No whole portion of mantissa.
				* TRUE: Whole portion of mantissa. */
	    cw_uint32_t whole_off; /* Offset to first digit of whole. */
	    cw_uint32_t whole_len; /* Length of whole. */

#ifdef CW_REAL
	    /* Fractional part of mantissa. */
	    cw_bool_t frac:1; /* FALSE: No fractional portion of mantissa.
			       * TRUE: Fractional portion of mantissa. */
	    cw_uint32_t frac_off; /* Offset to first digit of fractional. */
	    cw_uint32_t frac_len; /* Length of fractional. */

	    /* Exponent. */
	    cw_bool_t exp:1; /* FALSE: No exponent specified.
			      * TRUE: Exponential notation. */
	    cw_bool_t exp_sign:1; /* FALSE: No sign.  TRUE: Sign. */
	    cw_bool_t exp_neg:1; /* FALSE: Positive.  TRUE: Negative. */
	    cw_uint32_t exp_off; /* Offset to first digit of exponent. */
	    cw_uint32_t exp_len; /* Length of exponent. */
#endif
	} n;

	/* string. */
	struct
	{
	    cw_uint32_t q_depth;
	    cw_uint8_t hex_val;
	} s;

	/* name. */
	struct
	{
	    enum
	    {
		ACTION_EXECUTE,
		ACTION_LITERAL,
		ACTION_EVALUATE
	    } action;
	} m;	
    } m;
};

/* nxo_threadp. */
void
nxo_threadp_new(cw_nxo_threadp_t *a_threadp);

void
nxo_threadp_delete(cw_nxo_threadp_t *a_threadp, cw_nxo_t *a_thread);

void
nxo_threadp_position_get(cw_nxo_threadp_t *a_threadp, cw_uint32_t *r_line,
			 cw_uint32_t *r_column);

void
nxo_threadp_position_set(cw_nxo_threadp_t *a_threadp, cw_uint32_t a_line,
			 cw_uint32_t a_column);

/* nxo_thread. */
void
nxo_thread_new(cw_nxo_t *a_nxo, cw_nx_t *a_nx);

void
nxo_thread_start(cw_nxo_t *a_nxo);

void
nxo_thread_exit(cw_nxo_t *a_nxo);

#ifdef CW_THREADS
void
nxo_thread_thread(cw_nxo_t *a_nxo);

void
nxo_thread_detach(cw_nxo_t *a_nxo);

void
nxo_thread_join(cw_nxo_t *a_nxo);
#endif

cw_nxo_threadts_t
nxo_thread_state(cw_nxo_t *a_nxo);

cw_bool_t
nxo_thread_deferred(cw_nxo_t *a_nxo);

void
nxo_thread_reset(cw_nxo_t *a_nxo);

void
nxo_thread_loop(cw_nxo_t *a_nxo);

void
nxo_thread_interpret(cw_nxo_t *a_nxo, cw_nxo_threadp_t *a_threadp, const
		     cw_uint8_t *a_str, cw_uint32_t a_len);

void
nxo_thread_flush(cw_nxo_t *a_nxo, cw_nxo_threadp_t *a_threadp);

void
nxo_thread_nerror(cw_nxo_t *a_nxo, cw_nxn_t a_nxn);

void
nxo_thread_serror(cw_nxo_t *a_nxo, const cw_uint8_t *a_str, cw_uint32_t a_len);

cw_bool_t
nxo_thread_dstack_search(cw_nxo_t *a_nxo, cw_nxo_t *a_key, cw_nxo_t *r_value);

#ifdef CW_THREADS
cw_bool_t
nxo_thread_currentlocking(cw_nxo_t *a_nxo);

void
nxo_thread_setlocking(cw_nxo_t *a_nxo, cw_bool_t a_locking);
#else
#define nxo_thread_currentlocking(a_nxo) FALSE
#endif

#ifndef CW_USE_INLINES
cw_nx_t *
nxo_thread_nx_get(cw_nxo_t *a_nxo);

cw_nxo_t *
nxo_thread_ostack_get(cw_nxo_t *a_nxo);

cw_nxo_t *
nxo_thread_dstack_get(cw_nxo_t *a_nxo);

cw_nxo_t *
nxo_thread_estack_get(cw_nxo_t *a_nxo);

cw_nxo_t *
nxo_thread_istack_get(cw_nxo_t *a_nxo);

cw_nxo_t *
nxo_thread_tstack_get(cw_nxo_t *a_nxo);

cw_nxo_t *
nxo_thread_stdin_get(cw_nxo_t *a_nxo);

cw_nxo_t *
nxo_thread_stdout_get(cw_nxo_t *a_nxo);

cw_nxo_t *
nxo_thread_stderr_get(cw_nxo_t *a_nxo);
#endif

#if (defined(CW_USE_INLINES) || defined(CW_NXO_THREAD_C_))
CW_INLINE cw_nx_t *
nxo_thread_nx_get(cw_nxo_t *a_nxo)
{
    cw_nxoe_thread_t *thread;

    cw_check_ptr(a_nxo);
    cw_dassert(a_nxo->magic == CW_NXO_MAGIC);

    thread = (cw_nxoe_thread_t *) a_nxo->o.nxoe;
    cw_dassert(thread->nxoe.magic == CW_NXOE_MAGIC);
    cw_assert(thread->nxoe.type == NXOT_THREAD);

    return thread->nx;
}

CW_INLINE cw_nxo_t *
nxo_thread_ostack_get(cw_nxo_t *a_nxo)
{
    cw_nxoe_thread_t *thread;

    cw_check_ptr(a_nxo);
    cw_dassert(a_nxo->magic == CW_NXO_MAGIC);

    thread = (cw_nxoe_thread_t *) a_nxo->o.nxoe;
    cw_dassert(thread->nxoe.magic == CW_NXOE_MAGIC);
    cw_assert(thread->nxoe.type == NXOT_THREAD);

    return &thread->ostack;
}

CW_INLINE cw_nxo_t *
nxo_thread_dstack_get(cw_nxo_t *a_nxo)
{
    cw_nxoe_thread_t *thread;

    cw_check_ptr(a_nxo);
    cw_dassert(a_nxo->magic == CW_NXO_MAGIC);

    thread = (cw_nxoe_thread_t *) a_nxo->o.nxoe;
    cw_dassert(thread->nxoe.magic == CW_NXOE_MAGIC);
    cw_assert(thread->nxoe.type == NXOT_THREAD);

    return &thread->dstack;
}

CW_INLINE cw_nxo_t *
nxo_thread_estack_get(cw_nxo_t *a_nxo)
{
    cw_nxoe_thread_t *thread;

    cw_check_ptr(a_nxo);
    cw_dassert(a_nxo->magic == CW_NXO_MAGIC);

    thread = (cw_nxoe_thread_t *) a_nxo->o.nxoe;
    cw_dassert(thread->nxoe.magic == CW_NXOE_MAGIC);
    cw_assert(thread->nxoe.type == NXOT_THREAD);

    return &thread->estack;
}

CW_INLINE cw_nxo_t *
nxo_thread_istack_get(cw_nxo_t *a_nxo)
{
    cw_nxoe_thread_t *thread;

    cw_check_ptr(a_nxo);
    cw_dassert(a_nxo->magic == CW_NXO_MAGIC);

    thread = (cw_nxoe_thread_t *) a_nxo->o.nxoe;
    cw_dassert(thread->nxoe.magic == CW_NXOE_MAGIC);
    cw_assert(thread->nxoe.type == NXOT_THREAD);

    return &thread->istack;
}

CW_INLINE cw_nxo_t *
nxo_thread_tstack_get(cw_nxo_t *a_nxo)
{
    cw_nxoe_thread_t *thread;

    cw_check_ptr(a_nxo);
    cw_dassert(a_nxo->magic == CW_NXO_MAGIC);

    thread = (cw_nxoe_thread_t *) a_nxo->o.nxoe;
    cw_dassert(thread->nxoe.magic == CW_NXOE_MAGIC);
    cw_assert(thread->nxoe.type == NXOT_THREAD);

    return &thread->tstack;
}

CW_INLINE cw_nxo_t *
nxo_thread_stdin_get(cw_nxo_t *a_nxo)
{
    cw_nxoe_thread_t *thread;

    cw_check_ptr(a_nxo);
    cw_dassert(a_nxo->magic == CW_NXO_MAGIC);

    thread = (cw_nxoe_thread_t *) a_nxo->o.nxoe;
    cw_dassert(thread->nxoe.magic == CW_NXOE_MAGIC);
    cw_assert(thread->nxoe.type == NXOT_THREAD);

    return &thread->stdin_nxo;
}

CW_INLINE cw_nxo_t *
nxo_thread_stdout_get(cw_nxo_t *a_nxo)
{
    cw_nxoe_thread_t *thread;

    cw_check_ptr(a_nxo);
    cw_dassert(a_nxo->magic == CW_NXO_MAGIC);

    thread = (cw_nxoe_thread_t *) a_nxo->o.nxoe;
    cw_dassert(thread->nxoe.magic == CW_NXOE_MAGIC);
    cw_assert(thread->nxoe.type == NXOT_THREAD);

    return &thread->stdout_nxo;
}

CW_INLINE cw_nxo_t *
nxo_thread_stderr_get(cw_nxo_t *a_nxo)
{
    cw_nxoe_thread_t *thread;

    cw_check_ptr(a_nxo);
    cw_dassert(a_nxo->magic == CW_NXO_MAGIC);

    thread = (cw_nxoe_thread_t *) a_nxo->o.nxoe;
    cw_dassert(thread->nxoe.magic == CW_NXOE_MAGIC);
    cw_assert(thread->nxoe.type == NXOT_THREAD);

    return &thread->stderr_nxo;
}
#endif /* (defined(CW_USE_INLINES) || defined(CW_NXO_THREAD_C_)) */
