Files
2026-08-02 17:22:17 +09:00

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