14161 lines
388 KiB
C
14161 lines
388 KiB
C
/*
|
|
Copyright (c) 2006-2020 Chung, Hyung-Hwan. 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, this list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
notice, 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 AUTHOR "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 AUTHOR 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.
|
|
*/
|
|
|
|
#include "hawk-prv.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#if defined(HAWK_ATOMIC_CAS_BOOL) && defined(HAWK_ATOMIC_LOAD) && defined(HAWK_ATOMIC_STORE)
|
|
#define ATOMIC_EVAL_MODSYM
|
|
#endif
|
|
|
|
#define PRINT_IOERR (-99)
|
|
#define CMP_ERROR (-99)
|
|
#define DEF_BUF_CAPA (256)
|
|
#define HAWK_RTX_STACK_INCREMENT (512)
|
|
|
|
#if defined(HAWK_ENABLE_UCONTEXT)
|
|
#define HAWK_CO_STACK_SIZE (65536UL)
|
|
#define HAWK_CO_POOL_MAX 16
|
|
/* how many direct recursive AWK function calls are allowed per C stack segment
|
|
* before switching to a fresh heap-allocated stack. the main thread stack is
|
|
* large (typically 8 MB) so a generous limit is safe there; each 64 KB
|
|
* coroutine stack can only accommodate a small number of additional frames. */
|
|
#define HAWK_CO_MAIN_DEPTH_THRESHOLD 500
|
|
#define HAWK_CO_COROUTINE_DEPTH_THRESHOLD 24
|
|
|
|
static int run_block (hawk_rtx_t* rtx, hawk_nde_blk_t* nde); /* forward */
|
|
|
|
static char* hawk_co_get_stack (hawk_rtx_t* rtx)
|
|
{
|
|
/* get a stack from the free list */
|
|
if (rtx->co.pool_size > 0) return rtx->co.pool[--rtx->co.pool_size];
|
|
return (char*)hawk_rtx_allocmem(rtx, HAWK_CO_STACK_SIZE);
|
|
}
|
|
|
|
static void hawk_co_put_stack (hawk_rtx_t* rtx, char* stack)
|
|
{
|
|
/* add a stack to the free list. if the list is full, destroy it. */
|
|
if (rtx->co.pool_size < HAWK_CO_POOL_MAX)
|
|
rtx->co.pool[rtx->co.pool_size++] = stack;
|
|
else
|
|
hawk_rtx_freemem(rtx, stack);
|
|
}
|
|
|
|
/* makecontext only accepts int arguments, so we split the rtx pointer into
|
|
* two ints and reassemble inside the trampoline. each rtx carries its own
|
|
* pending_body/pending_result so no global state is needed. */
|
|
#if (HAWK_SIZEOF_VOID_P == 8)
|
|
static void hawk_co_trampoline (int hi, int lo)
|
|
{
|
|
hawk_rtx_t* rtx = (hawk_rtx_t*)((hawk_uintptr_t)(unsigned int)hi << 32 | (unsigned int)lo);
|
|
*rtx->co.pending_result = run_block(rtx, rtx->co.pending_body);
|
|
/* uc_link causes automatic return to caller context when we return */
|
|
}
|
|
#else
|
|
static void hawk_co_trampoline (int arg)
|
|
{
|
|
hawk_rtx_t* rtx = (hawk_rtx_t*)(hawk_uintptr_t)(unsigned int)arg;
|
|
*rtx->co.pending_result = run_block(rtx, rtx->co.pending_body);
|
|
/* uc_link causes automatic return to caller context when we return */
|
|
}
|
|
#endif
|
|
|
|
#endif /* HAWK_ENABLE_UCONTEXT */
|
|
|
|
enum exit_level_t
|
|
{
|
|
EXIT_NONE,
|
|
EXIT_BREAK,
|
|
EXIT_CONTINUE,
|
|
EXIT_FUNCTION,
|
|
EXIT_NEXT,
|
|
EXIT_GLOBAL,
|
|
EXIT_ABORT
|
|
};
|
|
|
|
enum cmp_op_t
|
|
{
|
|
CMP_OP_NONE = 0,
|
|
CMP_OP_EQ = 1,
|
|
CMP_OP_NE = 2,
|
|
CMP_OP_GT = 3,
|
|
CMP_OP_GE = 4,
|
|
CMP_OP_LT = 5,
|
|
CMP_OP_LE = 6
|
|
};
|
|
typedef enum cmp_op_t cmp_op_t;
|
|
|
|
struct pafv_t
|
|
{
|
|
hawk_val_t** args;
|
|
hawk_oow_t nargs;
|
|
const hawk_ooch_t* argspec;
|
|
hawk_oow_t argspeclen;
|
|
};
|
|
typedef struct pafv_t pafv_t;
|
|
|
|
struct pafn_t
|
|
{
|
|
hawk_nde_t* args;
|
|
hawk_oow_t nargs;
|
|
const hawk_ooch_t* argspec;
|
|
};
|
|
typedef struct pafn_t pafn_t;
|
|
|
|
#define DEFAULT_CONVFMT HAWK_T("%.6g")
|
|
#define DEFAULT_OFMT HAWK_T("%.6g")
|
|
#define DEFAULT_FS HAWK_T(" ")
|
|
#define DEFAULT_OFS HAWK_T(" ")
|
|
#define DEFAULT_ORS HAWK_T("\n")
|
|
#define DEFAULT_ORS_CRLF HAWK_T("\r\n")
|
|
#define DEFAULT_SUBSEP HAWK_T("\034")
|
|
|
|
/* the index of a positional variable should be a positive interger
|
|
* equal to or less than the maximum value of the type by which
|
|
* the index is represented. but it has an extra check against the
|
|
* maximum value of hawk_oow_t as the reference is represented
|
|
* in a pointer variable of hawk_val_ref_t and sizeof(void*) is
|
|
* equal to sizeof(hawk_oow_t). */
|
|
#define IS_VALID_POSIDX(idx) \
|
|
((idx) >= 0 && \
|
|
(idx) <= HAWK_TYPE_MAX(hawk_int_t) && \
|
|
(idx) <= HAWK_TYPE_MAX(hawk_oow_t))
|
|
|
|
#define CLRERR(rtx) hawk_rtx_clrerror(rtx)
|
|
#define ADJERR_LOC(rtx,l) do { (rtx)->gem_.errloc = *(l); } while (0)
|
|
|
|
/* Sentinel returned by xstack_fast_operand when the node is not a fast leaf
|
|
* (distinct from HAWK_NULL which signals allocation failure). */
|
|
static hawk_val_t xstack_notleaf_sentinel;
|
|
#define XSTACK_NOT_A_LEAF (&xstack_notleaf_sentinel)
|
|
|
|
static hawk_oow_t push_arg_from_vals (hawk_rtx_t* rtx, const hawk_loc_t* call_loc, void* data);
|
|
static hawk_oow_t push_arg_from_nde (hawk_rtx_t* rtx, const hawk_loc_t* call_loc, void* data);
|
|
static hawk_oow_t push_arg_from_nde_xstack (hawk_rtx_t* rtx, const hawk_loc_t* call_loc, void* data);
|
|
|
|
static int init_rtx (hawk_rtx_t* rtx, hawk_t* hawk, hawk_rio_cbs_t* rio);
|
|
static void fini_rtx (hawk_rtx_t* rtx, int fini_globals);
|
|
|
|
static int init_globals (hawk_rtx_t* rtx);
|
|
static void refdown_globals (hawk_rtx_t* rtx, int pop);
|
|
|
|
static int run_pblocks (hawk_rtx_t* rtx);
|
|
static int run_pblock_chain (hawk_rtx_t* rtx, hawk_chain_t* cha);
|
|
static int run_pblock (hawk_rtx_t* rtx, hawk_chain_t* cha, hawk_oow_t bno);
|
|
static int run_null_block_for_blockless_pattern (hawk_rtx_t* rtx);
|
|
static int run_block (hawk_rtx_t* rtx, hawk_nde_blk_t* nde);
|
|
static int run_statement (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static int run_if (hawk_rtx_t* rtx, hawk_nde_if_t* nde);
|
|
static int run_switch (hawk_rtx_t* rtx, const hawk_exec_stack_t* es, hawk_nde_switch_t* nde);
|
|
static int run_while (hawk_rtx_t* rtx, int state, hawk_nde_while_t* nde);
|
|
static int run_for (hawk_rtx_t* rtx, int state, hawk_nde_for_t* nde);
|
|
static int run_forin (hawk_rtx_t* rtx, const hawk_exec_stack_t* es, hawk_nde_forin_t* nde);
|
|
static int run_break (hawk_rtx_t* rtx, hawk_nde_break_t* nde);
|
|
static int run_continue (hawk_rtx_t* rtx, hawk_nde_continue_t* nde);
|
|
static int run_return (hawk_rtx_t* rtx, hawk_nde_return_t* nde);
|
|
static int run_exit (hawk_rtx_t* rtx, hawk_nde_exit_t* nde);
|
|
static int run_next (hawk_rtx_t* rtx, hawk_nde_next_t* nde);
|
|
static int run_nextfile (hawk_rtx_t* rtx, hawk_nde_nextfile_t* nde);
|
|
static int run_delete (hawk_rtx_t* rtx, hawk_nde_delete_t* nde);
|
|
static int run_reset (hawk_rtx_t* rtx, hawk_nde_reset_t* nde);
|
|
static int run_print (hawk_rtx_t* rtx, hawk_nde_print_t* nde);
|
|
static int run_printf (hawk_rtx_t* rtx, hawk_nde_print_t* nde);
|
|
|
|
static int output_formatted (hawk_rtx_t* rtx, hawk_out_type_t out_type, const hawk_ooch_t* dst, const hawk_ooch_t* fmt, hawk_oow_t fmt_len, hawk_nde_t* args);
|
|
static int output_formatted_bytes (hawk_rtx_t* rtx, hawk_out_type_t out_type, const hawk_ooch_t* dst, const hawk_bch_t* fmt, hawk_oow_t fmt_len, hawk_nde_t* args);
|
|
|
|
static hawk_val_t* eval_expression (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_expression0 (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static HAWK_INLINE_ALWAYS hawk_val_t* eval_expression0_xstack (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_expression_xstack (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
static int push_eframe (hawk_rtx_t* rtx, hawk_ef_state_t state, hawk_nde_t* nde);
|
|
static int push_eframe_with_aux (hawk_rtx_t* rtx, hawk_ef_state_t state, hawk_nde_t* nde, hawk_oow_t aux);
|
|
static int process_eframes (hawk_rtx_t* rtx, hawk_oow_t eframe_base, hawk_val_t** cur_inout);
|
|
static int run_driver (hawk_rtx_t* rtx, hawk_oow_t exec_base, hawk_oow_t eframe_base);
|
|
#endif
|
|
|
|
static hawk_val_t* eval_group (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
|
|
static hawk_val_t* eval_assignment (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* do_assignment (hawk_rtx_t* rtx, hawk_nde_t* var, hawk_val_t* val, int is_init);
|
|
static hawk_val_t* do_assignment_nonindexed (hawk_rtx_t* rtx, hawk_nde_var_t* var, hawk_val_t* val);
|
|
static hawk_val_t* do_assignment_indexed (hawk_rtx_t* rtx, hawk_nde_var_t* var, hawk_val_t* val);
|
|
static hawk_val_t* do_assignment_positional (hawk_rtx_t* rtx, hawk_nde_pos_t* pos, hawk_val_t* val);
|
|
|
|
static hawk_val_t* eval_binary (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_binop_lor (hawk_rtx_t* rtx, hawk_nde_t* left, hawk_nde_t* right);
|
|
static hawk_val_t* eval_binop_land (hawk_rtx_t* rtx, hawk_nde_t* left, hawk_nde_t* right);
|
|
static hawk_val_t* eval_binop_in (hawk_rtx_t* rtx, hawk_nde_t* left, hawk_nde_t* right);
|
|
|
|
static hawk_val_t* eval_binop_teq (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
static hawk_val_t* eval_binop_tne (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
static hawk_val_t* eval_binop_eq (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
static hawk_val_t* eval_binop_ne (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
static hawk_val_t* eval_binop_gt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
static hawk_val_t* eval_binop_ge (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
static hawk_val_t* eval_binop_lt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
static hawk_val_t* eval_binop_le (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
|
|
static HAWK_INLINE hawk_val_t* eval_binop_plus (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
static HAWK_INLINE hawk_val_t* eval_binop_minus (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
static HAWK_INLINE hawk_val_t* eval_binop_mul (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
static HAWK_INLINE hawk_val_t* eval_binop_div (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
static HAWK_INLINE hawk_val_t* eval_binop_idiv (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
static HAWK_INLINE hawk_val_t* eval_binop_mod (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
static HAWK_INLINE hawk_val_t* eval_binop_exp (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
static HAWK_INLINE hawk_val_t* eval_binop_concat (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
static HAWK_INLINE hawk_val_t* eval_binop_lshift (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
static HAWK_INLINE hawk_val_t* eval_binop_rshift (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
static HAWK_INLINE hawk_val_t* eval_binop_bor (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
static HAWK_INLINE hawk_val_t* eval_binop_bxor (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
static HAWK_INLINE hawk_val_t* eval_binop_band (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
|
|
static hawk_val_t* eval_binop_ma (hawk_rtx_t* rtx, hawk_nde_t* left, hawk_nde_t* right);
|
|
static hawk_val_t* eval_binop_nm (hawk_rtx_t* rtx, hawk_nde_t* left, hawk_nde_t* right);
|
|
|
|
static hawk_val_t* eval_unary (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_incpre (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_incpst (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_cnd (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
|
|
static hawk_val_t* eval_fncall_fnc (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
#define KEEP_EVAL_FNCALL_FUN
|
|
#if defined(KEEP_EVAL_FNCALL_FUN)
|
|
static HAWK_INLINE hawk_val_t* eval_fncall_fun (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
#endif
|
|
static hawk_val_t* eval_fncall_var (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
|
|
static int get_reference (hawk_rtx_t* rtx, hawk_nde_t* nde, hawk_val_t*** ref);
|
|
static hawk_val_t** get_reference_indexed (hawk_rtx_t* rtx, hawk_nde_var_t* var);
|
|
|
|
static hawk_val_t* eval_char (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_bchr (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_int (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_flt (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_str (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_mbs (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_rex (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_xnil (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_xtrue (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_xfalse (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_xargc (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_xargv (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_xargvidx (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_fun (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_modsym (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_named (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_gbl (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_lcl (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_arg (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_indexed (hawk_rtx_t* rtx, hawk_nde_var_t* var);
|
|
static hawk_val_t* eval_namedidx (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_gblidx (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_lclidx (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_argidx (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_pos (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_getline (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_print (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static hawk_val_t* eval_printf (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
|
|
static HAWK_INLINE_ALWAYS hawk_fun_t* resolve_fncall_fun (hawk_rtx_t* rtx, hawk_nde_fncall_t* call);
|
|
|
|
static int read_record (hawk_rtx_t* rtx);
|
|
|
|
static hawk_ooch_t* idxnde_to_str (hawk_rtx_t* rtx, hawk_nde_t* nde, hawk_ooch_t* buf, hawk_oow_t* len, hawk_nde_t** remidx, hawk_int_t* firstidxint);
|
|
static hawk_ooi_t idxnde_to_int (hawk_rtx_t* rtx, hawk_nde_t* nde, hawk_nde_t** remidx);
|
|
|
|
typedef hawk_val_t* (*binop_func_t) (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right);
|
|
typedef hawk_val_t* (*eval_expr_t) (hawk_rtx_t* rtx, hawk_nde_t* nde);
|
|
static binop_func_t get_binop_func (int opcode);
|
|
|
|
#define POS_VAL(rtx, idx) \
|
|
(((idx) == 0)? (rtx)->inrec.d0: \
|
|
((idx) > 0 && (idx) <= (hawk_int_t)(rtx)->inrec.nflds)? (rtx)->inrec.flds[(idx) - 1].val: \
|
|
hawk_val_zls)
|
|
|
|
HAWK_INLINE hawk_oow_t hawk_rtx_getnargs (hawk_rtx_t* rtx)
|
|
{
|
|
return (hawk_oow_t)HAWK_RTX_STACK_NARGS(rtx);
|
|
}
|
|
|
|
HAWK_INLINE hawk_val_t* hawk_rtx_getarg (hawk_rtx_t* rtx, hawk_oow_t idx)
|
|
{
|
|
return HAWK_RTX_STACK_ARG(rtx, idx);
|
|
}
|
|
|
|
HAWK_INLINE hawk_val_t* hawk_rtx_getgbl (hawk_rtx_t* rtx, int id)
|
|
{
|
|
HAWK_ASSERT(id >= 0 && id < (int)HAWK_ARR_SIZE(rtx->hawk->parse.gbls));
|
|
return HAWK_RTX_STACK_GBL(rtx, id);
|
|
}
|
|
|
|
const hawk_oocs_t* hawk_rtx_getsubsep (hawk_rtx_t* rtx)
|
|
{
|
|
return &rtx->gbl.subsep;
|
|
}
|
|
|
|
/* internal function to set a value to a global variable.
|
|
* this function can handle a few special global variables that
|
|
* require special treatment. */
|
|
static int set_global (hawk_rtx_t* rtx, int idx, hawk_nde_var_t* var, hawk_val_t* val, int assign)
|
|
{
|
|
hawk_val_t* old;
|
|
hawk_rtx_ecb_t* ecb, * ecb_next;
|
|
hawk_val_type_t vtype, old_vtype;
|
|
|
|
old = HAWK_RTX_STACK_GBL(rtx, idx);
|
|
|
|
vtype = HAWK_RTX_GETVALTYPE(rtx, val);
|
|
old_vtype = HAWK_RTX_GETVALTYPE(rtx, old);
|
|
|
|
if (!(rtx->hawk->opt.trait & HAWK_FLEXMAP))
|
|
{
|
|
hawk_errnum_t errnum = HAWK_ENOERR;
|
|
const hawk_ooch_t* errfmt;
|
|
|
|
if (vtype == HAWK_VAL_MAP || vtype == HAWK_VAL_ARR)
|
|
{
|
|
if (old_vtype == HAWK_VAL_NIL)
|
|
{
|
|
/* a nil value can be overridden with any values */
|
|
/* ok. no error */
|
|
}
|
|
else if (!assign && old_vtype == vtype)
|
|
{
|
|
/* when both are maps, how should this operation be
|
|
* interpreted?
|
|
*
|
|
* is it an assignment?
|
|
* old = new
|
|
*
|
|
* or is it to delete all elements in the array
|
|
* and add new items?
|
|
* for (i in old) delete old[i];
|
|
* for (i in new) old[i] = new[i];
|
|
*
|
|
* i interpret this operation as the latter.
|
|
*/
|
|
|
|
/* ok. no error */
|
|
}
|
|
else
|
|
{
|
|
errnum = HAWK_ESCALARTONONSCA;
|
|
errfmt = HAWK_T("not allowed to change a scalar value in '%.*js' to a nonscalar value");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (old_vtype == HAWK_VAL_MAP || old_vtype == HAWK_VAL_ARR)
|
|
{
|
|
errnum = HAWK_ENONSCATOSCALAR;
|
|
errfmt = HAWK_T("not allowed to change a nonscalar value in '%.*js' to a scalar value");
|
|
}
|
|
}
|
|
|
|
if (errnum != HAWK_ENOERR)
|
|
{
|
|
/* once a variable becomes a map, it cannot be assigned
|
|
* others value than another map. you can only add a member
|
|
* using indexing. */
|
|
if (var)
|
|
{
|
|
/* global variable */
|
|
hawk_rtx_seterrfmt(rtx, &var->loc, errnum, errfmt, var->id.name.len, var->id.name.ptr);
|
|
}
|
|
else
|
|
{
|
|
/* hawk_rtx_setgbl() has been called */
|
|
hawk_oocs_t ea;
|
|
ea.ptr = (hawk_ooch_t*)hawk_getgblname(hawk_rtx_gethawk(rtx), idx, &ea.len);
|
|
hawk_rtx_seterrfmt(rtx, HAWK_NULL, errnum, errfmt, ea.len, ea.ptr);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (vtype == HAWK_VAL_MAP || vtype == HAWK_VAL_ARR)
|
|
{
|
|
if (idx >= HAWK_MIN_GBL_ID && idx <= HAWK_MAX_GBL_ID)
|
|
{
|
|
/* short-circuit check block to prevent the basic built-in
|
|
* variables from being assigned a map. if you happen to add
|
|
* one and if that's allowed to be a map, you may have to
|
|
* change the condition here. */
|
|
|
|
/* TODO: use global variable attribute. can it be a map? can it be a scalar? is it read-only???? */
|
|
|
|
hawk_oocs_t ea;
|
|
ea.ptr = (hawk_ooch_t*)hawk_getgblname(hawk_rtx_gethawk(rtx), idx, &ea.len);
|
|
hawk_rtx_seterrfmt(rtx, HAWK_NULL, HAWK_ESCALARTONONSCA, HAWK_T("not allowed to change a scalar value in '%.*js' to a nonscalar value"), ea.len, ea.ptr);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (old == val && idx != HAWK_GBL_NF) /* read the comment in the case block for HAWK_GBL_NF below */
|
|
{
|
|
/* if the old value is the same as the new value, don't take any actions.
|
|
* note that several inspections have been performed before this check,
|
|
* mainly for consistency. anyway, this condition can be met if you execute
|
|
* a statement like 'ARGV=ARGV'. */
|
|
return 0;
|
|
}
|
|
|
|
/* perform actual assignment or assignment-like operation */
|
|
switch (idx)
|
|
{
|
|
case HAWK_GBL_CONVFMT:
|
|
{
|
|
hawk_oow_t i;
|
|
hawk_oocs_t str;
|
|
|
|
str.ptr = hawk_rtx_valtooocstrdup(rtx, val, &str.len);
|
|
if (HAWK_UNLIKELY(!str.ptr)) return -1;
|
|
|
|
for (i = 0; i < str.len; i++)
|
|
{
|
|
if (str.ptr[i] == '\0')
|
|
{
|
|
/* '\0' is included in the value */
|
|
hawk_rtx_freemem(rtx, str.ptr);
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_ECONVFMTCHR);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (rtx->gbl.convfmt.ptr) hawk_rtx_freemem(rtx, rtx->gbl.convfmt.ptr);
|
|
rtx->gbl.convfmt.ptr = str.ptr;
|
|
rtx->gbl.convfmt.len = str.len;
|
|
break;
|
|
}
|
|
|
|
case HAWK_GBL_FNR:
|
|
{
|
|
int n;
|
|
hawk_int_t lv;
|
|
|
|
n = hawk_rtx_valtoint_inline(rtx, val, &lv);
|
|
if (HAWK_UNLIKELY(n <= -1)) return -1;
|
|
|
|
rtx->gbl.fnr = lv;
|
|
break;
|
|
}
|
|
|
|
case HAWK_GBL_FS:
|
|
{
|
|
hawk_ooch_t* fs_ptr;
|
|
hawk_oow_t fs_len;
|
|
|
|
/* due to the expression evaluation rule, the
|
|
* regular expression can not be an assigned value */
|
|
HAWK_ASSERT(vtype != HAWK_VAL_REX);
|
|
|
|
fs_ptr = hawk_rtx_getvaloocstr(rtx, val, &fs_len);
|
|
if (HAWK_UNLIKELY(!fs_ptr)) return -1;
|
|
|
|
if (fs_len > 1 && !(fs_len == 5 && fs_ptr[0] == '?'))
|
|
{
|
|
/* it's a regular expression if FS contains multiple characters.
|
|
* however, it's not a regular expression if it's 5 character
|
|
* string beginning with a question mark. */
|
|
hawk_tre_t* rex, * irex;
|
|
|
|
if (hawk_rtx_buildrex(rtx, fs_ptr, fs_len, &rex, &irex) <= -1)
|
|
{
|
|
hawk_rtx_freevaloocstr(rtx, val, fs_ptr);
|
|
return -1;
|
|
}
|
|
|
|
if (rtx->gbl.fs[0]) hawk_rtx_freerex(rtx, rtx->gbl.fs[0], rtx->gbl.fs[1]);
|
|
|
|
rtx->gbl.fs[0] = rex;
|
|
rtx->gbl.fs[1] = irex;
|
|
}
|
|
|
|
hawk_rtx_freevaloocstr(rtx, val, fs_ptr);
|
|
break;
|
|
}
|
|
|
|
case HAWK_GBL_IGNORECASE:
|
|
{
|
|
hawk_int_t l;
|
|
hawk_flt_t r;
|
|
int vt;
|
|
|
|
vt = hawk_rtx_valtonum(rtx, val, &l, &r);
|
|
if (vt <= -1) return -1;
|
|
|
|
if (vt == 0)
|
|
rtx->gbl.ignorecase = ((l > 0)? 1: (l < 0)? -1: 0);
|
|
else
|
|
rtx->gbl.ignorecase = ((r > 0.0)? 1: (r < 0.0)? -1: 0);
|
|
break;
|
|
}
|
|
|
|
case HAWK_GBL_NF:
|
|
{
|
|
int n;
|
|
hawk_int_t lv;
|
|
|
|
n = hawk_rtx_valtoint_inline(rtx, val, &lv);
|
|
if (n <= -1) return -1;
|
|
|
|
if (lv < 0)
|
|
{
|
|
hawk_rtx_seterrfmt(rtx, HAWK_NULL, HAWK_EINVAL, HAWK_T("negative value into NF"));
|
|
return -1;
|
|
}
|
|
|
|
if (lv < (hawk_int_t)rtx->inrec.nflds || (assign && lv == (hawk_int_t)rtx->inrec.nflds))
|
|
{
|
|
/* when NF is assigned a value, it should rebuild $X values.
|
|
* even when there is no change in the value like NF = NF,
|
|
* it has to rebuil $X values with the current OFS value.
|
|
* { OFS=":"; NF=NF; print $0; }
|
|
* the NF=value assignment is indicated by a non-zero value in the 'assign' variable.
|
|
* 'assign' is 0 if this function is called from a different context such as
|
|
* explicit call to hawk_rtx_setgbl().
|
|
*/
|
|
|
|
if (hawk_rtx_truncrec(rtx, (hawk_oow_t)lv) <= -1)
|
|
{
|
|
/* adjust the error line */
|
|
/*if (var) ADJERR_LOC(rtx, &var->loc);*/
|
|
return -1;
|
|
}
|
|
}
|
|
else if (lv > (hawk_int_t)rtx->inrec.nflds)
|
|
{
|
|
hawk_oocs_t cs;
|
|
cs.ptr = HAWK_T("");
|
|
cs.len = 0;
|
|
if (hawk_rtx_setrec(rtx, lv, &cs, 0) <= -1) return -1;
|
|
}
|
|
|
|
/* for all other globals, it returns before this switch/case block is reached
|
|
* if the same value is assigned. but NF change requires extra action to take
|
|
* as coded before this switch/case block. */
|
|
if (old == val) return 0;
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
case HAWK_GBL_NR:
|
|
{
|
|
int n;
|
|
hawk_int_t lv;
|
|
|
|
n = hawk_rtx_valtoint_inline(rtx, val, &lv);
|
|
if (n <= -1) return -1;
|
|
|
|
rtx->gbl.nr = lv;
|
|
break;
|
|
}
|
|
|
|
case HAWK_GBL_NUMSTRDETECT:
|
|
{
|
|
hawk_int_t l;
|
|
hawk_flt_t r;
|
|
int vt;
|
|
|
|
vt = hawk_rtx_valtonum(rtx, val, &l, &r);
|
|
if (vt <= -1) return -1;
|
|
|
|
if (vt == 0)
|
|
rtx->gbl.numstrdetect = ((l > 0)? 1: (l < 0)? -1: 0);
|
|
else
|
|
rtx->gbl.numstrdetect = ((r > 0.0)? 1: (r < 0.0)? -1: 0);
|
|
break;
|
|
}
|
|
|
|
case HAWK_GBL_OFMT:
|
|
{
|
|
hawk_oow_t i;
|
|
hawk_oocs_t str;
|
|
|
|
str.ptr = hawk_rtx_valtooocstrdup(rtx, val, &str.len);
|
|
if (HAWK_UNLIKELY(!str.ptr)) return -1;
|
|
|
|
for (i = 0; i < str.len; i++)
|
|
{
|
|
if (str.ptr[i] == '\0')
|
|
{
|
|
/* '\0' is included in the value */
|
|
hawk_rtx_freemem(rtx, str.ptr);
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_ECONVFMTCHR);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (rtx->gbl.ofmt.ptr) hawk_rtx_freemem(rtx, rtx->gbl.ofmt.ptr);
|
|
rtx->gbl.ofmt.ptr = str.ptr;
|
|
rtx->gbl.ofmt.len = str.len;
|
|
}
|
|
|
|
case HAWK_GBL_OFS:
|
|
{
|
|
hawk_oocs_t str;
|
|
|
|
str.ptr = hawk_rtx_valtooocstrdup(rtx, val, &str.len);
|
|
if (HAWK_UNLIKELY(!str.ptr)) return -1;
|
|
|
|
if (rtx->gbl.ofs.ptr) hawk_rtx_freemem(rtx, rtx->gbl.ofs.ptr);
|
|
rtx->gbl.ofs.ptr = str.ptr;
|
|
rtx->gbl.ofs.len = str.len;
|
|
|
|
break;
|
|
}
|
|
|
|
case HAWK_GBL_ORS:
|
|
{
|
|
hawk_oocs_t str;
|
|
|
|
str.ptr = hawk_rtx_valtooocstrdup(rtx, val, &str.len);
|
|
if (HAWK_UNLIKELY(!str.ptr)) return -1;
|
|
|
|
if (rtx->gbl.ors.ptr) hawk_rtx_freemem(rtx, rtx->gbl.ors.ptr);
|
|
rtx->gbl.ors.ptr = str.ptr;
|
|
rtx->gbl.ors.len = str.len;
|
|
|
|
break;
|
|
}
|
|
|
|
case HAWK_GBL_PIPECLOEXEC:
|
|
{
|
|
hawk_int_t l;
|
|
hawk_flt_t r;
|
|
int vt;
|
|
|
|
vt = hawk_rtx_valtonum(rtx, val, &l, &r);
|
|
if (vt <= -1) return -1;
|
|
|
|
if (vt == 0)
|
|
rtx->gbl.pipecloexec = ((l > 0)? 1: (l < 0)? -1: 0);
|
|
else
|
|
rtx->gbl.pipecloexec = ((r > 0.0)? 1: (r < 0.0)? -1: 0);
|
|
break;
|
|
}
|
|
|
|
case HAWK_GBL_RS:
|
|
{
|
|
hawk_oocs_t rss;
|
|
|
|
/* due to the expression evaluation rule, the
|
|
* regular expression can not be an assigned
|
|
* value */
|
|
HAWK_ASSERT(vtype != HAWK_VAL_REX);
|
|
|
|
rss.ptr = hawk_rtx_getvaloocstr(rtx, val, &rss.len);
|
|
if (!rss.ptr) return -1;
|
|
|
|
if (rtx->gbl.rs[0])
|
|
{
|
|
hawk_rtx_freerex(rtx, rtx->gbl.rs[0], rtx->gbl.rs[1]);
|
|
rtx->gbl.rs[0] = HAWK_NULL;
|
|
rtx->gbl.rs[1] = HAWK_NULL;
|
|
}
|
|
|
|
if (rss.len > 1)
|
|
{
|
|
hawk_tre_t* rex, * irex;
|
|
|
|
/* compile the regular expression */
|
|
if (hawk_rtx_buildrex(rtx, rss.ptr, rss.len, &rex, &irex) <= -1)
|
|
{
|
|
hawk_rtx_freevaloocstr(rtx, val, rss.ptr);
|
|
return -1;
|
|
}
|
|
|
|
rtx->gbl.rs[0] = rex;
|
|
rtx->gbl.rs[1] = irex;
|
|
}
|
|
|
|
hawk_rtx_freevaloocstr(rtx, val, rss.ptr);
|
|
break;
|
|
}
|
|
|
|
case HAWK_GBL_STRIPRECSPC:
|
|
{
|
|
hawk_int_t l;
|
|
hawk_flt_t r;
|
|
int vt;
|
|
|
|
vt = hawk_rtx_valtonum(rtx, val, &l, &r);
|
|
if (vt <= -1) return -1;
|
|
|
|
if (vt == 0)
|
|
rtx->gbl.striprecspc = ((l > 0)? 1: (l < 0)? -1: 0);
|
|
else
|
|
rtx->gbl.striprecspc = ((r > 0.0)? 1: (r < 0.0)? -1: 0);
|
|
break;
|
|
}
|
|
|
|
case HAWK_GBL_STRIPSTRSPC:
|
|
{
|
|
hawk_int_t l;
|
|
hawk_flt_t r;
|
|
int vt;
|
|
|
|
vt = hawk_rtx_valtonum(rtx, val, &l, &r);
|
|
if (vt <= -1) return -1;
|
|
|
|
if (vt == 0)
|
|
rtx->gbl.stripstrspc = ((l > 0)? 1: (l < 0)? -1: 0);
|
|
else
|
|
rtx->gbl.stripstrspc = ((r > 0.0)? 1: (r < 0.0)? -1: 0);
|
|
break;
|
|
}
|
|
|
|
case HAWK_GBL_SUBSEP:
|
|
{
|
|
hawk_oocs_t str;
|
|
|
|
str.ptr = hawk_rtx_valtooocstrdup(rtx, val, &str.len);
|
|
if (HAWK_UNLIKELY(!str.ptr)) return -1;
|
|
|
|
if (rtx->gbl.subsep.ptr) hawk_rtx_freemem(rtx, rtx->gbl.subsep.ptr);
|
|
rtx->gbl.subsep.ptr = str.ptr;
|
|
rtx->gbl.subsep.len = str.len;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, old);
|
|
HAWK_RTX_STACK_GBL(rtx,idx) = val;
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
|
|
if (rtx->ecb_gblset_count > 0)
|
|
{
|
|
for (ecb = rtx->ecb; ecb != (hawk_rtx_ecb_t*)rtx; ecb = ecb_next)
|
|
{
|
|
ecb_next = ecb->next_;
|
|
if (ecb->gblset) ecb->gblset(rtx, idx, val, ecb->ctx);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
HAWK_INLINE void hawk_rtx_setretval (hawk_rtx_t* rtx, hawk_val_t* val)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_RETVAL(rtx));
|
|
HAWK_RTX_STACK_RETVAL(rtx) = val;
|
|
/* should use the same trick as run_return */
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
}
|
|
|
|
HAWK_INLINE int hawk_rtx_setgbl (hawk_rtx_t* rtx, int id, hawk_val_t* val)
|
|
{
|
|
HAWK_ASSERT(id >= 0 && id < (int)HAWK_ARR_SIZE(rtx->hawk->parse.gbls));
|
|
return set_global(rtx, id, HAWK_NULL, val, 0);
|
|
}
|
|
|
|
static HAWK_INLINE hawk_oow_t find_named_slot_by_oocs (hawk_rtx_t* rtx, const hawk_oocs_t* name)
|
|
{
|
|
hawk_htb_pair_t* pair;
|
|
hawk_oow_t idx;
|
|
|
|
pair = hawk_htb_search(rtx->named, name->ptr, name->len);
|
|
if (!pair)
|
|
{
|
|
pair = hawk_htb_search(rtx->hawk->parse.named, name->ptr, name->len);
|
|
if (!pair) return (hawk_oow_t)-1;
|
|
}
|
|
|
|
idx = (hawk_oow_t)HAWK_HTB_VPTR(pair);
|
|
return idx;
|
|
}
|
|
|
|
static HAWK_INLINE hawk_oow_t find_named_slot_by_oocstr (hawk_rtx_t* rtx, const hawk_ooch_t* name)
|
|
{
|
|
hawk_oocs_t tmp;
|
|
tmp.ptr = (hawk_ooch_t*)name;
|
|
tmp.len = hawk_count_oocstr(name);
|
|
return find_named_slot_by_oocs(rtx, &tmp);
|
|
}
|
|
|
|
static int ensure_named_slots (hawk_rtx_t* rtx, hawk_oow_t newcapa)
|
|
{
|
|
if (newcapa > rtx->named_slot_capa)
|
|
{
|
|
hawk_val_t** tmp;
|
|
hawk_oow_t i;
|
|
|
|
newcapa = HAWK_ALIGN_POW2(newcapa, 32);
|
|
tmp = hawk_rtx_reallocmem(rtx, rtx->named_slots, newcapa * HAWK_SIZEOF(*tmp));
|
|
if (HAWK_UNLIKELY(!tmp)) return -1;
|
|
|
|
for (i = rtx->named_slot_capa; i < newcapa; i++) tmp[i] = hawk_val_nil;
|
|
rtx->named_slot_capa = newcapa;
|
|
rtx->named_slots = tmp;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int hawk_rtx_setgbltostrbyname (hawk_rtx_t* rtx, const hawk_ooch_t* name, const hawk_ooch_t* val)
|
|
{
|
|
int id, n;
|
|
hawk_val_t* v;
|
|
|
|
/* find a a gloval variable by name and set its value to a string.
|
|
* this function is mainly to support -v on the command line
|
|
* or the arguments in the "x=y" format.
|
|
* hawk -v qqq=20 'BEGIN { ARGV[1] = "a=20"; } { print a, $1; }' dummy /etc/hosts */
|
|
HAWK_ASSERT(hawk_isvalidident(hawk_rtx_gethawk(rtx), name));
|
|
|
|
id = hawk_findgblwithoocstr(hawk_rtx_gethawk(rtx), name, 1);
|
|
v = hawk_rtx_makestrvalwithoocstr(rtx, val);
|
|
if (HAWK_UNLIKELY(!v)) return -1;
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
if (id <= -1)
|
|
{
|
|
hawk_oow_t named_id;
|
|
|
|
/* not found. it's not a builtin/declared global variable */
|
|
named_id = find_named_slot_by_oocstr(rtx, name);
|
|
if (named_id == (hawk_oow_t)-1)
|
|
{
|
|
/* not one of the existing named variables either */
|
|
named_id = rtx->named_slot_count;
|
|
if (ensure_named_slots(rtx, named_id + 1) <= -1 ||
|
|
hawk_htb_upsert(rtx->named, (void*)name, hawk_count_oocstr(name), (void*)named_id, 0) == HAWK_NULL)
|
|
{
|
|
n = -1;
|
|
goto done;
|
|
}
|
|
rtx->named_slot_count++;
|
|
}
|
|
|
|
n = (hawk_rtx_setnamedval(rtx, named_id, v) <= -1)? -1: 0;
|
|
}
|
|
else
|
|
{
|
|
n = set_global(rtx, id, HAWK_NULL, v, 0);
|
|
}
|
|
done:
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
|
|
return n;
|
|
}
|
|
|
|
int hawk_rtx_setscriptnamewithuchars (hawk_rtx_t* rtx, const hawk_uch_t* name, hawk_oow_t len)
|
|
{
|
|
hawk_val_t* tmp;
|
|
int n;
|
|
|
|
tmp = hawk_rtx_makestrvalwithuchars(rtx, name, len);
|
|
if (tmp == HAWK_NULL) return -1;
|
|
|
|
hawk_rtx_refupval_inline(rtx, tmp);
|
|
n = hawk_rtx_setgbl(rtx, HAWK_GBL_SCRIPTNAME, tmp);
|
|
hawk_rtx_refdownval_inline(rtx, tmp);
|
|
|
|
return n;
|
|
}
|
|
|
|
int hawk_rtx_setscriptnamewithbchars (hawk_rtx_t* rtx, const hawk_bch_t* name, hawk_oow_t len)
|
|
{
|
|
hawk_val_t* tmp;
|
|
int n;
|
|
|
|
tmp = hawk_rtx_makestrvalwithbchars(rtx, name, len);
|
|
if (tmp == HAWK_NULL) return -1;
|
|
|
|
hawk_rtx_refupval_inline(rtx, tmp);
|
|
n = hawk_rtx_setgbl(rtx, HAWK_GBL_SCRIPTNAME, tmp);
|
|
hawk_rtx_refdownval_inline(rtx, tmp);
|
|
|
|
return n;
|
|
}
|
|
|
|
int hawk_rtx_setfilenamewithuchars (hawk_rtx_t* rtx, const hawk_uch_t* name, hawk_oow_t len)
|
|
{
|
|
hawk_val_t* tmp;
|
|
int n;
|
|
|
|
tmp = hawk_rtx_makestrvalwithuchars(rtx, name, len);
|
|
if (HAWK_UNLIKELY(!tmp)) return -1;
|
|
|
|
hawk_rtx_refupval_inline(rtx, tmp);
|
|
n = hawk_rtx_setgbl(rtx, HAWK_GBL_FILENAME, tmp);
|
|
hawk_rtx_refdownval_inline(rtx, tmp);
|
|
|
|
return n;
|
|
}
|
|
|
|
int hawk_rtx_setfilenamewithbchars (hawk_rtx_t* rtx, const hawk_bch_t* name, hawk_oow_t len)
|
|
{
|
|
hawk_val_t* tmp;
|
|
int n;
|
|
|
|
tmp = hawk_rtx_makestrvalwithbchars(rtx, name, len);
|
|
if (HAWK_UNLIKELY(!tmp)) return -1;
|
|
|
|
hawk_rtx_refupval_inline(rtx, tmp);
|
|
n = hawk_rtx_setgbl(rtx, HAWK_GBL_FILENAME, tmp);
|
|
hawk_rtx_refdownval_inline(rtx, tmp);
|
|
|
|
return n;
|
|
}
|
|
|
|
|
|
int hawk_rtx_setofilenamewithuchars (hawk_rtx_t* rtx, const hawk_uch_t* name, hawk_oow_t len)
|
|
{
|
|
hawk_val_t* tmp;
|
|
int n;
|
|
|
|
if (rtx->hawk->opt.trait & HAWK_NEXTOFILE)
|
|
{
|
|
tmp = hawk_rtx_makestrvalwithuchars(rtx, name, len);
|
|
if (HAWK_UNLIKELY(!tmp)) return -1;
|
|
|
|
hawk_rtx_refupval_inline(rtx, tmp);
|
|
n = hawk_rtx_setgbl(rtx, HAWK_GBL_OFILENAME, tmp);
|
|
hawk_rtx_refdownval_inline(rtx, tmp);
|
|
}
|
|
else n = 0;
|
|
|
|
return n;
|
|
}
|
|
|
|
int hawk_rtx_setofilenamewithbchars (hawk_rtx_t* rtx, const hawk_bch_t* name, hawk_oow_t len)
|
|
{
|
|
hawk_val_t* tmp;
|
|
int n;
|
|
|
|
if (rtx->hawk->opt.trait & HAWK_NEXTOFILE)
|
|
{
|
|
tmp = hawk_rtx_makestrvalwithbchars(rtx, name, len);
|
|
if (HAWK_UNLIKELY(!tmp)) return -1;
|
|
|
|
hawk_rtx_refupval_inline(rtx, tmp);
|
|
n = hawk_rtx_setgbl(rtx, HAWK_GBL_OFILENAME, tmp);
|
|
hawk_rtx_refdownval_inline(rtx, tmp);
|
|
}
|
|
else n = 0;
|
|
|
|
return n;
|
|
}
|
|
|
|
int hawk_rtx_setnamedval (hawk_rtx_t* rtx, hawk_oow_t id, hawk_val_t* val)
|
|
{
|
|
hawk_val_t* oldval;
|
|
|
|
if (HAWK_UNLIKELY(id >= rtx->named_slot_count))
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
oldval = HAWK_RTX_STACK_NAMED(rtx, id);
|
|
if (oldval != val)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, oldval);
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
HAWK_RTX_STACK_NAMED(rtx, id) = val;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static HAWK_INLINE hawk_val_t* set_nv_itr_pair (hawk_rtx_t* rtx, hawk_rtx_nv_itr_t* itr, hawk_htb_pair_t* pair, int source)
|
|
{
|
|
hawk_oow_t slot_id;
|
|
|
|
slot_id = (hawk_oow_t)HAWK_HTB_VPTR(pair);
|
|
if (HAWK_UNLIKELY(slot_id >= rtx->named_slot_count))
|
|
{
|
|
itr->name.ptr = HAWK_NULL;
|
|
itr->name.len = 0;
|
|
itr->slot_id = (hawk_oow_t)-1;
|
|
itr->source = 0;
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EINTERN);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
itr->name.ptr = (hawk_ooch_t*)HAWK_HTB_KPTR(pair);
|
|
itr->name.len = HAWK_HTB_KLEN(pair);
|
|
itr->slot_id = slot_id;
|
|
itr->source = source;
|
|
return HAWK_RTX_STACK_NAMED(rtx, slot_id);
|
|
}
|
|
|
|
void hawk_init_rtx_nv_itr (hawk_rtx_nv_itr_t* itr)
|
|
{
|
|
hawk_init_htb_itr(&itr->parse_itr);
|
|
hawk_init_htb_itr(&itr->runtime_itr);
|
|
itr->name.ptr = HAWK_NULL;
|
|
itr->name.len = 0;
|
|
itr->slot_id = (hawk_oow_t)-1;
|
|
itr->source = 0;
|
|
}
|
|
|
|
hawk_val_t* hawk_rtx_getfirstnv (hawk_rtx_t* rtx, hawk_rtx_nv_itr_t* itr)
|
|
{
|
|
hawk_htb_pair_t* pair;
|
|
|
|
hawk_init_rtx_nv_itr(itr);
|
|
|
|
pair = hawk_htb_getfirstpair(rtx->hawk->parse.named, &itr->parse_itr);
|
|
if (pair) return set_nv_itr_pair(rtx, itr, pair, 1);
|
|
|
|
pair = hawk_htb_getfirstpair(rtx->named, &itr->runtime_itr);
|
|
if (pair) return set_nv_itr_pair(rtx, itr, pair, 2);
|
|
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
hawk_val_t* hawk_rtx_getnextnv (hawk_rtx_t* rtx, hawk_rtx_nv_itr_t* itr)
|
|
{
|
|
hawk_htb_pair_t* pair;
|
|
|
|
switch (itr->source)
|
|
{
|
|
case 1:
|
|
pair = hawk_htb_getnextpair(rtx->hawk->parse.named, &itr->parse_itr);
|
|
if (pair) return set_nv_itr_pair(rtx, itr, pair, 1);
|
|
|
|
pair = hawk_htb_getfirstpair(rtx->named, &itr->runtime_itr);
|
|
if (pair) return set_nv_itr_pair(rtx, itr, pair, 2);
|
|
break;
|
|
|
|
case 2:
|
|
pair = hawk_htb_getnextpair(rtx->named, &itr->runtime_itr);
|
|
if (pair) return set_nv_itr_pair(rtx, itr, pair, 2);
|
|
break;
|
|
}
|
|
|
|
itr->name.ptr = HAWK_NULL;
|
|
itr->name.len = 0;
|
|
itr->slot_id = (hawk_oow_t)-1;
|
|
itr->source = 0;
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
hawk_val_t* hawk_rtx_findnvbyucstr (hawk_rtx_t* rtx, const hawk_uch_t* name)
|
|
{
|
|
hawk_oocs_t tmp;
|
|
hawk_oow_t slot_id;
|
|
|
|
#if defined(HAWK_OOCH_IS_UCH)
|
|
tmp.ptr = (hawk_ooch_t*)name;
|
|
tmp.len = hawk_count_ucstr(name);
|
|
slot_id = find_named_slot_by_oocs(rtx, &tmp);
|
|
#else
|
|
hawk_bch_t* bcs;
|
|
|
|
bcs = hawk_rtx_duputobcstr(rtx, name, &tmp.len);
|
|
if (HAWK_UNLIKELY(!bcs)) return HAWK_NULL;
|
|
tmp.ptr = (hawk_ooch_t*)bcs;
|
|
slot_id = find_named_slot_by_oocs(rtx, &tmp);
|
|
hawk_rtx_freemem(rtx, bcs);
|
|
#endif
|
|
|
|
return (slot_id == (hawk_oow_t)-1)? HAWK_NULL: HAWK_RTX_STACK_NAMED(rtx, slot_id);
|
|
}
|
|
|
|
hawk_val_t* hawk_rtx_findnvbybcstr (hawk_rtx_t* rtx, const hawk_bch_t* name)
|
|
{
|
|
hawk_oocs_t tmp;
|
|
hawk_oow_t slot_id;
|
|
|
|
#if defined(HAWK_OOCH_IS_UCH)
|
|
hawk_uch_t* ucs;
|
|
|
|
ucs = hawk_rtx_dupbtoucstr(rtx, name, &tmp.len, 0);
|
|
if (HAWK_UNLIKELY(!ucs)) return HAWK_NULL;
|
|
tmp.ptr = (hawk_ooch_t*)ucs;
|
|
slot_id = find_named_slot_by_oocs(rtx, &tmp);
|
|
hawk_rtx_freemem(rtx, ucs);
|
|
#else
|
|
tmp.ptr = (hawk_ooch_t*)name;
|
|
tmp.len = hawk_count_bcstr(name);
|
|
slot_id = find_named_slot_by_oocs(rtx, &tmp);
|
|
#endif
|
|
|
|
return (slot_id == (hawk_oow_t)-1)? HAWK_NULL: HAWK_RTX_STACK_NAMED(rtx, slot_id);
|
|
}
|
|
|
|
|
|
struct module_init_ctx_t
|
|
{
|
|
hawk_oow_t count;
|
|
hawk_rtx_t* rtx;
|
|
};
|
|
|
|
struct module_fini_ctx_t
|
|
{
|
|
hawk_oow_t limit;
|
|
hawk_oow_t count;
|
|
hawk_rtx_t* rtx;
|
|
};
|
|
|
|
static hawk_rbt_walk_t init_module (hawk_rbt_t* rbt, hawk_rbt_pair_t* pair, void* ctx)
|
|
{
|
|
hawk_mod_data_t* md;
|
|
struct module_init_ctx_t* mic;
|
|
|
|
mic = (struct module_init_ctx_t*)ctx;
|
|
|
|
md = (hawk_mod_data_t*)HAWK_RBT_VPTR(pair);
|
|
if (md->mod.init && md->mod.init(&md->mod, mic->rtx) <= -1)
|
|
return HAWK_RBT_WALK_STOP;
|
|
|
|
mic->count++;
|
|
return HAWK_RBT_WALK_FORWARD;
|
|
}
|
|
|
|
static hawk_rbt_walk_t fini_module (hawk_rbt_t* rbt, hawk_rbt_pair_t* pair, void* ctx)
|
|
{
|
|
hawk_mod_data_t* md;
|
|
struct module_fini_ctx_t* mfc;
|
|
|
|
mfc = (struct module_fini_ctx_t*)ctx;
|
|
|
|
if (mfc->limit > 0 && mfc->count >= mfc->limit)
|
|
return HAWK_RBT_WALK_STOP;
|
|
|
|
md = (hawk_mod_data_t*)HAWK_RBT_VPTR(pair);
|
|
if (md->mod.fini) md->mod.fini(&md->mod, mfc->rtx);
|
|
|
|
mfc->count++;
|
|
return HAWK_RBT_WALK_FORWARD;
|
|
}
|
|
|
|
hawk_rtx_t* hawk_rtx_open (hawk_t* hawk, hawk_oow_t xtnsize, hawk_rio_cbs_t* rio)
|
|
{
|
|
hawk_rtx_t* rtx;
|
|
struct module_init_ctx_t mic;
|
|
hawk_oow_t i;
|
|
|
|
/* clear the hawk error code */
|
|
hawk_seterrnum(hawk, HAWK_NULL, HAWK_ENOERR);
|
|
|
|
/* check if the code has ever been parsed */
|
|
if (hawk->tree.ngbls == 0 &&
|
|
hawk->tree.init == HAWK_NULL &&
|
|
hawk->tree.begin == HAWK_NULL &&
|
|
hawk->tree.end == HAWK_NULL &&
|
|
hawk->tree.chain_size == 0 &&
|
|
hawk_htb_getsize(hawk->tree.funs) == 0)
|
|
{
|
|
hawk_seterrnum(hawk, HAWK_NULL, HAWK_EPERM);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
/* allocate the storage for the rtx object */
|
|
rtx = (hawk_rtx_t*)hawk_allocmem(hawk, HAWK_SIZEOF(hawk_rtx_t) + xtnsize);
|
|
if (HAWK_UNLIKELY(!rtx))
|
|
{
|
|
/* if it fails, the failure is reported thru the hawk object */
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
/* initialize the rtx object */
|
|
HAWK_MEMSET(rtx, 0, HAWK_SIZEOF(hawk_rtx_t) + xtnsize);
|
|
rtx->instsize_ = HAWK_SIZEOF(hawk_rtx_t);
|
|
if (HAWK_UNLIKELY(init_rtx(rtx, hawk, rio) <= -1))
|
|
{
|
|
/* because the error information is in the gem part,
|
|
* it should be ok to copy over rtx error to hawk even if
|
|
* rtx initialization fails. */
|
|
hawk_rtx_errortohawk(rtx, hawk);
|
|
hawk_freemem(hawk, rtx);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
rtx->ecb = (hawk_rtx_ecb_t*)rtx; /* use this as a special sentinel node */
|
|
rtx->ecb_stmt_count = 0;
|
|
rtx->ecb_gblset_count = 0;
|
|
|
|
/* initialize signal handling fields */
|
|
rtx->sig_handling = 0; /* indicate call_signal_handlers() is not in progress */
|
|
rtx->sig_pending_any = 0;
|
|
#if defined(HAWK_ENABLE_ATOMIC_SIG)
|
|
for (i = 0; i < HAWK_SIG_WORD_COUNT; i++) rtx->sig_pending[i] = 0;
|
|
#else
|
|
if (HAWK_UNLIKELY(hawk_mtx_init(&rtx->sig_mtx, hawk_rtx_getgem(rtx), 0) <= -1))
|
|
{
|
|
hawk_rtx_errortohawk(rtx, hawk);
|
|
fini_rtx(rtx, 0);
|
|
hawk_freemem(hawk, rtx);
|
|
return HAWK_NULL;
|
|
}
|
|
rtx->sig_mtx_inited = 1;
|
|
rtx->sig_raise[0].count = 0;
|
|
for (i = 0; i < HAWK_COUNTOF(rtx->sig_raise[0].pos); i++) rtx->sig_raise[0].pos[i] = -1;
|
|
#endif
|
|
|
|
/* global variables */
|
|
if (HAWK_UNLIKELY(init_globals(rtx) <= -1))
|
|
{
|
|
hawk_rtx_errortohawk(rtx, hawk);
|
|
fini_rtx(rtx, 0);
|
|
hawk_freemem(hawk, rtx);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
/* call init() on all the modules loaded at compile-time for this rtx object */
|
|
mic.count = 0;
|
|
mic.rtx = rtx;
|
|
hawk_mtx_lock(rtx->hawk->modmtx, HAWK_NULL);
|
|
hawk_rbt_walk(rtx->hawk->modtab, init_module, &mic);
|
|
if (mic.count != HAWK_RBT_SIZE(rtx->hawk->modtab))
|
|
{
|
|
if (mic.count > 0)
|
|
{
|
|
struct module_fini_ctx_t mfc;
|
|
|
|
mfc.limit = mic.count;
|
|
mfc.count = 0;
|
|
mfc.rtx = rtx;
|
|
hawk_rbt_walk(rtx->hawk->modtab, fini_module, &mfc);
|
|
|
|
/* call unload on the runtime loaded modules */
|
|
hawk_rbt_walk(rtx->modtab, hawk_modtab_unload_module, rtx->hawk);
|
|
}
|
|
|
|
hawk_mtx_unlock(rtx->hawk->modmtx);
|
|
fini_rtx(rtx, 1);
|
|
hawk_freemem(hawk, rtx);
|
|
return HAWK_NULL;
|
|
}
|
|
hawk_mtx_unlock(rtx->hawk->modmtx);
|
|
|
|
/* chain the ctos slots */
|
|
rtx->ctos.fi = HAWK_COUNTOF(rtx->ctos.b) - 1;
|
|
for (i = HAWK_COUNTOF(rtx->ctos.b); i > 1;)
|
|
{
|
|
--i;
|
|
rtx->ctos.b[i].c[0] = i - 1;
|
|
}
|
|
/* rtx->ctos.b[0] is not used as the free index of 0 indicates the end of the list.
|
|
* see hawk_rtx_getvaloocstr(). */
|
|
|
|
/* chain the bctos slots */
|
|
rtx->bctos.fi = HAWK_COUNTOF(rtx->bctos.b) - 1;
|
|
for (i = HAWK_COUNTOF(rtx->bctos.b); i > 1;)
|
|
{
|
|
--i;
|
|
rtx->bctos.b[i].c[0] = i - 1;
|
|
}
|
|
/* rtx->bctos.b[0] is not used as the free index of 0 indicates the end of the list.
|
|
* see hawk_rtx_getvaloocstr(). */
|
|
|
|
return rtx;
|
|
}
|
|
|
|
void hawk_rtx_close (hawk_rtx_t* rtx)
|
|
{
|
|
hawk_rtx_ecb_t* ecb, * ecb_next;
|
|
struct module_fini_ctx_t mfc;
|
|
|
|
/* call fini() over the runtime loaded/inited modules */
|
|
mfc.limit = 0;
|
|
mfc.count = 0;
|
|
mfc.rtx = rtx;
|
|
hawk_rbt_walk(rtx->modtab, fini_module, &mfc); /* don't care how many succeeded and failed */
|
|
|
|
/* call unload on the runtime loaded modules */
|
|
hawk_rbt_walk(rtx->modtab, hawk_modtab_unload_module, rtx->hawk);
|
|
|
|
/* call fini() over the compiled-loaded and runtime-inited modules */
|
|
mfc.limit = 0;
|
|
mfc.count = 0;
|
|
mfc.rtx = rtx;
|
|
hawk_mtx_lock(rtx->hawk->modmtx, HAWK_NULL);
|
|
hawk_rbt_walk(rtx->hawk->modtab, fini_module, &mfc);
|
|
hawk_mtx_unlock(rtx->hawk->modmtx); /* don't care how many succeeded and failed*/
|
|
|
|
for (ecb = rtx->ecb; ecb != (hawk_rtx_ecb_t*)rtx; ecb = ecb_next)
|
|
{
|
|
ecb_next = ecb->next_;
|
|
if (ecb->close) ecb->close(rtx, ecb->ctx);
|
|
}
|
|
|
|
do { ecb = hawk_rtx_popecb(rtx); } while (ecb);
|
|
HAWK_ASSERT(rtx->ecb == (hawk_rtx_ecb_t*)rtx);
|
|
HAWK_ASSERT(rtx->ecb_stmt_count == 0);
|
|
HAWK_ASSERT(rtx->ecb_gblset_count == 0);
|
|
|
|
/* NOTE:
|
|
* the close callbacks are called before data in rtx
|
|
* is destroyed. if the destruction count on any data
|
|
* destroyed by the close callback, something bad
|
|
* will happen.
|
|
*/
|
|
fini_rtx(rtx, 1);
|
|
|
|
hawk_freemem(hawk_rtx_gethawk(rtx), rtx);
|
|
}
|
|
|
|
void hawk_rtx_halt (hawk_rtx_t* rtx)
|
|
{
|
|
rtx->exit_level = EXIT_ABORT;
|
|
}
|
|
|
|
int hawk_rtx_ishalt (hawk_rtx_t* rtx)
|
|
{
|
|
return (rtx->exit_level == EXIT_ABORT || rtx->hawk->haltall);
|
|
}
|
|
|
|
void hawk_rtx_getrio (hawk_rtx_t* rtx, hawk_rio_cbs_t* rio)
|
|
{
|
|
rio->pipe = rtx->rio.handler[HAWK_RIO_PIPE];
|
|
rio->file = rtx->rio.handler[HAWK_RIO_FILE];
|
|
rio->console = rtx->rio.handler[HAWK_RIO_CONSOLE];
|
|
rio->env_mk = rtx->rio.env_mk;
|
|
}
|
|
|
|
void hawk_rtx_setrio (hawk_rtx_t* rtx, const hawk_rio_cbs_t* rio)
|
|
{
|
|
rtx->rio.handler[HAWK_RIO_PIPE] = rio->pipe;
|
|
rtx->rio.handler[HAWK_RIO_FILE] = rio->file;
|
|
rtx->rio.handler[HAWK_RIO_CONSOLE] = rio->console;
|
|
rtx->rio.env_mk = rio->env_mk;
|
|
}
|
|
|
|
void hawk_rtx_killecb (hawk_rtx_t* rtx, hawk_rtx_ecb_t* ecb)
|
|
{
|
|
/* TODO: make it faster with doubly-linked list? */
|
|
hawk_rtx_ecb_t* prev, * cur;
|
|
for (cur = rtx->ecb, prev = HAWK_NULL; cur != (hawk_rtx_ecb_t*)rtx; prev = cur, cur = cur->next_)
|
|
{
|
|
if (cur == ecb)
|
|
{
|
|
if (ecb->stmt) rtx->ecb_stmt_count--;
|
|
if (ecb->gblset) rtx->ecb_gblset_count--;
|
|
if (prev) prev->next_ = cur->next_;
|
|
else rtx->ecb = cur->next_;
|
|
cur->next_ = HAWK_NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
hawk_rtx_ecb_t* hawk_rtx_popecb (hawk_rtx_t* rtx)
|
|
{
|
|
hawk_rtx_ecb_t* top = rtx->ecb;
|
|
if (top == (hawk_rtx_ecb_t*)rtx) return HAWK_NULL;
|
|
if (top->stmt) rtx->ecb_stmt_count--;
|
|
if (top->gblset) rtx->ecb_gblset_count--;
|
|
rtx->ecb = top->next_;
|
|
top->next_ = HAWK_NULL;
|
|
return top;
|
|
}
|
|
|
|
void hawk_rtx_pushecb (hawk_rtx_t* rtx, hawk_rtx_ecb_t* ecb)
|
|
{
|
|
ecb->next_ = rtx->ecb;
|
|
rtx->ecb = ecb;
|
|
if (ecb->stmt) rtx->ecb_stmt_count++;
|
|
if (ecb->gblset) rtx->ecb_gblset_count++;
|
|
}
|
|
|
|
static int init_rtx (hawk_rtx_t* rtx, hawk_t* hawk, hawk_rio_cbs_t* rio)
|
|
{
|
|
hawk_oow_t stack_limit, i;
|
|
|
|
rtx->gem_ = hawk->gem_;
|
|
rtx->hawk = hawk;
|
|
|
|
CLRERR(rtx);
|
|
|
|
stack_limit = hawk->parse.pragma.rtx_stack_limit > 0? hawk->parse.pragma.rtx_stack_limit: hawk->opt.rtx_stack_limit;
|
|
if (stack_limit < HAWK_MIN_RTX_STACK_LIMIT) stack_limit = HAWK_MIN_RTX_STACK_LIMIT;
|
|
rtx->stack = hawk_rtx_allocmem(rtx, stack_limit * HAWK_SIZEOF(void*));
|
|
if (HAWK_UNLIKELY(!rtx->stack)) goto oops_0;
|
|
rtx->stack_top = 0;
|
|
rtx->stack_base = 0;
|
|
rtx->stack_limit = stack_limit;
|
|
|
|
rtx->exit_level = EXIT_NONE;
|
|
|
|
rtx->vmgr.ichunk = HAWK_NULL;
|
|
rtx->vmgr.ifree = HAWK_NULL;
|
|
rtx->vmgr.rchunk = HAWK_NULL;
|
|
rtx->vmgr.rfree = HAWK_NULL;
|
|
|
|
for (i = 0; i < HAWK_COUNTOF(rtx->gc.g); i++)
|
|
{
|
|
/* initialize circular doubly-linked list */
|
|
rtx->gc.g[i].gc_next = &rtx->gc.g[i];
|
|
rtx->gc.g[i].gc_prev = &rtx->gc.g[i];
|
|
|
|
/* initialize some counters */
|
|
rtx->gc.pressure[i] = 0;
|
|
rtx->gc.threshold[i] = (HAWK_COUNTOF(rtx->gc.g) - i) * 10;
|
|
if (i == 0 && rtx->gc.threshold[i] < 100) rtx->gc.threshold[i] = 100; /* minimum threshold for gen 0 is 100 */
|
|
}
|
|
|
|
rtx->gc.pressure[i] = 0; /* pressure is larger than other elements by 1 in size */
|
|
|
|
rtx->inrec.buf_pos = 0;
|
|
rtx->inrec.buf_len = 0;
|
|
rtx->inrec.flds = HAWK_NULL;
|
|
rtx->inrec.nflds = 0;
|
|
rtx->inrec.maxflds = 0;
|
|
rtx->inrec.d0 = hawk_val_nil;
|
|
|
|
if (HAWK_UNLIKELY(hawk_ooecs_init(&rtx->inrec.line, hawk_rtx_getgem(rtx), DEF_BUF_CAPA) <= -1)) goto oops_1;
|
|
if (HAWK_UNLIKELY(hawk_ooecs_init(&rtx->inrec.linew, hawk_rtx_getgem(rtx), DEF_BUF_CAPA) <= -1)) goto oops_2;
|
|
if (HAWK_UNLIKELY(hawk_ooecs_init(&rtx->inrec.lineg, hawk_rtx_getgem(rtx), DEF_BUF_CAPA) <= -1)) goto oops_3;
|
|
if (HAWK_UNLIKELY(hawk_becs_init(&rtx->inrec.linegb, hawk_rtx_getgem(rtx), DEF_BUF_CAPA) <= -1)) goto oops_4;
|
|
if (HAWK_UNLIKELY(hawk_ooecs_init(&rtx->format.out, hawk_rtx_getgem(rtx), 256) <= -1)) goto oops_5;
|
|
if (HAWK_UNLIKELY(hawk_ooecs_init(&rtx->format.fmt, hawk_rtx_getgem(rtx), 256) <= -1)) goto oops_6;
|
|
if (HAWK_UNLIKELY(hawk_ooecs_init(&rtx->format.fltout, hawk_rtx_getgem(rtx), 256) <= -1)) goto oops_6_1;
|
|
if (HAWK_UNLIKELY(hawk_ooecs_init(&rtx->format.fltfmt, hawk_rtx_getgem(rtx), 256) <= -1)) goto oops_6_2;
|
|
|
|
if (HAWK_UNLIKELY(hawk_becs_init(&rtx->formatmbs.out, hawk_rtx_getgem(rtx), 256) <= -1)) goto oops_7;
|
|
if (HAWK_UNLIKELY(hawk_becs_init(&rtx->formatmbs.fmt, hawk_rtx_getgem(rtx), 256) <= -1)) goto oops_8;
|
|
|
|
if (HAWK_UNLIKELY(hawk_becs_init(&rtx->fnc.bout, hawk_rtx_getgem(rtx), 256) <= -1)) goto oops_9;
|
|
if (HAWK_UNLIKELY(hawk_ooecs_init(&rtx->fnc.oout, hawk_rtx_getgem(rtx), 256) <= -1)) goto oops_10;
|
|
|
|
rtx->named = hawk_htb_open(hawk_rtx_getgem(rtx), HAWK_SIZEOF(rtx), 1024, 70, HAWK_SIZEOF(hawk_ooch_t), 1);
|
|
if (HAWK_UNLIKELY(!rtx->named)) goto oops_11;
|
|
*(hawk_rtx_t**)hawk_htb_getxtn(rtx->named) = rtx;
|
|
hawk_htb_setstyle(rtx->named, hawk_get_htb_style(HAWK_HTB_STYLE_INLINE_KEY_COPIER));
|
|
|
|
rtx->named_slot_capa = HAWK_HTB_SIZE(hawk->parse.named);
|
|
rtx->named_slot_count = HAWK_HTB_SIZE(hawk->parse.named);
|
|
if (rtx->named_slot_count > 0)
|
|
{
|
|
rtx->named_slots = (hawk_val_t**)hawk_rtx_allocmem(rtx, rtx->named_slot_capa * HAWK_SIZEOF(*rtx->named_slots));
|
|
if (HAWK_UNLIKELY(!rtx->named_slots)) goto oops_12_named_slots;
|
|
for (i = 0; i < rtx->named_slot_count; i++) rtx->named_slots[i] = hawk_val_nil;
|
|
}
|
|
else
|
|
{
|
|
rtx->named_slots = HAWK_NULL;
|
|
}
|
|
|
|
rtx->format.tmp.ptr = (hawk_ooch_t*)hawk_rtx_allocmem(rtx, 4096 * HAWK_SIZEOF(hawk_ooch_t));
|
|
if (HAWK_UNLIKELY(!rtx->format.tmp.ptr)) goto oops_12; /* the error is set on the hawk object after this jump is made */
|
|
rtx->format.tmp.len = 4096;
|
|
rtx->format.tmp.inc = 4096 * 2;
|
|
|
|
rtx->formatmbs.tmp.ptr = (hawk_bch_t*)hawk_rtx_allocmem(rtx, 4096 * HAWK_SIZEOF(hawk_bch_t));
|
|
if (HAWK_UNLIKELY(!rtx->formatmbs.tmp.ptr)) goto oops_13;
|
|
rtx->formatmbs.tmp.len = 4096;
|
|
rtx->formatmbs.tmp.inc = 4096 * 2;
|
|
|
|
if (rtx->hawk->tree.chain_size > 0)
|
|
{
|
|
rtx->pattern_range_state = (hawk_oob_t*)hawk_rtx_allocmem(rtx, rtx->hawk->tree.chain_size * HAWK_SIZEOF(hawk_oob_t));
|
|
if (HAWK_UNLIKELY(!rtx->pattern_range_state)) goto oops_14;
|
|
HAWK_MEMSET(rtx->pattern_range_state, 0, rtx->hawk->tree.chain_size * HAWK_SIZEOF(hawk_oob_t));
|
|
}
|
|
else rtx->pattern_range_state = HAWK_NULL;
|
|
|
|
rtx->modtab = hawk_rbt_open(hawk_getgem(hawk), 0, HAWK_SIZEOF(hawk_ooch_t), 1);
|
|
if (HAWK_UNLIKELY(!rtx->modtab)) goto oops_15;
|
|
hawk_rbt_setstyle(rtx->modtab, hawk_get_rbt_style(HAWK_RBT_STYLE_INLINE_COPIERS));
|
|
|
|
if (rio)
|
|
{
|
|
rtx->rio.handler[HAWK_RIO_PIPE] = rio->pipe;
|
|
rtx->rio.handler[HAWK_RIO_FILE] = rio->file;
|
|
rtx->rio.handler[HAWK_RIO_CONSOLE] = rio->console;
|
|
rtx->rio.env_mk = rio->env_mk;
|
|
rtx->rio.chain = HAWK_NULL;
|
|
}
|
|
|
|
rtx->gbl.rs[0] = HAWK_NULL;
|
|
rtx->gbl.rs[1] = HAWK_NULL;
|
|
rtx->gbl.fs[0] = HAWK_NULL;
|
|
rtx->gbl.fs[1] = HAWK_NULL;
|
|
rtx->gbl.ignorecase = 0;
|
|
rtx->gbl.numstrdetect = -1; /* means 'not set' */
|
|
rtx->gbl.pipecloexec = -1; /* means 'not set' */
|
|
rtx->gbl.striprecspc = -1; /* means 'not set' */
|
|
rtx->gbl.stripstrspc = -1; /* means 'not set' */
|
|
|
|
return 0;
|
|
|
|
oops_15:
|
|
if (rtx->pattern_range_state) hawk_rtx_freemem(rtx, rtx->pattern_range_state);
|
|
oops_14:
|
|
hawk_rtx_freemem(rtx, rtx->formatmbs.tmp.ptr);
|
|
oops_13:
|
|
hawk_rtx_freemem(rtx, rtx->format.tmp.ptr);
|
|
oops_12:
|
|
if (rtx->named_slots) hawk_rtx_freemem(rtx, rtx->named_slots);
|
|
oops_12_named_slots:
|
|
hawk_htb_close(rtx->named);
|
|
oops_11:
|
|
hawk_ooecs_fini(&rtx->fnc.oout);
|
|
oops_10:
|
|
hawk_becs_fini(&rtx->fnc.bout);
|
|
oops_9:
|
|
hawk_becs_fini(&rtx->formatmbs.fmt);
|
|
oops_8:
|
|
hawk_becs_fini(&rtx->formatmbs.out);
|
|
oops_7:
|
|
hawk_ooecs_fini(&rtx->format.fltfmt);
|
|
oops_6_2:
|
|
hawk_ooecs_fini(&rtx->format.fltout);
|
|
oops_6_1:
|
|
hawk_ooecs_fini(&rtx->format.fmt);
|
|
oops_6:
|
|
hawk_ooecs_fini(&rtx->format.out);
|
|
oops_5:
|
|
hawk_becs_fini(&rtx->inrec.linegb);
|
|
oops_4:
|
|
hawk_ooecs_fini(&rtx->inrec.lineg);
|
|
oops_3:
|
|
hawk_ooecs_fini(&rtx->inrec.linew);
|
|
oops_2:
|
|
hawk_ooecs_fini(&rtx->inrec.line);
|
|
oops_1:
|
|
if (rtx->exec_stack) hawk_rtx_freemem(rtx, rtx->exec_stack);
|
|
if (rtx->eframe_stack) hawk_rtx_freemem(rtx, rtx->eframe_stack);
|
|
hawk_rtx_freemem(rtx, rtx->stack);
|
|
oops_0:
|
|
return -1;
|
|
}
|
|
|
|
static void fini_rtx (hawk_rtx_t* rtx, int fini_globals)
|
|
{
|
|
#if !defined(HAWK_ENABLE_ATOMIC_SIG)
|
|
if (rtx->sig_mtx_inited)
|
|
{
|
|
hawk_mtx_fini(&rtx->sig_mtx);
|
|
rtx->sig_mtx_inited = 0;
|
|
}
|
|
#endif
|
|
|
|
if (rtx->forin.ptr)
|
|
{
|
|
while (rtx->forin.size > 0)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, rtx->forin.ptr[--rtx->forin.size]);
|
|
}
|
|
hawk_rtx_freemem(rtx, rtx->forin.ptr);
|
|
rtx->forin.ptr = HAWK_NULL;
|
|
rtx->forin.capa = 0;
|
|
}
|
|
|
|
HAWK_ASSERT(rtx->modtab != HAWK_NULL);
|
|
hawk_rbt_close(rtx->modtab);
|
|
|
|
if (rtx->pattern_range_state)
|
|
hawk_rtx_freemem(rtx, rtx->pattern_range_state);
|
|
|
|
/* close all pending io's */
|
|
/* TODO: what if this operation fails? */
|
|
hawk_rtx_clearallios(rtx);
|
|
HAWK_ASSERT(rtx->rio.chain == HAWK_NULL);
|
|
|
|
if (rtx->gbl.rs[0])
|
|
{
|
|
hawk_rtx_freerex(rtx, rtx->gbl.rs[0], rtx->gbl.rs[1]);
|
|
rtx->gbl.rs[0] = HAWK_NULL;
|
|
rtx->gbl.rs[1] = HAWK_NULL;
|
|
}
|
|
if (rtx->gbl.fs[0])
|
|
{
|
|
hawk_rtx_freerex(rtx, rtx->gbl.fs[0], rtx->gbl.fs[1]);
|
|
rtx->gbl.fs[0] = HAWK_NULL;
|
|
rtx->gbl.fs[1] = HAWK_NULL;
|
|
}
|
|
|
|
if (rtx->gbl.convfmt.ptr != HAWK_NULL &&
|
|
rtx->gbl.convfmt.ptr != DEFAULT_CONVFMT)
|
|
{
|
|
hawk_rtx_freemem(rtx, rtx->gbl.convfmt.ptr);
|
|
rtx->gbl.convfmt.ptr = HAWK_NULL;
|
|
rtx->gbl.convfmt.len = 0;
|
|
}
|
|
|
|
if (rtx->gbl.ofmt.ptr != HAWK_NULL &&
|
|
rtx->gbl.ofmt.ptr != DEFAULT_OFMT)
|
|
{
|
|
hawk_rtx_freemem(rtx, rtx->gbl.ofmt.ptr);
|
|
rtx->gbl.ofmt.ptr = HAWK_NULL;
|
|
rtx->gbl.ofmt.len = 0;
|
|
}
|
|
|
|
if (rtx->gbl.ofs.ptr != HAWK_NULL &&
|
|
rtx->gbl.ofs.ptr != DEFAULT_OFS)
|
|
{
|
|
hawk_rtx_freemem(rtx, rtx->gbl.ofs.ptr);
|
|
rtx->gbl.ofs.ptr = HAWK_NULL;
|
|
rtx->gbl.ofs.len = 0;
|
|
}
|
|
|
|
if (rtx->gbl.ors.ptr != HAWK_NULL &&
|
|
rtx->gbl.ors.ptr != DEFAULT_ORS &&
|
|
rtx->gbl.ors.ptr != DEFAULT_ORS_CRLF)
|
|
{
|
|
hawk_rtx_freemem(rtx, rtx->gbl.ors.ptr);
|
|
rtx->gbl.ors.ptr = HAWK_NULL;
|
|
rtx->gbl.ors.len = 0;
|
|
}
|
|
|
|
if (rtx->gbl.subsep.ptr != HAWK_NULL &&
|
|
rtx->gbl.subsep.ptr != DEFAULT_SUBSEP)
|
|
{
|
|
hawk_rtx_freemem(rtx, rtx->gbl.subsep.ptr);
|
|
rtx->gbl.subsep.ptr = HAWK_NULL;
|
|
rtx->gbl.subsep.len = 0;
|
|
}
|
|
|
|
hawk_ooecs_fini(&rtx->fnc.oout);
|
|
hawk_becs_fini(&rtx->fnc.bout);
|
|
|
|
hawk_rtx_freemem(rtx, rtx->formatmbs.tmp.ptr);
|
|
rtx->formatmbs.tmp.ptr = HAWK_NULL;
|
|
rtx->formatmbs.tmp.len = 0;
|
|
hawk_becs_fini(&rtx->formatmbs.fmt);
|
|
hawk_becs_fini(&rtx->formatmbs.out);
|
|
|
|
hawk_rtx_freemem(rtx, rtx->format.tmp.ptr);
|
|
rtx->format.tmp.ptr = HAWK_NULL;
|
|
rtx->format.tmp.len = 0;
|
|
hawk_ooecs_fini(&rtx->format.fltfmt);
|
|
hawk_ooecs_fini(&rtx->format.fltout);
|
|
hawk_ooecs_fini(&rtx->format.fmt);
|
|
hawk_ooecs_fini(&rtx->format.out);
|
|
|
|
/* destroy input record. hawk_rtx_clrrec() should be called
|
|
* before the stack has been destroyed because it may try
|
|
* to change the value to HAWK_GBL_NF. */
|
|
hawk_rtx_clrrec(rtx, 0);
|
|
if (rtx->inrec.flds)
|
|
{
|
|
hawk_rtx_freemem(rtx, rtx->inrec.flds);
|
|
rtx->inrec.flds = HAWK_NULL;
|
|
rtx->inrec.maxflds = 0;
|
|
}
|
|
hawk_becs_fini(&rtx->inrec.linegb);
|
|
hawk_ooecs_fini(&rtx->inrec.lineg);
|
|
hawk_ooecs_fini(&rtx->inrec.linew);
|
|
hawk_ooecs_fini(&rtx->inrec.line);
|
|
|
|
if (fini_globals) refdown_globals(rtx, 1);
|
|
|
|
/* destroy the stack if necessary */
|
|
if (rtx->stack)
|
|
{
|
|
HAWK_ASSERT(rtx->stack_top == 0);
|
|
|
|
hawk_rtx_freemem(rtx, rtx->stack);
|
|
rtx->stack = HAWK_NULL;
|
|
rtx->stack_top = 0;
|
|
rtx->stack_base = 0;
|
|
rtx->stack_limit = 0;
|
|
}
|
|
|
|
if (rtx->exec_stack)
|
|
{
|
|
hawk_rtx_freemem(rtx, rtx->exec_stack);
|
|
rtx->exec_stack = HAWK_NULL;
|
|
rtx->exec_stack_size = 0;
|
|
rtx->exec_stack_limit = 0;
|
|
}
|
|
|
|
if (rtx->eframe_stack)
|
|
{
|
|
hawk_rtx_freemem(rtx, rtx->eframe_stack);
|
|
rtx->eframe_stack = HAWK_NULL;
|
|
rtx->eframe_stack_size = 0;
|
|
rtx->eframe_stack_limit = 0;
|
|
}
|
|
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
if (rtx->call_stack)
|
|
{
|
|
hawk_rtx_freemem(rtx, rtx->call_stack);
|
|
rtx->call_stack = HAWK_NULL;
|
|
rtx->call_stack_size = 0;
|
|
rtx->call_stack_limit = 0;
|
|
}
|
|
if (rtx->driver_eval_result)
|
|
{
|
|
hawk_rtx_refupval_inline(rtx, rtx->driver_eval_result);
|
|
hawk_rtx_refdownval_inline(rtx, rtx->driver_eval_result);
|
|
rtx->driver_eval_result = HAWK_NULL;
|
|
}
|
|
#endif
|
|
|
|
#if defined(HAWK_ENABLE_UCONTEXT)
|
|
while (rtx->co.pool_size > 0)
|
|
hawk_rtx_freemem(rtx, rtx->co.pool[--rtx->co.pool_size]);
|
|
#endif
|
|
|
|
/* destroy named variables */
|
|
hawk_htb_close(rtx->named);
|
|
if (rtx->named_slots)
|
|
{
|
|
hawk_oow_t i;
|
|
|
|
/* refdown all named variables */
|
|
for (i = 0; i < rtx->named_slot_count; i++)
|
|
hawk_rtx_refdownval_inline(rtx, rtx->named_slots[i]); /* TODO: it is better to disable caching as it's shutting down anyways? */
|
|
|
|
hawk_rtx_freemem(rtx, rtx->named_slots);
|
|
rtx->named_slots = HAWK_NULL;
|
|
rtx->named_slot_count = 0;
|
|
rtx->named_slot_capa = 0;
|
|
}
|
|
|
|
#if defined(HAWK_ENABLE_GC)
|
|
/* collect garbage after having released global variables and named global variables */
|
|
hawk_rtx_gc(rtx, HAWK_RTX_GC_GEN_FULL);
|
|
#endif
|
|
|
|
/* destroy values in free list */
|
|
while (rtx->rcache_count > 0)
|
|
{
|
|
hawk_val_ref_t* tmp = rtx->rcache[--rtx->rcache_count];
|
|
hawk_rtx_freeval(rtx, (hawk_val_t*)tmp, 0);
|
|
}
|
|
|
|
#if defined(HAWK_ENABLE_STR_CACHE)
|
|
{
|
|
int i;
|
|
for (i = 0; i < HAWK_COUNTOF(rtx->str_cache_count); i++)
|
|
{
|
|
while (rtx->str_cache_count[i] > 0)
|
|
{
|
|
hawk_val_str_t* t = rtx->str_cache[i][--rtx->str_cache_count[i]];
|
|
hawk_rtx_freeval(rtx, (hawk_val_t*)t, 0);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(HAWK_ENABLE_MBS_CACHE)
|
|
{
|
|
int i;
|
|
for (i = 0; i < HAWK_COUNTOF(rtx->mbs_cache_count); i++)
|
|
{
|
|
while (rtx->mbs_cache_count[i] > 0)
|
|
{
|
|
hawk_val_mbs_t* t = rtx->mbs_cache[i][--rtx->mbs_cache_count[i]];
|
|
hawk_rtx_freeval(rtx, (hawk_val_t*)t, 0);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
hawk_rtx_freevalchunk(rtx, rtx->vmgr.ichunk);
|
|
hawk_rtx_freevalchunk(rtx, rtx->vmgr.rchunk);
|
|
rtx->vmgr.ichunk = HAWK_NULL;
|
|
rtx->vmgr.rchunk = HAWK_NULL;
|
|
}
|
|
|
|
hawk_mod_t* hawk_rtx_querymodulewithoocs (hawk_rtx_t* rtx, const hawk_oocs_t* name, hawk_mod_sym_t* sym, int flags)
|
|
{
|
|
hawk_mod_t* m;
|
|
hawk_oow_t old_size;
|
|
|
|
old_size = HAWK_RBT_SIZE(rtx->modtab);
|
|
if (flags & HAWK_RTX_QUERYMODULE_NOLOCK)
|
|
{
|
|
/* init/fini/unload is also called in the locked context. for example, see
|
|
* hawk_rtx_open() which calls the init method of a module. if you want to
|
|
* avoid recursive locking, the caller must specify HAWK_RTX_QUERYMODULE_NOLOCK
|
|
* when calling this function from such a context */
|
|
m = hawk_querymodulewithoocs(rtx->hawk, name, sym, rtx->modtab);
|
|
}
|
|
else
|
|
{
|
|
/* in a multi-threaded application where you have a rtx in different threads over
|
|
* the same hawk object, the mutex blow protects the commonly used hawk->modtab. */
|
|
hawk_mtx_lock(rtx->hawk->modmtx, HAWK_NULL);
|
|
m = hawk_querymodulewithoocs(rtx->hawk, name, sym, rtx->modtab);
|
|
hawk_mtx_unlock(rtx->hawk->modmtx);
|
|
}
|
|
|
|
if (m && old_size != HAWK_RBT_SIZE(rtx->modtab))
|
|
{
|
|
/* a new module must have been added to the runtime module table */
|
|
if (m->init && m->init(m, rtx) <= -1)
|
|
{
|
|
const hawk_ooch_t* dc;
|
|
hawk_oow_t len;
|
|
hawk_rbt_pair_t* pair;
|
|
|
|
/* the logic here must match hawk_querymodulewithoocs() in parse.c */
|
|
dc = hawk_find_oochars_in_oochars(name->ptr, name->len, HAWK_T("::"), 2, 0);
|
|
HAWK_ASSERT(dc != HAWK_NULL);
|
|
if (HAWK_UNLIKELY(!dc)) goto internal_error_unrolling; /* this must not happend but .. */
|
|
len = dc - name->ptr;
|
|
|
|
pair = hawk_rbt_search(rtx->modtab, name->ptr, len);
|
|
HAWK_ASSERT(pair != HAWK_NULL);
|
|
if (HAWK_UNLIKELY(!pair)) /* this must not happen but .. */
|
|
{
|
|
internal_error_unrolling:
|
|
hawk_rtx_seterrbfmt(rtx, HAWK_NULL, HAWK_EINTERN,
|
|
"internal runtime error - failed to unroll '%.*js' after initialization failure",
|
|
name->len, name->ptr);
|
|
}
|
|
else
|
|
{
|
|
/* skip the fini callback because the init callback returned failure. not calling m->fini().
|
|
* invoke the unload() callback if needed and unload the module itself. */
|
|
hawk_modtab_unload_module(rtx->modtab, pair, rtx->hawk);
|
|
hawk_rbt_delete(rtx->modtab, name->ptr, len);
|
|
}
|
|
|
|
m = HAWK_NULL; /* indicate failure */
|
|
}
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
hawk_mod_t* hawk_rtx_querymodulewithname (hawk_rtx_t* rtx, const hawk_ooch_t* name, hawk_mod_sym_t* sym, int flags)
|
|
{
|
|
hawk_oocs_t oocs;
|
|
oocs.ptr = (hawk_ooch_t*)name;
|
|
oocs.len = hawk_count_oocstr(name);
|
|
return hawk_rtx_querymodulewithoocs(rtx, &oocs, sym, flags);
|
|
}
|
|
|
|
static int update_fnr (hawk_rtx_t* rtx, hawk_int_t fnr, hawk_int_t nr)
|
|
{
|
|
hawk_val_t* tmp1, * tmp2;
|
|
int n;
|
|
|
|
tmp1 = hawk_rtx_makeintval_inline(rtx, fnr);
|
|
if (!tmp1) return -1;
|
|
|
|
hawk_rtx_refupval_inline(rtx, tmp1);
|
|
|
|
if (nr == fnr) tmp2 = tmp1;
|
|
else
|
|
{
|
|
tmp2 = hawk_rtx_makeintval_inline(rtx, nr);
|
|
if (HAWK_UNLIKELY(!tmp2)) { n = -1; goto oops; }
|
|
hawk_rtx_refupval_inline(rtx, tmp2);
|
|
}
|
|
|
|
n = (hawk_rtx_setgbl(rtx, HAWK_GBL_FNR, tmp1) <= -1 ||
|
|
hawk_rtx_setgbl(rtx, HAWK_GBL_NR, tmp2) <= -1)? -1: 0;
|
|
|
|
if (tmp2 != tmp1) hawk_rtx_refdownval_inline(rtx, tmp2);
|
|
|
|
oops:
|
|
hawk_rtx_refdownval_inline(rtx, tmp1);
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
* create global variables into the runtime stack
|
|
* each variable is initialized to nil or zero.
|
|
*/
|
|
static int prepare_globals (hawk_rtx_t* rtx)
|
|
{
|
|
hawk_oow_t saved_stack_top;
|
|
hawk_oow_t ngbls;
|
|
|
|
ngbls = rtx->hawk->tree.ngbls;
|
|
|
|
if (HAWK_UNLIKELY(HAWK_RTX_STACK_AVAIL(rtx) < ngbls))
|
|
{
|
|
hawk_rtx_seterrbfmt(rtx, HAWK_NULL, HAWK_ESTACK,
|
|
"stack full(avail=%zu, limit=%zu) for %zu global variables",
|
|
HAWK_RTX_STACK_AVAIL(rtx), rtx->stack_limit, ngbls);
|
|
return -1;
|
|
}
|
|
|
|
saved_stack_top = rtx->stack_top;
|
|
|
|
/* initialize all global variables to nil by push nils to the stack */
|
|
while (ngbls > 0)
|
|
{
|
|
--ngbls;
|
|
HAWK_RTX_STACK_PUSH(rtx, hawk_val_nil);
|
|
}
|
|
|
|
/* override NF to zero */
|
|
if (hawk_rtx_setgbl(rtx, HAWK_GBL_NF, HAWK_VAL_ZERO) <= -1)
|
|
{
|
|
/* restore the stack_top this way instead of calling HAWK_RTX_STACK_POP()
|
|
* as many times as successful HAWK_RTX_STACK_PUSH(). it is ok because
|
|
* the values pushed so far are hawk_val_nils and HAWK_VAL_ZEROs.
|
|
*/
|
|
rtx->stack_top = saved_stack_top;
|
|
return -1;
|
|
}
|
|
|
|
/* return success */
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* assign initial values to the global variables whose desired initial
|
|
* values are not nil or zero. some are handled in prepare_globals () and
|
|
* update_fnr().
|
|
*/
|
|
static int defaultify_globals (hawk_rtx_t* rtx)
|
|
{
|
|
struct gtab_t
|
|
{
|
|
int idx;
|
|
const hawk_ooch_t* str[2];
|
|
};
|
|
static struct gtab_t gtab[9] =
|
|
{
|
|
{ HAWK_GBL_CONVFMT, { DEFAULT_CONVFMT, DEFAULT_CONVFMT } },
|
|
{ HAWK_GBL_FILENAME, { HAWK_NULL, HAWK_NULL } },
|
|
{ HAWK_GBL_FS, { DEFAULT_FS, DEFAULT_FS } },
|
|
{ HAWK_GBL_OFILENAME, { HAWK_NULL, HAWK_NULL } },
|
|
{ HAWK_GBL_OFMT, { DEFAULT_OFMT, DEFAULT_OFMT } },
|
|
{ HAWK_GBL_OFS, { DEFAULT_OFS, DEFAULT_OFS } },
|
|
{ HAWK_GBL_ORS, { DEFAULT_ORS, DEFAULT_ORS_CRLF } },
|
|
{ HAWK_GBL_SCRIPTNAME, { HAWK_NULL, HAWK_NULL } },
|
|
{ HAWK_GBL_SUBSEP, { DEFAULT_SUBSEP, DEFAULT_SUBSEP } }
|
|
};
|
|
|
|
hawk_val_t* tmp;
|
|
hawk_oow_t i, j;
|
|
int stridx;
|
|
|
|
stridx = (rtx->hawk->opt.trait & HAWK_CRLF)? 1: 0;
|
|
for (i = 0; i < HAWK_COUNTOF(gtab); i++)
|
|
{
|
|
if (gtab[i].str[stridx] == HAWK_NULL || gtab[i].str[stridx][0] == HAWK_T('\0'))
|
|
{
|
|
tmp = hawk_val_zls;
|
|
}
|
|
else
|
|
{
|
|
tmp = hawk_rtx_makestrvalwithoocstr(rtx, gtab[i].str[stridx]);
|
|
if (tmp == HAWK_NULL) return -1;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, tmp);
|
|
|
|
HAWK_ASSERT(HAWK_RTX_STACK_GBL(rtx,gtab[i].idx) == hawk_val_nil);
|
|
|
|
if (hawk_rtx_setgbl(rtx, gtab[i].idx, tmp) == -1)
|
|
{
|
|
for (j = 0; j < i; j++)
|
|
{
|
|
hawk_rtx_setgbl(rtx, gtab[i].idx, hawk_val_nil);
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, tmp);
|
|
return -1;
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, tmp);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void refdown_globals (hawk_rtx_t* rtx, int pop)
|
|
{
|
|
hawk_oow_t ngbls;
|
|
|
|
ngbls = rtx->hawk->tree.ngbls;
|
|
while (ngbls > 0)
|
|
{
|
|
--ngbls;
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_GBL(rtx,ngbls));
|
|
if (pop) HAWK_RTX_STACK_POP(rtx);
|
|
else HAWK_RTX_STACK_GBL(rtx,ngbls) = hawk_val_nil;
|
|
}
|
|
}
|
|
|
|
static int init_globals (hawk_rtx_t* rtx)
|
|
{
|
|
/* the stack must be clean when this function is invoked */
|
|
HAWK_ASSERT(rtx->stack_base == 0);
|
|
HAWK_ASSERT(rtx->stack_top == 0);
|
|
|
|
if (prepare_globals(rtx) <= -1) return -1;
|
|
if (update_fnr(rtx, 0, 0) <= -1 || defaultify_globals(rtx) <= -1) goto oops;
|
|
return 0;
|
|
|
|
oops:
|
|
refdown_globals(rtx, 1);
|
|
return -1;
|
|
}
|
|
|
|
struct capture_retval_data_t
|
|
{
|
|
hawk_rtx_t* rtx;
|
|
hawk_val_t* val;
|
|
};
|
|
|
|
static void capture_retval_on_exit (void* arg)
|
|
{
|
|
struct capture_retval_data_t* data;
|
|
|
|
data = (struct capture_retval_data_t*)arg;
|
|
data->val = HAWK_RTX_STACK_RETVAL(data->rtx);
|
|
hawk_rtx_refupval_inline(data->rtx, data->val);
|
|
}
|
|
|
|
static int run_blocks_for_bpae_loop (hawk_rtx_t* rtx, hawk_nde_t* blk_head, int top_exit_level, int ret)
|
|
{
|
|
hawk_nde_t* nde;
|
|
|
|
/* top_exit_level
|
|
* init/BEGIN - not called if `exit[EXIT_GLOBAL]` has been called
|
|
* END - not called if `@abort[EXIT_ABORT]` has been called. */
|
|
for (nde = blk_head; ret == 0 && nde && rtx->exit_level < top_exit_level; nde = nde->next)
|
|
{
|
|
hawk_nde_blk_t* blk;
|
|
|
|
blk = (hawk_nde_blk_t*)nde;
|
|
HAWK_ASSERT(blk->type == HAWK_NDE_BLK);
|
|
|
|
rtx->active_block = blk;
|
|
rtx->exit_level = EXIT_NONE;
|
|
if (run_block(rtx, blk) <= -1) ret = -1;
|
|
else if (top_exit_level == EXIT_ABORT && rtx->exit_level >= EXIT_GLOBAL)
|
|
{
|
|
/* top_exit_level is EXIT_ABORT for END bloacks.
|
|
* once `exit` is called inside one of END blocks,
|
|
* subsequent END blocks must not be executed */
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ret <= -1 && hawk_rtx_geterrnum(rtx) == HAWK_ENOERR)
|
|
{
|
|
/* an error is returned with no error number set.
|
|
* this trait is used by eval_expression() to
|
|
* abort the evaluation when exit() is executed
|
|
* during function evaluation */
|
|
ret = 0;
|
|
CLRERR(rtx); /* clear it just in case */
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static hawk_val_t* run_bpae_loop (hawk_rtx_t* rtx)
|
|
{
|
|
hawk_oow_t nargs, i;
|
|
hawk_val_t* retv;
|
|
int ret = 0;
|
|
|
|
/* set nargs to zero */
|
|
nargs = 0;
|
|
HAWK_RTX_STACK_NARGS(rtx) = (void*)nargs;
|
|
|
|
if (!rtx->init_called)
|
|
{
|
|
rtx->init_called = 1;
|
|
if (rtx->hawk->tree.init) /* execute @global/@const initialization */
|
|
{
|
|
HAWK_ASSERT(rtx->hawk->tree.init->next == HAWK_NULL); /* there must exist only 1 init block */
|
|
ret = run_blocks_for_bpae_loop(rtx, rtx->hawk->tree.init, EXIT_GLOBAL, ret);
|
|
}
|
|
}
|
|
if (rtx->hawk->tree.begin) /* the begin blocks */
|
|
{
|
|
/* there may be multiple BEGIN blocks */
|
|
ret = run_blocks_for_bpae_loop(rtx, rtx->hawk->tree.begin, EXIT_GLOBAL, ret);
|
|
}
|
|
|
|
/* run pattern block loops */
|
|
if (ret == 0 &&
|
|
(rtx->hawk->tree.chain != HAWK_NULL || rtx->hawk->tree.end != HAWK_NULL) &&
|
|
rtx->exit_level < EXIT_GLOBAL)
|
|
{
|
|
if (run_pblocks(rtx) <= -1) ret = -1;
|
|
}
|
|
|
|
if (ret <= -1 && hawk_rtx_geterrnum(rtx) == HAWK_ENOERR)
|
|
{
|
|
/* an error is returned with no error number set.
|
|
* this trait is used by eval_expression() to
|
|
* abort the evaluation when exit() is executed
|
|
* during function evaluation */
|
|
ret = 0;
|
|
CLRERR(rtx); /* clear it just in case */
|
|
}
|
|
|
|
/* execute END blocks. the first END block is executed if the
|
|
* program is not explicitly aborted with hawk_rtx_halt().*/
|
|
if (rtx->hawk->tree.end)
|
|
{
|
|
/* there may be multiple END blocks */
|
|
ret = run_blocks_for_bpae_loop(rtx, rtx->hawk->tree.end, EXIT_ABORT, ret);
|
|
}
|
|
|
|
/* derefrence all arguments. however, there should be no arguments
|
|
* pushed to the stack as asserted below. we didn't push any arguments
|
|
* for BEGIN/pattern action/END block execution.*/
|
|
nargs = (hawk_oow_t)HAWK_RTX_STACK_NARGS(rtx);
|
|
HAWK_ASSERT(nargs == 0);
|
|
for (i = 0; i < nargs; i++) hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_ARG(rtx,i)); // just in case
|
|
|
|
/* get the return value in the current stack frame */
|
|
retv = HAWK_RTX_STACK_RETVAL(rtx);
|
|
|
|
if (ret <= -1)
|
|
{
|
|
/* end the life of the global return value upon error */
|
|
hawk_rtx_refdownval_inline(rtx, retv);
|
|
retv = HAWK_NULL;
|
|
}
|
|
|
|
return retv;
|
|
}
|
|
|
|
/* start the BEGIN-pattern block-END loop */
|
|
hawk_val_t* hawk_rtx_loop (hawk_rtx_t* rtx)
|
|
{
|
|
hawk_val_t* retv = HAWK_NULL;
|
|
hawk_oow_t saved_stack_top;
|
|
|
|
rtx->exit_level = EXIT_NONE;
|
|
|
|
/* make a new stack frame */
|
|
|
|
if (HAWK_UNLIKELY(HAWK_RTX_STACK_AVAIL(rtx) < 4))
|
|
{
|
|
/* restore the stack top in a cheesy(?) way.
|
|
* it is ok to do so as the values pushed are
|
|
* nils and binary numbers. */
|
|
hawk_rtx_seterrbfmt(rtx, HAWK_NULL, HAWK_ESTACK,
|
|
"stack full(avail=%zu, limit=%zu) in preparing call frame",
|
|
HAWK_RTX_STACK_AVAIL(rtx), rtx->stack_limit);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
saved_stack_top = rtx->stack_top; /* remember the current stack top */
|
|
HAWK_RTX_STACK_PUSH(rtx, (void*)rtx->stack_base); /* push the current stack base */
|
|
HAWK_RTX_STACK_PUSH(rtx, (void*)saved_stack_top); /* push the current stack top before push the current stack base */
|
|
HAWK_RTX_STACK_PUSH(rtx, hawk_val_nil); /* secure space for a return value */
|
|
HAWK_RTX_STACK_PUSH(rtx, hawk_val_nil); /* secure space for HAWK_RTX_STACK_NARGS */
|
|
|
|
/* enter the new stack frame */
|
|
rtx->stack_base = saved_stack_top; /* let the stack top remembered be the base of a new stack frame */
|
|
|
|
/* run the BEGIN/pattern-action/END loop */
|
|
retv = run_bpae_loop(rtx);
|
|
|
|
/* exit the stack frame */
|
|
HAWK_ASSERT((rtx->stack_top - rtx->stack_base) == 4); /* at this point, the current stack frame should have the 4 entries pushed above */
|
|
rtx->stack_top = (hawk_oow_t)rtx->stack[rtx->stack_base + 1];
|
|
rtx->stack_base = (hawk_oow_t)rtx->stack[rtx->stack_base + 0];
|
|
|
|
/* reset the exit level */
|
|
rtx->exit_level = EXIT_NONE;
|
|
|
|
/* flush all buffered io data */
|
|
hawk_rtx_flushallios(rtx);
|
|
|
|
return retv;
|
|
}
|
|
|
|
hawk_val_t* hawk_rtx_execwithucstrarr (hawk_rtx_t* rtx, const hawk_uch_t* args[], hawk_oow_t nargs)
|
|
{
|
|
hawk_val_t* v;
|
|
|
|
v = (rtx->hawk->parse.pragma.entry[0] != '\0')?
|
|
hawk_rtx_callwithooucstrarr(rtx, rtx->hawk->parse.pragma.entry, args, nargs):
|
|
hawk_rtx_loop(rtx);
|
|
|
|
#if defined(HAWK_ENABLE_GC)
|
|
/* i assume this function is a usual hawk program starter.
|
|
* call garbage collection after a whole program finishes */
|
|
hawk_rtx_gc(rtx, HAWK_RTX_GC_GEN_FULL);
|
|
#endif
|
|
|
|
return v;
|
|
}
|
|
|
|
hawk_val_t* hawk_rtx_execwithbcstrarr (hawk_rtx_t* rtx, const hawk_bch_t* args[], hawk_oow_t nargs)
|
|
{
|
|
hawk_val_t* v;
|
|
|
|
v = (rtx->hawk->parse.pragma.entry[0] != '\0')?
|
|
hawk_rtx_callwithoobcstrarr(rtx, rtx->hawk->parse.pragma.entry, args, nargs):
|
|
hawk_rtx_loop(rtx);
|
|
|
|
#if defined(HAWK_ENABLE_GC)
|
|
/* i assume this function is a usual hawk program starter.
|
|
* call garbage collection after a whole program finishes */
|
|
hawk_rtx_gc(rtx, HAWK_RTX_GC_GEN_FULL);
|
|
#endif
|
|
|
|
return v;
|
|
}
|
|
|
|
/* find an AWK function by name */
|
|
static hawk_fun_t* find_fun (hawk_rtx_t* rtx, const hawk_ooch_t* name)
|
|
{
|
|
hawk_htb_pair_t* pair;
|
|
|
|
pair = hawk_htb_search(rtx->hawk->tree.funs, name, hawk_count_oocstr(name));
|
|
if (!pair)
|
|
{
|
|
hawk_rtx_seterrfmt(rtx, HAWK_NULL, HAWK_EFUNNF, HAWK_T("unable to find function '%js'"), name);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
return (hawk_fun_t*)HAWK_HTB_VPTR(pair);
|
|
}
|
|
|
|
hawk_fun_t* hawk_rtx_findfunwithbcstr (hawk_rtx_t* rtx, const hawk_bch_t* name)
|
|
{
|
|
#if defined(HAWK_OOCH_IS_BCH)
|
|
return find_fun(rtx, name);
|
|
#else
|
|
hawk_ucs_t wcs;
|
|
hawk_fun_t* fun;
|
|
wcs.ptr = hawk_rtx_dupbtoucstr(rtx, name, &wcs.len, 0);
|
|
if (!wcs.ptr) return HAWK_NULL;
|
|
fun = find_fun(rtx, wcs.ptr);
|
|
hawk_rtx_freemem(rtx, wcs.ptr);
|
|
return fun;
|
|
#endif
|
|
}
|
|
|
|
hawk_fun_t* hawk_rtx_findfunwithucstr (hawk_rtx_t* rtx, const hawk_uch_t* name)
|
|
{
|
|
#if defined(HAWK_OOCH_IS_BCH)
|
|
hawk_bcs_t mbs;
|
|
hawk_fun_t* fun;
|
|
mbs.ptr = hawk_rtx_duputobcstr(rtx, name, &mbs.len);
|
|
if (!mbs.ptr) return HAWK_NULL;
|
|
fun = find_fun(rtx, mbs.ptr);
|
|
hawk_rtx_freemem(rtx, mbs.ptr);
|
|
return fun;
|
|
#else
|
|
return find_fun(rtx, name);
|
|
#endif
|
|
}
|
|
|
|
/* call an AWK function by the function structure */
|
|
hawk_val_t* hawk_rtx_callfun (hawk_rtx_t* rtx, hawk_fun_t* fun, hawk_val_t* args[], hawk_oow_t nargs)
|
|
{
|
|
struct capture_retval_data_t crdata;
|
|
hawk_val_t* v;
|
|
pafv_t pafv/*= { args, nargs }*/;
|
|
hawk_nde_fncall_t call;
|
|
|
|
HAWK_ASSERT(fun != HAWK_NULL);
|
|
|
|
if (HAWK_UNLIKELY(rtx->exit_level >= EXIT_GLOBAL))
|
|
{
|
|
/* cannot call the function again when exit() is called
|
|
* in an AWK program or hawk_rtx_halt() is invoked */
|
|
hawk_rtx_seterrfmt(rtx, HAWK_NULL, HAWK_EPERM, HAWK_T("not allowed to call '%.*js' after termination"), fun->name.len, fun->name.ptr);
|
|
return HAWK_NULL;
|
|
}
|
|
/*rtx->exit_level = EXIT_NONE;*/
|
|
|
|
#if 0
|
|
if (fun->argspec)
|
|
{
|
|
/* this function contains pass-by-reference parameters.
|
|
* i don't support the call here as it requires variables */
|
|
hawk_rtx_seterrfmt(rtx, HAWK_NULL, HAWK_EPERM, HAWK_T("not allowed to call '%.*js' with pass-by-reference parameters"), fun->name.len, fun->name.ptr);
|
|
return HAWK_NULL;
|
|
}
|
|
#endif
|
|
|
|
/* check if the number of arguments given is more than expected */
|
|
if (nargs > fun->nargs && !fun->variadic)
|
|
{
|
|
/* TODO: is this correct? what if i want to
|
|
* allow arbitrary numbers of arguments? */
|
|
hawk_rtx_seterrfmt(rtx, HAWK_NULL, HAWK_EARGTM, HAWK_T("too many arguments to '%.*js'"), fun->name.len, fun->name.ptr);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
if (HAWK_UNLIKELY(!rtx->init_called))
|
|
{
|
|
/* set this before actual execution of the init block.
|
|
* this is to make init_called 1 in both cases shown below:
|
|
* - if the init block doesn't exist
|
|
* - when it's really executed.
|
|
*/
|
|
rtx->init_called = 1;
|
|
|
|
if (rtx->hawk->tree.init)
|
|
{
|
|
int ret;
|
|
hawk_oow_t saved_stack_top;
|
|
hawk_oow_t nargs, i;
|
|
hawk_val_t* retv;
|
|
|
|
/* prepare the stack frame in the same way as hawk_rtx_loop() + run_bpae_loop() */
|
|
saved_stack_top = rtx->stack_top; /* remember the current stack top */
|
|
HAWK_RTX_STACK_PUSH(rtx, (void*)rtx->stack_base); /* push the current stack base */
|
|
HAWK_RTX_STACK_PUSH(rtx, (void*)saved_stack_top); /* push the current stack top before push the current stack base */
|
|
HAWK_RTX_STACK_PUSH(rtx, hawk_val_nil); /* secure space for a return value */
|
|
HAWK_RTX_STACK_PUSH(rtx, hawk_val_nil); /* secure space for HAWK_RTX_STACK_NARGS */
|
|
|
|
/* enter the new stack frame */
|
|
rtx->stack_base = saved_stack_top; /* let the stack top remembered be the base of a new stack frame */
|
|
|
|
/* zero arguments */
|
|
nargs = 0;
|
|
HAWK_RTX_STACK_NARGS(rtx) = (void*)nargs;
|
|
|
|
ret = run_blocks_for_bpae_loop(rtx, rtx->hawk->tree.init, EXIT_GLOBAL, 0);
|
|
|
|
nargs = (hawk_oow_t)HAWK_RTX_STACK_NARGS(rtx);
|
|
HAWK_ASSERT(nargs == 0); /* nargs on the stack must be 0 without pollution */
|
|
for (i = 0; i < nargs; i++) hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_ARG(rtx,i)); /* this is unnecessary, but in case */
|
|
|
|
/* get the return value in the current stack frame */
|
|
retv = HAWK_RTX_STACK_RETVAL(rtx);
|
|
hawk_rtx_refdownval_inline(rtx, retv); /* throw it away */
|
|
|
|
/* exit the stack frame */
|
|
HAWK_ASSERT((rtx->stack_top - rtx->stack_base) == 4); /* at this point, the current stack frame should have the 4 entries pushed above */
|
|
rtx->stack_top = (hawk_oow_t)rtx->stack[rtx->stack_base + 1];
|
|
rtx->stack_base = (hawk_oow_t)rtx->stack[rtx->stack_base + 0];
|
|
|
|
if (ret <= -1) return HAWK_NULL;
|
|
if (rtx->exit_level >= EXIT_GLOBAL)
|
|
{
|
|
/* `exit` or `@abort` has been called in the init block.
|
|
* this check is unique to hawk_rtx_callfun(). hawk_rtx_loop()
|
|
* doesn't have this check as invocation points of each block
|
|
* (begin, end, pattern-action) know how to deal with the
|
|
* exit level of the previous phase */
|
|
hawk_rtx_seterrfmt(rtx, HAWK_NULL, HAWK_EPERM, HAWK_T("terminated before actual call to '%.*js'"), fun->name.len, fun->name.ptr);
|
|
return HAWK_NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
pafv.args = args;
|
|
pafv.nargs = nargs;
|
|
pafv.argspec = fun->argspec;
|
|
pafv.argspeclen = fun->argspeclen;
|
|
|
|
/* forge a fake node containing a function call */
|
|
HAWK_MEMSET(&call, 0, HAWK_SIZEOF(call));
|
|
call.type = HAWK_NDE_FNCALL_FUN;
|
|
call.u.fun.name = fun->name;
|
|
call.nargs = nargs;
|
|
/* keep HAWK_NULL in call.args so that hawk_rtx_evalcall() knows it's a fake call structure */
|
|
|
|
/* now that the function is found and ok, let's execute it */
|
|
crdata.rtx = rtx;
|
|
crdata.val = HAWK_NULL;
|
|
|
|
v = hawk_rtx_evalcall(rtx, &call, fun, push_arg_from_vals, (void*)&pafv, capture_retval_on_exit, &crdata);
|
|
if (HAWK_UNLIKELY(!v))
|
|
{
|
|
/* an error occurred. let's check if it is caused by exit().
|
|
* if so, the return value should have been captured into
|
|
* crdata.val. */
|
|
if (crdata.val) v = crdata.val; /* yet it is */
|
|
}
|
|
else
|
|
{
|
|
/* the return value captured in termination by exit()
|
|
* is reference-counted up in capture_retval_on_exit().
|
|
* let's do the same thing for the return value normally
|
|
* returned. */
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
}
|
|
|
|
/* flush all buffered io data */
|
|
hawk_rtx_flushallios(rtx);
|
|
|
|
/* return the return value with its reference count at least 1.
|
|
* the caller of this function should count down its reference. */
|
|
return v;
|
|
}
|
|
|
|
/* call an AWK function by name */
|
|
hawk_val_t* hawk_rtx_callwithucstr (hawk_rtx_t* rtx, const hawk_uch_t* name, hawk_val_t* args[], hawk_oow_t nargs)
|
|
{
|
|
hawk_fun_t* fun;
|
|
|
|
fun = hawk_rtx_findfunwithucstr(rtx, name);
|
|
if (HAWK_UNLIKELY(!fun)) return HAWK_NULL;
|
|
|
|
return hawk_rtx_callfun(rtx, fun, args, nargs);
|
|
}
|
|
|
|
hawk_val_t* hawk_rtx_callwithbcstr (hawk_rtx_t* rtx, const hawk_bch_t* name, hawk_val_t* args[], hawk_oow_t nargs)
|
|
{
|
|
hawk_fun_t* fun;
|
|
|
|
fun = hawk_rtx_findfunwithbcstr(rtx, name);
|
|
if (HAWK_UNLIKELY(!fun)) return HAWK_NULL;
|
|
|
|
return hawk_rtx_callfun(rtx, fun, args, nargs);
|
|
}
|
|
|
|
hawk_val_t* hawk_rtx_callwithucstrarr (hawk_rtx_t* rtx, const hawk_uch_t* name, const hawk_uch_t* args[], hawk_oow_t nargs)
|
|
{
|
|
hawk_oow_t i;
|
|
hawk_val_t** v, * ret;
|
|
|
|
v = hawk_rtx_allocmem(rtx, HAWK_SIZEOF(*v) * nargs);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
|
|
for (i = 0; i < nargs; i++)
|
|
{
|
|
v[i] = hawk_rtx_makestrvalwithucstr(rtx, args[i]);
|
|
if (HAWK_UNLIKELY(!v[i]))
|
|
{
|
|
ret = HAWK_NULL;
|
|
goto oops;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, v[i]);
|
|
}
|
|
|
|
ret = hawk_rtx_callwithucstr(rtx, name, v, nargs);
|
|
|
|
oops:
|
|
while (i > 0) hawk_rtx_refdownval_inline(rtx, v[--i]);
|
|
hawk_rtx_freemem(rtx, v);
|
|
return ret;
|
|
}
|
|
|
|
hawk_val_t* hawk_rtx_callwithbcstrarr (hawk_rtx_t* rtx, const hawk_bch_t* name, const hawk_bch_t* args[], hawk_oow_t nargs)
|
|
{
|
|
hawk_oow_t i;
|
|
hawk_val_t** v, * ret;
|
|
|
|
v = hawk_rtx_allocmem(rtx, HAWK_SIZEOF(*v) * nargs);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
|
|
for (i = 0; i < nargs; i++)
|
|
{
|
|
v[i] = hawk_rtx_makestrvalwithbcstr(rtx, args[i]);
|
|
if (HAWK_UNLIKELY(!v[i]))
|
|
{
|
|
ret = HAWK_NULL;
|
|
goto oops;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, v[i]);
|
|
}
|
|
|
|
ret = hawk_rtx_callwithbcstr(rtx, name, v, nargs);
|
|
|
|
oops:
|
|
while (i > 0) hawk_rtx_refdownval_inline(rtx, v[--i]);
|
|
hawk_rtx_freemem(rtx, v);
|
|
return ret;
|
|
}
|
|
|
|
hawk_val_t* hawk_rtx_callwithooucstrarr (hawk_rtx_t* rtx, const hawk_ooch_t* name, const hawk_uch_t* args[], hawk_oow_t nargs)
|
|
{
|
|
hawk_oow_t i;
|
|
hawk_val_t** v, * ret;
|
|
|
|
v = hawk_rtx_allocmem(rtx, HAWK_SIZEOF(*v) * nargs);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
|
|
for (i = 0; i < nargs; i++)
|
|
{
|
|
v[i] = hawk_rtx_makestrvalwithucstr(rtx, args[i]);
|
|
if (HAWK_UNLIKELY(!v[i]))
|
|
{
|
|
ret = HAWK_NULL;
|
|
goto oops;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, v[i]);
|
|
}
|
|
|
|
ret = hawk_rtx_callwithoocstr(rtx, name, v, nargs);
|
|
|
|
oops:
|
|
while (i > 0) hawk_rtx_refdownval_inline(rtx, v[--i]);
|
|
hawk_rtx_freemem(rtx, v);
|
|
return ret;
|
|
}
|
|
|
|
hawk_val_t* hawk_rtx_callwithoobcstrarr (hawk_rtx_t* rtx, const hawk_ooch_t* name, const hawk_bch_t* args[], hawk_oow_t nargs)
|
|
{
|
|
hawk_oow_t i;
|
|
hawk_val_t** v, * ret;
|
|
|
|
v = hawk_rtx_allocmem(rtx, HAWK_SIZEOF(*v) * nargs);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
|
|
for (i = 0; i < nargs; i++)
|
|
{
|
|
v[i] = hawk_rtx_makestrvalwithbcstr(rtx, args[i]);
|
|
if (HAWK_UNLIKELY(!v[i]))
|
|
{
|
|
ret = HAWK_NULL;
|
|
goto oops;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, v[i]);
|
|
}
|
|
|
|
ret = hawk_rtx_callwithoocstr(rtx, name, v, nargs);
|
|
|
|
oops:
|
|
while (i > 0) hawk_rtx_refdownval_inline(rtx, v[--i]);
|
|
hawk_rtx_freemem(rtx, v);
|
|
return ret;
|
|
}
|
|
|
|
static int run_pblocks (hawk_rtx_t* rtx)
|
|
{
|
|
int n;
|
|
|
|
#define ADJUST_ERROR(rtx) \
|
|
if (rtx->hawk->tree.chain != HAWK_NULL) \
|
|
{ \
|
|
if (rtx->hawk->tree.chain->pattern != HAWK_NULL) \
|
|
ADJERR_LOC(rtx, &rtx->hawk->tree.chain->pattern->loc); \
|
|
else if (rtx->hawk->tree.chain->action != HAWK_NULL) \
|
|
ADJERR_LOC(rtx, &rtx->hawk->tree.chain->action->loc); \
|
|
} \
|
|
else if (rtx->hawk->tree.end != HAWK_NULL) \
|
|
{ \
|
|
ADJERR_LOC(rtx, &rtx->hawk->tree.end->loc); \
|
|
}
|
|
|
|
rtx->inrec.buf_pos = 0;
|
|
rtx->inrec.buf_len = 0;
|
|
rtx->inrec.eof = 0;
|
|
|
|
/* run each pattern block */
|
|
while (rtx->exit_level < EXIT_GLOBAL)
|
|
{
|
|
rtx->exit_level = EXIT_NONE;
|
|
|
|
n = read_record(rtx);
|
|
if (n <= -1)
|
|
{
|
|
ADJUST_ERROR(rtx);
|
|
return -1; /* error */
|
|
}
|
|
if (n == 0) break; /* end of input */
|
|
|
|
if (rtx->hawk->tree.chain)
|
|
{
|
|
if (run_pblock_chain(rtx, rtx->hawk->tree.chain) <= -1) return -1;
|
|
}
|
|
}
|
|
|
|
#undef ADJUST_ERROR
|
|
return 0;
|
|
}
|
|
|
|
static int run_pblock_chain (hawk_rtx_t* rtx, hawk_chain_t* chain)
|
|
{
|
|
hawk_oow_t bno = 0;
|
|
|
|
while (rtx->exit_level < EXIT_GLOBAL && chain)
|
|
{
|
|
if (rtx->exit_level == EXIT_NEXT)
|
|
{
|
|
rtx->exit_level = EXIT_NONE;
|
|
break;
|
|
}
|
|
|
|
if (run_pblock(rtx, chain, bno) <= -1) return -1;
|
|
|
|
chain = chain->next;
|
|
bno++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int run_pblock (hawk_rtx_t* rtx, hawk_chain_t* cha, hawk_oow_t bno)
|
|
{
|
|
hawk_nde_t* ptn;
|
|
hawk_nde_blk_t* blk;
|
|
|
|
ptn = cha->pattern;
|
|
blk = (hawk_nde_blk_t*)cha->action;
|
|
|
|
if (!ptn)
|
|
{
|
|
/* just execute the block */
|
|
HAWK_ASSERT(blk != HAWK_NULL); /* not possible to have no statement block */
|
|
rtx->active_block = blk;
|
|
if (run_block(rtx, blk) <= -1) return -1;
|
|
}
|
|
else
|
|
{
|
|
if (!ptn->next)
|
|
{
|
|
/* pattern { ... } */
|
|
hawk_val_t* v1;
|
|
|
|
v1 = eval_expression(rtx, ptn);
|
|
if (HAWK_UNLIKELY(!v1)) return -1;
|
|
|
|
hawk_rtx_refupval_inline(rtx, v1);
|
|
|
|
if (hawk_rtx_valtobool(rtx, v1))
|
|
{
|
|
int n;
|
|
rtx->active_block = blk; /* this may be null for a blockless pattern */
|
|
n = blk? run_block(rtx, blk): run_null_block_for_blockless_pattern(rtx);
|
|
if (n <= -1)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, v1);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, v1);
|
|
}
|
|
else
|
|
{
|
|
/* pattern, pattern { ... } */
|
|
HAWK_ASSERT(ptn->next->next == HAWK_NULL);
|
|
HAWK_ASSERT(rtx->pattern_range_state != HAWK_NULL);
|
|
|
|
if (rtx->pattern_range_state[bno] == 0)
|
|
{
|
|
hawk_val_t* v1;
|
|
|
|
v1 = eval_expression(rtx, ptn);
|
|
if (HAWK_UNLIKELY(!v1)) return -1;
|
|
hawk_rtx_refupval_inline(rtx, v1);
|
|
rtx->pattern_range_state[bno] = hawk_rtx_valtobool(rtx, v1);
|
|
hawk_rtx_refdownval_inline(rtx, v1);
|
|
}
|
|
|
|
if (rtx->pattern_range_state[bno] == 1)
|
|
{
|
|
hawk_val_t* v2;
|
|
|
|
v2 = eval_expression(rtx, ptn->next);
|
|
if (HAWK_UNLIKELY(!v2)) return -1;
|
|
hawk_rtx_refupval_inline(rtx, v2);
|
|
rtx->pattern_range_state[bno] = !hawk_rtx_valtobool(rtx, v2);
|
|
hawk_rtx_refdownval_inline(rtx, v2);
|
|
|
|
rtx->active_block = blk;
|
|
if (run_block(rtx, blk) <= -1) return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static HAWK_INLINE int run_null_block_for_blockless_pattern (hawk_rtx_t* rtx)
|
|
{
|
|
int n;
|
|
|
|
/* blockless pattern - execute print $0*/
|
|
hawk_rtx_refupval_inline(rtx, rtx->inrec.d0);
|
|
|
|
n = hawk_rtx_writeiostr(rtx, HAWK_OUT_CONSOLE, HAWK_T(""), HAWK_OOECS_PTR(&rtx->inrec.line), HAWK_OOECS_LEN(&rtx->inrec.line));
|
|
if (n <= -1) goto oops;
|
|
|
|
n = hawk_rtx_writeiostr(rtx, HAWK_OUT_CONSOLE, HAWK_T(""), rtx->gbl.ors.ptr, rtx->gbl.ors.len);
|
|
if (n <= -1) goto oops;
|
|
|
|
hawk_rtx_refdownval_inline(rtx, rtx->inrec.d0);
|
|
return 0;
|
|
|
|
oops:
|
|
hawk_rtx_refdownval_inline(rtx, rtx->inrec.d0);
|
|
/* ADJERR_LOC(rtx, &nde->loc); - can't adjust error location as nde is null */
|
|
return -1;
|
|
}
|
|
|
|
static HAWK_INLINE_ALWAYS int run_block0 (hawk_rtx_t* rtx, hawk_nde_blk_t* nde)
|
|
{
|
|
hawk_nde_t* p;
|
|
/*hawk_oow_t saved_stack_top;*/
|
|
int n = 0;
|
|
|
|
HAWK_ASSERT(nde != HAWK_NULL);
|
|
HAWK_ASSERT(nde->type == HAWK_NDE_BLK);
|
|
/*saved_stack_top = rtx->stack_top;*/
|
|
|
|
#if defined(DEBUG_RUN)
|
|
hawk_logbfmt(hawk_rtx_gethawk(rtx), "securing space for local variables nlcls = %d\n", (int)nde->nlcls);
|
|
#endif
|
|
|
|
if (nde->nlcls > 0)
|
|
{
|
|
hawk_oow_t tmp = nde->nlcls;
|
|
|
|
/* ensure sufficient space for local variables */
|
|
if (HAWK_UNLIKELY(HAWK_RTX_STACK_AVAIL(rtx) < tmp))
|
|
{
|
|
hawk_rtx_seterrbfmt(rtx, &nde->loc, HAWK_ESTACK,
|
|
"stack full(avail=%zu, limit=%zu) for %zu local variables",
|
|
HAWK_RTX_STACK_AVAIL(rtx), rtx->stack_limit, tmp);
|
|
return -1;
|
|
}
|
|
|
|
do
|
|
{
|
|
--tmp;
|
|
HAWK_RTX_STACK_PUSH(rtx, hawk_val_nil);
|
|
/* refupval_inline is not required for hawk_val_nil */
|
|
}
|
|
while (tmp > 0);
|
|
}
|
|
else if (nde->nlcls != nde->org_nlcls)
|
|
{
|
|
/* this part can be reached if:
|
|
* - the block is nested
|
|
* - the block declares local variables
|
|
* - the local variables have been migrated to the outer-most block by the parser.
|
|
*
|
|
* if the parser skips migrations, this part isn't reached but the normal push/pop
|
|
* will take place for a nested block with local variable declaration.
|
|
*/
|
|
hawk_oow_t tmp, end;
|
|
|
|
/* when the outer-most block has been entered, the space large enough for all local
|
|
* variables defined has been secured. nullify part of the stack to initialze local
|
|
* variables defined for a nested block */
|
|
end = nde->outer_nlcls + nde->org_nlcls;
|
|
for (tmp = nde->outer_nlcls; tmp < end; tmp++)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_LCL(rtx, tmp));
|
|
HAWK_RTX_STACK_LCL(rtx, tmp) = hawk_val_nil;
|
|
}
|
|
}
|
|
|
|
#if defined(DEBUG_RUN)
|
|
hawk_logbfmt(hawk_rtx_gethawk(rtx), "executing block statements\n");
|
|
#endif
|
|
|
|
p = nde->body;
|
|
while (p && rtx->exit_level == EXIT_NONE)
|
|
{
|
|
if (HAWK_UNLIKELY(run_statement(rtx, p) <= -1))
|
|
{
|
|
n = -1;
|
|
break;
|
|
}
|
|
p = p->next;
|
|
}
|
|
|
|
/* pop off local variables */
|
|
#if defined(DEBUG_RUN)
|
|
hawk_logbfmt(hawk_rtx_gethawk(rtx), "popping off local variables\n");
|
|
#endif
|
|
|
|
if (nde->nlcls > 0)
|
|
{
|
|
hawk_oow_t tmp = nde->nlcls;
|
|
|
|
do
|
|
{
|
|
--tmp;
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_LCL(rtx, tmp));
|
|
HAWK_RTX_STACK_POP(rtx);
|
|
}
|
|
while (tmp > 0);
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
static int run_block (hawk_rtx_t* rtx, hawk_nde_blk_t* nde)
|
|
{
|
|
int n;
|
|
|
|
if (rtx->hawk->opt.depth.s.block_run > 0 &&
|
|
rtx->depth.block >= rtx->hawk->opt.depth.s.block_run)
|
|
{
|
|
hawk_rtx_seterrbfmt(rtx, &nde->loc, HAWK_EBLKNST,
|
|
"run-time block depth(%zu) reached limit(%zu)",
|
|
rtx->depth.block, rtx->hawk->opt.depth.s.block_run);
|
|
return -1;
|
|
}
|
|
|
|
rtx->depth.block++;
|
|
n = run_block0(rtx, nde);
|
|
rtx->depth.block--;
|
|
|
|
return n;
|
|
}
|
|
|
|
/* ========================================================================= */
|
|
|
|
int hawk_rtx_raisesig(hawk_rtx_t* rtx, int sig)
|
|
{
|
|
if (sig < 0 || sig >= HAWK_COUNTOF(rtx->sig_handler))
|
|
{
|
|
hawk_rtx_seterrbfmt(rtx, HAWK_NULL, HAWK_EINVAL, "invalid signal number %d", sig);
|
|
return -1;
|
|
}
|
|
|
|
/* i assume the sys::signal() is called from the same thread as
|
|
* the main runtime context. so i don't use atomic or mutex on
|
|
* rtx->sig_handler. */
|
|
if (!rtx->sig_handler[sig])
|
|
{
|
|
hawk_rtx_seterrbfmt(rtx, HAWK_NULL, HAWK_EINVAL, "no handler set for signal number %d", sig);
|
|
return -1; /* no handler installed */
|
|
}
|
|
|
|
#if defined(HAWK_ENABLE_ATOMIC_SIG)
|
|
{
|
|
hawk_uintptr_t bit;
|
|
hawk_uintptr_t* word;
|
|
int widx;
|
|
|
|
widx = sig / HAWK_SIG_WORD_BITS;
|
|
bit = ((hawk_uintptr_t)1) << (sig % HAWK_SIG_WORD_BITS);
|
|
word = &rtx->sig_pending[widx];
|
|
(void)HAWK_ATOMIC_FETCH_OR(word, bit, HAWK_ATOMIC_RELAXED);
|
|
HAWK_ATOMIC_STORE(&rtx->sig_pending_any, 1, HAWK_ATOMIC_RELAXED);
|
|
}
|
|
#else
|
|
{
|
|
int pos;
|
|
hawk_mtx_lock(&rtx->sig_mtx, HAWK_NULL);
|
|
if (rtx->sig_raise[0].pos[sig] >= 0)
|
|
{
|
|
/* already raised */
|
|
hawk_mtx_unlock(&rtx->sig_mtx);
|
|
return 0;
|
|
}
|
|
pos = rtx->sig_raise[0].count;
|
|
rtx->sig_raise[0].pos[sig] = pos;
|
|
rtx->sig_raise[0].tab[pos] = sig;
|
|
rtx->sig_raise[0].count++;
|
|
#if defined(HAWK_ATOMIC_STORE)
|
|
HAWK_ATOMIC_STORE(&rtx->sig_pending_any, 1, HAWK_ATOMIC_RELAXED);
|
|
#else
|
|
rtx->sig_pending_any = 1;
|
|
#endif
|
|
hawk_mtx_unlock(&rtx->sig_mtx);
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hawk_rtx_setsighandler (hawk_rtx_t* rtx, int sig, hawk_fun_t* fun)
|
|
{
|
|
hawk_rtx_ecb_t* ecb, * ecb_next;
|
|
|
|
if (sig < 0 || sig >= HAWK_COUNTOF(rtx->sig_handler))
|
|
{
|
|
hawk_rtx_seterrbfmt(rtx, HAWK_NULL, HAWK_EINVAL, "invalid signal number %d", sig);
|
|
return -1;
|
|
}
|
|
|
|
/* TODO: anything to do for old handler */
|
|
|
|
/* i assume the sys::signal() is called from the same thread as
|
|
* the runtime context. so i don't use atomic or mutex on
|
|
* rtx->sig_handler. */
|
|
rtx->sig_handler[sig] = fun;
|
|
|
|
for (ecb = rtx->ecb; ecb != (hawk_rtx_ecb_t*)(rtx); ecb = ecb_next)
|
|
{
|
|
ecb_next = ecb->next_;
|
|
if (ecb->sigset) ecb->sigset(rtx, sig, fun);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(HAWK_ENABLE_ATOMIC_SIG)
|
|
static HAWK_INLINE int ctz_uintptr (hawk_uintptr_t v)
|
|
{
|
|
/* precondition - v != 0 */
|
|
#if defined(HAWK_HAVE_BUILTIN_CTZL) && (HAWK_SIZEOF_UINTPTR_T <= HAWK_SIZEOF_LONG)
|
|
return (int)__builtin_ctzl((unsigned long)v);
|
|
#elif defined(HAWK_HAVE_BUILTIN_CTZLL) && (HAWK_SIZEOF_UINTPTR_T <= HAWK_SIZEOF_LONG_LONG)
|
|
return (int)__builtin_ctzll((unsigned long long)v);
|
|
#else
|
|
int c = 0;
|
|
while ((v & 1) == 0) { v >>= 1; c++; }
|
|
return c;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
static int call_signal_handlers (hawk_rtx_t* rtx)
|
|
{
|
|
int i;
|
|
|
|
rtx->sig_handling = 1;
|
|
|
|
#if defined(HAWK_ENABLE_ATOMIC_SIG)
|
|
HAWK_ATOMIC_STORE(&rtx->sig_pending_any, 0, HAWK_ATOMIC_RELAXED);
|
|
|
|
for (i = 0; i < HAWK_SIG_WORD_COUNT; i++)
|
|
{
|
|
hawk_uintptr_t bits;
|
|
|
|
bits = HAWK_ATOMIC_EXCHANGE(&rtx->sig_pending[i], 0, HAWK_ATOMIC_ACQ_REL);
|
|
while (bits)
|
|
{
|
|
int sig;
|
|
hawk_fun_t* fun;
|
|
hawk_val_t* arg;
|
|
hawk_val_t* rv;
|
|
int bitpos;
|
|
|
|
bitpos = ctz_uintptr(bits);
|
|
sig = (i * HAWK_SIG_WORD_BITS) + bitpos;
|
|
if (sig < 0 || sig >= HAWK_NSIG)
|
|
{
|
|
bits &= (bits - 1);
|
|
continue;
|
|
}
|
|
|
|
fun = rtx->sig_handler[sig];
|
|
if (fun)
|
|
{
|
|
arg = hawk_rtx_makeintval_inline(rtx, sig);
|
|
if (HAWK_UNLIKELY(!arg)) return -1;
|
|
|
|
rv = hawk_rtx_callfun(rtx, fun, &arg, 1);
|
|
if (HAWK_UNLIKELY(!rv)) return -1;
|
|
|
|
hawk_rtx_refdownval_inline(rtx, rv);
|
|
}
|
|
|
|
bits &= (bits - 1);
|
|
}
|
|
}
|
|
#else
|
|
hawk_mtx_lock(&rtx->sig_mtx, HAWK_NULL);
|
|
#if defined(HAWK_ATOMIC_LOAD)
|
|
if (HAWK_ATOMIC_LOAD(&rtx->sig_pending_any, HAWK_ATOMIC_RELAXED) != 0)
|
|
#else
|
|
if (rtx->sig_pending_any)
|
|
#endif
|
|
{
|
|
/* copy the signal raising state */
|
|
rtx->sig_raise[1] = rtx->sig_raise[0];
|
|
#if defined(HAWK_ATOMIC_STORE)
|
|
HAWK_ATOMIC_STORE(&rtx->sig_pending_any, 0, HAWK_ATOMIC_RELAXED);
|
|
#else
|
|
rtx->sig_pending_any = 0;
|
|
#endif
|
|
|
|
/* reset the signal raising state modified by hawk_rtx_raisesig() */
|
|
rtx->sig_raise[0].count = 0;
|
|
for (i = 0; i < HAWK_COUNTOF(rtx->sig_raise[0].pos); i++) rtx->sig_raise[0].pos[i] = -1;
|
|
|
|
hawk_mtx_unlock(&rtx->sig_mtx);
|
|
|
|
for (i = 0; i < rtx->sig_raise[1].count; i++)
|
|
{
|
|
int sig;
|
|
hawk_fun_t* fun;
|
|
|
|
sig = rtx->sig_raise[1].tab[i];
|
|
fun = rtx->sig_handler[sig];
|
|
if (fun)
|
|
{
|
|
hawk_val_t* arg;
|
|
hawk_val_t* rv;
|
|
|
|
arg = hawk_rtx_makeintval_inline(rtx, sig);
|
|
if (HAWK_UNLIKELY(!arg)) return -1;
|
|
|
|
/* hawk_rtx_callfun() can invoke run_statement() again. if the signal
|
|
* handler is in progress, this function must not be invoked again. */
|
|
rv = hawk_rtx_callfun(rtx, fun, &arg, 1);
|
|
if (HAWK_UNLIKELY(!rv)) return -1;
|
|
|
|
hawk_rtx_refdownval_inline(rtx, rv);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
rtx->sig_handling = 0;
|
|
return 0;
|
|
}
|
|
|
|
/* ========================================================================= */
|
|
|
|
#define ON_STATEMENT(rtx,nde) do { \
|
|
if ((rtx)->hawk->haltall) (rtx)->exit_level = EXIT_ABORT; \
|
|
if (rtx->ecb_stmt_count > 0) { \
|
|
hawk_rtx_ecb_t* ecb, * ecb_next; \
|
|
for (ecb = (rtx)->ecb; ecb != (hawk_rtx_ecb_t*)(rtx); ecb = ecb_next) { \
|
|
ecb_next = ecb->next_; \
|
|
if (ecb->stmt) ecb->stmt(rtx, nde, ecb->ctx); \
|
|
} \
|
|
} \
|
|
} while(0)
|
|
|
|
static HAWK_INLINE int run_pending_signal_handlers (hawk_rtx_t* rtx)
|
|
{
|
|
if (rtx->sig_handling) return 0;
|
|
|
|
/* run singal handlers if there are raised signals */
|
|
#if defined(HAWK_ATOMIC_LOAD)
|
|
if (HAWK_ATOMIC_LOAD(&rtx->sig_pending_any, HAWK_ATOMIC_RELAXED) != 0 &&
|
|
call_signal_handlers(rtx) <= -1) return -1;
|
|
#else
|
|
/* NOTE:
|
|
* rtx->sig_pending_any accessed non-atomically without mutex concurrently
|
|
* from multiple threads(e.g. hawk_rtx_raisesig() called from a different
|
|
* thread) is undefined behavior. but ignore this path as i believe most
|
|
* modern compilers should support the atomic primitives. */
|
|
if (rtx->sig_pending_any && call_signal_handlers(rtx) <= -1) return -1;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
enum exec_state_t
|
|
{
|
|
EXEC_STATE_ENTER = 0,
|
|
EXEC_STATE_BLK_STEP,
|
|
EXEC_STATE_BLK_LEAVE,
|
|
EXEC_STATE_WHILE_BODY_DONE,
|
|
EXEC_STATE_DOWHILE_TEST,
|
|
EXEC_STATE_FOR_BODY_DONE,
|
|
EXEC_STATE_FORIN_STEP,
|
|
EXEC_STATE_FORIN_BODY_DONE,
|
|
EXEC_STATE_FORIN_LEAVE
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
/* AFTER_EVAL states: statement resumes after eframe expression evaluation */
|
|
,EXEC_STATE_RETURN_AFTER_EVAL /* return: expression done, set retval */
|
|
,EXEC_STATE_EXIT_AFTER_EVAL /* exit: expression done, set global retval */
|
|
,EXEC_STATE_EXPRST_AFTER_EVAL /* expression statement: result discarded */
|
|
,EXEC_STATE_IF_AFTER_EVAL /* if: test done, push branch */
|
|
,EXEC_STATE_WHILE_TEST_AFTER_EVAL /* while: test done */
|
|
,EXEC_STATE_DOWHILE_TEST_AFTER_EVAL /* do-while: test done */
|
|
,EXEC_STATE_FOR_INIT_AFTER_EVAL /* for: init expr done */
|
|
,EXEC_STATE_FOR_COND_AFTER_EVAL /* for: cond expr done */
|
|
,EXEC_STATE_FOR_INCR_AFTER_EVAL /* for: incr expr done */
|
|
#endif
|
|
};
|
|
|
|
static int push_exec_stack (hawk_rtx_t* rtx, int state, hawk_nde_t* nde)
|
|
{
|
|
if (rtx->exec_stack_size >= rtx->exec_stack_limit)
|
|
{
|
|
hawk_oow_t new_limit;
|
|
hawk_exec_stack_t* tmp;
|
|
new_limit = rtx->exec_stack_limit + 1;
|
|
new_limit = HAWK_ALIGN_POW2(new_limit, 64); /* TODO: change align factor */
|
|
tmp = hawk_rtx_reallocmem(rtx, rtx->exec_stack, HAWK_SIZEOF(*tmp) * new_limit);
|
|
if (HAWK_UNLIKELY(!tmp)) return -1;
|
|
rtx->exec_stack = tmp;
|
|
rtx->exec_stack_limit = new_limit;
|
|
}
|
|
rtx->exec_stack[rtx->exec_stack_size].state = state;
|
|
rtx->exec_stack[rtx->exec_stack_size].nde = nde;
|
|
rtx->exec_stack[rtx->exec_stack_size].ptr = HAWK_NULL;
|
|
rtx->exec_stack[rtx->exec_stack_size].iv = 0;
|
|
rtx->exec_stack[rtx->exec_stack_size].base = 0;
|
|
rtx->exec_stack_size++;
|
|
return 0;
|
|
}
|
|
|
|
static HAWK_INLINE_ALWAYS int push_exec_stack2 (hawk_rtx_t* rtx, int state, hawk_nde_t* nde, hawk_nde_t* ptr)
|
|
{
|
|
int n;
|
|
|
|
n = push_exec_stack(rtx, state, nde);
|
|
if (n <= -1) return -1;
|
|
rtx->exec_stack[rtx->exec_stack_size - 1].ptr = ptr;
|
|
return 0;
|
|
}
|
|
|
|
static HAWK_INLINE_ALWAYS int push_exec_stack3 (hawk_rtx_t* rtx, int state, hawk_nde_t* nde, hawk_oow_t iv, hawk_oow_t base)
|
|
{
|
|
int n;
|
|
|
|
n = push_exec_stack(rtx, state, nde);
|
|
if (n <= -1) return -1;
|
|
rtx->exec_stack[rtx->exec_stack_size - 1].iv = iv;
|
|
rtx->exec_stack[rtx->exec_stack_size - 1].base = base;
|
|
return 0;
|
|
}
|
|
|
|
/* TODO: return the item pointer? */
|
|
static HAWK_INLINE_ALWAYS hawk_exec_stack_t* pop_exec_stack (hawk_rtx_t* rtx, hawk_exec_stack_t* es)
|
|
{
|
|
if (rtx->exec_stack_size <= 0) return HAWK_NULL;
|
|
*es = rtx->exec_stack[--rtx->exec_stack_size];
|
|
return es;
|
|
}
|
|
|
|
static void leave_block_iterative (hawk_rtx_t* rtx, hawk_nde_blk_t* nde)
|
|
{
|
|
if (nde->nlcls > 0)
|
|
{
|
|
hawk_oow_t tmp = nde->nlcls;
|
|
|
|
do
|
|
{
|
|
--tmp;
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_LCL(rtx, tmp));
|
|
HAWK_RTX_STACK_POP(rtx);
|
|
}
|
|
while (tmp > 0);
|
|
}
|
|
|
|
#if 0
|
|
HAWK_ASSERT(rtx->depth.block > 0);
|
|
rtx->depth.block--;
|
|
#endif
|
|
}
|
|
|
|
static void leave_forin_iterative (hawk_rtx_t* rtx, hawk_oow_t base)
|
|
{
|
|
while (rtx->forin.size > base)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, rtx->forin.ptr[--rtx->forin.size]);
|
|
}
|
|
}
|
|
|
|
static void unwind_exec_stack (hawk_rtx_t* rtx, hawk_oow_t base)
|
|
{
|
|
while (rtx->exec_stack_size > base)
|
|
{
|
|
hawk_exec_stack_t es;
|
|
|
|
es = rtx->exec_stack[--rtx->exec_stack_size];
|
|
if (es.state == EXEC_STATE_BLK_LEAVE)
|
|
{
|
|
leave_block_iterative(rtx, (hawk_nde_blk_t*)es.nde);
|
|
}
|
|
else if (es.state == EXEC_STATE_FORIN_LEAVE)
|
|
{
|
|
leave_forin_iterative(rtx, es.base);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
static int push_call_frame (hawk_rtx_t* rtx, hawk_nde_fncall_t* call, hawk_fun_t* fun,
|
|
hawk_oow_t saved_stack_top, hawk_oow_t saved_stack_base, hawk_oow_t nargs)
|
|
{
|
|
if (rtx->call_stack_size >= rtx->call_stack_limit)
|
|
{
|
|
hawk_call_frame_t* tmp;
|
|
hawk_oow_t new_limit = rtx->call_stack_limit + 64;
|
|
tmp = (hawk_call_frame_t*)hawk_rtx_reallocmem(rtx, rtx->call_stack, HAWK_SIZEOF(*tmp) * new_limit);
|
|
if (HAWK_UNLIKELY(!tmp)) return -1;
|
|
rtx->call_stack = tmp;
|
|
rtx->call_stack_limit = new_limit;
|
|
}
|
|
rtx->call_stack[rtx->call_stack_size].call = call;
|
|
rtx->call_stack[rtx->call_stack_size].fun = fun;
|
|
rtx->call_stack[rtx->call_stack_size].saved_stack_top = saved_stack_top;
|
|
rtx->call_stack[rtx->call_stack_size].saved_stack_base = saved_stack_base;
|
|
rtx->call_stack[rtx->call_stack_size].nargs = nargs;
|
|
rtx->call_stack_size++;
|
|
return 0;
|
|
}
|
|
|
|
/* unwind call frames above base, restoring AWK stack state for each */
|
|
static void unwind_call_stack_to (hawk_rtx_t* rtx, hawk_oow_t base)
|
|
{
|
|
while (rtx->call_stack_size > base)
|
|
{
|
|
hawk_call_frame_t* cf = &rtx->call_stack[--rtx->call_stack_size];
|
|
/* restore the AWK value stack to its state before the call */
|
|
while (rtx->stack_top > cf->saved_stack_top)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, rtx->stack[rtx->stack_top - 1]);
|
|
HAWK_RTX_STACK_POP(rtx);
|
|
}
|
|
rtx->stack_base = cf->saved_stack_base;
|
|
}
|
|
}
|
|
#endif /* HAWK_ENABLE_XSTACK_EVAL */
|
|
|
|
static int enter_block_iterative (hawk_rtx_t* rtx, hawk_nde_blk_t* nde)
|
|
{
|
|
#if 0
|
|
if (rtx->hawk->opt.depth.s.block_run > 0 &&
|
|
rtx->depth.block >= rtx->hawk->opt.depth.s.block_run)
|
|
{
|
|
hawk_rtx_seterrbfmt(rtx, &nde->loc, HAWK_EBLKNST,
|
|
"run-time block depth(%zu) reached limit(%zu)",
|
|
rtx->depth.block, rtx->hawk->opt.depth.s.block_run);
|
|
return -1;
|
|
}
|
|
|
|
rtx->depth.block++;
|
|
#endif
|
|
|
|
if (nde->nlcls > 0)
|
|
{
|
|
hawk_oow_t tmp = nde->nlcls;
|
|
|
|
if (HAWK_UNLIKELY(HAWK_RTX_STACK_AVAIL(rtx) < tmp))
|
|
{
|
|
#if 0
|
|
rtx->depth.block--;
|
|
#endif
|
|
hawk_rtx_seterrbfmt(rtx, &nde->loc, HAWK_ESTACK,
|
|
"stack full(avail=%zu, limit=%zu) for %zu local variables",
|
|
HAWK_RTX_STACK_AVAIL(rtx), rtx->stack_limit, tmp);
|
|
return -1;
|
|
}
|
|
|
|
do
|
|
{
|
|
--tmp;
|
|
HAWK_RTX_STACK_PUSH(rtx, hawk_val_nil);
|
|
}
|
|
while (tmp > 0);
|
|
}
|
|
else if (nde->nlcls != nde->org_nlcls)
|
|
{
|
|
hawk_oow_t tmp, end;
|
|
|
|
end = nde->outer_nlcls + nde->org_nlcls;
|
|
for (tmp = nde->outer_nlcls; tmp < end; tmp++)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_LCL(rtx, tmp));
|
|
HAWK_RTX_STACK_LCL(rtx, tmp) = hawk_val_nil;
|
|
}
|
|
}
|
|
|
|
if (push_exec_stack(rtx, EXEC_STATE_BLK_LEAVE, (hawk_nde_t*)nde) <= -1)
|
|
{
|
|
leave_block_iterative(rtx, nde);
|
|
return -1;
|
|
}
|
|
|
|
if (nde->body &&
|
|
push_exec_stack2(rtx, EXEC_STATE_BLK_STEP, (hawk_nde_t*)nde, nde->body) <= -1)
|
|
{
|
|
rtx->exec_stack_size--;
|
|
leave_block_iterative(rtx, nde);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int run_statement0 (hawk_rtx_t* rtx, const hawk_exec_stack_t* es)
|
|
{
|
|
int xret = 0;
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
/* nothing */
|
|
#else
|
|
hawk_val_t* tmp;
|
|
#endif
|
|
int state;
|
|
hawk_nde_t* nde;
|
|
|
|
state = es->state;
|
|
nde = es->nde;
|
|
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
/* AFTER_EVAL continuations: expression result is in driver_eval_result */
|
|
switch (state)
|
|
{
|
|
case EXEC_STATE_EXPRST_AFTER_EVAL:
|
|
/* expression statement: result was evaluated for side effects; discard */
|
|
if (rtx->driver_eval_result)
|
|
{
|
|
hawk_rtx_refupval_inline(rtx, rtx->driver_eval_result);
|
|
hawk_rtx_refdownval_inline(rtx, rtx->driver_eval_result);
|
|
rtx->driver_eval_result = HAWK_NULL;
|
|
}
|
|
return 0;
|
|
|
|
case EXEC_STATE_RETURN_AFTER_EVAL:
|
|
{
|
|
hawk_val_t* val = rtx->driver_eval_result;
|
|
rtx->driver_eval_result = HAWK_NULL;
|
|
if (val)
|
|
{
|
|
hawk_nde_return_t* rnde = (hawk_nde_return_t*)nde;
|
|
if (!(rtx->hawk->opt.trait & HAWK_FLEXMAP))
|
|
{
|
|
hawk_val_type_t vtype = HAWK_RTX_GETVALTYPE(rtx, val);
|
|
if (vtype == HAWK_VAL_MAP || vtype == HAWK_VAL_ARR)
|
|
{
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
hawk_rtx_refdownval_inline(rtx, val);
|
|
hawk_rtx_seterrnum(rtx, &rnde->loc, HAWK_ENONSCARET);
|
|
return -1;
|
|
}
|
|
}
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_RETVAL(rtx));
|
|
HAWK_RTX_STACK_RETVAL(rtx) = val;
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
}
|
|
rtx->exit_level = EXIT_FUNCTION;
|
|
return 0;
|
|
}
|
|
|
|
case EXEC_STATE_EXIT_AFTER_EVAL:
|
|
{
|
|
hawk_val_t* val = rtx->driver_eval_result;
|
|
rtx->driver_eval_result = HAWK_NULL;
|
|
if (val)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_RETVAL_GBL(rtx));
|
|
HAWK_RTX_STACK_RETVAL_GBL(rtx) = val;
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
}
|
|
rtx->exit_level = (((hawk_nde_exit_t*)nde)->abort)? EXIT_ABORT: EXIT_GLOBAL;
|
|
return 0;
|
|
}
|
|
|
|
case EXEC_STATE_IF_AFTER_EVAL:
|
|
{
|
|
hawk_nde_if_t* nde_if = (hawk_nde_if_t*)nde;
|
|
hawk_val_t* test = rtx->driver_eval_result;
|
|
int bval;
|
|
rtx->driver_eval_result = HAWK_NULL;
|
|
/* test may be HAWK_NULL if eval produced exit signal; treat as false */
|
|
bval = test? hawk_rtx_valtobool(rtx, test): 0;
|
|
if (bval)
|
|
xret = push_exec_stack(rtx, EXEC_STATE_ENTER, nde_if->then_part);
|
|
else if (nde_if->else_part)
|
|
xret = push_exec_stack(rtx, EXEC_STATE_ENTER, nde_if->else_part);
|
|
return xret;
|
|
}
|
|
|
|
case EXEC_STATE_WHILE_TEST_AFTER_EVAL:
|
|
{
|
|
hawk_nde_while_t* wnde = (hawk_nde_while_t*)nde;
|
|
hawk_val_t* test = rtx->driver_eval_result;
|
|
int bval;
|
|
rtx->driver_eval_result = HAWK_NULL;
|
|
bval = test? hawk_rtx_valtobool(rtx, test): 0;
|
|
if (bval)
|
|
{
|
|
if (push_exec_stack(rtx, EXEC_STATE_WHILE_BODY_DONE, (hawk_nde_t*)wnde) <= -1 ||
|
|
push_exec_stack(rtx, EXEC_STATE_ENTER, wnde->body) <= -1) return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case EXEC_STATE_DOWHILE_TEST_AFTER_EVAL:
|
|
{
|
|
hawk_nde_while_t* wnde = (hawk_nde_while_t*)nde;
|
|
hawk_val_t* test = rtx->driver_eval_result;
|
|
int bval;
|
|
rtx->driver_eval_result = HAWK_NULL;
|
|
bval = test? hawk_rtx_valtobool(rtx, test): 0;
|
|
if (bval)
|
|
{
|
|
if (push_exec_stack(rtx, EXEC_STATE_DOWHILE_TEST, (hawk_nde_t*)wnde) <= -1 ||
|
|
push_exec_stack(rtx, EXEC_STATE_ENTER, wnde->body) <= -1) return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case EXEC_STATE_FOR_INIT_AFTER_EVAL:
|
|
{
|
|
/* init expr done; discard result, then push cond or body */
|
|
hawk_nde_for_t* fnde = (hawk_nde_for_t*)nde;
|
|
if (rtx->driver_eval_result)
|
|
{
|
|
hawk_rtx_refupval_inline(rtx, rtx->driver_eval_result);
|
|
hawk_rtx_refdownval_inline(rtx, rtx->driver_eval_result);
|
|
rtx->driver_eval_result = HAWK_NULL;
|
|
}
|
|
if (fnde->test)
|
|
{
|
|
ON_STATEMENT(rtx, fnde->test);
|
|
if (push_exec_stack(rtx, EXEC_STATE_FOR_COND_AFTER_EVAL, (hawk_nde_t*)fnde) <= -1) return -1;
|
|
return push_eframe(rtx, HAWK_EF_EVAL, fnde->test);
|
|
}
|
|
if (push_exec_stack(rtx, EXEC_STATE_FOR_BODY_DONE, (hawk_nde_t*)fnde) <= -1 ||
|
|
push_exec_stack(rtx, EXEC_STATE_ENTER, fnde->body) <= -1) return -1;
|
|
return 0;
|
|
}
|
|
|
|
case EXEC_STATE_FOR_COND_AFTER_EVAL:
|
|
{
|
|
hawk_nde_for_t* fnde = (hawk_nde_for_t*)nde;
|
|
hawk_val_t* test = rtx->driver_eval_result;
|
|
int bval;
|
|
rtx->driver_eval_result = HAWK_NULL;
|
|
bval = test? hawk_rtx_valtobool(rtx, test): 0;
|
|
if (bval)
|
|
{
|
|
if (push_exec_stack(rtx, EXEC_STATE_FOR_BODY_DONE, (hawk_nde_t*)fnde) <= -1 ||
|
|
push_exec_stack(rtx, EXEC_STATE_ENTER, fnde->body) <= -1) return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case EXEC_STATE_FOR_INCR_AFTER_EVAL:
|
|
{
|
|
/* incr expr done; discard, then push cond or body */
|
|
hawk_nde_for_t* fnde = (hawk_nde_for_t*)nde;
|
|
if (rtx->driver_eval_result)
|
|
{
|
|
hawk_rtx_refupval_inline(rtx, rtx->driver_eval_result);
|
|
hawk_rtx_refdownval_inline(rtx, rtx->driver_eval_result);
|
|
rtx->driver_eval_result = HAWK_NULL;
|
|
}
|
|
if (fnde->test)
|
|
{
|
|
ON_STATEMENT(rtx, fnde->test);
|
|
if (push_exec_stack(rtx, EXEC_STATE_FOR_COND_AFTER_EVAL, (hawk_nde_t*)fnde) <= -1) return -1;
|
|
return push_eframe(rtx, HAWK_EF_EVAL, fnde->test);
|
|
}
|
|
if (push_exec_stack(rtx, EXEC_STATE_FOR_BODY_DONE, (hawk_nde_t*)fnde) <= -1 ||
|
|
push_exec_stack(rtx, EXEC_STATE_ENTER, fnde->body) <= -1) return -1;
|
|
return 0;
|
|
}
|
|
|
|
default:
|
|
break; /* fall through to the main dispatch below */
|
|
}
|
|
#endif /* HAWK_ENABLE_XSTACK_EVAL */
|
|
|
|
if (state == EXEC_STATE_ENTER)
|
|
{
|
|
ON_STATEMENT(rtx, nde);
|
|
if (run_pending_signal_handlers(rtx) <= -1) return -1;
|
|
}
|
|
|
|
/* run the actual statement */
|
|
switch (nde->type)
|
|
{
|
|
case HAWK_NDE_NULL:
|
|
/* do nothing */
|
|
xret = 0;
|
|
break;
|
|
|
|
case HAWK_NDE_BLK:
|
|
if (state == EXEC_STATE_BLK_LEAVE)
|
|
{
|
|
leave_block_iterative(rtx, (hawk_nde_blk_t*)nde);
|
|
xret = 0;
|
|
}
|
|
else if (state == EXEC_STATE_BLK_STEP)
|
|
{
|
|
hawk_nde_t* stmt;
|
|
|
|
if (rtx->exit_level != EXIT_NONE)
|
|
{
|
|
xret = 0;
|
|
break;
|
|
}
|
|
|
|
stmt = es->ptr;
|
|
HAWK_ASSERT(stmt != HAWK_NULL);
|
|
|
|
if (stmt->next &&
|
|
push_exec_stack2(rtx, EXEC_STATE_BLK_STEP, nde, stmt->next) <= -1) return -1;
|
|
if (push_exec_stack(rtx, EXEC_STATE_ENTER, stmt) <= -1) return -1;
|
|
xret = 0;
|
|
}
|
|
else
|
|
{
|
|
xret = enter_block_iterative(rtx, (hawk_nde_blk_t*)nde);
|
|
}
|
|
break;
|
|
|
|
case HAWK_NDE_IF:
|
|
xret = run_if(rtx, (hawk_nde_if_t*)nde);
|
|
break;
|
|
|
|
case HAWK_NDE_SWITCH:
|
|
xret = run_switch(rtx, es, (hawk_nde_switch_t*)nde);
|
|
break;
|
|
|
|
case HAWK_NDE_CASE:
|
|
/* this must never happen - run_swtich must handle this part */
|
|
HAWK_ASSERT(!"should never happen - HAWK_NDE_CASE must not be fed to run_statement");
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_EINTERN);
|
|
xret = -1;
|
|
break;
|
|
|
|
case HAWK_NDE_WHILE:
|
|
case HAWK_NDE_DOWHILE:
|
|
xret = run_while(rtx, state, (hawk_nde_while_t*)nde);
|
|
break;
|
|
|
|
case HAWK_NDE_FOR:
|
|
xret = run_for(rtx, state, (hawk_nde_for_t*)nde);
|
|
break;
|
|
|
|
case HAWK_NDE_FORIN:
|
|
xret = run_forin(rtx, es, (hawk_nde_forin_t*)nde);
|
|
break;
|
|
|
|
case HAWK_NDE_BREAK:
|
|
xret = run_break(rtx, (hawk_nde_break_t*)nde);
|
|
break;
|
|
|
|
case HAWK_NDE_CONTINUE:
|
|
xret = run_continue(rtx, (hawk_nde_continue_t*)nde);
|
|
break;
|
|
|
|
case HAWK_NDE_RETURN:
|
|
xret = run_return(rtx, (hawk_nde_return_t*)nde);
|
|
break;
|
|
|
|
case HAWK_NDE_EXIT:
|
|
xret = run_exit(rtx, (hawk_nde_exit_t*)nde);
|
|
break;
|
|
|
|
case HAWK_NDE_NEXT:
|
|
xret = run_next(rtx, (hawk_nde_next_t*)nde);
|
|
break;
|
|
|
|
case HAWK_NDE_NEXTFILE:
|
|
xret = run_nextfile(rtx, (hawk_nde_nextfile_t*)nde);
|
|
break;
|
|
|
|
case HAWK_NDE_DELETE:
|
|
xret = run_delete(rtx, (hawk_nde_delete_t*)nde);
|
|
break;
|
|
|
|
case HAWK_NDE_RESET:
|
|
xret = run_reset(rtx, (hawk_nde_reset_t*)nde);
|
|
break;
|
|
|
|
case HAWK_NDE_PRINT:
|
|
if (rtx->hawk->opt.trait & HAWK_TOLERANT) goto __fallback__;
|
|
xret = run_print(rtx, (hawk_nde_print_t*)nde);
|
|
break;
|
|
|
|
case HAWK_NDE_PRINTF:
|
|
if (rtx->hawk->opt.trait & HAWK_TOLERANT) goto __fallback__;
|
|
xret = run_printf(rtx, (hawk_nde_print_t*)nde);
|
|
break;
|
|
|
|
default:
|
|
__fallback__:
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
/* push EXPRST_AFTER_EVAL exec frame; result discarded on return */
|
|
if (push_exec_stack(rtx, EXEC_STATE_EXPRST_AFTER_EVAL, nde) <= -1) return -1;
|
|
xret = push_eframe(rtx, HAWK_EF_EVAL, nde);
|
|
#else
|
|
tmp = eval_expression(rtx, nde);
|
|
if (HAWK_UNLIKELY(!tmp)) xret = -1;
|
|
else
|
|
{
|
|
/* destroy the value if not referenced */
|
|
hawk_rtx_refupval_inline(rtx, tmp);
|
|
hawk_rtx_refdownval_inline(rtx, tmp);
|
|
xret = 0;
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
/* clear the error if returning success.
|
|
* the handler for HAWK_EF_FNCALL_FUN_AWAIT in process_eframes()
|
|
* checks the error code to determine the success or failure of
|
|
* the function awaited. */
|
|
if (xret >= 0) hawk_rtx_clrerror(rtx);
|
|
#endif
|
|
return xret;
|
|
}
|
|
|
|
static int run_statement (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_oow_t exec_base;
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
hawk_oow_t eframe_base;
|
|
#else
|
|
int xret = 0;
|
|
hawk_exec_stack_t es;
|
|
#endif
|
|
|
|
HAWK_ASSERT(nde != HAWK_NULL);
|
|
exec_base = rtx->exec_stack_size;
|
|
|
|
if (push_exec_stack(rtx, EXEC_STATE_ENTER, nde) <= -1) return -1;
|
|
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
eframe_base = rtx->eframe_stack_size;
|
|
return run_driver(rtx, exec_base, eframe_base);
|
|
#else
|
|
while (1)
|
|
{
|
|
if (!pop_exec_stack(rtx, &es)) break;
|
|
|
|
xret = run_statement0(rtx, &es);
|
|
if (xret <= -1)
|
|
{
|
|
unwind_exec_stack(rtx, exec_base);
|
|
break;
|
|
}
|
|
|
|
if (rtx->exec_stack_size == exec_base) break;
|
|
}
|
|
|
|
return xret;
|
|
#endif
|
|
}
|
|
|
|
static int run_if (hawk_rtx_t* rtx, hawk_nde_if_t* nde)
|
|
{
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
HAWK_ASSERT(nde->test->next == HAWK_NULL);
|
|
if (push_exec_stack(rtx, EXEC_STATE_IF_AFTER_EVAL, (hawk_nde_t*)nde) <= -1) return -1;
|
|
return push_eframe(rtx, HAWK_EF_EVAL, nde->test);
|
|
#else
|
|
hawk_val_t* test;
|
|
int n = 0;
|
|
|
|
/* the test expression for the if statement cannot have
|
|
* chained expressions. this should not be allowed by the
|
|
* parser first of all */
|
|
HAWK_ASSERT(nde->test->next == HAWK_NULL);
|
|
|
|
test = eval_expression(rtx, nde->test);
|
|
if (HAWK_UNLIKELY(!test)) return -1;
|
|
|
|
hawk_rtx_refupval_inline(rtx, test);
|
|
if (hawk_rtx_valtobool(rtx, test))
|
|
{
|
|
n = push_exec_stack(rtx, EXEC_STATE_ENTER, nde->then_part);
|
|
}
|
|
else if (nde->else_part)
|
|
{
|
|
n = push_exec_stack(rtx, EXEC_STATE_ENTER, nde->else_part);
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, test);
|
|
return n;
|
|
#endif
|
|
}
|
|
|
|
static int run_switch (hawk_rtx_t* rtx, const hawk_exec_stack_t* es, hawk_nde_switch_t* nde)
|
|
{
|
|
hawk_val_t* test;
|
|
hawk_val_t* v;
|
|
hawk_nde_case_t* case_part;
|
|
hawk_nde_case_t* default_part;
|
|
int eval_true = 0;
|
|
int ret = 0;
|
|
|
|
(void)es;
|
|
|
|
/* the expression for the switch statement cannot have
|
|
* chained expressions. this should not be allowed by the
|
|
* parser first of all */
|
|
HAWK_ASSERT(nde->type == HAWK_NDE_SWITCH);
|
|
HAWK_ASSERT(nde->test->next == HAWK_NULL);
|
|
|
|
test = eval_expression(rtx, nde->test);
|
|
if (HAWK_UNLIKELY(!test)) return -1;
|
|
|
|
hawk_rtx_refupval_inline(rtx, test);
|
|
|
|
case_part = (hawk_nde_case_t*)nde->case_part;
|
|
default_part = (hawk_nde_case_t*)nde->default_part;
|
|
|
|
while (case_part)
|
|
{
|
|
int x;
|
|
|
|
if (eval_true) goto skip_eval;
|
|
if (case_part->val)
|
|
{
|
|
v = eval_expression(rtx, case_part->val);
|
|
if (HAWK_UNLIKELY(!v))
|
|
{
|
|
ret = -1;
|
|
goto done;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
x = hawk_rtx_cmpval(rtx, test, v, &ret);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
if (x <= -1)
|
|
{
|
|
ret = -1;
|
|
goto done;
|
|
}
|
|
|
|
if (ret == 0)
|
|
{
|
|
skip_eval:
|
|
eval_true = 1;
|
|
if (case_part->action)
|
|
{
|
|
hawk_nde_t* action;
|
|
|
|
action = case_part->action;
|
|
do
|
|
{
|
|
if (run_statement(rtx, action) <= -1)
|
|
{
|
|
ret = -1;
|
|
goto done;
|
|
}
|
|
|
|
if (rtx->exit_level == EXIT_BREAK)
|
|
{
|
|
rtx->exit_level = EXIT_NONE;
|
|
goto done;
|
|
}
|
|
else if (rtx->exit_level != EXIT_NONE) goto done;
|
|
|
|
action = action->next;
|
|
}
|
|
while (action);
|
|
}
|
|
}
|
|
}
|
|
|
|
case_part = (hawk_nde_case_t*)case_part->next;
|
|
}
|
|
|
|
if (!eval_true && default_part)
|
|
{
|
|
case_part = default_part;
|
|
do
|
|
{
|
|
if (case_part->action)
|
|
{
|
|
hawk_nde_t* action;
|
|
|
|
action = case_part->action;
|
|
do
|
|
{
|
|
if (run_statement(rtx, action) <= -1)
|
|
{
|
|
ret = -1;
|
|
goto done;
|
|
}
|
|
|
|
if (rtx->exit_level == EXIT_BREAK)
|
|
{
|
|
rtx->exit_level = EXIT_NONE;
|
|
goto done;
|
|
}
|
|
else if (rtx->exit_level != EXIT_NONE) goto done;
|
|
|
|
action = action->next;
|
|
}
|
|
while (action);
|
|
}
|
|
case_part = (hawk_nde_case_t*)case_part->next;
|
|
}
|
|
while (case_part);
|
|
}
|
|
|
|
done:
|
|
hawk_rtx_refdownval_inline(rtx, test);
|
|
return ret;
|
|
}
|
|
|
|
static int run_while (hawk_rtx_t* rtx, int state, hawk_nde_while_t* nde)
|
|
{
|
|
#if !defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
hawk_val_t* test;
|
|
#endif
|
|
|
|
if (nde->type == HAWK_NDE_WHILE)
|
|
{
|
|
HAWK_ASSERT(nde->test->next == HAWK_NULL);
|
|
|
|
if (state == EXEC_STATE_WHILE_BODY_DONE)
|
|
{
|
|
if (rtx->exit_level == EXIT_BREAK)
|
|
{
|
|
rtx->exit_level = EXIT_NONE;
|
|
return 0;
|
|
}
|
|
else if (rtx->exit_level == EXIT_CONTINUE)
|
|
{
|
|
rtx->exit_level = EXIT_NONE;
|
|
}
|
|
else if (rtx->exit_level != EXIT_NONE)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
ON_STATEMENT(rtx, nde->test);
|
|
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
if (push_exec_stack(rtx, EXEC_STATE_WHILE_TEST_AFTER_EVAL, (hawk_nde_t*)nde) <= -1) return -1;
|
|
return push_eframe(rtx, HAWK_EF_EVAL, nde->test);
|
|
#else
|
|
test = eval_expression(rtx, nde->test);
|
|
if (HAWK_UNLIKELY(!test)) return -1;
|
|
|
|
hawk_rtx_refupval_inline(rtx, test);
|
|
if (hawk_rtx_valtobool(rtx, test))
|
|
{
|
|
if (push_exec_stack(rtx, EXEC_STATE_WHILE_BODY_DONE, (hawk_nde_t*)nde) <= -1 ||
|
|
push_exec_stack(rtx, EXEC_STATE_ENTER, nde->body) <= -1)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, test);
|
|
return -1;
|
|
}
|
|
}
|
|
hawk_rtx_refdownval_inline(rtx, test);
|
|
#endif
|
|
}
|
|
else if (nde->type == HAWK_NDE_DOWHILE)
|
|
{
|
|
HAWK_ASSERT(nde->test->next == HAWK_NULL);
|
|
|
|
if (state == EXEC_STATE_ENTER)
|
|
{
|
|
if (push_exec_stack(rtx, EXEC_STATE_DOWHILE_TEST, (hawk_nde_t*)nde) <= -1 ||
|
|
push_exec_stack(rtx, EXEC_STATE_ENTER, nde->body) <= -1) return -1;
|
|
}
|
|
else
|
|
{
|
|
HAWK_ASSERT(state == EXEC_STATE_DOWHILE_TEST);
|
|
|
|
if (rtx->exit_level == EXIT_BREAK)
|
|
{
|
|
rtx->exit_level = EXIT_NONE;
|
|
return 0;
|
|
}
|
|
else if (rtx->exit_level == EXIT_CONTINUE)
|
|
{
|
|
rtx->exit_level = EXIT_NONE;
|
|
}
|
|
else if (rtx->exit_level != EXIT_NONE) return 0;
|
|
|
|
ON_STATEMENT(rtx, nde->test);
|
|
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
if (push_exec_stack(rtx, EXEC_STATE_DOWHILE_TEST_AFTER_EVAL, (hawk_nde_t*)nde) <= -1) return -1;
|
|
return push_eframe(rtx, HAWK_EF_EVAL, nde->test);
|
|
#else
|
|
test = eval_expression(rtx, nde->test);
|
|
if (HAWK_UNLIKELY(!test)) return -1;
|
|
|
|
hawk_rtx_refupval_inline(rtx, test);
|
|
|
|
if (hawk_rtx_valtobool(rtx, test))
|
|
{
|
|
if (push_exec_stack(rtx, EXEC_STATE_DOWHILE_TEST, (hawk_nde_t*)nde) <= -1 ||
|
|
push_exec_stack(rtx, EXEC_STATE_ENTER, nde->body) <= -1)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, test);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, test);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int run_for (hawk_rtx_t* rtx, int state, hawk_nde_for_t* nde)
|
|
{
|
|
if (state == EXEC_STATE_ENTER)
|
|
{
|
|
if (nde->init)
|
|
{
|
|
HAWK_ASSERT(nde->init->next == HAWK_NULL);
|
|
ON_STATEMENT(rtx, nde->init);
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
if (push_exec_stack(rtx, EXEC_STATE_FOR_INIT_AFTER_EVAL, (hawk_nde_t*)nde) <= -1) return -1;
|
|
return push_eframe(rtx, HAWK_EF_EVAL, nde->init);
|
|
#else
|
|
{
|
|
hawk_val_t* val = eval_expression(rtx, nde->init);
|
|
if (HAWK_UNLIKELY(!val)) return -1;
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
hawk_rtx_refdownval_inline(rtx, val);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HAWK_ASSERT(state == EXEC_STATE_FOR_BODY_DONE);
|
|
|
|
if (rtx->exit_level == EXIT_BREAK)
|
|
{
|
|
rtx->exit_level = EXIT_NONE;
|
|
return 0;
|
|
}
|
|
else if (rtx->exit_level == EXIT_CONTINUE)
|
|
{
|
|
rtx->exit_level = EXIT_NONE;
|
|
}
|
|
else if (rtx->exit_level != EXIT_NONE) return 0;
|
|
|
|
if (nde->incr != HAWK_NULL)
|
|
{
|
|
HAWK_ASSERT(nde->incr->next == HAWK_NULL);
|
|
ON_STATEMENT(rtx, nde->incr);
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
if (push_exec_stack(rtx, EXEC_STATE_FOR_INCR_AFTER_EVAL, (hawk_nde_t*)nde) <= -1) return -1;
|
|
return push_eframe(rtx, HAWK_EF_EVAL, nde->incr);
|
|
#else
|
|
{
|
|
hawk_val_t* val = eval_expression(rtx, nde->incr);
|
|
if (HAWK_UNLIKELY(!val)) return -1;
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
hawk_rtx_refdownval_inline(rtx, val);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
/* push cond test or directly enter body (no-init path reachable here too) */
|
|
if (nde->test)
|
|
{
|
|
HAWK_ASSERT(nde->test->next == HAWK_NULL);
|
|
ON_STATEMENT(rtx, nde->test);
|
|
if (push_exec_stack(rtx, EXEC_STATE_FOR_COND_AFTER_EVAL, (hawk_nde_t*)nde) <= -1) return -1;
|
|
return push_eframe(rtx, HAWK_EF_EVAL, nde->test);
|
|
}
|
|
if (push_exec_stack(rtx, EXEC_STATE_FOR_BODY_DONE, (hawk_nde_t*)nde) <= -1 ||
|
|
push_exec_stack(rtx, EXEC_STATE_ENTER, nde->body) <= -1) return -1;
|
|
return 0;
|
|
#else
|
|
if (nde->test)
|
|
{
|
|
hawk_val_t* test;
|
|
|
|
HAWK_ASSERT(nde->test->next == HAWK_NULL);
|
|
|
|
ON_STATEMENT(rtx, nde->test);
|
|
test = eval_expression(rtx, nde->test);
|
|
if (HAWK_UNLIKELY(!test)) return -1;
|
|
|
|
hawk_rtx_refupval_inline(rtx, test);
|
|
if (hawk_rtx_valtobool(rtx, test))
|
|
{
|
|
if (push_exec_stack(rtx, EXEC_STATE_FOR_BODY_DONE, (hawk_nde_t*)nde) <= -1 ||
|
|
push_exec_stack(rtx, EXEC_STATE_ENTER, nde->body) <= -1)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, test);
|
|
return -1;
|
|
}
|
|
}
|
|
hawk_rtx_refdownval_inline(rtx, test);
|
|
}
|
|
else
|
|
{
|
|
if (push_exec_stack(rtx, EXEC_STATE_FOR_BODY_DONE, (hawk_nde_t*)nde) <= -1 ||
|
|
push_exec_stack(rtx, EXEC_STATE_ENTER, nde->body) <= -1)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static int run_forin (hawk_rtx_t* rtx, const hawk_exec_stack_t* es, hawk_nde_forin_t* nde)
|
|
{
|
|
hawk_nde_exp_t* test;
|
|
hawk_val_t* rv;
|
|
hawk_val_type_t rvtype;
|
|
hawk_oow_t old_forin_size;
|
|
|
|
test = (hawk_nde_exp_t*)nde->test;
|
|
HAWK_ASSERT(test->type == HAWK_NDE_EXP_BIN && test->opcode == HAWK_BINOP_IN);
|
|
|
|
/* chained expressions should not be allowed by the parser first of all */
|
|
HAWK_ASSERT(test->right->next == HAWK_NULL);
|
|
|
|
if (es->state == EXEC_STATE_FORIN_STEP)
|
|
{
|
|
if (es->iv >= rtx->forin.size) return 0;
|
|
if (HAWK_UNLIKELY(!do_assignment(rtx, test->left, rtx->forin.ptr[es->iv], 0))) return -1;
|
|
if (push_exec_stack3(rtx, EXEC_STATE_FORIN_BODY_DONE, (hawk_nde_t*)nde, es->iv + 1, es->base) <= -1 ||
|
|
push_exec_stack(rtx, EXEC_STATE_ENTER, nde->body) <= -1) return -1;
|
|
return 0;
|
|
}
|
|
else if (es->state == EXEC_STATE_FORIN_BODY_DONE)
|
|
{
|
|
if (rtx->exit_level == EXIT_BREAK)
|
|
{
|
|
rtx->exit_level = EXIT_NONE;
|
|
return 0;
|
|
}
|
|
else if (rtx->exit_level == EXIT_CONTINUE)
|
|
{
|
|
rtx->exit_level = EXIT_NONE;
|
|
}
|
|
else if (rtx->exit_level != EXIT_NONE) return 0;
|
|
|
|
if (push_exec_stack3(rtx, EXEC_STATE_FORIN_STEP, (hawk_nde_t*)nde, es->iv, es->base) <= -1) return -1;
|
|
return 0;
|
|
}
|
|
else if (es->state == EXEC_STATE_FORIN_LEAVE)
|
|
{
|
|
leave_forin_iterative(rtx, es->base);
|
|
return 0;
|
|
}
|
|
|
|
rv = eval_expression(rtx, test->right);
|
|
if (HAWK_UNLIKELY(!rv)) return -1;
|
|
|
|
hawk_rtx_refupval_inline(rtx, rv);
|
|
rvtype = HAWK_RTX_GETVALTYPE(rtx, rv);
|
|
old_forin_size = rtx->forin.size;
|
|
|
|
switch (rvtype)
|
|
{
|
|
case HAWK_VAL_NIL:
|
|
break;
|
|
|
|
case HAWK_VAL_MAP:
|
|
{
|
|
hawk_map_t* map;
|
|
hawk_map_pair_t* pair;
|
|
hawk_map_itr_t itr;
|
|
|
|
map = ((hawk_val_map_t*)rv)->map;
|
|
|
|
if (rtx->forin.capa - rtx->forin.size < hawk_map_getsize(map))
|
|
{
|
|
hawk_val_t** tmp;
|
|
hawk_oow_t newcapa;
|
|
|
|
newcapa = rtx->forin.size + hawk_map_getsize(map);
|
|
newcapa = HAWK_ALIGN_POW2(newcapa, 128);
|
|
tmp = hawk_rtx_reallocmem(rtx, rtx->forin.ptr, newcapa * HAWK_SIZEOF(*tmp));
|
|
if (HAWK_UNLIKELY(!tmp))
|
|
{
|
|
ADJERR_LOC(rtx, &test->left->loc);
|
|
goto oops;
|
|
}
|
|
|
|
rtx->forin.ptr = tmp;
|
|
rtx->forin.capa = newcapa;
|
|
}
|
|
|
|
hawk_init_map_itr(&itr, 0);
|
|
pair = hawk_map_getfirstpair(map, &itr);
|
|
while (pair)
|
|
{
|
|
hawk_val_t* str;
|
|
|
|
str = (hawk_val_t*)hawk_rtx_makenstrvalwithoochars(rtx, HAWK_HTB_KPTR(pair), HAWK_HTB_KLEN(pair));
|
|
if (HAWK_UNLIKELY(!str))
|
|
{
|
|
ADJERR_LOC(rtx, &test->left->loc);
|
|
goto oops;
|
|
}
|
|
|
|
rtx->forin.ptr[rtx->forin.size++] = str;
|
|
hawk_rtx_refupval_inline(rtx, str);
|
|
|
|
pair = hawk_map_getnextpair(map, &itr);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case HAWK_VAL_ARR:
|
|
{
|
|
hawk_arr_t* arr;
|
|
hawk_oow_t arr_size, i;
|
|
|
|
arr = ((hawk_val_arr_t*)rv)->arr;
|
|
arr_size = HAWK_ARR_SIZE(arr);
|
|
|
|
if (rtx->forin.capa - rtx->forin.size < hawk_arr_getsize(arr))
|
|
{
|
|
hawk_val_t** tmp;
|
|
hawk_oow_t newcapa;
|
|
|
|
newcapa = rtx->forin.size + hawk_arr_getsize(arr);
|
|
newcapa = HAWK_ALIGN_POW2(newcapa, 128);
|
|
tmp = hawk_rtx_reallocmem(rtx, rtx->forin.ptr, newcapa * HAWK_SIZEOF(*tmp));
|
|
if (HAWK_UNLIKELY(!tmp))
|
|
{
|
|
ADJERR_LOC(rtx, &test->left->loc);
|
|
goto oops;
|
|
}
|
|
|
|
rtx->forin.ptr = tmp;
|
|
rtx->forin.capa = newcapa;
|
|
}
|
|
|
|
for (i = 0; i < arr_size; i++)
|
|
{
|
|
if (HAWK_ARR_SLOT(arr, i))
|
|
{
|
|
hawk_val_t* tmp;
|
|
|
|
tmp = (hawk_val_t*)hawk_rtx_makeintval_inline(rtx, i);
|
|
if (HAWK_UNLIKELY(!tmp))
|
|
{
|
|
ADJERR_LOC(rtx, &test->left->loc);
|
|
goto oops;
|
|
}
|
|
|
|
rtx->forin.ptr[rtx->forin.size++] = tmp;
|
|
hawk_rtx_refupval_inline(rtx, tmp);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
hawk_rtx_seterrnum(rtx, &test->right->loc, HAWK_EINROP);
|
|
goto oops;
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, rv);
|
|
if (old_forin_size < rtx->forin.size)
|
|
{
|
|
if (push_exec_stack3(rtx, EXEC_STATE_FORIN_LEAVE, (hawk_nde_t*)nde, 0, old_forin_size) <= -1 ||
|
|
push_exec_stack3(rtx, EXEC_STATE_FORIN_STEP, (hawk_nde_t*)nde, old_forin_size, old_forin_size) <= -1)
|
|
{
|
|
leave_forin_iterative(rtx, old_forin_size);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
oops:
|
|
hawk_rtx_refdownval_inline(rtx, rv);
|
|
leave_forin_iterative(rtx, old_forin_size);
|
|
return -1;
|
|
}
|
|
|
|
static int run_break (hawk_rtx_t* rtx, hawk_nde_break_t* nde)
|
|
{
|
|
rtx->exit_level = EXIT_BREAK;
|
|
return 0;
|
|
}
|
|
|
|
static int run_continue (hawk_rtx_t* rtx, hawk_nde_continue_t* nde)
|
|
{
|
|
rtx->exit_level = EXIT_CONTINUE;
|
|
return 0;
|
|
}
|
|
|
|
static int run_return (hawk_rtx_t* rtx, hawk_nde_return_t* nde)
|
|
{
|
|
if (nde->val)
|
|
{
|
|
HAWK_ASSERT(nde->val->next == HAWK_NULL);
|
|
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
if (push_exec_stack(rtx, EXEC_STATE_RETURN_AFTER_EVAL, (hawk_nde_t*)nde) <= -1) return -1;
|
|
return push_eframe(rtx, HAWK_EF_EVAL, nde->val);
|
|
#else
|
|
{
|
|
hawk_val_t* val;
|
|
|
|
val = eval_expression(rtx, nde->val);
|
|
if (HAWK_UNLIKELY(!val)) return -1;
|
|
|
|
if (!(rtx->hawk->opt.trait & HAWK_FLEXMAP))
|
|
{
|
|
hawk_val_type_t vtype = HAWK_RTX_GETVALTYPE(rtx, val);
|
|
|
|
if (vtype == HAWK_VAL_MAP || vtype == HAWK_VAL_ARR)
|
|
{
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
hawk_rtx_refdownval_inline(rtx, val);
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_ENONSCARET);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_RETVAL(rtx));
|
|
HAWK_RTX_STACK_RETVAL(rtx) = val;
|
|
|
|
/* NOTE: see eval_call() for the trick */
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
rtx->exit_level = EXIT_FUNCTION;
|
|
return 0;
|
|
}
|
|
|
|
static int run_exit (hawk_rtx_t* rtx, hawk_nde_exit_t* nde)
|
|
{
|
|
if (nde->val)
|
|
{
|
|
HAWK_ASSERT(nde->val->next == HAWK_NULL);
|
|
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
if (push_exec_stack(rtx, EXEC_STATE_EXIT_AFTER_EVAL, (hawk_nde_t*)nde) <= -1) return -1;
|
|
return push_eframe(rtx, HAWK_EF_EVAL, nde->val);
|
|
#else
|
|
{
|
|
hawk_val_t* val;
|
|
|
|
val = eval_expression(rtx, nde->val);
|
|
if (HAWK_UNLIKELY(!val)) return -1;
|
|
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_RETVAL_GBL(rtx));
|
|
HAWK_RTX_STACK_RETVAL_GBL(rtx) = val;
|
|
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
rtx->exit_level = (nde->abort)? EXIT_ABORT: EXIT_GLOBAL;
|
|
return 0;
|
|
}
|
|
|
|
static int run_next (hawk_rtx_t* rtx, hawk_nde_next_t* nde)
|
|
{
|
|
/* the parser checks if next has been called in the begin/end
|
|
* block or whereever inappropriate. so the tuntime doesn't
|
|
* check that explicitly */
|
|
if (rtx->active_block == (hawk_nde_blk_t*)rtx->hawk->tree.init)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_ERNEXTINIT);
|
|
return -1;
|
|
}
|
|
if (rtx->active_block == (hawk_nde_blk_t*)rtx->hawk->tree.begin)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_ERNEXTBEG);
|
|
return -1;
|
|
}
|
|
else if (rtx->active_block == (hawk_nde_blk_t*)rtx->hawk->tree.end)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_ERNEXTEND);
|
|
return -1;
|
|
}
|
|
|
|
rtx->exit_level = EXIT_NEXT;
|
|
return 0;
|
|
}
|
|
|
|
static int run_nextinfile (hawk_rtx_t* rtx, hawk_nde_nextfile_t* nde)
|
|
{
|
|
int n;
|
|
|
|
/* normal nextfile statement */
|
|
if (rtx->active_block == (hawk_nde_blk_t*)rtx->hawk->tree.init)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_ERNEXTFINIT);
|
|
return -1;
|
|
}
|
|
if (rtx->active_block == (hawk_nde_blk_t*)rtx->hawk->tree.begin)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_ERNEXTFBEG);
|
|
return -1;
|
|
}
|
|
else if (rtx->active_block == (hawk_nde_blk_t*)rtx->hawk->tree.end)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_ERNEXTFEND);
|
|
return -1;
|
|
}
|
|
|
|
n = hawk_rtx_nextio_read(rtx, HAWK_IN_CONSOLE, HAWK_T(""));
|
|
if (n <= -1)
|
|
{
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return -1;
|
|
}
|
|
|
|
if (n == 0)
|
|
{
|
|
/* no more input console */
|
|
rtx->exit_level = EXIT_GLOBAL;
|
|
return 0;
|
|
}
|
|
|
|
/* FNR resets to 0, NR remains the same */
|
|
if (update_fnr(rtx, 0, rtx->gbl.nr) <= -1)
|
|
{
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return -1;
|
|
}
|
|
|
|
rtx->exit_level = EXIT_NEXT;
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int run_nextoutfile (hawk_rtx_t* rtx, hawk_nde_nextfile_t* nde)
|
|
{
|
|
int n;
|
|
|
|
/* nextofile can be called from BEGIN and END block unlike nextfile */
|
|
|
|
n = hawk_rtx_nextio_write(rtx, HAWK_OUT_CONSOLE, HAWK_T(""));
|
|
if (n <= -1)
|
|
{
|
|
/* adjust the error line */
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return -1;
|
|
}
|
|
|
|
if (n == 0)
|
|
{
|
|
/* should it terminate the program there is no more
|
|
* output console? no. there will just be no more console
|
|
* output */
|
|
/*rtx->exit_level = EXIT_GLOBAL;*/
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int run_nextfile (hawk_rtx_t* rtx, hawk_nde_nextfile_t* nde)
|
|
{
|
|
return (nde->out)? run_nextoutfile(rtx, nde): run_nextinfile(rtx, nde);
|
|
}
|
|
|
|
static hawk_val_t* assign_newmapval_in_map (hawk_rtx_t* rtx, hawk_map_t* container, hawk_ooch_t* idxptr, hawk_oow_t idxlen)
|
|
{
|
|
hawk_val_t* tmp;
|
|
hawk_map_pair_t* pair;
|
|
|
|
tmp = hawk_rtx_makemapval(rtx);
|
|
if (HAWK_UNLIKELY(!tmp)) return HAWK_NULL;
|
|
|
|
/* as this is the assignment, it needs to update the reference count of the target value. */
|
|
hawk_rtx_refupval_inline(rtx, tmp);
|
|
pair = hawk_map_upsert(container, idxptr, idxlen, tmp, 0);
|
|
if (HAWK_UNLIKELY(!pair))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, tmp); /* decrement upon upsert() failure */
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
HAWK_ASSERT(tmp == HAWK_MAP_VPTR(pair));
|
|
return tmp;
|
|
}
|
|
|
|
static hawk_val_t* assign_newarrval_in_arr (hawk_rtx_t* rtx, hawk_arr_t* container, hawk_ooi_t idx)
|
|
{
|
|
hawk_val_t* tmp;
|
|
|
|
tmp = hawk_rtx_makearrval(rtx, -1);
|
|
if (HAWK_UNLIKELY(!tmp)) return HAWK_NULL;
|
|
|
|
/* as this is the assignment, it needs to update the reference count of the target value. */
|
|
hawk_rtx_refupval_inline(rtx, tmp);
|
|
if (HAWK_UNLIKELY(hawk_arr_upsert(container, idx, tmp, 0) == HAWK_ARR_NIL))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, tmp); /* decrement upon upsert() failure */
|
|
return HAWK_NULL;
|
|
}
|
|
return tmp;
|
|
}
|
|
|
|
static hawk_val_t* fetch_topval_from_var (hawk_rtx_t* rtx, hawk_nde_var_t* var)
|
|
{
|
|
hawk_val_t* val;
|
|
|
|
switch (var->type)
|
|
{
|
|
case HAWK_NDE_NAMED:
|
|
case HAWK_NDE_NAMEDIDX:
|
|
HAWK_ASSERT(var->id.idxa < rtx->named_slot_count);
|
|
val = HAWK_RTX_STACK_NAMED(rtx, var->id.idxa);
|
|
break;
|
|
|
|
case HAWK_NDE_GBL:
|
|
case HAWK_NDE_GBLIDX:
|
|
val = HAWK_RTX_STACK_GBL(rtx, var->id.idxa);
|
|
break;
|
|
|
|
case HAWK_NDE_LCL:
|
|
case HAWK_NDE_LCLIDX:
|
|
val = HAWK_RTX_STACK_LCL(rtx, var->id.idxa);
|
|
break;
|
|
|
|
default:
|
|
HAWK_ASSERT(var->type == HAWK_NDE_ARG || var->type == HAWK_NDE_ARGIDX);
|
|
val = HAWK_RTX_STACK_ARG(rtx, var->id.idxa);
|
|
break;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
static hawk_val_t* assign_topval_to_var (hawk_rtx_t* rtx, hawk_nde_var_t* var, hawk_val_t* val)
|
|
{
|
|
switch (var->type)
|
|
{
|
|
case HAWK_NDE_NAMED:
|
|
case HAWK_NDE_NAMEDIDX:
|
|
if (HAWK_UNLIKELY(hawk_rtx_setnamedval(rtx, var->id.idxa, val) <= -1))
|
|
{
|
|
ADJERR_LOC(rtx, &var->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
break;
|
|
|
|
case HAWK_NDE_GBL:
|
|
case HAWK_NDE_GBLIDX:
|
|
{
|
|
int x;
|
|
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
x = hawk_rtx_setgbl(rtx, (int)var->id.idxa, val);
|
|
hawk_rtx_refdownval_inline(rtx, val);
|
|
if (HAWK_UNLIKELY(x <= -1))
|
|
{
|
|
ADJERR_LOC(rtx, &var->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case HAWK_NDE_LCL:
|
|
case HAWK_NDE_LCLIDX:
|
|
{
|
|
hawk_val_t* old;
|
|
old = HAWK_RTX_STACK_LCL(rtx,var->id.idxa);
|
|
hawk_rtx_refdownval_inline(rtx, old);
|
|
HAWK_RTX_STACK_LCL(rtx,var->id.idxa) = val;
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
break;
|
|
}
|
|
|
|
default: /* HAWK_NDE_ARG, HAWK_NDE_ARGIDX */
|
|
{
|
|
hawk_val_t* old;
|
|
old = HAWK_RTX_STACK_ARG(rtx,var->id.idxa);
|
|
hawk_rtx_refdownval_inline(rtx, old);
|
|
HAWK_RTX_STACK_ARG(rtx,var->id.idxa) = val;
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
static hawk_val_t* assign_newmapval_to_var (hawk_rtx_t* rtx, hawk_nde_var_t* var)
|
|
{
|
|
hawk_val_t* tmp;
|
|
|
|
HAWK_ASSERT(var->type >= HAWK_NDE_NAMED && var->type <= HAWK_NDE_ARGIDX);
|
|
|
|
tmp = hawk_rtx_makemapval(rtx);
|
|
if (HAWK_UNLIKELY(!tmp))
|
|
{
|
|
ADJERR_LOC(rtx, &var->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
return assign_topval_to_var(rtx, var, tmp);
|
|
}
|
|
|
|
static HAWK_INLINE int delete_indexed (hawk_rtx_t* rtx, hawk_val_t* vv, hawk_nde_var_t* var)
|
|
{
|
|
hawk_map_t* map;
|
|
hawk_ooch_t* str = HAWK_NULL;
|
|
hawk_oow_t len;
|
|
hawk_ooch_t idxbuf[HAWK_IDX_BUF_SIZE];
|
|
|
|
hawk_arr_t* arr;
|
|
hawk_ooi_t idx;
|
|
|
|
hawk_nde_t* remidx;
|
|
hawk_val_type_t vtype;
|
|
|
|
HAWK_ASSERT(var->idx != HAWK_NULL);
|
|
|
|
/* delete x["abc"];
|
|
* delete x[20,"abc"]; */
|
|
|
|
vtype = HAWK_RTX_GETVALTYPE(rtx, vv);
|
|
if (vtype == HAWK_VAL_MAP)
|
|
{
|
|
len = HAWK_COUNTOF(idxbuf);
|
|
str = idxnde_to_str(rtx, var->idx, idxbuf, &len, &remidx, HAWK_NULL);
|
|
if (HAWK_UNLIKELY(!str)) goto oops;
|
|
map = ((hawk_val_map_t*)vv)->map;
|
|
}
|
|
else
|
|
{
|
|
HAWK_ASSERT(vtype == HAWK_VAL_ARR);
|
|
idx = idxnde_to_int(rtx, var->idx, &remidx);
|
|
if (HAWK_UNLIKELY(idx <= -1)) goto oops;
|
|
arr = ((hawk_val_arr_t*)vv)->arr;
|
|
}
|
|
|
|
#if defined(HAWK_ENABLE_GC)
|
|
while (remidx)
|
|
{
|
|
hawk_val_type_t container_vtype;
|
|
|
|
if (vtype == HAWK_VAL_MAP)
|
|
{
|
|
hawk_map_pair_t* pair;
|
|
pair = hawk_map_search(map, str, len);
|
|
vv = pair? (hawk_val_t*)HAWK_MAP_VPTR(pair): hawk_val_nil;
|
|
}
|
|
else
|
|
{
|
|
vv = (idx < HAWK_ARR_SIZE(arr) && HAWK_ARR_SLOT(arr, idx))? ((hawk_val_t*)HAWK_ARR_DPTR(arr, idx)): hawk_val_nil;
|
|
}
|
|
container_vtype = vtype;
|
|
vtype = HAWK_RTX_GETVALTYPE(rtx, vv);
|
|
|
|
switch (vtype)
|
|
{
|
|
case HAWK_VAL_MAP:
|
|
val_map:
|
|
if (str && str != idxbuf) hawk_rtx_freemem(rtx, str);
|
|
len = HAWK_COUNTOF(idxbuf);
|
|
str = idxnde_to_str(rtx, remidx, idxbuf, &len, &remidx, HAWK_NULL);
|
|
if (HAWK_UNLIKELY(!str)) goto oops;
|
|
map = ((hawk_val_map_t*)vv)->map;
|
|
break;
|
|
|
|
case HAWK_VAL_ARR:
|
|
val_arr:
|
|
idx = idxnde_to_int(rtx, remidx, &remidx);
|
|
if (HAWK_UNLIKELY(idx <= -1)) goto oops;
|
|
arr = ((hawk_val_arr_t*)vv)->arr;
|
|
break;
|
|
|
|
default:
|
|
if (vtype == HAWK_VAL_NIL || (rtx->hawk->opt.trait & HAWK_FLEXMAP))
|
|
{
|
|
if (container_vtype == HAWK_VAL_MAP)
|
|
{
|
|
vv = assign_newmapval_in_map(rtx, map, str, len);
|
|
if (HAWK_UNLIKELY(!vv)) { ADJERR_LOC(rtx, &var->loc); goto oops; }
|
|
vtype = HAWK_VAL_MAP;
|
|
goto val_map;
|
|
}
|
|
else
|
|
{
|
|
vv = assign_newarrval_in_arr(rtx, arr, idx);
|
|
if (HAWK_UNLIKELY(!vv)) { ADJERR_LOC(rtx, &var->loc); goto oops; }
|
|
vtype = HAWK_VAL_ARR;
|
|
goto val_arr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hawk_rtx_seterrfmt(rtx, &var->loc, HAWK_ENOTDEL, HAWK_T("nested scalar value under '%.*js' not deletable"), var->id.name.len, var->id.name.ptr);
|
|
goto oops;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (vtype == HAWK_VAL_MAP)
|
|
hawk_map_delete(map, str, len);
|
|
else
|
|
hawk_arr_uplete(arr, idx, 1); /* no reindexing by compaction. keep the place unset */
|
|
|
|
if (str && str != idxbuf) hawk_rtx_freemem(rtx, str);
|
|
return 0;
|
|
|
|
oops:
|
|
if (str && str != idxbuf) hawk_rtx_freemem(rtx, str);
|
|
return -1;
|
|
}
|
|
|
|
static int run_delete (hawk_rtx_t* rtx, hawk_nde_delete_t* nde)
|
|
{
|
|
hawk_nde_var_t* var;
|
|
hawk_val_t* val;
|
|
hawk_val_type_t vtype;
|
|
|
|
var = (hawk_nde_var_t*)nde->var;
|
|
if (var->is_const)
|
|
{
|
|
hawk_rtx_seterrfmt(rtx, &var->loc, HAWK_EPERM,
|
|
HAWK_T("cannot modify constant '%.*js'"), var->id.name.len, var->id.name.ptr);
|
|
return -1;
|
|
}
|
|
|
|
val = fetch_topval_from_var(rtx, var);
|
|
HAWK_ASSERT(val != HAWK_NULL);
|
|
|
|
vtype = HAWK_RTX_GETVALTYPE(rtx, val);
|
|
switch (vtype)
|
|
{
|
|
case HAWK_VAL_NIL:
|
|
/* value not set. create a map and assign it to the variable */
|
|
if (HAWK_UNLIKELY(assign_newmapval_to_var(rtx, var) == HAWK_NULL)) goto oops;
|
|
break;
|
|
|
|
case HAWK_VAL_MAP:
|
|
if (var->type == HAWK_NDE_NAMEDIDX || var->type == HAWK_NDE_GBLIDX ||
|
|
var->type == HAWK_NDE_LCLIDX || var->type == HAWK_NDE_ARGIDX)
|
|
{
|
|
if (delete_indexed(rtx, val, var) <= -1) goto oops;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
BEGIN {
|
|
@local a;
|
|
a[1] = 20; a[2] = "hello"; a[3][4][5]="ttt";
|
|
print typename(a), length(a);
|
|
delete a;
|
|
print typename(a), length(a);
|
|
}
|
|
*/
|
|
hawk_map_clear(((hawk_val_map_t*)val)->map);
|
|
}
|
|
break;
|
|
|
|
case HAWK_VAL_ARR:
|
|
if (var->type == HAWK_NDE_NAMEDIDX || var->type == HAWK_NDE_GBLIDX ||
|
|
var->type == HAWK_NDE_LCLIDX || var->type == HAWK_NDE_ARGIDX)
|
|
{
|
|
if (delete_indexed(rtx, val, var) <= -1) goto oops;
|
|
}
|
|
else
|
|
{
|
|
hawk_arr_clear(((hawk_val_arr_t*)val)->arr);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hawk_rtx_seterrfmt(rtx, &var->loc, HAWK_ENOTDEL, HAWK_T("'%.*js' not deletable"), var->id.name.len, var->id.name.ptr);
|
|
goto oops;
|
|
}
|
|
|
|
return 0;
|
|
|
|
oops:
|
|
ADJERR_LOC(rtx, &var->loc);
|
|
return -1;
|
|
|
|
}
|
|
|
|
static int run_reset (hawk_rtx_t* rtx, hawk_nde_reset_t* nde)
|
|
{
|
|
hawk_nde_var_t* var;
|
|
|
|
var = (hawk_nde_var_t*)nde->var;
|
|
if (var->is_const)
|
|
{
|
|
hawk_rtx_seterrfmt(rtx, &var->loc, HAWK_EPERM,
|
|
HAWK_T("cannot modify constant '%.*js'"), var->id.name.len, var->id.name.ptr);
|
|
return -1;
|
|
}
|
|
|
|
HAWK_ASSERT(var->type >= HAWK_NDE_NAMED && var->type <= HAWK_NDE_ARG);
|
|
|
|
switch (var->type)
|
|
{
|
|
case HAWK_NDE_NAMED:
|
|
/* if a named variable has an index part, something is definitely wrong */
|
|
HAWK_ASSERT(var->idx == HAWK_NULL);
|
|
|
|
HAWK_ASSERT(var->id.idxa < rtx->named_slot_count);
|
|
if (hawk_rtx_setnamedval(rtx, var->id.idxa, hawk_val_nil) <= -1)
|
|
{
|
|
ADJERR_LOC(rtx, &var->loc);
|
|
return -1;
|
|
}
|
|
|
|
/* a named variable can be reset if removed from the internal map to manage it */
|
|
return 0;
|
|
|
|
case HAWK_NDE_GBL:
|
|
case HAWK_NDE_LCL:
|
|
case HAWK_NDE_ARG:
|
|
{
|
|
hawk_val_t* val;
|
|
|
|
HAWK_ASSERT(var->idx == HAWK_NULL);
|
|
|
|
val = fetch_topval_from_var(rtx, var);
|
|
HAWK_ASSERT(val != HAWK_NULL);
|
|
|
|
if (HAWK_RTX_GETVALTYPE(rtx, val) != HAWK_VAL_NIL)
|
|
{
|
|
assign_topval_to_var(rtx, var, hawk_val_nil); /* this must not fail */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
default:
|
|
/* the reset statement can only be called with plain variables */
|
|
HAWK_ASSERT(!"should never happen - wrong target for @reset");
|
|
hawk_rtx_seterrnum(rtx, &var->loc, HAWK_EBADARG);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static int io_val_to_str (hawk_rtx_t* rtx, hawk_val_t* v, const hawk_loc_t* loc, hawk_oocs_t* dst, int seterr)
|
|
{
|
|
dst->ptr = hawk_rtx_getvaloocstr(rtx, v, &dst->len);
|
|
if (HAWK_UNLIKELY(!dst->ptr)) return -1;
|
|
|
|
if (seterr && dst->len <= 0)
|
|
{
|
|
hawk_rtx_seterrfmt(rtx, loc, HAWK_EIONMEM, HAWK_T("empty I/O name"));
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
hawk_oow_t len;
|
|
|
|
len = dst->len;
|
|
while (len > 0)
|
|
{
|
|
if (dst->ptr[--len] == '\0')
|
|
{
|
|
hawk_rtx_seterrfmt(rtx, loc, HAWK_EIONMNL, HAWK_T("invalid I/O name of length %zu containing '\\0'"), dst->len);
|
|
dst->len = 0;
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static hawk_val_t* io_nde_to_str (hawk_rtx_t* rtx, hawk_nde_t* nde, hawk_oocs_t* dst, int seterr)
|
|
{
|
|
hawk_val_t* v;
|
|
|
|
v = eval_expression(rtx, nde);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
if (HAWK_UNLIKELY(io_val_to_str(rtx, v, &nde->loc, dst, seterr) <= -1))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
static int run_print (hawk_rtx_t* rtx, hawk_nde_print_t* nde)
|
|
{
|
|
hawk_oocs_t out;
|
|
hawk_val_t* out_v = HAWK_NULL;
|
|
int n, xret = 0;
|
|
|
|
HAWK_ASSERT(
|
|
(nde->out_type == HAWK_OUT_PIPE && nde->out != HAWK_NULL) ||
|
|
(nde->out_type == HAWK_OUT_RWPIPE && nde->out != HAWK_NULL) ||
|
|
(nde->out_type == HAWK_OUT_FILE && nde->out != HAWK_NULL) ||
|
|
(nde->out_type == HAWK_OUT_APFILE && nde->out != HAWK_NULL) ||
|
|
(nde->out_type == HAWK_OUT_CONSOLE && nde->out == HAWK_NULL));
|
|
|
|
/* check if destination has been specified. */
|
|
if (nde->out)
|
|
{
|
|
out_v = io_nde_to_str(rtx, nde->out, &out, 1);
|
|
if (!out_v || out.len <= 0) goto oops_1;
|
|
}
|
|
else
|
|
{
|
|
out.ptr = (hawk_ooch_t*)HAWK_T("");
|
|
out.len = 0;
|
|
}
|
|
|
|
/* check if print is followed by any arguments */
|
|
if (!nde->args)
|
|
{
|
|
/* if it doesn't have any arguments, print the entire input record */
|
|
n = hawk_rtx_writeiostr(rtx, nde->out_type, out.ptr, HAWK_OOECS_PTR(&rtx->inrec.line), HAWK_OOECS_LEN(&rtx->inrec.line));
|
|
if (n <= -1 /*&& rtx->errinf.num != HAWK_EIOIMPL*/)
|
|
{
|
|
if (rtx->hawk->opt.trait & HAWK_TOLERANT)
|
|
{
|
|
xret = PRINT_IOERR;
|
|
}
|
|
else
|
|
{
|
|
goto oops;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* if it has any arguments, print the arguments separated by
|
|
* the value OFS */
|
|
hawk_nde_t* head, * np;
|
|
hawk_val_t* v;
|
|
|
|
if (nde->args->type == HAWK_NDE_GRP)
|
|
{
|
|
/* parenthesized print */
|
|
HAWK_ASSERT(nde->args->next == HAWK_NULL);
|
|
head = ((hawk_nde_grp_t*)nde->args)->body;
|
|
}
|
|
else head = nde->args;
|
|
|
|
for (np = head; np != HAWK_NULL; np = np->next)
|
|
{
|
|
if (np != head)
|
|
{
|
|
n = hawk_rtx_writeiostr(rtx, nde->out_type, out.ptr, rtx->gbl.ofs.ptr, rtx->gbl.ofs.len);
|
|
if (n <= -1 /*&& rtx->errinf.num != HAWK_EIOIMPL*/)
|
|
{
|
|
if (rtx->hawk->opt.trait & HAWK_TOLERANT)
|
|
{
|
|
xret = PRINT_IOERR;
|
|
}
|
|
else
|
|
{
|
|
goto oops;
|
|
}
|
|
}
|
|
}
|
|
|
|
v = eval_expression(rtx, np);
|
|
if (HAWK_UNLIKELY(!v)) goto oops_1;
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
n = hawk_rtx_writeioval(rtx, nde->out_type, out.ptr, v);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
|
|
if (n <= -1 /*&& rtx->errinf.num != HAWK_EIOIMPL*/)
|
|
{
|
|
if (rtx->hawk->opt.trait & HAWK_TOLERANT)
|
|
{
|
|
xret = PRINT_IOERR;
|
|
}
|
|
else
|
|
{
|
|
goto oops;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* print the value ORS to terminate the operation */
|
|
n = hawk_rtx_writeiostr(rtx, nde->out_type, out.ptr, rtx->gbl.ors.ptr, rtx->gbl.ors.len);
|
|
if (n <= -1 /*&& rtx->errinf.num != HAWK_EIOIMPL*/)
|
|
{
|
|
if (rtx->hawk->opt.trait & HAWK_TOLERANT)
|
|
{
|
|
xret = PRINT_IOERR;
|
|
}
|
|
else
|
|
{
|
|
goto oops;
|
|
}
|
|
}
|
|
|
|
/* unlike printf, flushio() is not needed here as print
|
|
* inserts <NL> that triggers auto-flush */
|
|
if (out_v)
|
|
{
|
|
hawk_rtx_freevaloocstr(rtx, out_v, out.ptr);
|
|
hawk_rtx_refdownval_inline(rtx, out_v);
|
|
}
|
|
|
|
return xret;
|
|
|
|
oops:
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
|
|
oops_1:
|
|
if (out_v)
|
|
{
|
|
hawk_rtx_freevaloocstr(rtx, out_v, out.ptr);
|
|
hawk_rtx_refdownval_inline(rtx, out_v);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int run_printf (hawk_rtx_t* rtx, hawk_nde_print_t* nde)
|
|
{
|
|
hawk_oocs_t out;
|
|
hawk_val_t* out_v = HAWK_NULL;
|
|
hawk_val_t* v;
|
|
hawk_val_type_t vtype;
|
|
hawk_nde_t* head;
|
|
int n, xret = 0;
|
|
|
|
HAWK_ASSERT(
|
|
(nde->out_type == HAWK_OUT_PIPE && nde->out != HAWK_NULL) ||
|
|
(nde->out_type == HAWK_OUT_RWPIPE && nde->out != HAWK_NULL) ||
|
|
(nde->out_type == HAWK_OUT_FILE && nde->out != HAWK_NULL) ||
|
|
(nde->out_type == HAWK_OUT_APFILE && nde->out != HAWK_NULL) ||
|
|
(nde->out_type == HAWK_OUT_CONSOLE && nde->out == HAWK_NULL));
|
|
|
|
if (nde->out)
|
|
{
|
|
out_v = io_nde_to_str(rtx, nde->out, &out, 1);
|
|
if (!out_v || out.len <= 0) goto oops_1;
|
|
}
|
|
else
|
|
{
|
|
out.ptr = (hawk_ooch_t*)HAWK_T("");
|
|
out.len = 0;
|
|
}
|
|
|
|
/*( valid printf statement should have at least one argument. the parser must ensure this. */
|
|
HAWK_ASSERT(nde->args != HAWK_NULL);
|
|
|
|
if (nde->args->type == HAWK_NDE_GRP)
|
|
{
|
|
/* parenthesized print */
|
|
HAWK_ASSERT(nde->args->next == HAWK_NULL);
|
|
head = ((hawk_nde_grp_t*)nde->args)->body;
|
|
}
|
|
else head = nde->args;
|
|
|
|
/* valid printf statement should have at least one argument. the parser must ensure this */
|
|
HAWK_ASSERT(head != HAWK_NULL);
|
|
|
|
v = eval_expression(rtx, head);
|
|
if (HAWK_UNLIKELY(!v)) goto oops_1;
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
vtype = HAWK_RTX_GETVALTYPE(rtx, v);
|
|
switch (vtype)
|
|
{
|
|
case HAWK_VAL_STR:
|
|
/* perform the formatted output */
|
|
n = output_formatted(rtx, nde->out_type, out.ptr, ((hawk_val_str_t*)v)->val.ptr, ((hawk_val_str_t*)v)->val.len, head->next);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
if (n <= -1)
|
|
{
|
|
if (n == PRINT_IOERR) xret = n;
|
|
else goto oops;
|
|
}
|
|
break;
|
|
|
|
case HAWK_VAL_MBS:
|
|
/* perform the formatted output */
|
|
n = output_formatted_bytes(rtx, nde->out_type, out.ptr, ((hawk_val_mbs_t*)v)->val.ptr, ((hawk_val_mbs_t*)v)->val.len, head->next);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
if (n <= -1)
|
|
{
|
|
if (n == PRINT_IOERR) xret = n;
|
|
else goto oops;
|
|
}
|
|
break;
|
|
|
|
case HAWK_VAL_BOB:
|
|
n = output_formatted_bytes(rtx, nde->out_type, out.ptr, ((hawk_val_bob_t*)v)->val.ptr, ((hawk_val_bob_t*)v)->val.len, head->next);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
if (n <= -1)
|
|
{
|
|
if (n == PRINT_IOERR) xret = n;
|
|
else goto oops;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* the remaining arguments are ignored as the format cannot
|
|
* contain any % characters. e.g. printf (1, "xxxx") */
|
|
n = hawk_rtx_writeioval(rtx, nde->out_type, out.ptr, v);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
|
|
if (n <= -1 /*&& rtx->errinf.num != HAWK_EIOIMPL*/)
|
|
{
|
|
if (rtx->hawk->opt.trait & HAWK_TOLERANT) xret = PRINT_IOERR;
|
|
else goto oops;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (hawk_rtx_flushio(rtx, nde->out_type, out.ptr) <= -1)
|
|
{
|
|
if (rtx->hawk->opt.trait & HAWK_TOLERANT) xret = PRINT_IOERR;
|
|
else goto oops_1;
|
|
}
|
|
|
|
if (out_v)
|
|
{
|
|
hawk_rtx_freevaloocstr(rtx, out_v, out.ptr);
|
|
hawk_rtx_refdownval_inline(rtx, out_v);
|
|
}
|
|
|
|
return xret;
|
|
|
|
oops:
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
|
|
oops_1:
|
|
if (out_v)
|
|
{
|
|
hawk_rtx_freevaloocstr(rtx, out_v, out.ptr);
|
|
hawk_rtx_refdownval_inline(rtx, out_v);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int output_formatted (
|
|
hawk_rtx_t* rtx, hawk_out_type_t out_type, const hawk_ooch_t* dst,
|
|
const hawk_ooch_t* fmt, hawk_oow_t fmt_len, hawk_nde_t* args)
|
|
{
|
|
hawk_ooch_t* ptr;
|
|
hawk_oow_t len;
|
|
int n;
|
|
|
|
ptr = hawk_rtx_format(rtx, HAWK_NULL, HAWK_NULL, fmt, fmt_len, 0, args, &len);
|
|
if (!ptr) return -1;
|
|
|
|
n = hawk_rtx_writeiostr(rtx, out_type, dst, ptr, len);
|
|
if (n <= -1 /*&& rtx->errinf.num != HAWK_EIOIMPL*/)
|
|
{
|
|
return (rtx->hawk->opt.trait & HAWK_TOLERANT)? PRINT_IOERR: -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int output_formatted_bytes (
|
|
hawk_rtx_t* rtx, hawk_out_type_t out_type, const hawk_ooch_t* dst,
|
|
const hawk_bch_t* fmt, hawk_oow_t fmt_len, hawk_nde_t* args)
|
|
{
|
|
hawk_bch_t* ptr;
|
|
hawk_oow_t len;
|
|
int n;
|
|
|
|
ptr = hawk_rtx_formatmbs(rtx, HAWK_NULL, HAWK_NULL, fmt, fmt_len, 0, args, &len);
|
|
if (!ptr) return -1;
|
|
|
|
n = hawk_rtx_writeiobytes(rtx, out_type, dst, ptr, len);
|
|
if (n <= -1 /*&& rtx->errinf.num != HAWK_EIOIMPL*/)
|
|
{
|
|
return (rtx->hawk->opt.trait & HAWK_TOLERANT)? PRINT_IOERR: -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Evaluate a binary operand that is either a variable read or a compile-time
|
|
* constant (INT, FLT, STR, CHAR, BCHR). Variable reads are returned as-is
|
|
* (no refcount change). Constants are returned with refcount already bumped
|
|
* so the caller can treat both uniformly and must call refdown on the result.
|
|
*
|
|
* Returns XSTACK_NOT_A_LEAF if nde is none of these simple types.
|
|
* Returns HAWK_NULL if the operand IS a simple type but allocation failed.
|
|
* Returns the value otherwise (caller must refdown it). */
|
|
static HAWK_INLINE hawk_val_t* xstack_fast_operand (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_val_t* v;
|
|
|
|
switch (nde->type)
|
|
{
|
|
/* variable reads: return stack pointer directly, caller must refup */
|
|
case HAWK_NDE_ARG:
|
|
v = HAWK_RTX_STACK_ARG(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
return v;
|
|
case HAWK_NDE_GBL:
|
|
v = HAWK_RTX_STACK_GBL(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
return v;
|
|
case HAWK_NDE_LCL:
|
|
v = HAWK_RTX_STACK_LCL(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
return v;
|
|
case HAWK_NDE_NAMED:
|
|
HAWK_ASSERT(((hawk_nde_var_t*)nde)->id.idxa < rtx->named_slot_count);
|
|
v = HAWK_RTX_STACK_NAMED(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
return v;
|
|
|
|
/* compile-time constants - allocate and return with refup already done */
|
|
case HAWK_NDE_INT: /* see eval_int */
|
|
v = hawk_rtx_makeintval_inline(rtx, ((hawk_nde_int_t*)nde)->val);
|
|
if (HAWK_VTR_IS_POINTER(v) && !HAWK_IS_STATICVAL(v))
|
|
{
|
|
((hawk_val_int_t*)v)->nde = nde;
|
|
hawk_rtx_refupval_nocheck_inline(rtx, v); /* use the nocheck version as the check has been done */
|
|
}
|
|
return v; /* HAWK_NULL on memory allocation failure for large ints */
|
|
|
|
case HAWK_NDE_FLT: /* see eval_flt */
|
|
v = hawk_rtx_makefltval(rtx, ((hawk_nde_flt_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
((hawk_val_flt_t*)v)->nde = nde;
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
return v;
|
|
|
|
case HAWK_NDE_STR: /* see eval_str */
|
|
v = hawk_rtx_makestrvalwithoochars(rtx, ((hawk_nde_str_t*)nde)->ptr, ((hawk_nde_str_t*)nde)->len);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
return v;
|
|
|
|
case HAWK_NDE_MBS: /* see eval_mbs */
|
|
v = hawk_rtx_makembsvalwithbchars(rtx, ((hawk_nde_mbs_t*)nde)->ptr, ((hawk_nde_mbs_t*)nde)->len);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
return v;
|
|
|
|
case HAWK_NDE_CHAR: /* see eval_char */
|
|
v = hawk_rtx_makecharval(rtx, ((hawk_nde_char_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
return v;
|
|
|
|
case HAWK_NDE_BCHR: /* see eval_bchr */
|
|
v = hawk_rtx_makebchrval(rtx, ((hawk_nde_bchr_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
return v;
|
|
|
|
default:
|
|
return XSTACK_NOT_A_LEAF;
|
|
}
|
|
}
|
|
|
|
static hawk_val_t* eval_expression (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_val_t* v;
|
|
int n;
|
|
|
|
#if 0
|
|
if (rtx->exit_level >= EXIT_GLOBAL)
|
|
{
|
|
/* returns HAWK_NULL as if an error occurred but
|
|
* clears the error number. run_main will
|
|
* detect this condition and treat it as a
|
|
* non-error condition.*/
|
|
rtx->errinf.num = HAWK_ENOERR;
|
|
return HAWK_NULL;
|
|
}
|
|
#endif
|
|
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
v = eval_expression0_xstack(rtx, nde);
|
|
#else
|
|
v = eval_expression0(rtx, nde);
|
|
#endif
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
|
|
if (HAWK_RTX_GETVALTYPE(rtx, v) == HAWK_VAL_REX)
|
|
{
|
|
hawk_oocs_t vs;
|
|
int free_vs = 0;
|
|
|
|
/* special case where a regular expression is used in
|
|
* without any match operators:
|
|
* print /abc/;
|
|
* perform match against $0.
|
|
*/
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
|
|
if (HAWK_RTX_GETVALTYPE(rtx, rtx->inrec.d0) == HAWK_VAL_NIL)
|
|
{
|
|
/* the record has never been read.
|
|
* probably, this function has been triggered
|
|
* by the statements in the BEGIN block */
|
|
vs.ptr = HAWK_T("");
|
|
vs.len = 0;
|
|
}
|
|
else
|
|
{
|
|
#if 0
|
|
/* the internal value representing $0 should always be of the string type
|
|
* once it has been set/updated. it is nil initially. */
|
|
HAWK_ASSERT(HAWK_RTX_GETVALTYPE(rtx, rtx->inrec.d0) == HAWK_VAL_STR);
|
|
vs.ptr = ((hawk_val_str_t*)rtx->inrec.d0)->val.ptr;
|
|
vs.len = ((hawk_val_str_t*)rtx->inrec.d0)->val.len;
|
|
#else
|
|
vs.ptr = hawk_rtx_getvaloocstr(rtx, rtx->inrec.d0, &vs.len);
|
|
if (!vs.ptr)
|
|
{
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
free_vs = 1;
|
|
#endif
|
|
}
|
|
|
|
n = hawk_rtx_matchvalwithoocs(rtx, v, &vs, &vs, HAWK_NULL, HAWK_NULL);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
if (free_vs) hawk_rtx_freevaloocstr(rtx, rtx->inrec.d0, vs.ptr);
|
|
if (n <= -1)
|
|
{
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
v = hawk_rtx_makeintval_inline(rtx, (n != 0));
|
|
HAWK_ASSERT(v != HAWK_NULL); /* this will never fail as the value is 0 or 1 */
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
static hawk_val_t* eval_expression0 (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
static eval_expr_t __evaluator[] =
|
|
{
|
|
/* the order of functions here should match the order
|
|
* of node types(hawk_nde_type_t) declared in hawk.h */
|
|
eval_group,
|
|
eval_assignment,
|
|
eval_binary,
|
|
eval_unary,
|
|
eval_incpre,
|
|
eval_incpst,
|
|
eval_cnd,
|
|
eval_fncall_fnc,
|
|
#if defined(KEEP_EVAL_FNCALL_FUN)
|
|
eval_fncall_fun,
|
|
#else
|
|
HAWK_NULL /*eval_fncall_fun*/,
|
|
#endif
|
|
eval_fncall_var,
|
|
eval_modsym,
|
|
eval_char,
|
|
eval_bchr,
|
|
eval_int,
|
|
eval_flt,
|
|
eval_str,
|
|
eval_mbs,
|
|
eval_rex,
|
|
eval_xnil,
|
|
eval_xtrue,
|
|
eval_xfalse,
|
|
eval_xargc,
|
|
eval_xargv,
|
|
eval_xargvidx,
|
|
eval_fun,
|
|
eval_named,
|
|
eval_gbl, /* not used for switch below */
|
|
eval_lcl, /* not used for switch below */
|
|
eval_arg, /* not used for switch below */
|
|
eval_namedidx,
|
|
eval_gblidx,
|
|
eval_lclidx,
|
|
eval_argidx,
|
|
eval_pos,
|
|
eval_getline,
|
|
eval_print,
|
|
eval_printf
|
|
};
|
|
|
|
hawk_val_t* v;
|
|
|
|
HAWK_ASSERT(nde->type >= HAWK_NDE_GRP && (nde->type - HAWK_NDE_GRP) < HAWK_COUNTOF(__evaluator));
|
|
|
|
switch(nde->type)
|
|
{
|
|
#if !defined(KEEP_EVAL_FNCALL_FUN)
|
|
case HAWK_NDE_FNCALL_FUN:
|
|
{
|
|
/* user-defined function */
|
|
hawk_nde_fncall_t* call = (hawk_nde_fncall_t*)nde;
|
|
hawk_fun_t* fun;
|
|
pafn_t pafn;
|
|
|
|
fun = resolve_fncall_fun(rtx, call);
|
|
if (!fun) goto oops;
|
|
|
|
/* push_arg_from_nde() has special handling for references when the function
|
|
* argument spec contains 'r' or 'R'.
|
|
* a reference is passed to a built-in function as a reference value
|
|
* but its evaluation result is passed to user-defined function.
|
|
* I pass HAWK_NULL to prevent special handling.
|
|
* the value change for a reference variable inside a user-defined function is
|
|
* reflected by hawk_rtx_evalcall() specially whereas a built-in function must
|
|
* call hawk_rtx_setrefval() to update the reference.
|
|
*/
|
|
pafn.args = call->args;
|
|
pafn.nargs = call->nargs;
|
|
pafn.argspec = HAWK_NULL; /* fun->argspec */
|
|
v = hawk_rtx_evalcall(rtx, call, fun, push_arg_from_nde, (void*)&pafn, HAWK_NULL, HAWK_NULL);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
case HAWK_NDE_CHAR:
|
|
v = hawk_rtx_makecharval(rtx, ((hawk_nde_char_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!v)) goto oops;
|
|
goto done; /* exit_level can never change. so skip to done */
|
|
|
|
case HAWK_NDE_BCHR:
|
|
v = hawk_rtx_makebchrval(rtx, ((hawk_nde_bchr_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!v)) goto oops;
|
|
goto done; /* exit_level can never change. so skip to done */
|
|
|
|
case HAWK_NDE_INT:
|
|
v = hawk_rtx_makeintval_inline(rtx, ((hawk_nde_int_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!v)) goto oops;
|
|
if (HAWK_VTR_IS_POINTER(v)) ((hawk_val_int_t*)v)->nde = nde;
|
|
goto done; /* exit_level can never change. so skip to done */
|
|
|
|
case HAWK_NDE_FLT:
|
|
v = hawk_rtx_makefltval(rtx, ((hawk_nde_flt_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!v)) goto oops;
|
|
((hawk_val_flt_t*)v)->nde = nde;
|
|
goto done; /* exit_level can never change. so skip to done */
|
|
|
|
case HAWK_NDE_STR:
|
|
v = hawk_rtx_makestrvalwithoochars(rtx, ((hawk_nde_str_t*)nde)->ptr, ((hawk_nde_str_t*)nde)->len);
|
|
if (HAWK_UNLIKELY(!v)) goto oops;
|
|
goto done; /* exit_level can never change. so skip to done */
|
|
|
|
case HAWK_NDE_MBS:
|
|
v = hawk_rtx_makembsvalwithbchars(rtx, ((hawk_nde_mbs_t*)nde)->ptr, ((hawk_nde_mbs_t*)nde)->len);
|
|
if (HAWK_UNLIKELY(!v)) goto oops;
|
|
goto done; /* exit_level can never change. so skip to done */
|
|
|
|
case HAWK_NDE_ARG:
|
|
v = HAWK_RTX_STACK_ARG(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
goto done; /* exit_level can never change. so skip to done */
|
|
|
|
case HAWK_NDE_GBL:
|
|
v = HAWK_RTX_STACK_GBL(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
goto done; /* exit_level can never change. so skip to done */
|
|
|
|
case HAWK_NDE_LCL:
|
|
v = HAWK_RTX_STACK_LCL(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
goto done; /* exit_level can never change. so skip to done */
|
|
|
|
case HAWK_NDE_NAMED:
|
|
HAWK_ASSERT(((hawk_nde_var_t*)nde)->id.idxa < rtx->named_slot_count);
|
|
v = HAWK_RTX_STACK_NAMED(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
goto done; /* exit_level can never change. so skip to done */
|
|
|
|
case HAWK_NDE_ARGIDX:
|
|
case HAWK_NDE_GBLIDX:
|
|
case HAWK_NDE_LCLIDX:
|
|
case HAWK_NDE_NAMEDIDX:
|
|
v = eval_indexed(rtx, (hawk_nde_var_t*)nde);
|
|
break;
|
|
|
|
default:
|
|
v = __evaluator[nde->type - HAWK_NDE_GRP](rtx, nde);
|
|
break;
|
|
}
|
|
|
|
if (HAWK_LIKELY(v) && HAWK_UNLIKELY(rtx->exit_level >= EXIT_GLOBAL))
|
|
{
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
|
|
/* returns HAWK_NULL as if an error occurred but
|
|
* clears the error number. run_main will
|
|
* detect this condition and treat it as a
|
|
* non-error condition.*/
|
|
CLRERR(rtx);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
done:
|
|
/* this function returns a regular expression without further matching.
|
|
* it is done in eval_expression(). */
|
|
return v;
|
|
|
|
oops:
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
static hawk_val_t* eval_group (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
#if 0
|
|
/* eval_binop_in evaluates the HAWK_NDE_GRP specially.
|
|
* so this function should never be reached. */
|
|
HAWK_ASSERT(!"should never happen - NDE_GRP only for in");
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_EINTERN);
|
|
return HAWK_NULL;
|
|
#endif
|
|
|
|
/* a group can be evauluated in a normal context
|
|
* if a program is parsed with HAWK_TOLERANT on.
|
|
* before the introduction of this option, the grouped
|
|
* expression was valid only coerced with the 'in'
|
|
* operator.
|
|
*/
|
|
|
|
/* when a group is evaluated in a normal context,
|
|
* we return the last expression as a value. */
|
|
|
|
hawk_val_t* val;
|
|
hawk_nde_t* np;
|
|
|
|
np = ((hawk_nde_grp_t*)nde)->body;
|
|
|
|
HAWK_ASSERT(np != HAWK_NULL);
|
|
|
|
loop:
|
|
val = eval_expression(rtx, np);
|
|
if (HAWK_UNLIKELY(!val)) return HAWK_NULL;
|
|
|
|
np = np->next;
|
|
if (np)
|
|
{
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
hawk_rtx_refdownval_inline(rtx, val);
|
|
goto loop;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
static hawk_val_t* eval_assignment (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_val_t* val, * ret;
|
|
hawk_nde_ass_t* ass = (hawk_nde_ass_t*)nde;
|
|
|
|
HAWK_ASSERT(ass->left != HAWK_NULL);
|
|
HAWK_ASSERT(ass->right != HAWK_NULL);
|
|
|
|
HAWK_ASSERT(ass->right->next == HAWK_NULL);
|
|
val = eval_expression(rtx, ass->right);
|
|
if (HAWK_UNLIKELY(!val)) return HAWK_NULL;
|
|
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
|
|
if (ass->opcode != HAWK_ASSOP_NONE)
|
|
{
|
|
hawk_val_t* val2, * tmp;
|
|
#if 0
|
|
static binop_func_t binop_func[] =
|
|
{
|
|
/* this table must match hawk_assop_type_t in run-prv.h */
|
|
HAWK_NULL, /* HAWK_ASSOP_NONE */
|
|
eval_binop_plus,
|
|
eval_binop_minus,
|
|
eval_binop_mul,
|
|
eval_binop_div,
|
|
eval_binop_idiv,
|
|
eval_binop_mod,
|
|
eval_binop_exp,
|
|
eval_binop_concat,
|
|
eval_binop_rshift,
|
|
eval_binop_lshift,
|
|
eval_binop_band,
|
|
eval_binop_bxor,
|
|
eval_binop_bor
|
|
};
|
|
#endif
|
|
|
|
HAWK_ASSERT(ass->left->next == HAWK_NULL);
|
|
val2 = eval_expression(rtx, ass->left);
|
|
if (HAWK_UNLIKELY(!val2))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, val);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, val2);
|
|
|
|
#if 0
|
|
HAWK_ASSERT(ass->opcode >= 0);
|
|
HAWK_ASSERT(ass->opcode < HAWK_COUNTOF(binop_func));
|
|
HAWK_ASSERT(binop_func[ass->opcode] != HAWK_NULL);
|
|
tmp = binop_func[ass->opcode](rtx, val2, val);
|
|
#else
|
|
switch (ass->opcode)
|
|
{
|
|
case HAWK_ASSOP_PLUS: tmp = eval_binop_plus(rtx, val2, val); break;
|
|
case HAWK_ASSOP_MINUS: tmp = eval_binop_minus(rtx, val2, val); break;
|
|
case HAWK_ASSOP_MUL: tmp = eval_binop_mul(rtx, val2, val); break;
|
|
case HAWK_ASSOP_DIV: tmp = eval_binop_div(rtx, val2, val); break;
|
|
case HAWK_ASSOP_IDIV: tmp = eval_binop_idiv(rtx, val2, val); break;
|
|
case HAWK_ASSOP_MOD: tmp = eval_binop_mod(rtx, val2, val); break;
|
|
case HAWK_ASSOP_EXP: tmp = eval_binop_exp(rtx, val2, val); break;
|
|
case HAWK_ASSOP_CONCAT: tmp = eval_binop_concat(rtx, val2, val); break;
|
|
case HAWK_ASSOP_RS: tmp = eval_binop_rshift(rtx, val2, val); break;
|
|
case HAWK_ASSOP_LS: tmp = eval_binop_lshift(rtx, val2, val); break;
|
|
case HAWK_ASSOP_BAND: tmp = eval_binop_band(rtx, val2, val); break;
|
|
case HAWK_ASSOP_BXOR: tmp = eval_binop_bxor(rtx, val2, val); break;
|
|
case HAWK_ASSOP_BOR: tmp = eval_binop_bor(rtx, val2, val); break;
|
|
default:
|
|
HAWK_ASSERT(!"should never happen - invalid assigment opcode detected");
|
|
hawk_rtx_seterrbfmt(rtx, &nde->loc, HAWK_EINTERN, "internal error - invalid assignment(%d) opcode detected", (int)ass->opcode);
|
|
tmp = HAWK_NULL;
|
|
break;
|
|
}
|
|
#endif
|
|
if (HAWK_UNLIKELY(!tmp))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, val2);
|
|
hawk_rtx_refdownval_inline(rtx, val);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, val2);
|
|
hawk_rtx_refdownval_inline(rtx, val);
|
|
|
|
val = tmp;
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
}
|
|
|
|
ret = do_assignment(rtx, ass->left, val, ass->is_init);
|
|
hawk_rtx_refdownval_inline(rtx, val);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static hawk_val_t* do_assignment (hawk_rtx_t* rtx, hawk_nde_t* var, hawk_val_t* val, int is_init)
|
|
{
|
|
hawk_val_t* ret;
|
|
hawk_errnum_t errnum;
|
|
|
|
if (var->type >= HAWK_NDE_NAMED && var->type <= HAWK_NDE_ARGIDX)
|
|
{
|
|
hawk_nde_var_t* v = (hawk_nde_var_t*)var;
|
|
if (v->is_const && !is_init)
|
|
{
|
|
hawk_rtx_seterrfmt(rtx, &v->loc, HAWK_EPERM,
|
|
HAWK_T("cannot assign to constant '%.*js'"), v->id.name.len, v->id.name.ptr);
|
|
return HAWK_NULL;
|
|
}
|
|
}
|
|
|
|
switch (var->type)
|
|
{
|
|
case HAWK_NDE_NAMED:
|
|
case HAWK_NDE_GBL:
|
|
case HAWK_NDE_LCL:
|
|
case HAWK_NDE_ARG:
|
|
ret = do_assignment_nonindexed(rtx, (hawk_nde_var_t*)var, val);
|
|
break;
|
|
|
|
case HAWK_NDE_NAMEDIDX:
|
|
case HAWK_NDE_GBLIDX:
|
|
case HAWK_NDE_LCLIDX:
|
|
case HAWK_NDE_ARGIDX:
|
|
{
|
|
#if !defined(HAWK_ENABLE_GC)
|
|
hawk_val_type_t vtype = HAWK_RTX_GETVALTYPE(rtx, val);
|
|
if (vtype == HAWK_VAL_MAP || vtype == HAWK_VAL_ARR)
|
|
{
|
|
/* a map cannot become a member of a map */
|
|
errnum = HAWK_ENONSCATOIDX;
|
|
goto exit_on_error;
|
|
}
|
|
#endif
|
|
|
|
ret = do_assignment_indexed(rtx, (hawk_nde_var_t*)var, val);
|
|
break;
|
|
}
|
|
|
|
case HAWK_NDE_POS:
|
|
{
|
|
hawk_val_type_t vtype = HAWK_RTX_GETVALTYPE(rtx, val);
|
|
if (vtype == HAWK_VAL_MAP || vtype == HAWK_VAL_ARR)
|
|
{
|
|
/* a map cannot be assigned to a positional */
|
|
errnum = HAWK_ENONSCATOPOS;
|
|
goto exit_on_error;
|
|
}
|
|
|
|
ret = do_assignment_positional(rtx, (hawk_nde_pos_t*)var, val);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
HAWK_ASSERT(!"should never happen - invalid variable type");
|
|
errnum = HAWK_EINTERN;
|
|
goto exit_on_error;
|
|
}
|
|
|
|
return ret;
|
|
|
|
exit_on_error:
|
|
hawk_rtx_seterrnum(rtx, &var->loc, errnum);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
static hawk_val_t* do_assignment_nonindexed (hawk_rtx_t* rtx, hawk_nde_var_t* var, hawk_val_t* val)
|
|
{
|
|
hawk_val_type_t vtype;
|
|
|
|
HAWK_ASSERT(
|
|
var->type == HAWK_NDE_NAMED ||
|
|
var->type == HAWK_NDE_GBL ||
|
|
var->type == HAWK_NDE_LCL ||
|
|
var->type == HAWK_NDE_ARG
|
|
);
|
|
|
|
HAWK_ASSERT(var->idx == HAWK_NULL);
|
|
vtype = HAWK_RTX_GETVALTYPE(rtx, val);
|
|
|
|
switch (var->type)
|
|
{
|
|
case HAWK_NDE_NAMED:
|
|
{
|
|
hawk_val_t* old;
|
|
|
|
HAWK_ASSERT(var->id.idxa < rtx->named_slot_count);
|
|
old = HAWK_RTX_STACK_NAMED(rtx, var->id.idxa);
|
|
|
|
if (!(rtx->hawk->opt.trait & HAWK_FLEXMAP))
|
|
{
|
|
hawk_val_type_t old_vtype;
|
|
if ((old_vtype = HAWK_RTX_GETVALTYPE(rtx, old)) == HAWK_VAL_MAP || old_vtype == HAWK_VAL_ARR)
|
|
{
|
|
/* old value is a map - it can only be accessed through indexing. */
|
|
if (vtype == old_vtype)
|
|
hawk_rtx_seterrfmt(rtx, &var->loc, HAWK_ENONSCATONONSCA, HAWK_T("not allowed to change a nonscalar value in '%.*js' to another nonscalar value"), var->id.name.len, var->id.name.ptr);
|
|
else
|
|
hawk_rtx_seterrfmt(rtx, &var->loc, HAWK_ENONSCATOSCALAR, HAWK_T("not allowed to change a nonscalar value in '%.*js' to a scalar value"), var->id.name.len, var->id.name.ptr);
|
|
return HAWK_NULL;
|
|
}
|
|
else if (vtype == HAWK_VAL_MAP || vtype == HAWK_VAL_ARR)
|
|
{
|
|
/* old value is not a map but a new value is a map.
|
|
* a map cannot be assigned to a variable if FLEXMAP is off. */
|
|
hawk_rtx_seterrfmt(rtx, &var->loc, HAWK_ENONSCATOVAR, HAWK_T("not allowed to assign a nonscalar value to a variable '%.*js'"), var->id.name.len, var->id.name.ptr);
|
|
return HAWK_NULL;
|
|
}
|
|
}
|
|
|
|
if (hawk_rtx_setnamedval(rtx, var->id.idxa, val) <= -1)
|
|
{
|
|
ADJERR_LOC(rtx, &var->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case HAWK_NDE_GBL:
|
|
{
|
|
if (set_global(rtx, var->id.idxa, var, val, 1) == -1)
|
|
{
|
|
ADJERR_LOC(rtx, &var->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case HAWK_NDE_LCL:
|
|
{
|
|
hawk_val_t* old = HAWK_RTX_STACK_LCL(rtx,var->id.idxa);
|
|
|
|
if (!(rtx->hawk->opt.trait & HAWK_FLEXMAP))
|
|
{
|
|
hawk_val_type_t old_type;
|
|
|
|
if ((old_type = HAWK_RTX_GETVALTYPE(rtx, old)) == HAWK_VAL_MAP || old_type == HAWK_VAL_ARR)
|
|
{
|
|
/* old value is a map - it can only be accessed through indexing. */
|
|
if (vtype == old_type)
|
|
hawk_rtx_seterrfmt(rtx, &var->loc, HAWK_ENONSCATONONSCA, HAWK_T("not allowed to change a nonscalar value in '%.*js' to another nonscalar value"), var->id.name.len, var->id.name.ptr);
|
|
else
|
|
hawk_rtx_seterrfmt(rtx, &var->loc, HAWK_ENONSCATOSCALAR, HAWK_T("not allowed to change a nonscalar value in '%.*js' to a scalar value"), var->id.name.len, var->id.name.ptr);
|
|
return HAWK_NULL;
|
|
}
|
|
else if (vtype == HAWK_VAL_MAP || vtype == HAWK_VAL_ARR)
|
|
{
|
|
/* old value is not a map but a new value is a map.
|
|
* a map cannot be assigned to a variable if FLEXMAP is off. */
|
|
hawk_rtx_seterrfmt(rtx, &var->loc, HAWK_ENONSCATOVAR, HAWK_T("not allowed to assign a nonscalar value to a variable '%.*js'"), var->id.name.len, var->id.name.ptr);
|
|
return HAWK_NULL;
|
|
}
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, old);
|
|
HAWK_RTX_STACK_LCL(rtx,var->id.idxa) = val;
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
break;
|
|
}
|
|
|
|
case HAWK_NDE_ARG:
|
|
{
|
|
hawk_val_t* old = HAWK_RTX_STACK_ARG(rtx,var->id.idxa);
|
|
|
|
if (!(rtx->hawk->opt.trait & HAWK_FLEXMAP))
|
|
{
|
|
hawk_val_type_t old_type;
|
|
|
|
if ((old_type = HAWK_RTX_GETVALTYPE(rtx, old) == HAWK_VAL_MAP) || old_type == HAWK_VAL_ARR)
|
|
{
|
|
/* old value is a map - it can only be accessed through indexing. */
|
|
if (vtype == old_type)
|
|
hawk_rtx_seterrfmt(rtx, &var->loc, HAWK_ENONSCATONONSCA, HAWK_T("not allowed to change a nonscalar value in '%.*js' to another nonscalar value"), var->id.name.len, var->id.name.ptr);
|
|
else
|
|
hawk_rtx_seterrfmt(rtx, &var->loc, HAWK_ENONSCATOSCALAR, HAWK_T("not allowed to change a nonscalar value in '%.*js' to a scalar value"), var->id.name.len, var->id.name.ptr);
|
|
return HAWK_NULL;
|
|
}
|
|
else if (vtype == HAWK_VAL_MAP || vtype == HAWK_VAL_ARR)
|
|
{
|
|
/* old value is not a map but a new value is a map.
|
|
* a map cannot be assigned to a variable if FLEXMAP is off. */
|
|
hawk_rtx_seterrfmt(rtx, &var->loc, HAWK_ENONSCATOVAR, HAWK_T("not allowed to assign a nonscalar value to a variable '%.*js'"), var->id.name.len, var->id.name.ptr);
|
|
return HAWK_NULL;
|
|
}
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, old);
|
|
HAWK_RTX_STACK_ARG(rtx,var->id.idxa) = val;
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
hawk_rtx_seterrnum(rtx, &var->loc, HAWK_EINTERN);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
static hawk_val_t* do_assignment_indexed (hawk_rtx_t* rtx, hawk_nde_var_t* var, hawk_val_t* val)
|
|
{
|
|
hawk_map_t* map;
|
|
hawk_ooch_t* str = HAWK_NULL;
|
|
hawk_oow_t len;
|
|
hawk_ooch_t idxbuf[HAWK_IDX_BUF_SIZE];
|
|
|
|
hawk_ooi_t idx;
|
|
hawk_arr_t* arr;
|
|
|
|
hawk_nde_t* remidx;
|
|
hawk_val_t* vv; /* existing value pointed to by var */
|
|
hawk_val_type_t vtype;
|
|
|
|
HAWK_ASSERT(
|
|
(var->type == HAWK_NDE_NAMEDIDX ||
|
|
var->type == HAWK_NDE_GBLIDX ||
|
|
var->type == HAWK_NDE_LCLIDX ||
|
|
var->type == HAWK_NDE_ARGIDX) && var->idx != HAWK_NULL);
|
|
#if !defined(HAWK_ENABLE_GC)
|
|
HAWK_ASSERT(HAWK_RTX_GETVALTYPE(rtx, val) != HAWK_VAL_MAP &&
|
|
HAWK_RTX_GETVALTYPE(rtx, val) != HAWK_VAL_ARR);
|
|
#endif
|
|
|
|
/* check the value that the assigned variable points to */
|
|
vv = fetch_topval_from_var(rtx, var);
|
|
vtype = HAWK_RTX_GETVALTYPE(rtx, vv);
|
|
switch (vtype)
|
|
{
|
|
case HAWK_VAL_MAP:
|
|
case HAWK_VAL_ARR:
|
|
{
|
|
val_map_or_arr:
|
|
if (vtype == HAWK_VAL_MAP)
|
|
{
|
|
len = HAWK_COUNTOF(idxbuf);
|
|
str = idxnde_to_str(rtx, var->idx, idxbuf, &len, &remidx, HAWK_NULL);
|
|
if (HAWK_UNLIKELY(!str)) goto oops;
|
|
map = ((hawk_val_map_t*)vv)->map;
|
|
}
|
|
else
|
|
{
|
|
idx = idxnde_to_int(rtx, var->idx, &remidx);
|
|
if (idx <= -1) goto oops;
|
|
arr = ((hawk_val_arr_t*)vv)->arr;
|
|
}
|
|
|
|
#if defined(HAWK_ENABLE_GC)
|
|
while (remidx)
|
|
{
|
|
hawk_val_type_t container_vtype;
|
|
|
|
if (vtype == HAWK_VAL_MAP)
|
|
{
|
|
hawk_map_pair_t* pair;
|
|
pair = hawk_map_search(map, str, len);
|
|
vv = pair? (hawk_val_t*)HAWK_MAP_VPTR(pair): hawk_val_nil;
|
|
}
|
|
else
|
|
{
|
|
vv = (idx < HAWK_ARR_SIZE(arr) && HAWK_ARR_SLOT(arr, idx))? ((hawk_val_t*)HAWK_ARR_DPTR(arr, idx)): hawk_val_nil;
|
|
}
|
|
container_vtype = vtype;
|
|
vtype = HAWK_RTX_GETVALTYPE(rtx, vv);
|
|
|
|
switch (vtype)
|
|
{
|
|
case HAWK_VAL_MAP:
|
|
val_map:
|
|
if (str != idxbuf) hawk_rtx_freemem(rtx, str);
|
|
len = HAWK_COUNTOF(idxbuf);
|
|
str = idxnde_to_str(rtx, remidx, idxbuf, &len, &remidx, HAWK_NULL);
|
|
if (HAWK_UNLIKELY(!str)) goto oops;
|
|
map = ((hawk_val_map_t*)vv)->map;
|
|
break;
|
|
|
|
case HAWK_VAL_ARR:
|
|
val_arr:
|
|
idx = idxnde_to_int(rtx, remidx, &remidx);
|
|
if (idx <= -1) goto oops;
|
|
arr = ((hawk_val_arr_t*)vv)->arr;
|
|
break;
|
|
|
|
default:
|
|
if (vtype == HAWK_VAL_NIL || (rtx->hawk->opt.trait & HAWK_FLEXMAP))
|
|
{
|
|
/* BEGIN { @local a, b; b="hello"; a[1]=b; a[1][2]=20; print a[1][2];} */
|
|
/* BEGIN { a[1]="hello"; a[1][2]=20; print a[1][2]; } */
|
|
/* BEGIN { b="hello"; a[1]=b; a[1][2]=20; print a[1][2]; } */
|
|
|
|
/* well, this is not the first level index.
|
|
* the logic is different from the first level where reset_variable is called.
|
|
* here it simply creates a new map. */
|
|
if (container_vtype == HAWK_VAL_MAP)
|
|
{
|
|
vv = assign_newmapval_in_map(rtx, map, str, len);
|
|
if (HAWK_UNLIKELY(!vv)) { ADJERR_LOC(rtx, &var->loc); goto oops; }
|
|
vtype = HAWK_VAL_MAP;
|
|
goto val_map;
|
|
}
|
|
else
|
|
{
|
|
vv = assign_newarrval_in_arr(rtx, arr, idx);
|
|
if (HAWK_UNLIKELY(!vv)) { ADJERR_LOC(rtx, &var->loc); goto oops; }
|
|
vtype = HAWK_VAL_ARR;
|
|
goto val_arr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hawk_rtx_seterrfmt(rtx, &var->loc, HAWK_ESCALARTONONSCA, HAWK_T("not allowed to change a nested scalar value under '%.*js' to a nonscalar value"), var->id.name.len, var->id.name.ptr);
|
|
goto oops;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(DEBUG_RUN)
|
|
hawk_logbfmt(hawk_rtx_gethawk(rtx), "**** index str=>%js, map->ref=%d, map->type=%d\n", str, (int)v->ref, (int)v->type);
|
|
#endif
|
|
|
|
if (vtype == HAWK_VAL_MAP)
|
|
{
|
|
if (HAWK_UNLIKELY(hawk_map_upsert(map, str, len, val, 0) == HAWK_NULL))
|
|
{
|
|
ADJERR_LOC(rtx, &var->loc);
|
|
goto oops;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (HAWK_UNLIKELY(hawk_arr_upsert(arr, idx, val, 0) == HAWK_ARR_NIL))
|
|
{
|
|
ADJERR_LOC(rtx, &var->loc);
|
|
goto oops;
|
|
}
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, val);
|
|
if (str && str != idxbuf) hawk_rtx_freemem(rtx, str);
|
|
return val;
|
|
}
|
|
|
|
default:
|
|
if (vtype == HAWK_VAL_NIL || (rtx->hawk->opt.trait & HAWK_FLEXMAP))
|
|
{
|
|
/* 1. switch a nil value to a map value at the first level */
|
|
/* 2. if FLEXMAP is on, you can switch a scalar value to a map value */
|
|
vv = assign_newmapval_to_var(rtx, var);
|
|
if (HAWK_UNLIKELY(!vv)) return HAWK_NULL;
|
|
vtype = HAWK_VAL_MAP;
|
|
goto val_map_or_arr;
|
|
}
|
|
else
|
|
{
|
|
/* you can't manipulate a variable pointing to
|
|
* a scalar value with an index if FLEXMAP is off. */
|
|
hawk_rtx_seterrfmt(rtx, &var->loc, HAWK_ESCALARTONONSCA, HAWK_T("not allowed to change a scalar value in '%.*js' to a nonscalar value"), var->id.name.len, var->id.name.ptr);
|
|
goto oops;
|
|
}
|
|
}
|
|
|
|
oops:
|
|
if (str && str != idxbuf) hawk_rtx_freemem(rtx, str);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
static hawk_val_t* do_assignment_positional (hawk_rtx_t* rtx, hawk_nde_pos_t* pos, hawk_val_t* val)
|
|
{
|
|
hawk_val_t* v;
|
|
hawk_val_type_t vtype;
|
|
hawk_int_t lv;
|
|
hawk_oocs_t str;
|
|
int n;
|
|
|
|
v = eval_expression(rtx, pos->val);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
n = hawk_rtx_valtoint_inline(rtx, v, &lv);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
|
|
if (n <= -1)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, &pos->loc, HAWK_EPOSIDX);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
if (!IS_VALID_POSIDX(lv))
|
|
{
|
|
hawk_rtx_seterrnum(rtx, &pos->loc, HAWK_EPOSIDX);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
vtype = HAWK_RTX_GETVALTYPE(rtx, val);
|
|
if (vtype == HAWK_VAL_STR)
|
|
{
|
|
str = ((hawk_val_str_t*)val)->val;
|
|
}
|
|
else
|
|
{
|
|
hawk_rtx_valtostr_out_t out;
|
|
|
|
out.type = HAWK_RTX_VALTOSTR_CPLDUP;
|
|
if (hawk_rtx_valtostr(rtx, val, &out) <= -1)
|
|
{
|
|
ADJERR_LOC(rtx, &pos->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
str = out.u.cpldup;
|
|
}
|
|
|
|
n = hawk_rtx_setrec(rtx, (hawk_oow_t)lv, &str, 0);
|
|
|
|
if (vtype == HAWK_VAL_STR)
|
|
{
|
|
/* do nothing */
|
|
}
|
|
else
|
|
{
|
|
hawk_rtx_freemem(rtx, str.ptr);
|
|
}
|
|
|
|
if (n <= -1) return HAWK_NULL;
|
|
return (lv == 0)? rtx->inrec.d0: rtx->inrec.flds[lv-1].val;
|
|
}
|
|
|
|
static binop_func_t get_binop_func (int opcode)
|
|
{
|
|
static binop_func_t binop_func[] =
|
|
{
|
|
/* the order of the functions should be inline with
|
|
* the operator declaration in rtx.h */
|
|
|
|
HAWK_NULL, /* eval_binop_lor */
|
|
HAWK_NULL, /* eval_binop_land */
|
|
HAWK_NULL, /* eval_binop_in */
|
|
|
|
eval_binop_bor,
|
|
eval_binop_bxor,
|
|
eval_binop_band,
|
|
|
|
eval_binop_teq,
|
|
eval_binop_tne,
|
|
eval_binop_eq,
|
|
eval_binop_ne,
|
|
eval_binop_gt,
|
|
eval_binop_ge,
|
|
eval_binop_lt,
|
|
eval_binop_le,
|
|
|
|
eval_binop_lshift,
|
|
eval_binop_rshift,
|
|
|
|
eval_binop_plus,
|
|
eval_binop_minus,
|
|
eval_binop_mul,
|
|
eval_binop_div,
|
|
eval_binop_idiv,
|
|
eval_binop_mod,
|
|
eval_binop_exp,
|
|
|
|
eval_binop_concat,
|
|
HAWK_NULL, /* eval_binop_ma */
|
|
HAWK_NULL /* eval_binop_nm */
|
|
};
|
|
|
|
HAWK_ASSERT(opcode >= 0 && opcode < HAWK_COUNTOF(binop_func));
|
|
return binop_func[opcode];
|
|
}
|
|
|
|
static hawk_val_t* eval_binary (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)nde;
|
|
hawk_val_t* left, * right, * res;
|
|
|
|
HAWK_ASSERT(exp->type == HAWK_NDE_EXP_BIN);
|
|
|
|
switch (exp->opcode)
|
|
{
|
|
case HAWK_BINOP_LAND:
|
|
res = eval_binop_land(rtx, exp->left, exp->right);
|
|
break;
|
|
|
|
case HAWK_BINOP_LOR:
|
|
res = eval_binop_lor(rtx, exp->left, exp->right);
|
|
break;
|
|
|
|
case HAWK_BINOP_IN:
|
|
/* treat the in operator specially */
|
|
res = eval_binop_in(rtx, exp->left, exp->right);
|
|
break;
|
|
|
|
case HAWK_BINOP_NM:
|
|
res = eval_binop_nm(rtx, exp->left, exp->right);
|
|
break;
|
|
|
|
case HAWK_BINOP_MA:
|
|
res = eval_binop_ma(rtx, exp->left, exp->right);
|
|
break;
|
|
|
|
default:
|
|
HAWK_ASSERT(exp->left->next == HAWK_NULL);
|
|
left = eval_expression(rtx, exp->left);
|
|
if (HAWK_UNLIKELY(!left)) return HAWK_NULL;
|
|
|
|
hawk_rtx_refupval_inline(rtx, left);
|
|
|
|
HAWK_ASSERT(exp->right->next == HAWK_NULL);
|
|
right = eval_expression(rtx, exp->right);
|
|
if (HAWK_UNLIKELY(!right))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, right);
|
|
|
|
HAWK_ASSERT(get_binop_func(exp->opcode) != HAWK_NULL);
|
|
res = get_binop_func(exp->opcode)(rtx, left, right);
|
|
if (HAWK_UNLIKELY(!res)) ADJERR_LOC(rtx, &nde->loc);
|
|
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
hawk_rtx_refdownval_inline(rtx, right);
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_lor (hawk_rtx_t* rtx, hawk_nde_t* left, hawk_nde_t* right)
|
|
{
|
|
/* short-circuit evaluation required special treatment */
|
|
hawk_val_t* lv, * rv, * res;
|
|
|
|
HAWK_ASSERT(left->next == HAWK_NULL);
|
|
lv = eval_expression(rtx, left);
|
|
if (HAWK_UNLIKELY(!lv)) return HAWK_NULL;
|
|
|
|
hawk_rtx_refupval_inline(rtx, lv);
|
|
if (hawk_rtx_valtobool(rtx, lv))
|
|
{
|
|
res = hawk_val_true;
|
|
}
|
|
else
|
|
{
|
|
HAWK_ASSERT(right->next == HAWK_NULL);
|
|
rv = eval_expression(rtx, right);
|
|
if (HAWK_UNLIKELY(!rv))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, lv);
|
|
return HAWK_NULL;
|
|
}
|
|
hawk_rtx_refupval_inline(rtx, rv);
|
|
|
|
res = hawk_rtx_valtobool(rtx,rv)? hawk_val_true: hawk_val_false;
|
|
hawk_rtx_refdownval_inline(rtx, rv);
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, lv);
|
|
|
|
return res;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_land (hawk_rtx_t* rtx, hawk_nde_t* left, hawk_nde_t* right)
|
|
{
|
|
/* short-circuit evaluation required special treatment */
|
|
hawk_val_t* lv, * rv, * res;
|
|
|
|
HAWK_ASSERT(left->next == HAWK_NULL);
|
|
lv = eval_expression(rtx, left);
|
|
if (HAWK_UNLIKELY(!lv)) return HAWK_NULL;
|
|
|
|
hawk_rtx_refupval_inline(rtx, lv);
|
|
if (!hawk_rtx_valtobool(rtx, lv))
|
|
{
|
|
res = hawk_val_false;
|
|
}
|
|
else
|
|
{
|
|
HAWK_ASSERT(right->next == HAWK_NULL);
|
|
rv = eval_expression(rtx, right);
|
|
if (HAWK_UNLIKELY(!rv))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, lv);
|
|
return HAWK_NULL;
|
|
}
|
|
hawk_rtx_refupval_inline(rtx, rv);
|
|
|
|
res = hawk_rtx_valtobool(rtx,rv)? hawk_val_true: hawk_val_false;
|
|
hawk_rtx_refdownval_inline(rtx, rv);
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, lv);
|
|
|
|
return res;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_in (hawk_rtx_t* rtx, hawk_nde_t* left, hawk_nde_t* right)
|
|
{
|
|
hawk_val_t* res;
|
|
hawk_val_t* ropv;
|
|
hawk_val_type_t ropvtype;
|
|
hawk_ooch_t* str;
|
|
hawk_oow_t len;
|
|
hawk_ooch_t idxbuf[HAWK_IDX_BUF_SIZE];
|
|
hawk_nde_t* remidx;
|
|
hawk_int_t idxint;
|
|
|
|
if (right->type == HAWK_NDE_XARGV) goto rvalue_ok;
|
|
|
|
#if defined(HAWK_ENABLE_GC)
|
|
if (right->type < HAWK_NDE_NAMED || right->type > HAWK_NDE_ARGIDX)
|
|
#else
|
|
if (right->type < HAWK_NDE_NAMED || right->type > HAWK_NDE_ARG)
|
|
#endif
|
|
{
|
|
/* the compiler should have handled this case */
|
|
HAWK_ASSERT(!"should never happen - it needs a plain variable");
|
|
hawk_rtx_seterrnum(rtx, &right->loc, HAWK_EINTERN);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
rvalue_ok:
|
|
/* evaluate the left-hand side of the operator */
|
|
len = HAWK_COUNTOF(idxbuf);
|
|
str = (left->type == HAWK_NDE_GRP)? /* it is inefficinet to call idxnde_to_str() for an array. but i don't know if the right hand side is a map or an array yet */
|
|
idxnde_to_str(rtx, ((hawk_nde_grp_t*)left)->body, idxbuf, &len, &remidx, HAWK_NULL):
|
|
idxnde_to_str(rtx, left, idxbuf, &len, &remidx, &idxint);
|
|
if (HAWK_UNLIKELY(!str)) return HAWK_NULL;
|
|
|
|
/* There is no way to express a true multi-dimensional indices for the 'in' operator.
|
|
* So remidx must be NULL here.
|
|
* a[10][20] <--- no way to express the test of 20 under 10 in a.
|
|
* You may use multi-level test conjoined with a logical and operator in such a case.
|
|
* ((10 in a) && (20 in a[10]))
|
|
*
|
|
* '(10, 20) in a' is to test for a[10,20] if 'a' is a map.
|
|
*/
|
|
HAWK_ASSERT(remidx == HAWK_NULL);
|
|
|
|
/* evaluate the right-hand side of the operator */
|
|
HAWK_ASSERT(right->next == HAWK_NULL);
|
|
ropv = eval_expression(rtx, right);
|
|
if (HAWK_UNLIKELY(!ropv))
|
|
{
|
|
if (str != idxbuf) hawk_rtx_freemem(rtx, str);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, ropv);
|
|
|
|
ropvtype = HAWK_RTX_GETVALTYPE(rtx, ropv);
|
|
switch (ropvtype)
|
|
{
|
|
case HAWK_VAL_NIL:
|
|
res = hawk_val_false;
|
|
break;
|
|
|
|
case HAWK_VAL_MAP:
|
|
{
|
|
hawk_map_t* map;
|
|
map = ((hawk_val_map_t*)ropv)->map;
|
|
res = (hawk_map_search(map, str, len) == HAWK_NULL)? hawk_val_false: hawk_val_true;
|
|
break;
|
|
}
|
|
|
|
case HAWK_VAL_ARR:
|
|
{
|
|
hawk_arr_t* arr;
|
|
|
|
if (left->type == HAWK_NDE_GRP)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, &left->loc, HAWK_EARRIDXMULTI);
|
|
goto oops;
|
|
}
|
|
|
|
arr = ((hawk_val_arr_t*)ropv)->arr;
|
|
res = (idxint < 0 || idxint >= HAWK_ARR_SIZE(arr) || !HAWK_ARR_SLOT(arr, idxint))? hawk_val_false: hawk_val_true;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
hawk_rtx_seterrnum(rtx, &right->loc, HAWK_EINROP);
|
|
goto oops;
|
|
}
|
|
|
|
if (str != idxbuf) hawk_rtx_freemem(rtx, str);
|
|
hawk_rtx_refdownval_inline(rtx, ropv);
|
|
return res;
|
|
|
|
oops:
|
|
if (str != idxbuf) hawk_rtx_freemem(rtx, str);
|
|
hawk_rtx_refdownval_inline(rtx, ropv);
|
|
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_bor (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
hawk_int_t l1, l2;
|
|
|
|
if (hawk_rtx_valtoint_inline(rtx, left, &l1) <= -1 ||
|
|
hawk_rtx_valtoint_inline(rtx, right, &l2) <= -1)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EOPERAND);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
return hawk_rtx_makeintval_inline(rtx, l1 | l2);
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_bxor (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
hawk_int_t l1, l2;
|
|
|
|
if (hawk_rtx_valtoint_inline(rtx, left, &l1) <= -1 ||
|
|
hawk_rtx_valtoint_inline(rtx, right, &l2) <= -1)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EOPERAND);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
return hawk_rtx_makeintval_inline(rtx, l1 ^ l2);
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_band (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
hawk_int_t l1, l2;
|
|
|
|
if (hawk_rtx_valtoint_inline(rtx, left, &l1) <= -1 ||
|
|
hawk_rtx_valtoint_inline(rtx, right, &l2) <= -1)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EOPERAND);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
return hawk_rtx_makeintval_inline(rtx, l1 & l2);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static HAWK_INLINE cmp_op_t inverse_cmp_op (cmp_op_t op)
|
|
{
|
|
static cmp_op_t inverse_cmp_op_tab[] =
|
|
{
|
|
CMP_OP_NONE,
|
|
CMP_OP_NE,
|
|
CMP_OP_EQ,
|
|
CMP_OP_LT,
|
|
CMP_OP_LE,
|
|
CMP_OP_GT,
|
|
CMP_OP_GE
|
|
};
|
|
return inverse_cmp_op_tab[op];
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_ensure_not_equal (hawk_rtx_t* rtx, cmp_op_t op_hint)
|
|
{
|
|
/* checking equality is mostly obvious. however, it is not possible
|
|
* to test if one is less/greater than the other for some operands.
|
|
* this function return a number that ensures to make NE to true and
|
|
* all other operations false */
|
|
|
|
switch (op_hint)
|
|
{
|
|
case CMP_OP_EQ:
|
|
case CMP_OP_NE:
|
|
return 1; /* not equal */
|
|
|
|
case CMP_OP_GT:
|
|
case CMP_OP_LT:
|
|
return 0; /* make GT or LT to be false by claiming equal */
|
|
|
|
case CMP_OP_GE:
|
|
return -1; /* make GE false by claiming less */
|
|
case CMP_OP_LE:
|
|
return 1; /* make LE false by claiming greater */
|
|
|
|
default:
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EOPERAND);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
static HAWK_INLINE int __cmp_nil_nil (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_nil_bool (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
if (((hawk_val_bool_t*)right)->val < 0) return 1;
|
|
if (((hawk_val_bool_t*)right)->val > 0) return -1;
|
|
return 0;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_nil_char (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_oochu_t v = HAWK_RTX_GETCHARFROMVAL(rtx, right);
|
|
return (v < 0)? 1: ((v > 0)? -1: 0);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_nil_bchr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_bchu_t v = HAWK_RTX_GETBCHRFROMVAL(rtx, right);
|
|
return (v < 0)? 1: ((v > 0)? -1: 0);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_nil_int (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_int_t v = HAWK_RTX_GETINTFROMVAL(rtx, right);
|
|
return (v < 0)? 1: ((v > 0)? -1: 0);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_nil_flt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
if (((hawk_val_flt_t*)right)->val < 0) return 1;
|
|
if (((hawk_val_flt_t*)right)->val > 0) return -1;
|
|
return 0;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_nil_str (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return (((hawk_val_str_t*)right)->val.len == 0)? 0: -1;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_nil_mbs (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return (((hawk_val_mbs_t*)right)->val.len == 0)? 0: -1;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_nil_fun (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
/* != -> true, all others -> false */
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_nil_map (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return (HAWK_MAP_SIZE(((hawk_val_map_t*)right)->map) == 0)? 0: -1;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_nil_arr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return (HAWK_ARR_SIZE(((hawk_val_arr_t*)right)->arr) == 0)? 0: -1;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static HAWK_INLINE int __cmp_bool_nil (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int v = ((hawk_val_bool_t*)left)->val;
|
|
if (v > 0) return 1; /* greater */
|
|
if (v == 0) return 0; /* equal */
|
|
return -1; /* less, must not happen */
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bool_bool (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int v1 = ((hawk_val_bool_t*)left)->val;
|
|
int v2 = ((hawk_val_bool_t*)right)->val;
|
|
return v1 - v2;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bool_char (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_ooch_t v2;
|
|
hawk_ooch_t* str0;
|
|
hawk_oow_t len0;
|
|
int n;
|
|
|
|
v2 = HAWK_RTX_GETCHARFROMVAL(rtx, right);
|
|
str0 = hawk_rtx_getvaloocstr(rtx, left, &len0);
|
|
if (!str0) return CMP_ERROR;
|
|
n = hawk_comp_oochars(str0, len0, &v2, 1, rtx->gbl.ignorecase);
|
|
hawk_rtx_freevaloocstr(rtx, left, str0);
|
|
return n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bool_bchr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_ooch_t v2;
|
|
hawk_ooch_t* str0;
|
|
hawk_oow_t len0;
|
|
int n;
|
|
|
|
v2 = (hawk_ooch_t)HAWK_RTX_GETBCHRFROMVAL(rtx, right);
|
|
str0 = hawk_rtx_getvaloocstr(rtx, left, &len0);
|
|
if (!str0) return CMP_ERROR;
|
|
n = hawk_comp_oochars(str0, len0, &v2, 1, rtx->gbl.ignorecase);
|
|
hawk_rtx_freevaloocstr(rtx, left, str0);
|
|
return n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bool_int (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int v1 = ((hawk_val_bool_t*)left)->val;
|
|
hawk_int_t v2 = HAWK_RTX_GETINTFROMVAL(rtx, right);
|
|
return (v1 > v2)? 1: ((v1 < v2)? -1: 0);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bool_flt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int v1 = ((hawk_val_bool_t*)left)->val;
|
|
if (v1 > ((hawk_val_flt_t*)right)->val) return 1;
|
|
if (v1 < ((hawk_val_flt_t*)right)->val) return -1;
|
|
return 0;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bool_str (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_t* hawk = hawk_rtx_gethawk(rtx);
|
|
hawk_ooch_t* str0;
|
|
hawk_oow_t len0;
|
|
int n;
|
|
|
|
if ((hawk->opt.trait & HAWK_NCMPONSTR) || right->v_nstr /*> 0*/)
|
|
{
|
|
hawk_int_t ll;
|
|
hawk_flt_t rr;
|
|
|
|
n = hawk_oochars_to_num(
|
|
HAWK_OOCHARS_TO_NUM_MAKE_OPTION(1, 0, HAWK_RTX_IS_STRIPSTRSPC_ON(rtx), 0),
|
|
((hawk_val_str_t*)right)->val.ptr,
|
|
((hawk_val_str_t*)right)->val.len,
|
|
&ll, &rr
|
|
);
|
|
|
|
if (n == 0)
|
|
{
|
|
int v1 = ((hawk_val_bool_t*)left)->val;
|
|
return (v1 > ll)? 1: ((v1 < ll)? -1: 0);
|
|
}
|
|
else if (n > 0)
|
|
{
|
|
int v1 = ((hawk_val_bool_t*)left)->val;
|
|
return (v1 > rr)? 1: ((v1 < rr)? -1: 0);
|
|
}
|
|
}
|
|
|
|
str0 = hawk_rtx_getvaloocstr(rtx, left, &len0);
|
|
if (!str0) return CMP_ERROR;
|
|
n = hawk_comp_oochars(str0, len0, ((hawk_val_str_t*)right)->val.ptr, ((hawk_val_str_t*)right)->val.len, rtx->gbl.ignorecase);
|
|
hawk_rtx_freevaloocstr(rtx, left, str0);
|
|
return n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bool_mbs (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_t* hawk = hawk_rtx_gethawk(rtx);
|
|
hawk_bch_t* str0;
|
|
hawk_oow_t len0;
|
|
int n;
|
|
|
|
if ((hawk->opt.trait & HAWK_NCMPONSTR) || right->v_nstr /*> 0*/)
|
|
{
|
|
hawk_int_t ll;
|
|
hawk_flt_t rr;
|
|
|
|
n = hawk_bchars_to_num(
|
|
HAWK_OOCHARS_TO_NUM_MAKE_OPTION(1, 0, HAWK_RTX_IS_STRIPSTRSPC_ON(rtx), 0),
|
|
((hawk_val_mbs_t*)right)->val.ptr,
|
|
((hawk_val_mbs_t*)right)->val.len,
|
|
&ll, &rr
|
|
);
|
|
|
|
if (n == 0)
|
|
{
|
|
int v1 = ((hawk_val_bool_t*)left)->val;
|
|
return (v1 > ll)? 1: ((v1 < ll)? -1: 0);
|
|
}
|
|
else if (n > 0)
|
|
{
|
|
int v1 = ((hawk_val_bool_t*)left)->val;
|
|
return (v1 > rr)? 1: ((v1 < rr)? -1: 0);
|
|
}
|
|
}
|
|
|
|
str0 = hawk_rtx_getvalbcstr(rtx, left, &len0);
|
|
if (!str0) return CMP_ERROR;
|
|
n = hawk_comp_bchars(str0, len0, ((hawk_val_mbs_t*)right)->val.ptr, ((hawk_val_mbs_t*)right)->val.len, rtx->gbl.ignorecase);
|
|
hawk_rtx_freevalbcstr(rtx, left, str0);
|
|
return n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bool_fun (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bool_map (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bool_arr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static HAWK_INLINE int __cmp_char_nil (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_nil_char(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_char_bool (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_bool_char(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_char_char (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_oochu_t v1 = HAWK_RTX_GETCHARFROMVAL(rtx, left);
|
|
hawk_oochu_t v2 = HAWK_RTX_GETCHARFROMVAL(rtx, right);
|
|
return (v1 > v2)? 1: ((v1 < v2)? -1: 0);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_char_bchr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_oochu_t v1 = HAWK_RTX_GETCHARFROMVAL(rtx, left);
|
|
hawk_bchu_t v2 = HAWK_RTX_GETBCHRFROMVAL(rtx, right);
|
|
return (v1 > v2)? 1: ((v1 < v2)? -1: 0);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_char_int (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
/*
|
|
hawk_oochu_t v1 = HAWK_RTX_GETCHARFROMVAL(rtx, left);
|
|
hawk_int_t v2 = HAWK_RTX_GETINTFROMVAL(rtx, right);
|
|
return (v1 > v2)? 1: ((v1 < v2)? -1: 0);
|
|
*/
|
|
hawk_ooch_t v1;
|
|
hawk_ooch_t* str0;
|
|
hawk_oow_t len0;
|
|
int n;
|
|
|
|
v1 = HAWK_RTX_GETCHARFROMVAL(rtx, left);
|
|
str0 = hawk_rtx_getvaloocstr(rtx, right, &len0);
|
|
if (!str0) return CMP_ERROR;
|
|
n = hawk_comp_oochars(&v1, 1, str0, len0, rtx->gbl.ignorecase);
|
|
hawk_rtx_freevaloocstr(rtx, right, str0);
|
|
return n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_char_flt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
/*
|
|
hawk_ooch_t v1 = HAWK_RTX_GETCHARFROMVAL(rtx, left);
|
|
if (v1 > ((hawk_val_flt_t*)right)->val) return 1;
|
|
if (v1 < ((hawk_val_flt_t*)right)->val) return -1;
|
|
return 0;
|
|
*/
|
|
return __cmp_char_int(rtx, left, right, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_char_str (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_ooch_t v1 = HAWK_RTX_GETCHARFROMVAL(rtx, left);
|
|
return hawk_comp_oochars(&v1, 1, ((hawk_val_str_t*)right)->val.ptr, ((hawk_val_str_t*)right)->val.len, rtx->gbl.ignorecase);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_char_mbs (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_ooch_t v1 = HAWK_RTX_GETCHARFROMVAL(rtx, left);
|
|
hawk_bch_t bc;
|
|
if (v1 > 0xFF) return 1;
|
|
bc = v1;
|
|
return hawk_comp_bchars(&bc, 1, ((hawk_val_mbs_t*)right)->val.ptr, ((hawk_val_mbs_t*)right)->val.len, rtx->gbl.ignorecase);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_char_fun (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_char_map (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_char_arr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static HAWK_INLINE int __cmp_bchr_nil (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_nil_bchr(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bchr_bool (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_bool_bchr(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bchr_char (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_char_bchr(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bchr_bchr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_bchu_t v1 = HAWK_RTX_GETBCHRFROMVAL(rtx, left);
|
|
hawk_bchu_t v2 = HAWK_RTX_GETBCHRFROMVAL(rtx, right);
|
|
return (v1 > v2)? 1: ((v1 < v2)? -1: 0);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bchr_int (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
/*
|
|
hawk_bchu_t v1 = HAWK_RTX_GETBCHRFROMVAL(rtx, left);
|
|
hawk_int_t v2 = HAWK_RTX_GETINTFROMVAL(rtx, right);
|
|
return (v1 > v2)? 1: ((v1 < v2)? -1: 0);
|
|
*/
|
|
hawk_bch_t v1;
|
|
hawk_bch_t* str0;
|
|
hawk_oow_t len0;
|
|
int n;
|
|
|
|
v1 = HAWK_RTX_GETBCHRFROMVAL(rtx, left);
|
|
str0 = hawk_rtx_getvalbcstr(rtx, right, &len0);
|
|
if (!str0) return CMP_ERROR;
|
|
n = hawk_comp_bchars(&v1, 1, str0, len0, rtx->gbl.ignorecase);
|
|
hawk_rtx_freevalbcstr(rtx, right, str0);
|
|
return n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bchr_flt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
/*
|
|
hawk_bchu_t v1 = HAWK_RTX_GETBCHRFROMVAL(rtx, left);
|
|
if (v1 > ((hawk_val_flt_t*)right)->val) return 1;
|
|
if (v1 < ((hawk_val_flt_t*)right)->val) return -1;
|
|
return 0;
|
|
*/
|
|
return __cmp_bchr_int(rtx, left, right, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bchr_str (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_bchu_t v1 = HAWK_RTX_GETBCHRFROMVAL(rtx, left);
|
|
hawk_oochu_t oc = v1;
|
|
return hawk_comp_oochars((const hawk_ooch_t*)&oc, 1, ((hawk_val_str_t*)right)->val.ptr, ((hawk_val_str_t*)right)->val.len, rtx->gbl.ignorecase);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bchr_mbs (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_bch_t v1 = HAWK_RTX_GETBCHRFROMVAL(rtx, left);
|
|
return hawk_comp_bchars(&v1, 1, ((hawk_val_mbs_t*)right)->val.ptr, ((hawk_val_mbs_t*)right)->val.len, rtx->gbl.ignorecase);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bchr_fun (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bchr_map (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_bchr_arr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static HAWK_INLINE int __cmp_int_nil (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_nil_int(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_int_bool (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_int_t v1 = HAWK_RTX_GETINTFROMVAL(rtx, left);
|
|
if (v1 > ((hawk_val_bool_t*)right)->val) return 1;
|
|
if (v1 < ((hawk_val_bool_t*)right)->val) return -1;
|
|
return 0;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_int_char (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_char_int(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_int_bchr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_bchr_int(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_int_int (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_int_t v1 = HAWK_RTX_GETINTFROMVAL(rtx, left);
|
|
hawk_int_t v2 = HAWK_RTX_GETINTFROMVAL(rtx, right);
|
|
return (v1 > v2)? 1: ((v1 < v2)? -1: 0);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_int_flt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_int_t v1 = HAWK_RTX_GETINTFROMVAL(rtx, left);
|
|
if (v1 > ((hawk_val_flt_t*)right)->val) return 1;
|
|
if (v1 < ((hawk_val_flt_t*)right)->val) return -1;
|
|
return 0;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_int_str (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_t* hawk = hawk_rtx_gethawk(rtx);
|
|
hawk_ooch_t* str0;
|
|
hawk_oow_t len0;
|
|
int n;
|
|
|
|
/* SCO CC doesn't seem to handle right->v_nstr > 0 properly */
|
|
if ((hawk->opt.trait & HAWK_NCMPONSTR) || right->v_nstr /*> 0*/)
|
|
{
|
|
hawk_int_t ll, v1;
|
|
hawk_flt_t rr;
|
|
|
|
n = hawk_oochars_to_num(
|
|
HAWK_OOCHARS_TO_NUM_MAKE_OPTION(1, 0, HAWK_RTX_IS_STRIPSTRSPC_ON(rtx), 0),
|
|
((hawk_val_str_t*)right)->val.ptr,
|
|
((hawk_val_str_t*)right)->val.len,
|
|
&ll, &rr
|
|
);
|
|
|
|
v1 = HAWK_RTX_GETINTFROMVAL(rtx, left);
|
|
if (n == 0)
|
|
{
|
|
/* a numeric integral string */
|
|
return (v1 > ll)? 1: ((v1 < ll)? -1: 0);
|
|
}
|
|
else if (n > 0)
|
|
{
|
|
/* a numeric floating-point string */
|
|
return (v1 > rr)? 1: ((v1 < rr)? -1: 0);
|
|
}
|
|
}
|
|
|
|
str0 = hawk_rtx_getvaloocstr(rtx, left, &len0);
|
|
if (!str0) return CMP_ERROR;
|
|
n = hawk_comp_oochars(str0, len0, ((hawk_val_str_t*)right)->val.ptr, ((hawk_val_str_t*)right)->val.len, rtx->gbl.ignorecase);
|
|
hawk_rtx_freevaloocstr(rtx, left, str0);
|
|
return n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_int_mbs (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_t* hawk = hawk_rtx_gethawk(rtx);
|
|
hawk_bch_t* str0;
|
|
hawk_oow_t len0;
|
|
int n;
|
|
|
|
if ((hawk->opt.trait & HAWK_NCMPONSTR) || right->v_nstr /*> 0*/)
|
|
{
|
|
hawk_int_t ll, v1;
|
|
hawk_flt_t rr;
|
|
|
|
n = hawk_bchars_to_num (
|
|
HAWK_OOCHARS_TO_NUM_MAKE_OPTION(1, 0, HAWK_RTX_IS_STRIPSTRSPC_ON(rtx), 0),
|
|
((hawk_val_mbs_t*)right)->val.ptr,
|
|
((hawk_val_mbs_t*)right)->val.len,
|
|
&ll, &rr
|
|
);
|
|
|
|
v1 = HAWK_RTX_GETINTFROMVAL(rtx, left);
|
|
if (n == 0)
|
|
{
|
|
/* a numeric integral string */
|
|
return (v1 > ll)? 1: ((v1 < ll)? -1: 0);
|
|
}
|
|
else if (n > 0)
|
|
{
|
|
/* a numeric floating-point string */
|
|
return (v1 > rr)? 1: ((v1 < rr)? -1: 0);
|
|
}
|
|
}
|
|
|
|
str0 = hawk_rtx_getvalbcstr(rtx, left, &len0);
|
|
if (!str0) return -1;
|
|
n = hawk_comp_bchars(str0, len0, ((hawk_val_mbs_t*)right)->val.ptr, ((hawk_val_mbs_t*)right)->val.len, rtx->gbl.ignorecase);
|
|
hawk_rtx_freevalbcstr(rtx, left, str0);
|
|
return n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_int_fun (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_int_map (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_int_arr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static HAWK_INLINE int __cmp_flt_nil (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_nil_flt(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_flt_bool (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_bool_flt(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_flt_char (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_char_flt(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_flt_bchr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_bchr_flt(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_flt_int (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_int_flt(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_flt_flt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
if (((hawk_val_flt_t*)left)->val > ((hawk_val_flt_t*)right)->val) return 1;
|
|
if (((hawk_val_flt_t*)left)->val < ((hawk_val_flt_t*)right)->val) return -1;
|
|
return 0;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_flt_str (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_t* hawk = hawk_rtx_gethawk(rtx);
|
|
hawk_ooch_t* str0;
|
|
hawk_oow_t len0;
|
|
int n;
|
|
|
|
/* SCO CC doesn't seem to handle right->v_nstr > 0 properly */
|
|
if ((hawk->opt.trait & HAWK_NCMPONSTR) || right->v_nstr /*> 0*/)
|
|
{
|
|
const hawk_ooch_t* end;
|
|
hawk_flt_t rr;
|
|
|
|
rr = hawk_oochars_to_flt(((hawk_val_str_t*)right)->val.ptr, ((hawk_val_str_t*)right)->val.len, &end, HAWK_RTX_IS_STRIPSTRSPC_ON(rtx));
|
|
if (end == ((hawk_val_str_t*)right)->val.ptr + ((hawk_val_str_t*)right)->val.len)
|
|
{
|
|
return (((hawk_val_flt_t*)left)->val > rr)? 1:
|
|
(((hawk_val_flt_t*)left)->val < rr)? -1: 0;
|
|
}
|
|
}
|
|
|
|
str0 = hawk_rtx_getvaloocstr(rtx, left, &len0);
|
|
if (!str0) return CMP_ERROR;
|
|
n = hawk_comp_oochars(str0, len0, ((hawk_val_str_t*)right)->val.ptr, ((hawk_val_str_t*)right)->val.len, rtx->gbl.ignorecase);
|
|
hawk_rtx_freevaloocstr(rtx, left, str0);
|
|
return n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_flt_mbs (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_t* hawk = hawk_rtx_gethawk(rtx);
|
|
hawk_bch_t* str0;
|
|
hawk_oow_t len0;
|
|
int n;
|
|
|
|
if ((hawk->opt.trait & HAWK_NCMPONSTR) || right->v_nstr /*> 0*/)
|
|
{
|
|
const hawk_bch_t* end;
|
|
hawk_flt_t rr;
|
|
|
|
rr = hawk_bchars_to_flt(((hawk_val_mbs_t*)right)->val.ptr, ((hawk_val_mbs_t*)right)->val.len, &end, HAWK_RTX_IS_STRIPSTRSPC_ON(rtx));
|
|
if (end == ((hawk_val_mbs_t*)right)->val.ptr + ((hawk_val_mbs_t*)right)->val.len)
|
|
{
|
|
return (((hawk_val_flt_t*)left)->val > rr)? 1:
|
|
(((hawk_val_flt_t*)left)->val < rr)? -1: 0;
|
|
}
|
|
}
|
|
|
|
str0 = hawk_rtx_getvalbcstr(rtx, left, &len0);
|
|
if (HAWK_UNLIKELY(!str0)) return CMP_ERROR;
|
|
n = hawk_comp_bchars(str0, len0, ((hawk_val_mbs_t*)right)->val.ptr, ((hawk_val_mbs_t*)right)->val.len, rtx->gbl.ignorecase);
|
|
hawk_rtx_freevalbcstr(rtx, left, str0);
|
|
return n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_flt_fun (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_flt_map (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_flt_arr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static HAWK_INLINE int __cmp_str_nil (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_nil_str(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_str_bool (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_bool_str(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_str_char (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_char_str(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_str_bchr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_bchr_str(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_str_int (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_int_str(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_str_flt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_flt_str(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_str_str (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_val_str_t* ls, * rs;
|
|
int stripspc;
|
|
|
|
ls = (hawk_val_str_t*)left;
|
|
rs = (hawk_val_str_t*)right;
|
|
|
|
if (HAWK_LIKELY(ls->v_nstr == 0 || rs->v_nstr == 0))
|
|
{
|
|
/* both are definitely strings */
|
|
return hawk_comp_oochars(ls->val.ptr, ls->val.len, rs->val.ptr, rs->val.len, rtx->gbl.ignorecase);
|
|
}
|
|
|
|
stripspc = HAWK_RTX_IS_STRIPSTRSPC_ON(rtx);
|
|
|
|
if (ls->v_nstr == 1)
|
|
{
|
|
hawk_int_t ll;
|
|
|
|
ll = hawk_oochars_to_int(ls->val.ptr, ls->val.len, HAWK_OOCHARS_TO_INT_MAKE_OPTION(stripspc, stripspc, 0), HAWK_NULL, HAWK_NULL);
|
|
|
|
if (rs->v_nstr == 1)
|
|
{
|
|
hawk_int_t rr;
|
|
|
|
rr = hawk_oochars_to_int(rs->val.ptr, rs->val.len, HAWK_OOCHARS_TO_INT_MAKE_OPTION(stripspc, stripspc, 0), HAWK_NULL, HAWK_NULL);
|
|
|
|
return (ll > rr)? 1:
|
|
(ll < rr)? -1: 0;
|
|
}
|
|
else
|
|
{
|
|
hawk_flt_t rr;
|
|
|
|
HAWK_ASSERT(rs->v_nstr == 2);
|
|
|
|
rr = hawk_oochars_to_flt(rs->val.ptr, rs->val.len, HAWK_NULL, stripspc);
|
|
|
|
return (ll > rr)? 1:
|
|
(ll < rr)? -1: 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hawk_flt_t ll;
|
|
|
|
HAWK_ASSERT(ls->v_nstr == 2);
|
|
|
|
ll = hawk_oochars_to_flt(ls->val.ptr, ls->val.len, HAWK_NULL, stripspc);
|
|
|
|
if (rs->v_nstr == 1)
|
|
{
|
|
hawk_int_t rr;
|
|
|
|
rr = hawk_oochars_to_int(rs->val.ptr, rs->val.len, HAWK_OOCHARS_TO_INT_MAKE_OPTION(stripspc, stripspc, 0), HAWK_NULL, HAWK_NULL);
|
|
|
|
return (ll > rr)? 1:
|
|
(ll < rr)? -1: 0;
|
|
}
|
|
else
|
|
{
|
|
hawk_flt_t rr;
|
|
|
|
HAWK_ASSERT(rs->v_nstr == 2);
|
|
|
|
rr = hawk_oochars_to_flt(rs->val.ptr, rs->val.len, HAWK_NULL, stripspc);
|
|
|
|
return (ll > rr)? 1:
|
|
(ll < rr)? -1: 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_str_mbs (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_val_str_t* ls = (hawk_val_str_t*)left;
|
|
hawk_val_mbs_t* rs = (hawk_val_mbs_t*)right;
|
|
|
|
#if (HAWK_SIZEOF_BCH_T != HAWK_SIZEOF_UINT8_T)
|
|
# error Unsupported size of hawk_bch_t
|
|
#endif
|
|
|
|
#if defined(HAWK_OOCH_IS_BCH)
|
|
return hawk_comp_bchars(ls->val.ptr, ls->val.len, rs->val.ptr, rs->val.len, rtx->gbl.ignorecase);
|
|
#else
|
|
hawk_bch_t* mbsptr;
|
|
hawk_oow_t mbslen;
|
|
int n;
|
|
|
|
mbsptr = hawk_rtx_duputobchars(rtx, ls->val.ptr, ls->val.len, &mbslen);
|
|
if (!mbsptr) return CMP_ERROR;
|
|
n = hawk_comp_bchars(mbsptr, mbslen, (const hawk_bch_t*)rs->val.ptr, rs->val.len, rtx->gbl.ignorecase);
|
|
hawk_rtx_freemem(rtx, mbsptr);
|
|
return n;
|
|
#endif
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_str_fun (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_str_map (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_str_arr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static HAWK_INLINE int __cmp_mbs_nil (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_nil_mbs(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_mbs_bool (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_bool_mbs(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_mbs_char (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_char_mbs(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_mbs_bchr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_bchr_mbs(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_mbs_int (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_int_mbs(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_mbs_flt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_flt_mbs(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_mbs_str (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_str_mbs(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_mbs_mbs (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
hawk_val_mbs_t* ls = (hawk_val_mbs_t*)left;
|
|
hawk_val_mbs_t* rs = (hawk_val_mbs_t*)right;
|
|
#if (HAWK_SIZEOF_BCH_T != HAWK_SIZEOF_UINT8_T)
|
|
# error Unsupported size of hawk_bch_t
|
|
#endif
|
|
return hawk_comp_bchars(ls->val.ptr, ls->val.len, rs->val.ptr, rs->val.len, rtx->gbl.ignorecase);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_mbs_fun (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_mbs_map (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_mbs_arr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static HAWK_INLINE int __cmp_fun_nil (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_fun_bool (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_fun_char (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_fun_bchr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_fun_int (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_fun_flt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_fun_str (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_fun_mbs (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_fun_fun (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return (((hawk_val_fun_t*)left)->fun == ((hawk_val_fun_t*)right)->fun)? 0: __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_fun_map (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_fun_arr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static HAWK_INLINE int __cmp_map_nil (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_nil_map(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_map_bool (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_bool_map(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_map_char (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_char_map(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_map_bchr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_bchr_map(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_map_int (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_int_map(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_map_flt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_flt_map(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_map_str (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_map_mbs (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_map_fun (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_map_map (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
/* can't compare a map with a map */
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EOPERAND);
|
|
return CMP_ERROR;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_map_arr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
/* can't compare a map with an array */
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EOPERAND);
|
|
return CMP_ERROR;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static HAWK_INLINE int __cmp_arr_nil (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_nil_arr(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_arr_bool (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_bool_arr(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_arr_char (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_char_arr(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_arr_bchr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_bchr_arr(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_arr_int (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_int_arr(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_arr_flt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
int n;
|
|
n = __cmp_flt_arr(rtx, right, left, inverse_cmp_op(op_hint));
|
|
if (n == CMP_ERROR) return CMP_ERROR;
|
|
return -n;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_arr_str (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_arr_mbs (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_arr_fun (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
return __cmp_ensure_not_equal(rtx, op_hint);
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_arr_map (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
/* can't compare a map with a map */
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EOPERAND);
|
|
return CMP_ERROR;
|
|
}
|
|
|
|
static HAWK_INLINE int __cmp_arr_arr (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint)
|
|
{
|
|
/* can't compare a map with an array */
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EOPERAND);
|
|
return CMP_ERROR;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
static int HAWK_INLINE cmp_val (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, cmp_op_t op_hint, int* ret)
|
|
{
|
|
int n;
|
|
hawk_val_type_t lvtype, rvtype;
|
|
typedef int (*cmp_val_t) (hawk_rtx_t*, hawk_val_t*, hawk_val_t*, cmp_op_t op_hint);
|
|
|
|
static cmp_val_t func[] =
|
|
{
|
|
/* this table must be synchronized with the HAWK_VAL_XXX values in hawk.h */
|
|
__cmp_nil_nil, __cmp_nil_bool, __cmp_nil_char, __cmp_nil_bchr, __cmp_nil_int, __cmp_nil_flt, __cmp_nil_str, __cmp_nil_mbs, __cmp_nil_fun, __cmp_nil_map, __cmp_nil_arr,
|
|
__cmp_bool_nil, __cmp_bool_bool, __cmp_bool_char, __cmp_bool_bchr, __cmp_bool_int, __cmp_bool_flt, __cmp_bool_str, __cmp_bool_mbs, __cmp_bool_fun, __cmp_bool_map, __cmp_bool_arr,
|
|
|
|
__cmp_char_nil, __cmp_char_bool, __cmp_char_char, __cmp_char_bchr, __cmp_char_int, __cmp_char_flt, __cmp_char_str, __cmp_char_mbs, __cmp_char_fun, __cmp_char_map, __cmp_char_arr,
|
|
__cmp_bchr_nil, __cmp_bchr_bool, __cmp_bchr_char, __cmp_bchr_bchr, __cmp_bchr_int, __cmp_bchr_flt, __cmp_bchr_str, __cmp_bchr_mbs, __cmp_bchr_fun, __cmp_bchr_map, __cmp_bchr_arr,
|
|
__cmp_int_nil, __cmp_int_bool, __cmp_int_char, __cmp_int_bchr, __cmp_int_int, __cmp_int_flt, __cmp_int_str, __cmp_int_mbs, __cmp_int_fun, __cmp_int_map, __cmp_int_arr,
|
|
__cmp_flt_nil, __cmp_flt_bool, __cmp_flt_char, __cmp_flt_bchr, __cmp_flt_int, __cmp_flt_flt, __cmp_flt_str, __cmp_flt_mbs, __cmp_flt_fun, __cmp_flt_map, __cmp_flt_arr,
|
|
__cmp_str_nil, __cmp_str_bool, __cmp_str_char, __cmp_str_bchr, __cmp_str_int, __cmp_str_flt, __cmp_str_str, __cmp_str_mbs, __cmp_str_fun, __cmp_str_map, __cmp_str_arr,
|
|
__cmp_mbs_nil, __cmp_mbs_bool, __cmp_mbs_char, __cmp_mbs_bchr, __cmp_mbs_int, __cmp_mbs_flt, __cmp_mbs_str, __cmp_mbs_mbs, __cmp_mbs_fun, __cmp_mbs_map, __cmp_mbs_arr,
|
|
__cmp_fun_nil, __cmp_fun_bool, __cmp_fun_char, __cmp_fun_bchr, __cmp_fun_int, __cmp_fun_flt, __cmp_fun_str, __cmp_fun_mbs, __cmp_fun_fun, __cmp_fun_map, __cmp_fun_arr,
|
|
__cmp_map_nil, __cmp_map_bool, __cmp_map_char, __cmp_map_bchr, __cmp_map_int, __cmp_map_flt, __cmp_map_str, __cmp_map_mbs, __cmp_map_fun, __cmp_map_map, __cmp_map_arr,
|
|
__cmp_arr_nil, __cmp_arr_bool, __cmp_arr_char, __cmp_arr_bchr, __cmp_arr_int, __cmp_arr_flt, __cmp_arr_str, __cmp_arr_mbs, __cmp_arr_fun, __cmp_arr_map, __cmp_arr_arr
|
|
};
|
|
|
|
lvtype = HAWK_RTX_GETVALTYPE(rtx, left);
|
|
rvtype = HAWK_RTX_GETVALTYPE(rtx, right);
|
|
if ((!(rtx->hawk->opt.trait & HAWK_FLEXMAP) && (lvtype == HAWK_VAL_MAP || rvtype == HAWK_VAL_MAP || lvtype == HAWK_VAL_ARR || rvtype == HAWK_VAL_ARR)) || (lvtype == HAWK_VAL_BOB || rvtype == HAWK_VAL_BOB))
|
|
{
|
|
/* a map can't be compared againt other values in the non-flexible mode. */
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EOPERAND);
|
|
return -1;
|
|
}
|
|
|
|
HAWK_ASSERT(lvtype >= HAWK_VAL_NIL && lvtype <= HAWK_VAL_ARR);
|
|
HAWK_ASSERT(rvtype >= HAWK_VAL_NIL && rvtype <= HAWK_VAL_ARR);
|
|
|
|
/* mapping fomula and table layout assume:
|
|
* HAWK_VAL_NIL = 0
|
|
* HAWK_VAL_BOOL = 1
|
|
* HAWK_VAL_CHAR = 2
|
|
* HAWK_VAL_BCHR = 3
|
|
* HAWK_VAL_INT = 4
|
|
* HAWK_VAL_FLT = 5
|
|
* HAWK_VAL_STR = 6
|
|
* HAWK_VAL_MBS = 7
|
|
* HAWK_VAL_FUN = 8
|
|
* HAWK_VAL_MAP = 9
|
|
* HAWK_VAL_ARR = 10
|
|
*
|
|
* op_hint indicate the operation in progress when this function is called.
|
|
* this hint doesn't require the comparison function to compare using this
|
|
* operation. the comparision function should return 0 if equal, -1 if less,
|
|
* 1 if greater, CMP_ERROR upon error regardless of this hint.
|
|
*/
|
|
n = func[lvtype * 11 + rvtype](rtx, left, right, op_hint);
|
|
if (n == CMP_ERROR) return -1;
|
|
|
|
*ret = n;
|
|
return 0;
|
|
}
|
|
|
|
int hawk_rtx_cmpval (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right, int* ret)
|
|
{
|
|
return cmp_val(rtx, left, right, CMP_OP_NONE, ret);
|
|
}
|
|
|
|
static int teq_val (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
int n;
|
|
hawk_val_type_t lt, rt;
|
|
|
|
if (left == right) n = 1;
|
|
else
|
|
{
|
|
lt = HAWK_RTX_GETVALTYPE(rtx, left);
|
|
rt = HAWK_RTX_GETVALTYPE(rtx, right);
|
|
|
|
if (lt != rt) n = 0;
|
|
else
|
|
{
|
|
switch (lt)
|
|
{
|
|
case HAWK_VAL_NIL:
|
|
n = 1;
|
|
break;
|
|
|
|
case HAWK_VAL_BOOL:
|
|
n = ((hawk_val_bool_t*)left)->val == ((hawk_val_bool_t*)right)->val;
|
|
break;
|
|
|
|
case HAWK_VAL_CHAR:
|
|
/* since a CHAR value is only reprensented in the value pointer,
|
|
* n is guaranteed to be 0 here. so the following check isn't needed */
|
|
n = (HAWK_RTX_GETCHARFROMVAL(rtx, left) == HAWK_RTX_GETCHARFROMVAL(rtx, right));
|
|
break;
|
|
|
|
case HAWK_VAL_BCHR:
|
|
/* since a BCHR value is only reprensented in the value pointer,
|
|
* n is guaranteed to be 0 here. so the following check isn't needed */
|
|
n = (HAWK_RTX_GETBCHRFROMVAL(rtx, left) == HAWK_RTX_GETBCHRFROMVAL(rtx, right));
|
|
break;
|
|
|
|
case HAWK_VAL_INT:
|
|
n = (HAWK_RTX_GETINTFROMVAL(rtx, left) == HAWK_RTX_GETINTFROMVAL(rtx, right));
|
|
break;
|
|
|
|
case HAWK_VAL_FLT:
|
|
n = ((hawk_val_flt_t*)left)->val == ((hawk_val_flt_t*)right)->val;
|
|
break;
|
|
|
|
case HAWK_VAL_STR:
|
|
n = hawk_comp_oochars(
|
|
((hawk_val_str_t*)left)->val.ptr,
|
|
((hawk_val_str_t*)left)->val.len,
|
|
((hawk_val_str_t*)right)->val.ptr,
|
|
((hawk_val_str_t*)right)->val.len,
|
|
rtx->gbl.ignorecase) == 0;
|
|
break;
|
|
|
|
case HAWK_VAL_MBS:
|
|
n = hawk_comp_bchars(
|
|
((hawk_val_mbs_t*)left)->val.ptr,
|
|
((hawk_val_mbs_t*)left)->val.len,
|
|
((hawk_val_mbs_t*)right)->val.ptr,
|
|
((hawk_val_mbs_t*)right)->val.len,
|
|
rtx->gbl.ignorecase) == 0;
|
|
break;
|
|
|
|
case HAWK_VAL_FUN:
|
|
n = ((hawk_val_fun_t*)left)->fun == ((hawk_val_fun_t*)right)->fun;
|
|
break;
|
|
|
|
default:
|
|
/* map-x and map-y are always different regardless of
|
|
* their contents. however, if they are pointing to the
|
|
* same map value, it won't reach here but will be
|
|
* handled by the first check in this function */
|
|
n = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_teq (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
return teq_val(rtx, left, right)? hawk_val_true: hawk_val_false;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_tne (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
return teq_val(rtx, left, right)? hawk_val_false: hawk_val_true;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_eq (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
int n;
|
|
if (cmp_val(rtx, left, right, CMP_OP_EQ, &n) <= -1) return HAWK_NULL;
|
|
return (n == 0)? hawk_val_true: hawk_val_false;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_ne (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
int n;
|
|
if (cmp_val(rtx, left, right, CMP_OP_NE, &n) <= -1) return HAWK_NULL;
|
|
return (n != 0)? hawk_val_true: hawk_val_false;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_gt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
int n;
|
|
if (cmp_val(rtx, left, right, CMP_OP_GT, &n) <= -1) return HAWK_NULL;
|
|
return (n > 0)? hawk_val_true: hawk_val_false;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_ge (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
int n;
|
|
if (cmp_val(rtx, left, right, CMP_OP_GE, &n) <= -1) return HAWK_NULL;
|
|
return (n >= 0)? hawk_val_true: hawk_val_false;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_lt (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
int n;
|
|
if (cmp_val(rtx, left, right, CMP_OP_LT, &n) <= -1) return HAWK_NULL;
|
|
return (n < 0)? hawk_val_true: hawk_val_false;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_le (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
int n;
|
|
if (cmp_val(rtx, left, right, CMP_OP_LE, &n) <= -1) return HAWK_NULL;
|
|
return (n <= 0)? hawk_val_true: hawk_val_false;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_lshift (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
hawk_int_t l1, l2;
|
|
|
|
if (hawk_rtx_valtoint_inline(rtx, left, &l1) <= -1 ||
|
|
hawk_rtx_valtoint_inline(rtx, right, &l2) <= -1)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EOPERAND);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
return hawk_rtx_makeintval_inline(rtx, l1 << l2);
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_rshift (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
hawk_int_t l1, l2;
|
|
|
|
if (hawk_rtx_valtoint_inline(rtx, left, &l1) <= -1 ||
|
|
hawk_rtx_valtoint_inline(rtx, right, &l2) <= -1)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EOPERAND);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
return hawk_rtx_makeintval_inline(rtx, l1 >> l2);
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_plus (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
int n1, n2, n3;
|
|
hawk_int_t l1, l2;
|
|
hawk_flt_t r1, r2;
|
|
|
|
n1 = hawk_rtx_valtonum(rtx, left, &l1, &r1);
|
|
n2 = hawk_rtx_valtonum(rtx, right, &l2, &r2);
|
|
|
|
if (n1 <= -1 || n2 <= -1)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EOPERAND);
|
|
return HAWK_NULL;
|
|
}
|
|
/*
|
|
n1 n2 n3
|
|
0 0 = 0
|
|
1 0 = 1
|
|
0 1 = 2
|
|
1 1 = 3
|
|
*/
|
|
n3 = n1 + (n2 << 1);
|
|
HAWK_ASSERT(n3 >= 0 && n3 <= 3);
|
|
|
|
return (n3 == 0)? hawk_rtx_makeintval_inline(rtx,(hawk_int_t)l1+(hawk_int_t)l2):
|
|
(n3 == 1)? hawk_rtx_makefltval(rtx,(hawk_flt_t)r1+(hawk_flt_t)l2):
|
|
(n3 == 2)? hawk_rtx_makefltval(rtx,(hawk_flt_t)l1+(hawk_flt_t)r2):
|
|
hawk_rtx_makefltval(rtx,(hawk_flt_t)r1+(hawk_flt_t)r2);
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_minus (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
int n1, n2, n3;
|
|
hawk_int_t l1, l2;
|
|
hawk_flt_t r1, r2;
|
|
|
|
n1 = hawk_rtx_valtonum(rtx, left, &l1, &r1);
|
|
n2 = hawk_rtx_valtonum(rtx, right, &l2, &r2);
|
|
|
|
if (n1 <= -1 || n2 <= -1)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EOPERAND);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
n3 = n1 + (n2 << 1);
|
|
HAWK_ASSERT(n3 >= 0 && n3 <= 3);
|
|
return (n3 == 0)? hawk_rtx_makeintval_inline(rtx,(hawk_int_t)l1-(hawk_int_t)l2):
|
|
(n3 == 1)? hawk_rtx_makefltval(rtx,(hawk_flt_t)r1-(hawk_flt_t)l2):
|
|
(n3 == 2)? hawk_rtx_makefltval(rtx,(hawk_flt_t)l1-(hawk_flt_t)r2):
|
|
hawk_rtx_makefltval(rtx,(hawk_flt_t)r1-(hawk_flt_t)r2);
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_mul (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
int n1, n2, n3;
|
|
hawk_int_t l1, l2;
|
|
hawk_flt_t r1, r2;
|
|
|
|
n1 = hawk_rtx_valtonum(rtx, left, &l1, &r1);
|
|
n2 = hawk_rtx_valtonum(rtx, right, &l2, &r2);
|
|
|
|
if (n1 <= -1 || n2 <= -1)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EOPERAND);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
n3 = n1 + (n2 << 1);
|
|
HAWK_ASSERT(n3 >= 0 && n3 <= 3);
|
|
return (n3 == 0)? hawk_rtx_makeintval_inline(rtx,(hawk_int_t)l1*(hawk_int_t)l2):
|
|
(n3 == 1)? hawk_rtx_makefltval(rtx,(hawk_flt_t)r1*(hawk_flt_t)l2):
|
|
(n3 == 2)? hawk_rtx_makefltval(rtx,(hawk_flt_t)l1*(hawk_flt_t)r2):
|
|
hawk_rtx_makefltval(rtx,(hawk_flt_t)r1*(hawk_flt_t)r2);
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_div (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
int n1, n2, n3;
|
|
hawk_int_t l1, l2;
|
|
hawk_flt_t r1, r2;
|
|
hawk_val_t* res;
|
|
|
|
n1 = hawk_rtx_valtonum(rtx, left, &l1, &r1);
|
|
n2 = hawk_rtx_valtonum(rtx, right, &l2, &r2);
|
|
|
|
if (n1 <= -1 || n2 <= -1)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EOPERAND);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
n3 = n1 + (n2 << 1);
|
|
switch (n3)
|
|
{
|
|
case 0:
|
|
if (l2 == 0)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EDIVBY0);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
if ((l1 % l2) == 0)
|
|
{
|
|
res = hawk_rtx_makeintval_inline(rtx, l1 / l2);
|
|
}
|
|
else
|
|
{
|
|
res = hawk_rtx_makefltval(rtx, (hawk_flt_t)l1 / (hawk_flt_t)l2);
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
res = hawk_rtx_makefltval(rtx, r1 / (hawk_flt_t)l2);
|
|
break;
|
|
|
|
case 2:
|
|
res = hawk_rtx_makefltval(rtx, (hawk_flt_t)l1 / r2);
|
|
break;
|
|
|
|
case 3:
|
|
res = hawk_rtx_makefltval(rtx, r1 / r2);
|
|
break;
|
|
|
|
default:
|
|
HAWK_ASSERT(!"should never happen - invalid intermediate state in handling div");
|
|
hawk_rtx_seterrbfmt(rtx, HAWK_NULL, HAWK_EINTERN, "internal error in handling div");
|
|
res = HAWK_NULL;
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_idiv (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
int n1, n2, n3;
|
|
hawk_int_t l1, l2;
|
|
hawk_flt_t r1, r2, quo;
|
|
hawk_val_t* res;
|
|
|
|
n1 = hawk_rtx_valtonum(rtx, left, &l1, &r1);
|
|
n2 = hawk_rtx_valtonum(rtx, right, &l2, &r2);
|
|
|
|
if (n1 <= -1 || n2 <= -1)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EOPERAND);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
n3 = n1 + (n2 << 1);
|
|
switch (n3)
|
|
{
|
|
case 0:
|
|
if (l2 == 0)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EDIVBY0);
|
|
return HAWK_NULL;
|
|
}
|
|
res = hawk_rtx_makeintval_inline(rtx, l1 / l2);
|
|
break;
|
|
|
|
case 1:
|
|
quo = r1 / (hawk_flt_t)l2;
|
|
res = hawk_rtx_makeintval_inline(rtx, (hawk_int_t)quo);
|
|
break;
|
|
|
|
case 2:
|
|
quo = (hawk_flt_t)l1 / r2;
|
|
res = hawk_rtx_makeintval_inline(rtx, (hawk_int_t)quo);
|
|
break;
|
|
|
|
case 3:
|
|
quo = r1 / r2;
|
|
res = hawk_rtx_makeintval_inline(rtx, (hawk_int_t)quo);
|
|
break;
|
|
|
|
default:
|
|
HAWK_ASSERT(!"should never happen - invalid intermediate state in handling idiv");
|
|
hawk_rtx_seterrbfmt(rtx, HAWK_NULL, HAWK_EINTERN, "internal error in handling idiv");
|
|
res = HAWK_NULL;
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_mod (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
int n1, n2, n3;
|
|
hawk_int_t l1, l2;
|
|
hawk_flt_t r1, r2;
|
|
hawk_val_t* res;
|
|
|
|
/* the mod function must be provided when the hawk object is created to be able to calculate floating-pointer remainder */
|
|
HAWK_ASSERT(rtx->hawk->prm.math.mod != HAWK_NULL);
|
|
|
|
n1 = hawk_rtx_valtonum(rtx, left, &l1, &r1);
|
|
n2 = hawk_rtx_valtonum(rtx, right, &l2, &r2);
|
|
|
|
if (n1 <= -1 || n2 <= -1)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EOPERAND);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
n3 = n1 + (n2 << 1);
|
|
switch (n3)
|
|
{
|
|
case 0:
|
|
if (l2 == 0)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EDIVBY0);
|
|
return HAWK_NULL;
|
|
}
|
|
res = hawk_rtx_makeintval_inline(rtx, l1 % l2);
|
|
break;
|
|
|
|
case 1:
|
|
res = hawk_rtx_makefltval(rtx, rtx->hawk->prm.math.mod(hawk_rtx_gethawk(rtx), r1, (hawk_flt_t)l2));
|
|
break;
|
|
|
|
case 2:
|
|
res = hawk_rtx_makefltval(rtx, rtx->hawk->prm.math.mod(hawk_rtx_gethawk(rtx), (hawk_flt_t)l1, r2));
|
|
break;
|
|
|
|
case 3:
|
|
res = hawk_rtx_makefltval(rtx, rtx->hawk->prm.math.mod(hawk_rtx_gethawk(rtx), (hawk_flt_t)r1, (hawk_flt_t)r2));
|
|
break;
|
|
|
|
default:
|
|
HAWK_ASSERT(!"should never happen - invalid intermediate state in handling mod");
|
|
hawk_rtx_seterrbfmt(rtx, HAWK_NULL, HAWK_EINTERN, "internal error in handling mod");
|
|
res = HAWK_NULL;
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_exp (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
int n1, n2, n3;
|
|
hawk_int_t l1, l2;
|
|
hawk_flt_t r1, r2;
|
|
hawk_val_t* res;
|
|
|
|
n1 = hawk_rtx_valtonum(rtx, left, &l1, &r1);
|
|
n2 = hawk_rtx_valtonum(rtx, right, &l2, &r2);
|
|
|
|
if (n1 <= -1 || n2 <= -1)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EOPERAND);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
n3 = n1 + (n2 << 1);
|
|
switch (n3)
|
|
{
|
|
case 0:
|
|
/* left - int, right - int */
|
|
if (l2 >= 0)
|
|
{
|
|
hawk_int_t v = 1;
|
|
while (l2-- > 0) v *= l1;
|
|
res = hawk_rtx_makeintval_inline(rtx, v);
|
|
}
|
|
else if (l1 == 0)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EDIVBY0);
|
|
return HAWK_NULL;
|
|
}
|
|
else
|
|
{
|
|
hawk_flt_t v = 1.0;
|
|
l2 *= -1;
|
|
while (l2-- > 0) v /= l1;
|
|
res = hawk_rtx_makefltval(rtx, v);
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
/* left - real, right - int */
|
|
if (l2 >= 0)
|
|
{
|
|
hawk_flt_t v = 1.0;
|
|
while (l2-- > 0) v *= r1;
|
|
res = hawk_rtx_makefltval(rtx, v);
|
|
}
|
|
else if (r1 == 0.0)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EDIVBY0);
|
|
return HAWK_NULL;
|
|
}
|
|
else
|
|
{
|
|
hawk_flt_t v = 1.0;
|
|
l2 *= -1;
|
|
while (l2-- > 0) v /= r1;
|
|
res = hawk_rtx_makefltval(rtx, v);
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
/* left - int, right - real */
|
|
res = hawk_rtx_makefltval(
|
|
rtx,
|
|
rtx->hawk->prm.math.pow(hawk_rtx_gethawk(rtx), (hawk_flt_t)l1, r2)
|
|
);
|
|
break;
|
|
|
|
case 3:
|
|
/* left - real, right - real */
|
|
res = hawk_rtx_makefltval(
|
|
rtx,
|
|
rtx->hawk->prm.math.pow(hawk_rtx_gethawk(rtx), r1, r2)
|
|
);
|
|
break;
|
|
|
|
default:
|
|
HAWK_ASSERT(!"should never happen - invalid intermediate state in handling exp");
|
|
hawk_rtx_seterrbfmt(rtx, HAWK_NULL, HAWK_EINTERN, "internal error in handling exp");
|
|
res = HAWK_NULL;
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_concat (hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right)
|
|
{
|
|
hawk_val_t* res;
|
|
|
|
switch (HAWK_RTX_GETVALTYPE(rtx, left))
|
|
{
|
|
case HAWK_VAL_BCHR:
|
|
case HAWK_VAL_MBS:
|
|
case HAWK_VAL_BOB:
|
|
{
|
|
hawk_bcs_t l, r;
|
|
|
|
l.ptr = hawk_rtx_getvalbcstr(rtx, left, &l.len);
|
|
if (HAWK_UNLIKELY(!l.ptr)) return HAWK_NULL;
|
|
|
|
r.ptr = hawk_rtx_getvalbcstr(rtx, right, &r.len);
|
|
if (HAWK_UNLIKELY(!r.ptr))
|
|
{
|
|
hawk_rtx_freevalbcstr(rtx, left, l.ptr);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
res = (hawk_val_t*)hawk_rtx_makembsvalwithbchars2(rtx, l.ptr, l.len, r.ptr, r.len);
|
|
|
|
hawk_rtx_freevalbcstr(rtx, right, r.ptr);
|
|
hawk_rtx_freevalbcstr(rtx, left, l.ptr);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
hawk_oocs_t l, r;
|
|
|
|
l.ptr = hawk_rtx_getvaloocstr(rtx, left, &l.len);
|
|
if (HAWK_UNLIKELY(!l.ptr)) return HAWK_NULL;
|
|
|
|
r.ptr = hawk_rtx_getvaloocstr(rtx, right, &r.len);
|
|
if (HAWK_UNLIKELY(!r.ptr))
|
|
{
|
|
hawk_rtx_freevaloocstr(rtx, left, l.ptr);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
res = (hawk_val_t*)hawk_rtx_makestrvalwithoochars2(rtx, l.ptr, l.len, r.ptr, r.len);
|
|
|
|
hawk_rtx_freevaloocstr(rtx, right, r.ptr);
|
|
hawk_rtx_freevaloocstr(rtx, left, l.ptr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_match0 (
|
|
hawk_rtx_t* rtx, hawk_val_t* left, hawk_val_t* right,
|
|
const hawk_loc_t* lloc, const hawk_loc_t* rloc, int ret)
|
|
{
|
|
hawk_val_t* res;
|
|
hawk_oocs_t out;
|
|
int n;
|
|
|
|
out.ptr = hawk_rtx_getvaloocstr(rtx, left, &out.len);
|
|
if (HAWK_UNLIKELY(!out.ptr)) return HAWK_NULL;
|
|
|
|
n = hawk_rtx_matchvalwithoocs(rtx, right, &out, &out, HAWK_NULL, HAWK_NULL);
|
|
hawk_rtx_freevaloocstr(rtx, left, out.ptr);
|
|
|
|
if (HAWK_UNLIKELY(n <= -1))
|
|
{
|
|
ADJERR_LOC(rtx, lloc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
res = hawk_rtx_makeintval_inline(rtx, (n == ret));
|
|
if (HAWK_UNLIKELY(!res))
|
|
{
|
|
ADJERR_LOC(rtx, lloc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_ma (hawk_rtx_t* rtx, hawk_nde_t* left, hawk_nde_t* right)
|
|
{
|
|
hawk_val_t* lv, * rv, * res;
|
|
|
|
HAWK_ASSERT(left->next == HAWK_NULL);
|
|
HAWK_ASSERT(right->next == HAWK_NULL);
|
|
|
|
lv = eval_expression(rtx, left);
|
|
if (HAWK_UNLIKELY(!lv)) return HAWK_NULL;
|
|
|
|
hawk_rtx_refupval_inline(rtx, lv);
|
|
|
|
rv = eval_expression0(rtx, right);
|
|
if (HAWK_UNLIKELY(!rv))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, lv);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, rv);
|
|
|
|
res = eval_binop_match0(rtx, lv, rv, &left->loc, &right->loc, 1);
|
|
|
|
hawk_rtx_refdownval_inline(rtx, rv);
|
|
hawk_rtx_refdownval_inline(rtx, lv);
|
|
|
|
return res;
|
|
}
|
|
|
|
static hawk_val_t* eval_binop_nm (hawk_rtx_t* rtx, hawk_nde_t* left, hawk_nde_t* right)
|
|
{
|
|
hawk_val_t* lv, * rv, * res;
|
|
|
|
HAWK_ASSERT(left->next == HAWK_NULL);
|
|
HAWK_ASSERT(right->next == HAWK_NULL);
|
|
|
|
lv = eval_expression(rtx, left);
|
|
if (HAWK_UNLIKELY(!lv)) return HAWK_NULL;
|
|
|
|
hawk_rtx_refupval_inline(rtx, lv);
|
|
|
|
rv = eval_expression0(rtx, right);
|
|
if (HAWK_UNLIKELY(!rv))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, lv);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, rv);
|
|
|
|
res = eval_binop_match0(rtx, lv, rv, &left->loc, &right->loc, 0);
|
|
|
|
hawk_rtx_refdownval_inline(rtx, rv);
|
|
hawk_rtx_refdownval_inline(rtx, lv);
|
|
|
|
return res;
|
|
}
|
|
|
|
static hawk_val_t* eval_unary (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_val_t* left, * res = HAWK_NULL;
|
|
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)nde;
|
|
int n;
|
|
hawk_int_t l;
|
|
hawk_flt_t r;
|
|
|
|
HAWK_ASSERT(
|
|
exp->type == HAWK_NDE_EXP_UNR);
|
|
HAWK_ASSERT(
|
|
exp->left != HAWK_NULL && exp->right == HAWK_NULL);
|
|
HAWK_ASSERT(
|
|
exp->opcode == HAWK_UNROP_PLUS ||
|
|
exp->opcode == HAWK_UNROP_MINUS ||
|
|
exp->opcode == HAWK_UNROP_LNOT ||
|
|
exp->opcode == HAWK_UNROP_BNOT);
|
|
|
|
HAWK_ASSERT(exp->left->next == HAWK_NULL);
|
|
left = eval_expression(rtx, exp->left);
|
|
if (HAWK_UNLIKELY(!left)) return HAWK_NULL;
|
|
|
|
hawk_rtx_refupval_inline(rtx, left);
|
|
|
|
switch (exp->opcode)
|
|
{
|
|
case HAWK_UNROP_MINUS:
|
|
n = hawk_rtx_valtonum(rtx, left, &l, &r);
|
|
if (HAWK_UNLIKELY(n <= -1)) goto exit_func;
|
|
res = (n == 0)? hawk_rtx_makeintval_inline(rtx, -l): hawk_rtx_makefltval(rtx, -r);
|
|
break;
|
|
|
|
case HAWK_UNROP_LNOT:
|
|
res = hawk_rtx_makeboolval(rtx, !hawk_rtx_valtobool(rtx, left));
|
|
break;
|
|
|
|
case HAWK_UNROP_BNOT:
|
|
n = hawk_rtx_valtoint_inline(rtx, left, &l);
|
|
if (HAWK_UNLIKELY(n <= -1)) goto exit_func;
|
|
res = hawk_rtx_makeintval_inline(rtx, ~l);
|
|
break;
|
|
|
|
case HAWK_UNROP_PLUS:
|
|
n = hawk_rtx_valtonum(rtx, left, &l, &r);
|
|
if (HAWK_UNLIKELY(n <= -1)) goto exit_func;
|
|
|
|
res = (n == 0)? hawk_rtx_makeintval_inline(rtx, l):
|
|
hawk_rtx_makefltval(rtx, r);
|
|
break;
|
|
}
|
|
|
|
exit_func:
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
if (HAWK_UNLIKELY(!res)) ADJERR_LOC(rtx, &nde->loc);
|
|
return res;
|
|
}
|
|
|
|
static hawk_val_t* eval_incpre (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_val_t* left, * res;
|
|
hawk_val_type_t left_vtype;
|
|
hawk_int_t inc_val_int;
|
|
hawk_flt_t inc_val_flt;
|
|
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)nde;
|
|
|
|
HAWK_ASSERT(exp->type == HAWK_NDE_EXP_INCPRE);
|
|
HAWK_ASSERT(exp->left != HAWK_NULL && exp->right == HAWK_NULL);
|
|
|
|
/* this way of checking if the l-value is assignable is
|
|
* ugly as it is dependent on the node types defined in hawk.h
|
|
* but let's keep going this way for the time being. */
|
|
if (exp->left->type < HAWK_NDE_NAMED ||
|
|
/*exp->left->type > HAWK_NDE_ARGIDX) XXX */
|
|
exp->left->type > HAWK_NDE_POS)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_EOPERAND);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
if (exp->opcode == HAWK_INCOP_PLUS)
|
|
{
|
|
inc_val_int = 1;
|
|
inc_val_flt = 1.0;
|
|
}
|
|
else if (exp->opcode == HAWK_INCOP_MINUS)
|
|
{
|
|
inc_val_int = -1;
|
|
inc_val_flt = -1.0;
|
|
}
|
|
else
|
|
{
|
|
HAWK_ASSERT(!"should never happen - invalid opcode");
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_EINTERN);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
HAWK_ASSERT(exp->left->next == HAWK_NULL);
|
|
left = eval_expression(rtx, exp->left);
|
|
if (HAWK_UNLIKELY(!left)) return HAWK_NULL;
|
|
|
|
hawk_rtx_refupval_inline(rtx, left);
|
|
left_vtype = HAWK_RTX_GETVALTYPE(rtx, left);
|
|
|
|
switch (left_vtype)
|
|
{
|
|
case HAWK_VAL_INT:
|
|
{
|
|
hawk_int_t r = HAWK_RTX_GETINTFROMVAL(rtx, left);
|
|
res = hawk_rtx_makeintval_inline(rtx, r + inc_val_int);
|
|
if (HAWK_UNLIKELY(!res))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case HAWK_VAL_FLT:
|
|
{
|
|
hawk_flt_t r = ((hawk_val_flt_t*)left)->val;
|
|
res = hawk_rtx_makefltval(rtx, r + inc_val_flt);
|
|
if (HAWK_UNLIKELY(!res))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
hawk_int_t v1;
|
|
hawk_flt_t v2;
|
|
int n;
|
|
|
|
n = hawk_rtx_valtonum(rtx, left, &v1, &v2);
|
|
if (HAWK_UNLIKELY(n <= -1))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
if (n == 0)
|
|
{
|
|
res = hawk_rtx_makeintval_inline(rtx, v1 + inc_val_int);
|
|
}
|
|
else /* if (n == 1) */
|
|
{
|
|
HAWK_ASSERT(n == 1);
|
|
res = hawk_rtx_makefltval(rtx, v2 + inc_val_flt);
|
|
}
|
|
|
|
if (HAWK_UNLIKELY(!res))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (HAWK_UNLIKELY(do_assignment(rtx, exp->left, res, 0) == HAWK_NULL))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
return res;
|
|
}
|
|
|
|
static hawk_val_t* eval_incpst (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_val_t* left, * res, * res2;
|
|
hawk_val_type_t left_vtype;
|
|
hawk_int_t inc_val_int;
|
|
hawk_flt_t inc_val_flt;
|
|
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)nde;
|
|
|
|
HAWK_ASSERT(exp->type == HAWK_NDE_EXP_INCPST);
|
|
HAWK_ASSERT(exp->left != HAWK_NULL && exp->right == HAWK_NULL);
|
|
|
|
/* this way of checking if the l-value is assignable is
|
|
* ugly as it is dependent on the node types defined in hawk.h.
|
|
* but let's keep going this way for the time being. */
|
|
if (exp->left->type < HAWK_NDE_NAMED ||
|
|
/*exp->left->type > HAWK_NDE_ARGIDX) XXX */
|
|
exp->left->type > HAWK_NDE_POS)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_EOPERAND);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
if (exp->opcode == HAWK_INCOP_PLUS)
|
|
{
|
|
inc_val_int = 1;
|
|
inc_val_flt = 1.0;
|
|
}
|
|
else if (exp->opcode == HAWK_INCOP_MINUS)
|
|
{
|
|
inc_val_int = -1;
|
|
inc_val_flt = -1.0;
|
|
}
|
|
else
|
|
{
|
|
HAWK_ASSERT(!"should never happen - invalid opcode");
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_EINTERN);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
HAWK_ASSERT(exp->left->next == HAWK_NULL);
|
|
left = eval_expression(rtx, exp->left);
|
|
if (HAWK_UNLIKELY(!left)) return HAWK_NULL;
|
|
|
|
hawk_rtx_refupval_inline(rtx, left);
|
|
|
|
left_vtype = HAWK_RTX_GETVALTYPE(rtx, left);
|
|
|
|
switch (left_vtype)
|
|
{
|
|
case HAWK_VAL_INT:
|
|
{
|
|
hawk_int_t r = HAWK_RTX_GETINTFROMVAL(rtx, left);
|
|
res = hawk_rtx_makeintval_inline(rtx, r);
|
|
if (HAWK_UNLIKELY(!res))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
res2 = hawk_rtx_makeintval_inline(rtx, r + inc_val_int);
|
|
if (HAWK_UNLIKELY(!res2))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
hawk_rtx_freeval(rtx, res, HAWK_RTX_FREEVAL_CACHE);
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case HAWK_VAL_FLT:
|
|
{
|
|
hawk_flt_t r = ((hawk_val_flt_t*)left)->val;
|
|
res = hawk_rtx_makefltval(rtx, r);
|
|
if (HAWK_UNLIKELY(!res))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
res2 = hawk_rtx_makefltval(rtx, r + inc_val_flt);
|
|
if (HAWK_UNLIKELY(!res2))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
hawk_rtx_freeval(rtx, res, HAWK_RTX_FREEVAL_CACHE);
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
hawk_int_t v1;
|
|
hawk_flt_t v2;
|
|
int n;
|
|
|
|
n = hawk_rtx_valtonum(rtx, left, &v1, &v2);
|
|
if (HAWK_UNLIKELY(n <= -1))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
if (n == 0)
|
|
{
|
|
res = hawk_rtx_makeintval_inline(rtx, v1);
|
|
if (HAWK_UNLIKELY(!res))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
res2 = hawk_rtx_makeintval_inline(rtx, v1 + inc_val_int);
|
|
if (HAWK_UNLIKELY(!res2))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
hawk_rtx_freeval(rtx, res, HAWK_RTX_FREEVAL_CACHE);
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
}
|
|
else /* if (n == 1) */
|
|
{
|
|
HAWK_ASSERT(n == 1);
|
|
res = hawk_rtx_makefltval(rtx, v2);
|
|
if (HAWK_UNLIKELY(!res))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
res2 = hawk_rtx_makefltval(rtx, v2 + inc_val_flt);
|
|
if (HAWK_UNLIKELY(!res2))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
hawk_rtx_freeval(rtx, res, HAWK_RTX_FREEVAL_CACHE);
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (HAWK_UNLIKELY(do_assignment(rtx, exp->left, res2, 0) == HAWK_NULL))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
return res;
|
|
}
|
|
|
|
static hawk_val_t* eval_cnd (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_val_t* tv, * v;
|
|
hawk_nde_cnd_t* cnd = (hawk_nde_cnd_t*)nde;
|
|
|
|
HAWK_ASSERT(cnd->test->next == HAWK_NULL);
|
|
|
|
tv = eval_expression(rtx, cnd->test);
|
|
if (HAWK_UNLIKELY(!tv)) return HAWK_NULL;
|
|
|
|
hawk_rtx_refupval_inline(rtx, tv);
|
|
|
|
HAWK_ASSERT(cnd->left->next == HAWK_NULL && cnd->right->next == HAWK_NULL);
|
|
v = (hawk_rtx_valtobool(rtx, tv))? eval_expression(rtx, cnd->left): eval_expression(rtx, cnd->right);
|
|
|
|
hawk_rtx_refdownval_inline(rtx, tv);
|
|
return v;
|
|
}
|
|
|
|
#define MOD_CAS_FNC_WINNER ((hawk_mod_t*)1)
|
|
|
|
static hawk_val_t* eval_fncall_fnc (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
/* intrinsic function */
|
|
hawk_nde_fncall_t* call = (hawk_nde_fncall_t*)nde;
|
|
pafn_t pafn;
|
|
|
|
if (call->u.fnc.flags & HAWK_NDE_FNCALL_FNC_DEFERRED_MODFNC)
|
|
{
|
|
hawk_mod_t* mod;
|
|
hawk_mod_sym_t sym;
|
|
hawk_nde_fncall_t tmp;
|
|
|
|
#if defined(ATOMIC_EVAL_MODSYM)
|
|
mod = HAWK_ATOMIC_LOAD(&call->u.fnc.info.mod, HAWK_ATOMIC_ACQUIRE);
|
|
#else
|
|
mod = call->u.fnc.info.mod;
|
|
#endif
|
|
if (!mod || mod == MOD_CAS_FNC_WINNER)
|
|
{
|
|
#if defined(ATOMIC_EVAL_MODSYM)
|
|
hawk_mod_t* expected;
|
|
#endif
|
|
/* resolve a deferred module function */
|
|
mod = hawk_rtx_querymodulewithoocs(rtx, &call->u.fnc.info.name, &sym, 0);
|
|
if (!mod)
|
|
{
|
|
const hawk_ooch_t* olderrmsg = hawk_backuperrmsg(rtx->hawk);
|
|
hawk_rtx_seterrfmt(rtx, &nde->loc, hawk_geterrnum(rtx->hawk),
|
|
HAWK_T("unable to resolve '%.*js' at runtime - %js"),
|
|
call->u.fnc.info.name.len, call->u.fnc.info.name.ptr, olderrmsg);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
if (sym.type != HAWK_MOD_FNC || ((rtx->hawk->opt.trait & sym.u.fnc_.trait) != sym.u.fnc_.trait))
|
|
{
|
|
hawk_rtx_seterrfmt(rtx, &nde->loc, HAWK_ENOENT, HAWK_T("'%.*js' not a function or unsupported"), call->u.fnc.info.name.len, call->u.fnc.info.name.ptr);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
/* cache it */
|
|
#if defined(ATOMIC_EVAL_MODSYM)
|
|
expected = HAWK_NULL;
|
|
if (HAWK_ATOMIC_CAS_BOOL(&call->u.fnc.info.mod, &expected, MOD_CAS_FNC_WINNER, HAWK_ATOMIC_ACQ_REL, HAWK_ATOMIC_ACQUIRE))
|
|
{
|
|
call->u.fnc.spec = sym.u.fnc_;
|
|
HAWK_ATOMIC_STORE(&call->u.fnc.info.mod, mod, HAWK_ATOMIC_RELEASE);
|
|
}
|
|
#else
|
|
call->u.fnc.spec = sym.u.fnc_;
|
|
call->u.fnc.info.mod = mod;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
/* reuse the cached info */
|
|
#if 0
|
|
/* these two fields are not used below. so skip setting them */
|
|
sym.type = HAWK_MOD_FNC;
|
|
sym.name = call->u.fnc.info.name.ptr;
|
|
#endif
|
|
sym.u.fnc_ = call->u.fnc.spec;
|
|
}
|
|
|
|
if (call->nargs > sym.u.fnc_.arg.max)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_EARGTM);
|
|
return HAWK_NULL;
|
|
}
|
|
else if (call->nargs < sym.u.fnc_.arg.min)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_EARGTF);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
tmp = *call;
|
|
tmp.u.fnc.info.mod = mod;
|
|
tmp.u.fnc.spec = sym.u.fnc_;
|
|
tmp.u.fnc.flags = 0;
|
|
|
|
pafn.args = call->args;
|
|
pafn.nargs = call->nargs;
|
|
pafn.argspec = sym.u.fnc_.arg.spec;
|
|
return hawk_rtx_evalcall(rtx, &tmp, HAWK_NULL, push_arg_from_nde, (void*)&pafn, HAWK_NULL, HAWK_NULL);
|
|
}
|
|
|
|
/* the parser must make sure that the number of arguments is proper */
|
|
HAWK_ASSERT(call->nargs >= call->u.fnc.spec.arg.min && call->nargs <= call->u.fnc.spec.arg.max);
|
|
pafn.args = call->args;
|
|
pafn.nargs = call->nargs;
|
|
pafn.argspec = call->u.fnc.spec.arg.spec;
|
|
return hawk_rtx_evalcall(rtx, call, HAWK_NULL, push_arg_from_nde, (void*)&pafn, HAWK_NULL, HAWK_NULL);
|
|
}
|
|
|
|
static HAWK_INLINE_ALWAYS hawk_fun_t* resolve_fncall_fun (hawk_rtx_t* rtx, hawk_nde_fncall_t* call)
|
|
{
|
|
hawk_fun_t* fun;
|
|
#if defined(HAWK_ATOMIC_CAS_BOOL)
|
|
hawk_fun_t* expected;
|
|
#endif
|
|
hawk_htb_pair_t* pair;
|
|
|
|
#if defined(HAWK_ATOMIC_LOAD)
|
|
fun = HAWK_ATOMIC_LOAD(&call->u.fun.fun, HAWK_ATOMIC_RELAXED);
|
|
#else
|
|
fun = call->u.fun.fun;
|
|
#endif
|
|
if (!fun)
|
|
{
|
|
/* there can be multiple runtime instances for a single hawk object.
|
|
* changing the parse tree from one runtime instance can affect
|
|
* other instances. however i do change the parse tree without protection
|
|
* hoping that the pointer assignment is atomic. (call->u.fun.fun = fun).
|
|
* i don't mind each instance performs a search duplicately for a short while */
|
|
|
|
pair = hawk_htb_search(rtx->hawk->tree.funs, call->u.fun.name.ptr, call->u.fun.name.len);
|
|
if (!pair)
|
|
{
|
|
hawk_rtx_seterrfmt(rtx, &call->loc, HAWK_EFUNNF, HAWK_T("function '%.*js' not found"), call->u.fun.name.len, call->u.fun.name.ptr);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
fun = (hawk_fun_t*)HAWK_HTB_VPTR(pair);
|
|
HAWK_ASSERT(fun != HAWK_NULL);
|
|
#if defined(HAWK_ATOMIC_CAS_BOOL)
|
|
expected = HAWK_NULL; /* if no thread published the cache slot, it must be HAWK_NULL */
|
|
if (!HAWK_ATOMIC_CAS_BOOL(&call->u.fun.fun, &expected, fun, HAWK_ATOMIC_RELAXED, HAWK_ATOMIC_RELAXED))
|
|
{
|
|
/* another thread must have cached a value
|
|
* we don't need to do anything */
|
|
#if 0
|
|
#if defined(HAWK_ATOMIC_CAS_BOOL_YIELD_OLDVAL)
|
|
/* exchange failed. expected has the value published by another thread */
|
|
fun = expected;
|
|
#else
|
|
/* exchange failed. i need to load the value published by another thread */
|
|
expected = HAWK_ATOMIC_LOAD(&call->u.fun.fun, HAWK_ATOMIC_RELAXED);
|
|
if (expected) fun = expected; /* to be more defensive */
|
|
#endif
|
|
#endif
|
|
}
|
|
#elif defined(HAWK_ATOMIC_STORE)
|
|
HAWK_ATOMIC_STORE(&call->u.fun.fun, fun, HAWK_ATOMIC_RELAXED);
|
|
#else
|
|
/* cache the search result */
|
|
call->u.fun.fun = fun;
|
|
#endif
|
|
}
|
|
|
|
if (call->nargs > fun->nargs && !fun->variadic)
|
|
{
|
|
hawk_rtx_seterrfmt(rtx, &call->loc, HAWK_EARGTM, HAWK_T("too many arguments to '%.*js'"), fun->name.len, fun->name.ptr);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
return fun;
|
|
}
|
|
|
|
#if defined(KEEP_EVAL_FNCALL_FUN)
|
|
static HAWK_INLINE hawk_val_t* eval_fncall_fun (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
/* user-defined function */
|
|
hawk_nde_fncall_t* call = (hawk_nde_fncall_t*)nde;
|
|
hawk_fun_t* fun;
|
|
pafn_t pafn;
|
|
|
|
fun = resolve_fncall_fun(rtx, call);
|
|
if (!fun) return HAWK_NULL;
|
|
|
|
/* push_arg_from_nde() has special handling for references when the function
|
|
* argument spec contains 'r' or 'R'.
|
|
* a reference is passed to a built-in function as a reference value
|
|
* but its evaluation result is passed to user-defined function.
|
|
* I pass HAWK_NULL to prevent special handling.
|
|
* the value change for a reference variable inside a user-defined function is
|
|
* reflected by hawk_rtx_evalcall() specially whereas a built-in function must
|
|
* call hawk_rtx_setrefval() to update the reference.
|
|
*/
|
|
pafn.args = call->args;
|
|
pafn.nargs = call->nargs;
|
|
pafn.argspec = HAWK_NULL; /* fun->argspec */
|
|
return hawk_rtx_evalcall(rtx, call, fun, push_arg_from_nde, (void*)&pafn, HAWK_NULL, HAWK_NULL);
|
|
}
|
|
#endif
|
|
|
|
static HAWK_INLINE int nde_is_var (const hawk_nde_t* nde)
|
|
{
|
|
switch (nde->type)
|
|
{
|
|
case HAWK_NDE_NAMED:
|
|
case HAWK_NDE_GBL:
|
|
case HAWK_NDE_LCL:
|
|
case HAWK_NDE_ARG:
|
|
case HAWK_NDE_NAMEDIDX:
|
|
case HAWK_NDE_GBLIDX:
|
|
case HAWK_NDE_LCLIDX:
|
|
case HAWK_NDE_ARGIDX:
|
|
return 1;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static hawk_val_t* eval_fncall_var (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_nde_fncall_t* call = (hawk_nde_fncall_t*)nde;
|
|
hawk_nde_t* callee;
|
|
hawk_val_t* fv, * rv;
|
|
hawk_fun_t* fun;
|
|
|
|
callee = call->u.expr.callable;
|
|
|
|
fv = eval_expression(rtx, callee);
|
|
if (HAWK_UNLIKELY(!fv)) return HAWK_NULL;
|
|
|
|
hawk_rtx_refupval_inline(rtx, fv);
|
|
fun = hawk_rtx_valtofun(rtx, fv);
|
|
if (HAWK_UNLIKELY(!fun))
|
|
{
|
|
if (hawk_rtx_geterrnum(rtx) == HAWK_EINVAL)
|
|
{
|
|
if (nde_is_var(callee))
|
|
{
|
|
hawk_nde_var_t* v = (hawk_nde_var_t*)callee;
|
|
hawk_rtx_seterrfmt(rtx, &nde->loc, HAWK_ENOTFUN,
|
|
HAWK_T("non-function value in '%.*js'"),
|
|
v->id.name.len, v->id.name.ptr);
|
|
}
|
|
else
|
|
{
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_ENOTFUN);
|
|
}
|
|
}
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
rv = HAWK_NULL;
|
|
}
|
|
else if (call->nargs > fun->nargs && !fun->variadic)
|
|
{
|
|
hawk_rtx_seterrfmt(rtx, &nde->loc, HAWK_EARGTM, HAWK_T("too many arguments to '%.*js'"), fun->name.len, fun->name.ptr);
|
|
rv = HAWK_NULL;
|
|
}
|
|
else
|
|
{
|
|
pafn_t pafn;
|
|
/* pass HAWK_NULL for the argument spec regardless of the actual spec.
|
|
* see comments in eval_fncall_fun() for more */
|
|
pafn.args = call->args;
|
|
pafn.nargs = call->nargs;
|
|
pafn.argspec = HAWK_NULL; /* fun->argspec */
|
|
rv = hawk_rtx_evalcall(rtx, call, fun, push_arg_from_nde, &pafn, HAWK_NULL, HAWK_NULL);
|
|
}
|
|
hawk_rtx_refdownval_inline(rtx, fv);
|
|
|
|
return rv;
|
|
}
|
|
|
|
hawk_val_t* hawk_rtx_evalcall (
|
|
hawk_rtx_t* rtx, hawk_nde_fncall_t* call, hawk_fun_t* fun,
|
|
hawk_oow_t(*argpusher)(hawk_rtx_t*,const hawk_loc_t*,void*), void* apdata,
|
|
void(*errhandler)(void*), void* eharg)
|
|
{
|
|
hawk_oow_t saved_stack_top, saved_arg_stack_top;
|
|
hawk_oow_t nargs, i, stack_req;
|
|
hawk_val_t* v;
|
|
int n;
|
|
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
/* when xstack evaluation is active, use the iterative arg evaluator so
|
|
* that argument expressions do not recurse on the C stack */
|
|
if (argpusher == push_arg_from_nde) argpusher = push_arg_from_nde_xstack;
|
|
#endif
|
|
|
|
/*
|
|
* ---------------------
|
|
* lcln <- stack top
|
|
* ---------------------
|
|
* ....
|
|
* ---------------------
|
|
* lcl0 local variables are pushed by run_block
|
|
* =====================
|
|
* argn
|
|
* ---------------------
|
|
* ....
|
|
* ---------------------
|
|
* arg1
|
|
* ---------------------
|
|
* arg0
|
|
* ---------------------
|
|
* nargs
|
|
* ---------------------
|
|
* return value
|
|
* ---------------------
|
|
* previous stack top
|
|
* ---------------------
|
|
* previous stack base <- stack base
|
|
* =====================
|
|
* 0 (nargs) <- stack top
|
|
* ---------------------
|
|
* return value
|
|
* ---------------------
|
|
* previous stack top
|
|
* ---------------------
|
|
* previous stack base <- stack base
|
|
* =====================
|
|
* gbln
|
|
* ---------------------
|
|
* ....
|
|
* ---------------------
|
|
* gbl0
|
|
* ---------------------
|
|
*/
|
|
|
|
HAWK_ASSERT(HAWK_SIZEOF(void*) >= HAWK_SIZEOF(rtx->stack_top));
|
|
HAWK_ASSERT(HAWK_SIZEOF(void*) >= HAWK_SIZEOF(rtx->stack_base));
|
|
HAWK_ASSERT(call != HAWK_NULL);
|
|
|
|
saved_stack_top = rtx->stack_top;
|
|
|
|
#if defined(DEBUG_RUN)
|
|
hawk_logbfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_DEBUG, "setting up function stack frame top=%zd base=%zd\n", (hawk_oow_t)rtx->stack_top, (hawk_oow_t)rtx->stack_base);
|
|
#endif
|
|
|
|
/* make a new stack frame */
|
|
stack_req = 4 + call->nargs;
|
|
if (fun && fun->nargs > call->nargs)
|
|
{
|
|
/* for variadic functions, fun->nargs can be less than call->nargs.
|
|
* for non-variadic functions, the compiler must guarantee that fun->nargs >= call->nargs
|
|
* this case can happen for for non-variadic functions only. */
|
|
stack_req += fun->nargs - call->nargs;
|
|
}
|
|
/* if fun is HAWK_NULL, there is no way for this function to know expected argument numbers.
|
|
* the argument pusher must ensure the stack availality before pushing arguments */
|
|
|
|
if (HAWK_UNLIKELY(HAWK_RTX_STACK_AVAIL(rtx) < stack_req))
|
|
{
|
|
hawk_rtx_seterrbfmt(rtx, &call->loc, HAWK_ESTACK,
|
|
"stack full(avail=%zu, limit=%zu) for call frame with %zu arguments",
|
|
HAWK_RTX_STACK_AVAIL(rtx), rtx->stack_limit, call->nargs);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
HAWK_RTX_STACK_PUSH(rtx, (void*)rtx->stack_base);
|
|
HAWK_RTX_STACK_PUSH(rtx, (void*)saved_stack_top);
|
|
HAWK_RTX_STACK_PUSH(rtx, hawk_val_nil); /* space for return value */
|
|
HAWK_RTX_STACK_PUSH(rtx, hawk_val_nil); /* space for number of arguments */
|
|
|
|
saved_arg_stack_top = rtx->stack_top;
|
|
|
|
/* push all arguments onto the stack */
|
|
nargs = argpusher(rtx, &call->loc, apdata);
|
|
if (nargs == (hawk_oow_t)-1) goto oops_making_stack_frame;
|
|
|
|
HAWK_ASSERT(nargs == call->nargs);
|
|
|
|
if (fun)
|
|
{
|
|
/* extra step for normal hawk functions */
|
|
while (nargs < fun->nargs)
|
|
{
|
|
/* push as many nils as the number of missing actual arguments */
|
|
HAWK_RTX_STACK_PUSH(rtx, hawk_val_nil);
|
|
nargs++;
|
|
}
|
|
}
|
|
|
|
/* entering a new stack frame */
|
|
rtx->stack_base = saved_stack_top;
|
|
HAWK_RTX_STACK_NARGS(rtx) = (void*)nargs;
|
|
|
|
#if defined(DEBUG_RUN)
|
|
hawk_logbfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_DEBUG, "running function body\n");
|
|
#endif
|
|
|
|
if (fun)
|
|
{
|
|
/* normal hawk function */
|
|
HAWK_ASSERT(fun->body->type == HAWK_NDE_BLK);
|
|
#if defined(HAWK_ENABLE_UCONTEXT)
|
|
{
|
|
hawk_oow_t co_threshold = rtx->co.in_coroutine?
|
|
HAWK_CO_COROUTINE_DEPTH_THRESHOLD : HAWK_CO_MAIN_DEPTH_THRESHOLD;
|
|
if (rtx->depth.block - rtx->co.stack_base >= co_threshold)
|
|
{
|
|
/* approaching the C stack limit of the current segment -
|
|
* run this call on a fresh heap-allocated stack */
|
|
char* costack;
|
|
ucontext_t caller_ctx, coctx;
|
|
hawk_oow_t saved_base = rtx->co.stack_base;
|
|
int saved_in_coroutine = rtx->co.in_coroutine;
|
|
|
|
costack = hawk_co_get_stack(rtx);
|
|
if (HAWK_UNLIKELY(!costack))
|
|
{
|
|
n = -1;
|
|
}
|
|
else
|
|
{
|
|
rtx->co.stack_base = rtx->depth.block;
|
|
rtx->co.in_coroutine = 1;
|
|
|
|
getcontext(&coctx);
|
|
coctx.uc_stack.ss_sp = costack;
|
|
coctx.uc_stack.ss_size = HAWK_CO_STACK_SIZE;
|
|
coctx.uc_link = &caller_ctx;
|
|
|
|
rtx->co.pending_body = (hawk_nde_blk_t*)fun->body;
|
|
rtx->co.pending_result = &n;
|
|
|
|
#if (HAWK_SIZEOF_VOID_P == 8)
|
|
{
|
|
hawk_uintptr_t ptr = (hawk_uintptr_t)rtx;
|
|
int hi = (int)(ptr >> 32);
|
|
int lo = (int)(ptr & 0xFFFFFFFFU);
|
|
makecontext(&coctx, (void(*)(void))hawk_co_trampoline, 2, hi, lo);
|
|
}
|
|
#elif (HAWK_SIZEOF_VOID_P == 4)
|
|
makecontext(&coctx, (void(*)(void))hawk_co_trampoline, 1, (int)(hawk_uintptr_t)rtx);
|
|
#else
|
|
#error UNSUPPORTED POINTER SIZE
|
|
#endif
|
|
swapcontext(&caller_ctx, &coctx);
|
|
|
|
hawk_co_put_stack(rtx, costack);
|
|
rtx->co.stack_base = saved_base;
|
|
rtx->co.in_coroutine = saved_in_coroutine;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
n = run_block(rtx, (hawk_nde_blk_t*)fun->body);
|
|
}
|
|
}
|
|
#else
|
|
n = run_block(rtx, (hawk_nde_blk_t*)fun->body);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
n = 0;
|
|
|
|
/* intrinsic function */
|
|
HAWK_ASSERT(call->nargs >= call->u.fnc.spec.arg.min && call->nargs <= call->u.fnc.spec.arg.max);
|
|
|
|
if (call->u.fnc.spec.impl)
|
|
{
|
|
n = call->u.fnc.spec.impl(rtx, &call->u.fnc.info);
|
|
if (HAWK_UNLIKELY(n <= -1)) ADJERR_LOC(rtx, &call->loc);
|
|
}
|
|
}
|
|
|
|
/* refdown args in the rtx.stack */
|
|
nargs = (hawk_oow_t)HAWK_RTX_STACK_NARGS(rtx);
|
|
#if defined(DEBUG_RUN)
|
|
hawk_logbfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_DEBUG, "block rtx complete nargs = %d\n", (int)nargs);
|
|
#endif
|
|
|
|
i = 0;
|
|
if (fun && fun->argspec && call->nargs > 0) /* hawk_rtx_callfun() sets up a fake call structure with call->nargs > 0 but call->args == HAWK_NULL */
|
|
{
|
|
/* set back the values for pass-by-reference parameters of normal functions.
|
|
* the intrinsic functions are not handled here but their implementation would
|
|
* call hawk_rtx_setrefval() */
|
|
|
|
/* even if fun->argspec exists, call->nargs may still be 0. so i test if call->nargs > 0.
|
|
* function x(a1, &a2) {}
|
|
* BEGIN { x(); }
|
|
* all parameters are @nils in this case. nargs and fun->nargs are 2 but call->nargs is 0.
|
|
*/
|
|
|
|
if (call->args)
|
|
{
|
|
hawk_oow_t cur_stack_base = rtx->stack_base;
|
|
hawk_oow_t prev_stack_base = (hawk_oow_t)rtx->stack[rtx->stack_base + 0];
|
|
hawk_nde_t* p = call->args;
|
|
|
|
if (fun->hasrefarg) /* just for optimization */
|
|
{
|
|
for (; i < call->nargs; i++)
|
|
{
|
|
if (n >= 0 && i < fun->argspeclen && (fun->argspec[i] == 'r' || fun->argspec[i] == 'R'))
|
|
{
|
|
/* if the function call is successful, update the call-by-reference arguments
|
|
* with hawk_rtx_setrefval() */
|
|
|
|
hawk_val_t** ref;
|
|
hawk_val_ref_t refv;
|
|
hawk_val_t* av;
|
|
int r;
|
|
|
|
av = HAWK_RTX_STACK_ARG(rtx, i);
|
|
if (HAWK_RTX_GETVALTYPE(rtx, av) == HAWK_VAL_REF)
|
|
{
|
|
/* the argument still has the reference type.
|
|
* this means, the argument has not been set.
|
|
*
|
|
* function f1(&a, &b) { b = 20 }
|
|
*
|
|
* since a is not set in f1, the value for a is still the pushed
|
|
* value which is a reference.
|
|
*/
|
|
/* ---- DO NOTHING ---- */
|
|
}
|
|
else
|
|
{
|
|
/* if an argument passed is a local variable or a parameter to the previous caller,
|
|
* the argument node information stored is relative to the previous stack frame.
|
|
* i revert rtx->stack_base to the previous stack frame base before calling
|
|
* get_reference() and restors it back to the current base. this tactic
|
|
* is very ugly because the assumption for this is dependent on get_reference()
|
|
* implementation */
|
|
rtx->stack_base = prev_stack_base; /* UGLY */
|
|
r = get_reference(rtx, p, &ref);
|
|
rtx->stack_base = cur_stack_base; /* UGLY */
|
|
|
|
/* if argspec is 'r', get_reference() must succeed all the time.
|
|
* if argspec is 'R', it may fail. if it happens, don't copy the value */
|
|
if (HAWK_LIKELY(r >= 0))
|
|
{
|
|
HAWK_RTX_INIT_REF_VAL(&refv, p->type - HAWK_NDE_NAMED, ref, 9); /* initialize a fake reference variable. 9 chosen randomly */
|
|
if (HAWK_UNLIKELY(hawk_rtx_setrefval(rtx, &refv, av) <= -1))
|
|
{
|
|
n = -1;
|
|
ADJERR_LOC(rtx, &call->loc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_ARG(rtx,i));
|
|
p = p->next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (; i < call->nargs; i++)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_ARG(rtx,i));
|
|
p = p->next;
|
|
}
|
|
}
|
|
}
|
|
else if (call->arg_base > 0) /* special case. set by hawk::call() */
|
|
{
|
|
/*
|
|
* function f1(a,&b) { b *= 20; }
|
|
* BEGIN { q = 4; hawk::call(r, "f1", 20, q); print q; }
|
|
*
|
|
* the fourth argument to hawk::call() must map to the second argument to f1().
|
|
* hawk::call() accepts the third to the last arguments as reference if possible.
|
|
* this function attempts to copy back the pass-by-reference values to
|
|
* one stack frame up.
|
|
*
|
|
* f1(1, 2) is an error as 2 is not referenceable.
|
|
* hawk::call(r, "f1", 1, 2) is not an error but can't capture changes made inside f1.
|
|
*/
|
|
if (fun->hasrefarg) /* just for optimization */
|
|
{
|
|
for (; i < call->nargs; i++)
|
|
{
|
|
if (n >= 0 && i < fun->argspeclen && (fun->argspec[i] == 'r' || fun->argspec[i] == 'R'))
|
|
{
|
|
hawk_val_t* v, * av;
|
|
|
|
av = HAWK_RTX_STACK_ARG(rtx, i);
|
|
if (HAWK_RTX_GETVALTYPE(rtx, av) == HAWK_VAL_REF)
|
|
{
|
|
/* ---- DO NOTHING ---- */
|
|
}
|
|
else
|
|
{
|
|
/* original arguments are in another stack frame */
|
|
v = rtx->stack[call->arg_base + i]; /* UGLY */
|
|
if (HAWK_RTX_GETVALTYPE(rtx, v) == HAWK_VAL_REF)
|
|
{
|
|
if (HAWK_UNLIKELY(hawk_rtx_setrefval(rtx, (hawk_val_ref_t*)v, av) <= -1))
|
|
{
|
|
n = -1;
|
|
ADJERR_LOC(rtx, &call->loc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_ARG(rtx,i));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (; i < call->nargs; i++)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_ARG(rtx,i));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (; i < nargs; i++)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_ARG(rtx,i));
|
|
}
|
|
|
|
#if defined(DEBUG_RUN)
|
|
hawk_logbfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_DEBUG, "got return value\n");
|
|
#endif
|
|
|
|
v = HAWK_RTX_STACK_RETVAL(rtx);
|
|
if (HAWK_UNLIKELY(n <= -1))
|
|
{
|
|
if (hawk_rtx_geterrnum(rtx) == HAWK_ENOERR && errhandler != HAWK_NULL)
|
|
{
|
|
/* errhandler is passed only when hawk_rtx_evalcall() is
|
|
* invoked from hawk_rtx_call(). Under this
|
|
* circumstance, this stack frame is the first
|
|
* activated and the stack base is the first element
|
|
* after the global variables. so HAWK_RTX_STACK_RETVAL(rtx)
|
|
* effectively becomes HAWK_RTX_STACK_RETVAL_GBL(rtx).
|
|
* As hawk_rtx_evalcall() returns HAWK_NULL on error and
|
|
* the reference count of HAWK_RTX_STACK_RETVAL(rtx) should be
|
|
* decremented, it can't get the return value
|
|
* if it turns out to be terminated by exit().
|
|
* The return value could be destroyed by then.
|
|
* Unlikely, rtx_bpae_loop() just checks if rtx->errinf.num
|
|
* is HAWK_ENOERR and gets HAWK_RTX_STACK_RETVAL_GBL(rtx)
|
|
* to determine if it is terminated by exit().
|
|
*
|
|
* The handler capture_retval_on_exit()
|
|
* increments the reference of HAWK_RTX_STACK_RETVAL(rtx)
|
|
* and stores the pointer into accompanying space.
|
|
* This way, the return value is preserved upon
|
|
* termination by exit() out to the caller.
|
|
*/
|
|
errhandler(eharg);
|
|
}
|
|
|
|
/* if the earlier operations failed and this function
|
|
* has to return a error, the return value is just
|
|
* destroyed and replaced by nil */
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
HAWK_RTX_STACK_RETVAL(rtx) = hawk_val_nil;
|
|
}
|
|
else
|
|
{
|
|
/* this trick has been mentioned in rtx_return.
|
|
* adjust the reference count of the return value.
|
|
* the value must not be freed even if the reference count
|
|
* reached zero because its reference has been incremented
|
|
* in rtx_return or directly by hawk_rtx_setretval()
|
|
* regardless of its reference count. */
|
|
hawk_rtx_refdownval_nofree_inline(rtx, v);
|
|
}
|
|
|
|
rtx->stack_top = (hawk_oow_t)rtx->stack[rtx->stack_base + 1];
|
|
rtx->stack_base = (hawk_oow_t)rtx->stack[rtx->stack_base + 0];
|
|
|
|
if (rtx->exit_level == EXIT_FUNCTION) rtx->exit_level = EXIT_NONE;
|
|
|
|
#if defined(DEBUG_RUN)
|
|
hawk_logbfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_DEBUG, "returning from function top=%zd, base=%zd\n", (hawk_oow_t)rtx->stack_top, (hawk_oow_t)rtx->stack_base);
|
|
#endif
|
|
return (n <= -1)? HAWK_NULL: v;
|
|
|
|
oops_making_stack_frame:
|
|
while (rtx->stack_top > saved_arg_stack_top)
|
|
{
|
|
/* call hawk_rtx_refdownval_inline() for all arguments.
|
|
* it is safe because nil or quickint is immune to excessive hawk_rtx_refdownval_inline() calls */
|
|
hawk_rtx_refdownval_inline(rtx, rtx->stack[rtx->stack_top - 1]);
|
|
HAWK_RTX_STACK_POP(rtx);
|
|
}
|
|
HAWK_ASSERT(rtx->stack_top - saved_stack_top == 4);
|
|
while (rtx->stack_top > saved_stack_top)
|
|
{
|
|
/* the stack frame prologue does not have a reference-counted value
|
|
* before being entered. so no hawk_rtx_refdownval_inline().
|
|
* three slots contains raw integers for internal use.. only one
|
|
* slot that contains the return value would be reference counted
|
|
* after it is set to a reference counted value. here, it never happens */
|
|
HAWK_RTX_STACK_POP(rtx);
|
|
}
|
|
ADJERR_LOC(rtx, &call->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
static hawk_oow_t push_arg_from_vals (hawk_rtx_t* rtx, const hawk_loc_t* call_loc, void* data)
|
|
{
|
|
pafv_t* pafv = (struct pafv_t*)data;
|
|
hawk_oow_t nargs = 0;
|
|
|
|
if (HAWK_UNLIKELY(HAWK_RTX_STACK_AVAIL(rtx) < pafv->nargs))
|
|
{
|
|
hawk_rtx_seterrbfmt(rtx, call_loc, HAWK_ESTACK,
|
|
"stack full(avail=%zu, limit=%zu) in pushing %zu arguments from values",
|
|
HAWK_RTX_STACK_AVAIL(rtx), rtx->stack_limit, pafv->nargs);
|
|
return (hawk_oow_t)-1;
|
|
}
|
|
|
|
for (nargs = 0; nargs < pafv->nargs; nargs++)
|
|
{
|
|
if (pafv->argspec && (pafv->argspec[nargs] == 'r' || pafv->argspec[nargs] == 'R'))
|
|
{
|
|
hawk_val_t** ref;
|
|
hawk_val_t* v;
|
|
|
|
ref = (hawk_val_t**)&pafv->args[nargs];
|
|
v = hawk_rtx_makerefval(rtx, HAWK_VAL_REF_LCL, ref); /* this type(HAWK_VAL_REF_LCL) is fake */
|
|
if (HAWK_UNLIKELY(!v))
|
|
{
|
|
ADJERR_LOC(rtx, call_loc);
|
|
return (hawk_oow_t)-1;
|
|
}
|
|
|
|
HAWK_RTX_STACK_PUSH(rtx, v);
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
}
|
|
else
|
|
{
|
|
HAWK_RTX_STACK_PUSH(rtx, pafv->args[nargs]);
|
|
hawk_rtx_refupval_inline(rtx, pafv->args[nargs]);
|
|
}
|
|
}
|
|
|
|
return nargs;
|
|
}
|
|
|
|
static hawk_oow_t push_arg_from_nde (hawk_rtx_t* rtx, const hawk_loc_t* call_loc, void* data)
|
|
{
|
|
pafn_t* pafn = (pafn_t*)data;
|
|
hawk_nde_t* p;
|
|
hawk_val_t* v;
|
|
hawk_oow_t nargs;
|
|
hawk_oow_t spec_len;
|
|
|
|
if (HAWK_UNLIKELY(HAWK_RTX_STACK_AVAIL(rtx) < pafn->nargs))
|
|
{
|
|
hawk_rtx_seterrbfmt(rtx, call_loc, HAWK_ESTACK,
|
|
"stack full(avail=%zu, limit=%zu) in push %zu arguments",
|
|
HAWK_RTX_STACK_AVAIL(rtx), rtx->stack_limit, pafn->nargs);
|
|
return (hawk_oow_t)-1;
|
|
}
|
|
|
|
/* in practice, this function gets NULL for a user-defined function regardless of its actual spec.
|
|
* it may get a non-NULL arg_spec for builtin/module functions */
|
|
spec_len = pafn->argspec? hawk_count_oocstr(pafn->argspec): 0;
|
|
for (p = pafn->args, nargs = 0; p; p = p->next, nargs++)
|
|
{
|
|
hawk_ooch_t spec;
|
|
|
|
/* if not sufficient number of spec characters given, take the last value and use it.
|
|
* as said above, arg_spec is provided for the implicit functions only.
|
|
* so the logic taking the last value at run-time is elsewhere (e.g hawk_rtx_evalcall()). */
|
|
spec = (spec_len <= 0)? '\0': pafn->argspec[((nargs < spec_len)? nargs: spec_len - 1)];
|
|
|
|
switch (spec)
|
|
{
|
|
case 'R': /* make reference if a referenceable is given. otherwise accept a normal value - useful for hawk::call() */
|
|
case 'r': /* make reference. a non-referenceable value is rejected */
|
|
{
|
|
hawk_val_t** ref;
|
|
|
|
if (get_reference(rtx, p, &ref) <= -1)
|
|
{
|
|
if (spec == 'R') goto normal_arg;
|
|
return (hawk_oow_t)-1; /* return -1 without unwinding stack as hawk_rtx_evalcall() does it */
|
|
}
|
|
|
|
/* 'p->type - HAWK_NDE_NAMED' must produce a relevant HAWK_VAL_REF_XXX value. */
|
|
v = hawk_rtx_makerefval(rtx, p->type - HAWK_NDE_NAMED, ref);
|
|
break;
|
|
}
|
|
|
|
case 'x':
|
|
/* a regular expression is passed to the function as it is */
|
|
v = eval_expression0(rtx, p);
|
|
break;
|
|
|
|
default:
|
|
normal_arg:
|
|
v = eval_expression(rtx, p);
|
|
break;
|
|
}
|
|
|
|
if (HAWK_UNLIKELY(!v)) return (hawk_oow_t)-1; /* return -1 without unwinding stack as hawk_rtx_evalcall() does it */
|
|
|
|
HAWK_RTX_STACK_PUSH(rtx, v);
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
}
|
|
|
|
HAWK_ASSERT(pafn->nargs == nargs);
|
|
return nargs;
|
|
}
|
|
|
|
static int get_reference (hawk_rtx_t* rtx, hawk_nde_t* nde, hawk_val_t*** ref)
|
|
{
|
|
hawk_nde_var_t* tgt = (hawk_nde_var_t*)nde;
|
|
hawk_val_t** tmp;
|
|
|
|
/* refer to eval_indexed for application of a similar concept */
|
|
|
|
switch (nde->type)
|
|
{
|
|
case HAWK_NDE_NAMED:
|
|
HAWK_ASSERT(tgt->id.idxa < rtx->named_slot_count);
|
|
*ref = (hawk_val_t**)((hawk_oow_t)tgt->id.idxa);
|
|
return 0;
|
|
|
|
case HAWK_NDE_GBL:
|
|
/* *ref = (hawk_val_t**)&HAWK_RTX_STACK_GBL(rtx,tgt->id.idxa); */
|
|
*ref = (hawk_val_t**)((hawk_oow_t)tgt->id.idxa);
|
|
return 0;
|
|
|
|
case HAWK_NDE_LCL:
|
|
*ref = (hawk_val_t**)&HAWK_RTX_STACK_LCL(rtx,tgt->id.idxa);
|
|
return 0;
|
|
|
|
case HAWK_NDE_ARG:
|
|
*ref = (hawk_val_t**)&HAWK_RTX_STACK_ARG(rtx,tgt->id.idxa);
|
|
return 0;
|
|
|
|
case HAWK_NDE_NAMEDIDX:
|
|
case HAWK_NDE_GBLIDX:
|
|
case HAWK_NDE_LCLIDX:
|
|
case HAWK_NDE_ARGIDX:
|
|
tmp = get_reference_indexed(rtx, tgt);
|
|
if (HAWK_UNLIKELY(!tmp)) return -1;
|
|
*ref = tmp;
|
|
return 0;
|
|
|
|
case HAWK_NDE_POS:
|
|
{
|
|
int n;
|
|
hawk_int_t lv;
|
|
hawk_val_t* v;
|
|
|
|
/* the position number is returned for the positional
|
|
* variable unlike other reference types. */
|
|
v = eval_expression(rtx, ((hawk_nde_pos_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!v)) return -1;
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
n = hawk_rtx_valtoint_inline(rtx, v, &lv);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
|
|
if (HAWK_UNLIKELY(n <= -1))
|
|
{
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_EPOSIDX);
|
|
return -1;
|
|
}
|
|
|
|
if (!IS_VALID_POSIDX(lv))
|
|
{
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_EPOSIDX);
|
|
return -1;
|
|
}
|
|
|
|
*ref = (hawk_val_t**)((hawk_oow_t)lv);
|
|
return 0;
|
|
}
|
|
|
|
default:
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_ENOTREF);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static hawk_val_t** get_reference_indexed (hawk_rtx_t* rtx, hawk_nde_var_t* var)
|
|
{
|
|
hawk_map_t* map;
|
|
hawk_ooch_t* str = HAWK_NULL;
|
|
hawk_oow_t len;
|
|
hawk_ooch_t idxbuf[HAWK_IDX_BUF_SIZE];
|
|
|
|
hawk_arr_t* arr;
|
|
hawk_ooi_t idx;
|
|
|
|
hawk_val_t* v;
|
|
hawk_val_type_t vtype;
|
|
hawk_nde_t* remidx;
|
|
|
|
HAWK_ASSERT(var->idx != HAWK_NULL);
|
|
|
|
v = fetch_topval_from_var(rtx, var);
|
|
vtype = HAWK_RTX_GETVALTYPE(rtx, v);
|
|
|
|
switch (vtype)
|
|
{
|
|
case HAWK_VAL_NIL:
|
|
v = assign_newmapval_to_var(rtx, var);
|
|
if (HAWK_UNLIKELY(!v)) goto oops;
|
|
vtype = HAWK_VAL_MAP;
|
|
goto val_map_init;
|
|
|
|
case HAWK_VAL_MAP:
|
|
val_map_init:
|
|
len = HAWK_COUNTOF(idxbuf);
|
|
str = idxnde_to_str(rtx, var->idx, idxbuf, &len, &remidx, HAWK_NULL);
|
|
if (HAWK_UNLIKELY(!str)) goto oops;
|
|
map = ((hawk_val_map_t*)v)->map;
|
|
break;
|
|
|
|
case HAWK_VAL_ARR:
|
|
idx = idxnde_to_int(rtx, var->idx, &remidx);
|
|
if (HAWK_UNLIKELY(idx <= -1)) goto oops;
|
|
arr = ((hawk_val_arr_t*)v)->arr;
|
|
break;
|
|
|
|
default:
|
|
hawk_rtx_seterrnum(rtx, &var->loc, HAWK_ENOTIDXACC);
|
|
goto oops;
|
|
}
|
|
|
|
#if defined(HAWK_ENABLE_GC)
|
|
while (remidx)
|
|
{
|
|
hawk_val_type_t container_vtype;
|
|
|
|
if (vtype == HAWK_VAL_MAP)
|
|
{
|
|
hawk_map_pair_t* pair;
|
|
pair = hawk_map_search(map, str, len);
|
|
v = pair? (hawk_val_t*)HAWK_MAP_VPTR(pair): hawk_val_nil;
|
|
}
|
|
else
|
|
{
|
|
v = (idx < HAWK_ARR_SIZE(arr) && HAWK_ARR_SLOT(arr, idx))? ((hawk_val_t*)HAWK_ARR_DPTR(arr, idx)): hawk_val_nil;
|
|
}
|
|
container_vtype = vtype;
|
|
vtype = HAWK_RTX_GETVALTYPE(rtx, v);
|
|
|
|
switch (vtype)
|
|
{
|
|
case HAWK_VAL_MAP:
|
|
val_map:
|
|
if (str && str != idxbuf) hawk_rtx_freemem(rtx, str);
|
|
len = HAWK_COUNTOF(idxbuf);
|
|
str = idxnde_to_str(rtx, remidx, idxbuf, &len, &remidx, HAWK_NULL);
|
|
if (HAWK_UNLIKELY(!str)) goto oops;
|
|
map = ((hawk_val_map_t*)v)->map;
|
|
break;
|
|
|
|
case HAWK_VAL_ARR:
|
|
val_arr:
|
|
idx = idxnde_to_int(rtx, remidx, &remidx);
|
|
if (HAWK_UNLIKELY(idx <= -1)) goto oops;
|
|
arr = ((hawk_val_arr_t*)v)->arr;
|
|
break;
|
|
|
|
default:
|
|
if (vtype == HAWK_VAL_NIL /* || (rtx->hawk->opt.trait & HAWK_FLEXMAP) no flexmap because this is in a 'get' context */)
|
|
{
|
|
if (container_vtype == HAWK_VAL_MAP)
|
|
{
|
|
v = assign_newmapval_in_map(rtx, map, str, len);
|
|
if (HAWK_UNLIKELY(!v)) { ADJERR_LOC(rtx, &var->loc); goto oops; }
|
|
vtype = HAWK_VAL_MAP;
|
|
goto val_map;
|
|
}
|
|
else
|
|
{
|
|
v = assign_newarrval_in_arr(rtx, arr, idx);
|
|
if (HAWK_UNLIKELY(!v)) { ADJERR_LOC(rtx, &var->loc); goto oops; }
|
|
vtype = HAWK_VAL_ARR;
|
|
goto val_arr;
|
|
}
|
|
}
|
|
|
|
hawk_rtx_seterrnum(rtx, &var->loc, HAWK_ENOTIDXACC);
|
|
goto oops;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (vtype == HAWK_VAL_MAP)
|
|
{
|
|
hawk_map_pair_t* pair;
|
|
pair = hawk_map_search(map, str, len);
|
|
if (!pair)
|
|
{
|
|
/* if the value doesn't exist for the given key, insert a nil for it to create a placeholder for the reference */
|
|
pair = hawk_map_upsert(map, str, len, hawk_val_nil, 0);
|
|
if (HAWK_UNLIKELY(!pair)) { ADJERR_LOC(rtx, &var->loc); goto oops; }
|
|
HAWK_ASSERT(HAWK_MAP_VPTR(pair) == hawk_val_nil);
|
|
/* no reference count increment as hawk_val_nil is upserted
|
|
hawk_rtx_refupval_inline(rtx, HAWK_MAP_VPTR(pair)); */
|
|
}
|
|
|
|
if (str && str != idxbuf) hawk_rtx_freemem(rtx, str);
|
|
return (hawk_val_t**)&HAWK_MAP_VPTR(pair);
|
|
}
|
|
else
|
|
{
|
|
if (str && str != idxbuf) hawk_rtx_freemem(rtx, str);
|
|
if (idx >= HAWK_ARR_SIZE(arr) || !HAWK_ARR_SLOT(arr, idx))
|
|
{
|
|
/* if the value doesn't exist for the given index, insert a nil at that position to create a placeholder for the reference */
|
|
if (HAWK_UNLIKELY(hawk_arr_upsert (arr, idx, hawk_val_nil, 0) == HAWK_ARR_NIL)) { ADJERR_LOC(rtx, &var->loc); goto oops; }
|
|
}
|
|
return (hawk_val_t**)&HAWK_ARR_DPTR(arr, idx);
|
|
}
|
|
|
|
oops:
|
|
if (str && str != idxbuf) hawk_rtx_freemem(rtx, str);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
static hawk_val_t* eval_char (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_val_t* val;
|
|
val = hawk_rtx_makecharval(rtx, ((hawk_nde_char_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!val)) ADJERR_LOC(rtx, &nde->loc);
|
|
return val;
|
|
}
|
|
|
|
static hawk_val_t* eval_bchr (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_val_t* val;
|
|
val = hawk_rtx_makebchrval(rtx, ((hawk_nde_bchr_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!val)) ADJERR_LOC(rtx, &nde->loc);
|
|
return val;
|
|
}
|
|
|
|
static hawk_val_t* eval_int (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_val_t* val;
|
|
val = hawk_rtx_makeintval_inline(rtx, ((hawk_nde_int_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!val)) ADJERR_LOC(rtx, &nde->loc);
|
|
else if (HAWK_VTR_IS_POINTER(val) && !HAWK_IS_STATICVAL(val)) ((hawk_val_int_t*)val)->nde = nde;
|
|
return val;
|
|
}
|
|
|
|
static hawk_val_t* eval_flt (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_val_t* val;
|
|
val = hawk_rtx_makefltval(rtx, ((hawk_nde_flt_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!val)) ADJERR_LOC(rtx, &nde->loc);
|
|
else ((hawk_val_flt_t*)val)->nde = nde;
|
|
return val;
|
|
}
|
|
|
|
static hawk_val_t* eval_str (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_val_t* val;
|
|
val = hawk_rtx_makestrvalwithoochars(rtx, ((hawk_nde_str_t*)nde)->ptr, ((hawk_nde_str_t*)nde)->len);
|
|
if (HAWK_UNLIKELY(!val)) ADJERR_LOC(rtx, &nde->loc);
|
|
return val;
|
|
}
|
|
|
|
static hawk_val_t* eval_mbs (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_val_t* val;
|
|
val = hawk_rtx_makembsvalwithbchars(rtx, ((hawk_nde_mbs_t*)nde)->ptr, ((hawk_nde_mbs_t*)nde)->len);
|
|
if (HAWK_UNLIKELY(!val)) ADJERR_LOC(rtx, &nde->loc);
|
|
return val;
|
|
}
|
|
|
|
static hawk_val_t* eval_rex (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_val_t* val;
|
|
val = hawk_rtx_makerexval(rtx, &((hawk_nde_rex_t*)nde)->str, ((hawk_nde_rex_t*)nde)->code);
|
|
if (HAWK_UNLIKELY(!val)) ADJERR_LOC(rtx, &nde->loc);
|
|
return val;
|
|
}
|
|
|
|
static hawk_val_t* eval_xnil (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
return hawk_rtx_makenilval(rtx); /* this never fails */
|
|
}
|
|
|
|
static hawk_val_t* eval_xtrue (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
return hawk_rtx_makebooltrueval(rtx); /* this never fails */
|
|
}
|
|
|
|
static hawk_val_t* eval_xfalse (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
return hawk_rtx_makeboolfalseval(rtx); /* this never fails */
|
|
}
|
|
|
|
static hawk_val_t* eval_xargc (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_int_t nargs = (hawk_int_t)hawk_rtx_getnargs(rtx);
|
|
return hawk_rtx_makeintval_inline(rtx, nargs);
|
|
}
|
|
|
|
static hawk_val_t* eval_xargv (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
/* this create a new array. referencing @argv without a subscript
|
|
* slows hawk. TODO: fix this issue */
|
|
hawk_oow_t nargs, i;
|
|
hawk_val_t* v;
|
|
|
|
nargs = hawk_rtx_getnargs(rtx);
|
|
v = hawk_rtx_makearrval(rtx, nargs);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
|
|
hawk_rtx_refupval_inline(rtx,v);
|
|
for (i = 0; i < nargs; i++)
|
|
{
|
|
if (HAWK_UNLIKELY(!hawk_rtx_setarrvalfld(rtx, v, i, hawk_rtx_getarg(rtx, i))))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
return HAWK_NULL;
|
|
}
|
|
}
|
|
|
|
hawk_rtx_refdownval_nofree_inline(rtx,v);
|
|
return v;
|
|
}
|
|
|
|
static hawk_val_t* eval_xargvidx (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_nde_xargvidx_t* xarg;
|
|
hawk_val_t* v;
|
|
hawk_int_t pos;
|
|
int n;
|
|
|
|
xarg = (hawk_nde_xargvidx_t*)nde;
|
|
|
|
v = eval_expression(rtx, xarg->pos);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
n = hawk_rtx_valtoint_inline(rtx, v, &pos);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
if (n <= -1)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, &xarg->pos->loc, HAWK_EPOSIDX);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
return (pos < 0 || pos >= hawk_rtx_getnargs(rtx))? hawk_rtx_makenilval(rtx): hawk_rtx_getarg(rtx, pos);
|
|
}
|
|
|
|
static hawk_val_t* eval_fun (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_val_t* val;
|
|
hawk_fun_t* fun;
|
|
|
|
fun = ((hawk_nde_fun_t*)nde)->funptr;
|
|
if (!fun)
|
|
{
|
|
hawk_htb_pair_t* pair;
|
|
/* for a program like this,
|
|
BEGIN { @local x; abc(); x = abc; x(); } function abc() { print "abc"; }
|
|
* abc is defined after BEGIN but is know to be a function inside BEGIN.
|
|
* the funptr field in the node can be HAWK_NULL */
|
|
|
|
/* TODO: support builtin functions?, support module functions? */
|
|
pair = hawk_htb_search(rtx->hawk->tree.funs, ((hawk_nde_fun_t*)nde)->name.ptr, ((hawk_nde_fun_t*)nde)->name.len);
|
|
if (!pair)
|
|
{
|
|
/* it's unlikely to be not found in the function table for the parser structure. but keep this code in case */
|
|
hawk_rtx_seterrfmt(rtx, &nde->loc, HAWK_EFUNNF, HAWK_T("function '%.*js' not found"), ((hawk_nde_fun_t*)nde)->name.len, ((hawk_nde_fun_t*)nde)->name.ptr);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
fun = (hawk_fun_t*)HAWK_HTB_VPTR(pair);
|
|
HAWK_ASSERT(fun != HAWK_NULL);
|
|
}
|
|
|
|
val = hawk_rtx_makefunval(rtx, fun);
|
|
if (HAWK_UNLIKELY(!val)) ADJERR_LOC(rtx, &nde->loc);
|
|
return val;
|
|
}
|
|
|
|
#define MOD_CAS_CONST_WINNER ((int)-2)
|
|
static hawk_val_t* eval_modsym (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_nde_modsym_t* symnde;
|
|
hawk_mod_t* mod;
|
|
hawk_mod_sym_t sym;
|
|
hawk_val_t* v = HAWK_NULL;
|
|
#if defined(ATOMIC_EVAL_MODSYM)
|
|
int expected = -1;
|
|
#endif
|
|
int cache_type;
|
|
|
|
/* this function is unlikely to be reached for resolved symbols at compile time.
|
|
* the parser attempts to resolve a symbol first and create a modsym node
|
|
* only if it fails to resolve it. so virtually all symbols evaluated here must
|
|
* be unresolvable if not for module availability change between compile-time and
|
|
* run-time.
|
|
*
|
|
* but if the compiler/parser changes the behavior to always defer module resolution
|
|
* to runtime, the seemingly redundant code here will become more useful. */
|
|
|
|
symnde = (hawk_nde_modsym_t*)nde;
|
|
|
|
#if defined(ATOMIC_EVAL_MODSYM)
|
|
cache_type = HAWK_ATOMIC_LOAD(&symnde->cache_type, HAWK_ATOMIC_ACQUIRE);
|
|
#else
|
|
cache_type = symnde->cache_type;
|
|
#endif
|
|
if (cache_type == HAWK_MOD_INT)
|
|
{
|
|
/* settings the fields used by code after the jump position 'mod_int' only.
|
|
* be careful whening updating code there if it happens to use different fields as well */
|
|
sym.u.int_.trait = symnde->cache_trait;
|
|
sym.u.int_.val = symnde->cache.i;
|
|
goto mod_int;
|
|
}
|
|
else if (cache_type == HAWK_MOD_FLT)
|
|
{
|
|
/* settings the fields used by code after the jump position 'mod_flt' only.
|
|
* be careful whening updating code there if it happens to use different fields as well */
|
|
sym.u.flt_.trait = symnde->cache_trait;
|
|
sym.u.flt_.val = symnde->cache.f;
|
|
goto mod_flt;
|
|
}
|
|
else
|
|
{
|
|
/* TODO: cache optimization. skip calling hawk_rtx_querymodulewithoocs() more than once */
|
|
|
|
mod = hawk_rtx_querymodulewithoocs(rtx, &symnde->name, &sym, 0);
|
|
if (HAWK_UNLIKELY(!mod))
|
|
{
|
|
const hawk_ooch_t* olderrmsg = hawk_backuperrmsg(rtx->hawk);
|
|
hawk_rtx_seterrfmt(rtx, &nde->loc, hawk_geterrnum(rtx->hawk),
|
|
HAWK_T("unable to resolve '%.*js' at runtime - %js"),
|
|
symnde->name.len, symnde->name.ptr, olderrmsg);
|
|
return HAWK_NULL;
|
|
}
|
|
}
|
|
|
|
switch (sym.type)
|
|
{
|
|
case HAWK_MOD_INT:
|
|
#if defined(ATOMIC_EVAL_MODSYM)
|
|
if (HAWK_ATOMIC_CAS_BOOL(&symnde->cache_type, &expected, MOD_CAS_CONST_WINNER, HAWK_ATOMIC_ACQ_REL, HAWK_ATOMIC_ACQUIRE))
|
|
{
|
|
#endif
|
|
symnde->cache.i = sym.u.int_.val;
|
|
symnde->cache_trait = sym.u.int_.trait;
|
|
#if defined(ATOMIC_EVAL_MODSYM)
|
|
HAWK_ATOMIC_STORE(&symnde->cache_type, sym.type, HAWK_ATOMIC_RELEASE);
|
|
#else
|
|
symnde->cache_type = sym.type; /* set cache_type after all other fields */
|
|
#endif
|
|
#if defined(ATOMIC_EVAL_MODSYM)
|
|
}
|
|
#endif
|
|
mod_int:
|
|
if ((rtx->hawk->opt.trait & sym.u.int_.trait) != sym.u.int_.trait) goto unsupported;
|
|
v = hawk_rtx_makeintval_inline(rtx, sym.u.int_.val);
|
|
break;
|
|
|
|
case HAWK_MOD_FLT:
|
|
#if defined(ATOMIC_EVAL_MODSYM)
|
|
if (HAWK_ATOMIC_CAS_BOOL(&symnde->cache_type, &expected, -2, HAWK_ATOMIC_ACQ_REL, HAWK_ATOMIC_ACQUIRE))
|
|
{
|
|
#endif
|
|
symnde->cache.f = sym.u.flt_.val;
|
|
symnde->cache_trait = sym.u.flt_.trait;
|
|
#if defined(ATOMIC_EVAL_MODSYM)
|
|
HAWK_ATOMIC_STORE(&symnde->cache_type, sym.type, HAWK_ATOMIC_RELEASE);
|
|
#else
|
|
symnde->cache_type = sym.type; /* set cache_type after all other fields */
|
|
#endif
|
|
#if defined(ATOMIC_EVAL_MODSYM)
|
|
}
|
|
#endif
|
|
mod_flt:
|
|
if ((rtx->hawk->opt.trait & sym.u.flt_.trait) != sym.u.flt_.trait) goto unsupported;
|
|
v = hawk_rtx_makefltval(rtx, sym.u.flt_.val);
|
|
break;
|
|
|
|
default:
|
|
unsupported:
|
|
hawk_rtx_seterrfmt(rtx, &symnde->loc, HAWK_ENOENT,
|
|
HAWK_T("'%.*js' not a scalar module symbol or unsupported"),
|
|
symnde->name.len, symnde->name.ptr);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
if (HAWK_UNLIKELY(!v)) ADJERR_LOC(rtx, &symnde->loc);
|
|
return v;
|
|
}
|
|
|
|
static hawk_val_t* eval_named (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
HAWK_ASSERT(((hawk_nde_var_t*)nde)->id.idxa < rtx->named_slot_count);
|
|
return HAWK_RTX_STACK_NAMED(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
}
|
|
|
|
static hawk_val_t* eval_gbl (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
return HAWK_RTX_STACK_GBL(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
}
|
|
|
|
static hawk_val_t* eval_lcl (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
return HAWK_RTX_STACK_LCL(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
}
|
|
|
|
static hawk_val_t* eval_arg (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
return HAWK_RTX_STACK_ARG(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
}
|
|
|
|
static hawk_val_t* eval_indexed (hawk_rtx_t* rtx, hawk_nde_var_t* var)
|
|
{
|
|
hawk_map_t* map; /* containing map */
|
|
hawk_ooch_t* str = HAWK_NULL;
|
|
hawk_oow_t len;
|
|
hawk_ooch_t idxbuf[HAWK_IDX_BUF_SIZE];
|
|
|
|
hawk_arr_t* arr; /* containing array */
|
|
hawk_ooi_t idx;
|
|
|
|
hawk_nde_t* remidx;
|
|
|
|
hawk_val_t* v;
|
|
hawk_val_type_t vtype;
|
|
|
|
v = fetch_topval_from_var(rtx, var);
|
|
vtype = HAWK_RTX_GETVALTYPE(rtx, v);
|
|
|
|
HAWK_ASSERT(var->idx != HAWK_NULL);
|
|
|
|
switch (vtype)
|
|
{
|
|
case HAWK_VAL_NIL:
|
|
v = assign_newmapval_to_var(rtx, var);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
vtype = HAWK_VAL_MAP;
|
|
goto init_val_map;
|
|
|
|
case HAWK_VAL_MAP:
|
|
init_val_map:
|
|
len = HAWK_COUNTOF(idxbuf);
|
|
str = idxnde_to_str(rtx, var->idx, idxbuf, &len, &remidx, HAWK_NULL);
|
|
if (HAWK_UNLIKELY(!str)) goto oops;
|
|
map = ((hawk_val_map_t*)v)->map;
|
|
break;
|
|
|
|
case HAWK_VAL_ARR:
|
|
idx = idxnde_to_int(rtx, var->idx, &remidx);
|
|
if (idx <= -1) goto oops;
|
|
arr = ((hawk_val_arr_t*)v)->arr;
|
|
break;
|
|
|
|
default:
|
|
hawk_rtx_seterrnum(rtx, &var->loc, HAWK_ENOTIDXACC);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
#if defined(HAWK_ENABLE_GC)
|
|
while (remidx)
|
|
{
|
|
hawk_val_type_t container_vtype;
|
|
|
|
if (vtype == HAWK_VAL_MAP)
|
|
{
|
|
hawk_map_pair_t* pair;
|
|
pair = hawk_map_search(map, str, len);
|
|
v = pair? (hawk_val_t*)HAWK_MAP_VPTR(pair): hawk_val_nil;
|
|
}
|
|
else
|
|
{
|
|
v = (idx < HAWK_ARR_SIZE(arr) && HAWK_ARR_SLOT(arr, idx))? ((hawk_val_t*)HAWK_ARR_DPTR(arr, idx)): hawk_val_nil;
|
|
}
|
|
container_vtype = vtype;
|
|
vtype = HAWK_RTX_GETVALTYPE(rtx, v);
|
|
|
|
switch (vtype)
|
|
{
|
|
case HAWK_VAL_MAP:
|
|
val_map:
|
|
if (str && str != idxbuf) hawk_rtx_freemem(rtx, str);
|
|
len = HAWK_COUNTOF(idxbuf);
|
|
str = idxnde_to_str(rtx, remidx, idxbuf, &len, &remidx, HAWK_NULL);
|
|
if (HAWK_UNLIKELY(!str)) goto oops;
|
|
map = ((hawk_val_map_t*)v)->map;
|
|
break;
|
|
|
|
case HAWK_VAL_ARR:
|
|
val_arr:
|
|
idx = idxnde_to_int(rtx, remidx, &remidx);
|
|
if (HAWK_UNLIKELY(idx <= -1)) goto oops;
|
|
arr = ((hawk_val_arr_t*)v)->arr;
|
|
break;
|
|
|
|
default:
|
|
if (vtype == HAWK_VAL_NIL /* || (rtx->hawk->opt.trait & HAWK_FLEXMAP) no flexmap because this is in a 'get' context */)
|
|
{
|
|
if (container_vtype == HAWK_VAL_MAP)
|
|
{
|
|
v = assign_newmapval_in_map(rtx, map, str, len);
|
|
if (HAWK_UNLIKELY(!v)) { ADJERR_LOC(rtx, &var->loc); goto oops; }
|
|
vtype = HAWK_VAL_MAP;
|
|
goto val_map;
|
|
}
|
|
else
|
|
{
|
|
v = assign_newarrval_in_arr(rtx, arr, idx);
|
|
if (HAWK_UNLIKELY(!v)) { ADJERR_LOC(rtx, &var->loc); goto oops; }
|
|
vtype = HAWK_VAL_ARR;
|
|
goto val_arr;
|
|
}
|
|
}
|
|
|
|
hawk_rtx_seterrnum(rtx, &var->loc, HAWK_ENOTIDXACC);
|
|
goto oops;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (vtype == HAWK_VAL_MAP)
|
|
{
|
|
hawk_map_pair_t* pair;
|
|
pair = hawk_map_search(map, str, len);
|
|
if (str && str != idxbuf) hawk_rtx_freemem(rtx, str);
|
|
return pair? (hawk_val_t*)HAWK_MAP_VPTR(pair): hawk_val_nil;
|
|
}
|
|
else
|
|
{
|
|
/* return nil if the index is out of range or the element at the index is not set.
|
|
* no check for a negative index as it's guaranteed to be positive by idxnde_to_int() */
|
|
if (str && str != idxbuf) hawk_rtx_freemem(rtx, str);
|
|
return (idx < HAWK_ARR_SIZE(arr) && HAWK_ARR_SLOT(arr, idx))? ((hawk_val_t*)HAWK_ARR_DPTR(arr, idx)): hawk_val_nil;
|
|
}
|
|
|
|
oops:
|
|
if (str && str != idxbuf) hawk_rtx_freemem(rtx, str);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
static hawk_val_t* eval_namedidx (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
return eval_indexed(rtx, (hawk_nde_var_t*)nde);
|
|
}
|
|
|
|
static hawk_val_t* eval_gblidx (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
return eval_indexed(rtx, (hawk_nde_var_t*)nde);
|
|
}
|
|
|
|
static hawk_val_t* eval_lclidx (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
return eval_indexed(rtx, (hawk_nde_var_t*)nde);
|
|
}
|
|
|
|
static hawk_val_t* eval_argidx (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
return eval_indexed(rtx, (hawk_nde_var_t*)nde);
|
|
}
|
|
|
|
static hawk_val_t* eval_pos (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_nde_pos_t* pos = (hawk_nde_pos_t*)nde;
|
|
hawk_val_t* v;
|
|
hawk_int_t lv;
|
|
int n;
|
|
|
|
v = eval_expression(rtx, pos->val);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
n = hawk_rtx_valtoint_inline(rtx, v, &lv);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
if (n <= -1)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_EPOSIDX);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
if (lv < 0)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_EPOSIDX);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
v = POS_VAL(rtx, lv);
|
|
#if 0
|
|
if (lv == 0) v = rtx->inrec.d0;
|
|
else if (lv > 0 && lv <= (hawk_int_t)rtx->inrec.nflds)
|
|
v = rtx->inrec.flds[lv-1].val;
|
|
else v = hawk_val_zls; /*hawk_val_nil;*/
|
|
#endif
|
|
|
|
return v;
|
|
}
|
|
|
|
static hawk_val_t* __eval_getline (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_nde_getline_t* p;
|
|
hawk_val_t* v = HAWK_NULL, * tmp;
|
|
hawk_oocs_t dst = { HAWK_NULL, 0 };
|
|
hawk_ooecs_t* buf;
|
|
int n, x;
|
|
|
|
p = (hawk_nde_getline_t*)nde;
|
|
|
|
HAWK_ASSERT(
|
|
(p->in_type == HAWK_IN_PIPE && p->in != HAWK_NULL) ||
|
|
(p->in_type == HAWK_IN_RWPIPE && p->in != HAWK_NULL) ||
|
|
(p->in_type == HAWK_IN_FILE && p->in != HAWK_NULL) ||
|
|
(p->in_type == HAWK_IN_CONSOLE && p->in == HAWK_NULL));
|
|
|
|
if (p->in)
|
|
{
|
|
v = io_nde_to_str(rtx, p->in, &dst, 0);
|
|
if (!v || dst.len <= 0)
|
|
{
|
|
n = -1;
|
|
goto skip_read;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dst.ptr = (hawk_ooch_t*)HAWK_T("");
|
|
dst.len = 0;
|
|
}
|
|
|
|
buf = &rtx->inrec.lineg;
|
|
read_console_again:
|
|
hawk_ooecs_clear(&rtx->inrec.lineg);
|
|
|
|
n = hawk_rtx_readio(rtx, p->in_type, dst.ptr, buf);
|
|
|
|
if (n <= 0)
|
|
{
|
|
/* make getline return -1 */
|
|
if (p->var)
|
|
{
|
|
/* it's important to call do_assignment(). consider an expression like this:
|
|
* getline x[++j]
|
|
* withtout do_assignment(), ++j is skipped when getline returns 0. */
|
|
tmp = do_assignment(rtx, p->var, hawk_val_zls, 0);
|
|
if (HAWK_UNLIKELY(!tmp)) goto oops;
|
|
}
|
|
}
|
|
else /*if (n > 0)*/
|
|
{
|
|
if (p->in_type == HAWK_IN_CONSOLE)
|
|
{
|
|
HAWK_ASSERT(p->in == HAWK_NULL);
|
|
if (rtx->nrflt.limit > 0)
|
|
{
|
|
/* record filter based on record number(NR) */
|
|
if (((rtx->gbl.nr / rtx->nrflt.limit) % rtx->nrflt.size) != rtx->nrflt.rank)
|
|
{
|
|
if (update_fnr(rtx, rtx->gbl.fnr + 1, rtx->gbl.nr + 1) <= -1) goto oops;
|
|
/* this jump is a bit dirty. the 'if' block below hawk_rtx_readio()
|
|
* will never be true. but this makes code confusing */
|
|
goto read_console_again;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!p->var)
|
|
{
|
|
/* set $0 with the input value */
|
|
x = hawk_rtx_setrec(rtx, 0, HAWK_OOECS_OOCS(buf), 1);
|
|
if (x <= -1) goto oops;
|
|
}
|
|
else
|
|
{
|
|
hawk_val_t* vv;
|
|
|
|
/* treat external input numerically if it can compose a number. */
|
|
/*vv = hawk_rtx_makestrvalwithoocs(rtx, HAWK_OOECS_OOCS(buf));*/
|
|
vv = hawk_rtx_makenumorstrvalwithoochars(rtx, HAWK_OOECS_PTR(buf), HAWK_OOECS_LEN(buf), 0);
|
|
if (HAWK_UNLIKELY(!vv))
|
|
{
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
goto oops;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, vv);
|
|
tmp = do_assignment(rtx, p->var, vv, 0);
|
|
hawk_rtx_refdownval_inline(rtx, vv);
|
|
if (HAWK_UNLIKELY(!tmp)) goto oops;
|
|
}
|
|
|
|
/* update FNR & NR if reading from console */
|
|
if (p->in_type == HAWK_IN_CONSOLE &&
|
|
update_fnr(rtx, rtx->gbl.fnr + 1, rtx->gbl.nr + 1) <= -1)
|
|
{
|
|
goto oops;
|
|
}
|
|
}
|
|
|
|
skip_read:
|
|
if (v)
|
|
{
|
|
if (dst.ptr) hawk_rtx_freevaloocstr(rtx, v, dst.ptr);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
}
|
|
|
|
tmp = hawk_rtx_makeintval_inline(rtx, n);
|
|
if (HAWK_UNLIKELY(!tmp)) ADJERR_LOC(rtx, &nde->loc);
|
|
return tmp;
|
|
|
|
oops:
|
|
if (v)
|
|
{
|
|
if (dst.ptr) hawk_rtx_freevaloocstr(rtx, v, dst.ptr);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
}
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
static hawk_val_t* __eval_getbline (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_nde_getline_t* p;
|
|
hawk_val_t* v = HAWK_NULL, * tmp;
|
|
hawk_oocs_t dst = {HAWK_NULL, 0};
|
|
hawk_becs_t* buf;
|
|
int n;
|
|
|
|
p = (hawk_nde_getline_t*)nde;
|
|
|
|
HAWK_ASSERT(
|
|
(p->in_type == HAWK_IN_PIPE && p->in != HAWK_NULL) ||
|
|
(p->in_type == HAWK_IN_RWPIPE && p->in != HAWK_NULL) ||
|
|
(p->in_type == HAWK_IN_FILE && p->in != HAWK_NULL) ||
|
|
(p->in_type == HAWK_IN_CONSOLE && p->in == HAWK_NULL));
|
|
|
|
if (p->in)
|
|
{
|
|
v = io_nde_to_str(rtx, p->in, &dst, 0);
|
|
if (!v || dst.len <= 0)
|
|
{
|
|
n = -1;
|
|
goto skip_read;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dst.ptr = (hawk_ooch_t*)HAWK_T("");
|
|
dst.len = 0;
|
|
}
|
|
|
|
buf = &rtx->inrec.linegb;
|
|
read_console_again:
|
|
hawk_becs_clear(&rtx->inrec.linegb);
|
|
|
|
n = hawk_rtx_readiobytes(rtx, p->in_type, dst.ptr, buf);
|
|
|
|
if (n <= 0)
|
|
{
|
|
/* make getline return -1 or 0*/
|
|
if (p->var)
|
|
{
|
|
tmp = do_assignment(rtx, p->var, hawk_val_zls, 0);
|
|
if (HAWK_UNLIKELY(!tmp)) goto oops;
|
|
}
|
|
}
|
|
else /*if (n > 0)*/
|
|
{
|
|
if (p->in_type == HAWK_IN_CONSOLE)
|
|
{
|
|
HAWK_ASSERT(p->in == HAWK_NULL);
|
|
if (rtx->nrflt.limit > 0)
|
|
{
|
|
/* record filter based on record number(NR) */
|
|
if (((rtx->gbl.nr / rtx->nrflt.limit) % rtx->nrflt.size) != rtx->nrflt.rank)
|
|
{
|
|
if (update_fnr(rtx, rtx->gbl.fnr + 1, rtx->gbl.nr + 1) <= -1) goto oops;
|
|
/* this jump is a bit dirty. the 'if' block below hawk_rtx_readio()
|
|
* will never be true. but this makes code confusing */
|
|
goto read_console_again;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p->var == HAWK_NULL)
|
|
{
|
|
/* set $0 with the input value */
|
|
/*x = hawk_rtx_setbrec(rtx, 0, HAWK_BECS_BCS(buf));
|
|
if (x <= -1) goto oops;*/
|
|
/* TODO: can i support this? */
|
|
hawk_rtx_seterrfmt(rtx, &nde->loc, HAWK_ENOIMPL, HAWK_T("getbline without a variable not supported"));
|
|
goto oops;
|
|
}
|
|
else
|
|
{
|
|
hawk_val_t* vv;
|
|
|
|
/* treat external input numerically if it can compose a number. */
|
|
/*vv = hawk_rtx_makembsvalwithbcs(rtx, HAWK_BECS_BCS(buf));*/
|
|
vv = hawk_rtx_makenumormbsvalwithbchars(rtx, HAWK_BECS_PTR(buf), HAWK_BECS_LEN(buf), 0);
|
|
if (vv == HAWK_NULL)
|
|
{
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
goto oops;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, vv);
|
|
tmp = do_assignment(rtx, p->var, vv, 0);
|
|
hawk_rtx_refdownval_inline(rtx, vv);
|
|
if (tmp == HAWK_NULL) goto oops;
|
|
}
|
|
|
|
/* update FNR & NR if reading from console */
|
|
if (p->in_type == HAWK_IN_CONSOLE &&
|
|
update_fnr(rtx, rtx->gbl.fnr + 1, rtx->gbl.nr + 1) <= -1)
|
|
{
|
|
goto oops;
|
|
}
|
|
}
|
|
|
|
skip_read:
|
|
if (v)
|
|
{
|
|
if (dst.ptr) hawk_rtx_freevaloocstr(rtx, v, dst.ptr);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
}
|
|
tmp = hawk_rtx_makeintval_inline(rtx, n);
|
|
if (!tmp) ADJERR_LOC(rtx, &nde->loc);
|
|
return tmp;
|
|
|
|
oops:
|
|
if (v)
|
|
{
|
|
if (dst.ptr) hawk_rtx_freevaloocstr(rtx, v, dst.ptr);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
}
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
static hawk_val_t* eval_getline (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
return ((hawk_nde_getline_t*)nde)->mbs? __eval_getbline(rtx, nde): __eval_getline(rtx, nde);
|
|
}
|
|
|
|
static hawk_val_t* eval_print (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
int n = run_print(rtx, (hawk_nde_print_t*)nde);
|
|
if (n == PRINT_IOERR) n = -1; /* let print return -1 */
|
|
else if (n <= -1) return HAWK_NULL;
|
|
return hawk_rtx_makeintval_inline(rtx, n);
|
|
}
|
|
|
|
static hawk_val_t* eval_printf (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
int n = run_printf(rtx, (hawk_nde_print_t*)nde);
|
|
if (n == PRINT_IOERR) n = -1; /* let print return -1 */
|
|
else if (n <= -1) return HAWK_NULL;
|
|
return hawk_rtx_makeintval_inline(rtx, n);
|
|
}
|
|
|
|
static int read_record (hawk_rtx_t* rtx)
|
|
{
|
|
hawk_ooi_t n;
|
|
hawk_ooecs_t* buf;
|
|
|
|
read_again:
|
|
if (hawk_rtx_clrrec(rtx, 0) <= -1) return -1;
|
|
|
|
buf = &rtx->inrec.line;
|
|
n = hawk_rtx_readio(rtx, HAWK_IN_CONSOLE, HAWK_T(""), buf);
|
|
if (n <= -1)
|
|
{
|
|
hawk_rtx_clrrec(rtx, 0);
|
|
return -1;
|
|
}
|
|
|
|
#if defined(DEBUG_RUN)
|
|
hawk_logbfmt(hawk_rtx_gethawk(rtx), "record len = %d str=[%.*js]\n", (int)HAWK_OOECS_LEN(buf), HAWK_OOECS_LEN(buf), HAWK_OOECS_PTR(buf));
|
|
#endif
|
|
if (n == 0)
|
|
{
|
|
HAWK_ASSERT(HAWK_OOECS_LEN(buf) == 0);
|
|
return 0;
|
|
}
|
|
|
|
if (rtx->nrflt.limit > 0)
|
|
{
|
|
if (((rtx->gbl.nr / rtx->nrflt.limit) % rtx->nrflt.size) != rtx->nrflt.rank)
|
|
{
|
|
if (update_fnr(rtx, rtx->gbl.fnr + 1, rtx->gbl.nr + 1) <= -1) return -1;
|
|
goto read_again;
|
|
}
|
|
}
|
|
|
|
if (hawk_rtx_setrec(rtx, 0, HAWK_OOECS_OOCS(buf), 1) <= -1 ||
|
|
update_fnr(rtx, rtx->gbl.fnr + 1, rtx->gbl.nr + 1) <= -1) return -1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static hawk_ooch_t* idxnde_to_str (hawk_rtx_t* rtx, hawk_nde_t* nde, hawk_ooch_t* buf, hawk_oow_t* len, hawk_nde_t** remidx, hawk_int_t* firstidxint)
|
|
{
|
|
hawk_ooch_t* str;
|
|
hawk_val_t* idx;
|
|
hawk_int_t idxint;
|
|
|
|
HAWK_ASSERT(nde != HAWK_NULL);
|
|
|
|
if (!nde->next)
|
|
{
|
|
hawk_rtx_valtostr_out_t out;
|
|
|
|
/* single node index */
|
|
idx = eval_expression(rtx, nde);
|
|
if (HAWK_UNLIKELY(!idx)) return HAWK_NULL;
|
|
|
|
hawk_rtx_refupval_inline(rtx, idx);
|
|
|
|
if (firstidxint)
|
|
{
|
|
if (hawk_rtx_valtoint_inline(rtx, idx, &idxint) <= -1)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, idx);
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
}
|
|
|
|
str = HAWK_NULL;
|
|
|
|
if (buf)
|
|
{
|
|
/* try with a fixed-size buffer if given */
|
|
out.type = HAWK_RTX_VALTOSTR_CPLCPY;
|
|
out.u.cplcpy.ptr = buf;
|
|
out.u.cplcpy.len = *len;
|
|
|
|
if (hawk_rtx_valtostr(rtx, idx, &out) >= 0)
|
|
{
|
|
str = out.u.cplcpy.ptr;
|
|
*len = out.u.cplcpy.len;
|
|
HAWK_ASSERT(str == buf);
|
|
}
|
|
}
|
|
|
|
if (!str)
|
|
{
|
|
/* if no fixed-size buffer was given or the fixed-size
|
|
* conversion failed, switch to the dynamic mode */
|
|
out.type = HAWK_RTX_VALTOSTR_CPLDUP;
|
|
if (hawk_rtx_valtostr(rtx, idx, &out) <= -1)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, idx);
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
str = out.u.cpldup.ptr;
|
|
*len = out.u.cpldup.len;
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, idx);
|
|
*remidx = HAWK_NULL;
|
|
}
|
|
else
|
|
{
|
|
/* multidimensional index - e.g. [1,2,3] */
|
|
hawk_ooecs_t idxstr;
|
|
hawk_oocs_t tmp;
|
|
hawk_rtx_valtostr_out_t out;
|
|
hawk_nde_t* xnde;
|
|
int first = 1;
|
|
|
|
out.type = HAWK_RTX_VALTOSTR_STRPCAT;
|
|
out.u.strpcat = &idxstr;
|
|
|
|
if (hawk_ooecs_init(&idxstr, hawk_rtx_getgem(rtx), DEF_BUF_CAPA) <= -1)
|
|
{
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
xnde = nde;
|
|
#if defined(HAWK_ENABLE_GC)
|
|
while (nde && nde->type != HAWK_NDE_NULL)
|
|
#else
|
|
while (nde)
|
|
#endif
|
|
{
|
|
idx = eval_expression(rtx, nde);
|
|
if (HAWK_UNLIKELY(!idx))
|
|
{
|
|
hawk_ooecs_fini(&idxstr);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, idx);
|
|
|
|
if (firstidxint && first)
|
|
{
|
|
if (hawk_rtx_valtoint_inline(rtx, idx, &idxint) <= -1)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, idx);
|
|
hawk_ooecs_fini(&idxstr);
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
first = 0;
|
|
}
|
|
|
|
if (xnde != nde && hawk_ooecs_ncat(&idxstr, rtx->gbl.subsep.ptr, rtx->gbl.subsep.len) == (hawk_oow_t)-1)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, idx);
|
|
hawk_ooecs_fini(&idxstr);
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
if (hawk_rtx_valtostr(rtx, idx, &out) <= -1)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, idx);
|
|
hawk_ooecs_fini(&idxstr);
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, idx);
|
|
nde = nde->next;
|
|
}
|
|
|
|
hawk_ooecs_yield(&idxstr, &tmp, 0);
|
|
str = tmp.ptr;
|
|
*len = tmp.len;
|
|
|
|
hawk_ooecs_fini(&idxstr);
|
|
|
|
/* if nde is not HAWK_NULL, it should be of the HAWK_NDE_NULL type */
|
|
*remidx = nde? nde->next: nde;
|
|
}
|
|
|
|
if (firstidxint) *firstidxint = idxint;
|
|
return str;
|
|
}
|
|
|
|
static hawk_ooi_t idxnde_to_int (hawk_rtx_t* rtx, hawk_nde_t* nde, hawk_nde_t** remidx)
|
|
{
|
|
hawk_int_t v;
|
|
hawk_val_t* tmp;
|
|
int n;
|
|
|
|
if (nde->next && nde->next->type != HAWK_NDE_NULL)
|
|
{
|
|
/* multidimensional indices inside a single brakcet is not allowed for an array */
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_EARRIDXMULTI);
|
|
return -1;
|
|
}
|
|
|
|
tmp = eval_expression(rtx, nde);
|
|
if (HAWK_UNLIKELY(!tmp)) return -1;
|
|
|
|
hawk_rtx_refupval_inline(rtx, tmp);
|
|
n = hawk_rtx_valtoint_inline(rtx, tmp, &v);
|
|
hawk_rtx_refdownval_inline(rtx, tmp);
|
|
if (HAWK_UNLIKELY(n <= -1))
|
|
{
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return -1;
|
|
}
|
|
|
|
if (v < 0 || v > HAWK_INT_MAX)
|
|
{
|
|
/* array index out of permitted range */
|
|
hawk_rtx_seterrnum(rtx, &nde->loc, HAWK_EARRIDXRANGE);
|
|
return -1;
|
|
}
|
|
|
|
*remidx = nde->next? nde->next->next: HAWK_NULL;
|
|
return (hawk_ooi_t)v;
|
|
}
|
|
|
|
/* ========================================================================= */
|
|
static hawk_val_t* get_arg_val_for_format(hawk_rtx_t* rtx, hawk_oow_t stack_arg_idx, hawk_oow_t nargs_on_stack, hawk_nde_t* args, hawk_val_t* val)
|
|
{
|
|
hawk_val_t* v;
|
|
|
|
if (!args)
|
|
{
|
|
/* get the argument off the stack */
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EFMTARG);
|
|
return HAWK_NULL;
|
|
}
|
|
v = hawk_rtx_getarg(rtx, stack_arg_idx);
|
|
}
|
|
else
|
|
{
|
|
/* get the argument from the given parse tree */
|
|
if (val)
|
|
{
|
|
/* -1 was passed to hawk_rtx_format() for nargs_on_stack. at the beginning
|
|
* of the function, the mutation was performed to set val and update nargs_on_stack to 2 */
|
|
if (stack_arg_idx >= nargs_on_stack)
|
|
{
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EFMTARG);
|
|
return HAWK_NULL;
|
|
}
|
|
v = val;
|
|
}
|
|
else
|
|
{
|
|
v = eval_expression(rtx, args);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
}
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
|
|
/* for wp_idx */
|
|
#define FMT_WP_WIDTH (0)
|
|
#define FMT_WP_PRECISION (1)
|
|
|
|
/* for fmt_flags */
|
|
#define FMT_FLAG_SPACE (1 << 0)
|
|
#define FMT_FLAG_HASH (1 << 1)
|
|
#define FMT_FLAG_ZERO (1 << 2)
|
|
#define FMT_FLAG_PLUS (1 << 3)
|
|
#define FMT_FLAG_MINUS (1 << 4)
|
|
|
|
#define FMT_V_FLT_TO_STR (1)
|
|
#define FMT_V_CS_TO_STR (2)
|
|
#define FMT_V_FUN_TO_STR (3)
|
|
|
|
/* ========================================================================= */
|
|
|
|
static int format_str(hawk_rtx_t* rtx, hawk_ooecs_t* out, hawk_ooecs_t* fbu, hawk_val_t* v, int fmt_v, hawk_ooch_t fmtc, hawk_int_t wp[2], int wp_idx, int fmt_flags)
|
|
{
|
|
hawk_ooch_t* str_ptr;
|
|
hawk_ooch_t* str_free = HAWK_NULL;
|
|
hawk_oow_t str_len;
|
|
hawk_val_type_t vtype;
|
|
int bytetostr_flagged_radix = 16;
|
|
|
|
if (fmt_v == FMT_V_CS_TO_STR || fmt_v == FMT_V_FUN_TO_STR)
|
|
{
|
|
/* leave str_free as HAWK_NULL and arrange some hakcy values in other variables */
|
|
str_ptr = ((hawk_oocs_t*)v)->ptr;
|
|
str_len = ((hawk_oocs_t*)v)->len;
|
|
vtype = HAWK_VAL_STR; /* this is fake */
|
|
if (fmt_v == FMT_V_FUN_TO_STR) str_len += 6; /* super hacky - 6 for the length of <FUN:> */
|
|
goto do_strfmt;
|
|
}
|
|
|
|
if (fmt_v == FMT_V_FLT_TO_STR) /* hacky */
|
|
{
|
|
/* val_flt_to_str() in val.c calls hawk_rtx_format() with nargs_on_stack of (hawk_oow_t)-1 and the actual value.
|
|
* the actual value is assigned to 'val' at the beginning of hawk_rtx_format().
|
|
*
|
|
* the following code can drive here.
|
|
* BEGIN { CONVFMT="%s"; a=98.76 ""; }
|
|
*
|
|
* when the first attempt to convert 98.76 to a textual form invokes this function with %s and 98.76.
|
|
* it comes to this part because the format specifier is 's'. since the floating-point type is not
|
|
* specially handled, hawk_rtx_valtooocstrdup() is called below. it calls val_flt_to_str() again,
|
|
* which eventually creates recursion and stack depletion.
|
|
*
|
|
* assuming only val_flt_to_str() calls it this way, i must convert the floating point number
|
|
* to text in a rather crude way without calling hawk_rtx_valtooocstrdup().
|
|
*/
|
|
hawk_flt_t r;
|
|
int n;
|
|
|
|
HAWK_ASSERT(HAWK_RTX_GETVALTYPE(rtx, v) == HAWK_VAL_FLT);
|
|
|
|
n = hawk_rtx_valtoflt(rtx, v, &r);
|
|
if (n <= -1) goto oops;
|
|
|
|
/* format the value as if '%g' is given. no flags, precision, width are honored. */
|
|
#if defined(HAWK_USE_FLTMAX)
|
|
/* see fmt.c for info on jj */
|
|
if (hawk_ooecs_ncat(fbu, HAWK_T("jjg"), 3) == (hawk_oow_t)-1) goto oops;
|
|
if (hawk_ooecs_fcat(out, HAWK_OOECS_PTR(fbu), &r) == (hawk_oow_t)-1) goto oops;
|
|
#else
|
|
if (hawk_ooecs_ncat(fbu, HAWK_T("zg"), 2) == (hawk_oow_t)-1) goto oops;
|
|
if (hawk_ooecs_fcat(out, HAWK_OOECS_PTR(fbu), r) == (hawk_oow_t)-1) goto oops;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
hawk_ooch_t ooch_tmp;
|
|
hawk_bch_t bch_tmp;
|
|
hawk_int_t k;
|
|
|
|
vtype = HAWK_RTX_GETVALTYPE(rtx, v);
|
|
switch (vtype)
|
|
{
|
|
case HAWK_VAL_NIL:
|
|
str_ptr = HAWK_T("");
|
|
str_len = 0;
|
|
break;
|
|
|
|
case HAWK_VAL_CHAR:
|
|
ooch_tmp = HAWK_RTX_GETCHARFROMVAL(rtx, v);
|
|
str_ptr = &ooch_tmp;
|
|
str_len = 1;
|
|
break;
|
|
|
|
case HAWK_VAL_STR:
|
|
str_ptr = ((hawk_val_str_t*)v)->val.ptr;
|
|
str_len = ((hawk_val_str_t*)v)->val.len;
|
|
break;
|
|
|
|
case HAWK_VAL_BCHR:
|
|
#if defined(HAWK_OOCH_IS_BCH)
|
|
ooch_tmp = HAWK_RTX_GETBCHRFROMVAL(rtx, v);
|
|
str_ptr = &ooch_tmp;
|
|
str_len = 1;
|
|
#else
|
|
if (fmtc == HAWK_T('s')) goto duplicate;
|
|
bch_tmp = HAWK_RTX_GETBCHRFROMVAL(rtx, v);
|
|
str_ptr = (hawk_ooch_t*)&bch_tmp;
|
|
str_len = 1;
|
|
#endif
|
|
break;
|
|
|
|
case HAWK_VAL_MBS:
|
|
#if defined(HAWK_OOCH_IS_BCH)
|
|
str_ptr = ((hawk_val_mbs_t*)v)->val.ptr;
|
|
str_len = ((hawk_val_mbs_t*)v)->val.len;
|
|
#else
|
|
if (fmtc == HAWK_T('s')) goto duplicate;
|
|
str_ptr = (hawk_ooch_t*)((hawk_val_mbs_t*)v)->val.ptr;
|
|
str_len = ((hawk_val_mbs_t*)v)->val.len;
|
|
#endif
|
|
break;
|
|
|
|
case HAWK_VAL_BOB:
|
|
#if defined(HAWK_OOCH_IS_BCH)
|
|
str_ptr = ((hawk_val_bob_t*)v)->val.ptr;
|
|
str_len = ((hawk_val_bob_t*)v)->val.len;
|
|
#else
|
|
if (fmtc == HAWK_T('s')) goto duplicate;
|
|
str_ptr = (hawk_ooch_t*)((hawk_val_bob_t*)v)->val.ptr;
|
|
str_len = ((hawk_val_bob_t*)v)->val.len;
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
duplicate:
|
|
str_ptr = hawk_rtx_valtooocstrdup(rtx, v, &str_len);
|
|
if (HAWK_UNLIKELY(!str_ptr)) goto oops;
|
|
str_free = str_ptr;
|
|
break;
|
|
}
|
|
|
|
do_strfmt:
|
|
if (wp_idx != FMT_WP_PRECISION || wp[FMT_WP_PRECISION] <= -1 || wp[FMT_WP_PRECISION] > (hawk_int_t)str_len)
|
|
{
|
|
/* precision not specified, or specified to a negative value or greater than the actual length */
|
|
wp[FMT_WP_PRECISION] = (hawk_int_t)str_len;
|
|
}
|
|
if (wp[FMT_WP_PRECISION] > wp[FMT_WP_WIDTH]) wp[FMT_WP_WIDTH] = wp[FMT_WP_PRECISION];
|
|
/* NOTE the wp array itself was mutated in this function */
|
|
|
|
if (!(fmt_flags & FMT_FLAG_MINUS))
|
|
{
|
|
/* right align */
|
|
while (wp[FMT_WP_WIDTH] > wp[FMT_WP_PRECISION])
|
|
{
|
|
if (hawk_ooecs_ccat(out, HAWK_T(' ')) == (hawk_oow_t)-1) goto oops;
|
|
wp[FMT_WP_WIDTH]--;
|
|
}
|
|
}
|
|
|
|
if (fmtc == 'k' || fmtc == 'w') bytetostr_flagged_radix |= HAWK_BYTE_TO_BCSTR_LOWERCASE;
|
|
|
|
k = 0;
|
|
if (fmt_v == FMT_V_FUN_TO_STR)
|
|
{
|
|
/* WARNING - most likely i won't remember how i came out with this ugly code.
|
|
* this is to avoid dynamic memory allocation. but i must find a bettr way
|
|
* to achieve the same thing by refactoring the code.
|
|
* if the precision is not greater than 6, the actual function name is
|
|
* never printed. for example, it produces '<FUN:>' for precision 6. */
|
|
hawk_oow_t count;
|
|
count = (wp[FMT_WP_PRECISION] < 5)? wp[FMT_WP_PRECISION]: 5;
|
|
if (hawk_ooecs_ncat(out, HAWK_T("<FUN:"), count) == (hawk_oow_t)-1) goto oops;
|
|
k += count;
|
|
str_ptr -= 5;
|
|
wp[FMT_WP_PRECISION]--; /* worst code ever - for the trailing > in <FUN:> */
|
|
}
|
|
|
|
for (; k < wp[FMT_WP_PRECISION]; k++)
|
|
{
|
|
hawk_ooch_t curc;
|
|
|
|
#if defined(HAWK_OOCH_IS_BCH)
|
|
curc = str_ptr[k];
|
|
#else
|
|
if ((vtype == HAWK_VAL_MBS || vtype == HAWK_VAL_BCHR) && fmtc != HAWK_T('s'))
|
|
curc = (hawk_uint8_t)((hawk_bch_t*)str_ptr)[k];
|
|
else curc = str_ptr[k];
|
|
#endif
|
|
|
|
if ((fmtc != 's' && !HAWK_BYTE_PRINTABLE(curc)) || fmtc == 'w' || fmtc == 'W')
|
|
{
|
|
hawk_ooch_t xbuf[3];
|
|
if (curc <= 0xFF)
|
|
{
|
|
if (hawk_ooecs_ncat(out, HAWK_T("\\x"), 2) == (hawk_oow_t)-1) goto oops;
|
|
hawk_byte_to_oocstr(curc, xbuf, HAWK_COUNTOF(xbuf), bytetostr_flagged_radix, HAWK_T('0'));
|
|
if (hawk_ooecs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto oops;
|
|
}
|
|
else if (curc <= 0xFFFF)
|
|
{
|
|
hawk_uint16_t u16 = curc;
|
|
if (hawk_ooecs_ncat(out, HAWK_T("\\u"), 2) == (hawk_oow_t)-1) goto oops;
|
|
hawk_byte_to_oocstr((u16 >> 8) & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetostr_flagged_radix, HAWK_T('0'));
|
|
if (hawk_ooecs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto oops;
|
|
hawk_byte_to_oocstr(u16 & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetostr_flagged_radix, HAWK_T('0'));
|
|
if (hawk_ooecs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto oops;
|
|
}
|
|
else
|
|
{
|
|
hawk_uint32_t u32 = curc;
|
|
if (hawk_ooecs_ncat(out, HAWK_T("\\U"), 2) == (hawk_oow_t)-1) goto oops;
|
|
hawk_byte_to_oocstr((u32 >> 24) & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetostr_flagged_radix, HAWK_T('0'));
|
|
if (hawk_ooecs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto oops;
|
|
hawk_byte_to_oocstr((u32 >> 16) & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetostr_flagged_radix, HAWK_T('0'));
|
|
if (hawk_ooecs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto oops;
|
|
hawk_byte_to_oocstr((u32 >> 8) & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetostr_flagged_radix, HAWK_T('0'));
|
|
if (hawk_ooecs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto oops;
|
|
hawk_byte_to_oocstr(u32 & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetostr_flagged_radix, HAWK_T('0'));
|
|
if (hawk_ooecs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto oops;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (hawk_ooecs_ccat(out, curc) == (hawk_oow_t)-1) goto oops;
|
|
}
|
|
}
|
|
|
|
if (fmt_v == FMT_V_FUN_TO_STR)
|
|
{
|
|
wp[FMT_WP_PRECISION]++; /* worst code ever - to make up for decrement above */
|
|
if (k < wp[FMT_WP_PRECISION])
|
|
{
|
|
if (hawk_ooecs_ccat(out, HAWK_T('>')) == (hawk_oow_t)-1) goto oops;
|
|
}
|
|
}
|
|
|
|
if (fmt_flags & FMT_FLAG_MINUS)
|
|
{
|
|
/* left align */
|
|
while (wp[FMT_WP_WIDTH] > wp[FMT_WP_PRECISION])
|
|
{
|
|
if (hawk_ooecs_ccat(out, HAWK_T(' ')) == (hawk_oow_t)-1) goto oops;
|
|
wp[FMT_WP_WIDTH]--;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (str_free) hawk_rtx_freemem(rtx, str_free);
|
|
return 0;
|
|
|
|
oops:
|
|
if (str_free) hawk_rtx_freemem(rtx, str_free);
|
|
return -1;
|
|
}
|
|
|
|
static int format_mbs (hawk_rtx_t* rtx, hawk_becs_t* out, hawk_becs_t* fbu, hawk_val_t* v, int fmt_v, hawk_ooch_t fmtc, hawk_int_t wp[2], int wp_idx, int fmt_flags)
|
|
{
|
|
hawk_bch_t* str_ptr;
|
|
hawk_bch_t* str_free = HAWK_NULL;
|
|
hawk_oow_t str_len;
|
|
hawk_val_type_t vtype;
|
|
int bytetombs_flagged_radix = 16;
|
|
|
|
if (fmt_v == FMT_V_CS_TO_STR || fmt_v == FMT_V_FUN_TO_STR)
|
|
{
|
|
/* leave str_free as HAWK_NULL and arrange some hakcy values in other variables */
|
|
str_ptr = ((hawk_bcs_t*)v)->ptr;
|
|
str_len = ((hawk_bcs_t*)v)->len;
|
|
vtype = HAWK_VAL_STR; /* this is fake */
|
|
if (fmt_v == FMT_V_FUN_TO_STR) str_len += 6; /* super hacky - 6 for the length of <FUN:> */
|
|
goto do_strfmt;
|
|
}
|
|
|
|
if (fmt_v == FMT_V_FLT_TO_STR) /* hacky */
|
|
{
|
|
/* val_flt_to_str() in val.c calls hawk_rtx_format() with nargs_on_stack of (hawk_oow_t)-1 and the actual value.
|
|
* the actual value is assigned to 'val' at the beginning of this function.
|
|
*
|
|
* the following code can drive here.
|
|
* BEGIN { CONVFMT="%s"; a=98.76 ""; }
|
|
*
|
|
* when the first attempt to convert 98.76 to a textual form invokes this function with %s and 98.76.
|
|
* it comes to this part because the format specifier is 's'. since the floating-point type is not
|
|
* specially handled, hawk_rtx_valtooocstrdup() is called below. it calls val_flt_to_str() again,
|
|
* which eventually creates recursion and stack depletion.
|
|
*
|
|
* assuming only val_flt_to_str() calls it this way, i must convert the floating point number
|
|
* to text in a rather crude way without calling hawk_rtx_valtooocstrdup().
|
|
*/
|
|
hawk_flt_t r;
|
|
int n;
|
|
|
|
HAWK_ASSERT(HAWK_RTX_GETVALTYPE(rtx, v) == HAWK_VAL_FLT);
|
|
|
|
n = hawk_rtx_valtoflt(rtx, v, &r);
|
|
if (n <= -1) goto oops;
|
|
|
|
/* format the value as if '%g' is given */
|
|
#if defined(HAWK_USE_FLTMAX)
|
|
/* see fmt.c for info on jj */
|
|
if (hawk_becs_ncat(fbu, HAWK_BT("jjg"), 3) == (hawk_oow_t)-1) goto oops;
|
|
if (hawk_becs_fcat(out, HAWK_BECS_PTR(fbu), &r) == (hawk_oow_t)-1) goto oops;
|
|
#else
|
|
if (hawk_becs_ncat(fbu, HAWK_BT("zg"), 2) == (hawk_oow_t)-1) goto oops;
|
|
if (hawk_becs_fcat(out, HAWK_BECS_PTR(fbu), r) == (hawk_oow_t)-1) goto oops;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
hawk_bch_t bchr_tmp;
|
|
hawk_ooch_t ooch_tmp;
|
|
hawk_int_t k;
|
|
|
|
vtype = HAWK_RTX_GETVALTYPE(rtx, v);
|
|
switch (vtype)
|
|
{
|
|
case HAWK_VAL_NIL:
|
|
str_ptr = HAWK_BT("");
|
|
str_len = 0;
|
|
break;
|
|
|
|
case HAWK_VAL_BCHR:
|
|
bchr_tmp = HAWK_RTX_GETBCHRFROMVAL(rtx, v);
|
|
str_ptr = &bchr_tmp;
|
|
str_len = 1;
|
|
break;
|
|
|
|
case HAWK_VAL_MBS:
|
|
str_ptr = ((hawk_val_mbs_t*)v)->val.ptr;
|
|
str_len = ((hawk_val_mbs_t*)v)->val.len;
|
|
break;
|
|
|
|
case HAWK_VAL_BOB:
|
|
str_ptr = (hawk_bch_t*)((hawk_val_bob_t*)v)->val.ptr;
|
|
str_len = ((hawk_val_bob_t*)v)->val.len;
|
|
break;
|
|
|
|
case HAWK_VAL_CHAR:
|
|
#if defined(HAWK_OOCH_IS_BCH)
|
|
bchr_tmp = HAWK_RTX_GETBCHRFROMVAL(rtx, v);
|
|
str_ptr = &bchr_tmp;
|
|
str_len = 1;
|
|
#else
|
|
if (fmtc == HAWK_BT('s')) goto duplicate;
|
|
ooch_tmp = HAWK_RTX_GETCHARFROMVAL(rtx, v);
|
|
str_ptr = (hawk_bch_t*)&ooch_tmp;
|
|
str_len = 1 * (HAWK_SIZEOF_OOCH_T / HAWK_SIZEOF_BCH_T);
|
|
#endif
|
|
break;
|
|
|
|
case HAWK_VAL_STR:
|
|
#if defined(HAWK_OOCH_IS_BCH)
|
|
str_ptr = ((hawk_val_str_t*)v)->val.ptr;
|
|
str_len = ((hawk_val_str_t*)v)->val.len;
|
|
#else
|
|
if (fmtc == HAWK_BT('s')) goto duplicate;
|
|
/* arrange to print the wide character string byte by byte regardless of byte order */
|
|
str_ptr = (hawk_bch_t*)((hawk_val_str_t*)v)->val.ptr;
|
|
str_len = ((hawk_val_str_t*)v)->val.len * (HAWK_SIZEOF_OOCH_T / HAWK_SIZEOF_BCH_T);
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
duplicate:
|
|
str_ptr = hawk_rtx_valtobcstrdup(rtx, v, &str_len);
|
|
if (!str_ptr) goto oops;
|
|
str_free = str_ptr;
|
|
break;
|
|
}
|
|
|
|
do_strfmt:
|
|
if (wp_idx != FMT_WP_PRECISION || wp[FMT_WP_PRECISION] <= -1 || wp[FMT_WP_PRECISION] > (hawk_int_t)str_len)
|
|
{
|
|
/* precision not specified, or specified to a negative value or greater than the actual length */
|
|
wp[FMT_WP_PRECISION] = (hawk_int_t)str_len;
|
|
}
|
|
if (wp[FMT_WP_PRECISION] > wp[FMT_WP_WIDTH]) wp[FMT_WP_WIDTH] = wp[FMT_WP_PRECISION];
|
|
/* NOTE the wp array itself was mutated in this function */
|
|
|
|
if (!(fmt_flags & FMT_FLAG_MINUS))
|
|
{
|
|
/* right align */
|
|
while (wp[FMT_WP_WIDTH] > wp[FMT_WP_PRECISION])
|
|
{
|
|
if (hawk_becs_ccat(out, HAWK_BT(' ')) == (hawk_oow_t)-1) goto oops;
|
|
wp[FMT_WP_WIDTH]--;
|
|
}
|
|
}
|
|
|
|
if (fmtc == 'k' || fmtc == 'w') bytetombs_flagged_radix |= HAWK_BYTE_TO_BCSTR_LOWERCASE;
|
|
|
|
k = 0;
|
|
if (fmt_v == FMT_V_FUN_TO_STR)
|
|
{
|
|
/* WARNING - most likely i won't remember how i came out with this ugly code.
|
|
* this is to avoid dynamic memory allocation. but i must find a bettr way
|
|
* to achieve the same thing by refactoring the code.
|
|
* if the precision is not greater than 6, the actual function name is
|
|
* never printed. for example, it produces '<FUN:>' for precision 6. */
|
|
hawk_oow_t count;
|
|
count = (wp[FMT_WP_PRECISION] < 5)? wp[FMT_WP_PRECISION]: 5;
|
|
if (hawk_becs_ncat(out, HAWK_BT("<FUN:"), count) == (hawk_oow_t)-1) goto oops;
|
|
k += count;
|
|
str_ptr -= 5;
|
|
wp[FMT_WP_PRECISION]--; /* worst code ever - for the trailing > in <FUN:> */
|
|
}
|
|
|
|
for (; k < wp[FMT_WP_PRECISION]; k++)
|
|
{
|
|
hawk_bch_t curc;
|
|
|
|
curc = str_ptr[k];
|
|
|
|
if ((fmtc != 's' && !HAWK_BYTE_PRINTABLE(curc)) || fmtc == 'w' || fmtc == 'W')
|
|
{
|
|
hawk_bch_t xbuf[3];
|
|
#if 0 /* the range check isn't needed for hawk_bch_t. it's always <= 0xFF */
|
|
|
|
if (curc <= 0xFF)
|
|
{
|
|
#endif
|
|
if (hawk_becs_ncat(out, HAWK_BT("\\x"), 2) == (hawk_oow_t)-1) goto oops;
|
|
hawk_byte_to_bcstr(curc, xbuf, HAWK_COUNTOF(xbuf), bytetombs_flagged_radix, HAWK_BT('0'));
|
|
if (hawk_becs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto oops;
|
|
#if 0
|
|
}
|
|
else if (curc <= 0xFFFF)
|
|
{
|
|
hawk_uint16_t u16 = curc;
|
|
if (hawk_becs_ncat(out, HAWK_BT("\\u"), 2) == (hawk_oow_t)-1) goto oops;
|
|
hawk_byte_to_bcstr((u16 >> 8) & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetombs_flagged_radix, HAWK_BT('0'));
|
|
if (hawk_becs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto oops;
|
|
hawk_byte_to_bcstr(u16 & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetombs_flagged_radix, HAWK_BT('0'));
|
|
if (hawk_becs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto oops;
|
|
}
|
|
else
|
|
{
|
|
hawk_uint32_t u32 = curc;
|
|
if (hawk_becs_ncat(out, HAWK_BT("\\U"), 2) == (hawk_oow_t)-1) goto oops;
|
|
hawk_byte_to_bcstr((u32 >> 24) & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetombs_flagged_radix, HAWK_BT('0'));
|
|
if (hawk_becs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto oops;
|
|
hawk_byte_to_bcstr((u32 >> 16) & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetombs_flagged_radix, HAWK_BT('0'));
|
|
if (hawk_becs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto oops;
|
|
hawk_byte_to_bcstr((u32 >> 8) & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetombs_flagged_radix, HAWK_BT('0'));
|
|
if (hawk_becs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto oops;
|
|
hawk_byte_to_bcstr(u32 & 0xFF, xbuf, HAWK_COUNTOF(xbuf), bytetombs_flagged_radix, HAWK_BT('0'));
|
|
if (hawk_becs_ncat(out, xbuf, 2) == (hawk_oow_t)-1) goto oops;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (hawk_becs_ccat(out, curc) == (hawk_oow_t)-1) goto oops;
|
|
}
|
|
}
|
|
|
|
if (fmt_v == FMT_V_FUN_TO_STR)
|
|
{
|
|
wp[FMT_WP_PRECISION]++; /* worst code ever - to make up for decrement above */
|
|
if (k < wp[FMT_WP_PRECISION])
|
|
{
|
|
if (hawk_becs_ccat(out, HAWK_BT('>')) == (hawk_oow_t)-1) goto oops;
|
|
}
|
|
}
|
|
|
|
if (fmt_flags & FMT_FLAG_MINUS)
|
|
{
|
|
/* left align */
|
|
while (wp[FMT_WP_WIDTH] > wp[FMT_WP_PRECISION])
|
|
{
|
|
if (hawk_becs_ccat(out, HAWK_BT(' ')) == (hawk_oow_t)-1) goto oops;
|
|
wp[FMT_WP_WIDTH]--;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (str_free) hawk_rtx_freemem(rtx, str_free);
|
|
return 0;
|
|
|
|
oops:
|
|
if (str_free) hawk_rtx_freemem(rtx, str_free);
|
|
return -1;
|
|
}
|
|
hawk_ooch_t* hawk_rtx_format (
|
|
hawk_rtx_t* rtx, hawk_ooecs_t* out, hawk_ooecs_t* fbu,
|
|
const hawk_ooch_t* fmt, hawk_oow_t fmt_len,
|
|
hawk_oow_t nargs_on_stack, hawk_nde_t* args, hawk_oow_t* len)
|
|
{
|
|
hawk_oow_t i;
|
|
hawk_oow_t stack_arg_idx = 1;
|
|
hawk_val_t* val;
|
|
|
|
#define OUT_CHAR(c) do { if (hawk_ooecs_ccat(out, (c)) == (hawk_oow_t)-1) return HAWK_NULL; } while(0)
|
|
|
|
#define OUT_STR(ptr,len) do { if (hawk_ooecs_ncat(out, (ptr), (len)) == (hawk_oow_t)-1) return HAWK_NULL; } while(0)
|
|
|
|
#define FMT_CHAR(c) do { if (hawk_ooecs_ccat(fbu, (c)) == (hawk_oow_t)-1) return HAWK_NULL; } while(0)
|
|
|
|
#define FMT_STR(ptr,len) do { if (hawk_ooecs_ncat(fbu, (ptr), (len)) == (hawk_oow_t)-1) return HAWK_NULL; } while(0)
|
|
|
|
#define GROW(buf) do { \
|
|
if ((buf)->ptr) \
|
|
{ \
|
|
hawk_rtx_freemem(rtx, (buf)->ptr); \
|
|
(buf)->ptr = HAWK_NULL; \
|
|
} \
|
|
(buf)->len += (buf)->inc; \
|
|
(buf)->ptr = (hawk_ooch_t*)hawk_rtx_allocmem(rtx, (buf)->len * HAWK_SIZEOF(hawk_ooch_t)); \
|
|
if ((buf)->ptr == HAWK_NULL) \
|
|
{ \
|
|
(buf)->len = 0; \
|
|
return HAWK_NULL; \
|
|
} \
|
|
} while(0)
|
|
|
|
#define GROW_WITH_INC(buf,incv) do { \
|
|
if ((buf)->ptr) \
|
|
{ \
|
|
hawk_rtx_freemem(rtx, (buf)->ptr); \
|
|
(buf)->ptr = HAWK_NULL; \
|
|
} \
|
|
(buf)->len += ((incv) > (buf)->inc)? (incv): (buf)->inc; \
|
|
(buf)->ptr = (hawk_ooch_t*)hawk_rtx_allocmem(rtx, (buf)->len * HAWK_SIZEOF(hawk_ooch_t)); \
|
|
if ((buf)->ptr == HAWK_NULL) \
|
|
{ \
|
|
(buf)->len = 0; \
|
|
return HAWK_NULL; \
|
|
} \
|
|
} while(0)
|
|
|
|
/* run->format.tmp.ptr should have been assigned a pointer to a block of memory before this function has been called */
|
|
HAWK_ASSERT(rtx->format.tmp.ptr != HAWK_NULL);
|
|
|
|
if (nargs_on_stack == (hawk_oow_t)-1)
|
|
{
|
|
/* dirty hack to support a single value argument instead of a tree node */
|
|
val = (hawk_val_t*)args;
|
|
nargs_on_stack = 2; /* indicate 2 arguments of a formatting specifier and the given value */
|
|
}
|
|
else
|
|
{
|
|
val = HAWK_NULL;
|
|
}
|
|
|
|
if (out == HAWK_NULL) out = &rtx->format.out;
|
|
if (fbu == HAWK_NULL) fbu = &rtx->format.fmt;
|
|
|
|
hawk_ooecs_clear(out);
|
|
hawk_ooecs_clear(fbu);
|
|
|
|
for (i = 0; i < fmt_len; i++)
|
|
{
|
|
hawk_val_t* vxx;
|
|
hawk_ooch_t fmtc;
|
|
hawk_int_t wp[2];
|
|
int wp_idx;
|
|
|
|
int flags;
|
|
|
|
if (HAWK_OOECS_LEN(fbu) == 0)
|
|
{
|
|
/* format specifier is empty */
|
|
if (fmt[i] == HAWK_T('%'))
|
|
{
|
|
/* add % to format specifier (fbu) */
|
|
FMT_CHAR(fmt[i]);
|
|
}
|
|
else
|
|
{
|
|
/* normal output */
|
|
OUT_CHAR(fmt[i]);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* handle flags */
|
|
flags = 0;
|
|
while (i < fmt_len)
|
|
{
|
|
switch (fmt[i])
|
|
{
|
|
case HAWK_T(' '):
|
|
flags |= FMT_FLAG_SPACE;
|
|
break;
|
|
case HAWK_T('#'):
|
|
flags |= FMT_FLAG_HASH;
|
|
break;
|
|
case HAWK_T('0'):
|
|
flags |= FMT_FLAG_ZERO;
|
|
break;
|
|
case HAWK_T('+'):
|
|
flags |= FMT_FLAG_PLUS;
|
|
break;
|
|
case HAWK_T('-'):
|
|
flags |= FMT_FLAG_MINUS;
|
|
break;
|
|
default:
|
|
goto wp_mod_init;
|
|
}
|
|
|
|
FMT_CHAR(fmt[i]); i++;
|
|
}
|
|
|
|
wp_mod_init:
|
|
wp[FMT_WP_WIDTH] = 0; /* width */
|
|
wp[FMT_WP_PRECISION] = -1; /* precision */
|
|
wp_idx = FMT_WP_WIDTH; /* width first */
|
|
|
|
wp_mod_main:
|
|
if (i < fmt_len && fmt[i] == HAWK_T('*'))
|
|
{
|
|
/* variable width/precision modifier.
|
|
* take the width/precision from a parameter and
|
|
* transform it to a fixed length format */
|
|
hawk_val_t* v;
|
|
int n;
|
|
|
|
v = get_arg_val_for_format(rtx, stack_arg_idx, nargs_on_stack, args, val);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
n = hawk_rtx_valtoint_inline(rtx, v, &wp[wp_idx]);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
if (HAWK_UNLIKELY(n <= -1)) return HAWK_NULL;
|
|
|
|
do
|
|
{
|
|
n = hawk_fmt_intmax_to_oocstr(
|
|
rtx->format.tmp.ptr,
|
|
rtx->format.tmp.len,
|
|
wp[wp_idx],
|
|
10 | HAWK_FMT_INTMAX_NOTRUNC | HAWK_FMT_INTMAX_NONULL,
|
|
-1,
|
|
HAWK_T('\0'),
|
|
HAWK_NULL
|
|
);
|
|
if (n <= -1)
|
|
{
|
|
/* -n is the number of characters required
|
|
* including terminating null */
|
|
GROW_WITH_INC(&rtx->format.tmp, -n);
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
while (1);
|
|
|
|
FMT_STR(rtx->format.tmp.ptr, n);
|
|
|
|
if (!args || val) stack_arg_idx++;
|
|
else args = args->next;
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
/* fixed width/precision modifier */
|
|
if (i < fmt_len && hawk_is_ooch_digit(fmt[i]))
|
|
{
|
|
wp[wp_idx] = 0;
|
|
do
|
|
{
|
|
wp[wp_idx] = wp[wp_idx] * 10 + fmt[i] - HAWK_T('0');
|
|
FMT_CHAR(fmt[i]); i++;
|
|
}
|
|
while (i < fmt_len && hawk_is_ooch_digit(fmt[i]));
|
|
}
|
|
}
|
|
|
|
if (wp_idx == FMT_WP_WIDTH && i < fmt_len && fmt[i] == HAWK_T('.'))
|
|
{
|
|
FMT_CHAR(fmt[i]); i++;
|
|
wp[FMT_WP_PRECISION] = 0;
|
|
wp_idx = FMT_WP_PRECISION; /* change index to precision */
|
|
goto wp_mod_main;
|
|
}
|
|
|
|
if (i >= fmt_len) break;
|
|
|
|
if (wp[FMT_WP_WIDTH] < 0)
|
|
{
|
|
wp[FMT_WP_WIDTH] = -wp[FMT_WP_WIDTH];
|
|
flags |= FMT_FLAG_MINUS;
|
|
}
|
|
|
|
fmtc = fmt[i]; /* 'i' don't get increment after this */
|
|
vxx = HAWK_NULL;
|
|
|
|
if (fmtc == 'v')
|
|
{
|
|
hawk_val_type_t vtype;
|
|
hawk_oocs_t fixed_str;
|
|
int xx;
|
|
|
|
vxx = get_arg_val_for_format(rtx, stack_arg_idx, nargs_on_stack, args, val);
|
|
if (HAWK_UNLIKELY(!vxx)) return HAWK_NULL;
|
|
|
|
/* depending on the value, mutate the formatting specifier */
|
|
vtype = HAWK_RTX_GETVALTYPE(rtx, vxx);
|
|
switch (vtype)
|
|
{
|
|
case HAWK_VAL_NIL:
|
|
hawk_rtx_refupval_inline(rtx, vxx);
|
|
fixed_str.ptr = HAWK_T("<@nil>");
|
|
fixed_str.len = 6;
|
|
xx = format_str(rtx, out, fbu, (hawk_val_t*)&fixed_str, FMT_V_CS_TO_STR, fmtc, wp, wp_idx, flags);
|
|
hawk_rtx_refdownval_inline(rtx, vxx);
|
|
if (xx <= -1) return HAWK_NULL;
|
|
goto next_arg;
|
|
|
|
case HAWK_VAL_BOOL:
|
|
hawk_rtx_refupval_inline(rtx, vxx);
|
|
if (((hawk_val_bool_t*)vxx)->val)
|
|
{
|
|
fixed_str.ptr = HAWK_T("<@true>");
|
|
fixed_str.len = 7;
|
|
}
|
|
else
|
|
{
|
|
fixed_str.ptr = HAWK_T("<@false>");
|
|
fixed_str.len = 8;
|
|
}
|
|
xx = format_str(rtx, out, fbu, (hawk_val_t*)&fixed_str, FMT_V_CS_TO_STR, fmtc, wp, wp_idx, flags);
|
|
hawk_rtx_refdownval_inline(rtx, vxx);
|
|
if (xx <= -1) return HAWK_NULL;
|
|
goto next_arg;
|
|
|
|
|
|
case HAWK_VAL_CHAR:
|
|
fmtc = 'c';
|
|
break;
|
|
case HAWK_VAL_BCHR:
|
|
fmtc = 'k';
|
|
break;
|
|
case HAWK_VAL_INT:
|
|
fmtc = 'd';
|
|
break;
|
|
case HAWK_VAL_FLT:
|
|
fmtc = 'f';
|
|
break;
|
|
case HAWK_VAL_STR:
|
|
fmtc = 's';
|
|
break;
|
|
case HAWK_VAL_MBS:
|
|
fmtc = 'k';
|
|
break;
|
|
case HAWK_VAL_BOB:
|
|
fmtc = 'k';
|
|
break;
|
|
|
|
case HAWK_VAL_ARR: /* TOOD: */
|
|
hawk_rtx_refupval_inline(rtx, vxx);
|
|
fixed_str.ptr = HAWK_T("<ARRAY>");
|
|
fixed_str.len = 7;
|
|
xx = format_str(rtx, out, fbu, (hawk_val_t*)&fixed_str, FMT_V_CS_TO_STR, fmtc, wp, wp_idx, flags);
|
|
hawk_rtx_refdownval_inline(rtx, vxx);
|
|
if (xx <= -1) return HAWK_NULL;
|
|
goto next_arg;
|
|
|
|
case HAWK_VAL_MAP: /* TOOD: */
|
|
hawk_rtx_refupval_inline(rtx, vxx);
|
|
fixed_str.ptr = HAWK_T("<MAP>");
|
|
fixed_str.len = 5;
|
|
xx = format_str(rtx, out, fbu, (hawk_val_t*)&fixed_str, FMT_V_CS_TO_STR, fmtc, wp, wp_idx, flags);
|
|
hawk_rtx_refdownval_inline(rtx, vxx);
|
|
if (xx <= -1) return HAWK_NULL;
|
|
goto next_arg;
|
|
|
|
case HAWK_VAL_FUN:
|
|
hawk_rtx_refupval_inline(rtx, vxx);
|
|
xx = format_str(rtx, out, fbu, (hawk_val_t*)&((hawk_val_fun_t*)vxx)->fun->name, FMT_V_FUN_TO_STR, fmtc, wp, wp_idx, flags);
|
|
hawk_rtx_refdownval_inline(rtx, vxx);
|
|
if (xx <= -1) return HAWK_NULL;
|
|
goto next_arg;
|
|
|
|
default:
|
|
/* do nothing for now */
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fmtc == 'd' || fmtc == 'i' ||
|
|
fmtc == 'x' || fmtc == 'X' ||
|
|
fmtc == 'b' || fmtc == 'B' ||
|
|
fmtc == 'o' || fmtc == 'u')
|
|
{
|
|
hawk_val_t* v;
|
|
hawk_int_t l;
|
|
int n;
|
|
|
|
int fmt_flags;
|
|
int fmt_uint = 0;
|
|
int fmt_width;
|
|
hawk_ooch_t fmt_fill = HAWK_T('\0');
|
|
const hawk_ooch_t* fmt_prefix = HAWK_NULL;
|
|
|
|
if (vxx) v = vxx;
|
|
else
|
|
{
|
|
v = get_arg_val_for_format(rtx, stack_arg_idx, nargs_on_stack, args, val);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
n = hawk_rtx_valtoint_inline(rtx, v, &l);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
if (HAWK_UNLIKELY(n <= -1)) return HAWK_NULL;
|
|
|
|
fmt_flags = HAWK_FMT_INTMAX_NOTRUNC | HAWK_FMT_INTMAX_NONULL;
|
|
|
|
if (l == 0 && wp_idx == FMT_WP_PRECISION && wp[FMT_WP_PRECISION] == 0)
|
|
{
|
|
/* printf ("%.d", 0); printf ("%.0d", 0); printf ("%.*d", 0, 0); */
|
|
/* A zero value with a precision of zero produces no character. */
|
|
fmt_flags |= HAWK_FMT_INTMAX_NOZERO;
|
|
}
|
|
|
|
if (wp[FMT_WP_WIDTH] > 0)
|
|
{
|
|
/* justification for width greater than 0 */
|
|
if (flags & FMT_FLAG_ZERO)
|
|
{
|
|
if (flags & FMT_FLAG_MINUS)
|
|
{
|
|
/* FMT_FLAG_MINUS wins if both FMT_FLAG_ZERO
|
|
* and FMT_FLAG_MINUS are specified. */
|
|
fmt_fill = HAWK_T(' ');
|
|
if (flags & FMT_FLAG_MINUS)
|
|
{
|
|
/* left justification. need to fill the right side */
|
|
fmt_flags |= HAWK_FMT_INTMAX_FILLRIGHT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (wp_idx != FMT_WP_PRECISION) /* if precision is not specified, wp_idx is at FMT_WP_WIDTH */
|
|
{
|
|
/* precision not specified.
|
|
* FMT_FLAG_ZERO can take effect */
|
|
fmt_fill = HAWK_T('0');
|
|
fmt_flags |= HAWK_FMT_INTMAX_FILLCENTER;
|
|
}
|
|
else
|
|
{
|
|
fmt_fill = HAWK_T(' ');
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fmt_fill = HAWK_T(' ');
|
|
if (flags & FMT_FLAG_MINUS)
|
|
{
|
|
/* left justification. need to fill the right side */
|
|
fmt_flags |= HAWK_FMT_INTMAX_FILLRIGHT;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (fmtc)
|
|
{
|
|
case 'B':
|
|
case 'b':
|
|
fmt_flags |= 2;
|
|
fmt_uint = 1;
|
|
if (l && (flags & FMT_FLAG_HASH))
|
|
{
|
|
/* A nonzero value is prefixed with 0b */
|
|
fmt_prefix = HAWK_T("0b");
|
|
}
|
|
break;
|
|
|
|
case 'X':
|
|
fmt_flags |= HAWK_FMT_INTMAX_UPPERCASE;
|
|
case 'x':
|
|
fmt_flags |= 16;
|
|
fmt_uint = 1;
|
|
if (l && (flags & FMT_FLAG_HASH))
|
|
{
|
|
/* A nonzero value is prefixed with 0x */
|
|
fmt_prefix = HAWK_T("0x");
|
|
}
|
|
break;
|
|
|
|
case 'o':
|
|
fmt_flags |= 8;
|
|
fmt_uint = 1;
|
|
if (flags & FMT_FLAG_HASH)
|
|
{
|
|
/* Force a leading zero digit including zero.
|
|
* 0 with FMT_FLAG_HASH and precision 0 still emits '0'.
|
|
* On the contrary, 'X' and 'x' emit no digits
|
|
* for 0 with FMT_FLAG_HASH and precision 0. */
|
|
fmt_flags |= HAWK_FMT_INTMAX_ZEROLEAD;
|
|
}
|
|
break;
|
|
|
|
case 'u':
|
|
fmt_uint = 1;
|
|
default:
|
|
fmt_flags |= 10;
|
|
if (flags & FMT_FLAG_PLUS)
|
|
fmt_flags |= HAWK_FMT_INTMAX_PLUSSIGN;
|
|
if (flags & FMT_FLAG_SPACE)
|
|
fmt_flags |= HAWK_FMT_INTMAX_EMPTYSIGN;
|
|
break;
|
|
}
|
|
|
|
|
|
if (wp[FMT_WP_WIDTH] > 0)
|
|
{
|
|
if (wp[FMT_WP_WIDTH] > rtx->format.tmp.len)
|
|
GROW_WITH_INC(&rtx->format.tmp, wp[FMT_WP_WIDTH] - rtx->format.tmp.len);
|
|
fmt_width = wp[FMT_WP_WIDTH];
|
|
}
|
|
else fmt_width = rtx->format.tmp.len;
|
|
|
|
do
|
|
{
|
|
if (fmt_uint)
|
|
{
|
|
/* Explicit type-casting for 'l' from hawk_int_t
|
|
* to hawk_uint_t is needed before passing it to
|
|
* hawk_fmt_uintmax_to_oocstr().
|
|
*
|
|
* Consider a value of -1 for example.
|
|
* -1 is a value with all bits set.
|
|
* If hawk_int_t is 4 bytes and hawk_uintmax_t
|
|
* is 8 bytes, the value is shown below for
|
|
* each type respectively .
|
|
* -1 - 0xFFFFFFFF (hawk_int_t)
|
|
* -1 - 0xFFFFFFFFFFFFFFFF (hawk_uintmax_t)
|
|
* Implicit typecasting of -1 from hawk_int_t to
|
|
* to hawk_uintmax_t results in 0xFFFFFFFFFFFFFFFF,
|
|
* though 0xFFFFFFF is expected in hexadecimal.
|
|
*/
|
|
n = hawk_fmt_uintmax_to_oocstr(
|
|
rtx->format.tmp.ptr,
|
|
fmt_width,
|
|
(hawk_uint_t)l,
|
|
fmt_flags,
|
|
wp[FMT_WP_PRECISION],
|
|
fmt_fill,
|
|
fmt_prefix
|
|
);
|
|
}
|
|
else
|
|
{
|
|
n = hawk_fmt_intmax_to_oocstr(
|
|
rtx->format.tmp.ptr,
|
|
fmt_width,
|
|
l,
|
|
fmt_flags,
|
|
wp[FMT_WP_PRECISION],
|
|
fmt_fill,
|
|
fmt_prefix
|
|
);
|
|
}
|
|
if (n <= -1)
|
|
{
|
|
/* -n is the number of characters required */
|
|
GROW_WITH_INC(&rtx->format.tmp, -n);
|
|
fmt_width = -n;
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
while (1);
|
|
|
|
OUT_STR(rtx->format.tmp.ptr, n);
|
|
}
|
|
else if (fmtc == HAWK_T('e') || fmtc == HAWK_T('E') ||
|
|
fmtc == HAWK_T('g') || fmtc == HAWK_T('G') ||
|
|
fmtc == HAWK_T('f'))
|
|
{
|
|
|
|
hawk_val_t* v;
|
|
hawk_flt_t r;
|
|
int n;
|
|
|
|
if (vxx) v = vxx;
|
|
else
|
|
{
|
|
v = get_arg_val_for_format(rtx, stack_arg_idx, nargs_on_stack, args, val);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
n = hawk_rtx_valtoflt(rtx, v, &r);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
if (n <= -1) return HAWK_NULL;
|
|
|
|
#if defined(HAWK_USE_FLTMAX)
|
|
/*FMT_CHAR(HAWK_T('j'));*/
|
|
FMT_STR(HAWK_T("jj"), 2); /* see fmt.c for info on jj */
|
|
FMT_CHAR(fmtc);
|
|
/*if (hawk_ooecs_fcat(out, HAWK_OOECS_PTR(fbu), r) == (hawk_oow_t)-1) return HAWK_NULL;*/
|
|
if (hawk_ooecs_fcat(out, HAWK_OOECS_PTR(fbu), &r) == (hawk_oow_t)-1) return HAWK_NULL;
|
|
#else
|
|
FMT_CHAR(HAWK_T('z'));
|
|
FMT_CHAR(fmtc);
|
|
if (hawk_ooecs_fcat(out, HAWK_OOECS_PTR(fbu), r) == (hawk_oow_t)-1) return HAWK_NULL;
|
|
#endif
|
|
}
|
|
else if (fmtc == HAWK_T('c'))
|
|
{
|
|
hawk_ooch_t ch;
|
|
hawk_oow_t ch_len;
|
|
hawk_val_t* v;
|
|
hawk_val_type_t vtype;
|
|
|
|
if (vxx) v = vxx;
|
|
else
|
|
{
|
|
v = get_arg_val_for_format(rtx, stack_arg_idx, nargs_on_stack, args, val);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
vtype = HAWK_RTX_GETVALTYPE(rtx, v);
|
|
switch (vtype)
|
|
{
|
|
case HAWK_VAL_NIL:
|
|
ch = HAWK_T('\0');
|
|
ch_len = 0;
|
|
break;
|
|
|
|
case HAWK_VAL_CHAR:
|
|
ch = (hawk_ooch_t)HAWK_RTX_GETCHARFROMVAL(rtx, v);
|
|
ch_len = 1;
|
|
break;
|
|
|
|
case HAWK_VAL_BCHR:
|
|
ch = (hawk_ooch_t)HAWK_RTX_GETBCHRFROMVAL(rtx, v);
|
|
ch_len = 1;
|
|
break;
|
|
|
|
case HAWK_VAL_INT:
|
|
ch = (hawk_ooch_t)HAWK_RTX_GETINTFROMVAL(rtx, v);
|
|
ch_len = 1;
|
|
break;
|
|
|
|
case HAWK_VAL_FLT:
|
|
ch = (hawk_ooch_t)((hawk_val_flt_t*)v)->val;
|
|
ch_len = 1;
|
|
break;
|
|
|
|
case HAWK_VAL_STR:
|
|
/* printf("%c", "") => produces '\0' character */
|
|
ch = (((hawk_val_str_t*)v)->val.len > 0)? ((hawk_val_str_t*)v)->val.ptr[0]: '\0';
|
|
ch_len = 1;
|
|
break;
|
|
|
|
case HAWK_VAL_MBS:
|
|
ch = (((hawk_val_mbs_t*)v)->val.len > 0)? ((hawk_val_mbs_t*)v)->val.ptr[0]: '\0';
|
|
ch_len = 1;
|
|
break;
|
|
|
|
case HAWK_VAL_BOB:
|
|
ch = (((hawk_val_bob_t*)v)->val.len > 0)? ((hawk_bch_t*)((hawk_val_bob_t*)v)->val.ptr)[0]: '\0';
|
|
ch_len = 1;
|
|
break;
|
|
|
|
default:
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EVALTOCHR);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
if (wp[FMT_WP_PRECISION] <= 0 || wp[FMT_WP_PRECISION] > (hawk_int_t)ch_len)
|
|
{
|
|
wp[FMT_WP_PRECISION] = (hawk_int_t)ch_len;
|
|
}
|
|
|
|
if (wp[FMT_WP_PRECISION] > wp[FMT_WP_WIDTH]) wp[FMT_WP_WIDTH] = wp[FMT_WP_PRECISION];
|
|
|
|
if (!(flags & FMT_FLAG_MINUS))
|
|
{
|
|
/* right align */
|
|
while (wp[FMT_WP_WIDTH] > wp[FMT_WP_PRECISION])
|
|
{
|
|
if (hawk_ooecs_ccat(out, HAWK_T(' ')) == (hawk_oow_t)-1)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
return HAWK_NULL;
|
|
}
|
|
wp[FMT_WP_WIDTH]--;
|
|
}
|
|
}
|
|
|
|
if (wp[FMT_WP_PRECISION] > 0)
|
|
{
|
|
if (hawk_ooecs_ccat(out, ch) == (hawk_oow_t)-1)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
return HAWK_NULL;
|
|
}
|
|
}
|
|
|
|
if (flags & FMT_FLAG_MINUS)
|
|
{
|
|
/* left align */
|
|
while (wp[FMT_WP_WIDTH] > wp[FMT_WP_PRECISION])
|
|
{
|
|
if (hawk_ooecs_ccat (out, HAWK_T(' ')) == (hawk_oow_t)-1)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
return HAWK_NULL;
|
|
}
|
|
wp[FMT_WP_WIDTH]--;
|
|
}
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
}
|
|
else if (fmtc == 's' || fmtc == 'k' || fmtc == 'K' || fmtc == 'w' || fmtc == 'W')
|
|
{
|
|
hawk_val_t* v;
|
|
int xx;
|
|
|
|
if (vxx) v = vxx; /* 'v' mutated to fmtc */
|
|
else
|
|
{
|
|
v = get_arg_val_for_format(rtx, stack_arg_idx, nargs_on_stack, args, val);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
xx = format_str(rtx, out, fbu, v, (val? FMT_V_FLT_TO_STR: 0), fmtc, wp, wp_idx, flags);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
if (xx <= -1) return HAWK_NULL;
|
|
}
|
|
else
|
|
{
|
|
HAWK_ASSERT(vxx == HAWK_NULL); /* vxx is set for 'v'. the 'v' handler never cause the flow to reach here */
|
|
if (fmtc != HAWK_T('%')) OUT_STR(HAWK_OOECS_PTR(fbu), HAWK_OOECS_LEN(fbu));
|
|
OUT_CHAR(fmtc);
|
|
goto skip_taking_arg;
|
|
}
|
|
|
|
next_arg:
|
|
if (!args || val) stack_arg_idx++;
|
|
else args = args->next;
|
|
skip_taking_arg:
|
|
hawk_ooecs_clear(fbu);
|
|
}
|
|
|
|
/* flush uncompleted formatting sequence */
|
|
OUT_STR(HAWK_OOECS_PTR(fbu), HAWK_OOECS_LEN(fbu));
|
|
|
|
*len = HAWK_OOECS_LEN(out);
|
|
return HAWK_OOECS_PTR(out);
|
|
}
|
|
|
|
/* ========================================================================= */
|
|
|
|
hawk_bch_t* hawk_rtx_formatmbs (
|
|
hawk_rtx_t* rtx, hawk_becs_t* out, hawk_becs_t* fbu,
|
|
const hawk_bch_t* fmt, hawk_oow_t fmt_len,
|
|
hawk_oow_t nargs_on_stack, hawk_nde_t* args, hawk_oow_t* len)
|
|
{
|
|
hawk_oow_t i;
|
|
hawk_oow_t stack_arg_idx = 1;
|
|
hawk_val_t* val;
|
|
|
|
#define OUT_BCHAR(c) do { if (hawk_becs_ccat(out, (c)) == (hawk_oow_t)-1) return HAWK_NULL; } while(0)
|
|
|
|
#define OUT_MBS(ptr,len) do { if (hawk_becs_ncat(out, (ptr), (len)) == (hawk_oow_t)-1) return HAWK_NULL; } while(0)
|
|
|
|
#define FMT_BCHAR(c) do { if (hawk_becs_ccat(fbu, (c)) == (hawk_oow_t)-1) return HAWK_NULL; } while(0)
|
|
|
|
#define FMT_MBS(ptr,len) do { if (hawk_becs_ncat(fbu, (ptr), (len)) == (hawk_oow_t)-1) return HAWK_NULL; } while(0)
|
|
|
|
#define GROW_MBSBUF(buf) do { \
|
|
if ((buf)->ptr) \
|
|
{ \
|
|
hawk_rtx_freemem(rtx, (buf)->ptr); \
|
|
(buf)->ptr = HAWK_NULL; \
|
|
} \
|
|
(buf)->len += (buf)->inc; \
|
|
(buf)->ptr = (hawk_bch_t*)hawk_rtx_allocmem(rtx, (buf)->len * HAWK_SIZEOF(hawk_bch_t)); \
|
|
if (HAWK_UNLIKELY(!(buf)->ptr)) \
|
|
{ \
|
|
(buf)->len = 0; \
|
|
return HAWK_NULL; \
|
|
} \
|
|
} while(0)
|
|
|
|
#define GROW_MBSBUF_WITH_INC(buf,incv) do { \
|
|
if ((buf)->ptr) \
|
|
{ \
|
|
hawk_rtx_freemem(rtx, (buf)->ptr); \
|
|
(buf)->ptr = HAWK_NULL; \
|
|
} \
|
|
(buf)->len += ((incv) > (buf)->inc)? (incv): (buf)->inc; \
|
|
(buf)->ptr = (hawk_bch_t*)hawk_rtx_allocmem(rtx, (buf)->len * HAWK_SIZEOF(hawk_bch_t)); \
|
|
if (HAWK_UNLIKELY(!(buf)->ptr)) \
|
|
{ \
|
|
(buf)->len = 0; \
|
|
return HAWK_NULL; \
|
|
} \
|
|
} while(0)
|
|
|
|
/* run->format.tmp.ptr should have been assigned a pointer to a block of memory before this function has been called */
|
|
HAWK_ASSERT(rtx->formatmbs.tmp.ptr != HAWK_NULL);
|
|
|
|
if (nargs_on_stack == (hawk_oow_t)-1)
|
|
{
|
|
/* dirty hack to support a single value argument instead of a tree node */
|
|
val = (hawk_val_t*)args;
|
|
nargs_on_stack = 2;
|
|
}
|
|
else
|
|
{
|
|
val = HAWK_NULL;
|
|
}
|
|
|
|
if (out == HAWK_NULL) out = &rtx->formatmbs.out;
|
|
if (fbu == HAWK_NULL) fbu = &rtx->formatmbs.fmt;
|
|
|
|
hawk_becs_clear(out);
|
|
hawk_becs_clear(fbu);
|
|
|
|
for (i = 0; i < fmt_len; i++)
|
|
{
|
|
hawk_val_t* vxx;
|
|
hawk_bch_t fmtc;
|
|
hawk_int_t wp[2];
|
|
int wp_idx;
|
|
int flags;
|
|
|
|
if (HAWK_BECS_LEN(fbu) == 0)
|
|
{
|
|
/* format specifier is empty */
|
|
if (fmt[i] == HAWK_BT('%'))
|
|
{
|
|
/* add % to format specifier (fbu) */
|
|
FMT_BCHAR(fmt[i]);
|
|
}
|
|
else
|
|
{
|
|
/* normal output */
|
|
OUT_BCHAR(fmt[i]);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* handle flags */
|
|
flags = 0;
|
|
while (i < fmt_len)
|
|
{
|
|
switch (fmt[i])
|
|
{
|
|
case HAWK_BT(' '):
|
|
flags |= FMT_FLAG_SPACE;
|
|
break;
|
|
case HAWK_BT('#'):
|
|
flags |= FMT_FLAG_HASH;
|
|
break;
|
|
case HAWK_BT('0'):
|
|
flags |= FMT_FLAG_ZERO;
|
|
break;
|
|
case HAWK_BT('+'):
|
|
flags |= FMT_FLAG_PLUS;
|
|
break;
|
|
case HAWK_BT('-'):
|
|
flags |= FMT_FLAG_MINUS;
|
|
break;
|
|
default:
|
|
goto wp_mod_init;
|
|
}
|
|
|
|
FMT_BCHAR(fmt[i]); i++;
|
|
}
|
|
|
|
wp_mod_init:
|
|
wp[FMT_WP_WIDTH] = 0; /* width */
|
|
wp[FMT_WP_PRECISION] = -1; /* precision */
|
|
wp_idx = FMT_WP_WIDTH; /* width first */
|
|
|
|
wp_mod_main:
|
|
if (i < fmt_len && fmt[i] == HAWK_BT('*'))
|
|
{
|
|
/* variable width/precision modifier.
|
|
* take the width/precision from a parameter and
|
|
* transform it to a fixed length format */
|
|
hawk_val_t* v;
|
|
int n;
|
|
|
|
v = get_arg_val_for_format(rtx, stack_arg_idx, nargs_on_stack, args, val);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
n = hawk_rtx_valtoint_inline(rtx, v, &wp[wp_idx]);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
if (HAWK_UNLIKELY(n <= -1)) return HAWK_NULL;
|
|
|
|
do
|
|
{
|
|
n = hawk_fmt_intmax_to_bcstr(
|
|
rtx->formatmbs.tmp.ptr,
|
|
rtx->formatmbs.tmp.len,
|
|
wp[wp_idx],
|
|
10 | HAWK_FMT_INTMAX_NOTRUNC | HAWK_FMT_INTMAX_NONULL,
|
|
-1,
|
|
HAWK_BT('\0'),
|
|
HAWK_NULL
|
|
);
|
|
if (n <= -1)
|
|
{
|
|
/* -n is the number of characters required
|
|
* including terminating null */
|
|
GROW_MBSBUF_WITH_INC(&rtx->formatmbs.tmp, -n);
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
while (1);
|
|
|
|
FMT_MBS(rtx->formatmbs.tmp.ptr, n);
|
|
|
|
if (!args || val) stack_arg_idx++;
|
|
else args = args->next;
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
/* fixed width/precision modifier */
|
|
if (i < fmt_len && hawk_is_bch_digit(fmt[i]))
|
|
{
|
|
wp[wp_idx] = 0;
|
|
do
|
|
{
|
|
wp[wp_idx] = wp[wp_idx] * 10 + fmt[i] - HAWK_BT('0');
|
|
FMT_BCHAR(fmt[i]); i++;
|
|
}
|
|
while (i < fmt_len && hawk_is_bch_digit(fmt[i]));
|
|
}
|
|
}
|
|
|
|
if (wp_idx == FMT_WP_WIDTH && i < fmt_len && fmt[i] == HAWK_BT('.'))
|
|
{
|
|
FMT_BCHAR(fmt[i]); i++;
|
|
|
|
wp[FMT_WP_PRECISION] = 0;
|
|
wp_idx = FMT_WP_PRECISION; /* change index to precision */
|
|
goto wp_mod_main;
|
|
}
|
|
|
|
if (i >= fmt_len) break;
|
|
|
|
if (wp[FMT_WP_WIDTH] < 0)
|
|
{
|
|
wp[FMT_WP_WIDTH] = -wp[FMT_WP_WIDTH];
|
|
flags |= FMT_FLAG_MINUS;
|
|
}
|
|
|
|
fmtc = fmt[i]; /* 'i' don't get increment after this */
|
|
vxx = HAWK_NULL;
|
|
|
|
if (fmtc == 'v')
|
|
{
|
|
hawk_val_type_t vtype;
|
|
hawk_bcs_t fixed_str;
|
|
int xx;
|
|
|
|
vxx = get_arg_val_for_format(rtx, stack_arg_idx, nargs_on_stack, args, val);
|
|
if (HAWK_UNLIKELY(!vxx)) return HAWK_NULL;
|
|
|
|
/* depending on the value, mutate the formatting specifier */
|
|
vtype = HAWK_RTX_GETVALTYPE(rtx, vxx);
|
|
switch (vtype)
|
|
{
|
|
case HAWK_VAL_NIL:
|
|
hawk_rtx_refupval_inline(rtx, vxx);
|
|
fixed_str.ptr = HAWK_BT("<@nil>");
|
|
fixed_str.len = 6;
|
|
xx = format_mbs(rtx, out, fbu, (hawk_val_t*)&fixed_str, FMT_V_CS_TO_STR, fmtc, wp, wp_idx, flags);
|
|
hawk_rtx_refdownval_inline(rtx, vxx);
|
|
if (xx <= -1) return HAWK_NULL;
|
|
goto next_arg;
|
|
|
|
case HAWK_VAL_CHAR:
|
|
fmtc = 'c';
|
|
break;
|
|
case HAWK_VAL_BCHR:
|
|
fmtc = 'k';
|
|
break;
|
|
case HAWK_VAL_INT:
|
|
fmtc = 'd';
|
|
break;
|
|
case HAWK_VAL_FLT:
|
|
fmtc = 'f';
|
|
break;
|
|
case HAWK_VAL_STR:
|
|
fmtc = 's';
|
|
break;
|
|
case HAWK_VAL_MBS:
|
|
fmtc = 'k';
|
|
break;
|
|
case HAWK_VAL_BOB:
|
|
fmtc = 'k';
|
|
break;
|
|
|
|
case HAWK_VAL_ARR: /* TOOD: */
|
|
hawk_rtx_refupval_inline(rtx, vxx);
|
|
fixed_str.ptr = HAWK_BT("<ARRAY>");
|
|
fixed_str.len = 7;
|
|
xx = format_mbs(rtx, out, fbu, (hawk_val_t*)&fixed_str, FMT_V_CS_TO_STR, fmtc, wp, wp_idx, flags);
|
|
hawk_rtx_refdownval_inline(rtx, vxx);
|
|
if (xx <= -1) return HAWK_NULL;
|
|
goto next_arg;
|
|
|
|
case HAWK_VAL_MAP: /* TOOD: */
|
|
hawk_rtx_refupval_inline(rtx, vxx);
|
|
fixed_str.ptr = HAWK_BT("<MAP>");
|
|
fixed_str.len = 5;
|
|
xx = format_mbs(rtx, out, fbu, (hawk_val_t*)&fixed_str, FMT_V_CS_TO_STR, fmtc, wp, wp_idx, flags);
|
|
hawk_rtx_refdownval_inline(rtx, vxx);
|
|
if (xx <= -1) return HAWK_NULL;
|
|
goto next_arg;
|
|
|
|
case HAWK_VAL_FUN:
|
|
hawk_rtx_refupval_inline(rtx, vxx);
|
|
#if defined(HAWK_OOCH_IS_BCH)
|
|
xx = format_mbs(rtx, out, fbu, (hawk_val_t*)&((hawk_val_fun_t*)vxx)->fun->name, FMT_V_FUN_TO_STR, fmtc, wp, wp_idx, flags);
|
|
#else
|
|
/* the function name is always in oocs.
|
|
* I can't pass the name to format_mbs without conversion,
|
|
* nor can I use FMT_V_FUN_TO_STR */
|
|
fixed_str.ptr = hawk_rtx_dupu3tobchars(rtx, HAWK_UT("<FUN:"), 5, ((hawk_val_fun_t*)vxx)->fun->name.ptr, ((hawk_val_fun_t*)vxx)->fun->name.len, HAWK_UT(">"), 1, &fixed_str.len);
|
|
if (HAWK_UNLIKELY(!fixed_str.ptr))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, vxx);
|
|
return HAWK_NULL;
|
|
}
|
|
xx = format_mbs(rtx, out, fbu, (hawk_val_t*)&fixed_str, FMT_V_CS_TO_STR, fmtc, wp, wp_idx, flags);
|
|
hawk_rtx_freemem(rtx, fixed_str.ptr);
|
|
#endif
|
|
hawk_rtx_refdownval_inline(rtx, vxx);
|
|
if (xx <= -1) return HAWK_NULL;
|
|
goto next_arg;
|
|
|
|
default:
|
|
/* do nothing for now */
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fmtc == HAWK_BT('d') || fmtc == HAWK_BT('i') ||
|
|
fmtc == HAWK_BT('x') || fmtc == HAWK_BT('X') ||
|
|
fmtc == HAWK_BT('b') || fmtc == HAWK_BT('B') ||
|
|
fmtc == HAWK_BT('o'))
|
|
{
|
|
hawk_val_t* v;
|
|
hawk_int_t l;
|
|
int n;
|
|
|
|
int fmt_flags;
|
|
int fmt_uint = 0;
|
|
int fmt_width;
|
|
hawk_bch_t fmt_fill = HAWK_BT('\0');
|
|
const hawk_bch_t* fmt_prefix = HAWK_NULL;
|
|
|
|
if (vxx) v = vxx;
|
|
else
|
|
{
|
|
v = get_arg_val_for_format(rtx, stack_arg_idx, nargs_on_stack, args, val);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
n = hawk_rtx_valtoint_inline(rtx, v, &l);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
if (HAWK_UNLIKELY(n <= -1)) return HAWK_NULL;
|
|
|
|
fmt_flags = HAWK_FMT_INTMAX_NOTRUNC | HAWK_FMT_INTMAX_NONULL;
|
|
|
|
if (l == 0 && wp_idx == FMT_WP_PRECISION && wp[FMT_WP_PRECISION] == 0)
|
|
{
|
|
/* printf ("%.d", 0); printf ("%.0d", 0); printf ("%.*d", 0, 0); */
|
|
/* A zero value with a precision of zero produces no character. */
|
|
fmt_flags |= HAWK_FMT_INTMAX_NOZERO;
|
|
}
|
|
|
|
if (wp[FMT_WP_WIDTH] > 0)
|
|
{
|
|
/* justification for width greater than 0 */
|
|
if (flags & FMT_FLAG_ZERO)
|
|
{
|
|
if (flags & FMT_FLAG_MINUS)
|
|
{
|
|
/* FMT_FLAG_MINUS wins if both FMT_FLAG_ZERO
|
|
* and FMT_FLAG_MINUS are specified. */
|
|
fmt_fill = HAWK_BT(' ');
|
|
if (flags & FMT_FLAG_MINUS)
|
|
{
|
|
/* left justification. need to fill the right side */
|
|
fmt_flags |= HAWK_FMT_INTMAX_FILLRIGHT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (wp_idx != FMT_WP_PRECISION) /* if precision is not set, wp_idx is at FMT_WP_WIDTH */
|
|
{
|
|
/* precision not specified.
|
|
* FMT_FLAG_ZERO can take effect */
|
|
fmt_fill = HAWK_BT('0');
|
|
fmt_flags |= HAWK_FMT_INTMAX_FILLCENTER;
|
|
}
|
|
else
|
|
{
|
|
fmt_fill = HAWK_BT(' ');
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fmt_fill = HAWK_BT(' ');
|
|
if (flags & FMT_FLAG_MINUS)
|
|
{
|
|
/* left justification. need to fill the right side */
|
|
fmt_flags |= HAWK_FMT_INTMAX_FILLRIGHT;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (fmtc)
|
|
{
|
|
case HAWK_BT('B'):
|
|
case HAWK_BT('b'):
|
|
fmt_flags |= 2;
|
|
fmt_uint = 1;
|
|
if (l && (flags & FMT_FLAG_HASH))
|
|
{
|
|
/* A nonzero value is prefixed with 0b */
|
|
fmt_prefix = HAWK_BT("0b");
|
|
}
|
|
break;
|
|
|
|
case HAWK_BT('X'):
|
|
fmt_flags |= HAWK_FMT_INTMAX_UPPERCASE;
|
|
case HAWK_BT('x'):
|
|
fmt_flags |= 16;
|
|
fmt_uint = 1;
|
|
if (l && (flags & FMT_FLAG_HASH))
|
|
{
|
|
/* A nonzero value is prefixed with 0x */
|
|
fmt_prefix = HAWK_BT("0x");
|
|
}
|
|
break;
|
|
|
|
case HAWK_BT('o'):
|
|
fmt_flags |= 8;
|
|
fmt_uint = 1;
|
|
if (flags & FMT_FLAG_HASH)
|
|
{
|
|
/* Force a leading zero digit including zero.
|
|
* 0 with FMT_FLAG_HASH and precision 0 still emits '0'.
|
|
* On the contrary, 'X' and 'x' emit no digits
|
|
* for 0 with FMT_FLAG_HASH and precision 0. */
|
|
fmt_flags |= HAWK_FMT_INTMAX_ZEROLEAD;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fmt_flags |= 10;
|
|
if (flags & FMT_FLAG_PLUS)
|
|
fmt_flags |= HAWK_FMT_INTMAX_PLUSSIGN;
|
|
if (flags & FMT_FLAG_SPACE)
|
|
fmt_flags |= HAWK_FMT_INTMAX_EMPTYSIGN;
|
|
break;
|
|
}
|
|
|
|
|
|
if (wp[FMT_WP_WIDTH] > 0)
|
|
{
|
|
if (wp[FMT_WP_WIDTH] > rtx->formatmbs.tmp.len)
|
|
GROW_MBSBUF_WITH_INC(&rtx->formatmbs.tmp, wp[FMT_WP_WIDTH] - rtx->formatmbs.tmp.len);
|
|
fmt_width = wp[FMT_WP_WIDTH];
|
|
}
|
|
else fmt_width = rtx->formatmbs.tmp.len;
|
|
|
|
do
|
|
{
|
|
if (fmt_uint)
|
|
{
|
|
/* Explicit type-casting for 'l' from hawk_int_t
|
|
* to hawk_uint_t is needed before passing it to
|
|
* hawk_fmt_uintmax_to_bcstr().
|
|
*
|
|
* Consider a value of -1 for example.
|
|
* -1 is a value with all bits set.
|
|
* If hawk_int_t is 4 bytes and hawk_uintmax_t
|
|
* is 8 bytes, the value is shown below for
|
|
* each type respectively .
|
|
* -1 - 0xFFFFFFFF (hawk_int_t)
|
|
* -1 - 0xFFFFFFFFFFFFFFFF (hawk_uintmax_t)
|
|
* Implicit typecasting of -1 from hawk_int_t to
|
|
* to hawk_uintmax_t results in 0xFFFFFFFFFFFFFFFF,
|
|
* though 0xFFFFFFF is expected in hexadecimal.
|
|
*/
|
|
n = hawk_fmt_uintmax_to_bcstr(
|
|
rtx->formatmbs.tmp.ptr,
|
|
fmt_width,
|
|
(hawk_uint_t)l,
|
|
fmt_flags,
|
|
wp[FMT_WP_PRECISION],
|
|
fmt_fill,
|
|
fmt_prefix
|
|
);
|
|
}
|
|
else
|
|
{
|
|
n = hawk_fmt_intmax_to_bcstr(
|
|
rtx->formatmbs.tmp.ptr,
|
|
fmt_width,
|
|
l,
|
|
fmt_flags,
|
|
wp[FMT_WP_PRECISION],
|
|
fmt_fill,
|
|
fmt_prefix
|
|
);
|
|
}
|
|
if (n <= -1)
|
|
{
|
|
/* -n is the number of characters required */
|
|
GROW_MBSBUF_WITH_INC(&rtx->formatmbs.tmp, -n);
|
|
fmt_width = -n;
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
while (1);
|
|
|
|
OUT_MBS(rtx->formatmbs.tmp.ptr, n);
|
|
}
|
|
else if (fmtc == HAWK_BT('e') || fmtc == HAWK_BT('E') ||
|
|
fmtc == HAWK_BT('g') || fmtc == HAWK_BT('G') ||
|
|
fmtc == HAWK_BT('f'))
|
|
{
|
|
hawk_val_t* v;
|
|
hawk_flt_t r;
|
|
int n;
|
|
|
|
if (vxx) v = vxx;
|
|
else
|
|
{
|
|
v = get_arg_val_for_format(rtx, stack_arg_idx, nargs_on_stack, args, val);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
n = hawk_rtx_valtoflt(rtx, v, &r);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
if (n <= -1) return HAWK_NULL;
|
|
|
|
#if defined(HAWK_USE_FLTMAX)
|
|
/*FMT_BCHAR(HAWK_BT('j'));*/
|
|
FMT_MBS(HAWK_BT("jj"), 2); /* see fmt.c for info on jj */
|
|
FMT_BCHAR(fmtc);
|
|
/*if (hawk_becs_fcat(out, HAWK_BECS_PTR(fbu), r) == (hawk_oow_t)-1) return HAWK_NULL;*/
|
|
if (hawk_becs_fcat(out, HAWK_BECS_PTR(fbu), &r) == (hawk_oow_t)-1) return HAWK_NULL;
|
|
#else
|
|
FMT_BCHAR(HAWK_BT('z'));
|
|
FMT_BCHAR(fmtc);
|
|
if (hawk_becs_fcat(out, HAWK_BECS_PTR(fbu), r) == (hawk_oow_t)-1) return HAWK_NULL;
|
|
#endif
|
|
}
|
|
else if (fmtc == HAWK_BT('c'))
|
|
{
|
|
hawk_bch_t ch;
|
|
hawk_oow_t ch_len;
|
|
hawk_val_t* v;
|
|
hawk_val_type_t vtype;
|
|
|
|
if (vxx) v = vxx;
|
|
else
|
|
{
|
|
v = get_arg_val_for_format(rtx, stack_arg_idx, nargs_on_stack, args, val);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
vtype = HAWK_RTX_GETVALTYPE(rtx, v);
|
|
switch (vtype)
|
|
{
|
|
case HAWK_VAL_NIL:
|
|
ch = HAWK_BT('\0');
|
|
ch_len = 0;
|
|
break;
|
|
|
|
case HAWK_VAL_CHAR:
|
|
ch = (hawk_bch_t)HAWK_RTX_GETCHARFROMVAL(rtx, v); /* the value may get truncated */
|
|
ch_len = 1;
|
|
break;
|
|
|
|
case HAWK_VAL_BCHR:
|
|
ch = HAWK_RTX_GETBCHRFROMVAL(rtx, v);
|
|
ch_len = 1;
|
|
break;
|
|
|
|
case HAWK_VAL_INT:
|
|
ch = (hawk_bch_t)HAWK_RTX_GETINTFROMVAL(rtx, v);
|
|
ch_len = 1;
|
|
break;
|
|
|
|
case HAWK_VAL_FLT:
|
|
ch = (hawk_bch_t)((hawk_val_flt_t*)v)->val;
|
|
ch_len = 1;
|
|
break;
|
|
|
|
case HAWK_VAL_STR:
|
|
ch_len = ((hawk_val_str_t*)v)->val.len;
|
|
if (ch_len > 0)
|
|
{
|
|
/* value truncation is expected */
|
|
ch = ((hawk_val_str_t*)v)->val.ptr[0];
|
|
ch_len = 1;
|
|
}
|
|
else ch = HAWK_BT('\0');
|
|
break;
|
|
|
|
case HAWK_VAL_MBS:
|
|
ch_len = ((hawk_val_mbs_t*)v)->val.len;
|
|
if (ch_len > 0)
|
|
{
|
|
ch = ((hawk_val_mbs_t*)v)->val.ptr[0];
|
|
ch_len = 1;
|
|
}
|
|
else ch = HAWK_BT('\0');
|
|
break;
|
|
|
|
case HAWK_VAL_BOB:
|
|
ch_len = ((hawk_val_bob_t*)v)->val.len;
|
|
if (ch_len > 0)
|
|
{
|
|
ch = ((hawk_bch_t*)((hawk_val_bob_t*)v)->val.ptr)[0];
|
|
ch_len = 1;
|
|
}
|
|
else ch = 0;
|
|
break;
|
|
|
|
default:
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EVALTOCHR);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
if (wp[FMT_WP_PRECISION] <= 0 || wp[FMT_WP_PRECISION] > (hawk_int_t)ch_len)
|
|
{
|
|
wp[FMT_WP_PRECISION] = (hawk_int_t)ch_len;
|
|
}
|
|
|
|
if (wp[FMT_WP_PRECISION] > wp[FMT_WP_WIDTH]) wp[FMT_WP_WIDTH] = wp[FMT_WP_PRECISION];
|
|
|
|
if (!(flags & FMT_FLAG_MINUS))
|
|
{
|
|
/* right align */
|
|
while (wp[FMT_WP_WIDTH] > wp[FMT_WP_PRECISION])
|
|
{
|
|
if (hawk_becs_ccat(out, HAWK_BT(' ')) == (hawk_oow_t)-1)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
return HAWK_NULL;
|
|
}
|
|
wp[FMT_WP_WIDTH]--;
|
|
}
|
|
}
|
|
|
|
if (wp[FMT_WP_PRECISION] > 0)
|
|
{
|
|
if (hawk_becs_ccat(out, ch) == (hawk_oow_t)-1)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
return HAWK_NULL;
|
|
}
|
|
}
|
|
|
|
if (flags & FMT_FLAG_MINUS)
|
|
{
|
|
/* left align */
|
|
while (wp[FMT_WP_WIDTH] > wp[FMT_WP_PRECISION])
|
|
{
|
|
if (hawk_becs_ccat(out, HAWK_BT(' ')) == (hawk_oow_t)-1)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
return HAWK_NULL;
|
|
}
|
|
wp[FMT_WP_WIDTH]--;
|
|
}
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
}
|
|
else if (fmtc == 's' || fmtc == 'k' || fmtc == 'K' || fmtc == 'w' || fmtc == 'W')
|
|
{
|
|
hawk_val_t* v;
|
|
int xx;
|
|
|
|
if (vxx) v = vxx;
|
|
else
|
|
{
|
|
v = get_arg_val_for_format(rtx, stack_arg_idx, nargs_on_stack, args, val);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
xx = format_mbs(rtx, out, fbu, v, (val? FMT_V_FLT_TO_STR: 0), fmtc, wp, wp_idx, flags);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
if (xx <= -1) return HAWK_NULL;
|
|
}
|
|
else
|
|
{
|
|
HAWK_ASSERT(vxx != HAWK_NULL);
|
|
if (fmtc != HAWK_BT('%')) OUT_MBS(HAWK_BECS_PTR(fbu), HAWK_BECS_LEN(fbu));
|
|
OUT_BCHAR(fmtc);
|
|
goto skip_taking_arg;
|
|
}
|
|
|
|
next_arg:
|
|
if (!args || val) stack_arg_idx++;
|
|
else args = args->next;
|
|
skip_taking_arg:
|
|
hawk_becs_clear(fbu);
|
|
}
|
|
|
|
/* flush uncompleted formatting sequence */
|
|
OUT_MBS(HAWK_BECS_PTR(fbu), HAWK_BECS_LEN(fbu));
|
|
|
|
*len = HAWK_BECS_LEN(out);
|
|
return HAWK_BECS_PTR(out);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
void hawk_rtx_setnrflt (hawk_rtx_t* rtx, const hawk_nrflt_t* nrflt)
|
|
{
|
|
rtx->nrflt = *nrflt;
|
|
}
|
|
|
|
void hawk_rtx_getnrflt (hawk_rtx_t* rtx, hawk_nrflt_t* nrflt)
|
|
{
|
|
*nrflt = rtx->nrflt;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
hawk_oow_t hawk_rtx_fmttoucstr_ (hawk_rtx_t* rtx, hawk_uch_t* buf, hawk_oow_t bufsz, const hawk_uch_t* fmt, ...)
|
|
{
|
|
va_list ap;
|
|
hawk_oow_t n;
|
|
va_start(ap, fmt);
|
|
n = hawk_gem_vfmttoucstr(hawk_rtx_getgem(rtx), buf, bufsz, fmt, ap);
|
|
va_end(ap);
|
|
return n;
|
|
}
|
|
|
|
hawk_oow_t hawk_rtx_fmttobcstr_ (hawk_rtx_t* rtx, hawk_bch_t* buf, hawk_oow_t bufsz, const hawk_bch_t* fmt, ...)
|
|
{
|
|
va_list ap;
|
|
hawk_oow_t n;
|
|
va_start(ap, fmt);
|
|
n = hawk_gem_vfmttobcstr(hawk_rtx_getgem(rtx), buf, bufsz, fmt, ap);
|
|
va_end(ap);
|
|
return n;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
int hawk_rtx_buildrex (hawk_rtx_t* rtx, const hawk_ooch_t* ptn, hawk_oow_t len, hawk_tre_t** code, hawk_tre_t** icode)
|
|
{
|
|
return hawk_gem_buildrex(hawk_rtx_getgem(rtx), ptn, len, !(rtx->hawk->opt.trait & HAWK_REXBOUND), code, icode);
|
|
}
|
|
|
|
/* ========================================================================
|
|
* Iterative (explicit-stack) expression evaluator and evalcall variant.
|
|
*
|
|
* eval_expression0_xstack — drop-in for eval_expression0
|
|
* eval_expression_xstack — drop-in for eval_expression (adds rex matching)
|
|
* hawk_rtx_evalcall_xstack — drop-in for hawk_rtx_evalcall
|
|
*
|
|
* The eframe_stack held in hawk_rtx_t replaces C-stack recursion for the
|
|
* binary / unary / conditional / assignment expression nodes. Function calls
|
|
* still make one C call per depth level (through hawk_rtx_evalcall_xstack),
|
|
* but expressions passed as arguments and the body of the function are all
|
|
* evaluated without additional C recursion.
|
|
* ======================================================================== */
|
|
|
|
static int push_eframe (hawk_rtx_t* rtx, hawk_ef_state_t state, hawk_nde_t* nde)
|
|
{
|
|
if (rtx->eframe_stack_size >= rtx->eframe_stack_limit)
|
|
{
|
|
hawk_eframe_t* tmp;
|
|
hawk_oow_t new_limit = rtx->eframe_stack_limit + 64;
|
|
|
|
tmp = (hawk_eframe_t*)hawk_rtx_reallocmem(rtx, rtx->eframe_stack, HAWK_SIZEOF(*tmp) * new_limit);
|
|
if (HAWK_UNLIKELY(!tmp)) return -1;
|
|
rtx->eframe_stack = tmp;
|
|
rtx->eframe_stack_limit = new_limit;
|
|
}
|
|
rtx->eframe_stack[rtx->eframe_stack_size].state = state;
|
|
rtx->eframe_stack[rtx->eframe_stack_size].nde = nde;
|
|
rtx->eframe_stack[rtx->eframe_stack_size].val = HAWK_NULL;
|
|
rtx->eframe_stack[rtx->eframe_stack_size].aux = 0;
|
|
rtx->eframe_stack_size++;
|
|
return 0;
|
|
}
|
|
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
static HAWK_INLINE_ALWAYS int push_eframe_with_aux (hawk_rtx_t* rtx, hawk_ef_state_t state, hawk_nde_t* nde, hawk_oow_t aux)
|
|
{
|
|
int n = push_eframe(rtx, state, nde);
|
|
if (HAWK_LIKELY(n == 0)) rtx->eframe_stack[rtx->eframe_stack_size - 1].aux = aux;
|
|
return n;
|
|
}
|
|
#endif
|
|
|
|
/* Pop all eframes down to (not including) save_base, refdowning any held
|
|
* intermediate values. Used for error unwinding. */
|
|
static HAWK_INLINE_ALWAYS void unwind_eframe_stack (hawk_rtx_t* rtx, hawk_oow_t save_base)
|
|
{
|
|
while (rtx->eframe_stack_size > save_base)
|
|
{
|
|
hawk_eframe_t* f = &rtx->eframe_stack[--rtx->eframe_stack_size];
|
|
if (f->val) hawk_rtx_refdownval_inline(rtx, f->val);
|
|
}
|
|
}
|
|
|
|
#if defined(HAWK_ENABLE_XSTACK_EVAL)
|
|
/* Unwind eframes to save_base, handling FNCALL_FUN_AWAIT frames by restoring
|
|
* the AWK value stack for each matched call_stack entry. */
|
|
static void unwind_eframe_stack_with_calls (hawk_rtx_t* rtx, hawk_oow_t save_base)
|
|
{
|
|
while (rtx->eframe_stack_size > save_base)
|
|
{
|
|
hawk_eframe_t* f = &rtx->eframe_stack[rtx->eframe_stack_size - 1];
|
|
if (f->state == HAWK_EF_FNCALL_FUN_AWAIT && rtx->call_stack_size > 0)
|
|
{
|
|
hawk_call_frame_t* cf = &rtx->call_stack[rtx->call_stack_size - 1];
|
|
hawk_oow_t i;
|
|
|
|
/* unwind the function body's exec frames first */
|
|
unwind_exec_stack(rtx, f->aux);
|
|
|
|
/* refdown all arguments in the AWK value stack */
|
|
for (i = 0; i < cf->nargs; i++)
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_ARG(rtx, i));
|
|
|
|
/* drop the return value slot */
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_RETVAL(rtx));
|
|
HAWK_RTX_STACK_RETVAL(rtx) = hawk_val_nil;
|
|
|
|
/* restore AWK value stack to the pre-call state */
|
|
rtx->stack_top = cf->saved_stack_top;
|
|
rtx->stack_base = cf->saved_stack_base;
|
|
rtx->call_stack_size--;
|
|
}
|
|
else if (f->val)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, f->val);
|
|
}
|
|
rtx->eframe_stack_size--;
|
|
}
|
|
}
|
|
|
|
/* ============================================================
|
|
* process_eframes — run the eframe dispatch loop.
|
|
*
|
|
* Returns:
|
|
* 1 all eframes consumed; *cur_inout holds the result value
|
|
* 0 yield: top eframe is FNCALL_FUN_AWAIT with body still on exec_stack
|
|
* -1 error: eframes unwound to eframe_base; error set in rtx
|
|
*
|
|
* On yield the caller (run_driver) must process exec frames until
|
|
* exec_stack_size drops to the await frame's aux, then call again.
|
|
* ============================================================ */
|
|
|
|
static int process_eframes (hawk_rtx_t* rtx, hawk_oow_t eframe_base, hawk_val_t** cur_inout)
|
|
{
|
|
hawk_val_t* cur = *cur_inout;
|
|
|
|
while (rtx->eframe_stack_size > eframe_base)
|
|
{
|
|
hawk_eframe_t* f = &rtx->eframe_stack[rtx->eframe_stack_size - 1];
|
|
|
|
/* yield if the top frame is a function body awaiting exec completion */
|
|
if (f->state == HAWK_EF_FNCALL_FUN_AWAIT && rtx->exec_stack_size > f->aux)
|
|
{
|
|
*cur_inout = cur;
|
|
return 0;
|
|
}
|
|
|
|
switch (f->state)
|
|
{
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_EVAL:
|
|
{
|
|
hawk_nde_t* nde = f->nde;
|
|
rtx->eframe_stack_size--; /* pop EVAL frame */
|
|
|
|
switch (nde->type)
|
|
{
|
|
/* ---- binary expression ---- */
|
|
case HAWK_NDE_EXP_BIN:
|
|
{
|
|
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)nde;
|
|
switch (exp->opcode)
|
|
{
|
|
case HAWK_BINOP_LAND:
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_LAND_LEFT, nde) <= -1)) goto oops;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, exp->left) <= -1)) goto oops;
|
|
break;
|
|
case HAWK_BINOP_LOR:
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_LOR_LEFT, nde) <= -1)) goto oops;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, exp->left) <= -1)) goto oops;
|
|
break;
|
|
case HAWK_BINOP_IN:
|
|
cur = eval_binop_in(rtx, exp->left, exp->right);
|
|
if (HAWK_UNLIKELY(!cur)) goto oops;
|
|
break;
|
|
case HAWK_BINOP_NM:
|
|
cur = eval_binop_nm(rtx, exp->left, exp->right);
|
|
if (HAWK_UNLIKELY(!cur)) goto oops;
|
|
break;
|
|
case HAWK_BINOP_MA:
|
|
cur = eval_binop_ma(rtx, exp->left, exp->right);
|
|
if (HAWK_UNLIKELY(!cur)) goto oops;
|
|
break;
|
|
default:
|
|
{
|
|
hawk_val_t* lv = xstack_fast_operand(rtx, exp->left);
|
|
if (HAWK_UNLIKELY(!lv)) { ADJERR_LOC(rtx, &nde->loc); goto oops; }
|
|
else if (lv != XSTACK_NOT_A_LEAF)
|
|
{
|
|
hawk_val_t* rv = xstack_fast_operand(rtx, exp->right);
|
|
if (rv != XSTACK_NOT_A_LEAF)
|
|
{
|
|
binop_func_t fn = get_binop_func(exp->opcode);
|
|
HAWK_ASSERT(fn != HAWK_NULL);
|
|
if (HAWK_UNLIKELY(!rv)) { hawk_rtx_refdownval_inline(rtx, lv); ADJERR_LOC(rtx, &nde->loc); goto oops; }
|
|
cur = fn(rtx, lv, rv);
|
|
if (HAWK_UNLIKELY(!cur)) ADJERR_LOC(rtx, &nde->loc);
|
|
hawk_rtx_refdownval_inline(rtx, lv);
|
|
hawk_rtx_refdownval_inline(rtx, rv);
|
|
if (HAWK_UNLIKELY(!cur)) goto oops;
|
|
break;
|
|
}
|
|
hawk_rtx_refdownval_inline(rtx, lv);
|
|
}
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_BIN_LEFT, nde) <= -1)) goto oops;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, exp->left) <= -1)) goto oops;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* ---- unary expression ---- */
|
|
case HAWK_NDE_EXP_UNR:
|
|
{
|
|
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)nde;
|
|
hawk_val_t* ov = xstack_fast_operand(rtx, exp->left);
|
|
if (HAWK_UNLIKELY(!ov)) { ADJERR_LOC(rtx, &nde->loc); goto oops; }
|
|
else if (ov != XSTACK_NOT_A_LEAF)
|
|
{
|
|
hawk_val_t* res = HAWK_NULL;
|
|
int un; hawk_int_t ul; hawk_flt_t ur;
|
|
switch (exp->opcode)
|
|
{
|
|
case HAWK_UNROP_MINUS:
|
|
un = hawk_rtx_valtonum(rtx, ov, &ul, &ur);
|
|
if (HAWK_LIKELY(un >= 0))
|
|
res = (un == 0)? hawk_rtx_makeintval_inline(rtx, -ul): hawk_rtx_makefltval(rtx, -ur);
|
|
break;
|
|
|
|
case HAWK_UNROP_LNOT:
|
|
res = hawk_rtx_makeboolval(rtx, !hawk_rtx_valtobool(rtx, ov));
|
|
break;
|
|
|
|
case HAWK_UNROP_BNOT:
|
|
un = hawk_rtx_valtoint_inline(rtx, ov, &ul);
|
|
if (HAWK_LIKELY(un >= 0)) res = hawk_rtx_makeintval_inline(rtx, ~ul);
|
|
break;
|
|
|
|
case HAWK_UNROP_PLUS:
|
|
un = hawk_rtx_valtonum(rtx, ov, &ul, &ur);
|
|
if (HAWK_LIKELY(un >= 0))
|
|
res = (un == 0)? hawk_rtx_makeintval_inline(rtx, ul): hawk_rtx_makefltval(rtx, ur);
|
|
break;
|
|
}
|
|
hawk_rtx_refdownval_inline(rtx, ov);
|
|
if (HAWK_UNLIKELY(!res)) { ADJERR_LOC(rtx, &nde->loc); goto oops; }
|
|
cur = res;
|
|
break;
|
|
}
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_UNARY, nde) <= -1)) goto oops;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, exp->left) <= -1)) goto oops;
|
|
break;
|
|
}
|
|
|
|
/* ---- conditional (ternary) ---- */
|
|
case HAWK_NDE_CND:
|
|
{
|
|
hawk_nde_cnd_t* cnd = (hawk_nde_cnd_t*)nde;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_CND, nde) <= -1)) goto oops;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, cnd->test) <= -1)) goto oops;
|
|
break;
|
|
}
|
|
|
|
/* ---- assignment ---- */
|
|
case HAWK_NDE_ASS:
|
|
{
|
|
hawk_nde_ass_t* ass = (hawk_nde_ass_t*)nde;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_ASS_RHS, nde) <= -1)) goto oops;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, ass->right) <= -1)) goto oops;
|
|
break;
|
|
}
|
|
|
|
/* ---- leaf nodes ---- */
|
|
case HAWK_NDE_CHAR:
|
|
cur = hawk_rtx_makecharval(rtx, ((hawk_nde_char_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!cur)) { ADJERR_LOC(rtx, &nde->loc); goto oops; }
|
|
break;
|
|
case HAWK_NDE_BCHR:
|
|
cur = hawk_rtx_makebchrval(rtx, ((hawk_nde_bchr_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!cur)) { ADJERR_LOC(rtx, &nde->loc); goto oops; }
|
|
break;
|
|
case HAWK_NDE_INT:
|
|
cur = hawk_rtx_makeintval_inline(rtx, ((hawk_nde_int_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!cur)) { ADJERR_LOC(rtx, &nde->loc); goto oops; }
|
|
if (HAWK_VTR_IS_POINTER(cur)) ((hawk_val_int_t*)cur)->nde = nde;
|
|
break;
|
|
case HAWK_NDE_FLT:
|
|
cur = hawk_rtx_makefltval(rtx, ((hawk_nde_flt_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!cur)) { ADJERR_LOC(rtx, &nde->loc); goto oops; }
|
|
((hawk_val_flt_t*)cur)->nde = nde;
|
|
break;
|
|
case HAWK_NDE_STR:
|
|
cur = hawk_rtx_makestrvalwithoochars(rtx, ((hawk_nde_str_t*)nde)->ptr, ((hawk_nde_str_t*)nde)->len);
|
|
if (HAWK_UNLIKELY(!cur)) { ADJERR_LOC(rtx, &nde->loc); goto oops; }
|
|
break;
|
|
case HAWK_NDE_MBS:
|
|
cur = hawk_rtx_makembsvalwithbchars(rtx, ((hawk_nde_mbs_t*)nde)->ptr, ((hawk_nde_mbs_t*)nde)->len);
|
|
if (HAWK_UNLIKELY(!cur)) { ADJERR_LOC(rtx, &nde->loc); goto oops; }
|
|
break;
|
|
case HAWK_NDE_ARG:
|
|
cur = HAWK_RTX_STACK_ARG(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
break;
|
|
case HAWK_NDE_GBL:
|
|
cur = HAWK_RTX_STACK_GBL(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
break;
|
|
case HAWK_NDE_LCL:
|
|
cur = HAWK_RTX_STACK_LCL(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
break;
|
|
case HAWK_NDE_NAMED:
|
|
HAWK_ASSERT(((hawk_nde_var_t*)nde)->id.idxa < rtx->named_slot_count);
|
|
cur = HAWK_RTX_STACK_NAMED(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
break;
|
|
|
|
/* ---- user-defined function call: iterative via exec_stack ---- */
|
|
case HAWK_NDE_FNCALL_FUN:
|
|
{
|
|
hawk_nde_fncall_t* call = (hawk_nde_fncall_t*)nde;
|
|
hawk_fun_t* fun;
|
|
pafn_t pafn;
|
|
hawk_oow_t saved_stack_top, saved_stack_base;
|
|
hawk_oow_t saved_arg_stack_top, nargs, stack_req;
|
|
hawk_oow_t exec_body_base;
|
|
|
|
fun = resolve_fncall_fun(rtx, call);
|
|
if (HAWK_UNLIKELY(!fun)) goto oops;
|
|
|
|
if (HAWK_UNLIKELY(call->nargs > fun->nargs && !fun->variadic))
|
|
{
|
|
hawk_rtx_seterrfmt(rtx, &call->loc, HAWK_EARGTM,
|
|
HAWK_T("too many arguments to '%.*js'"), fun->name.len, fun->name.ptr);
|
|
goto oops;
|
|
}
|
|
|
|
saved_stack_top = rtx->stack_top;
|
|
saved_stack_base = rtx->stack_base;
|
|
|
|
stack_req = 4 + call->nargs;
|
|
if (fun->nargs > call->nargs) stack_req += fun->nargs - call->nargs;
|
|
|
|
if (HAWK_UNLIKELY(HAWK_RTX_STACK_AVAIL(rtx) < stack_req))
|
|
{
|
|
hawk_rtx_seterrbfmt(rtx, &call->loc, HAWK_ESTACK,
|
|
"stack full(avail=%zu, limit=%zu) for call frame with %zu arguments",
|
|
HAWK_RTX_STACK_AVAIL(rtx), rtx->stack_limit, call->nargs);
|
|
goto oops;
|
|
}
|
|
|
|
/* push call frame prologue */
|
|
HAWK_RTX_STACK_PUSH(rtx, (void*)rtx->stack_base);
|
|
HAWK_RTX_STACK_PUSH(rtx, (void*)saved_stack_top);
|
|
HAWK_RTX_STACK_PUSH(rtx, hawk_val_nil); /* return value */
|
|
HAWK_RTX_STACK_PUSH(rtx, hawk_val_nil); /* nargs placeholder */
|
|
saved_arg_stack_top = rtx->stack_top;
|
|
|
|
/* push arguments */
|
|
pafn.args = call->args;
|
|
pafn.nargs = call->nargs;
|
|
pafn.argspec = HAWK_NULL;
|
|
nargs = push_arg_from_nde(rtx, &call->loc, &pafn);
|
|
if (HAWK_UNLIKELY(nargs == (hawk_oow_t)-1))
|
|
{
|
|
/* cleanup partially-pushed args + prologue */
|
|
while (rtx->stack_top > saved_arg_stack_top)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, rtx->stack[rtx->stack_top - 1]);
|
|
HAWK_RTX_STACK_POP(rtx);
|
|
}
|
|
HAWK_ASSERT(rtx->stack_top - saved_stack_top == 4);
|
|
while (rtx->stack_top > saved_stack_top) HAWK_RTX_STACK_POP(rtx);
|
|
rtx->stack_base = saved_stack_base;
|
|
goto oops;
|
|
}
|
|
|
|
/* fill missing args with nils */
|
|
while (nargs < fun->nargs)
|
|
{
|
|
HAWK_RTX_STACK_PUSH(rtx, hawk_val_nil);
|
|
nargs++;
|
|
}
|
|
|
|
/* activate new frame */
|
|
rtx->stack_base = saved_stack_top;
|
|
HAWK_RTX_STACK_NARGS(rtx) = (void*)nargs;
|
|
|
|
exec_body_base = rtx->exec_stack_size;
|
|
|
|
if (HAWK_UNLIKELY(enter_block_iterative(rtx, (hawk_nde_blk_t*)fun->body) <= -1))
|
|
{
|
|
/* undo full AWK frame */
|
|
while (rtx->stack_top > saved_arg_stack_top)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, rtx->stack[rtx->stack_top - 1]);
|
|
HAWK_RTX_STACK_POP(rtx);
|
|
}
|
|
rtx->stack_top = saved_stack_top;
|
|
rtx->stack_base = saved_stack_base;
|
|
goto oops;
|
|
}
|
|
|
|
if (HAWK_UNLIKELY(push_call_frame(rtx, call, fun, saved_stack_top, saved_stack_base, nargs) <= -1))
|
|
{
|
|
unwind_exec_stack(rtx, exec_body_base);
|
|
while (rtx->stack_top > saved_arg_stack_top)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, rtx->stack[rtx->stack_top - 1]);
|
|
HAWK_RTX_STACK_POP(rtx);
|
|
}
|
|
rtx->stack_top = saved_stack_top;
|
|
rtx->stack_base = saved_stack_base;
|
|
goto oops;
|
|
}
|
|
|
|
/* convert the EVAL frame to FNCALL_FUN_AWAIT in-place.
|
|
* eframe_stack_size was already decremented when we "popped"
|
|
* the EVAL frame, so the freed slot is at index [eframe_stack_size],
|
|
* not [eframe_stack_size - 1]. Re-push by incrementing again. */
|
|
f = &rtx->eframe_stack[rtx->eframe_stack_size];
|
|
rtx->eframe_stack_size++; /* re-push the reused the frame */
|
|
f->state = HAWK_EF_FNCALL_FUN_AWAIT;
|
|
f->nde = (hawk_nde_t*)call;
|
|
f->val = HAWK_NULL;
|
|
f->aux = exec_body_base;
|
|
|
|
*cur_inout = cur;
|
|
return 0; /* yield to run_driver to execute the body */
|
|
}
|
|
|
|
/* ---- everything else - fall back to the original recursive evaluator ---- */
|
|
default:
|
|
cur = eval_expression0(rtx, nde);
|
|
if (HAWK_UNLIKELY(!cur))
|
|
{
|
|
if (rtx->exit_level < EXIT_GLOBAL) goto oops;
|
|
}
|
|
if (HAWK_LIKELY(cur) && HAWK_UNLIKELY(rtx->exit_level >= EXIT_GLOBAL))
|
|
{
|
|
hawk_rtx_refupval_inline(rtx, cur);
|
|
hawk_rtx_refdownval_inline(rtx, cur);
|
|
CLRERR(rtx);
|
|
cur = HAWK_NULL;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_FNCALL_FUN_AWAIT:
|
|
{
|
|
/* body execution complete (exec_stack_size == f->aux); tear down */
|
|
hawk_nde_fncall_t* call = (hawk_nde_fncall_t*)f->nde;
|
|
hawk_call_frame_t* cf = &rtx->call_stack[rtx->call_stack_size - 1];
|
|
hawk_fun_t* fun = cf->fun;
|
|
hawk_oow_t nargs = cf->nargs;
|
|
hawk_oow_t i = 0;
|
|
int n;
|
|
|
|
/* is this really a reliable way to check if the function body execution was good?
|
|
* there is explict error clearance at the bottom of run_statement0(), mainly for this */
|
|
n = (hawk_rtx_geterrnum(rtx) == HAWK_ENOERR)? 0: -1;
|
|
|
|
/* ref-arg writeback: mirrors hawk_rtx_evalcall post-body logic */
|
|
if (fun && fun->argspec && call->nargs > 0 && call->args)
|
|
{
|
|
hawk_oow_t cur_sb = rtx->stack_base;
|
|
hawk_oow_t prev_sb = (hawk_oow_t)rtx->stack[rtx->stack_base + 0];
|
|
hawk_nde_t* p = call->args;
|
|
|
|
if (fun->hasrefarg)
|
|
{
|
|
for (; i < call->nargs; i++, p = p->next)
|
|
{
|
|
if (n >= 0 && i < fun->argspeclen &&
|
|
(fun->argspec[i] == 'r' || fun->argspec[i] == 'R'))
|
|
{
|
|
hawk_val_t* av = HAWK_RTX_STACK_ARG(rtx, i);
|
|
if (HAWK_RTX_GETVALTYPE(rtx, av) != HAWK_VAL_REF)
|
|
{
|
|
hawk_val_t** ref;
|
|
hawk_val_ref_t refv;
|
|
int r;
|
|
rtx->stack_base = prev_sb;
|
|
r = get_reference(rtx, p, &ref);
|
|
rtx->stack_base = cur_sb;
|
|
if (HAWK_LIKELY(r >= 0))
|
|
{
|
|
HAWK_RTX_INIT_REF_VAL(&refv, p->type - HAWK_NDE_NAMED, ref, 9);
|
|
if (HAWK_UNLIKELY(hawk_rtx_setrefval(rtx, &refv, av) <= -1))
|
|
{
|
|
n = -1;
|
|
ADJERR_LOC(rtx, &call->loc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_ARG(rtx, i));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (; i < call->nargs; i++, p = p->next)
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_ARG(rtx, i));
|
|
}
|
|
}
|
|
|
|
/* also refdown any remaining (missing) args filled with nil */
|
|
for (; i < nargs; i++)
|
|
hawk_rtx_refdownval_inline(rtx, HAWK_RTX_STACK_ARG(rtx, i));
|
|
|
|
{
|
|
hawk_val_t* v = HAWK_RTX_STACK_RETVAL(rtx);
|
|
if (n <= -1)
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
HAWK_RTX_STACK_RETVAL(rtx) = hawk_val_nil;
|
|
}
|
|
else
|
|
{
|
|
hawk_rtx_refdownval_nofree_inline(rtx, v);
|
|
}
|
|
|
|
rtx->stack_top = cf->saved_stack_top;
|
|
rtx->stack_base = cf->saved_stack_base;
|
|
rtx->call_stack_size--;
|
|
if (rtx->exit_level == EXIT_FUNCTION) rtx->exit_level = EXIT_NONE;
|
|
|
|
f->val = HAWK_NULL;
|
|
rtx->eframe_stack_size--;
|
|
|
|
if (n <= -1) goto oops;
|
|
cur = v;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_BIN_LEFT:
|
|
{
|
|
if (HAWK_UNLIKELY(!cur)) { ADJERR_LOC(rtx, &f->nde->loc); goto oops; }
|
|
hawk_rtx_refupval_inline(rtx, cur);
|
|
f->val = cur;
|
|
f->state = HAWK_EF_BIN_RIGHT;
|
|
cur = HAWK_NULL;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, ((hawk_nde_exp_t*)f->nde)->right) <= -1))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, f->val); f->val = HAWK_NULL; goto oops;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_BIN_RIGHT:
|
|
{
|
|
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)f->nde;
|
|
hawk_val_t* left = f->val;
|
|
hawk_val_t* right = cur;
|
|
hawk_val_t* res;
|
|
if (HAWK_UNLIKELY(!right))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, left); f->val = HAWK_NULL;
|
|
ADJERR_LOC(rtx, &f->nde->loc); goto oops;
|
|
}
|
|
hawk_rtx_refupval_inline(rtx, right);
|
|
HAWK_ASSERT(get_binop_func(exp->opcode) != HAWK_NULL);
|
|
res = get_binop_func(exp->opcode)(rtx, left, right);
|
|
if (HAWK_UNLIKELY(!res)) ADJERR_LOC(rtx, &f->nde->loc);
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
hawk_rtx_refdownval_inline(rtx, right);
|
|
f->val = HAWK_NULL;
|
|
rtx->eframe_stack_size--;
|
|
cur = res;
|
|
if (HAWK_UNLIKELY(!cur)) goto oops;
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_UNARY:
|
|
{
|
|
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)f->nde;
|
|
hawk_val_t* left = cur;
|
|
hawk_val_t* res = HAWK_NULL;
|
|
int n; hawk_int_t l; hawk_flt_t r;
|
|
if (HAWK_UNLIKELY(!left)) { ADJERR_LOC(rtx, &f->nde->loc); goto oops; }
|
|
hawk_rtx_refupval_inline(rtx, left);
|
|
switch (exp->opcode)
|
|
{
|
|
case HAWK_UNROP_MINUS:
|
|
n = hawk_rtx_valtonum(rtx, left, &l, &r);
|
|
if (HAWK_LIKELY(n >= 0))
|
|
res = (n == 0)? hawk_rtx_makeintval_inline(rtx, -l): hawk_rtx_makefltval(rtx, -r);
|
|
break;
|
|
|
|
case HAWK_UNROP_LNOT:
|
|
res = hawk_rtx_makeboolval(rtx, !hawk_rtx_valtobool(rtx, left));
|
|
break;
|
|
|
|
case HAWK_UNROP_BNOT:
|
|
n = hawk_rtx_valtoint_inline(rtx, left, &l);
|
|
if (HAWK_LIKELY(n >= 0)) res = hawk_rtx_makeintval_inline(rtx, ~l);
|
|
break;
|
|
|
|
case HAWK_UNROP_PLUS:
|
|
n = hawk_rtx_valtonum(rtx, left, &l, &r);
|
|
if (HAWK_LIKELY(n >= 0))
|
|
res = (n == 0)? hawk_rtx_makeintval_inline(rtx, l): hawk_rtx_makefltval(rtx, r);
|
|
break;
|
|
}
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
if (HAWK_UNLIKELY(!res)) ADJERR_LOC(rtx, &f->nde->loc);
|
|
rtx->eframe_stack_size--;
|
|
cur = res;
|
|
if (HAWK_UNLIKELY(!cur)) goto oops;
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_CND:
|
|
{
|
|
hawk_nde_cnd_t* cnd = (hawk_nde_cnd_t*)f->nde;
|
|
hawk_nde_t* branch;
|
|
int bval;
|
|
if (HAWK_UNLIKELY(!cur)) { ADJERR_LOC(rtx, &f->nde->loc); goto oops; }
|
|
hawk_rtx_refupval_inline(rtx, cur);
|
|
bval = hawk_rtx_valtobool(rtx, cur);
|
|
hawk_rtx_refdownval_inline(rtx, cur);
|
|
branch = bval? cnd->left: cnd->right;
|
|
f->state = HAWK_EF_EVAL;
|
|
f->nde = branch;
|
|
f->val = HAWK_NULL;
|
|
cur = HAWK_NULL;
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_ASS_RHS:
|
|
{
|
|
hawk_nde_ass_t* ass = (hawk_nde_ass_t*)f->nde;
|
|
if (HAWK_UNLIKELY(!cur)) { ADJERR_LOC(rtx, &f->nde->loc); goto oops; }
|
|
hawk_rtx_refupval_inline(rtx, cur);
|
|
if (ass->opcode != HAWK_ASSOP_NONE)
|
|
{
|
|
f->val = cur;
|
|
f->state = HAWK_EF_ASS_LHS;
|
|
cur = HAWK_NULL;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, ass->left) <= -1))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, f->val); f->val = HAWK_NULL; goto oops;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hawk_val_t* res = do_assignment(rtx, ass->left, cur, ass->is_init);
|
|
hawk_rtx_refdownval_inline(rtx, cur);
|
|
rtx->eframe_stack_size--;
|
|
cur = res;
|
|
if (HAWK_UNLIKELY(!cur)) goto oops;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_ASS_LHS:
|
|
{
|
|
hawk_nde_ass_t* ass = (hawk_nde_ass_t*)f->nde;
|
|
hawk_val_t* rhs = f->val;
|
|
hawk_val_t* lhs = cur;
|
|
hawk_val_t* tmp, * res;
|
|
if (HAWK_UNLIKELY(!lhs))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, rhs);
|
|
f->val = HAWK_NULL;
|
|
ADJERR_LOC(rtx, &f->nde->loc);
|
|
goto oops;
|
|
}
|
|
hawk_rtx_refupval_inline(rtx, lhs);
|
|
switch (ass->opcode)
|
|
{
|
|
case HAWK_ASSOP_PLUS: tmp = eval_binop_plus(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_MINUS: tmp = eval_binop_minus(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_MUL: tmp = eval_binop_mul(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_DIV: tmp = eval_binop_div(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_IDIV: tmp = eval_binop_idiv(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_MOD: tmp = eval_binop_mod(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_EXP: tmp = eval_binop_exp(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_CONCAT: tmp = eval_binop_concat(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_RS: tmp = eval_binop_rshift(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_LS: tmp = eval_binop_lshift(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_BAND: tmp = eval_binop_band(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_BXOR: tmp = eval_binop_bxor(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_BOR: tmp = eval_binop_bor(rtx, lhs, rhs); break;
|
|
default:
|
|
HAWK_ASSERT(!"should never happen - invalid assignment opcode");
|
|
hawk_rtx_seterrbfmt(rtx, &f->nde->loc, HAWK_EINTERN, "internal error - invalid assignment opcode(%d)", (int)ass->opcode);
|
|
tmp = HAWK_NULL;
|
|
break;
|
|
}
|
|
hawk_rtx_refdownval_inline(rtx, lhs);
|
|
hawk_rtx_refdownval_inline(rtx, rhs);
|
|
f->val = HAWK_NULL;
|
|
if (HAWK_UNLIKELY(!tmp)) { ADJERR_LOC(rtx, &f->nde->loc); goto oops; }
|
|
hawk_rtx_refupval_inline(rtx, tmp);
|
|
res = do_assignment(rtx, ass->left, tmp, ass->is_init);
|
|
hawk_rtx_refdownval_inline(rtx, tmp);
|
|
rtx->eframe_stack_size--;
|
|
cur = res;
|
|
if (HAWK_UNLIKELY(!cur)) goto oops;
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_LAND_LEFT:
|
|
{
|
|
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)f->nde;
|
|
if (HAWK_UNLIKELY(!cur)) { ADJERR_LOC(rtx, &f->nde->loc); goto oops; }
|
|
hawk_rtx_refupval_inline(rtx, cur);
|
|
if (hawk_rtx_valtobool(rtx, cur))
|
|
{
|
|
f->val = cur;
|
|
f->state = HAWK_EF_LAND_RIGHT;
|
|
cur = HAWK_NULL;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, exp->right) <= -1))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, f->val);
|
|
f->val = HAWK_NULL;
|
|
goto oops;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, cur);
|
|
rtx->eframe_stack_size--;
|
|
cur = hawk_rtx_makeboolval(rtx, 0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_LAND_RIGHT:
|
|
{
|
|
hawk_val_t* rv = cur;
|
|
if (HAWK_UNLIKELY(!rv))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, f->val); f->val = HAWK_NULL;
|
|
ADJERR_LOC(rtx, &f->nde->loc); goto oops;
|
|
}
|
|
hawk_rtx_refupval_inline(rtx, rv);
|
|
cur = hawk_rtx_makeboolval(rtx, hawk_rtx_valtobool(rtx, rv));
|
|
hawk_rtx_refdownval_inline(rtx, rv);
|
|
hawk_rtx_refdownval_inline(rtx, f->val);
|
|
f->val = HAWK_NULL;
|
|
rtx->eframe_stack_size--;
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_LOR_LEFT:
|
|
{
|
|
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)f->nde;
|
|
if (HAWK_UNLIKELY(!cur)) { ADJERR_LOC(rtx, &f->nde->loc); goto oops; }
|
|
hawk_rtx_refupval_inline(rtx, cur);
|
|
if (!hawk_rtx_valtobool(rtx, cur))
|
|
{
|
|
f->val = cur;
|
|
f->state = HAWK_EF_LOR_RIGHT;
|
|
cur = HAWK_NULL;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, exp->right) <= -1))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, f->val);
|
|
f->val = HAWK_NULL;
|
|
goto oops;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, cur);
|
|
rtx->eframe_stack_size--;
|
|
cur = hawk_rtx_makeboolval(rtx, 1);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_LOR_RIGHT:
|
|
{
|
|
hawk_val_t* rv = cur;
|
|
if (HAWK_UNLIKELY(!rv))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, f->val); f->val = HAWK_NULL;
|
|
ADJERR_LOC(rtx, &f->nde->loc);
|
|
goto oops;
|
|
}
|
|
hawk_rtx_refupval_inline(rtx, rv);
|
|
cur = hawk_rtx_makeboolval(rtx, hawk_rtx_valtobool(rtx, rv));
|
|
hawk_rtx_refdownval_inline(rtx, rv);
|
|
hawk_rtx_refdownval_inline(rtx, f->val);
|
|
f->val = HAWK_NULL;
|
|
rtx->eframe_stack_size--;
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
default:
|
|
HAWK_ASSERT(!"process_eframes: unexpected eframe state");
|
|
hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EINTERN);
|
|
goto oops;
|
|
}
|
|
}
|
|
|
|
*cur_inout = cur;
|
|
return 1; /* all eframes consumed */
|
|
|
|
oops:
|
|
unwind_eframe_stack_with_calls(rtx, eframe_base);
|
|
*cur_inout = HAWK_NULL;
|
|
return -1;
|
|
}
|
|
|
|
/* ============================================================
|
|
* run_driver — unified dispatcher for exec_stack and eframe_stack.
|
|
*
|
|
* Drives statement execution (exec_stack) interleaved with expression
|
|
* evaluation (eframe_stack). Calls process_eframes when eframes are
|
|
* present and not blocked by a pending FNCALL_FUN_AWAIT body, and
|
|
* pops / dispatches exec frames otherwise.
|
|
*
|
|
* Returns 0 on success, -1 on error.
|
|
* ============================================================ */
|
|
static int run_driver (hawk_rtx_t* rtx, hawk_oow_t exec_base, hawk_oow_t eframe_base)
|
|
{
|
|
while (rtx->exec_stack_size > exec_base || rtx->eframe_stack_size > eframe_base)
|
|
{
|
|
if (rtx->eframe_stack_size > eframe_base)
|
|
{
|
|
hawk_eframe_t* etop;
|
|
int blocked;
|
|
|
|
etop = &rtx->eframe_stack[rtx->eframe_stack_size - 1];
|
|
blocked = (etop->state == HAWK_EF_FNCALL_FUN_AWAIT && rtx->exec_stack_size > etop->aux);
|
|
|
|
if (!blocked)
|
|
{
|
|
hawk_val_t* cur;
|
|
int n;
|
|
|
|
cur = HAWK_NULL;
|
|
n = process_eframes(rtx, eframe_base, &cur);
|
|
if (HAWK_UNLIKELY(n < 0)) goto oops;
|
|
|
|
if (n == 1)
|
|
{
|
|
/* expression done: pass result to the waiting AFTER_EVAL exec frame */
|
|
if (rtx->driver_eval_result)
|
|
{
|
|
hawk_rtx_refupval_inline(rtx, rtx->driver_eval_result);
|
|
hawk_rtx_refdownval_inline(rtx, rtx->driver_eval_result);
|
|
}
|
|
rtx->driver_eval_result = cur;
|
|
continue;
|
|
}
|
|
|
|
/* n == 0: blocked by FNCALL_FUN_AWAIT.
|
|
* If process_eframes completed a sub-expression (cur != NULL) before
|
|
* hitting the block, deliver it to the pending AFTER_EVAL exec frame. */
|
|
if (cur != HAWK_NULL)
|
|
{
|
|
if (rtx->driver_eval_result)
|
|
{
|
|
hawk_rtx_refupval_inline(rtx, rtx->driver_eval_result);
|
|
hawk_rtx_refdownval_inline(rtx, rtx->driver_eval_result);
|
|
}
|
|
rtx->driver_eval_result = cur;
|
|
}
|
|
/* fall through to process an exec frame */
|
|
}
|
|
}
|
|
|
|
if (rtx->exec_stack_size > exec_base)
|
|
{
|
|
hawk_exec_stack_t es;
|
|
int n;
|
|
|
|
if (HAWK_UNLIKELY(!pop_exec_stack(rtx, &es))) break;
|
|
|
|
n = run_statement0(rtx, &es);
|
|
if (HAWK_UNLIKELY(n < 0))
|
|
{
|
|
/* error from exec frame: if a FNCALL_FUN_AWAIT is blocked at the top
|
|
* of the eframe_stack, unwind its body exec frames and let
|
|
* process_eframes handle the teardown (with the error flag set). */
|
|
if (rtx->eframe_stack_size > eframe_base)
|
|
{
|
|
hawk_eframe_t* etop = &rtx->eframe_stack[rtx->eframe_stack_size - 1];
|
|
if (etop->state == HAWK_EF_FNCALL_FUN_AWAIT && rtx->exec_stack_size > etop->aux)
|
|
{
|
|
unwind_exec_stack(rtx, etop->aux);
|
|
continue; /* re-enter: process_eframes will teardown with error */
|
|
}
|
|
}
|
|
goto oops;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
oops:
|
|
unwind_exec_stack(rtx, exec_base);
|
|
unwind_eframe_stack_with_calls(rtx, eframe_base);
|
|
return -1;
|
|
}
|
|
#endif /* HAWK_ENABLE_XSTACK_EVAL */
|
|
|
|
static HAWK_INLINE_ALWAYS hawk_val_t* eval_expression0_xstack (hawk_rtx_t* rtx, hawk_nde_t* root_nde)
|
|
{
|
|
hawk_oow_t frame_base = rtx->eframe_stack_size;
|
|
hawk_val_t* cur = HAWK_NULL; /* result of last completed sub-expression */
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Pre-loop fast path - handles the most common node shapes without
|
|
* touching the eframe_stack at all.
|
|
* ---------------------------------------------------------------- */
|
|
switch (root_nde->type)
|
|
{
|
|
/* variable reads: no allocation, no failure */
|
|
case HAWK_NDE_ARG:
|
|
return HAWK_RTX_STACK_ARG(rtx, ((hawk_nde_var_t*)root_nde)->id.idxa);
|
|
case HAWK_NDE_GBL:
|
|
return HAWK_RTX_STACK_GBL(rtx, ((hawk_nde_var_t*)root_nde)->id.idxa);
|
|
case HAWK_NDE_LCL:
|
|
return HAWK_RTX_STACK_LCL(rtx, ((hawk_nde_var_t*)root_nde)->id.idxa);
|
|
case HAWK_NDE_NAMED:
|
|
HAWK_ASSERT(((hawk_nde_var_t*)root_nde)->id.idxa < rtx->named_slot_count);
|
|
return HAWK_RTX_STACK_NAMED(rtx, ((hawk_nde_var_t*)root_nde)->id.idxa);
|
|
|
|
/* binary op - both operands are fast leaves (variable or compile-time constant) */
|
|
case HAWK_NDE_EXP_BIN:
|
|
{
|
|
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)root_nde;
|
|
if (exp->opcode != HAWK_BINOP_LAND && exp->opcode != HAWK_BINOP_LOR &&
|
|
exp->opcode != HAWK_BINOP_IN && exp->opcode != HAWK_BINOP_NM &&
|
|
exp->opcode != HAWK_BINOP_MA)
|
|
{
|
|
hawk_val_t* lv = xstack_fast_operand(rtx, exp->left);
|
|
if (HAWK_UNLIKELY(!lv))
|
|
{
|
|
ADJERR_LOC(rtx, &root_nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
else if (lv != XSTACK_NOT_A_LEAF)
|
|
{
|
|
hawk_val_t* rv = xstack_fast_operand(rtx, exp->right);
|
|
if (rv != XSTACK_NOT_A_LEAF)
|
|
{
|
|
hawk_val_t* res;
|
|
binop_func_t fn = get_binop_func(exp->opcode);
|
|
HAWK_ASSERT(fn != HAWK_NULL);
|
|
if (HAWK_UNLIKELY(!rv))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, lv);
|
|
ADJERR_LOC(rtx, &root_nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
res = fn(rtx, lv, rv);
|
|
if (HAWK_UNLIKELY(!res)) ADJERR_LOC(rtx, &root_nde->loc);
|
|
hawk_rtx_refdownval_inline(rtx, lv);
|
|
hawk_rtx_refdownval_inline(rtx, rv);
|
|
return res;
|
|
}
|
|
hawk_rtx_refdownval_inline(rtx, lv);
|
|
}
|
|
}
|
|
break; /* fall through to general path */
|
|
}
|
|
|
|
/* unary op - operand is a fast leaf */
|
|
case HAWK_NDE_EXP_UNR:
|
|
{
|
|
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)root_nde;
|
|
hawk_val_t* ov = xstack_fast_operand(rtx, exp->left);
|
|
|
|
if (HAWK_UNLIKELY(!ov))
|
|
{
|
|
ADJERR_LOC(rtx, &root_nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
else if (ov != XSTACK_NOT_A_LEAF)
|
|
{
|
|
hawk_val_t* res = HAWK_NULL;
|
|
int n;
|
|
hawk_int_t l;
|
|
hawk_flt_t r;
|
|
|
|
switch (exp->opcode)
|
|
{
|
|
case HAWK_UNROP_MINUS:
|
|
n = hawk_rtx_valtonum(rtx, ov, &l, &r);
|
|
if (HAWK_LIKELY(n >= 0)) res = (n == 0)? hawk_rtx_makeintval_inline(rtx, -l): hawk_rtx_makefltval(rtx, -r);
|
|
break;
|
|
case HAWK_UNROP_LNOT:
|
|
res = hawk_rtx_makeboolval(rtx, !hawk_rtx_valtobool(rtx, ov));
|
|
break;
|
|
case HAWK_UNROP_BNOT:
|
|
n = hawk_rtx_valtoint_inline(rtx, ov, &l);
|
|
if (HAWK_LIKELY(n >= 0)) res = hawk_rtx_makeintval_inline(rtx, ~l);
|
|
break;
|
|
case HAWK_UNROP_PLUS:
|
|
n = hawk_rtx_valtonum(rtx, ov, &l, &r);
|
|
if (HAWK_LIKELY(n >= 0)) res = (n == 0)? hawk_rtx_makeintval_inline(rtx, l): hawk_rtx_makefltval(rtx, r);
|
|
break;
|
|
}
|
|
hawk_rtx_refdownval_inline(rtx, ov);
|
|
if (HAWK_UNLIKELY(!res)) { ADJERR_LOC(rtx, &root_nde->loc); return HAWK_NULL; }
|
|
return res;
|
|
}
|
|
break; /* fall through to general path */
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* seed: push the root node to evaluate */
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, root_nde) <= -1)) return HAWK_NULL;
|
|
|
|
while (rtx->eframe_stack_size > frame_base)
|
|
{
|
|
hawk_eframe_t* f = &rtx->eframe_stack[rtx->eframe_stack_size - 1];
|
|
|
|
switch (f->state)
|
|
{
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_EVAL:
|
|
{
|
|
hawk_nde_t* nde = f->nde;
|
|
|
|
/* pop this EVAL frame now; every branch below either
|
|
* - sets cur and continues (leaf), or
|
|
* - converts this frame to a continuation state, or
|
|
* - converts this frame and pushes a child EVAL frame */
|
|
rtx->eframe_stack_size--;
|
|
|
|
switch (nde->type)
|
|
{
|
|
/* binary expression */
|
|
case HAWK_NDE_EXP_BIN:
|
|
{
|
|
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)nde;
|
|
|
|
switch (exp->opcode)
|
|
{
|
|
case HAWK_BINOP_LAND:
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_LAND_LEFT, nde) <= -1)) goto oops;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, exp->left) <= -1)) goto oops;
|
|
break;
|
|
|
|
case HAWK_BINOP_LOR:
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_LOR_LEFT, nde) <= -1)) goto oops;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, exp->left) <= -1)) goto oops;
|
|
break;
|
|
|
|
case HAWK_BINOP_IN:
|
|
/* eval_binop_in uses its own sub-evaluation internally;
|
|
* delegate directly to avoid re-implementing it */
|
|
cur = eval_binop_in(rtx, exp->left, exp->right);
|
|
if (HAWK_UNLIKELY(!cur)) goto oops;
|
|
break;
|
|
|
|
case HAWK_BINOP_NM:
|
|
cur = eval_binop_nm(rtx, exp->left, exp->right);
|
|
if (HAWK_UNLIKELY(!cur)) goto oops;
|
|
break;
|
|
|
|
case HAWK_BINOP_MA:
|
|
cur = eval_binop_ma(rtx, exp->left, exp->right);
|
|
if (HAWK_UNLIKELY(!cur)) goto oops;
|
|
break;
|
|
|
|
default:
|
|
{
|
|
/* fast path - both operands are fast leaves(variable or constant) —
|
|
* compute directly without touching the frame stack */
|
|
hawk_val_t* lv = xstack_fast_operand(rtx, exp->left);
|
|
if (HAWK_UNLIKELY(!lv))
|
|
{
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
goto oops;
|
|
}
|
|
else if (lv != XSTACK_NOT_A_LEAF)
|
|
{
|
|
hawk_val_t* rv = xstack_fast_operand(rtx, exp->right);
|
|
if (rv != XSTACK_NOT_A_LEAF)
|
|
{
|
|
binop_func_t fn = get_binop_func(exp->opcode);
|
|
HAWK_ASSERT(fn != HAWK_NULL);
|
|
if (HAWK_UNLIKELY(!rv))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, lv);
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
goto oops;
|
|
}
|
|
cur = fn(rtx, lv, rv);
|
|
if (HAWK_UNLIKELY(!cur)) ADJERR_LOC(rtx, &nde->loc);
|
|
hawk_rtx_refdownval_inline(rtx, lv);
|
|
hawk_rtx_refdownval_inline(rtx, rv);
|
|
if (HAWK_UNLIKELY(!cur)) goto oops;
|
|
break;
|
|
}
|
|
hawk_rtx_refdownval_inline(rtx, lv);
|
|
}
|
|
|
|
/* general path: push continuation then evaluate left child */
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_BIN_LEFT, nde) <= -1)) goto oops;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, exp->left) <= -1)) goto oops;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* unary expression */
|
|
case HAWK_NDE_EXP_UNR:
|
|
{
|
|
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)nde;
|
|
/* fast path - operand is a fast leaf(variable or constant) */
|
|
hawk_val_t* ov = xstack_fast_operand(rtx, exp->left);
|
|
if (HAWK_UNLIKELY(!ov))
|
|
{
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
goto oops;
|
|
}
|
|
else if (ov != XSTACK_NOT_A_LEAF)
|
|
{
|
|
hawk_val_t* res = HAWK_NULL;
|
|
int un;
|
|
hawk_int_t ul;
|
|
hawk_flt_t ur;
|
|
|
|
/* fast path - evaluation completed */
|
|
switch (exp->opcode)
|
|
{
|
|
case HAWK_UNROP_MINUS:
|
|
un = hawk_rtx_valtonum(rtx, ov, &ul, &ur);
|
|
if (HAWK_LIKELY(un >= 0)) res = (un == 0)? hawk_rtx_makeintval_inline(rtx, -ul): hawk_rtx_makefltval(rtx, -ur);
|
|
break;
|
|
case HAWK_UNROP_LNOT:
|
|
res = hawk_rtx_makeboolval(rtx, !hawk_rtx_valtobool(rtx, ov));
|
|
break;
|
|
case HAWK_UNROP_BNOT:
|
|
un = hawk_rtx_valtoint_inline(rtx, ov, &ul);
|
|
if (HAWK_LIKELY(un >= 0)) res = hawk_rtx_makeintval_inline(rtx, ~ul);
|
|
break;
|
|
case HAWK_UNROP_PLUS:
|
|
un = hawk_rtx_valtonum(rtx, ov, &ul, &ur);
|
|
if (HAWK_LIKELY(un >= 0)) res = (un == 0)? hawk_rtx_makeintval_inline(rtx, ul): hawk_rtx_makefltval(rtx, ur);
|
|
break;
|
|
}
|
|
|
|
hawk_rtx_refdownval_inline(rtx, ov);
|
|
if (HAWK_UNLIKELY(!res)) { ADJERR_LOC(rtx, &nde->loc); goto oops; }
|
|
cur = res;
|
|
break;
|
|
}
|
|
|
|
/* general path */
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_UNARY, nde) <= -1)) goto oops;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, exp->left) <= -1)) goto oops;
|
|
break;
|
|
}
|
|
|
|
/* conditional (ternary) */
|
|
case HAWK_NDE_CND:
|
|
{
|
|
hawk_nde_cnd_t* cnd = (hawk_nde_cnd_t*)nde;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_CND, nde) <= -1)) goto oops;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, cnd->test) <= -1)) goto oops;
|
|
break;
|
|
}
|
|
|
|
/* assignment */
|
|
case HAWK_NDE_ASS:
|
|
{
|
|
hawk_nde_ass_t* ass = (hawk_nde_ass_t*)nde;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_ASS_RHS, nde) <= -1)) goto oops;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, ass->right) <= -1)) goto oops;
|
|
break;
|
|
}
|
|
|
|
/* fast-path leaf nodes */
|
|
case HAWK_NDE_CHAR:
|
|
cur = hawk_rtx_makecharval(rtx, ((hawk_nde_char_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!cur))
|
|
{
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
goto oops;
|
|
}
|
|
break;
|
|
|
|
case HAWK_NDE_BCHR:
|
|
cur = hawk_rtx_makebchrval(rtx, ((hawk_nde_bchr_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!cur))
|
|
{
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
goto oops;
|
|
}
|
|
break;
|
|
|
|
case HAWK_NDE_INT:
|
|
cur = hawk_rtx_makeintval_inline(rtx, ((hawk_nde_int_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!cur))
|
|
{
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
goto oops;
|
|
}
|
|
if (HAWK_VTR_IS_POINTER(cur)) ((hawk_val_int_t*)cur)->nde = nde;
|
|
break;
|
|
|
|
case HAWK_NDE_FLT:
|
|
cur = hawk_rtx_makefltval(rtx, ((hawk_nde_flt_t*)nde)->val);
|
|
if (HAWK_UNLIKELY(!cur))
|
|
{
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
goto oops;
|
|
}
|
|
((hawk_val_flt_t*)cur)->nde = nde;
|
|
break;
|
|
|
|
case HAWK_NDE_STR:
|
|
cur = hawk_rtx_makestrvalwithoochars(rtx, ((hawk_nde_str_t*)nde)->ptr, ((hawk_nde_str_t*)nde)->len);
|
|
if (HAWK_UNLIKELY(!cur))
|
|
{
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
goto oops;
|
|
}
|
|
break;
|
|
|
|
case HAWK_NDE_MBS:
|
|
cur = hawk_rtx_makembsvalwithbchars(rtx, ((hawk_nde_mbs_t*)nde)->ptr, ((hawk_nde_mbs_t*)nde)->len);
|
|
if (HAWK_UNLIKELY(!cur))
|
|
{
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
goto oops;
|
|
}
|
|
break;
|
|
|
|
case HAWK_NDE_ARG:
|
|
cur = HAWK_RTX_STACK_ARG(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
break;
|
|
|
|
case HAWK_NDE_GBL:
|
|
cur = HAWK_RTX_STACK_GBL(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
break;
|
|
|
|
case HAWK_NDE_LCL:
|
|
cur = HAWK_RTX_STACK_LCL(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
break;
|
|
|
|
case HAWK_NDE_NAMED:
|
|
HAWK_ASSERT(((hawk_nde_var_t*)nde)->id.idxa < rtx->named_slot_count);
|
|
cur = HAWK_RTX_STACK_NAMED(rtx, ((hawk_nde_var_t*)nde)->id.idxa);
|
|
break;
|
|
|
|
default:
|
|
/* fall through to original dispatch table for everything else:
|
|
* incpre, incpst, fncall_fnc, fncall_fun, fncall_var, modsym,
|
|
* rex, pos, getline, print, printf, xnil/xtrue/xfalse, etc. */
|
|
cur = eval_expression0(rtx, nde);
|
|
if (HAWK_UNLIKELY(!cur))
|
|
{
|
|
if (rtx->exit_level < EXIT_GLOBAL) goto oops;
|
|
/* exit_level is set; treat NULL as "exit signal" not error */
|
|
}
|
|
if (HAWK_LIKELY(cur) && HAWK_UNLIKELY(rtx->exit_level >= EXIT_GLOBAL))
|
|
{
|
|
hawk_rtx_refupval_inline(rtx, cur);
|
|
hawk_rtx_refdownval_inline(rtx, cur);
|
|
CLRERR(rtx);
|
|
cur = HAWK_NULL;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_BIN_LEFT:
|
|
{
|
|
/* cur = left operand result */
|
|
if (HAWK_UNLIKELY(!cur))
|
|
{
|
|
ADJERR_LOC(rtx, &f->nde->loc);
|
|
goto oops;
|
|
}
|
|
hawk_rtx_refupval_inline(rtx, cur);
|
|
|
|
/* mutate the stack top to a different state withthout popping off */
|
|
f->val = cur;
|
|
f->state = HAWK_EF_BIN_RIGHT;
|
|
cur = HAWK_NULL;
|
|
|
|
/* push right child eval */
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, ((hawk_nde_exp_t*)f->nde)->right) <= -1))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, f->val);
|
|
f->val = HAWK_NULL;
|
|
goto oops;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_BIN_RIGHT:
|
|
{
|
|
/* cur = right operand result; f->val = left (refupped) */
|
|
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)f->nde;
|
|
hawk_val_t* left = f->val;
|
|
hawk_val_t* right = cur;
|
|
hawk_val_t* res;
|
|
|
|
if (HAWK_UNLIKELY(!right))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
f->val = HAWK_NULL;
|
|
ADJERR_LOC(rtx, &f->nde->loc);
|
|
goto oops;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, right);
|
|
HAWK_ASSERT(get_binop_func(exp->opcode) != HAWK_NULL);
|
|
res = get_binop_func(exp->opcode)(rtx, left, right);
|
|
if (HAWK_UNLIKELY(!res)) ADJERR_LOC(rtx, &f->nde->loc);
|
|
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
hawk_rtx_refdownval_inline(rtx, right);
|
|
|
|
f->val = HAWK_NULL; /* nullify the top frame value */
|
|
rtx->eframe_stack_size--; /* pop off */
|
|
|
|
cur = res;
|
|
if (HAWK_UNLIKELY(!cur)) goto oops;
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_UNARY:
|
|
{
|
|
/* cur = operand result */
|
|
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)f->nde;
|
|
hawk_val_t* left = cur;
|
|
hawk_val_t* res = HAWK_NULL;
|
|
int n;
|
|
hawk_int_t l;
|
|
hawk_flt_t r;
|
|
|
|
if (HAWK_UNLIKELY(!left))
|
|
{
|
|
ADJERR_LOC(rtx, &f->nde->loc);
|
|
goto oops;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, left);
|
|
switch (exp->opcode)
|
|
{
|
|
case HAWK_UNROP_MINUS:
|
|
n = hawk_rtx_valtonum(rtx, left, &l, &r);
|
|
if (HAWK_LIKELY(n >= 0))
|
|
{
|
|
res = (n == 0)? hawk_rtx_makeintval_inline(rtx, -l):
|
|
hawk_rtx_makefltval(rtx, -r);
|
|
}
|
|
break;
|
|
case HAWK_UNROP_LNOT:
|
|
res = hawk_rtx_makeboolval(rtx, !hawk_rtx_valtobool(rtx, left));
|
|
break;
|
|
case HAWK_UNROP_BNOT:
|
|
n = hawk_rtx_valtoint_inline(rtx, left, &l);
|
|
if (HAWK_LIKELY(n >= 0)) res = hawk_rtx_makeintval_inline(rtx, ~l);
|
|
break;
|
|
case HAWK_UNROP_PLUS:
|
|
n = hawk_rtx_valtonum(rtx, left, &l, &r);
|
|
if (HAWK_LIKELY(n >= 0))
|
|
{
|
|
res = (n == 0)? hawk_rtx_makeintval_inline(rtx, l):
|
|
hawk_rtx_makefltval(rtx, r);
|
|
}
|
|
break;
|
|
}
|
|
hawk_rtx_refdownval_inline(rtx, left);
|
|
if (HAWK_UNLIKELY(!res)) ADJERR_LOC(rtx, &f->nde->loc);
|
|
|
|
rtx->eframe_stack_size--;
|
|
|
|
cur = res;
|
|
if (HAWK_UNLIKELY(!cur)) goto oops;
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_CND:
|
|
{
|
|
/* cur = test result */
|
|
hawk_nde_cnd_t* cnd = (hawk_nde_cnd_t*)f->nde;
|
|
hawk_nde_t* branch;
|
|
int bval;
|
|
|
|
if (HAWK_UNLIKELY(!cur))
|
|
{
|
|
ADJERR_LOC(rtx, &f->nde->loc);
|
|
goto oops;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, cur);
|
|
bval = hawk_rtx_valtobool(rtx, cur);
|
|
hawk_rtx_refdownval_inline(rtx, cur);
|
|
branch = bval? cnd->left: cnd->right;
|
|
|
|
/* replace CND frame with EVAL frame for chosen branch. no popping */
|
|
f->state = HAWK_EF_EVAL;
|
|
f->nde = branch;
|
|
f->val = HAWK_NULL;
|
|
cur = HAWK_NULL;
|
|
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_ASS_RHS:
|
|
{
|
|
/* cur = RHS result */
|
|
hawk_nde_ass_t* ass = (hawk_nde_ass_t*)f->nde;
|
|
|
|
if (HAWK_UNLIKELY(!cur))
|
|
{
|
|
ADJERR_LOC(rtx, &f->nde->loc);
|
|
goto oops;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, cur);
|
|
|
|
if (ass->opcode != HAWK_ASSOP_NONE)
|
|
{
|
|
/* compound assignment: need LHS value too.
|
|
* +=, -=, etc */
|
|
f->val = cur;
|
|
f->state = HAWK_EF_ASS_LHS; /* mutate the top frame without popping */
|
|
cur = HAWK_NULL;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, ass->left) <= -1))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, f->val);
|
|
f->val = HAWK_NULL;
|
|
goto oops;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* simple assignment: do it now */
|
|
hawk_val_t* res = do_assignment(rtx, ass->left, cur, ass->is_init);
|
|
hawk_rtx_refdownval_inline(rtx, cur);
|
|
rtx->eframe_stack_size--; /* popping */
|
|
cur = res;
|
|
if (HAWK_UNLIKELY(!cur)) goto oops;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_ASS_LHS:
|
|
{
|
|
/* cur = LHS result; f->val = RHS (refupped) */
|
|
#if 0
|
|
static binop_func_t binop_func[] =
|
|
{
|
|
/* this table must be in sync with hawk_assop_type_t in run-prv.h */
|
|
HAWK_NULL, /* HAWK_ASSOP_NONE */
|
|
eval_binop_plus,
|
|
eval_binop_minus,
|
|
eval_binop_mul,
|
|
eval_binop_div,
|
|
eval_binop_idiv,
|
|
eval_binop_mod,
|
|
eval_binop_exp,
|
|
eval_binop_concat,
|
|
eval_binop_rshift,
|
|
eval_binop_lshift,
|
|
eval_binop_band,
|
|
eval_binop_bxor,
|
|
eval_binop_bor
|
|
};
|
|
#endif
|
|
hawk_nde_ass_t* ass = (hawk_nde_ass_t*)f->nde;
|
|
hawk_val_t* rhs = f->val;
|
|
hawk_val_t* lhs = cur;
|
|
hawk_val_t* tmp, * res;
|
|
|
|
if (HAWK_UNLIKELY(!lhs))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, rhs);
|
|
f->val = HAWK_NULL;
|
|
ADJERR_LOC(rtx, &f->nde->loc);
|
|
goto oops;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, lhs);
|
|
|
|
#if 0
|
|
HAWK_ASSERT(ass->opcode >= 0 && ass->opcode < HAWK_COUNTOF(binop_func));
|
|
HAWK_ASSERT(binop_func[ass->opcode] != HAWK_NULL);
|
|
tmp = binop_func[ass->opcode](rtx, lhs, rhs);
|
|
#else
|
|
switch (ass->opcode)
|
|
{
|
|
case HAWK_ASSOP_PLUS: tmp = eval_binop_plus(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_MINUS: tmp = eval_binop_minus(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_MUL: tmp = eval_binop_mul(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_DIV: tmp = eval_binop_div(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_IDIV: tmp = eval_binop_idiv(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_MOD: tmp = eval_binop_mod(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_EXP: tmp = eval_binop_exp(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_CONCAT: tmp = eval_binop_concat(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_RS: tmp = eval_binop_rshift(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_LS: tmp = eval_binop_lshift(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_BAND: tmp = eval_binop_band(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_BXOR: tmp = eval_binop_bxor(rtx, lhs, rhs); break;
|
|
case HAWK_ASSOP_BOR: tmp = eval_binop_bor(rtx, lhs, rhs); break;
|
|
default:
|
|
HAWK_ASSERT(!"should never happen - invalid assigment opcode detected");
|
|
hawk_rtx_seterrbfmt(rtx, &f->nde->loc, HAWK_EINTERN, "internal error - invalid assignment(%d) opcode detected", (int)ass->opcode);
|
|
tmp = HAWK_NULL;
|
|
break;
|
|
}
|
|
#endif
|
|
hawk_rtx_refdownval_inline(rtx, lhs);
|
|
hawk_rtx_refdownval_inline(rtx, rhs);
|
|
f->val = HAWK_NULL;
|
|
|
|
if (HAWK_UNLIKELY(!tmp))
|
|
{
|
|
ADJERR_LOC(rtx, &f->nde->loc);
|
|
goto oops;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, tmp);
|
|
res = do_assignment(rtx, ass->left, tmp, ass->is_init);
|
|
hawk_rtx_refdownval_inline(rtx, tmp);
|
|
|
|
rtx->eframe_stack_size--;
|
|
cur = res;
|
|
if (HAWK_UNLIKELY(!cur)) goto oops;
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_LAND_LEFT:
|
|
{
|
|
/* cur = left operand of && */
|
|
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)f->nde;
|
|
|
|
if (HAWK_UNLIKELY(!cur))
|
|
{
|
|
ADJERR_LOC(rtx, &f->nde->loc);
|
|
goto oops;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, cur);
|
|
if (hawk_rtx_valtobool(rtx, cur))
|
|
{
|
|
/* left is true, must evaluate right */
|
|
f->val = cur;
|
|
f->state = HAWK_EF_LAND_RIGHT;
|
|
cur = HAWK_NULL;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, exp->right) <= -1))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, f->val);
|
|
f->val = HAWK_NULL;
|
|
goto oops;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* short-circuit, result is false */
|
|
hawk_rtx_refdownval_inline(rtx, cur);
|
|
rtx->eframe_stack_size--;
|
|
cur = hawk_rtx_makeboolval(rtx, 0);
|
|
/* no failure check as makeboolval returns a preallocated val_nil or
|
|
* val_true/false — never NULL */
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_LAND_RIGHT:
|
|
{
|
|
/* cur = right operand of &&; f->val = left (refupped) */
|
|
hawk_val_t* rv = cur;
|
|
|
|
if (HAWK_UNLIKELY(!rv))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, f->val);
|
|
f->val = HAWK_NULL;
|
|
ADJERR_LOC(rtx, &f->nde->loc);
|
|
goto oops;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, rv);
|
|
cur = hawk_rtx_makeboolval(rtx, hawk_rtx_valtobool(rtx, rv));
|
|
hawk_rtx_refdownval_inline(rtx, rv);
|
|
hawk_rtx_refdownval_inline(rtx, f->val);
|
|
f->val = HAWK_NULL;
|
|
|
|
rtx->eframe_stack_size--;
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_LOR_LEFT:
|
|
{
|
|
/* cur = left operand of || */
|
|
hawk_nde_exp_t* exp = (hawk_nde_exp_t*)f->nde;
|
|
|
|
if (HAWK_UNLIKELY(!cur))
|
|
{
|
|
ADJERR_LOC(rtx, &f->nde->loc);
|
|
goto oops;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, cur);
|
|
if (!hawk_rtx_valtobool(rtx, cur))
|
|
{
|
|
/* left is false: must evaluate right */
|
|
f->val = cur;
|
|
f->state = HAWK_EF_LOR_RIGHT;
|
|
cur = HAWK_NULL;
|
|
if (HAWK_UNLIKELY(push_eframe(rtx, HAWK_EF_EVAL, exp->right) <= -1))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, f->val);
|
|
f->val = HAWK_NULL;
|
|
goto oops;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* short-circuit: result is true */
|
|
hawk_rtx_refdownval_inline(rtx, cur);
|
|
rtx->eframe_stack_size--;
|
|
cur = hawk_rtx_makeboolval(rtx, 1);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
case HAWK_EF_LOR_RIGHT:
|
|
{
|
|
/* cur = right operand of ||; f->val = left (refupped) */
|
|
hawk_val_t* rv = cur;
|
|
|
|
if (HAWK_UNLIKELY(!rv))
|
|
{
|
|
hawk_rtx_refdownval_inline(rtx, f->val);
|
|
f->val = HAWK_NULL;
|
|
ADJERR_LOC(rtx, &f->nde->loc);
|
|
goto oops;
|
|
}
|
|
|
|
hawk_rtx_refupval_inline(rtx, rv);
|
|
cur = hawk_rtx_makeboolval(rtx, hawk_rtx_valtobool(rtx, rv));
|
|
hawk_rtx_refdownval_inline(rtx, rv);
|
|
hawk_rtx_refdownval_inline(rtx, f->val);
|
|
f->val = HAWK_NULL;
|
|
|
|
rtx->eframe_stack_size--;
|
|
break;
|
|
}
|
|
|
|
/* --------------------------------------------------------- */
|
|
default:
|
|
HAWK_ASSERT(!"eval_expression0_xstack: unexpected eframe state");
|
|
goto oops;
|
|
}
|
|
}
|
|
|
|
return cur;
|
|
|
|
oops:
|
|
unwind_eframe_stack(rtx, frame_base);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
/* Wrapper that adds the regex-matching layer, mirroring eval_expression() */
|
|
static hawk_val_t* eval_expression_xstack (hawk_rtx_t* rtx, hawk_nde_t* nde)
|
|
{
|
|
hawk_val_t* v;
|
|
int n;
|
|
|
|
v = eval_expression0_xstack(rtx, nde);
|
|
if (HAWK_UNLIKELY(!v)) return HAWK_NULL;
|
|
|
|
if (HAWK_RTX_GETVALTYPE(rtx, v) == HAWK_VAL_REX)
|
|
{
|
|
hawk_oocs_t vs;
|
|
int free_vs = 0;
|
|
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
|
|
if (HAWK_RTX_GETVALTYPE(rtx, rtx->inrec.d0) == HAWK_VAL_NIL)
|
|
{
|
|
vs.ptr = HAWK_T("");
|
|
vs.len = 0;
|
|
}
|
|
else
|
|
{
|
|
vs.ptr = hawk_rtx_getvaloocstr(rtx, rtx->inrec.d0, &vs.len);
|
|
if (!vs.ptr)
|
|
{
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
return HAWK_NULL;
|
|
}
|
|
free_vs = 1;
|
|
}
|
|
|
|
n = hawk_rtx_matchvalwithoocs(rtx, v, &vs, &vs, HAWK_NULL, HAWK_NULL);
|
|
hawk_rtx_refdownval_inline(rtx, v);
|
|
if (free_vs) hawk_rtx_freevaloocstr(rtx, rtx->inrec.d0, vs.ptr);
|
|
if (n <= -1)
|
|
{
|
|
ADJERR_LOC(rtx, &nde->loc);
|
|
return HAWK_NULL;
|
|
}
|
|
|
|
v = hawk_rtx_makeintval_inline(rtx, (n != 0));
|
|
HAWK_ASSERT(v != HAWK_NULL);
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
/* push_arg_from_nde variant that uses eval_expression_xstack for argument eval */
|
|
static hawk_oow_t push_arg_from_nde_xstack (hawk_rtx_t* rtx, const hawk_loc_t* call_loc, void* data)
|
|
{
|
|
pafn_t* pafn = (pafn_t*)data;
|
|
hawk_nde_t* p;
|
|
hawk_val_t* v;
|
|
hawk_oow_t nargs;
|
|
hawk_oow_t spec_len;
|
|
|
|
if (HAWK_UNLIKELY(HAWK_RTX_STACK_AVAIL(rtx) < pafn->nargs))
|
|
{
|
|
hawk_rtx_seterrbfmt(rtx, call_loc, HAWK_ESTACK,
|
|
"stack full(avail=%zu, limit=%zu) in push %zu arguments",
|
|
HAWK_RTX_STACK_AVAIL(rtx), rtx->stack_limit, pafn->nargs);
|
|
return (hawk_oow_t)-1;
|
|
}
|
|
|
|
spec_len = pafn->argspec? hawk_count_oocstr(pafn->argspec): 0;
|
|
for (p = pafn->args, nargs = 0; p; p = p->next, nargs++)
|
|
{
|
|
hawk_ooch_t spec;
|
|
|
|
spec = (spec_len <= 0)? '\0': pafn->argspec[((nargs < spec_len)? nargs: spec_len - 1)];
|
|
|
|
switch (spec)
|
|
{
|
|
case 'R':
|
|
case 'r':
|
|
{
|
|
hawk_val_t** ref;
|
|
|
|
if (get_reference(rtx, p, &ref) <= -1)
|
|
{
|
|
if (spec == 'R') goto normal_arg_xs;
|
|
return (hawk_oow_t)-1;
|
|
}
|
|
|
|
v = hawk_rtx_makerefval(rtx, p->type - HAWK_NDE_NAMED, ref);
|
|
break;
|
|
}
|
|
|
|
case 'x':
|
|
v = eval_expression0_xstack(rtx, p);
|
|
break;
|
|
|
|
default:
|
|
normal_arg_xs:
|
|
/* use eval_expression (not eval_expression_xstack) so the fast
|
|
* paths in eval_expression for variable reads and simple binary
|
|
* expressions are applied to argument expressions too */
|
|
v = eval_expression(rtx, p);
|
|
break;
|
|
}
|
|
|
|
if (HAWK_UNLIKELY(!v)) return (hawk_oow_t)-1;
|
|
|
|
HAWK_RTX_STACK_PUSH(rtx, v);
|
|
hawk_rtx_refupval_inline(rtx, v);
|
|
}
|
|
|
|
HAWK_ASSERT(pafn->nargs == nargs);
|
|
return nargs;
|
|
}
|
|
|
|
|
|
#if 0
|
|
/* hawk_rtx_evalcall_xstack — identical to hawk_rtx_evalcall but uses
|
|
* push_arg_from_nde_xstack when the caller supplies push_arg_from_nde as
|
|
* the argpusher, so that argument expressions are evaluated iteratively. */
|
|
hawk_val_t* hawk_rtx_evalcall_xstack (
|
|
hawk_rtx_t* rtx, hawk_nde_fncall_t* call, hawk_fun_t* fun,
|
|
hawk_oow_t(*argpusher)(hawk_rtx_t*,const hawk_loc_t*,void*), void* apdata,
|
|
void(*errhandler)(void*), void* eharg)
|
|
{
|
|
/* When the caller uses push_arg_from_nde, swap in the xstack variant so
|
|
* argument expressions are evaluated iteratively. Any other argpusher
|
|
* (e.g. push_arg_from_vals used by hawk_rtx_callfun) is passed through
|
|
* unchanged because it does not call eval_expression directly. */
|
|
if (argpusher == push_arg_from_nde) argpusher = push_arg_from_nde_xstack;
|
|
|
|
return hawk_rtx_evalcall(rtx, call, fun, argpusher, apdata, errhandler, eharg);
|
|
}
|
|
#endif
|